From 1c668ce1269eee3c0afd0f085d96ede5742a122c Mon Sep 17 00:00:00 2001 From: Mike Gabriel Date: Sat, 27 Feb 2021 12:19:34 +0000 Subject: [PATCH] Import veyon_4.5.3+repack1.orig.tar.xz [dgit import orig veyon_4.5.3+repack1.orig.tar.xz] --- .gitignore | 6 + .gitmodules | 23 + .mailmap | 1 + .tx/config | 9 + 3rdparty/kldap/.gitignore | 20 + 3rdparty/kldap/.gitlab-ci.yml | 30 + 3rdparty/kldap/CMakeLists.txt | 113 + 3rdparty/kldap/KF5LdapConfig.cmake.in | 3 + 3rdparty/kldap/LICENSES/BSD-3-Clause.txt | 26 + 3rdparty/kldap/LICENSES/LGPL-2.0-or-later.txt | 446 ++ 3rdparty/kldap/LICENSES/MIT.txt | 19 + 3rdparty/kldap/README.md | 3 + 3rdparty/kldap/cmake/FindLdap.cmake | 99 + 3rdparty/kldap/metainfo.yaml | 16 + 3rdparty/kldap/src/CMakeLists.txt | 161 + 3rdparty/kldap/src/Messages.sh | 3 + 3rdparty/kldap/src/core/ber.cpp | 456 ++ 3rdparty/kldap/src/core/ber.h | 115 + 3rdparty/kldap/src/core/ldapconnection.cpp | 448 ++ 3rdparty/kldap/src/core/ldapconnection.h | 130 + 3rdparty/kldap/src/core/ldapcontrol.cpp | 144 + 3rdparty/kldap/src/core/ldapcontrol.h | 103 + 3rdparty/kldap/src/core/ldapdefs.h | 148 + 3rdparty/kldap/src/core/ldapdn.cpp | 201 + 3rdparty/kldap/src/core/ldapdn.h | 72 + 3rdparty/kldap/src/core/ldapobject.cpp | 138 + 3rdparty/kldap/src/core/ldapobject.h | 104 + 3rdparty/kldap/src/core/ldapoperation.cpp | 1298 +++++ 3rdparty/kldap/src/core/ldapoperation.h | 280 + 3rdparty/kldap/src/core/ldapsearch.cpp | 342 ++ 3rdparty/kldap/src/core/ldapsearch.h | 130 + 3rdparty/kldap/src/core/ldapserver.cpp | 429 ++ 3rdparty/kldap/src/core/ldapserver.h | 317 ++ 3rdparty/kldap/src/core/ldapurl.cpp | 288 ++ 3rdparty/kldap/src/core/ldapurl.h | 170 + 3rdparty/kldap/src/core/ldif.cpp | 434 ++ 3rdparty/kldap/src/core/ldif.h | 183 + 3rdparty/kldap/src/core/w32-ldap-help.h | 119 + 3rdparty/kldap/src/kldap_config.h.cmake | 12 + 3rdparty/kldap/src/widgets/addhostdialog.cpp | 188 + 3rdparty/kldap/src/widgets/addhostdialog.h | 42 + 3rdparty/kldap/src/widgets/ldapclient.cpp | 283 ++ 3rdparty/kldap/src/widgets/ldapclient.h | 141 + .../kldap/src/widgets/ldapclientsearch.cpp | 431 ++ 3rdparty/kldap/src/widgets/ldapclientsearch.h | 177 + .../src/widgets/ldapclientsearchconfig.cpp | 64 + .../src/widgets/ldapclientsearchconfig.h | 44 + .../ldapclientsearchconfigreadconfigjob.cpp | 199 + .../ldapclientsearchconfigreadconfigjob.h | 50 + .../ldapclientsearchconfigwriteconfigjob.cpp | 149 + .../ldapclientsearchconfigwriteconfigjob.h | 52 + .../kldap/src/widgets/ldapconfigurewidget.cpp | 323 ++ .../kldap/src/widgets/ldapconfigurewidget.h | 63 + .../kldap/src/widgets/ldapconfigwidget.cpp | 911 ++++ 3rdparty/kldap/src/widgets/ldapconfigwidget.h | 264 + .../ldapsearchclientreadconfigserverjob.cpp | 87 + .../ldapsearchclientreadconfigserverjob.h | 47 + .../kldap/src/widgets/ldapwidgetitem_p.cpp | 37 + 3rdparty/kldap/src/widgets/ldapwidgetitem_p.h | 32 + .../ldapwidgetitemreadconfigserverjob.cpp | 76 + .../ldapwidgetitemreadconfigserverjob.h | 45 + 3rdparty/kldap/tests/CMakeLists.txt | 8 + 3rdparty/kldap/tests/testldapclient.cpp | 176 + 3rdparty/kldap/tests/testldapclient.h | 46 + 3rdparty/qthttpserver/.gitignore | 154 + 3rdparty/qthttpserver/.gitmodules | 3 + 3rdparty/qthttpserver/.qmake.conf | 3 + 3rdparty/qthttpserver/CMakeLists.txt | 15 + 3rdparty/qthttpserver/LICENSE.GPL3 | 674 +++ .../coin/product_dependencies.yaml | 3 + 3rdparty/qthttpserver/dependencies.yaml | 4 + 3rdparty/qthttpserver/examples/CMakeLists.txt | 7 + 3rdparty/qthttpserver/examples/examples.pro | 4 + .../examples/httpserver/CMakeLists.txt | 4 + .../httpserver/afterrequest/CMakeLists.txt | 27 + .../httpserver/afterrequest/afterrequest.pro | 13 + .../examples/httpserver/afterrequest/main.cpp | 80 + .../examples/httpserver/httpserver.pro | 6 + .../examples/httpserver/simple/CMakeLists.txt | 40 + .../examples/httpserver/simple/assets.qrc | 5 + .../httpserver/simple/assets/qt-logo.png | Bin 0 -> 1032 bytes .../examples/httpserver/simple/main.cpp | 126 + .../examples/httpserver/simple/simple.pro | 16 + 3rdparty/qthttpserver/qthttpserver.pro | 1 + .../qthttpserver/src/3rdparty/http-parser.pri | 6 + .../src/3rdparty/http-parser/.gitignore | 30 + .../src/3rdparty/http-parser/.mailmap | 8 + .../src/3rdparty/http-parser/.travis.yml | 13 + .../src/3rdparty/http-parser/AUTHORS | 68 + .../src/3rdparty/http-parser/LICENSE-MIT | 19 + .../src/3rdparty/http-parser/Makefile | 160 + .../src/3rdparty/http-parser/README.md | 246 + .../src/3rdparty/http-parser/bench.c | 128 + .../http-parser/contrib/parsertrace.c | 157 + .../3rdparty/http-parser/contrib/url_parser.c | 47 + .../src/3rdparty/http-parser/http_parser.c | 2501 +++++++++ .../src/3rdparty/http-parser/http_parser.gyp | 111 + .../src/3rdparty/http-parser/http_parser.h | 439 ++ .../src/3rdparty/http-parser/test.c | 4515 +++++++++++++++++ .../src/3rdparty/qt_attribution.json | 12 + 3rdparty/qthttpserver/src/CMakeLists.txt | 6 + .../src/httpserver/CMakeLists.txt | 56 + .../src/httpserver/httpserver.pro | 48 + .../src/httpserver/qabstracthttpserver.cpp | 310 ++ .../src/httpserver/qabstracthttpserver.h | 97 + .../src/httpserver/qabstracthttpserver_p.h | 82 + .../src/httpserver/qhttpserver.cpp | 183 + .../qthttpserver/src/httpserver/qhttpserver.h | 222 + .../src/httpserver/qhttpserver_p.h | 69 + .../httpserver/qhttpserverfutureresponse.cpp | 166 + .../httpserver/qhttpserverfutureresponse.h | 162 + .../src/httpserver/qhttpserverliterals.cpp | 59 + .../src/httpserver/qhttpserverliterals_p.h | 62 + .../src/httpserver/qhttpserverrequest.cpp | 319 ++ .../src/httpserver/qhttpserverrequest.h | 108 + .../src/httpserver/qhttpserverrequest_p.h | 113 + .../src/httpserver/qhttpserverresponder.cpp | 413 ++ .../src/httpserver/qhttpserverresponder.h | 187 + .../src/httpserver/qhttpserverresponder_p.h | 73 + .../src/httpserver/qhttpserverresponse.cpp | 370 ++ .../src/httpserver/qhttpserverresponse.h | 129 + .../src/httpserver/qhttpserverresponse_p.h | 76 + .../src/httpserver/qhttpserverrouter.cpp | 312 ++ .../src/httpserver/qhttpserverrouter.h | 161 + .../src/httpserver/qhttpserverrouter_p.h | 65 + .../src/httpserver/qhttpserverrouterrule.cpp | 276 + .../src/httpserver/qhttpserverrouterrule.h | 90 + .../src/httpserver/qhttpserverrouterrule_p.h | 62 + .../httpserver/qhttpserverrouterviewtraits.h | 164 + .../src/httpserver/qhttpserverviewtraits.h | 109 + .../httpserver/qhttpserverviewtraits_impl.h | 194 + .../src/httpserver/qthttpserverglobal.h | 50 + 3rdparty/qthttpserver/src/src.pro | 10 + .../qthttpserver/src/sslserver/CMakeLists.txt | 16 + .../qthttpserver/src/sslserver/qsslserver.cpp | 72 + .../qthttpserver/src/sslserver/qsslserver.h | 65 + .../qthttpserver/src/sslserver/qsslserver_p.h | 45 + .../src/sslserver/qtsslserverglobal.h | 49 + .../qthttpserver/src/sslserver/sslserver.pro | 14 + 3rdparty/qthttpserver/sync.profile | 4 + 3rdparty/qthttpserver/tests/CMakeLists.txt | 7 + .../qthttpserver/tests/auto/CMakeLists.txt | 7 + 3rdparty/qthttpserver/tests/auto/auto.pro | 11 + .../tests/auto/cmake/CMakeLists.txt | 12 + .../cmake/abstracthttpserver/CMakeLists.txt | 7 + .../auto/cmake/abstracthttpserver/main.cpp | 41 + .../qthttpserver/tests/auto/cmake/cmake.pro | 4 + .../auto/qabstracthttpserver/CMakeLists.txt | 12 + .../qabstracthttpserver.pro | 5 + .../tst_qabstracthttpserver.cpp | 280 + .../tests/auto/qhttpserver/CMakeLists.txt | 17 + .../auto/qhttpserver/data/application.json | 1 + .../tests/auto/qhttpserver/data/text.html | 1 + .../tests/auto/qhttpserver/qhttpserver.pro | 7 + .../auto/qhttpserver/tst_qhttpserver.cpp | 977 ++++ .../auto/qhttpserverresponder/CMakeLists.txt | 17 + .../auto/qhttpserverresponder/data/index.html | 1 + .../qhttpserverresponder.pro | 7 + .../tst_qhttpserverresponder.cpp | 284 ++ .../auto/qhttpserverresponse/CMakeLists.txt | 17 + .../qhttpserverresponse/data/application.json | 1 + .../tests/auto/qhttpserverresponse/data/empty | 0 .../auto/qhttpserverresponse/data/image.jpeg | Bin 0 -> 5514 bytes .../auto/qhttpserverresponse/data/image.png | Bin 0 -> 1032 bytes .../auto/qhttpserverresponse/data/image.svg | 67 + .../auto/qhttpserverresponse/data/text.html | 1 + .../auto/qhttpserverresponse/data/text.plain | 1 + .../qhttpserverresponse.pro | 7 + .../tst_qhttpserverresponse.cpp | 212 + .../auto/qhttpserverrouter/CMakeLists.txt | 12 + .../qhttpserverrouter/qhttpserverrouter.pro | 5 + .../tst_qhttpserverrouter.cpp | 544 ++ 3rdparty/qthttpserver/tests/tests.pro | 4 + CMakeLists.txt | 324 ++ CONTRIBUTORS | 26 + COPYING | 352 ++ INSTALL | 91 + README.md | 162 + cli/CMakeLists.txt | 19 + cli/data/veyon-cli.exe.manifest.in | 14 + cli/data/veyon-wcli.exe.manifest.in | 14 + cli/src/main.cpp | 203 + cli/veyon-cli.1 | 371 ++ cli/veyon-cli.rc.in | 31 + cli/veyon-wcli.rc.in | 31 + cmake/CPackDefinitions.cmake | 115 + cmake/modules/BuildVeyonApplication.cmake | 20 + cmake/modules/BuildVeyonPlugin.cmake | 22 + cmake/modules/COPYING-CMAKE-SCRIPTS | 22 + cmake/modules/ConfigureFiles.cmake | 7 + cmake/modules/CreateTranslations.cmake | 32 + cmake/modules/FindLZO.cmake | 29 + cmake/modules/FindLdap.cmake | 114 + cmake/modules/FindLibVNCClient.cmake | 61 + cmake/modules/FindLibVNCServer.cmake | 61 + cmake/modules/FindPAM.cmake | 74 + cmake/modules/FindQCA.cmake | 99 + cmake/modules/FindQtTranslations.cmake | 37 + cmake/modules/FindSasl2.cmake | 114 + cmake/modules/LibVNCServerIntegration.cmake | 76 + .../modules/SetDefaultTargetProperties.cmake | 3 + cmake/modules/WindowsBuildHelpers.cmake | 26 + cmake/modules/WindowsInstaller.cmake | 75 + cmake/modules/XdgInstall.cmake | 12 + configurator/CMakeLists.txt | 18 + .../io.veyon.veyon-configurator.policy.in | 20 + .../data/veyon-configurator.desktop.in | 11 + .../data/veyon-configurator.exe.manifest.in | 21 + configurator/data/veyon-configurator.png | Bin 0 -> 781 bytes configurator/data/veyon-configurator.svg | 110 + configurator/data/veyon-configurator.xpm | 62 + configurator/resources/access-rule-ask.png | Bin 0 -> 1600 bytes .../application-x-ms-dos-executable.png | Bin 0 -> 349 bytes .../resources/application-x-sharedlib.png | Bin 0 -> 1485 bytes configurator/resources/configurator.qrc | 16 + .../resources/configure-shortcuts.png | Bin 0 -> 574 bytes configurator/resources/help-about.png | Bin 0 -> 1168 bytes .../resources/media-playback-start.png | Bin 0 -> 297 bytes .../resources/media-playback-stop.png | Bin 0 -> 198 bytes configurator/resources/network-vpn.png | Bin 0 -> 1146 bytes configurator/resources/vcs-conflicting.png | Bin 0 -> 1171 bytes configurator/resources/vcs-normal.png | Bin 0 -> 1157 bytes configurator/resources/vcs-removed.png | Bin 0 -> 961 bytes configurator/resources/veyon-configurator.png | Bin 0 -> 1828 bytes configurator/src/AccessControlPage.cpp | 293 ++ configurator/src/AccessControlPage.h | 71 + configurator/src/AccessControlPage.ui | 608 +++ .../src/AccessControlRuleEditDialog.cpp | 171 + .../src/AccessControlRuleEditDialog.h | 50 + .../src/AccessControlRuleEditDialog.ui | 429 ++ .../src/AccessControlRuleListModel.cpp | 101 + configurator/src/AccessControlRuleListModel.h | 46 + .../src/AccessControlRulesTestDialog.cpp | 90 + .../src/AccessControlRulesTestDialog.h | 46 + .../src/AccessControlRulesTestDialog.ui | 128 + configurator/src/GeneralConfigurationPage.cpp | 249 + configurator/src/GeneralConfigurationPage.h | 59 + configurator/src/GeneralConfigurationPage.ui | 411 ++ configurator/src/MainWindow.cpp | 393 ++ configurator/src/MainWindow.h | 71 + configurator/src/MainWindow.ui | 357 ++ configurator/src/MasterConfigurationPage.cpp | 188 + configurator/src/MasterConfigurationPage.h | 62 + configurator/src/MasterConfigurationPage.ui | 613 +++ configurator/src/ServiceConfigurationPage.cpp | 168 + configurator/src/ServiceConfigurationPage.h | 66 + configurator/src/ServiceConfigurationPage.ui | 424 ++ configurator/src/main.cpp | 78 + configurator/veyon-configurator.1 | 24 + configurator/veyon-configurator.rc.in | 32 + core/CMakeLists.txt | 99 + core/builddata.qrc.in | 5 + core/include/AboutDialog.h | 48 + core/include/AccessControlProvider.h | 86 + core/include/AccessControlRule.h | 194 + core/include/AuthenticationCredentials.h | 123 + core/include/AuthenticationProxy.h | 103 + core/include/BuiltinFeatures.h | 67 + core/include/CommandLineIO.h | 60 + core/include/CommandLinePluginInterface.h | 61 + core/include/Computer.h | 107 + core/include/ComputerControlInterface.h | 180 + core/include/ComputerListModel.h | 94 + core/include/Configuration/JsonStore.h | 51 + core/include/Configuration/LocalStore.h | 50 + core/include/Configuration/Object.h | 105 + core/include/Configuration/Password.h | 55 + core/include/Configuration/Property.h | 181 + core/include/Configuration/Proxy.h | 86 + core/include/Configuration/Store.h | 111 + core/include/Configuration/UiMapping.h | 128 + core/include/ConfigurationManager.h | 52 + core/include/ConfigurationPage.h | 45 + .../ConfigurationPagePluginInterface.h | 44 + core/include/CryptoCore.h | 62 + core/include/DesktopAccessDialog.h | 147 + core/include/EnumHelper.h | 47 + core/include/Feature.h | 219 + core/include/FeatureControl.h | 109 + core/include/FeatureManager.h | 83 + core/include/FeatureMessage.h | 128 + core/include/FeatureProviderInterface.h | 179 + core/include/FeatureWorkerManager.h | 83 + core/include/FileSystemBrowser.h | 71 + core/include/Filesystem.h | 50 + core/include/HashList.h | 39 + core/include/HostAddress.h | 71 + core/include/KeyboardShortcutTrapper.h | 56 + core/include/LockWidget.h | 53 + core/include/Lockable.h | 44 + core/include/LockingPointer.h | 62 + core/include/Logger.h | 104 + core/include/MessageContext.h | 53 + core/include/MonitoringMode.h | 117 + core/include/NetworkObject.h | 163 + core/include/NetworkObjectDirectory.h | 88 + core/include/NetworkObjectDirectoryManager.h | 50 + .../NetworkObjectDirectoryPluginInterface.h | 46 + core/include/NetworkObjectModel.h | 52 + core/include/ObjectManager.h | 146 + core/include/PasswordDialog.h | 54 + core/include/PlatformCoreFunctions.h | 66 + core/include/PlatformFilesystemFunctions.h | 46 + core/include/PlatformInputDeviceFunctions.h | 41 + core/include/PlatformNetworkFunctions.h | 48 + core/include/PlatformPluginInterface.h | 56 + core/include/PlatformPluginManager.h | 43 + core/include/PlatformServiceFunctions.h | 57 + core/include/PlatformSessionFunctions.h | 45 + core/include/PlatformUserFunctions.h | 53 + core/include/Plugin.h | 49 + core/include/PluginInterface.h | 58 + core/include/PluginManager.h | 72 + core/include/ProcessHelper.h | 43 + core/include/ProgressWidget.h | 49 + core/include/QtCompat.h | 375 ++ core/include/RfbVeyonAuth.h | 62 + core/include/Screenshot.h | 90 + core/include/ServiceControl.h | 66 + core/include/SocketDevice.h | 91 + core/include/SystemTrayIcon.h | 119 + core/include/ToolButton.h | 152 + core/include/TranslationLoader.h | 38 + core/include/UserGroupsBackendInterface.h | 47 + core/include/UserGroupsBackendManager.h | 58 + core/include/VariantArrayMessage.h | 65 + core/include/VariantStream.h | 43 + core/include/VeyonConfiguration.h | 51 + core/include/VeyonConfigurationProperties.h | 166 + core/include/VeyonConnection.h | 111 + core/include/VeyonCore.h | 224 + core/include/VeyonMasterInterface.h | 53 + core/include/VeyonServerInterface.h | 42 + core/include/VeyonServiceControl.h | 42 + core/include/VeyonWorkerInterface.h | 37 + core/include/VncClientProtocol.h | 143 + core/include/VncConnection.h | 248 + core/include/VncEvents.h | 78 + core/include/VncFeatureMessageEvent.h | 42 + core/include/VncServerClient.h | 174 + core/include/VncServerPluginInterface.h | 65 + core/include/VncServerProtocol.h | 102 + core/include/VncView.h | 172 + core/include/VncViewWidget.h | 81 + core/include/veyonconfig.h.in | 9 + core/resources/application-x-pem-key.png | Bin 0 -> 1990 bytes core/resources/core.qrc | 44 + core/resources/default-pkey.pem | 52 + core/resources/dialog-ok-apply.png | Bin 0 -> 994 bytes core/resources/document-edit.png | Bin 0 -> 606 bytes core/resources/document-open.png | Bin 0 -> 370 bytes core/resources/document-save.png | Bin 0 -> 357 bytes core/resources/edit-delete.png | Bin 0 -> 388 bytes core/resources/edit-find.png | Bin 0 -> 1863 bytes core/resources/go-down.png | Bin 0 -> 1576 bytes core/resources/go-next.png | Bin 0 -> 1314 bytes core/resources/go-previous.png | Bin 0 -> 1297 bytes core/resources/go-up.png | Bin 0 -> 1566 bytes core/resources/help-about.png | Bin 0 -> 2335 bytes core/resources/icon128.png | Bin 0 -> 3080 bytes core/resources/icon16.png | Bin 0 -> 424 bytes core/resources/icon22.png | Bin 0 -> 810 bytes core/resources/icon32.png | Bin 0 -> 1097 bytes core/resources/icon64.png | Bin 0 -> 2022 bytes core/resources/languages.png | Bin 0 -> 344 bytes core/resources/license.png | Bin 0 -> 2863 bytes core/resources/list-add.png | Bin 0 -> 254 bytes core/resources/presentation-none.png | Bin 0 -> 618 bytes core/resources/toolbar-background.png | Bin 0 -> 223 bytes core/resources/user-group-new.png | Bin 0 -> 1805 bytes core/resources/watch1.png | Bin 0 -> 2616 bytes core/resources/watch10.png | Bin 0 -> 2635 bytes core/resources/watch11.png | Bin 0 -> 2585 bytes core/resources/watch12.png | Bin 0 -> 2630 bytes core/resources/watch13.png | Bin 0 -> 2630 bytes core/resources/watch14.png | Bin 0 -> 2609 bytes core/resources/watch15.png | Bin 0 -> 2534 bytes core/resources/watch16.png | Bin 0 -> 2655 bytes core/resources/watch2.png | Bin 0 -> 2605 bytes core/resources/watch3.png | Bin 0 -> 2568 bytes core/resources/watch4.png | Bin 0 -> 2603 bytes core/resources/watch5.png | Bin 0 -> 2630 bytes core/resources/watch6.png | Bin 0 -> 2623 bytes core/resources/watch7.png | Bin 0 -> 2568 bytes core/resources/watch8.png | Bin 0 -> 2625 bytes core/resources/watch9.png | Bin 0 -> 2641 bytes core/src/AboutDialog.cpp | 67 + core/src/AboutDialog.ui | 340 ++ core/src/AccessControlProvider.cpp | 467 ++ core/src/AccessControlRule.cpp | 129 + core/src/AuthenticationCredentials.cpp | 98 + core/src/BuiltinFeatures.cpp | 53 + core/src/CommandLineIO.cpp | 190 + core/src/Computer.cpp | 38 + core/src/ComputerControlInterface.cpp | 362 ++ core/src/ComputerListModel.cpp | 63 + core/src/Configuration/JsonStore.cpp | 198 + core/src/Configuration/LocalStore.cpp | 197 + core/src/Configuration/Object.cpp | 362 ++ core/src/Configuration/Password.cpp | 55 + core/src/Configuration/Property.cpp | 138 + core/src/Configuration/Proxy.cpp | 99 + core/src/Configuration/UiMapping.cpp | 224 + core/src/ConfigurationManager.cpp | 103 + core/src/ConfigurationPage.cpp | 30 + core/src/CryptoCore.cpp | 97 + core/src/DesktopAccessDialog.cpp | 170 + core/src/FeatureControl.cpp | 100 + core/src/FeatureManager.cpp | 259 + core/src/FeatureMessage.cpp | 79 + core/src/FeatureWorkerManager.cpp | 343 ++ core/src/FileSystemBrowser.cpp | 105 + core/src/Filesystem.cpp | 170 + core/src/HostAddress.cpp | 315 ++ core/src/LockWidget.cpp | 107 + core/src/Logger.cpp | 350 ++ core/src/MonitoringMode.cpp | 123 + core/src/NetworkObject.cpp | 237 + core/src/NetworkObjectDirectory.cpp | 376 ++ core/src/NetworkObjectDirectoryManager.cpp | 104 + core/src/PasswordDialog.cpp | 108 + core/src/PasswordDialog.ui | 142 + core/src/PlatformPluginManager.cpp | 48 + core/src/PluginManager.cpp | 215 + core/src/PrecompiledHeader.h | 29 + core/src/ProcessHelper.cpp | 81 + core/src/ProgressWidget.cpp | 80 + core/src/QtCompat.cpp | 166 + core/src/Screenshot.cpp | 223 + core/src/ServiceControl.cpp | 147 + core/src/SystemTrayIcon.cpp | 141 + core/src/ToolButton.cpp | 409 ++ core/src/TranslationLoader.cpp | 76 + core/src/UserGroupsBackendManager.cpp | 96 + core/src/VariantArrayMessage.cpp | 115 + core/src/VariantStream.cpp | 53 + core/src/VeyonConfiguration.cpp | 71 + core/src/VeyonConnection.cpp | 341 ++ core/src/VeyonCore.cpp | 687 +++ core/src/VeyonServiceControl.cpp | 68 + core/src/VncClientProtocol.cpp | 795 +++ core/src/VncConnection.cpp | 802 +++ core/src/VncEvents.cpp | 70 + core/src/VncFeatureMessageEvent.cpp | 48 + core/src/VncServerProtocol.cpp | 328 ++ core/src/VncView.cpp | 649 +++ core/src/VncViewWidget.cpp | 344 ++ core/src/d3des.c | 437 ++ core/src/d3des.h | 46 + master/CMakeLists.txt | 35 + master/data/veyon-master.desktop.in | 11 + master/data/veyon-master.exe.manifest.in | 14 + master/data/veyon-master.png | Bin 0 -> 1514 bytes master/data/veyon-master.svg | 92 + master/data/veyon-master.xpm | 112 + master/resources/align-grid.png | Bin 0 -> 286 bytes master/resources/camera-photo.png | Bin 0 -> 2986 bytes master/resources/computer-slideshow.png | Bin 0 -> 492 bytes master/resources/computers.png | Bin 0 -> 279 bytes master/resources/example-screenshot.png | Bin 0 -> 9305 bytes master/resources/examples.qrc | 5 + .../resources/exchange-positions-zorder.png | Bin 0 -> 1984 bytes master/resources/master.qrc | 22 + master/resources/media-playback-pause.png | Bin 0 -> 196 bytes master/resources/media-playback-start.png | Bin 0 -> 392 bytes master/resources/powered-on.png | Bin 0 -> 2479 bytes .../preferences-desktop-display-blue.png | Bin 0 -> 1242 bytes .../preferences-desktop-display-gray.png | Bin 0 -> 1055 bytes .../preferences-desktop-display-orange.png | Bin 0 -> 1206 bytes .../preferences-desktop-display-red.png | Bin 0 -> 1239 bytes .../resources/preferences-desktop-display.png | Bin 0 -> 1193 bytes master/resources/splash.png | Bin 0 -> 5466 bytes master/resources/spotlight.png | Bin 0 -> 3682 bytes master/resources/update-realtime-disabled.png | Bin 0 -> 2984 bytes master/resources/update-realtime-enabled.png | Bin 0 -> 3610 bytes master/resources/zoom-fit-best.png | Bin 0 -> 566 bytes master/src/CheckableItemProxyModel.cpp | 257 + master/src/CheckableItemProxyModel.h | 68 + master/src/ComputerControlListModel.cpp | 537 ++ master/src/ComputerControlListModel.h | 99 + master/src/ComputerManager.cpp | 476 ++ master/src/ComputerManager.h | 98 + master/src/ComputerMonitoringModel.cpp | 75 + master/src/ComputerMonitoringModel.h | 58 + master/src/ComputerMonitoringView.cpp | 212 + master/src/ComputerMonitoringView.h | 103 + master/src/ComputerMonitoringWidget.cpp | 327 ++ master/src/ComputerMonitoringWidget.h | 80 + master/src/ComputerSelectPanel.cpp | 174 + master/src/ComputerSelectPanel.h | 59 + master/src/ComputerSelectPanel.ui | 90 + master/src/DocumentationFigureCreator.cpp | 605 +++ master/src/DocumentationFigureCreator.h | 76 + master/src/FlexibleListView.cpp | 254 + master/src/FlexibleListView.h | 66 + master/src/KItemModelsIntegration.cpp | 4 + master/src/LocationDialog.cpp | 72 + master/src/LocationDialog.h | 55 + master/src/LocationDialog.ui | 138 + master/src/MainToolBar.cpp | 105 + master/src/MainToolBar.h | 47 + master/src/MainWindow.cpp | 533 ++ master/src/MainWindow.h | 91 + master/src/MainWindow.ui | 465 ++ master/src/NetworkObjectFilterProxyModel.cpp | 90 + master/src/NetworkObjectFilterProxyModel.h | 50 + master/src/NetworkObjectOverlayDataModel.cpp | 83 + master/src/NetworkObjectOverlayDataModel.h | 46 + master/src/NetworkObjectTreeModel.cpp | 263 + master/src/NetworkObjectTreeModel.h | 67 + master/src/RecursiveFilterProxyModel.cpp | 37 + master/src/RecursiveFilterProxyModel.h | 34 + master/src/ScreenshotManagementPanel.cpp | 169 + master/src/ScreenshotManagementPanel.h | 71 + master/src/ScreenshotManagementPanel.ui | 160 + master/src/SlideshowModel.cpp | 202 + master/src/SlideshowModel.h | 60 + master/src/SlideshowPanel.cpp | 95 + master/src/SlideshowPanel.h | 58 + master/src/SlideshowPanel.ui | 164 + master/src/SpotlightModel.cpp | 112 + master/src/SpotlightModel.h | 56 + master/src/SpotlightPanel.cpp | 179 + master/src/SpotlightPanel.h | 64 + master/src/SpotlightPanel.ui | 229 + master/src/UserConfig.cpp | 43 + master/src/UserConfig.h | 62 + master/src/VeyonMaster.cpp | 324 ++ master/src/VeyonMaster.h | 125 + master/src/kitemmodels_debug.h | 7 + master/src/kitemmodels_export.h | 2 + master/src/main.cpp | 67 + master/veyon-master.1 | 31 + master/veyon-master.rc.in | 32 + plugins/CMakeLists.txt | 7 + .../authkeys/AuthKeysConfigurationPage.cpp | 288 ++ plugins/authkeys/AuthKeysConfigurationPage.h | 63 + plugins/authkeys/AuthKeysConfigurationPage.ui | 244 + plugins/authkeys/AuthKeysManager.cpp | 554 ++ plugins/authkeys/AuthKeysManager.h | 78 + plugins/authkeys/AuthKeysPlugin.cpp | 335 ++ plugins/authkeys/AuthKeysPlugin.h | 110 + plugins/authkeys/AuthKeysTableModel.cpp | 114 + plugins/authkeys/AuthKeysTableModel.h | 62 + plugins/authkeys/CMakeLists.txt | 13 + plugins/builtindirectory/BuiltinDirectory.cpp | 85 + plugins/builtindirectory/BuiltinDirectory.h | 44 + .../BuiltinDirectoryConfiguration.h | 36 + .../BuiltinDirectoryConfigurationPage.cpp | 271 + .../BuiltinDirectoryConfigurationPage.h | 66 + .../BuiltinDirectoryConfigurationPage.ui | 308 ++ .../BuiltinDirectoryPlugin.cpp | 770 +++ .../builtindirectory/BuiltinDirectoryPlugin.h | 214 + plugins/builtindirectory/CMakeLists.txt | 13 + plugins/builtindirectory/builtindirectory.png | Bin 0 -> 1654 bytes plugins/builtindirectory/builtindirectory.qrc | 5 + plugins/config/CMakeLists.txt | 3 + plugins/config/ConfigCommandLinePlugin.cpp | 363 ++ plugins/config/ConfigCommandLinePlugin.h | 109 + plugins/demo/CMakeLists.txt | 21 + plugins/demo/DemoClient.cpp | 121 + plugins/demo/DemoClient.h | 45 + plugins/demo/DemoConfiguration.h | 36 + plugins/demo/DemoConfigurationPage.cpp | 66 + plugins/demo/DemoConfigurationPage.h | 52 + plugins/demo/DemoConfigurationPage.ui | 129 + plugins/demo/DemoFeaturePlugin.cpp | 629 +++ plugins/demo/DemoFeaturePlugin.h | 161 + plugins/demo/DemoServer.cpp | 341 ++ plugins/demo/DemoServer.h | 112 + plugins/demo/DemoServerConnection.cpp | 199 + plugins/demo/DemoServerConnection.h | 73 + plugins/demo/DemoServerProtocol.cpp | 88 + plugins/demo/DemoServerProtocol.h | 49 + plugins/demo/demo.png | Bin 0 -> 5302 bytes plugins/demo/demo.qrc | 8 + plugins/demo/presentation-fullscreen.png | Bin 0 -> 354 bytes plugins/demo/presentation-window.png | Bin 0 -> 746 bytes plugins/demo/window-duplicate.png | Bin 0 -> 311 bytes plugins/desktopservices/CMakeLists.txt | 20 + .../desktopservices/DesktopServiceObject.cpp | 83 + .../desktopservices/DesktopServiceObject.h | 90 + .../DesktopServicesConfiguration.h | 34 + .../DesktopServicesConfigurationPage.cpp | 219 + .../DesktopServicesConfigurationPage.h | 69 + .../DesktopServicesConfigurationPage.ui | 283 ++ .../DesktopServicesFeaturePlugin.cpp | 508 ++ .../DesktopServicesFeaturePlugin.h | 134 + plugins/desktopservices/OpenWebsiteDialog.cpp | 81 + plugins/desktopservices/OpenWebsiteDialog.h | 60 + plugins/desktopservices/OpenWebsiteDialog.ui | 145 + plugins/desktopservices/RunProgramDialog.cpp | 74 + plugins/desktopservices/RunProgramDialog.h | 60 + plugins/desktopservices/RunProgramDialog.ui | 143 + plugins/desktopservices/desktop-services.png | Bin 0 -> 1292 bytes plugins/desktopservices/desktopservices.qrc | 7 + .../desktopservices/internet-web-browser.png | Bin 0 -> 5425 bytes .../preferences-desktop-launch-feedback.png | Bin 0 -> 3859 bytes plugins/filetransfer/CMakeLists.txt | 21 + plugins/filetransfer/FileReadThread.cpp | 128 + plugins/filetransfer/FileReadThread.h | 61 + .../filetransfer/FileTransferConfiguration.h | 38 + .../FileTransferConfigurationPage.cpp | 86 + .../FileTransferConfigurationPage.h | 54 + .../FileTransferConfigurationPage.ui | 107 + .../filetransfer/FileTransferController.cpp | 280 + plugins/filetransfer/FileTransferController.h | 104 + plugins/filetransfer/FileTransferDialog.cpp | 114 + plugins/filetransfer/FileTransferDialog.h | 51 + plugins/filetransfer/FileTransferDialog.ui | 138 + .../filetransfer/FileTransferListModel.cpp | 69 + plugins/filetransfer/FileTransferListModel.h | 47 + plugins/filetransfer/FileTransferPlugin.cpp | 346 ++ plugins/filetransfer/FileTransferPlugin.h | 140 + .../FileTransferUserConfiguration.h | 45 + plugins/filetransfer/applications-office.png | Bin 0 -> 2322 bytes plugins/filetransfer/document-share.png | Bin 0 -> 614 bytes plugins/filetransfer/file-finished.png | Bin 0 -> 1677 bytes plugins/filetransfer/file-scheduled.png | Bin 0 -> 1507 bytes plugins/filetransfer/file-transferring.png | Bin 0 -> 3077 bytes plugins/filetransfer/filetransfer.qrc | 9 + plugins/ldap/CMakeLists.txt | 11 + plugins/ldap/LdapPlugin.cpp | 309 ++ plugins/ldap/LdapPlugin.h | 135 + plugins/ldap/common/CMakeLists.txt | 29 + plugins/ldap/common/LdapBrowseDialog.cpp | 96 + plugins/ldap/common/LdapBrowseDialog.h | 55 + plugins/ldap/common/LdapBrowseDialog.ui | 80 + plugins/ldap/common/LdapBrowseModel.cpp | 421 ++ plugins/ldap/common/LdapBrowseModel.h | 79 + plugins/ldap/common/LdapClient.cpp | 657 +++ plugins/ldap/common/LdapClient.h | 153 + plugins/ldap/common/LdapCommon.h | 32 + plugins/ldap/common/LdapConfiguration.cpp | 28 + plugins/ldap/common/LdapConfiguration.h | 77 + plugins/ldap/common/LdapConfigurationPage.cpp | 783 +++ plugins/ldap/common/LdapConfigurationPage.h | 100 + plugins/ldap/common/LdapConfigurationPage.ui | 1369 +++++ plugins/ldap/common/LdapDirectory.cpp | 489 ++ plugins/ldap/common/LdapDirectory.h | 154 + .../common/LdapNetworkObjectDirectory.cpp | 229 + .../ldap/common/LdapNetworkObjectDirectory.h | 50 + .../application-x-kexi-connectiondata.png | Bin 0 -> 2255 bytes plugins/ldap/common/attribute.png | Bin 0 -> 1388 bytes plugins/ldap/common/computer.png | Bin 0 -> 304 bytes plugins/ldap/common/configure.png | Bin 0 -> 205 bytes .../common/distribute-vertical-margin.png | Bin 0 -> 206 bytes plugins/ldap/common/folder-stash.png | Bin 0 -> 388 bytes plugins/ldap/common/ldap.qrc | 11 + plugins/ldap/common/user-group-properties.png | Bin 0 -> 888 bytes plugins/ldap/kldap/CMakeLists.txt | 43 + plugins/ldap/kldap/KLdapIntegration.cpp | 27 + plugins/ldap/kldap/KLocalizedString | 1 + plugins/ldap/kldap/kldap_export.h | 28 + plugins/ldap/kldap/klocalizedstring.h | 49 + plugins/ldap/kldap/ldap_debug.h | 31 + plugins/platform/CMakeLists.txt | 7 + plugins/platform/common/LogonHelper.cpp | 69 + plugins/platform/common/LogonHelper.h | 44 + .../common/PersistentLogonCredentials.cpp | 92 + .../common/PersistentLogonCredentials.h | 40 + .../common/PlatformSessionManager.cpp | 159 + .../platform/common/PlatformSessionManager.h | 78 + .../platform/common/ServiceDataManager.cpp | 206 + plugins/platform/common/ServiceDataManager.h | 90 + plugins/platform/linux/CMakeLists.txt | 77 + plugins/platform/linux/LinuxCoreFunctions.cpp | 390 ++ plugins/platform/linux/LinuxCoreFunctions.h | 86 + .../platform/linux/LinuxDesktopIntegration.h | 71 + .../linux/LinuxFilesystemFunctions.cpp | 185 + .../platform/linux/LinuxFilesystemFunctions.h | 44 + .../linux/LinuxInputDeviceFunctions.cpp | 106 + .../linux/LinuxInputDeviceFunctions.h | 53 + plugins/platform/linux/LinuxKeyboardInput.cpp | 70 + plugins/platform/linux/LinuxKeyboardInput.h | 50 + .../linux/LinuxKeyboardShortcutTrapper.h | 45 + .../platform/linux/LinuxNetworkFunctions.cpp | 90 + .../platform/linux/LinuxNetworkFunctions.h | 39 + .../linux/LinuxPlatformConfiguration.h | 34 + .../linux/LinuxPlatformConfigurationPage.cpp | 67 + .../linux/LinuxPlatformConfigurationPage.h | 49 + .../linux/LinuxPlatformConfigurationPage.ui | 85 + .../platform/linux/LinuxPlatformPlugin.cpp | 56 + plugins/platform/linux/LinuxPlatformPlugin.h | 128 + plugins/platform/linux/LinuxServiceCore.cpp | 293 ++ plugins/platform/linux/LinuxServiceCore.h | 71 + .../platform/linux/LinuxServiceFunctions.cpp | 122 + .../platform/linux/LinuxServiceFunctions.h | 47 + .../platform/linux/LinuxSessionFunctions.cpp | 210 + .../platform/linux/LinuxSessionFunctions.h | 85 + plugins/platform/linux/LinuxUserFunctions.cpp | 374 ++ plugins/platform/linux/LinuxUserFunctions.h | 59 + .../platform/linux/auth-helper/CMakeLists.txt | 12 + .../linux/auth-helper/VeyonAuthHelper.cpp | 104 + plugins/platform/linux/linux.qrc | 5 + plugins/platform/linux/tux.png | Bin 0 -> 3296 bytes plugins/powercontrol/CMakeLists.txt | 10 + .../PowerControlFeaturePlugin.cpp | 442 ++ .../powercontrol/PowerControlFeaturePlugin.h | 130 + .../powercontrol/PowerDownTimeInputDialog.cpp | 67 + .../powercontrol/PowerDownTimeInputDialog.h | 49 + .../powercontrol/PowerDownTimeInputDialog.ui | 128 + plugins/powercontrol/powercontrol.qrc | 7 + .../preferences-system-power-management.png | Bin 0 -> 4217 bytes plugins/powercontrol/system-reboot.png | Bin 0 -> 6038 bytes plugins/powercontrol/system-shutdown.png | Bin 0 -> 5688 bytes plugins/remoteaccess/CMakeLists.txt | 5 + .../RemoteAccessFeaturePlugin.cpp | 274 + .../remoteaccess/RemoteAccessFeaturePlugin.h | 116 + plugins/remoteaccess/RemoteAccessWidget.cpp | 421 ++ plugins/remoteaccess/RemoteAccessWidget.h | 115 + plugins/remoteaccess/application-exit.png | Bin 0 -> 207 bytes plugins/remoteaccess/camera-photo.png | Bin 0 -> 2986 bytes plugins/remoteaccess/kmag.png | Bin 0 -> 6369 bytes plugins/remoteaccess/krdc.png | Bin 0 -> 6420 bytes .../preferences-desktop-keyboard.png | Bin 0 -> 622 bytes plugins/remoteaccess/remoteaccess.qrc | 10 + plugins/remoteaccess/view-fullscreen.png | Bin 0 -> 486 bytes plugins/screenlock/CMakeLists.txt | 3 + .../screenlock/ScreenLockFeaturePlugin.cpp | 166 + plugins/screenlock/ScreenLockFeaturePlugin.h | 98 + .../screenlock/locked-screen-background.png | Bin 0 -> 2217 bytes plugins/screenlock/screenlock.qrc | 6 + plugins/screenlock/system-lock-screen.png | Bin 0 -> 4324 bytes plugins/screenshot/CMakeLists.txt | 7 + .../screenshot/ScreenshotFeaturePlugin.cpp | 90 + plugins/screenshot/ScreenshotFeaturePlugin.h | 82 + plugins/screenshot/camera-photo.png | Bin 0 -> 2986 bytes plugins/screenshot/screenshot.qrc | 5 + plugins/servicecontrol/CMakeLists.txt | 6 + .../servicecontrol/ServiceControlPlugin.cpp | 113 + plugins/servicecontrol/ServiceControlPlugin.h | 99 + plugins/shell/CMakeLists.txt | 3 + plugins/shell/ShellCommandLinePlugin.cpp | 104 + plugins/shell/ShellCommandLinePlugin.h | 90 + plugins/systemusergroups/CMakeLists.txt | 6 + .../SystemUserGroupsPlugin.cpp | 53 + .../systemusergroups/SystemUserGroupsPlugin.h | 83 + plugins/testing/CMakeLists.txt | 5 + plugins/testing/TestingCommandLinePlugin.cpp | 120 + plugins/testing/TestingCommandLinePlugin.h | 91 + plugins/textmessage/CMakeLists.txt | 11 + plugins/textmessage/TextMessageDialog.cpp | 56 + plugins/textmessage/TextMessageDialog.h | 49 + plugins/textmessage/TextMessageDialog.ui | 73 + .../textmessage/TextMessageFeaturePlugin.cpp | 149 + .../textmessage/TextMessageFeaturePlugin.h | 97 + plugins/textmessage/dialog-information.png | Bin 0 -> 1889 bytes plugins/textmessage/textmessage.qrc | 5 + plugins/usersessioncontrol/CMakeLists.txt | 11 + .../usersessioncontrol/UserLoginDialog.cpp | 75 + plugins/usersessioncontrol/UserLoginDialog.h | 49 + plugins/usersessioncontrol/UserLoginDialog.ui | 142 + .../UserSessionControlPlugin.cpp | 166 + .../UserSessionControlPlugin.h | 99 + plugins/usersessioncontrol/login-user.png | Bin 0 -> 4617 bytes plugins/usersessioncontrol/logout-user.png | Bin 0 -> 10642 bytes .../usersessioncontrol/usersessioncontrol.qrc | 6 + plugins/vncserver/CMakeLists.txt | 10 + plugins/vncserver/external/CMakeLists.txt | 11 + .../vncserver/external/ExternalVncServer.cpp | 96 + .../vncserver/external/ExternalVncServer.h | 98 + .../external/ExternalVncServerConfiguration.h | 33 + .../ExternalVncServerConfigurationWidget.cpp | 48 + .../ExternalVncServerConfigurationWidget.h | 47 + .../ExternalVncServerConfigurationWidget.ui | 59 + plugins/vncserver/headless/CMakeLists.txt | 16 + .../headless/HeadlessVncConfiguration.h | 34 + .../vncserver/headless/HeadlessVncServer.cpp | 147 + .../vncserver/headless/HeadlessVncServer.h | 113 + .../BuiltinUltraVncServer.cpp | 246 + .../ultravnc-builtin/BuiltinUltraVncServer.h | 122 + .../vncserver/ultravnc-builtin/CMakeLists.txt | 105 + .../ultravnc-builtin/LogoffEventFilter.cpp | 71 + .../ultravnc-builtin/LogoffEventFilter.h | 42 + .../ultravnc-builtin/UltraVncConfiguration.h | 38 + .../UltraVncConfigurationWidget.cpp | 50 + .../UltraVncConfigurationWidget.h | 47 + .../UltraVncConfigurationWidget.ui | 87 + .../vncserver/ultravnc-builtin/ultravnc.cpp | 19 + .../ultravnc-builtin/vnchooks/CMakeLists.txt | 20 + .../vncserver/ultravnc-builtin/vncntlm.cpp | 31 + .../x11vnc-builtin/BuiltinX11VncServer.cpp | 158 + .../x11vnc-builtin/BuiltinX11VncServer.h | 98 + .../vncserver/x11vnc-builtin/CMakeLists.txt | 262 + .../x11vnc-builtin/X11VncConfiguration.h | 33 + .../X11VncConfigurationWidget.cpp | 47 + .../X11VncConfigurationWidget.h | 47 + .../X11VncConfigurationWidget.ui | 50 + plugins/vncserver/x11vnc-builtin/config.h.in | 305 ++ .../vncserver/x11vnc-builtin/x11vnc-veyon.c | 7 + plugins/webapi/CMakeLists.txt | 39 + plugins/webapi/WebApiAuthenticationProxy.cpp | 146 + plugins/webapi/WebApiAuthenticationProxy.h | 66 + plugins/webapi/WebApiConfiguration.h | 41 + plugins/webapi/WebApiConfigurationPage.cpp | 75 + plugins/webapi/WebApiConfigurationPage.h | 51 + plugins/webapi/WebApiConfigurationPage.ui | 249 + plugins/webapi/WebApiConnection.cpp | 179 + plugins/webapi/WebApiConnection.h | 109 + plugins/webapi/WebApiController.cpp | 491 ++ plugins/webapi/WebApiController.h | 148 + plugins/webapi/WebApiHttpServer.cpp | 320 ++ plugins/webapi/WebApiHttpServer.h | 69 + plugins/webapi/WebApiPlugin.cpp | 98 + plugins/webapi/WebApiPlugin.h | 113 + plugins/webapi/python/LICENSE | 352 ++ plugins/webapi/python/README.md | 4 + plugins/webapi/python/setup.py | 22 + .../webapi/python/tests/test_veyon_webapi.py | 75 + plugins/webapi/python/veyon/__init__.py | 194 + plugins/webapi/qthttpserver/CMakeLists.txt | 70 + plugins/webapi/webapi.png | Bin 0 -> 3265 bytes plugins/webapi/webapi.qrc | 5 + plugins/webapi/webapi.svg | 70 + project.yml | 19 + server/CMakeLists.txt | 19 + server/data/veyon-server.exe.manifest.in | 14 + server/src/ComputerControlClient.cpp | 73 + server/src/ComputerControlClient.h | 73 + server/src/ComputerControlServer.cpp | 253 + server/src/ComputerControlServer.h | 100 + server/src/ServerAccessControlManager.cpp | 232 + server/src/ServerAccessControlManager.h | 67 + server/src/ServerAuthenticationManager.cpp | 247 + server/src/ServerAuthenticationManager.h | 61 + server/src/VeyonServerProtocol.cpp | 66 + server/src/VeyonServerProtocol.h | 51 + server/src/VncProxyConnection.cpp | 250 + server/src/VncProxyConnection.h | 87 + server/src/VncProxyConnectionFactory.h | 46 + server/src/VncProxyServer.cpp | 130 + server/src/VncProxyServer.h | 73 + server/src/VncServer.cpp | 163 + server/src/VncServer.h | 55 + server/src/main.cpp | 45 + server/veyon-server.1 | 45 + server/veyon-server.rc.in | 31 + service/CMakeLists.txt | 19 + service/data/veyon-service.exe.manifest.in | 14 + service/src/main.cpp | 44 + service/veyon-service.1 | 45 + service/veyon-service.rc.in | 31 + service/veyon.service.in | 17 + translations/CMakeLists.txt | 12 + translations/veyon.ts | 4056 +++++++++++++++ translations/veyon_ar.ts | 4056 +++++++++++++++ translations/veyon_bg.ts | 4083 +++++++++++++++ translations/veyon_ca_ES.ts | 4056 +++++++++++++++ translations/veyon_cs.ts | 4079 +++++++++++++++ translations/veyon_de.ts | 4080 +++++++++++++++ translations/veyon_el.ts | 4056 +++++++++++++++ translations/veyon_es_ES.ts | 4085 +++++++++++++++ translations/veyon_et.ts | 4083 +++++++++++++++ translations/veyon_fa.ts | 4057 +++++++++++++++ translations/veyon_fr.ts | 4083 +++++++++++++++ translations/veyon_he.ts | 4059 +++++++++++++++ translations/veyon_hu.ts | 4080 +++++++++++++++ translations/veyon_id.ts | 4061 +++++++++++++++ translations/veyon_it.ts | 4069 +++++++++++++++ translations/veyon_ja.ts | 4063 +++++++++++++++ translations/veyon_ko.ts | 4081 +++++++++++++++ translations/veyon_lt.ts | 4061 +++++++++++++++ translations/veyon_lv.ts | 4058 +++++++++++++++ translations/veyon_mn.ts | 4056 +++++++++++++++ translations/veyon_nl.ts | 4065 +++++++++++++++ translations/veyon_no_NO.ts | 4056 +++++++++++++++ translations/veyon_pl.ts | 4078 +++++++++++++++ translations/veyon_pt_BR.ts | 4066 +++++++++++++++ translations/veyon_pt_PT.ts | 4058 +++++++++++++++ translations/veyon_ru.ts | 4083 +++++++++++++++ translations/veyon_sl.ts | 4081 +++++++++++++++ translations/veyon_sr.ts | 4083 +++++++++++++++ translations/veyon_sv.ts | 4058 +++++++++++++++ translations/veyon_th.ts | 4060 +++++++++++++++ translations/veyon_tr.ts | 4075 +++++++++++++++ translations/veyon_uk.ts | 4082 +++++++++++++++ translations/veyon_vi.ts | 4059 +++++++++++++++ translations/veyon_zh_CN.ts | 4082 +++++++++++++++ translations/veyon_zh_TW.ts | 4081 +++++++++++++++ worker/CMakeLists.txt | 12 + worker/data/veyon-worker.exe.manifest.in | 14 + worker/src/FeatureWorkerManagerConnection.cpp | 103 + worker/src/FeatureWorkerManagerConnection.h | 61 + worker/src/VeyonWorker.cpp | 70 + worker/src/VeyonWorker.h | 50 + worker/src/main.cpp | 51 + worker/veyon-worker.1 | 45 + worker/veyon-worker.rc.in | 31 + 889 files changed, 240592 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .mailmap create mode 100644 .tx/config create mode 100644 3rdparty/kldap/.gitignore create mode 100644 3rdparty/kldap/.gitlab-ci.yml create mode 100644 3rdparty/kldap/CMakeLists.txt create mode 100644 3rdparty/kldap/KF5LdapConfig.cmake.in create mode 100644 3rdparty/kldap/LICENSES/BSD-3-Clause.txt create mode 100644 3rdparty/kldap/LICENSES/LGPL-2.0-or-later.txt create mode 100644 3rdparty/kldap/LICENSES/MIT.txt create mode 100644 3rdparty/kldap/README.md create mode 100644 3rdparty/kldap/cmake/FindLdap.cmake create mode 100644 3rdparty/kldap/metainfo.yaml create mode 100644 3rdparty/kldap/src/CMakeLists.txt create mode 100644 3rdparty/kldap/src/Messages.sh create mode 100644 3rdparty/kldap/src/core/ber.cpp create mode 100644 3rdparty/kldap/src/core/ber.h create mode 100644 3rdparty/kldap/src/core/ldapconnection.cpp create mode 100644 3rdparty/kldap/src/core/ldapconnection.h create mode 100644 3rdparty/kldap/src/core/ldapcontrol.cpp create mode 100644 3rdparty/kldap/src/core/ldapcontrol.h create mode 100644 3rdparty/kldap/src/core/ldapdefs.h create mode 100644 3rdparty/kldap/src/core/ldapdn.cpp create mode 100644 3rdparty/kldap/src/core/ldapdn.h create mode 100644 3rdparty/kldap/src/core/ldapobject.cpp create mode 100644 3rdparty/kldap/src/core/ldapobject.h create mode 100644 3rdparty/kldap/src/core/ldapoperation.cpp create mode 100644 3rdparty/kldap/src/core/ldapoperation.h create mode 100644 3rdparty/kldap/src/core/ldapsearch.cpp create mode 100644 3rdparty/kldap/src/core/ldapsearch.h create mode 100644 3rdparty/kldap/src/core/ldapserver.cpp create mode 100644 3rdparty/kldap/src/core/ldapserver.h create mode 100644 3rdparty/kldap/src/core/ldapurl.cpp create mode 100644 3rdparty/kldap/src/core/ldapurl.h create mode 100644 3rdparty/kldap/src/core/ldif.cpp create mode 100644 3rdparty/kldap/src/core/ldif.h create mode 100644 3rdparty/kldap/src/core/w32-ldap-help.h create mode 100644 3rdparty/kldap/src/kldap_config.h.cmake create mode 100644 3rdparty/kldap/src/widgets/addhostdialog.cpp create mode 100644 3rdparty/kldap/src/widgets/addhostdialog.h create mode 100644 3rdparty/kldap/src/widgets/ldapclient.cpp create mode 100644 3rdparty/kldap/src/widgets/ldapclient.h create mode 100644 3rdparty/kldap/src/widgets/ldapclientsearch.cpp create mode 100644 3rdparty/kldap/src/widgets/ldapclientsearch.h create mode 100644 3rdparty/kldap/src/widgets/ldapclientsearchconfig.cpp create mode 100644 3rdparty/kldap/src/widgets/ldapclientsearchconfig.h create mode 100644 3rdparty/kldap/src/widgets/ldapclientsearchconfigreadconfigjob.cpp create mode 100644 3rdparty/kldap/src/widgets/ldapclientsearchconfigreadconfigjob.h create mode 100644 3rdparty/kldap/src/widgets/ldapclientsearchconfigwriteconfigjob.cpp create mode 100644 3rdparty/kldap/src/widgets/ldapclientsearchconfigwriteconfigjob.h create mode 100644 3rdparty/kldap/src/widgets/ldapconfigurewidget.cpp create mode 100644 3rdparty/kldap/src/widgets/ldapconfigurewidget.h create mode 100644 3rdparty/kldap/src/widgets/ldapconfigwidget.cpp create mode 100644 3rdparty/kldap/src/widgets/ldapconfigwidget.h create mode 100644 3rdparty/kldap/src/widgets/ldapsearchclientreadconfigserverjob.cpp create mode 100644 3rdparty/kldap/src/widgets/ldapsearchclientreadconfigserverjob.h create mode 100644 3rdparty/kldap/src/widgets/ldapwidgetitem_p.cpp create mode 100644 3rdparty/kldap/src/widgets/ldapwidgetitem_p.h create mode 100644 3rdparty/kldap/src/widgets/ldapwidgetitemreadconfigserverjob.cpp create mode 100644 3rdparty/kldap/src/widgets/ldapwidgetitemreadconfigserverjob.h create mode 100644 3rdparty/kldap/tests/CMakeLists.txt create mode 100644 3rdparty/kldap/tests/testldapclient.cpp create mode 100644 3rdparty/kldap/tests/testldapclient.h create mode 100644 3rdparty/qthttpserver/.gitignore create mode 100644 3rdparty/qthttpserver/.gitmodules create mode 100644 3rdparty/qthttpserver/.qmake.conf create mode 100644 3rdparty/qthttpserver/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/LICENSE.GPL3 create mode 100644 3rdparty/qthttpserver/coin/product_dependencies.yaml create mode 100644 3rdparty/qthttpserver/dependencies.yaml create mode 100644 3rdparty/qthttpserver/examples/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/examples/examples.pro create mode 100644 3rdparty/qthttpserver/examples/httpserver/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/examples/httpserver/afterrequest/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/examples/httpserver/afterrequest/afterrequest.pro create mode 100644 3rdparty/qthttpserver/examples/httpserver/afterrequest/main.cpp create mode 100644 3rdparty/qthttpserver/examples/httpserver/httpserver.pro create mode 100644 3rdparty/qthttpserver/examples/httpserver/simple/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/examples/httpserver/simple/assets.qrc create mode 100644 3rdparty/qthttpserver/examples/httpserver/simple/assets/qt-logo.png create mode 100644 3rdparty/qthttpserver/examples/httpserver/simple/main.cpp create mode 100644 3rdparty/qthttpserver/examples/httpserver/simple/simple.pro create mode 100644 3rdparty/qthttpserver/qthttpserver.pro create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser.pri create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser/.gitignore create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser/.mailmap create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser/.travis.yml create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser/AUTHORS create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser/LICENSE-MIT create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser/Makefile create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser/README.md create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser/bench.c create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser/contrib/parsertrace.c create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser/contrib/url_parser.c create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser/http_parser.c create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser/http_parser.gyp create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser/http_parser.h create mode 100644 3rdparty/qthttpserver/src/3rdparty/http-parser/test.c create mode 100644 3rdparty/qthttpserver/src/3rdparty/qt_attribution.json create mode 100644 3rdparty/qthttpserver/src/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/src/httpserver/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/src/httpserver/httpserver.pro create mode 100644 3rdparty/qthttpserver/src/httpserver/qabstracthttpserver.cpp create mode 100644 3rdparty/qthttpserver/src/httpserver/qabstracthttpserver.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qabstracthttpserver_p.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserver.cpp create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserver.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserver_p.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverfutureresponse.cpp create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverfutureresponse.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverliterals.cpp create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverliterals_p.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverrequest.cpp create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverrequest.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverrequest_p.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverresponder.cpp create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverresponder.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverresponder_p.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverresponse.cpp create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverresponse.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverresponse_p.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverrouter.cpp create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverrouter.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverrouter_p.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverrouterrule.cpp create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverrouterrule.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverrouterrule_p.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverrouterviewtraits.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverviewtraits.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qhttpserverviewtraits_impl.h create mode 100644 3rdparty/qthttpserver/src/httpserver/qthttpserverglobal.h create mode 100644 3rdparty/qthttpserver/src/src.pro create mode 100644 3rdparty/qthttpserver/src/sslserver/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/src/sslserver/qsslserver.cpp create mode 100644 3rdparty/qthttpserver/src/sslserver/qsslserver.h create mode 100644 3rdparty/qthttpserver/src/sslserver/qsslserver_p.h create mode 100644 3rdparty/qthttpserver/src/sslserver/qtsslserverglobal.h create mode 100644 3rdparty/qthttpserver/src/sslserver/sslserver.pro create mode 100644 3rdparty/qthttpserver/sync.profile create mode 100644 3rdparty/qthttpserver/tests/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/tests/auto/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/tests/auto/auto.pro create mode 100644 3rdparty/qthttpserver/tests/auto/cmake/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/tests/auto/cmake/abstracthttpserver/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/tests/auto/cmake/abstracthttpserver/main.cpp create mode 100644 3rdparty/qthttpserver/tests/auto/cmake/cmake.pro create mode 100644 3rdparty/qthttpserver/tests/auto/qabstracthttpserver/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/tests/auto/qabstracthttpserver/qabstracthttpserver.pro create mode 100644 3rdparty/qthttpserver/tests/auto/qabstracthttpserver/tst_qabstracthttpserver.cpp create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserver/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserver/data/application.json create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserver/data/text.html create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserver/qhttpserver.pro create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserver/tst_qhttpserver.cpp create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverresponder/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverresponder/data/index.html create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverresponder/qhttpserverresponder.pro create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverresponder/tst_qhttpserverresponder.cpp create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverresponse/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/application.json create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/empty create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/image.jpeg create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/image.png create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/image.svg create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/text.html create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/text.plain create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverresponse/qhttpserverresponse.pro create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverresponse/tst_qhttpserverresponse.cpp create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverrouter/CMakeLists.txt create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverrouter/qhttpserverrouter.pro create mode 100644 3rdparty/qthttpserver/tests/auto/qhttpserverrouter/tst_qhttpserverrouter.cpp create mode 100644 3rdparty/qthttpserver/tests/tests.pro create mode 100644 CMakeLists.txt create mode 100644 CONTRIBUTORS create mode 100644 COPYING create mode 100644 INSTALL create mode 100644 README.md create mode 100644 cli/CMakeLists.txt create mode 100644 cli/data/veyon-cli.exe.manifest.in create mode 100644 cli/data/veyon-wcli.exe.manifest.in create mode 100644 cli/src/main.cpp create mode 100644 cli/veyon-cli.1 create mode 100644 cli/veyon-cli.rc.in create mode 100644 cli/veyon-wcli.rc.in create mode 100644 cmake/CPackDefinitions.cmake create mode 100644 cmake/modules/BuildVeyonApplication.cmake create mode 100644 cmake/modules/BuildVeyonPlugin.cmake create mode 100644 cmake/modules/COPYING-CMAKE-SCRIPTS create mode 100644 cmake/modules/ConfigureFiles.cmake create mode 100644 cmake/modules/CreateTranslations.cmake create mode 100644 cmake/modules/FindLZO.cmake create mode 100644 cmake/modules/FindLdap.cmake create mode 100644 cmake/modules/FindLibVNCClient.cmake create mode 100644 cmake/modules/FindLibVNCServer.cmake create mode 100644 cmake/modules/FindPAM.cmake create mode 100644 cmake/modules/FindQCA.cmake create mode 100644 cmake/modules/FindQtTranslations.cmake create mode 100644 cmake/modules/FindSasl2.cmake create mode 100644 cmake/modules/LibVNCServerIntegration.cmake create mode 100644 cmake/modules/SetDefaultTargetProperties.cmake create mode 100644 cmake/modules/WindowsBuildHelpers.cmake create mode 100644 cmake/modules/WindowsInstaller.cmake create mode 100644 cmake/modules/XdgInstall.cmake create mode 100644 configurator/CMakeLists.txt create mode 100644 configurator/data/io.veyon.veyon-configurator.policy.in create mode 100644 configurator/data/veyon-configurator.desktop.in create mode 100644 configurator/data/veyon-configurator.exe.manifest.in create mode 100644 configurator/data/veyon-configurator.png create mode 100644 configurator/data/veyon-configurator.svg create mode 100644 configurator/data/veyon-configurator.xpm create mode 100644 configurator/resources/access-rule-ask.png create mode 100644 configurator/resources/application-x-ms-dos-executable.png create mode 100644 configurator/resources/application-x-sharedlib.png create mode 100644 configurator/resources/configurator.qrc create mode 100644 configurator/resources/configure-shortcuts.png create mode 100644 configurator/resources/help-about.png create mode 100644 configurator/resources/media-playback-start.png create mode 100644 configurator/resources/media-playback-stop.png create mode 100644 configurator/resources/network-vpn.png create mode 100644 configurator/resources/vcs-conflicting.png create mode 100644 configurator/resources/vcs-normal.png create mode 100644 configurator/resources/vcs-removed.png create mode 100644 configurator/resources/veyon-configurator.png create mode 100644 configurator/src/AccessControlPage.cpp create mode 100644 configurator/src/AccessControlPage.h create mode 100644 configurator/src/AccessControlPage.ui create mode 100644 configurator/src/AccessControlRuleEditDialog.cpp create mode 100644 configurator/src/AccessControlRuleEditDialog.h create mode 100644 configurator/src/AccessControlRuleEditDialog.ui create mode 100644 configurator/src/AccessControlRuleListModel.cpp create mode 100644 configurator/src/AccessControlRuleListModel.h create mode 100644 configurator/src/AccessControlRulesTestDialog.cpp create mode 100644 configurator/src/AccessControlRulesTestDialog.h create mode 100644 configurator/src/AccessControlRulesTestDialog.ui create mode 100644 configurator/src/GeneralConfigurationPage.cpp create mode 100644 configurator/src/GeneralConfigurationPage.h create mode 100644 configurator/src/GeneralConfigurationPage.ui create mode 100644 configurator/src/MainWindow.cpp create mode 100644 configurator/src/MainWindow.h create mode 100644 configurator/src/MainWindow.ui create mode 100644 configurator/src/MasterConfigurationPage.cpp create mode 100644 configurator/src/MasterConfigurationPage.h create mode 100644 configurator/src/MasterConfigurationPage.ui create mode 100644 configurator/src/ServiceConfigurationPage.cpp create mode 100644 configurator/src/ServiceConfigurationPage.h create mode 100644 configurator/src/ServiceConfigurationPage.ui create mode 100644 configurator/src/main.cpp create mode 100644 configurator/veyon-configurator.1 create mode 100644 configurator/veyon-configurator.rc.in create mode 100644 core/CMakeLists.txt create mode 100644 core/builddata.qrc.in create mode 100644 core/include/AboutDialog.h create mode 100644 core/include/AccessControlProvider.h create mode 100644 core/include/AccessControlRule.h create mode 100644 core/include/AuthenticationCredentials.h create mode 100644 core/include/AuthenticationProxy.h create mode 100644 core/include/BuiltinFeatures.h create mode 100644 core/include/CommandLineIO.h create mode 100644 core/include/CommandLinePluginInterface.h create mode 100644 core/include/Computer.h create mode 100644 core/include/ComputerControlInterface.h create mode 100644 core/include/ComputerListModel.h create mode 100644 core/include/Configuration/JsonStore.h create mode 100644 core/include/Configuration/LocalStore.h create mode 100644 core/include/Configuration/Object.h create mode 100644 core/include/Configuration/Password.h create mode 100644 core/include/Configuration/Property.h create mode 100644 core/include/Configuration/Proxy.h create mode 100644 core/include/Configuration/Store.h create mode 100644 core/include/Configuration/UiMapping.h create mode 100644 core/include/ConfigurationManager.h create mode 100644 core/include/ConfigurationPage.h create mode 100644 core/include/ConfigurationPagePluginInterface.h create mode 100644 core/include/CryptoCore.h create mode 100644 core/include/DesktopAccessDialog.h create mode 100644 core/include/EnumHelper.h create mode 100644 core/include/Feature.h create mode 100644 core/include/FeatureControl.h create mode 100644 core/include/FeatureManager.h create mode 100644 core/include/FeatureMessage.h create mode 100644 core/include/FeatureProviderInterface.h create mode 100644 core/include/FeatureWorkerManager.h create mode 100644 core/include/FileSystemBrowser.h create mode 100644 core/include/Filesystem.h create mode 100644 core/include/HashList.h create mode 100644 core/include/HostAddress.h create mode 100644 core/include/KeyboardShortcutTrapper.h create mode 100644 core/include/LockWidget.h create mode 100644 core/include/Lockable.h create mode 100644 core/include/LockingPointer.h create mode 100644 core/include/Logger.h create mode 100644 core/include/MessageContext.h create mode 100644 core/include/MonitoringMode.h create mode 100644 core/include/NetworkObject.h create mode 100644 core/include/NetworkObjectDirectory.h create mode 100644 core/include/NetworkObjectDirectoryManager.h create mode 100644 core/include/NetworkObjectDirectoryPluginInterface.h create mode 100644 core/include/NetworkObjectModel.h create mode 100644 core/include/ObjectManager.h create mode 100644 core/include/PasswordDialog.h create mode 100644 core/include/PlatformCoreFunctions.h create mode 100644 core/include/PlatformFilesystemFunctions.h create mode 100644 core/include/PlatformInputDeviceFunctions.h create mode 100644 core/include/PlatformNetworkFunctions.h create mode 100644 core/include/PlatformPluginInterface.h create mode 100644 core/include/PlatformPluginManager.h create mode 100644 core/include/PlatformServiceFunctions.h create mode 100644 core/include/PlatformSessionFunctions.h create mode 100644 core/include/PlatformUserFunctions.h create mode 100644 core/include/Plugin.h create mode 100644 core/include/PluginInterface.h create mode 100644 core/include/PluginManager.h create mode 100644 core/include/ProcessHelper.h create mode 100644 core/include/ProgressWidget.h create mode 100644 core/include/QtCompat.h create mode 100644 core/include/RfbVeyonAuth.h create mode 100644 core/include/Screenshot.h create mode 100644 core/include/ServiceControl.h create mode 100644 core/include/SocketDevice.h create mode 100644 core/include/SystemTrayIcon.h create mode 100644 core/include/ToolButton.h create mode 100644 core/include/TranslationLoader.h create mode 100644 core/include/UserGroupsBackendInterface.h create mode 100644 core/include/UserGroupsBackendManager.h create mode 100644 core/include/VariantArrayMessage.h create mode 100644 core/include/VariantStream.h create mode 100644 core/include/VeyonConfiguration.h create mode 100644 core/include/VeyonConfigurationProperties.h create mode 100644 core/include/VeyonConnection.h create mode 100644 core/include/VeyonCore.h create mode 100644 core/include/VeyonMasterInterface.h create mode 100644 core/include/VeyonServerInterface.h create mode 100644 core/include/VeyonServiceControl.h create mode 100644 core/include/VeyonWorkerInterface.h create mode 100644 core/include/VncClientProtocol.h create mode 100644 core/include/VncConnection.h create mode 100644 core/include/VncEvents.h create mode 100644 core/include/VncFeatureMessageEvent.h create mode 100644 core/include/VncServerClient.h create mode 100644 core/include/VncServerPluginInterface.h create mode 100644 core/include/VncServerProtocol.h create mode 100644 core/include/VncView.h create mode 100644 core/include/VncViewWidget.h create mode 100644 core/include/veyonconfig.h.in create mode 100644 core/resources/application-x-pem-key.png create mode 100644 core/resources/core.qrc create mode 100644 core/resources/default-pkey.pem create mode 100644 core/resources/dialog-ok-apply.png create mode 100644 core/resources/document-edit.png create mode 100644 core/resources/document-open.png create mode 100644 core/resources/document-save.png create mode 100644 core/resources/edit-delete.png create mode 100644 core/resources/edit-find.png create mode 100644 core/resources/go-down.png create mode 100644 core/resources/go-next.png create mode 100644 core/resources/go-previous.png create mode 100644 core/resources/go-up.png create mode 100644 core/resources/help-about.png create mode 100644 core/resources/icon128.png create mode 100644 core/resources/icon16.png create mode 100644 core/resources/icon22.png create mode 100644 core/resources/icon32.png create mode 100644 core/resources/icon64.png create mode 100644 core/resources/languages.png create mode 100644 core/resources/license.png create mode 100644 core/resources/list-add.png create mode 100644 core/resources/presentation-none.png create mode 100644 core/resources/toolbar-background.png create mode 100644 core/resources/user-group-new.png create mode 100644 core/resources/watch1.png create mode 100644 core/resources/watch10.png create mode 100644 core/resources/watch11.png create mode 100644 core/resources/watch12.png create mode 100644 core/resources/watch13.png create mode 100644 core/resources/watch14.png create mode 100644 core/resources/watch15.png create mode 100644 core/resources/watch16.png create mode 100644 core/resources/watch2.png create mode 100644 core/resources/watch3.png create mode 100644 core/resources/watch4.png create mode 100644 core/resources/watch5.png create mode 100644 core/resources/watch6.png create mode 100644 core/resources/watch7.png create mode 100644 core/resources/watch8.png create mode 100644 core/resources/watch9.png create mode 100644 core/src/AboutDialog.cpp create mode 100644 core/src/AboutDialog.ui create mode 100644 core/src/AccessControlProvider.cpp create mode 100644 core/src/AccessControlRule.cpp create mode 100644 core/src/AuthenticationCredentials.cpp create mode 100644 core/src/BuiltinFeatures.cpp create mode 100644 core/src/CommandLineIO.cpp create mode 100644 core/src/Computer.cpp create mode 100644 core/src/ComputerControlInterface.cpp create mode 100644 core/src/ComputerListModel.cpp create mode 100644 core/src/Configuration/JsonStore.cpp create mode 100644 core/src/Configuration/LocalStore.cpp create mode 100644 core/src/Configuration/Object.cpp create mode 100644 core/src/Configuration/Password.cpp create mode 100644 core/src/Configuration/Property.cpp create mode 100644 core/src/Configuration/Proxy.cpp create mode 100644 core/src/Configuration/UiMapping.cpp create mode 100644 core/src/ConfigurationManager.cpp create mode 100644 core/src/ConfigurationPage.cpp create mode 100644 core/src/CryptoCore.cpp create mode 100644 core/src/DesktopAccessDialog.cpp create mode 100644 core/src/FeatureControl.cpp create mode 100644 core/src/FeatureManager.cpp create mode 100644 core/src/FeatureMessage.cpp create mode 100644 core/src/FeatureWorkerManager.cpp create mode 100644 core/src/FileSystemBrowser.cpp create mode 100644 core/src/Filesystem.cpp create mode 100644 core/src/HostAddress.cpp create mode 100644 core/src/LockWidget.cpp create mode 100644 core/src/Logger.cpp create mode 100644 core/src/MonitoringMode.cpp create mode 100644 core/src/NetworkObject.cpp create mode 100644 core/src/NetworkObjectDirectory.cpp create mode 100644 core/src/NetworkObjectDirectoryManager.cpp create mode 100644 core/src/PasswordDialog.cpp create mode 100644 core/src/PasswordDialog.ui create mode 100644 core/src/PlatformPluginManager.cpp create mode 100644 core/src/PluginManager.cpp create mode 100644 core/src/PrecompiledHeader.h create mode 100644 core/src/ProcessHelper.cpp create mode 100644 core/src/ProgressWidget.cpp create mode 100644 core/src/QtCompat.cpp create mode 100644 core/src/Screenshot.cpp create mode 100644 core/src/ServiceControl.cpp create mode 100644 core/src/SystemTrayIcon.cpp create mode 100644 core/src/ToolButton.cpp create mode 100644 core/src/TranslationLoader.cpp create mode 100644 core/src/UserGroupsBackendManager.cpp create mode 100644 core/src/VariantArrayMessage.cpp create mode 100644 core/src/VariantStream.cpp create mode 100644 core/src/VeyonConfiguration.cpp create mode 100644 core/src/VeyonConnection.cpp create mode 100644 core/src/VeyonCore.cpp create mode 100644 core/src/VeyonServiceControl.cpp create mode 100644 core/src/VncClientProtocol.cpp create mode 100644 core/src/VncConnection.cpp create mode 100644 core/src/VncEvents.cpp create mode 100644 core/src/VncFeatureMessageEvent.cpp create mode 100644 core/src/VncServerProtocol.cpp create mode 100644 core/src/VncView.cpp create mode 100644 core/src/VncViewWidget.cpp create mode 100644 core/src/d3des.c create mode 100644 core/src/d3des.h create mode 100644 master/CMakeLists.txt create mode 100644 master/data/veyon-master.desktop.in create mode 100644 master/data/veyon-master.exe.manifest.in create mode 100644 master/data/veyon-master.png create mode 100644 master/data/veyon-master.svg create mode 100644 master/data/veyon-master.xpm create mode 100644 master/resources/align-grid.png create mode 100644 master/resources/camera-photo.png create mode 100644 master/resources/computer-slideshow.png create mode 100644 master/resources/computers.png create mode 100644 master/resources/example-screenshot.png create mode 100644 master/resources/examples.qrc create mode 100644 master/resources/exchange-positions-zorder.png create mode 100644 master/resources/master.qrc create mode 100644 master/resources/media-playback-pause.png create mode 100644 master/resources/media-playback-start.png create mode 100644 master/resources/powered-on.png create mode 100644 master/resources/preferences-desktop-display-blue.png create mode 100644 master/resources/preferences-desktop-display-gray.png create mode 100644 master/resources/preferences-desktop-display-orange.png create mode 100644 master/resources/preferences-desktop-display-red.png create mode 100644 master/resources/preferences-desktop-display.png create mode 100644 master/resources/splash.png create mode 100644 master/resources/spotlight.png create mode 100644 master/resources/update-realtime-disabled.png create mode 100644 master/resources/update-realtime-enabled.png create mode 100644 master/resources/zoom-fit-best.png create mode 100644 master/src/CheckableItemProxyModel.cpp create mode 100644 master/src/CheckableItemProxyModel.h create mode 100644 master/src/ComputerControlListModel.cpp create mode 100644 master/src/ComputerControlListModel.h create mode 100644 master/src/ComputerManager.cpp create mode 100644 master/src/ComputerManager.h create mode 100644 master/src/ComputerMonitoringModel.cpp create mode 100644 master/src/ComputerMonitoringModel.h create mode 100644 master/src/ComputerMonitoringView.cpp create mode 100644 master/src/ComputerMonitoringView.h create mode 100644 master/src/ComputerMonitoringWidget.cpp create mode 100644 master/src/ComputerMonitoringWidget.h create mode 100644 master/src/ComputerSelectPanel.cpp create mode 100644 master/src/ComputerSelectPanel.h create mode 100644 master/src/ComputerSelectPanel.ui create mode 100644 master/src/DocumentationFigureCreator.cpp create mode 100644 master/src/DocumentationFigureCreator.h create mode 100644 master/src/FlexibleListView.cpp create mode 100644 master/src/FlexibleListView.h create mode 100644 master/src/KItemModelsIntegration.cpp create mode 100644 master/src/LocationDialog.cpp create mode 100644 master/src/LocationDialog.h create mode 100644 master/src/LocationDialog.ui create mode 100644 master/src/MainToolBar.cpp create mode 100644 master/src/MainToolBar.h create mode 100644 master/src/MainWindow.cpp create mode 100644 master/src/MainWindow.h create mode 100644 master/src/MainWindow.ui create mode 100644 master/src/NetworkObjectFilterProxyModel.cpp create mode 100644 master/src/NetworkObjectFilterProxyModel.h create mode 100644 master/src/NetworkObjectOverlayDataModel.cpp create mode 100644 master/src/NetworkObjectOverlayDataModel.h create mode 100644 master/src/NetworkObjectTreeModel.cpp create mode 100644 master/src/NetworkObjectTreeModel.h create mode 100644 master/src/RecursiveFilterProxyModel.cpp create mode 100644 master/src/RecursiveFilterProxyModel.h create mode 100644 master/src/ScreenshotManagementPanel.cpp create mode 100644 master/src/ScreenshotManagementPanel.h create mode 100644 master/src/ScreenshotManagementPanel.ui create mode 100644 master/src/SlideshowModel.cpp create mode 100644 master/src/SlideshowModel.h create mode 100644 master/src/SlideshowPanel.cpp create mode 100644 master/src/SlideshowPanel.h create mode 100644 master/src/SlideshowPanel.ui create mode 100644 master/src/SpotlightModel.cpp create mode 100644 master/src/SpotlightModel.h create mode 100644 master/src/SpotlightPanel.cpp create mode 100644 master/src/SpotlightPanel.h create mode 100644 master/src/SpotlightPanel.ui create mode 100644 master/src/UserConfig.cpp create mode 100644 master/src/UserConfig.h create mode 100644 master/src/VeyonMaster.cpp create mode 100644 master/src/VeyonMaster.h create mode 100644 master/src/kitemmodels_debug.h create mode 100644 master/src/kitemmodels_export.h create mode 100644 master/src/main.cpp create mode 100644 master/veyon-master.1 create mode 100644 master/veyon-master.rc.in create mode 100644 plugins/CMakeLists.txt create mode 100644 plugins/authkeys/AuthKeysConfigurationPage.cpp create mode 100644 plugins/authkeys/AuthKeysConfigurationPage.h create mode 100644 plugins/authkeys/AuthKeysConfigurationPage.ui create mode 100644 plugins/authkeys/AuthKeysManager.cpp create mode 100644 plugins/authkeys/AuthKeysManager.h create mode 100644 plugins/authkeys/AuthKeysPlugin.cpp create mode 100644 plugins/authkeys/AuthKeysPlugin.h create mode 100644 plugins/authkeys/AuthKeysTableModel.cpp create mode 100644 plugins/authkeys/AuthKeysTableModel.h create mode 100644 plugins/authkeys/CMakeLists.txt create mode 100644 plugins/builtindirectory/BuiltinDirectory.cpp create mode 100644 plugins/builtindirectory/BuiltinDirectory.h create mode 100644 plugins/builtindirectory/BuiltinDirectoryConfiguration.h create mode 100644 plugins/builtindirectory/BuiltinDirectoryConfigurationPage.cpp create mode 100644 plugins/builtindirectory/BuiltinDirectoryConfigurationPage.h create mode 100644 plugins/builtindirectory/BuiltinDirectoryConfigurationPage.ui create mode 100644 plugins/builtindirectory/BuiltinDirectoryPlugin.cpp create mode 100644 plugins/builtindirectory/BuiltinDirectoryPlugin.h create mode 100644 plugins/builtindirectory/CMakeLists.txt create mode 100644 plugins/builtindirectory/builtindirectory.png create mode 100644 plugins/builtindirectory/builtindirectory.qrc create mode 100644 plugins/config/CMakeLists.txt create mode 100644 plugins/config/ConfigCommandLinePlugin.cpp create mode 100644 plugins/config/ConfigCommandLinePlugin.h create mode 100644 plugins/demo/CMakeLists.txt create mode 100644 plugins/demo/DemoClient.cpp create mode 100644 plugins/demo/DemoClient.h create mode 100644 plugins/demo/DemoConfiguration.h create mode 100644 plugins/demo/DemoConfigurationPage.cpp create mode 100644 plugins/demo/DemoConfigurationPage.h create mode 100644 plugins/demo/DemoConfigurationPage.ui create mode 100644 plugins/demo/DemoFeaturePlugin.cpp create mode 100644 plugins/demo/DemoFeaturePlugin.h create mode 100644 plugins/demo/DemoServer.cpp create mode 100644 plugins/demo/DemoServer.h create mode 100644 plugins/demo/DemoServerConnection.cpp create mode 100644 plugins/demo/DemoServerConnection.h create mode 100644 plugins/demo/DemoServerProtocol.cpp create mode 100644 plugins/demo/DemoServerProtocol.h create mode 100644 plugins/demo/demo.png create mode 100644 plugins/demo/demo.qrc create mode 100644 plugins/demo/presentation-fullscreen.png create mode 100644 plugins/demo/presentation-window.png create mode 100644 plugins/demo/window-duplicate.png create mode 100644 plugins/desktopservices/CMakeLists.txt create mode 100644 plugins/desktopservices/DesktopServiceObject.cpp create mode 100644 plugins/desktopservices/DesktopServiceObject.h create mode 100644 plugins/desktopservices/DesktopServicesConfiguration.h create mode 100644 plugins/desktopservices/DesktopServicesConfigurationPage.cpp create mode 100644 plugins/desktopservices/DesktopServicesConfigurationPage.h create mode 100644 plugins/desktopservices/DesktopServicesConfigurationPage.ui create mode 100644 plugins/desktopservices/DesktopServicesFeaturePlugin.cpp create mode 100644 plugins/desktopservices/DesktopServicesFeaturePlugin.h create mode 100644 plugins/desktopservices/OpenWebsiteDialog.cpp create mode 100644 plugins/desktopservices/OpenWebsiteDialog.h create mode 100644 plugins/desktopservices/OpenWebsiteDialog.ui create mode 100644 plugins/desktopservices/RunProgramDialog.cpp create mode 100644 plugins/desktopservices/RunProgramDialog.h create mode 100644 plugins/desktopservices/RunProgramDialog.ui create mode 100644 plugins/desktopservices/desktop-services.png create mode 100644 plugins/desktopservices/desktopservices.qrc create mode 100644 plugins/desktopservices/internet-web-browser.png create mode 100644 plugins/desktopservices/preferences-desktop-launch-feedback.png create mode 100644 plugins/filetransfer/CMakeLists.txt create mode 100644 plugins/filetransfer/FileReadThread.cpp create mode 100644 plugins/filetransfer/FileReadThread.h create mode 100644 plugins/filetransfer/FileTransferConfiguration.h create mode 100644 plugins/filetransfer/FileTransferConfigurationPage.cpp create mode 100644 plugins/filetransfer/FileTransferConfigurationPage.h create mode 100644 plugins/filetransfer/FileTransferConfigurationPage.ui create mode 100644 plugins/filetransfer/FileTransferController.cpp create mode 100644 plugins/filetransfer/FileTransferController.h create mode 100644 plugins/filetransfer/FileTransferDialog.cpp create mode 100644 plugins/filetransfer/FileTransferDialog.h create mode 100644 plugins/filetransfer/FileTransferDialog.ui create mode 100644 plugins/filetransfer/FileTransferListModel.cpp create mode 100644 plugins/filetransfer/FileTransferListModel.h create mode 100644 plugins/filetransfer/FileTransferPlugin.cpp create mode 100644 plugins/filetransfer/FileTransferPlugin.h create mode 100644 plugins/filetransfer/FileTransferUserConfiguration.h create mode 100644 plugins/filetransfer/applications-office.png create mode 100644 plugins/filetransfer/document-share.png create mode 100644 plugins/filetransfer/file-finished.png create mode 100644 plugins/filetransfer/file-scheduled.png create mode 100644 plugins/filetransfer/file-transferring.png create mode 100644 plugins/filetransfer/filetransfer.qrc create mode 100644 plugins/ldap/CMakeLists.txt create mode 100644 plugins/ldap/LdapPlugin.cpp create mode 100644 plugins/ldap/LdapPlugin.h create mode 100644 plugins/ldap/common/CMakeLists.txt create mode 100644 plugins/ldap/common/LdapBrowseDialog.cpp create mode 100644 plugins/ldap/common/LdapBrowseDialog.h create mode 100644 plugins/ldap/common/LdapBrowseDialog.ui create mode 100644 plugins/ldap/common/LdapBrowseModel.cpp create mode 100644 plugins/ldap/common/LdapBrowseModel.h create mode 100644 plugins/ldap/common/LdapClient.cpp create mode 100644 plugins/ldap/common/LdapClient.h create mode 100644 plugins/ldap/common/LdapCommon.h create mode 100644 plugins/ldap/common/LdapConfiguration.cpp create mode 100644 plugins/ldap/common/LdapConfiguration.h create mode 100644 plugins/ldap/common/LdapConfigurationPage.cpp create mode 100644 plugins/ldap/common/LdapConfigurationPage.h create mode 100644 plugins/ldap/common/LdapConfigurationPage.ui create mode 100644 plugins/ldap/common/LdapDirectory.cpp create mode 100644 plugins/ldap/common/LdapDirectory.h create mode 100644 plugins/ldap/common/LdapNetworkObjectDirectory.cpp create mode 100644 plugins/ldap/common/LdapNetworkObjectDirectory.h create mode 100644 plugins/ldap/common/application-x-kexi-connectiondata.png create mode 100644 plugins/ldap/common/attribute.png create mode 100644 plugins/ldap/common/computer.png create mode 100644 plugins/ldap/common/configure.png create mode 100644 plugins/ldap/common/distribute-vertical-margin.png create mode 100644 plugins/ldap/common/folder-stash.png create mode 100644 plugins/ldap/common/ldap.qrc create mode 100644 plugins/ldap/common/user-group-properties.png create mode 100644 plugins/ldap/kldap/CMakeLists.txt create mode 100644 plugins/ldap/kldap/KLdapIntegration.cpp create mode 100644 plugins/ldap/kldap/KLocalizedString create mode 100644 plugins/ldap/kldap/kldap_export.h create mode 100644 plugins/ldap/kldap/klocalizedstring.h create mode 100644 plugins/ldap/kldap/ldap_debug.h create mode 100644 plugins/platform/CMakeLists.txt create mode 100644 plugins/platform/common/LogonHelper.cpp create mode 100644 plugins/platform/common/LogonHelper.h create mode 100644 plugins/platform/common/PersistentLogonCredentials.cpp create mode 100644 plugins/platform/common/PersistentLogonCredentials.h create mode 100644 plugins/platform/common/PlatformSessionManager.cpp create mode 100644 plugins/platform/common/PlatformSessionManager.h create mode 100644 plugins/platform/common/ServiceDataManager.cpp create mode 100644 plugins/platform/common/ServiceDataManager.h create mode 100644 plugins/platform/linux/CMakeLists.txt create mode 100644 plugins/platform/linux/LinuxCoreFunctions.cpp create mode 100644 plugins/platform/linux/LinuxCoreFunctions.h create mode 100644 plugins/platform/linux/LinuxDesktopIntegration.h create mode 100644 plugins/platform/linux/LinuxFilesystemFunctions.cpp create mode 100644 plugins/platform/linux/LinuxFilesystemFunctions.h create mode 100644 plugins/platform/linux/LinuxInputDeviceFunctions.cpp create mode 100644 plugins/platform/linux/LinuxInputDeviceFunctions.h create mode 100644 plugins/platform/linux/LinuxKeyboardInput.cpp create mode 100644 plugins/platform/linux/LinuxKeyboardInput.h create mode 100644 plugins/platform/linux/LinuxKeyboardShortcutTrapper.h create mode 100644 plugins/platform/linux/LinuxNetworkFunctions.cpp create mode 100644 plugins/platform/linux/LinuxNetworkFunctions.h create mode 100644 plugins/platform/linux/LinuxPlatformConfiguration.h create mode 100644 plugins/platform/linux/LinuxPlatformConfigurationPage.cpp create mode 100644 plugins/platform/linux/LinuxPlatformConfigurationPage.h create mode 100644 plugins/platform/linux/LinuxPlatformConfigurationPage.ui create mode 100644 plugins/platform/linux/LinuxPlatformPlugin.cpp create mode 100644 plugins/platform/linux/LinuxPlatformPlugin.h create mode 100644 plugins/platform/linux/LinuxServiceCore.cpp create mode 100644 plugins/platform/linux/LinuxServiceCore.h create mode 100644 plugins/platform/linux/LinuxServiceFunctions.cpp create mode 100644 plugins/platform/linux/LinuxServiceFunctions.h create mode 100644 plugins/platform/linux/LinuxSessionFunctions.cpp create mode 100644 plugins/platform/linux/LinuxSessionFunctions.h create mode 100644 plugins/platform/linux/LinuxUserFunctions.cpp create mode 100644 plugins/platform/linux/LinuxUserFunctions.h create mode 100644 plugins/platform/linux/auth-helper/CMakeLists.txt create mode 100644 plugins/platform/linux/auth-helper/VeyonAuthHelper.cpp create mode 100644 plugins/platform/linux/linux.qrc create mode 100644 plugins/platform/linux/tux.png create mode 100644 plugins/powercontrol/CMakeLists.txt create mode 100644 plugins/powercontrol/PowerControlFeaturePlugin.cpp create mode 100644 plugins/powercontrol/PowerControlFeaturePlugin.h create mode 100644 plugins/powercontrol/PowerDownTimeInputDialog.cpp create mode 100644 plugins/powercontrol/PowerDownTimeInputDialog.h create mode 100644 plugins/powercontrol/PowerDownTimeInputDialog.ui create mode 100644 plugins/powercontrol/powercontrol.qrc create mode 100644 plugins/powercontrol/preferences-system-power-management.png create mode 100644 plugins/powercontrol/system-reboot.png create mode 100644 plugins/powercontrol/system-shutdown.png create mode 100644 plugins/remoteaccess/CMakeLists.txt create mode 100644 plugins/remoteaccess/RemoteAccessFeaturePlugin.cpp create mode 100644 plugins/remoteaccess/RemoteAccessFeaturePlugin.h create mode 100644 plugins/remoteaccess/RemoteAccessWidget.cpp create mode 100644 plugins/remoteaccess/RemoteAccessWidget.h create mode 100644 plugins/remoteaccess/application-exit.png create mode 100644 plugins/remoteaccess/camera-photo.png create mode 100644 plugins/remoteaccess/kmag.png create mode 100644 plugins/remoteaccess/krdc.png create mode 100644 plugins/remoteaccess/preferences-desktop-keyboard.png create mode 100644 plugins/remoteaccess/remoteaccess.qrc create mode 100644 plugins/remoteaccess/view-fullscreen.png create mode 100644 plugins/screenlock/CMakeLists.txt create mode 100644 plugins/screenlock/ScreenLockFeaturePlugin.cpp create mode 100644 plugins/screenlock/ScreenLockFeaturePlugin.h create mode 100644 plugins/screenlock/locked-screen-background.png create mode 100644 plugins/screenlock/screenlock.qrc create mode 100644 plugins/screenlock/system-lock-screen.png create mode 100644 plugins/screenshot/CMakeLists.txt create mode 100644 plugins/screenshot/ScreenshotFeaturePlugin.cpp create mode 100644 plugins/screenshot/ScreenshotFeaturePlugin.h create mode 100644 plugins/screenshot/camera-photo.png create mode 100644 plugins/screenshot/screenshot.qrc create mode 100644 plugins/servicecontrol/CMakeLists.txt create mode 100644 plugins/servicecontrol/ServiceControlPlugin.cpp create mode 100644 plugins/servicecontrol/ServiceControlPlugin.h create mode 100644 plugins/shell/CMakeLists.txt create mode 100644 plugins/shell/ShellCommandLinePlugin.cpp create mode 100644 plugins/shell/ShellCommandLinePlugin.h create mode 100644 plugins/systemusergroups/CMakeLists.txt create mode 100644 plugins/systemusergroups/SystemUserGroupsPlugin.cpp create mode 100644 plugins/systemusergroups/SystemUserGroupsPlugin.h create mode 100644 plugins/testing/CMakeLists.txt create mode 100644 plugins/testing/TestingCommandLinePlugin.cpp create mode 100644 plugins/testing/TestingCommandLinePlugin.h create mode 100644 plugins/textmessage/CMakeLists.txt create mode 100644 plugins/textmessage/TextMessageDialog.cpp create mode 100644 plugins/textmessage/TextMessageDialog.h create mode 100644 plugins/textmessage/TextMessageDialog.ui create mode 100644 plugins/textmessage/TextMessageFeaturePlugin.cpp create mode 100644 plugins/textmessage/TextMessageFeaturePlugin.h create mode 100644 plugins/textmessage/dialog-information.png create mode 100644 plugins/textmessage/textmessage.qrc create mode 100644 plugins/usersessioncontrol/CMakeLists.txt create mode 100644 plugins/usersessioncontrol/UserLoginDialog.cpp create mode 100644 plugins/usersessioncontrol/UserLoginDialog.h create mode 100644 plugins/usersessioncontrol/UserLoginDialog.ui create mode 100644 plugins/usersessioncontrol/UserSessionControlPlugin.cpp create mode 100644 plugins/usersessioncontrol/UserSessionControlPlugin.h create mode 100644 plugins/usersessioncontrol/login-user.png create mode 100644 plugins/usersessioncontrol/logout-user.png create mode 100644 plugins/usersessioncontrol/usersessioncontrol.qrc create mode 100644 plugins/vncserver/CMakeLists.txt create mode 100644 plugins/vncserver/external/CMakeLists.txt create mode 100644 plugins/vncserver/external/ExternalVncServer.cpp create mode 100644 plugins/vncserver/external/ExternalVncServer.h create mode 100644 plugins/vncserver/external/ExternalVncServerConfiguration.h create mode 100644 plugins/vncserver/external/ExternalVncServerConfigurationWidget.cpp create mode 100644 plugins/vncserver/external/ExternalVncServerConfigurationWidget.h create mode 100644 plugins/vncserver/external/ExternalVncServerConfigurationWidget.ui create mode 100644 plugins/vncserver/headless/CMakeLists.txt create mode 100644 plugins/vncserver/headless/HeadlessVncConfiguration.h create mode 100644 plugins/vncserver/headless/HeadlessVncServer.cpp create mode 100644 plugins/vncserver/headless/HeadlessVncServer.h create mode 100644 plugins/vncserver/ultravnc-builtin/BuiltinUltraVncServer.cpp create mode 100644 plugins/vncserver/ultravnc-builtin/BuiltinUltraVncServer.h create mode 100644 plugins/vncserver/ultravnc-builtin/CMakeLists.txt create mode 100644 plugins/vncserver/ultravnc-builtin/LogoffEventFilter.cpp create mode 100644 plugins/vncserver/ultravnc-builtin/LogoffEventFilter.h create mode 100644 plugins/vncserver/ultravnc-builtin/UltraVncConfiguration.h create mode 100644 plugins/vncserver/ultravnc-builtin/UltraVncConfigurationWidget.cpp create mode 100644 plugins/vncserver/ultravnc-builtin/UltraVncConfigurationWidget.h create mode 100644 plugins/vncserver/ultravnc-builtin/UltraVncConfigurationWidget.ui create mode 100644 plugins/vncserver/ultravnc-builtin/ultravnc.cpp create mode 100644 plugins/vncserver/ultravnc-builtin/vnchooks/CMakeLists.txt create mode 100644 plugins/vncserver/ultravnc-builtin/vncntlm.cpp create mode 100644 plugins/vncserver/x11vnc-builtin/BuiltinX11VncServer.cpp create mode 100644 plugins/vncserver/x11vnc-builtin/BuiltinX11VncServer.h create mode 100644 plugins/vncserver/x11vnc-builtin/CMakeLists.txt create mode 100644 plugins/vncserver/x11vnc-builtin/X11VncConfiguration.h create mode 100644 plugins/vncserver/x11vnc-builtin/X11VncConfigurationWidget.cpp create mode 100644 plugins/vncserver/x11vnc-builtin/X11VncConfigurationWidget.h create mode 100644 plugins/vncserver/x11vnc-builtin/X11VncConfigurationWidget.ui create mode 100644 plugins/vncserver/x11vnc-builtin/config.h.in create mode 100644 plugins/vncserver/x11vnc-builtin/x11vnc-veyon.c create mode 100644 plugins/webapi/CMakeLists.txt create mode 100644 plugins/webapi/WebApiAuthenticationProxy.cpp create mode 100644 plugins/webapi/WebApiAuthenticationProxy.h create mode 100644 plugins/webapi/WebApiConfiguration.h create mode 100644 plugins/webapi/WebApiConfigurationPage.cpp create mode 100644 plugins/webapi/WebApiConfigurationPage.h create mode 100644 plugins/webapi/WebApiConfigurationPage.ui create mode 100644 plugins/webapi/WebApiConnection.cpp create mode 100644 plugins/webapi/WebApiConnection.h create mode 100644 plugins/webapi/WebApiController.cpp create mode 100644 plugins/webapi/WebApiController.h create mode 100644 plugins/webapi/WebApiHttpServer.cpp create mode 100644 plugins/webapi/WebApiHttpServer.h create mode 100644 plugins/webapi/WebApiPlugin.cpp create mode 100644 plugins/webapi/WebApiPlugin.h create mode 100644 plugins/webapi/python/LICENSE create mode 100644 plugins/webapi/python/README.md create mode 100644 plugins/webapi/python/setup.py create mode 100644 plugins/webapi/python/tests/test_veyon_webapi.py create mode 100755 plugins/webapi/python/veyon/__init__.py create mode 100644 plugins/webapi/qthttpserver/CMakeLists.txt create mode 100644 plugins/webapi/webapi.png create mode 100644 plugins/webapi/webapi.qrc create mode 100644 plugins/webapi/webapi.svg create mode 100644 project.yml create mode 100644 server/CMakeLists.txt create mode 100644 server/data/veyon-server.exe.manifest.in create mode 100644 server/src/ComputerControlClient.cpp create mode 100644 server/src/ComputerControlClient.h create mode 100644 server/src/ComputerControlServer.cpp create mode 100644 server/src/ComputerControlServer.h create mode 100644 server/src/ServerAccessControlManager.cpp create mode 100644 server/src/ServerAccessControlManager.h create mode 100644 server/src/ServerAuthenticationManager.cpp create mode 100644 server/src/ServerAuthenticationManager.h create mode 100644 server/src/VeyonServerProtocol.cpp create mode 100644 server/src/VeyonServerProtocol.h create mode 100644 server/src/VncProxyConnection.cpp create mode 100644 server/src/VncProxyConnection.h create mode 100644 server/src/VncProxyConnectionFactory.h create mode 100644 server/src/VncProxyServer.cpp create mode 100644 server/src/VncProxyServer.h create mode 100644 server/src/VncServer.cpp create mode 100644 server/src/VncServer.h create mode 100644 server/src/main.cpp create mode 100644 server/veyon-server.1 create mode 100644 server/veyon-server.rc.in create mode 100644 service/CMakeLists.txt create mode 100644 service/data/veyon-service.exe.manifest.in create mode 100644 service/src/main.cpp create mode 100644 service/veyon-service.1 create mode 100644 service/veyon-service.rc.in create mode 100644 service/veyon.service.in create mode 100644 translations/CMakeLists.txt create mode 100644 translations/veyon.ts create mode 100644 translations/veyon_ar.ts create mode 100644 translations/veyon_bg.ts create mode 100644 translations/veyon_ca_ES.ts create mode 100644 translations/veyon_cs.ts create mode 100644 translations/veyon_de.ts create mode 100644 translations/veyon_el.ts create mode 100644 translations/veyon_es_ES.ts create mode 100644 translations/veyon_et.ts create mode 100644 translations/veyon_fa.ts create mode 100644 translations/veyon_fr.ts create mode 100644 translations/veyon_he.ts create mode 100644 translations/veyon_hu.ts create mode 100644 translations/veyon_id.ts create mode 100644 translations/veyon_it.ts create mode 100644 translations/veyon_ja.ts create mode 100644 translations/veyon_ko.ts create mode 100644 translations/veyon_lt.ts create mode 100644 translations/veyon_lv.ts create mode 100644 translations/veyon_mn.ts create mode 100644 translations/veyon_nl.ts create mode 100644 translations/veyon_no_NO.ts create mode 100644 translations/veyon_pl.ts create mode 100644 translations/veyon_pt_BR.ts create mode 100644 translations/veyon_pt_PT.ts create mode 100644 translations/veyon_ru.ts create mode 100644 translations/veyon_sl.ts create mode 100644 translations/veyon_sr.ts create mode 100644 translations/veyon_sv.ts create mode 100644 translations/veyon_th.ts create mode 100644 translations/veyon_tr.ts create mode 100644 translations/veyon_uk.ts create mode 100644 translations/veyon_vi.ts create mode 100644 translations/veyon_zh_CN.ts create mode 100644 translations/veyon_zh_TW.ts create mode 100644 worker/CMakeLists.txt create mode 100644 worker/data/veyon-worker.exe.manifest.in create mode 100644 worker/src/FeatureWorkerManagerConnection.cpp create mode 100644 worker/src/FeatureWorkerManagerConnection.h create mode 100644 worker/src/VeyonWorker.cpp create mode 100644 worker/src/VeyonWorker.h create mode 100644 worker/src/main.cpp create mode 100644 worker/veyon-worker.1 create mode 100644 worker/veyon-worker.rc.in diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dd70d7e --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.vagrant/* +.DS_Store +svg +Makefile +plugins/addons +plugins/extra diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5bbad70 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,23 @@ +[submodule "3rdparty/ultravnc"] + path = 3rdparty/ultravnc + url = https://github.com/veyon/ultravnc.git +[submodule "3rdparty/kldap"] + path = 3rdparty/kldap + url = https://invent.kde.org/pim/kldap.git +[submodule "3rdparty/libvncserver"] + path = 3rdparty/libvncserver + url = https://github.com/veyon/libvncserver.git +[submodule "3rdparty/x11vnc"] + path = 3rdparty/x11vnc + url = https://github.com/veyon/x11vnc.git +[submodule "3rdparty/kitemmodels"] + path = 3rdparty/kitemmodels + url = https://invent.kde.org/frameworks/kitemmodels.git + branch = master +[submodule "3rdparty/libfakekey"] + path = 3rdparty/libfakekey + url = https://git.yoctoproject.org/git/libfakekey + branch = master +[submodule "3rdparty/qthttpserver"] + path = 3rdparty/qthttpserver + url = http://code.qt.io/qt-labs/qthttpserver.git diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..c670eca --- /dev/null +++ b/.mailmap @@ -0,0 +1 @@ +Tobias Junghans diff --git a/.tx/config b/.tx/config new file mode 100644 index 0000000..111da52 --- /dev/null +++ b/.tx/config @@ -0,0 +1,9 @@ +[main] +host = https://www.transifex.com +minimum_perc = 5 + +[veyon.veyon_45] +file_filter = translations/veyon_.ts +source_file = translations/veyon.ts +source_lang = en +type = QT diff --git a/3rdparty/kldap/.gitignore b/3rdparty/kldap/.gitignore new file mode 100644 index 0000000..d9b8e3c --- /dev/null +++ b/3rdparty/kldap/.gitignore @@ -0,0 +1,20 @@ +# Ignore the following files +*~ +*.[oa] +*.diff +*.kate-swp +*.kdev4 +.kdev_include_paths +*.kdevelop.pcs +*.moc +*.moc.cpp +*.orig +*.user +.*.swp +.swp.* +Doxyfile +Makefile +/build*/ +.cmake/ +CMakeLists.txt.user* +*.unc-backup* diff --git a/3rdparty/kldap/.gitlab-ci.yml b/3rdparty/kldap/.gitlab-ci.yml new file mode 100644 index 0000000..b631385 --- /dev/null +++ b/3rdparty/kldap/.gitlab-ci.yml @@ -0,0 +1,30 @@ +include: + - https://invent.kde.org/sysadmin/ci-tooling/raw/master/invent/ci-before.yml + - https://invent.kde.org/sysadmin/ci-tooling/raw/master/invent/ci-applications-linux.yml + +build_clazy_clang_tidy: + stage: build + image: kdeorg/ci-suse-qt515 + extends: .linux + #only: + # - merge_requests + before_script: + - zypper install -y clazy clang11 + - git clone --depth=1 https://invent.kde.org/sysadmin/ci-tooling.git $CI_TOOLING + - git clone --depth=1 https://invent.kde.org/sysadmin/repo-metadata.git $CI_TOOLING/repo-metadata + - git clone --depth=1 https://invent.kde.org/sysadmin/kde-build-metadata.git $CI_TOOLING/kde-build-metadata + - git clone --depth=1 https://invent.kde.org/sdk/kde-dev-scripts.git $CI_TOOLING/kde-dev-scripts + + script: + - export CXX=clazy + - export CC=clang + - export CXXFLAGS="-Wno-deprecated-declarations" + - export CLAZY_HEADER_FILTER='^(?!ui_)\w+.h\$' + - export CLAZY_CHECKS="level0,level1,detaching-member,ifndef-define-typo,isempty-vs-count,qrequiredresult-candidates,reserve-candidates,signal-with-return-value,unneeded-cast,function-args-by-ref,function-args-by-value,returning-void-expression,no-ctor-missing-parent-argument,isempty-vs-count,qhash-with-char-pointer-key,raw-environment-function,qproperty-type-mismatch,old-style-connect,qstring-allocations,container-inside-loop,heap-allocated-small-trivial-type,inefficient-qlist,qstring-varargs" + - python3 -u $CI_TOOLING/helpers/prepare-dependencies.py --product $PRODUCT --project $PROJECT --branchGroup $BRANCH_GROUP --environment production --platform $PLATFORM --installTo $INSTALL_PREFIX + - python3 -u $CI_TOOLING/helpers/configure-build.py --product $PRODUCT --project $PROJECT --branchGroup $BRANCH_GROUP --platform $PLATFORM --installTo $INSTALL_PREFIX + - python3 -u $CI_TOOLING/helpers/compile-build.py --product $PRODUCT --project $PROJECT --branchGroup $BRANCH_GROUP --platform $PLATFORM --usingInstall $INSTALL_PREFIX + - cd build && run-clang-tidy + variables: + PLATFORM: SUSEQt5.15 + BRANCH_GROUP: kf5-qt5 diff --git a/3rdparty/kldap/CMakeLists.txt b/3rdparty/kldap/CMakeLists.txt new file mode 100644 index 0000000..c972237 --- /dev/null +++ b/3rdparty/kldap/CMakeLists.txt @@ -0,0 +1,113 @@ +cmake_minimum_required(VERSION 3.5) +set(PIM_VERSION "5.16.44") + +project(KLdap VERSION ${PIM_VERSION}) + +# ECM setup +set(KF5_MIN_VERSION "5.76.0") + +find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED) +set(CMAKE_MODULE_PATH ${KLdap_SOURCE_DIR}/cmake ${ECM_MODULE_PATH}) + +include(KDEInstallDirs) +include(KDECMakeSettings) +include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) + +include(GenerateExportHeader) +include(ECMGenerateHeaders) +include(ECMGeneratePriFile) + +include(ECMSetupVersion) +include(FeatureSummary) +include(ECMQtDeclareLoggingCategory) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(KLDAP_LIB_VERSION ${PIM_VERSION}) + +find_package(KF5KIO ${KF5_MIN_VERSION} CONFIG REQUIRED) +find_package(KF5I18n ${KF5_MIN_VERSION} CONFIG REQUIRED) +find_package(KF5DocTools ${KF5_MIN_VERSION} CONFIG) +find_package(KF5KIO ${KF5_MIN_VERSION} CONFIG REQUIRED) + +find_package(Qt5Keychain CONFIG) +set_package_properties(Qt5Keychain PROPERTIES + DESCRIPTION "Provides support for secure credentials storage" + URL "https://github.com/frankosterfeld/qtkeychain" + TYPE REQUIRED) + +# tell what is missing without doctools +set_package_properties(KF5DocTools PROPERTIES DESCRIPTION "Provides tools to generate documentation in various format from DocBook files" + TYPE OPTIONAL + PURPOSE "Required to build documentation") + +ecm_setup_version(PROJECT VARIABLE_PREFIX KLDAP + VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kldap_version.h" + PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5LdapConfigVersion.cmake" + SOVERSION 5 +) + +########### Find packages ########### +find_package(KF5WidgetsAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) + +add_definitions(-DQT_NO_FOREACH) +add_definitions(-DQT_NO_KEYWORDS) +add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050f00) +add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x054D00) + +find_package(Ldap) +set_package_properties(Ldap PROPERTIES + TYPE RECOMMENDED + PURPOSE "Needed to provide LDAP functionality in KDE" +) + +find_package(Sasl2) +set_package_properties(Sasl2 PROPERTIES TYPE REQUIRED) + +if (Ldap_FOUND) + set(LDAP_FOUND 1) +endif() + +add_definitions(-DTRANSLATION_DOMAIN=\"libkldap5\") + + +########### CMake Config Files ########### +set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Ldap") + +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/KF5LdapConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/KF5LdapConfig.cmake" + INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} +) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/KF5LdapConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/KF5LdapConfigVersion.cmake" + DESTINATION "${CMAKECONFIG_INSTALL_DIR}" + COMPONENT Devel +) + +install(EXPORT KF5LdapTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5LdapTargets.cmake NAMESPACE KF5::) + +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/kldap_version.h + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} + COMPONENT Devel +) + +########### Targets ########### +add_subdirectory(src) +add_subdirectory(kioslave) +if(BUILD_TESTING) + add_subdirectory(autotests) + add_subdirectory(tests) +endif() + +ecm_qt_install_logging_categories( + EXPORT KLDAP + FILE kldap.categories + DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR} + ) + +feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/3rdparty/kldap/KF5LdapConfig.cmake.in b/3rdparty/kldap/KF5LdapConfig.cmake.in new file mode 100644 index 0000000..79dd9b6 --- /dev/null +++ b/3rdparty/kldap/KF5LdapConfig.cmake.in @@ -0,0 +1,3 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/KF5LdapTargets.cmake") diff --git a/3rdparty/kldap/LICENSES/BSD-3-Clause.txt b/3rdparty/kldap/LICENSES/BSD-3-Clause.txt new file mode 100644 index 0000000..0741db7 --- /dev/null +++ b/3rdparty/kldap/LICENSES/BSD-3-Clause.txt @@ -0,0 +1,26 @@ +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/3rdparty/kldap/LICENSES/LGPL-2.0-or-later.txt b/3rdparty/kldap/LICENSES/LGPL-2.0-or-later.txt new file mode 100644 index 0000000..5c96471 --- /dev/null +++ b/3rdparty/kldap/LICENSES/LGPL-2.0-or-later.txt @@ -0,0 +1,446 @@ +GNU LIBRARY GENERAL PUBLIC LICENSE + +Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. + +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is numbered 2 because +it goes with version 2 of the ordinary GPL.] + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public Licenses are intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. + +This license, the Library General Public License, applies to some specially +designated Free Software Foundation software, and to any other libraries whose +authors decide to use it. You can use it for your libraries, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom +to distribute copies of free software (and charge for this service if you +wish), that you receive source code or can get it if you want it, that you +can change the software or use pieces of it in new free programs; and that +you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of +the library, or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for +a fee, you must give the recipients all the rights that we gave you. You must +make sure that they, too, receive or can get the source code. If you link +a program with the library, you must provide complete object files to the +recipients so that they can relink them with the library, after making changes +to the library and recompiling it. And you must show them these terms so they +know their rights. + +Our method of protecting your rights has two steps: (1) copyright the library, +and (2) offer you this license which gives you legal permission to copy, distribute +and/or modify the library. + +Also, for each distributor's protection, we want to make certain that everyone +understands that there is no warranty for this free library. If the library +is modified by someone else and passed on, we want its recipients to know +that what they have is not the original version, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that companies distributing free software will individually +obtain patent licenses, thus in effect transforming the program into proprietary +software. To prevent this, we have made it clear that any patent must be licensed +for everyone's free use or not licensed at all. + +Most GNU software, including some libraries, is covered by the ordinary GNU +General Public License, which was designed for utility programs. This license, +the GNU Library General Public License, applies to certain designated libraries. +This license is quite different from the ordinary one; be sure to read it +in full, and don't assume that anything in it is the same as in the ordinary +license. + +The reason we have a separate public license for some libraries is that they +blur the distinction we usually make between modifying or adding to a program +and simply using it. Linking a program with a library, without changing the +library, is in some sense simply using the library, and is analogous to running +a utility program or application program. However, in a textual and legal +sense, the linked executable is a combined work, a derivative of the original +library, and the ordinary General Public License treats it as such. + +Because of this blurred distinction, using the ordinary General Public License +for libraries did not effectively promote software sharing, because most developers +did not use the libraries. We concluded that weaker conditions might promote +sharing better. + +However, unrestricted linking of non-free programs would deprive the users +of those programs of all benefit from the free status of the libraries themselves. +This Library General Public License is intended to permit developers of non-free +programs to use free libraries, while preserving your freedom as a user of +such programs to change the free libraries that are incorporated in them. +(We have not seen how to achieve this as regards changes in header files, +but we have achieved it as regards changes in the actual functions of the +Library.) The hope is that this will lead to faster development of free libraries. + +The precise terms and conditions for copying, distribution and modification +follow. Pay close attention to the difference between a "work based on the +library" and a "work that uses the library". The former contains code derived +from the library, while the latter only works together with the library. + +Note that it is possible for a library to be covered by the ordinary General +Public License rather than by this special one. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library which contains a +notice placed by the copyright holder or other authorized party saying it +may be distributed under the terms of this Library General Public License +(also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared +so as to be conveniently linked with application programs (which use some +of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has +been distributed under these terms. A "work based on the Library" means either +the Library or any derivative work under copyright law: that is to say, a +work containing the Library or a portion of it, either verbatim or with modifications +and/or translated straightforwardly into another language. (Hereinafter, translation +is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications +to it. For a library, complete source code means all the source code for all +modules it contains, plus any associated interface definition files, plus +the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered +by this License; they are outside its scope. The act of running a program +using the Library is not restricted, and output from such a program is covered +only if its contents constitute a work based on the Library (independent of +the use of the Library in a tool for writing it). Whether that is true depends +on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and disclaimer +of warranty; keep intact all the notices that refer to this License and to +the absence of any warranty; and distribute a copy of this License along with +the Library. + +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, +thus forming a work based on the Library, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all +of these conditions: + + a) The modified work must itself be a software library. + +b) You must cause the files modified to carry prominent notices stating that +you changed the files and the date of any change. + +c) You must cause the whole of the work to be licensed at no charge to all +third parties under the terms of this License. + +d) If a facility in the modified Library refers to a function or a table of +data to be supplied by an application program that uses the facility, other +than as an argument passed when the facility is invoked, then you must make +a good faith effort to ensure that, in the event an application does not supply +such function or table, the facility still operates, and performs whatever +part of its purpose remains meaningful. + +(For example, a function in a library to compute square roots has a purpose +that is entirely well-defined independent of the application. Therefore, Subsection +2d requires that any application-supplied function or table used by this function +must be optional: if the application does not supply it, the square root function +must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Library, and can be reasonably +considered independent and separate works in themselves, then this License, +and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole +which is a work based on the Library, the distribution of the whole must be +on the terms of this License, whose permissions for other licensees extend +to the entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise +the right to control the distribution of derivative or collective works based +on the Library. + +In addition, mere aggregation of another work not based on the Library with +the Library (or with a work based on the Library) on a volume of a storage +or distribution medium does not bring the other work under the scope of this +License. + +3. You may opt to apply the terms of the ordinary GNU General Public License +instead of this License to a given copy of the Library. To do this, you must +alter all the notices that refer to this License, so that they refer to the +ordinary GNU General Public License, version 2, instead of to this License. +(If a newer version than version 2 of the ordinary GNU General Public License +has appeared, then you can specify that version instead if you wish.) Do not +make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, +so the ordinary GNU General Public License applies to all subsequent copies +and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library +into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of +it, under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you accompany it with the complete corresponding +machine-readable source code, which must be distributed under the terms of +Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated +place, then offering equivalent access to copy the source code from the same +place satisfies the requirement to distribute the source code, even though +third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but +is designed to work with the Library by being compiled or linked with it, +is called a "work that uses the Library". Such a work, in isolation, is not +a derivative work of the Library, and therefore falls outside the scope of +this License. + +However, linking a "work that uses the Library" with the Library creates an +executable that is a derivative of the Library (because it contains portions +of the Library), rather than a "work that uses the library". The executable +is therefore covered by this License. Section 6 states terms for distribution +of such executables. + +When a "work that uses the Library" uses material from a header file that +is part of the Library, the object code for the work may be a derivative work +of the Library even though the source code is not. Whether this is true is +especially significant if the work can be linked without the Library, or if +the work is itself a library. The threshold for this to be true is not precisely +defined by law. + +If such an object file uses only numerical parameters, data structure layouts +and accessors, and small macros and small inline functions (ten lines or less +in length), then the use of the object file is unrestricted, regardless of +whether it is legally a derivative work. (Executables containing this object +code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute +the object code for the work under the terms of Section 6. Any executables +containing that work also fall under Section 6, whether or not they are linked +directly with the Library itself. + +6. As an exception to the Sections above, you may also compile or link a "work +that uses the Library" with the Library to produce a work containing portions +of the Library, and distribute that work under terms of your choice, provided +that the terms permit modification of the work for the customer's own use +and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library +is used in it and that the Library and its use are covered by this License. +You must supply a copy of this License. If the work during execution displays +copyright notices, you must include the copyright notice for the Library among +them, as well as a reference directing the user to the copy of this License. +Also, you must do one of these things: + +a) Accompany the work with the complete corresponding machine-readable source +code for the Library including whatever changes were used in the work (which +must be distributed under Sections 1 and 2 above); and, if the work is an +executable linked with the Library, with the complete machine-readable "work +that uses the Library", as object code and/or source code, so that the user +can modify the Library and then relink to produce a modified executable containing +the modified Library. (It is understood that the user who changes the contents +of definitions files in the Library will not necessarily be able to recompile +the application to use the modified definitions.) + +b) Accompany the work with a written offer, valid for at least three years, +to give the same user the materials specified in Subsection 6a, above, for +a charge no more than the cost of performing this distribution. + +c) If distribution of the work is made by offering access to copy from a designated +place, offer equivalent access to copy the above specified materials from +the same place. + +d) Verify that the user has already received a copy of these materials or +that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must +include any data and utility programs needed for reproducing the executable +from it. However, as a special exception, the source code distributed need +not include anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the operating +system on which the executable runs, unless that component itself accompanies +the executable. + +It may happen that this requirement contradicts the license restrictions of +other proprietary libraries that do not normally accompany the operating system. +Such a contradiction means you cannot use both them and the Library together +in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side +in a single library together with other library facilities not covered by +this License, and distribute such a combined library, provided that the separate +distribution of the work based on the Library and of the other library facilities +is otherwise permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work based on the +Library, uncombined with any other library facilities. This must be distributed +under the terms of the Sections above. + +b) Give prominent notice with the combined library of the fact that part of +it is a work based on the Library, and explaining where to find the accompanying +uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library +except as expressly provided under this License. Any attempt otherwise to +copy, modify, sublicense, link with, or distribute the Library is void, and +will automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will not +have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed +it. However, nothing else grants you permission to modify or distribute the +Library or its derivative works. These actions are prohibited by law if you +do not accept this License. Therefore, by modifying or distributing the Library +(or any work based on the Library), you indicate your acceptance of this License +to do so, and all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), +the recipient automatically receives a license from the original licensor +to copy, distribute, link with or modify the Library subject to these terms +and conditions. You may not impose any further restrictions on the recipients' +exercise of the rights granted herein. You are not responsible for enforcing +compliance by third parties to this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement +or for any other reason (not limited to patent issues), conditions are imposed +on you (whether by court order, agreement or otherwise) that contradict the +conditions of this License, they do not excuse you from the conditions of +this License. If you cannot distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, then as +a consequence you may not distribute the Library at all. For example, if a +patent license would not permit royalty-free redistribution of the Library +by all those who receive copies directly or indirectly through you, then the +only way you could satisfy both it and this License would be to refrain entirely +from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents +or other property right claims or to contest validity of any such claims; +this section has the sole purpose of protecting the integrity of the free +software distribution system which is implemented by public license practices. +Many people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose +that choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain +countries either by patents or by copyrighted interfaces, the original copyright +holder who places the Library under this License may add an explicit geographical +distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this +License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of +the Library General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to address +new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that version +or of any later version published by the Free Software Foundation. If the +Library does not specify a license version number, you may choose any version +ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs +whose distribution conditions are incompatible with these, write to the author +to ask for permission. For software which is copyrighted by the Free Software +Foundation, write to the Free Software Foundation; we sometimes make exceptions +for this. Our decision will be guided by the two goals of preserving the free +status of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY +"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE +OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE +THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE +OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA +OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES +OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH +HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible +use to the public, we recommend making it free software that everyone can +redistribute and change. You can do so by permitting redistribution under +these terms (or, alternatively, under the terms of the ordinary General Public +License). + +To apply these terms, attach the following notices to the library. It is safest +to attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the "copyright" +line and a pointer to where the full notice is found. + +one line to give the library's name and an idea of what it does. + +Copyright (C) year name of author + +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your option) +any later version. + +This library 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 Library General Public License for more +details. + +You should have received a copy of the GNU Library General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the library, if necessary. Here +is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in + +the library `Frob' (a library for tweaking knobs) written + +by James Random Hacker. + +signature of Ty Coon, 1 April 1990 + +Ty Coon, President of Vice + +That's all there is to it! diff --git a/3rdparty/kldap/LICENSES/MIT.txt b/3rdparty/kldap/LICENSES/MIT.txt new file mode 100644 index 0000000..204b93d --- /dev/null +++ b/3rdparty/kldap/LICENSES/MIT.txt @@ -0,0 +1,19 @@ +MIT License Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/3rdparty/kldap/README.md b/3rdparty/kldap/README.md new file mode 100644 index 0000000..7496795 --- /dev/null +++ b/3rdparty/kldap/README.md @@ -0,0 +1,3 @@ +# KLDAP # + +Allows LDAP accessing with a convenient Qt style C++ API. diff --git a/3rdparty/kldap/cmake/FindLdap.cmake b/3rdparty/kldap/cmake/FindLdap.cmake new file mode 100644 index 0000000..a60645f --- /dev/null +++ b/3rdparty/kldap/cmake/FindLdap.cmake @@ -0,0 +1,99 @@ +#.rst: +# FindLdap +# -------- +# +# Try to find the LDAP client libraries. +# +# This will define the following variables: +# +# ``Ldap_FOUND`` +# True if libldap is available. +# +# ``Ldap_VERSION`` +# The version of libldap +# +# ``Ldap_INCLUDE_DIRS`` +# This should be passed to target_include_directories() if +# the target is not used for linking +# +# ``Ldap_LIBRARIES`` +# The LDAP libraries (libldap + liblber if available) +# This can be passed to target_link_libraries() instead of +# the ``Ldap::Ldap`` target +# +# If ``Ldap_FOUND`` is TRUE, the following imported target +# will be available: +# +# ``Ldap::Ldap`` +# The LDAP library +# +# Since pre-5.0.0. +# +# Imported target since 5.1.41 +# +#============================================================================= +# SPDX-FileCopyrightText: 2006 Szombathelyi György +# SPDX-FileCopyrightText: 2007-2020 Laurent Montel +# +# SPDX-License-Identifier: BSD-3-Clause +#============================================================================= + +find_path(Ldap_INCLUDE_DIRS NAMES ldap.h) + +if(APPLE) + find_library(Ldap_LIBRARIES NAMES LDAP + PATHS + /System/Library/Frameworks + /Library/Frameworks + ) +else() + find_library(Ldap_LIBRARY NAMES ldap) + find_library(Lber_LIBRARY NAMES lber) +endif() + +if(Ldap_LIBRARY AND Lber_LIBRARY) + set(Ldap_LIBRARIES ${Ldap_LIBRARY} ${Lber_LIBRARY}) +endif() + +if(EXISTS ${Ldap_INCLUDE_DIRS}/ldap_features.h) + file(READ ${Ldap_INCLUDE_DIRS}/ldap_features.h LDAP_FEATURES_H_CONTENT) + string(REGEX MATCH "#define LDAP_VENDOR_VERSION_MAJOR[ ]+[0-9]+" _LDAP_VERSION_MAJOR_MATCH ${LDAP_FEATURES_H_CONTENT}) + string(REGEX MATCH "#define LDAP_VENDOR_VERSION_MINOR[ ]+[0-9]+" _LDAP_VERSION_MINOR_MATCH ${LDAP_FEATURES_H_CONTENT}) + string(REGEX MATCH "#define LDAP_VENDOR_VERSION_PATCH[ ]+[0-9]+" _LDAP_VERSION_PATCH_MATCH ${LDAP_FEATURES_H_CONTENT}) + + string(REGEX REPLACE ".*_MAJOR[ ]+(.*)" "\\1" LDAP_VERSION_MAJOR ${_LDAP_VERSION_MAJOR_MATCH}) + string(REGEX REPLACE ".*_MINOR[ ]+(.*)" "\\1" LDAP_VERSION_MINOR ${_LDAP_VERSION_MINOR_MATCH}) + string(REGEX REPLACE ".*_PATCH[ ]+(.*)" "\\1" LDAP_VERSION_PATCH ${_LDAP_VERSION_PATCH_MATCH}) + + set(Ldap_VERSION "${LDAP_VERSION_MAJOR}.${LDAP_VERSION_MINOR}.${LDAP_VERSION_PATCH}") +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Ldap + FOUND_VAR Ldap_FOUND + REQUIRED_VARS Ldap_LIBRARIES Ldap_INCLUDE_DIRS + VERSION_VAR Ldap_VERSION +) + +if(Ldap_FOUND AND NOT TARGET Lber::Lber) + add_library(Lber::Lber UNKNOWN IMPORTED) + set_target_properties(Lber::Lber PROPERTIES + IMPORTED_LOCATION "${Lber_LIBRARY}") +endif() + +if(Ldap_FOUND AND NOT TARGET Ldap::Ldap) + add_library(Ldap::Ldap UNKNOWN IMPORTED) + set_target_properties(Ldap::Ldap PROPERTIES + IMPORTED_LOCATION "${Ldap_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${Ldap_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES Lber::Lber) +endif() + +mark_as_advanced(Ldap_INCLUDE_DIRS Ldap_LIBRARY Lber_LIBRARY Ldap_LIBRARIES) + +include(FeatureSummary) +set_package_properties(Ldap PROPERTIES + URL "https://www.openldap.org/" + DESCRIPTION "LDAP (Lightweight Directory Access Protocol) libraries." +) diff --git a/3rdparty/kldap/metainfo.yaml b/3rdparty/kldap/metainfo.yaml new file mode 100644 index 0000000..4908f95 --- /dev/null +++ b/3rdparty/kldap/metainfo.yaml @@ -0,0 +1,16 @@ +maintainer: mlaurent +description: LDAP support library +tier: 3 +type: functional +platforms: + - name: All +portingAid: false +deprecated: false +release: false +libraries: + - qmake: Ldap + cmake: "KF5::Ldap" +cmakename: KF5Ldap + +public_lib: true +group: kdepim diff --git a/3rdparty/kldap/src/CMakeLists.txt b/3rdparty/kldap/src/CMakeLists.txt new file mode 100644 index 0000000..de74337 --- /dev/null +++ b/3rdparty/kldap/src/CMakeLists.txt @@ -0,0 +1,161 @@ +include(CheckFunctionExists) +include(CheckIncludeFiles) +include(CheckSymbolExists) + +check_include_files(sys/time.h HAVE_SYS_TIME_H) + +set(kldap_EXTRA_LIBS) + +if(Ldap_FOUND) + set(kldap_EXTRA_LIBS Ldap::Ldap) + if(WIN32) + set(kldap_EXTRA_LIBS ${kldap_EXTRA_LIBS} ws2_32) + endif() + set(HAVE_LDAP_H) + set(CMAKE_REQUIRED_INCLUDES lber.h ldap.h) + set(CMAKE_REQUIRED_LIBRARIES Ldap::Ldap) + check_function_exists(ldap_start_tls_s HAVE_LDAP_START_TLS_S) + check_function_exists(ldap_initialize HAVE_LDAP_INITIALIZE) + check_function_exists(ber_memfree HAVE_BER_MEMFREE) + check_function_exists(ldap_unbind_ext HAVE_LDAP_UNBIND_EXT) + check_function_exists(ldap_extended_operation HAVE_LDAP_EXTENDED_OPERATION) + check_function_exists(ldap_extended_operation_s HAVE_LDAP_EXTENDED_OPERATION_S) + check_symbol_exists(ldap_extended_operation ldap.h HAVE_LDAP_EXTENDED_OPERATION_PROTOTYPE) + check_symbol_exists(ldap_extended_operation_s ldap.h HAVE_LDAP_EXTENDED_OPERATION_S_PROTOTYPE) +endif() + +set(kldap_EXTRA_LIBS ${kldap_EXTRA_LIBS} Sasl2::Sasl2) + +configure_file(kldap_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kldap_config.h) + +########### next target ############### + +set(kldap_LIB_core_SRCS + core/ber.cpp + core/ldif.cpp + core/ldapurl.cpp + core/ldapserver.cpp + core/ldapobject.cpp + core/ldapconnection.cpp + core/ldapoperation.cpp + core/ldapcontrol.cpp + core/ldapsearch.cpp + core/ldapdn.cpp +) + +set(kldap_LIB_widgets_SRCS + widgets/ldapconfigwidget.cpp + widgets/addhostdialog.cpp + widgets/ldapclient.cpp + widgets/ldapclientsearch.cpp + widgets/ldapclientsearchconfig.cpp + widgets/ldapconfigurewidget.cpp + widgets/ldapclientsearchconfigreadconfigjob.cpp + widgets/ldapclientsearchconfigwriteconfigjob.cpp + widgets/ldapwidgetitem_p.cpp + widgets/ldapwidgetitemreadconfigserverjob.cpp + widgets/ldapsearchclientreadconfigserverjob.cpp + ) + + set(kldap_LIB_SRCS + ${kldap_LIB_core_SRCS} + ${kldap_LIB_widgets_SRCS} + ) + +ecm_qt_declare_logging_category(kldap_LIB_SRCS HEADER ldap_debug.h IDENTIFIER LDAP_LOG CATEGORY_NAME org.kde.pim.ldap + DESCRIPTION "kldaplib (kldap)" + OLD_CATEGORY_NAMES log_ldap + EXPORT KLDAP + ) + +ecm_qt_declare_logging_category(kldap_LIB_SRCS HEADER ldapclient_debug.h IDENTIFIER LDAPCLIENT_LOG CATEGORY_NAME org.kde.pim.ldapclient + DESCRIPTION "ldapclient (libkdepim)" + OLD_CATEGORY_NAMES log_ldapclient + EXPORT KLDAP + ) + + + +add_library(KF5Ldap ${kldap_LIB_SRCS}) + +generate_export_header(KF5Ldap BASE_NAME kldap) + +add_library(KF5::Ldap ALIAS KF5Ldap) + +target_link_libraries(KF5Ldap +PRIVATE + Qt5::Widgets + KF5::I18n + KF5::WidgetsAddons + KF5::ConfigCore + KF5::CoreAddons + KF5::KIOCore + ${kldap_EXTRA_LIBS} + qt5keychain +) + +target_include_directories(KF5Ldap INTERFACE "$") +target_include_directories(KF5Ldap PUBLIC "$") + +set_target_properties(KF5Ldap PROPERTIES + VERSION ${KLDAP_VERSION_STRING} + SOVERSION ${KLDAP_SOVERSION} + EXPORT_NAME Ldap +) + +install(TARGETS KF5Ldap EXPORT KF5LdapTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) + +########### install files ############### + +ecm_generate_headers(KLdapCore_CamelCase_HEADERS + HEADER_NAMES + Ber + LdapConnection + LdapControl + LdapDN + LdapObject + LdapOperation + LdapSearch + LdapServer + LdapDefs + LdapUrl + Ldif + RELATIVE core + PREFIX KLDAP + REQUIRED_HEADERS KLdapCore_HEADERS +) + +ecm_generate_headers(KLdapWidgets_CamelCase_HEADERS + HEADER_NAMES + LdapConfigWidget + LdapClientSearchConfig + LdapClientSearch + AddHostDialog + LdapClient + LdapConfigureWidget + LdapClientSearchConfigWriteConfigJob + LdapClientSearchConfigReadConfigJob + LdapSearchClientReadConfigServerJob + RELATIVE widgets + PREFIX KLDAP + REQUIRED_HEADERS KLdapWidgets_HEADERS +) + +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/kldap_export.h + ${KLdapCore_HEADERS} + ${KLdapWidgets_HEADERS} + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KLDAP/kldap + COMPONENT Devel +) + +install(FILES + ${KLdapCore_CamelCase_HEADERS} + ${KLdapWidgets_CamelCase_HEADERS} + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KLDAP/KLDAP/ + COMPONENT Devel +) + +ecm_generate_pri_file(BASE_NAME Ldap LIB_NAME KF5Ldap FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KLDAP/) +install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) + diff --git a/3rdparty/kldap/src/Messages.sh b/3rdparty/kldap/src/Messages.sh new file mode 100644 index 0000000..f415639 --- /dev/null +++ b/3rdparty/kldap/src/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$XGETTEXT core/*.cpp widgets/*.cpp -o $podir/libkldap5.pot + diff --git a/3rdparty/kldap/src/core/ber.cpp b/3rdparty/kldap/src/core/ber.cpp new file mode 100644 index 0000000..3549e49 --- /dev/null +++ b/3rdparty/kldap/src/core/ber.cpp @@ -0,0 +1,456 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "ber.h" +#include "kldap_config.h" + +#include "ldap_debug.h" + +#include +#include + +#include + +#ifdef LDAP_FOUND + +#ifdef Q_OS_SOLARIS //krazy:exclude=cpp +#define BC31 1 +#endif + +#ifndef HAVE_WINLDAP_H +#include +#include +#else +#include +#endif + +#ifndef LBER_USE_DER +#define LBER_USE_DER 1 +#endif + +#ifndef HAVE_BER_MEMFREE +# ifndef HAVE_WINLDAP_H +# define ber_memfree(x) ldap_memfree(x) +# else +# define ber_memfree(x) win_ldap_memfree(x) +# endif +#endif + +#endif + +using namespace KLDAP; + +class Q_DECL_HIDDEN Ber::BerPrivate +{ +public: +#ifdef LDAP_FOUND + BerElement *mBer = nullptr; +#endif +}; + +#ifdef LDAP_FOUND +Ber::Ber() + : d(new BerPrivate) +{ + d->mBer = ber_alloc_t(LBER_USE_DER); + Q_ASSERT(d->mBer); +} + +Ber::Ber(const QByteArray &value) + : d(new BerPrivate) +{ + struct berval bv; + bv.bv_val = (char *)value.data(); + bv.bv_len = value.size(); + d->mBer = ber_init(&bv); + Q_ASSERT(d->mBer); +} + +Ber::~Ber() +{ + ber_free(d->mBer, 1); + delete d; +} + +Ber::Ber(const Ber &that) + : d(new BerPrivate) +{ + struct berval *bv; + if (ber_flatten(that.d->mBer, &bv) == 0) { + d->mBer = ber_init(bv); + ber_bvfree(bv); + } +} + +Ber &Ber::operator=(const Ber &that) +{ + if (this == &that) { + return *this; + } + + struct berval *bv; + if (ber_flatten(that.d->mBer, &bv) == 0) { + d->mBer = ber_init(bv); + ber_bvfree(bv); + } + return *this; +} + +QByteArray Ber::flatten() const +{ + QByteArray ret; + struct berval *bv; + if (ber_flatten(d->mBer, &bv) == 0) { + ret = QByteArray(bv->bv_val, bv->bv_len); + ber_bvfree(bv); + } + return ret; +} + +int Ber::printf(QString format, ...) +{ + char fmt[2]; + va_list args; + va_start(args, format); + fmt[1] = '\0'; + + int i = 0, ret = 0; + while (i < format.length()) { + fmt[0] = format[i].toLatin1(); + i++; + switch (fmt[0]) { + case 'b': + case 'e': + case 'i': + { + ber_int_t v = va_arg(args, int); + ret = ber_printf(d->mBer, fmt, v); + break; + } + case 'B': + { + //FIXME: QBitArray vould be logical, but how to access the bits? + QByteArray *B = va_arg(args, QByteArray *); + int Bc = va_arg(args, int); + ret = ber_printf(d->mBer, fmt, B->data(), Bc); + break; + } + case 'o': + { + QByteArray *o = va_arg(args, QByteArray *); + ret = ber_printf(d->mBer, fmt, o->data(), o->size()); + break; + } + case 'O': + { + QByteArray *O = va_arg(args, QByteArray *); + struct berval bv; + bv.bv_val = (char *)O->data(); + bv.bv_len = O->size(); + ret = ber_printf(d->mBer, fmt, &bv); + break; + break; + } + case 's': + { + QByteArray *s = va_arg(args, QByteArray *); + ret = ber_printf(d->mBer, fmt, s->data()); + break; + break; + } + case 't': + { + unsigned int t = va_arg(args, unsigned int); + ret = ber_printf(d->mBer, fmt, t); + break; + break; + } + case 'v': + { + QList *v = va_arg(args, QList *); + QVarLengthArray l(v->count() + 1); + int j; + for (j = 0; j < v->count(); j++) { + l[j] = v->at(j).data(); + } + l[j] = nullptr; + ret = ber_printf(d->mBer, fmt, l.data()); + break; + } + case 'V': + { + QList *V = va_arg(args, QList *); + QVarLengthArray bv(V->count() + 1); + QVarLengthArray bvs(V->count()); + int j; + for (j = 0; j < V->count(); j++) { + bvs[j].bv_val = (char *)V->at(j).data(); + bvs[j].bv_len = V->at(j).size(); + bv[j] = &bvs[j]; + } + bv[V->count()] = nullptr; + ret = ber_printf(d->mBer, fmt, bv.data()); + break; + } + case 'n': + case '{': + case '}': + case '[': + case ']': + ret = ber_printf(d->mBer, fmt); + break; + default: + qCWarning(LDAP_LOG) << "Invalid BER format parameter: '" << fmt << "'"; + ret = -1; + } + qCDebug(LDAP_LOG) << "ber_printf format:" << fmt << "ret:" << ret; + if (ret == -1) { + break; + } + } + va_end(args); + return ret; +} + +int Ber::scanf(QString format, ...) +{ + char fmt[2]; + va_list args; + va_start(args, format); + fmt[1] = '\0'; + + int i = 0, ret = 0; + while (i < format.length()) { + fmt[0] = format[i].toLatin1(); + i++; + switch (fmt[0]) { + case 'l': + case 'b': + case 'e': + case 'i': + { + int *v = va_arg(args, int *); + ret = ber_scanf(d->mBer, fmt, v); + break; + } + case 'B': + { + //FIXME: QBitArray vould be logical, but how to access the bits? + QByteArray *B = va_arg(args, QByteArray *); + int *Bc = va_arg(args, int *); + char *c; + ret = ber_scanf(d->mBer, fmt, &c, Bc); + if (ret != -1) { + *B = QByteArray(c, (*Bc + 7) / 8); + ber_memfree(c); + } + break; + } + case 'o': + { + QByteArray *o = va_arg(args, QByteArray *); + struct berval bv; + ret = ber_scanf(d->mBer, fmt, &bv); + if (ret != -1) { + *o = QByteArray(bv.bv_val, bv.bv_len); + ber_memfree(bv.bv_val); + } + break; + } + case 'O': + { + QByteArray *O = va_arg(args, QByteArray *); + struct berval *bv; + ret = ber_scanf(d->mBer, fmt, &bv); + if (ret != -1) { + *O = QByteArray(bv->bv_val, bv->bv_len); + ber_bvfree(bv); + } + break; + break; + } + case 'm': + { //the same as 'O', just *bv should not be freed. + QByteArray *m = va_arg(args, QByteArray *); + struct berval *bv; + ret = ber_scanf(d->mBer, fmt, &bv); + if (ret != -1) { + *m = QByteArray(bv->bv_val, bv->bv_len); + } + break; + } + case 'a': + { + QByteArray *a = va_arg(args, QByteArray *); + char *c; + ret = ber_scanf(d->mBer, fmt, &c); + if (ret != -1) { + *a = QByteArray(c); + ber_memfree(c); + } + break; + } + + case 's': + { + QByteArray *s = va_arg(args, QByteArray *); + char buf[255]; + ber_len_t l = sizeof(buf); + ret = ber_scanf(d->mBer, fmt, &buf, &l); + if (ret != -1) { + *s = QByteArray(buf, l); + } + break; + } + case 't': + case 'T': + { + unsigned int *t = va_arg(args, unsigned int *); + ret = ber_scanf(d->mBer, fmt, t); + break; + break; + } + case 'v': + { + QList *v = va_arg(args, QList *); + char **c, **c2; + ret = ber_scanf(d->mBer, fmt, &c); + if (ret != -1 && c) { + c2 = c; + while (*c) { + v->append(QByteArray(*c)); + ber_memfree(*c); + c++; + } + ber_memfree((char *)c2); + } + break; + } + case 'V': + { + QList *v = va_arg(args, QList *); + struct berval **bv, **bv2; + ret = ber_scanf(d->mBer, fmt, &bv); + if (ret != -1 && bv) { + bv2 = bv; + while (*bv) { + v->append(QByteArray((*bv)->bv_val, (*bv)->bv_len)); + bv++; + } + ber_bvecfree(bv2); + } + break; + } + case 'x': + case 'n': + case '{': + case '}': + case '[': + case ']': + ret = ber_scanf(d->mBer, fmt); + break; + default: + qCWarning(LDAP_LOG) << "Invalid BER format parameter: '" << fmt << "'"; + ret = -1; + } + + qCDebug(LDAP_LOG) << "ber_scanf format:" << fmt << "ret:" << ret; + if (ret == -1) { + break; + } + } + va_end(args); + return ret; +} + +unsigned int Ber::peekTag(int &size) +{ + unsigned int ret; + ber_len_t len; + ret = ber_peek_tag(d->mBer, &len); + size = len; + return ret; +} + +unsigned int Ber::skipTag(int &size) +{ + unsigned int ret; + ber_len_t len; + ret = ber_skip_tag(d->mBer, &len); + size = len; + return ret; +} + +#else + +Ber::Ber() + : d(new BerPrivate) +{ + qCritical() << "LDAP support not compiled"; +} + +Ber::Ber(const QByteArray &) + : d(new BerPrivate) +{ + qCritical() << "LDAP support not compiled"; +} + +Ber::~Ber() +{ + delete d; +} + +Ber::Ber(const Ber &) + : d(new BerPrivate) +{ + qCritical() << "LDAP support not compiled"; +} + +Ber &Ber::operator=(const Ber &that) +{ + if (this == &that) { + return *this; + } + qCritical() << "LDAP support not compiled"; + return *this; +} + +QByteArray Ber::flatten() const +{ + qCritical() << "LDAP support not compiled"; + return QByteArray(); +} + +int Ber::printf(QString format, ...) +{ + Q_UNUSED(format) + qCritical() << "LDAP support not compiled"; + return -1; +} + +int Ber::scanf(QString format, ...) +{ + Q_UNUSED(format) + qCritical() << "LDAP support not compiled"; + return -1; +} + +unsigned int Ber::peekTag(int &size) +{ + Q_UNUSED(size) + qCritical() << "LDAP support not compiled"; + return 0; +} + +unsigned int Ber::skipTag(int &size) +{ + Q_UNUSED(size) + qCritical() << "LDAP support not compiled"; + return 0; +} + +#endif diff --git a/3rdparty/kldap/src/core/ber.h b/3rdparty/kldap/src/core/ber.h new file mode 100644 index 0000000..d94112c --- /dev/null +++ b/3rdparty/kldap/src/core/ber.h @@ -0,0 +1,115 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KLDAP_BER_H +#define KLDAP_BER_H + +#include + +#include "kldap_export.h" + +// clazy:excludeall=copyable-polymorphic + +namespace KLDAP { +/** + * This class allows encoding and decoding Qt structures using Basic + * Encoding Rules. + */ +class KLDAP_EXPORT Ber +{ +public: + /** + * Constructs a Ber object. + */ + Ber(); + /** + * Constructs a Ber object from the value. + */ + explicit Ber(const QByteArray &value); + /** + * Destroys the Ber object. + */ + ~Ber(); + + Ber(const Ber &that); + Ber &operator=(const Ber &that); + + /** + * Returns the Ber object as a flat QByteArray. + */ + Q_REQUIRED_RESULT QByteArray flatten() const; + + /** + * Appends the data with the specified format to the Ber object. + * This function works like printf, except that it's appending the + * parameters, not replacing them. The allowed format characters and + * the expected parameter types are: + *
    + *
  • + * b Boolean. An int parameter should be supplied. + * A boolean element is output. + *
  • + *
  • + * e Enumeration. An int parameter should be supplied. + * An enumeration element is output. + *
  • + *
  • + * i Integer. An int parameter should be supplied. + * An integer element is output. + *
  • + *
  • + * B Bitstring. A pointer to a QByteArray which contains the + * bitstring is supplied, followed by the number of bits in the + * bitstring. A bitstring element is output. + *
  • + *
  • + * n Null. No parameter is required. A null element is output. + *
  • + *
  • + * O,o,s Octet string. A QByteArray * is supplied. + * An octet string element is output. + * Due to versatility of Qt's QByteArray, these three format + * strings are all accepts the same parameter, but using the 's' + * format the string will be encoded only to the first zero + * character (a null terminated string)! + *
  • + *
  • + * t Tag. An int specifying the tag to give the next element + * is provided. This works across calls. + *
  • + *
  • + * v,V Several octet strings. A QList* is supplied. + * Note that a construct like ’{v}’ is required to get an actual + * SEQUENCE OF octet strings. Also note that the 'v' format recognizes + * the QByteArray only to the first zero character, so it's not + * appropriate for binary data, just only for null terminated strings! + *
  • + *
  • + * { Begin sequence. No parameter is required. + *
  • + *
  • + * } End sequence. No parameter is required. + *
  • + *
  • + * [ Begin set. No parameter is required. + *
  • + *
  • + * ] End set. No parameter is required. + *
  • + *
+ */ + int printf(QString format, ...); // Passing by-value since it's used by va_start + int scanf(QString format, ...); + unsigned int peekTag(int &size); + unsigned int skipTag(int &size); + +private: + class BerPrivate; + BerPrivate *const d; +}; +} +#endif diff --git a/3rdparty/kldap/src/core/ldapconnection.cpp b/3rdparty/kldap/src/core/ldapconnection.cpp new file mode 100644 index 0000000..925bf63 --- /dev/null +++ b/3rdparty/kldap/src/core/ldapconnection.cpp @@ -0,0 +1,448 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "ldapconnection.h" +#include "ldapdefs.h" +#include "kldap_config.h" // LDAP_FOUND + +#include +#include +#include "ldap_debug.h" + +#include +static const sasl_callback_t callbacks[] = { + { SASL_CB_ECHOPROMPT, nullptr, nullptr }, + { SASL_CB_NOECHOPROMPT, nullptr, nullptr }, + { SASL_CB_GETREALM, nullptr, nullptr }, + { SASL_CB_USER, nullptr, nullptr }, + { SASL_CB_AUTHNAME, nullptr, nullptr }, + { SASL_CB_PASS, nullptr, nullptr }, + { SASL_CB_CANON_USER, nullptr, nullptr }, + { SASL_CB_LIST_END, nullptr, nullptr } +}; + +static bool ldapoperation_sasl_initialized = false; + +#ifdef LDAP_FOUND +# ifndef HAVE_WINLDAP_H +# include +# include +#else +# include +#endif // HAVE_WINLDAP_H + +#ifndef LDAP_OPT_SUCCESS +#define LDAP_OPT_SUCCESS 0 +#endif + +#endif + +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapConnection::LdapConnectionPrivate +{ +public: + LdapConnectionPrivate(); + LdapServer mServer; + QString mConnectionError; + +#ifdef LDAP_FOUND + LDAP *mLDAP; +#else + void *mLDAP; +#endif + sasl_conn_t *mSASLconn; +}; + +LdapConnection::LdapConnectionPrivate::LdapConnectionPrivate() +{ + mSASLconn = nullptr; + if (!ldapoperation_sasl_initialized) { + sasl_client_init(nullptr); + ldapoperation_sasl_initialized = true; + } +} + +LdapConnection::LdapConnection() + : d(new LdapConnectionPrivate) +{ + d->mLDAP = nullptr; +} + +LdapConnection::LdapConnection(const LdapUrl &url) + : d(new LdapConnectionPrivate) +{ + d->mLDAP = nullptr; + setUrl(url); +} + +LdapConnection::LdapConnection(const LdapServer &server) + : d(new LdapConnectionPrivate) +{ + d->mLDAP = nullptr; + setServer(server); +} + +LdapConnection::~LdapConnection() +{ + close(); + delete d; +} + +void LdapConnection::setUrl(const LdapUrl &url) +{ + d->mServer.setUrl(url); +} + +void LdapConnection::setServer(const LdapServer &server) +{ + d->mServer = server; +} + +const LdapServer &LdapConnection::server() const +{ + return d->mServer; +} + +void *LdapConnection::handle() const +{ + return (void *)d->mLDAP; +} + +void *LdapConnection::saslHandle() const +{ + return (void *)d->mSASLconn; +} + +QString LdapConnection::errorString(int code) +{ + //No translated error messages yet +#ifdef LDAP_FOUND + return QString::fromUtf8(ldap_err2string(code)); +#else + return i18n("No LDAP Support..."); +#endif +} + +QString LdapConnection::saslErrorString() const +{ + const char *str; + str = sasl_errdetail(d->mSASLconn); + return QString::fromLocal8Bit(str); +} + +QString LdapConnection::connectionError() const +{ + return d->mConnectionError; +} + +#ifdef LDAP_FOUND +int LdapConnection::getOption(int option, void *value) const +{ + Q_ASSERT(d->mLDAP); + return ldap_get_option(d->mLDAP, option, value); +} + +int LdapConnection::setOption(int option, void *value) +{ + Q_ASSERT(d->mLDAP); + return ldap_set_option(d->mLDAP, option, value); +} + +int LdapConnection::ldapErrorCode() const +{ + Q_ASSERT(d->mLDAP); + int err; + ldap_get_option(d->mLDAP, LDAP_OPT_ERROR_NUMBER, &err); + return err; +} + +QString LdapConnection::ldapErrorString() const +{ + Q_ASSERT(d->mLDAP); + char *errmsg; + ldap_get_option(d->mLDAP, LDAP_OPT_ERROR_STRING, &errmsg); + QString msg = QString::fromLocal8Bit(errmsg); + free(errmsg); + return msg; +} + +bool LdapConnection::setSizeLimit(int sizelimit) +{ + Q_ASSERT(d->mLDAP); + qCDebug(LDAP_LOG) << "sizelimit:" << sizelimit; + if (setOption(LDAP_OPT_SIZELIMIT, &sizelimit) != LDAP_OPT_SUCCESS) { + return false; + } + return true; +} + +int LdapConnection::sizeLimit() const +{ + Q_ASSERT(d->mLDAP); + int sizelimit; + if (getOption(LDAP_OPT_SIZELIMIT, &sizelimit) != LDAP_OPT_SUCCESS) { + return -1; + } + return sizelimit; +} + +bool LdapConnection::setTimeLimit(int timelimit) +{ + Q_ASSERT(d->mLDAP); + qCDebug(LDAP_LOG) << "timelimit:" << timelimit; + if (setOption(LDAP_OPT_TIMELIMIT, &timelimit) != LDAP_OPT_SUCCESS) { + return false; + } + return true; +} + +int LdapConnection::timeLimit() const +{ + Q_ASSERT(d->mLDAP); + int timelimit; + if (getOption(LDAP_OPT_TIMELIMIT, &timelimit) != LDAP_OPT_SUCCESS) { + return -1; + } + return timelimit; +} + +int LdapConnection::connect() +{ + int ret; + QString url; + if (d->mLDAP) { + close(); + } + + int version = d->mServer.version(); + int timeout = d->mServer.timeout(); + + url = d->mServer.security() == LdapServer::SSL ? QStringLiteral("ldaps") : QStringLiteral("ldap"); + url += QLatin1String("://"); + url += d->mServer.host(); + url += QLatin1Char(':'); + url += QString::number(d->mServer.port()); + qCDebug(LDAP_LOG) << "ldap url:" << url; +#ifdef HAVE_LDAP_INITIALIZE + ret = ldap_initialize(&d->mLDAP, url.toLatin1().constData()); +#else + d->mLDAP = ldap_init(d->mServer.host().toLatin1().data(), d->mServer.port()); + if (d->mLDAP == 0) { + ret = -1; + } else { + ret = LDAP_SUCCESS; + } +#endif + if (ret != LDAP_SUCCESS) { + d->mConnectionError = i18n("An error occurred during the connection initialization phase."); + return ret; + } + + qCDebug(LDAP_LOG) << "setting version to:" << version; + if (setOption(LDAP_OPT_PROTOCOL_VERSION, &version) != LDAP_OPT_SUCCESS) { + ret = ldapErrorCode(); + d->mConnectionError = i18n("Cannot set protocol version to %1.", version); + close(); + return ret; + } + +#if defined(LDAP_OPT_TIMEOUT) + qCDebug(LDAP_LOG) << "setting timeout to:" << timeout; + + if (timeout) { + if (setOption(LDAP_OPT_TIMEOUT, &timeout) != LDAP_OPT_SUCCESS) { + ret = ldapErrorCode(); + d->mConnectionError = i18np("Cannot set timeout to %1 second.", + "Cannot set timeout to %1 seconds.", + timeout); + close(); + return ret; + } + } +#endif + + qCDebug(LDAP_LOG) << "setting security to:" << d->mServer.security(); + if (d->mServer.security() != LdapServer::None) { + bool initContext = false; + if (d->mServer.tlsCACertFile().isEmpty() == false) { + if (setOption(LDAP_OPT_X_TLS_CACERTFILE, d->mServer.tlsCACertFile().toUtf8().data()) != LDAP_OPT_SUCCESS) { + d->mConnectionError = i18n("Could not set CA certificate file."); + return -1; + } + initContext = true; + } + + if (d->mServer.tlsRequireCertificate() != LdapServer::TLSReqCertDefault) { + int reqcert; + switch (d->mServer.tlsRequireCertificate()) { + case LdapServer::TLSReqCertAllow: + reqcert = LDAP_OPT_X_TLS_ALLOW; + break; + case LdapServer::TLSReqCertDemand: + reqcert = LDAP_OPT_X_TLS_DEMAND; + break; + case LdapServer::TLSReqCertHard: + reqcert = LDAP_OPT_X_TLS_HARD; + break; + case LdapServer::TLSReqCertNever: + reqcert = LDAP_OPT_X_TLS_NEVER; + break; + case LdapServer::TLSReqCertTry: + reqcert = LDAP_OPT_X_TLS_TRY; + break; + default: + d->mConnectionError = i18n("Invalid TLS require certificate mode."); + return -1; + } + + if (setOption(LDAP_OPT_X_TLS_REQUIRE_CERT, &reqcert) != LDAP_OPT_SUCCESS) { + d->mConnectionError = i18n("Could not set TLS require certificate mode."); + return -1; + } + initContext = true; + } + + if (initContext) { + int isServer = 0; + if (setOption(LDAP_OPT_X_TLS_NEWCTX, &isServer) != LDAP_OPT_SUCCESS) { + d->mConnectionError = i18n("Could not initialize new TLS context."); + return -1; + } + } + } + + if (d->mServer.security() == LdapServer::TLS) { + qCDebug(LDAP_LOG) << "start TLS"; + +#ifdef HAVE_LDAP_START_TLS_S + if ((ret = ldap_start_tls_s(d->mLDAP, nullptr, nullptr)) != LDAP_SUCCESS) { + d->mConnectionError = ldapErrorString(); + close(); + return ret; + } +#else + close(); + d->mConnectionError = i18n("TLS support not available in the LDAP client libraries."); + return -1; +#endif + } + + qCDebug(LDAP_LOG) << "setting sizelimit to:" << d->mServer.sizeLimit(); + if (d->mServer.sizeLimit()) { + if (!setSizeLimit(d->mServer.sizeLimit())) { + ret = ldapErrorCode(); + close(); + d->mConnectionError = i18n("Cannot set size limit."); + return ret; + } + } + + qCDebug(LDAP_LOG) << "setting timelimit to:" << d->mServer.timeLimit(); + if (d->mServer.timeLimit()) { + if (!setTimeLimit(d->mServer.timeLimit())) { + ret = ldapErrorCode(); + close(); + d->mConnectionError = i18n("Cannot set time limit."); + return ret; + } + } + + qCDebug(LDAP_LOG) << "initializing SASL client"; + const int saslresult = sasl_client_new("ldap", d->mServer.host().toLatin1().constData(), + nullptr, nullptr, callbacks, 0, &d->mSASLconn); + if (saslresult != SASL_OK) { + d->mConnectionError = i18n("Cannot initialize the SASL client."); + return KLDAP_SASL_ERROR; + } + + return 0; +} + +void LdapConnection::close() +{ + if (d->mLDAP) { +#ifdef HAVE_LDAP_UNBIND_EXT + ldap_unbind_ext(d->mLDAP, nullptr, nullptr); +#else + ldap_unbind(d->mLDAP); +#endif + } + d->mLDAP = nullptr; + if (d->mSASLconn) { + sasl_dispose(&d->mSASLconn); + d->mSASLconn = nullptr; + } + qCDebug(LDAP_LOG) << "connection closed!"; +} + +#else //LDAP_FOUND + +int LdapConnection::getOption(int option, void *value) const +{ + qCritical() << "No LDAP support..."; + return -1; +} + +int LdapConnection::setOption(int option, void *value) +{ + qCritical() << "No LDAP support..."; + return -1; +} + +int LdapConnection::ldapErrorCode() const +{ + qCritical() << "No LDAP support..."; + return -1; +} + +QString LdapConnection::ldapErrorString() const +{ + qCritical() << "No LDAP support..."; + return QString(); +} + +bool LdapConnection::setSizeLimit(int sizelimit) +{ + qCritical() << "No LDAP support..."; + return false; +} + +int LdapConnection::sizeLimit() const +{ + qCritical() << "No LDAP support..."; + return -1; +} + +bool LdapConnection::setTimeLimit(int timelimit) +{ + qCritical() << "No LDAP support..."; + return false; +} + +int LdapConnection::timeLimit() const +{ + qCritical() << "No LDAP support..."; + return -1; +} + +int LdapConnection::connect() +{ + d->mConnectionError + = i18n("LDAP support not compiled in. Please recompile libkldap with the " + "OpenLDAP (or compatible) client libraries, or complain to your " + "distribution packagers."); + qCritical() << "No LDAP support..."; + return -1; +} + +void LdapConnection::close() +{ + qCritical() << "No LDAP support..."; +} + +#endif diff --git a/3rdparty/kldap/src/core/ldapconnection.h b/3rdparty/kldap/src/core/ldapconnection.h new file mode 100644 index 0000000..a87308f --- /dev/null +++ b/3rdparty/kldap/src/core/ldapconnection.h @@ -0,0 +1,130 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KLDAP_LDAPCONNECTION_H +#define KLDAP_LDAPCONNECTION_H + +#include + +#include "ldapurl.h" +#include "ldapserver.h" +#include "kldap_export.h" + +namespace KLDAP { +/** + * @brief + * This class represents a connection to an LDAP server. + */ +class KLDAP_EXPORT LdapConnection +{ +public: + + enum SASL_Fields { + SASL_Authname = 0x1, + SASL_Authzid = 0x2, + SASL_Realm = 0x4, + SASL_Password = 0x8 + }; + + /** Constructs an LdapConnection object */ + LdapConnection(); + /** Constructs an LdapConnection with the parameters given in url */ + explicit LdapConnection(const LdapUrl &url); + /** Constructs an LdapConnection with the parameters given in server */ + explicit LdapConnection(const LdapServer &server); + + ~LdapConnection(); + + /** + * Sets the connection parameters via the specified url. After this, + * you need to call connect() to connect with the new parameters. + * @param url the URL containing the connection parameters + */ + void setUrl(const LdapUrl &url); + /** + * Returns the connection parameters which was specified with an LDAP Url + * or a LdapServer structure. + */ + const LdapServer &server() const; + /** + * Sets the connection parameters via the specified server structure. After + * this, you need to call connect() to connect with the new parameters. + * @param server the server object containing the connection parameters + */ + void setServer(const LdapServer &server); + + /** + * Sets up the connection parameters with creating a handle to the LDAP server. + * Also sets sizelimit and timelimit and starts TLS if it is requested. + * Returns 0 if successful, else returns an LDAP error code, and an error + * string which is available via connectionError(). + */ + int connect(); + /** + * Returns a translated error string if connect() failed. + */ + Q_REQUIRED_RESULT QString connectionError() const; + /** + * Closes the LDAP connection. + */ + void close(); + + /** Sets the size limit for the connection. + * @param sizelimit the connection size limit to set + */ + Q_REQUIRED_RESULT bool setSizeLimit(int sizelimit); + /** Returns the current size limit. */ + Q_REQUIRED_RESULT int sizeLimit() const; + + /** Sets the time limit for the connection. + * @param timelimit the connection time limit to set + */ + Q_REQUIRED_RESULT bool setTimeLimit(int timelimit); + /** Returns the current time limit. */ + Q_REQUIRED_RESULT int timeLimit() const; + + /** Gets an option from the connection. The option value can be client + * library specific, so avoid this function if possible + * @param option the connection option to return + * @param value the value of option to get + */ + int getOption(int option, void *value) const; + /** Sets an option in the connection. The option value can be client + * library specific, so avoid this function if possible */ + int setOption(int option, void *value); + + /** Returns the LDAP error code from the last operation */ + Q_REQUIRED_RESULT int ldapErrorCode() const; + /** Returns the LDAP error string from the last operation */ + Q_REQUIRED_RESULT QString ldapErrorString() const; + /** Returns a translated error message from the specified LDAP error code */ + Q_REQUIRED_RESULT static QString errorString(int code); + + /** Returns the SASL error string from the last SASL operation */ + Q_REQUIRED_RESULT QString saslErrorString() const; + + /** + * Returns the opaqe client-library specific LDAP object. + * Avoid its usage if you can. + */ + void *handle() const; + + /** + * Returns the opaqe sasl-library specific SASL object. + * Avoid its usage if you can. + */ + void *saslHandle() const; + +private: + class LdapConnectionPrivate; + LdapConnectionPrivate *const d; + + Q_DISABLE_COPY(LdapConnection) +}; +} + +#endif diff --git a/3rdparty/kldap/src/core/ldapcontrol.cpp b/3rdparty/kldap/src/core/ldapcontrol.cpp new file mode 100644 index 0000000..a886935 --- /dev/null +++ b/3rdparty/kldap/src/core/ldapcontrol.cpp @@ -0,0 +1,144 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "ldapcontrol.h" +#include "ber.h" + +#include + +using namespace KLDAP; + +class LdapControlPrivate : public QSharedData +{ +public: + LdapControlPrivate() + { + } + + LdapControlPrivate(const LdapControlPrivate &other) + : QSharedData(other) + { + mOid = other.mOid; + mValue = other.mValue; + mCritical = other.mCritical; + } + + QString mOid; + QByteArray mValue; + bool mCritical = false; +}; + +LdapControl::LdapControl() + : d(new LdapControlPrivate) +{ + setControl(QString(), QByteArray(), false); +} + +LdapControl::LdapControl(const QString &oid, const QByteArray &value, bool critical) + : d(new LdapControlPrivate) +{ + setControl(oid, value, critical); +} + +LdapControl::LdapControl(const LdapControl &that) + : d(that.d) +{ + setControl(that.d->mOid, that.d->mValue, that.d->mCritical); +} + +LdapControl &LdapControl::operator=(const LdapControl &that) +{ + if (this != &that) { + d = that.d; + } + + setControl(that.d->mOid, that.d->mValue, that.d->mCritical); + + return *this; +} + +LdapControl::~LdapControl() +{ +} + +void LdapControl::setControl(const QString &oid, const QByteArray &value, bool critical) +{ + d->mOid = oid; + d->mValue = value; + d->mCritical = critical; +} + +QString LdapControl::oid() const +{ + return d->mOid; +} + +QByteArray LdapControl::value() const +{ + return d->mValue; +} + +bool LdapControl::critical() const +{ + return d->mCritical; +} + +void LdapControl::setOid(const QString &oid) +{ + d->mOid = oid; +} + +void LdapControl::setValue(const QByteArray &value) +{ + d->mValue = value; +} + +void LdapControl::setCritical(bool critical) +{ + d->mCritical = critical; +} + +int LdapControl::parsePageControl(QByteArray &cookie) const +{ + if (d->mOid != QLatin1String("1.2.840.113556.1.4.319")) { + return -1; + } + + Ber ber(d->mValue); + int size; + if (ber.scanf(QStringLiteral("{iO}"), &size, &cookie) == -1) { + return -1; + } else { + return size; + } +} + +LdapControl LdapControl::createPageControl(int pagesize, const QByteArray &cookie) +{ + LdapControl control; + Ber ber; + + ber.printf(QStringLiteral("{iO}"), pagesize, &cookie); + control.setOid(QStringLiteral("1.2.840.113556.1.4.319")); + control.setValue(ber.flatten()); + return control; +} + +void LdapControl::insert(LdapControls &list, const LdapControl &ctrl) +{ + LdapControls::iterator it; + LdapControls::iterator endit = list.end(); + const QString oid = ctrl.oid(); + + for (it = list.begin(); it != endit; ++it) { + if (it->oid() == oid) { + *it = ctrl; + return; + } + } + list.append(ctrl); +} diff --git a/3rdparty/kldap/src/core/ldapcontrol.h b/3rdparty/kldap/src/core/ldapcontrol.h new file mode 100644 index 0000000..578934f --- /dev/null +++ b/3rdparty/kldap/src/core/ldapcontrol.h @@ -0,0 +1,103 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KLDAP_LDAPCONTROL_H +#define KLDAP_LDAPCONTROL_H + +#include +#include +#include +class LdapControlPrivate; + +#include "kldap_export.h" + +// clazy:excludeall=copyable-polymorphic + +namespace KLDAP { +class LdapControl; +typedef QVector LdapControls; + +/** + @brief + This class represents an LDAP Control +*/ +class KLDAP_EXPORT LdapControl +{ +public: + /** + * Creates an empty control. + */ + LdapControl(); + /** + * Creates a control with the given OID, value and criticality. + */ + LdapControl(const QString &oid, const QByteArray &value, bool critical = false); + + LdapControl(const LdapControl &that); + LdapControl &operator=(const LdapControl &that); + /** + * Destroys the control object. + */ + ~LdapControl(); + /** + * Sets the control's OID, value and criticality. + */ + void setControl(const QString &oid, const QByteArray &value, bool critical = false); + /** + * Sets the control's OID. + */ + void setOid(const QString &oid); + /** + * Sets the control's value. + */ + void setValue(const QByteArray &value); + /** + * Sets the control's criticality. + */ + void setCritical(bool critical); + /** + * Returns the control's OID. + */ + Q_REQUIRED_RESULT QString oid() const; + /** + * Returns the control's value. + */ + Q_REQUIRED_RESULT QByteArray value() const; + /** + * Returns the control's criticality. + */ + Q_REQUIRED_RESULT bool critical() const; + + /** + * Parses a paging results control, which the server returned. + * Puts the server's cookie into @p cookie, and returns the estimated + * result set size. If the OID is not the page control's OID, or the + * value cannot be decoded, returns -1. + * @param cookie the cookie to hold server's cookie + */ + Q_REQUIRED_RESULT int parsePageControl(QByteArray &cookie) const; + /** + * Creates a paging search control. + */ + Q_REQUIRED_RESULT static LdapControl createPageControl(int pagesize, const QByteArray &cookie = QByteArray()); + + /** + * Inserts a unique control against a list of controls. + * If the control already exists in the list is is updated, otherwise + * it is appended to the list. + * @param list the current list of controls + * @param ctrl the control to insert + * @since 4.4 + */ + static void insert(LdapControls &list, const LdapControl &ctrl); + +private: + QSharedDataPointer d; +}; +} + +#endif diff --git a/3rdparty/kldap/src/core/ldapdefs.h b/3rdparty/kldap/src/core/ldapdefs.h new file mode 100644 index 0000000..03a0d82 --- /dev/null +++ b/3rdparty/kldap/src/core/ldapdefs.h @@ -0,0 +1,148 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KLDAP_DEFS_H +#define KLDAP_DEFS_H + +/** + * LDAP Error codes. + * These codes taken from openldap's ldap.h, and prefixed with KLDAP_ + * instead of LDAP_, just for applications which uses the kldap library + * doesn't need to include openldap headers + */ + +#define KLDAP_SUCCESS 0x00 + +#define KLDAP_RANGE(n, x, y) (((x) <= (n)) && ((n) <= (y))) + +#define KLDAP_OPERATIONS_ERROR 0x01 +#define KLDAP_PROTOCOL_ERROR 0x02 +#define KLDAP_TIMELIMIT_EXCEEDED 0x03 +#define KLDAP_SIZELIMIT_EXCEEDED 0x04 +#define KLDAP_COMPARE_FALSE 0x05 +#define KLDAP_COMPARE_TRUE 0x06 +#define KLDAP_AUTH_METHOD_NOT_SUPPORTED 0x07 +#define KLDAP_STRONG_AUTH_NOT_SUPPORTED KLDAP_AUTH_METHOD_NOT_SUPPORTED +#define KLDAP_STRONG_AUTH_REQUIRED 0x08 +#define KLDAP_STRONGER_AUTH_REQUIRED KLDAP_STRONG_AUTH_REQUIRED +#define KLDAP_PARTIAL_RESULTS 0x09 /* LDAPv2+ (not LDAPv3) */ + +#define KLDAP_REFERRAL 0x0a /* LDAPv3 */ +#define KLDAP_ADMINLIMIT_EXCEEDED 0x0b /* LDAPv3 */ +#define KLDAP_UNAVAILABLE_CRITICAL_EXTENSION 0x0c /* LDAPv3 */ +#define KLDAP_CONFIDENTIALITY_REQUIRED 0x0d /* LDAPv3 */ +#define KLDAP_SASL_BIND_IN_PROGRESS 0x0e /* LDAPv3 */ + +#define KLDAP_ATTR_ERROR(n) KLDAP_RANGE((n), 0x10, 0x15) /* 16-21 */ + +#define KLDAP_NO_SUCH_ATTRIBUTE 0x10 +#define KLDAP_UNDEFINED_TYPE 0x11 +#define KLDAP_INAPPROPRIATE_MATCHING 0x12 +#define KLDAP_CONSTRAINT_VIOLATION 0x13 +#define KLDAP_TYPE_OR_VALUE_EXISTS 0x14 +#define KLDAP_INVALID_SYNTAX 0x15 + +#define KLDAP_NAME_ERROR(n) KLDAP_RANGE((n), 0x20, 0x24) /* 32-34,36 */ + +#define KLDAP_NO_SUCH_OBJECT 0x20 +#define KLDAP_ALIAS_PROBLEM 0x21 +#define KLDAP_INVALID_DN_SYNTAX 0x22 +#define KLDAP_IS_LEAF 0x23 /* not LDAPv3 */ +#define KLDAP_ALIAS_DEREF_PROBLEM 0x24 + +#define KLDAP_SECURITY_ERROR(n) KLDAP_RANGE((n), 0x2F, 0x32) /* 47-50 */ + +#define KLDAP_PROXY_AUTHZ_FAILURE 0x2F /* LDAPv3 proxy authorization */ +#define KLDAP_INAPPROPRIATE_AUTH 0x30 +#define KLDAP_INVALID_CREDENTIALS 0x31 +#define KLDAP_INSUFFICIENT_ACCESS 0x32 + +#define KLDAP_SERVICE_ERROR(n) KLDAP_RANGE((n), 0x33, 0x36) /* 51-54 */ + +#define KLDAP_BUSY 0x33 +#define KLDAP_UNAVAILABLE 0x34 +#define KLDAP_UNWILLING_TO_PERFORM 0x35 +#define KLDAP_LOOP_DETECT 0x36 + +#define KLDAP_UPDATE_ERROR(n) KLDAP_RANGE((n), 0x40, 0x47) /* 64-69,71 */ + +#define KLDAP_NAMING_VIOLATION 0x40 +#define KLDAP_OBJECT_CLASS_VIOLATION 0x41 +#define KLDAP_NOT_ALLOWED_ON_NONLEAF 0x42 +#define KLDAP_NOT_ALLOWED_ON_RDN 0x43 +#define KLDAP_ALREADY_EXISTS 0x44 +#define KLDAP_NO_OBJECT_CLASS_MODS 0x45 +#define KLDAP_RESULTS_TOO_LARGE 0x46 /* CLDAP */ +#define KLDAP_AFFECTS_MULTIPLE_DSAS 0x47 + +#define KLDAP_OTHER 0x50 + +/* LCUP operation codes (113-117) - not implemented */ +#define KLDAP_CUP_RESOURCES_EXHAUSTED 0x71 +#define KLDAP_CUP_SECURITY_VIOLATION 0x72 +#define KLDAP_CUP_INVALID_DATA 0x73 +#define KLDAP_CUP_UNSUPPORTED_SCHEME 0x74 +#define KLDAP_CUP_RELOAD_REQUIRED 0x75 + +/* Cancel operation codes (118-121) */ +#define KLDAP_CANCELLED 0x76 +#define KLDAP_NO_SUCH_OPERATION 0x77 +#define KLDAP_TOO_LATE 0x78 + +#define KLDAP_CANNOT_CANCEL 0x79 + +/* Assertion control (122) */ +#define KLDAP_ASSERTION_FAILED 0x7A + +/* Experimental result codes */ +#define KLDAP_E_ERROR(n) KLDAP_RANGE((n), 0x1000, 0x3FFF) + +/* LDAP Sync (4096) */ +#define KLDAP_SYNC_REFRESH_REQUIRED 0x1000 + +/* Private Use result codes */ +#define KLDAP_X_ERROR(n) KLDAP_RANGE((n), 0x4000, 0xFFFF) + +#define KLDAP_X_SYNC_REFRESH_REQUIRED 0x4100 /* defunct */ +#define KLDAP_X_ASSERTION_FAILED 0x410f /* defunct */ + +/* for the LDAP No-Op control */ +#define KLDAP_X_NO_OPERATION 0x410e + +/** API Error Codes + * + * Based on draft-ietf-ldap-c-api-xx + * but with new negative code values + */ +#define KLDAP_API_ERROR(n) ((n) < 0) +#define KLDAP_API_RESULT(n) ((n) <= 0) + +#define KLDAP_SERVER_DOWN (-1) +#define KLDAP_LOCAL_ERROR (-2) +#define KLDAP_ENCODING_ERROR (-3) +#define KLDAP_DECODING_ERROR (-4) +#define KLDAP_TIMEOUT (-5) +#define KLDAP_AUTH_UNKNOWN (-6) +#define KLDAP_FILTER_ERROR (-7) +#define KLDAP_USER_CANCELLED (-8) +#define KLDAP_PARAM_ERROR (-9) +#define KLDAP_NO_MEMORY (-10) +#define KLDAP_CONNECT_ERROR (-11) +#define KLDAP_NOT_SUPPORTED (-12) +#define KLDAP_CONTROL_NOT_FOUND (-13) +#define KLDAP_NO_RESULTS_RETURNED (-14) +#define KLDAP_MORE_RESULTS_TO_RETURN (-15) /* Obsolete */ +#define KLDAP_CLIENT_LOOP (-16) +#define KLDAP_REFERRAL_LIMIT_EXCEEDED (-17) + +/* + * KLDAP Specific + */ + +#define KLDAP_SASL_ERROR -0xff + +#endif //KLDAP_DEFS_H diff --git a/3rdparty/kldap/src/core/ldapdn.cpp b/3rdparty/kldap/src/core/ldapdn.cpp new file mode 100644 index 0000000..7b0d309 --- /dev/null +++ b/3rdparty/kldap/src/core/ldapdn.cpp @@ -0,0 +1,201 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2006 Sean Harmer + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "ldapdn.h" + +#include + +#include "ldap_debug.h" + +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapDN::LdapDNPrivate +{ +public: + LdapDNPrivate() : m_dn() + { + } + + ~LdapDNPrivate() + { + } + + bool isValidRDNString(const QString &rdn) const; + QStringList splitOnNonEscapedChar(const QString &rdn, QChar ch) const; + + QString m_dn; +}; + +bool LdapDN::LdapDNPrivate::isValidRDNString(const QString &rdn) const +{ + qCDebug(LDAP_LOG) << "Testing rdn:" << rdn; + + // If it is a muli-valued rdn, split it into its constituent parts + const QStringList rdnParts = splitOnNonEscapedChar(rdn, QLatin1Char('+')); + const int rdnPartsSize(rdnParts.size()); + if (rdnPartsSize > 1) { + for (int i = 0; i < rdnPartsSize; i++) { + if (!isValidRDNString(rdnParts.at(i))) { + return false; + } + } + return true; + } + + // Split the rdn into the attribute name and value parts + const QVector components = rdn.splitRef(QLatin1Char('=')); + + // We should have exactly two parts + if (components.size() != 2) { + return false; + } + + return true; +} + +QStringList LdapDN::LdapDNPrivate::splitOnNonEscapedChar(const QString &str, QChar ch) const +{ + QStringList strParts; + int index = 0; + int searchFrom = 0; + int strPartStartIndex = 0; + while ((index = str.indexOf(ch, searchFrom)) != -1) { + const QChar prev = str[std::max(0, index - 1)]; + if (prev != QLatin1Char('\\')) { + // Found a component of a multi-valued RDN + //qCDebug(LDAP_LOG) << "Found" << ch << "at index" << index; + QString tmp = str.mid(strPartStartIndex, index - strPartStartIndex); + //qCDebug(LDAP_LOG) << "Adding part:" << tmp; + strParts.append(tmp); + strPartStartIndex = index + 1; + } + + searchFrom = index + 1; + } + + // Add on the part after the last found delimeter + QString tmp = str.mid(strPartStartIndex); + //qCDebug(LDAP_LOG) << "Adding part:" << tmp; + strParts.append(tmp); + + return strParts; +} + +LdapDN::LdapDN() + : d(new LdapDNPrivate) +{ +} + +LdapDN::LdapDN(const QString &dn) + : d(new LdapDNPrivate) +{ + d->m_dn = dn; +} + +LdapDN::LdapDN(const LdapDN &that) + : d(new LdapDNPrivate) +{ + *d = *that.d; +} + +LdapDN &LdapDN::operator=(const LdapDN &that) +{ + if (this == &that) { + return *this; + } + + *d = *that.d; + return *this; +} + +LdapDN::~LdapDN() +{ + delete d; +} + +void LdapDN::clear() +{ + d->m_dn.clear(); +} + +bool LdapDN::isEmpty() const +{ + return d->m_dn.isEmpty(); +} + +QString LdapDN::toString() const +{ + return d->m_dn; +} + +QString LdapDN::toString(int depth) const +{ + const QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(',')); + if (depth >= rdns.size()) { + return QString(); + } + + // Construct a DN down to the requested depth + QString dn; + for (int i = depth; i >= 0; i--) { + dn += rdns.at(rdns.size() - 1 - i) + QLatin1Char(','); + qCDebug(LDAP_LOG) << "dn =" << dn; + } + dn.chop(1); // Strip off the extraneous comma + + return dn; +} + +QString LdapDN::rdnString() const +{ + /** \TODO We should move this into the d pointer as we calculate rdns quite a lot */ + const QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(',')); + return rdns.at(0); +} + +QString LdapDN::rdnString(int depth) const +{ + const QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(',')); + if (depth >= rdns.size()) { + return QString(); + } + return rdns.at(rdns.size() - 1 - depth); +} + +bool LdapDN::isValid() const +{ + qCDebug(LDAP_LOG) << "Testing dn:" << d->m_dn; + + // Break the string into rdn's + const QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(',')); + + // Test to see if each rdn is valid + const int rdnsSize(rdns.size()); + for (int i = 0; i < rdnsSize; i++) { + if (!d->isValidRDNString(rdns.at(i))) { + return false; + } + } + + return true; +} + +int LdapDN::depth() const +{ + const QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(',')); + return rdns.size(); +} + +bool LdapDN::operator ==(const LdapDN &rhs) const +{ + return d->m_dn == rhs.d->m_dn; +} + +bool LdapDN::operator !=(const LdapDN &rhs) const +{ + return d->m_dn != rhs.d->m_dn; +} diff --git a/3rdparty/kldap/src/core/ldapdn.h b/3rdparty/kldap/src/core/ldapdn.h new file mode 100644 index 0000000..2b5e907 --- /dev/null +++ b/3rdparty/kldap/src/core/ldapdn.h @@ -0,0 +1,72 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2006 Sean Harmer + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KLDAP_LDAPDN_H +#define KLDAP_LDAPDN_H + +#include "kldap_export.h" +#include + +namespace KLDAP { +class KLDAP_EXPORT LdapDN +{ +public: + explicit LdapDN(); + explicit LdapDN(const QString &dn); + + LdapDN(const LdapDN &that); + LdapDN &operator=(const LdapDN &that); + + ~LdapDN(); + + void clear(); + + bool isEmpty() const; + + /** + * \returns A QString representing the DN. + */ + Q_REQUIRED_RESULT QString toString() const; + + /** + * \param depth The depth of the DN to return using a zero-based index. + * \returns A QString representing the DN levels deep in the directory. + */ + Q_REQUIRED_RESULT QString toString(int depth) const; + + /** + * \returns A QString representing the RDN of this DN. + */ + Q_REQUIRED_RESULT QString rdnString() const; + + /** + * \param depth The depth of the RDN to return using a zero-based index. + * \returns A QString representing the RDN levels deep in the directory. + */ + Q_REQUIRED_RESULT QString rdnString(int depth) const; + + /** + * \returns True if this is a valid DN, false otherwise. + */ + Q_REQUIRED_RESULT bool isValid() const; + + /** + * \returns The depth of this DN in the directory. + */ + Q_REQUIRED_RESULT int depth() const; + + Q_REQUIRED_RESULT bool operator ==(const LdapDN &rhs) const; + + Q_REQUIRED_RESULT bool operator !=(const LdapDN &rhs) const; + +private: + class LdapDNPrivate; + LdapDNPrivate *const d; +}; +} + +#endif diff --git a/3rdparty/kldap/src/core/ldapobject.cpp b/3rdparty/kldap/src/core/ldapobject.cpp new file mode 100644 index 0000000..a56ba0e --- /dev/null +++ b/3rdparty/kldap/src/core/ldapobject.cpp @@ -0,0 +1,138 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "ldapobject.h" +#include "ldif.h" + +#include + +using namespace KLDAP; + +class LdapObjectPrivate : public QSharedData +{ +public: + LdapObjectPrivate() + { + } + + LdapObjectPrivate(const LdapObjectPrivate &other) + : QSharedData(other) + { + mDn = other.mDn; + mAttrs = other.mAttrs; + } + + LdapDN mDn; + LdapAttrMap mAttrs; +}; + +LdapObject::LdapObject() + : d(new LdapObjectPrivate) +{ +} + +LdapObject::LdapObject(const QString &dn) + : d(new LdapObjectPrivate) +{ + d->mDn = LdapDN(dn); +} + +LdapObject::~LdapObject() +{ +} + +LdapObject::LdapObject(const LdapObject &that) + : d(that.d) +{ +} + +LdapObject &LdapObject::operator=(const LdapObject &that) +{ + if (this != &that) { + d = that.d; + } + + return *this; +} + +void LdapObject::setDn(const LdapDN &dn) +{ + d->mDn = dn; +} + +void LdapObject::setDn(const QString &dn) +{ + d->mDn = LdapDN(dn); +} + +void LdapObject::setAttributes(const LdapAttrMap &attrs) +{ + d->mAttrs = attrs; +} + +LdapDN LdapObject::dn() const +{ + return d->mDn; +} + +const LdapAttrMap &LdapObject::attributes() const +{ + return d->mAttrs; +} + +QString LdapObject::toString() const +{ + QString result = QStringLiteral("dn: %1\n").arg(d->mDn.toString()); + LdapAttrMap::ConstIterator end(d->mAttrs.constEnd()); + for (LdapAttrMap::ConstIterator it = d->mAttrs.constBegin(); it != end; ++it) { + const QString attr = it.key(); + LdapAttrValue::ConstIterator end2((*it).constEnd()); + for (LdapAttrValue::ConstIterator it2 = (*it).constBegin(); it2 != end2; ++it2) { + result += QString::fromUtf8(Ldif::assembleLine(attr, *it2, 76)) + QLatin1Char('\n'); + } + } + return result; +} + +void LdapObject::clear() +{ + d->mDn.clear(); + d->mAttrs.clear(); +} + +void LdapObject::setValues(const QString &attributeName, const LdapAttrValue &values) +{ + d->mAttrs[ attributeName ] = values; +} + +void LdapObject::addValue(const QString &attributeName, const QByteArray &value) +{ + d->mAttrs[ attributeName ].append(value); +} + +LdapAttrValue LdapObject::values(const QString &attributeName) const +{ + if (hasAttribute(attributeName)) { + return d->mAttrs.value(attributeName); + } else { + return LdapAttrValue(); + } +} + +QByteArray LdapObject::value(const QString &attributeName) const +{ + if (hasAttribute(attributeName)) { + return d->mAttrs.value(attributeName).first(); + } else { + return QByteArray(); + } +} + +bool LdapObject::hasAttribute(const QString &attributeName) const +{ + return d->mAttrs.contains(attributeName); +} diff --git a/3rdparty/kldap/src/core/ldapobject.h b/3rdparty/kldap/src/core/ldapobject.h new file mode 100644 index 0000000..1299d3e --- /dev/null +++ b/3rdparty/kldap/src/core/ldapobject.h @@ -0,0 +1,104 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KLDAP_LDAPOBJECT_H +#define KLDAP_LDAPOBJECT_H + +#include +#include +#include +#include +class LdapObjectPrivate; + +#include "ldapdn.h" +#include "kldap_export.h" + +// clazy:excludeall=copyable-polymorphic + +namespace KLDAP { +typedef QList LdapAttrValue; +typedef QMap LdapAttrMap; + +/** + * @brief + * This class represents an LDAP Object +*/ +class KLDAP_EXPORT LdapObject +{ +public: + LdapObject(); + explicit LdapObject(const QString &dn); + ~LdapObject(); + + LdapObject(const LdapObject &that); + LdapObject &operator=(const LdapObject &that); + + /** + * Returns the text presentation (LDIF format) of the object. + */ + Q_REQUIRED_RESULT QString toString() const; + + /** + * Clears the name and attributes of the object. + */ + void clear(); + /** + * Sets the Distinguished Name of the object. + */ + void setDn(const LdapDN &dn); + /** + * Sets the Distinguished Name of the object. + */ + void setDn(const QString &dn); + /** + * Sets the attributes and attribute values of the object. + */ + void setAttributes(const LdapAttrMap &attrs); + /** + * Sets the given attribute values. If the given attribute not exists, + * then it's created, if exists, it's overwritten. + * @param attributeName the attribute name for which to set values + * @param values the values of attribute to set + */ + void setValues(const QString &attributeName, const LdapAttrValue &values); + /** + * Adds the given value to the specified attribute. If the given attribute + * not exists, then it's created. + * @param attributeName the attribute for which to add a value + * @param value the attribute value to add + */ + void addValue(const QString &attributeName, const QByteArray &value); + /** + * Return the Distinguished Name of the object. + */ + Q_REQUIRED_RESULT LdapDN dn() const; + /** + * Returns the attributes and their values. + */ + const LdapAttrMap &attributes() const; + /** + * Returns all values of the attribute with the given name. + */ + Q_REQUIRED_RESULT LdapAttrValue values(const QString &attributeName) const; + /** + * Returns the first value of the attribute with the given name + * or an empty byte array if the attribute does not exists. + */ + Q_REQUIRED_RESULT QByteArray value(const QString &attributeName) const; + /** + * Returns true if the given attributethe exists, false otherwise. + */ + Q_REQUIRED_RESULT bool hasAttribute(const QString &attributeName) const; + +private: + QSharedDataPointer d; +}; + +typedef QVector LdapObjects; +} + +#endif diff --git a/3rdparty/kldap/src/core/ldapoperation.cpp b/3rdparty/kldap/src/core/ldapoperation.cpp new file mode 100644 index 0000000..3cd29a1 --- /dev/null +++ b/3rdparty/kldap/src/core/ldapoperation.cpp @@ -0,0 +1,1298 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "ldapoperation.h" +#include "kldap_config.h" + +#include "ldap_debug.h" + +#include + +#include + +//for struct timeval +#if defined(HAVE_SYS_TIME_H) +# include +#elif defined(_WIN32) +# include +#endif + +#include + +#ifdef LDAP_FOUND +# ifndef HAVE_WINLDAP_H +# include +# include +# else +# include +# endif // HAVE_WINLDAP_H +#endif // LDAP_FOUND + +#include "ldapdefs.h" + +using namespace KLDAP; + +#ifdef LDAP_FOUND +static void extractControls(LdapControls &ctrls, LDAPControl **pctrls); +#endif // LDAP_FOUND + +/* + Returns the difference between msecs and elapsed. If msecs is -1, + however, -1 is returned. +*/ +static int kldap_timeout_value(int msecs, int elapsed) +{ + if (msecs == -1) { + return -1; + } + + int timeout = msecs - elapsed; + return timeout < 0 ? 0 : timeout; +} + +class Q_DECL_HIDDEN LdapOperation::LdapOperationPrivate +{ +public: + LdapOperationPrivate(); + ~LdapOperationPrivate(); +#ifdef LDAP_FOUND + int processResult(int rescode, LDAPMessage *msg); + int bind(const QByteArray &creds, SASL_Callback_Proc *saslproc, void *data, bool async); +#endif + LdapControls mClientCtrls, mServerCtrls, mControls; + LdapObject mObject; + QByteArray mExtOid, mExtData; + QByteArray mServerCred; + QString mMatchedDn; + QList mReferrals; + + LdapConnection *mConnection = nullptr; +}; + +LdapOperation::LdapOperation() + : d(new LdapOperationPrivate) +{ + d->mConnection = nullptr; +} + +LdapOperation::LdapOperation(LdapConnection &conn) + : d(new LdapOperationPrivate) +{ + setConnection(conn); +} + +LdapOperation::~LdapOperation() +{ + delete d; +} + +void LdapOperation::setConnection(LdapConnection &conn) +{ + d->mConnection = &conn; +} + +LdapConnection &LdapOperation::connection() +{ + return *d->mConnection; +} + +void LdapOperation::setClientControls(const LdapControls &ctrls) +{ + d->mClientCtrls = ctrls; +} + +void LdapOperation::setServerControls(const LdapControls &ctrls) +{ + d->mServerCtrls = ctrls; +} + +LdapControls LdapOperation::clientControls() const +{ + return d->mClientCtrls; +} + +LdapControls LdapOperation::serverControls() const +{ + return d->mServerCtrls; +} + +LdapObject LdapOperation::object() const +{ + return d->mObject; +} + +LdapControls LdapOperation::controls() const +{ + return d->mControls; +} + +QByteArray LdapOperation::extendedOid() const +{ + return d->mExtOid; +} + +QByteArray LdapOperation::extendedData() const +{ + return d->mExtData; +} + +QString LdapOperation::matchedDn() const +{ + return d->mMatchedDn; +} + +QList LdapOperation::referrals() const +{ + return d->mReferrals; +} + +QByteArray LdapOperation::serverCred() const +{ + return d->mServerCred; +} + +LdapOperation::LdapOperationPrivate::LdapOperationPrivate() +{ +} + +LdapOperation::LdapOperationPrivate::~LdapOperationPrivate() +{ +} + +#ifdef LDAP_FOUND + +static int kldap_sasl_interact(sasl_interact_t *interact, LdapOperation::SASL_Data *data) +{ + if (data->proc) { + for (; interact->id != SASL_CB_LIST_END; interact++) { + switch (interact->id) { + case SASL_CB_GETREALM: + data->creds.fields |= LdapOperation::SASL_Realm; + break; + case SASL_CB_AUTHNAME: + data->creds.fields |= LdapOperation::SASL_Authname; + break; + case SASL_CB_PASS: + data->creds.fields |= LdapOperation::SASL_Password; + break; + case SASL_CB_USER: + data->creds.fields |= LdapOperation::SASL_Authzid; + break; + } + } + int retval; + if ((retval = data->proc(data->creds, data->data))) { + return retval; + } + } + + QString value; + + while (interact->id != SASL_CB_LIST_END) { + value.clear(); + switch (interact->id) { + case SASL_CB_GETREALM: + value = data->creds.realm; + qCDebug(LDAP_LOG) << "SASL_REALM=" << value; + break; + case SASL_CB_AUTHNAME: + value = data->creds.authname; + qCDebug(LDAP_LOG) << "SASL_AUTHNAME=" << value; + break; + case SASL_CB_PASS: + value = data->creds.password; + qCDebug(LDAP_LOG) << "SASL_PASSWD=[hidden]"; + break; + case SASL_CB_USER: + value = data->creds.authzid; + qCDebug(LDAP_LOG) << "SASL_AUTHZID=" << value; + break; + } + if (value.isEmpty()) { + interact->result = nullptr; + interact->len = 0; + } else { + interact->result = strdup(value.toUtf8().constData()); + interact->len = strlen((const char *)interact->result); + } + interact++; + } + return KLDAP_SUCCESS; +} + +int LdapOperation::LdapOperationPrivate::bind(const QByteArray &creds, SASL_Callback_Proc *saslproc, void *data, bool async) +{ + Q_ASSERT(mConnection); + LDAP *ld = (LDAP *)mConnection->handle(); + LdapServer server; + server = mConnection->server(); + + int ret; + + if (server.auth() == LdapServer::SASL) { +#if !defined(HAVE_WINLDAP_H) + auto *saslconn = (sasl_conn_t *)mConnection->saslHandle(); + sasl_interact_t *client_interact = nullptr; + const char *out = nullptr; + uint outlen; + const char *mechusing = nullptr; + struct berval ccred, *scred; + int saslresult; + QByteArray sdata = creds; + + QString mech = server.mech(); + if (mech.isEmpty()) { + mech = QStringLiteral("DIGEST-MD5"); + } + + SASL_Data sasldata; + sasldata.proc = saslproc; + sasldata.data = data; + sasldata.creds.fields = 0; + sasldata.creds.realm = server.realm(); + sasldata.creds.authname = server.user(); + sasldata.creds.authzid = server.bindDn(); + sasldata.creds.password = server.password(); + + do { + if (sdata.isEmpty()) { + do { + saslresult = sasl_client_start(saslconn, mech.toLatin1().constData(), + &client_interact, &out, &outlen, &mechusing); + + if (saslresult == SASL_INTERACT) { + if (kldap_sasl_interact(client_interact, &sasldata) != KLDAP_SUCCESS) { + return KLDAP_SASL_ERROR; + } + } + qCDebug(LDAP_LOG) << "sasl_client_start mech: " + << mechusing << " outlen " << outlen + << " result: " << saslresult; + } while (saslresult == SASL_INTERACT); + if (saslresult != SASL_CONTINUE && saslresult != SASL_OK) { + return KLDAP_SASL_ERROR; + } + } else { + qCDebug(LDAP_LOG) << "sasl_client_step"; + do { + saslresult = sasl_client_step(saslconn, sdata.data(), sdata.size(), + &client_interact, &out, &outlen); + if (saslresult == SASL_INTERACT) { + if (kldap_sasl_interact(client_interact, &sasldata) != KLDAP_SUCCESS) { + return KLDAP_SASL_ERROR; + } + } + } while (saslresult == SASL_INTERACT); + qCDebug(LDAP_LOG) << "sasl_client_step result" << saslresult; + if (saslresult != SASL_CONTINUE && saslresult != SASL_OK) { + return KLDAP_SASL_ERROR; + } + } + + ccred.bv_val = (char *)out; + ccred.bv_len = outlen; + + if (async) { + qCDebug(LDAP_LOG) << "ldap_sasl_bind"; + int msgid; + ret + = ldap_sasl_bind(ld, server.bindDn().toUtf8().constData(), mech.toLatin1().constData(), + &ccred, nullptr, nullptr, &msgid); + if (ret == 0) { + ret = msgid; + } + qCDebug(LDAP_LOG) << "ldap_sasl_bind msgid" << ret; + } else { + qCDebug(LDAP_LOG) << "ldap_sasl_bind_s"; + ret + = ldap_sasl_bind_s(ld, server.bindDn().toUtf8().constData(), mech.toLatin1().constData(), + &ccred, nullptr, nullptr, &scred); + qCDebug(LDAP_LOG) << "ldap_sasl_bind_s ret" << ret; + if (scred) { + sdata = QByteArray(scred->bv_val, scred->bv_len); + } else { + sdata = QByteArray(); + } + } + } while (!async && ret == KLDAP_SASL_BIND_IN_PROGRESS); +#else + qCritical() << "SASL authentication is not available " + << "(re-compile kldap with cyrus-sasl and OpenLDAP development)."; + return KLDAP_SASL_ERROR; +#endif + } else { //simple auth + QByteArray bindname, pass; + struct berval ccred; + if (server.auth() == LdapServer::Simple) { + bindname = server.bindDn().toUtf8(); + pass = server.password().toUtf8(); + } + ccred.bv_val = pass.data(); + ccred.bv_len = pass.size(); + qCDebug(LDAP_LOG) << "binding to server, bindname: " << bindname << " password: *****"; + + if (async) { + qCDebug(LDAP_LOG) << "ldap_sasl_bind (simple)"; +#ifndef HAVE_WINLDAP_H + int msgid = 0; + ret = ldap_sasl_bind(ld, bindname.data(), nullptr, &ccred, nullptr, nullptr, &msgid); + if (ret == 0) { + ret = msgid; + } +#else + ret = ldap_simple_bind(ld, bindname.data(), pass.data()); +#endif + } else { + qCDebug(LDAP_LOG) << "ldap_sasl_bind_s (simple)"; +#ifndef HAVE_WINLDAP_H + ret = ldap_sasl_bind_s(ld, bindname.data(), nullptr, &ccred, nullptr, nullptr, nullptr); +#else + ret = ldap_simple_bind_s(ld, bindname.data(), pass.data()); +#endif + } + } + return ret; +} + +int LdapOperation::LdapOperationPrivate::processResult(int rescode, LDAPMessage *msg) +{ + //qCDebug(LDAP_LOG); + int retval; + LDAP *ld = (LDAP *)mConnection->handle(); + + qCDebug(LDAP_LOG) << "rescode: " << rescode; + switch (rescode) { + case RES_SEARCH_ENTRY: + { + //qCDebug(LDAP_LOG) << "Found search entry"; + mObject.clear(); + LdapAttrMap attrs; + char *name; + struct berval **bvals; + BerElement *entry; + LdapAttrValue values; + + char *dn = ldap_get_dn(ld, msg); + mObject.setDn(QString::fromUtf8(dn)); + ldap_memfree(dn); + + // iterate over the attributes + name = ldap_first_attribute(ld, msg, &entry); + while (name != nullptr) { + // print the values + bvals = ldap_get_values_len(ld, msg, name); + if (bvals) { + for (int i = 0; bvals[i] != nullptr; i++) { + char *val = bvals[i]->bv_val; + unsigned long len = bvals[i]->bv_len; + values.append(QByteArray(val, len)); + } + ldap_value_free_len(bvals); + } + attrs[ QString::fromLatin1(name) ] = values; + values.clear(); + ldap_memfree(name); + + // next attribute + name = ldap_next_attribute(ld, msg, entry); + } + ber_free(entry, 0); + mObject.setAttributes(attrs); + break; + } + case RES_SEARCH_REFERENCE: + // Will only get this if following references is disabled. ignore it + rescode = 0; + break; + case RES_EXTENDED: + { + char *retoid; + struct berval *retdata; + retval = ldap_parse_extended_result(ld, msg, &retoid, &retdata, 0); + if (retval != KLDAP_SUCCESS) { + ldap_msgfree(msg); + return -1; + } + mExtOid = retoid ? QByteArray(retoid) : QByteArray(); + mExtData = retdata ? QByteArray(retdata->bv_val, retdata->bv_len) : QByteArray(); + ldap_memfree(retoid); + ber_bvfree(retdata); + break; + } + case RES_BIND: + { + struct berval *servercred = nullptr; +#ifndef HAVE_WINLDAP_H + // FIXME: Error handling Winldap does not have ldap_parse_sasl_bind_result + retval = ldap_parse_sasl_bind_result(ld, msg, &servercred, 0); +#else + retval = KLDAP_SUCCESS; +#endif + if (retval != KLDAP_SUCCESS && retval != KLDAP_SASL_BIND_IN_PROGRESS) { + qCDebug(LDAP_LOG) << "RES_BIND error: " << retval; + ldap_msgfree(msg); + return -1; + } + qCDebug(LDAP_LOG) << "RES_BIND rescode" << rescode << "retval:" << retval; + if (servercred) { + mServerCred = QByteArray(servercred->bv_val, servercred->bv_len); + ber_bvfree(servercred); + } else { + mServerCred = QByteArray(); + } + break; + } + default: + { + LDAPControl **serverctrls = nullptr; + char *matcheddn = nullptr, *errmsg = nullptr; + char **referralsp; + int errcodep; + retval + = ldap_parse_result(ld, msg, &errcodep, &matcheddn, &errmsg, &referralsp, + &serverctrls, 0); + qCDebug(LDAP_LOG) << "rescode" << rescode << "retval:" << retval + << "matcheddn:" << matcheddn << "errcode:" + << errcodep << "errmsg:" << errmsg; + if (retval != KLDAP_SUCCESS) { + ldap_msgfree(msg); + return -1; + } + mControls.clear(); + if (serverctrls) { + extractControls(mControls, serverctrls); + ldap_controls_free(serverctrls); + } + mReferrals.clear(); + if (referralsp) { + char **tmp = referralsp; + while (*tmp) { + mReferrals.append(QByteArray(*tmp)); + ldap_memfree(*tmp); + tmp++; + } + ldap_memfree((char *)referralsp); + } + mMatchedDn.clear(); + if (matcheddn) { + mMatchedDn = QString::fromUtf8(matcheddn); + ldap_memfree(matcheddn); + } + if (errmsg) { + ldap_memfree(errmsg); + } + } + } + + ldap_msgfree(msg); + + return rescode; +} + +static void addModOp(LDAPMod ***pmods, int mod_type, const QString &attr, const QByteArray *value = nullptr) +{ + // qCDebug(LDAP_LOG) << "type:" << mod_type << "attr:" << attr << + // "value:" << QString::fromUtf8(value,value.size()) << + // "size:" << value.size(); + LDAPMod **mods; + + mods = *pmods; + + uint i = 0; + + if (mods == nullptr) { + mods = (LDAPMod **)malloc(2 * sizeof(LDAPMod *)); + mods[ 0 ] = (LDAPMod *)malloc(sizeof(LDAPMod)); + mods[ 1 ] = nullptr; + memset(mods[ 0 ], 0, sizeof(LDAPMod)); + } else { + while (mods[ i ] != nullptr + && (strcmp(attr.toUtf8().constData(), mods[i]->mod_type) != 0 + || (mods[ i ]->mod_op & ~LDAP_MOD_BVALUES) != mod_type)) { + i++; + } + + if (mods[ i ] == nullptr) { + mods = (LDAPMod **)realloc(mods, (i + 2) * sizeof(LDAPMod *)); + if (mods == nullptr) { + qCritical() << "addModOp: realloc"; + return; + } + mods[ i + 1 ] = nullptr; + mods[ i ] = (LDAPMod *)malloc(sizeof(LDAPMod)); + memset(mods[ i ], 0, sizeof(LDAPMod)); + } + } + + mods[ i ]->mod_op = mod_type | LDAP_MOD_BVALUES; + if (mods[ i ]->mod_type == nullptr) { + mods[ i ]->mod_type = strdup(attr.toUtf8().constData()); + } + + *pmods = mods; + + if (value == nullptr) { + return; + } + + int vallen = value->size(); + BerValue *berval; + berval = (BerValue *)malloc(sizeof(BerValue)); + berval->bv_len = vallen; + if (vallen > 0) { + berval->bv_val = (char *)malloc(vallen); + memcpy(berval->bv_val, value->data(), vallen); + } else { + berval->bv_val = nullptr; + } + + if (mods[ i ]->mod_vals.modv_bvals == nullptr) { + mods[ i ]->mod_vals.modv_bvals + = (BerValue **)malloc(sizeof(BerValue *) * 2); + mods[ i ]->mod_vals.modv_bvals[ 0 ] = berval; + mods[ i ]->mod_vals.modv_bvals[ 1 ] = nullptr; +// qCDebug(LDAP_LOG) << "new bervalue struct" << attr << value; + } else { + uint j = 0; + while (mods[ i ]->mod_vals.modv_bvals[ j ] != nullptr) { + j++; + } + mods[ i ]->mod_vals.modv_bvals + = (BerValue **)realloc(mods[ i ]->mod_vals.modv_bvals, + (j + 2) * sizeof(BerValue *)); + if (mods[ i ]->mod_vals.modv_bvals == nullptr) { + qCritical() << "addModOp: realloc"; + free(berval); + return; + } + mods[ i ]->mod_vals.modv_bvals[ j ] = berval; + mods[ i ]->mod_vals.modv_bvals[ j + 1 ] = nullptr; + qCDebug(LDAP_LOG) << j << ". new bervalue"; + } +} + +static void addControlOp(LDAPControl ***pctrls, const QString &oid, const QByteArray &value, bool critical) +{ + LDAPControl **ctrls; + auto *ctrl = (LDAPControl *)malloc(sizeof(LDAPControl)); + + ctrls = *pctrls; + + qCDebug(LDAP_LOG) << "oid:'" << oid << "' val: '" << value << "'"; + int vallen = value.size(); + ctrl->ldctl_value.bv_len = vallen; + if (vallen) { + ctrl->ldctl_value.bv_val = (char *)malloc(vallen); + memcpy(ctrl->ldctl_value.bv_val, value.data(), vallen); + } else { + ctrl->ldctl_value.bv_val = nullptr; + } + ctrl->ldctl_iscritical = critical; + ctrl->ldctl_oid = strdup(oid.toUtf8().constData()); + + uint i = 0; + + if (ctrls == nullptr) { + ctrls = (LDAPControl **)malloc(2 * sizeof(LDAPControl *)); + ctrls[ 0 ] = nullptr; + ctrls[ 1 ] = nullptr; + } else { + while (ctrls[ i ] != nullptr) { + i++; + } + ctrls[ i + 1 ] = nullptr; + ctrls + = (LDAPControl **)realloc(ctrls, (i + 2) * sizeof(LDAPControl *)); + } + ctrls[ i ] = ctrl; + *pctrls = ctrls; +} + +static void createControls(LDAPControl ***pctrls, const LdapControls &ctrls) +{ + for (int i = 0; i < ctrls.count(); ++i) { + addControlOp(pctrls, ctrls[i].oid(), ctrls[i].value(), ctrls[i].critical()); + } +} + +static void extractControls(LdapControls &ctrls, LDAPControl **pctrls) +{ + LdapControl control; + int i = 0; + + while (pctrls[i]) { + LDAPControl *ctrl = pctrls[ i ]; + control.setOid(QString::fromUtf8(ctrl->ldctl_oid)); + control.setValue(QByteArray(ctrl->ldctl_value.bv_val, + ctrl->ldctl_value.bv_len)); + control.setCritical(ctrl->ldctl_iscritical); + ctrls.append(control); + i++; + } +} + +int LdapOperation::bind(const QByteArray &creds, SASL_Callback_Proc *saslproc, void *data) +{ + return d->bind(creds, saslproc, data, true); +} + +int LdapOperation::bind_s(SASL_Callback_Proc *saslproc, void *data) +{ + return d->bind(QByteArray(), saslproc, data, false); +} + +int LdapOperation::search(const LdapDN &base, LdapUrl::Scope scope, const QString &filter, const QStringList &attributes) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + char **attrs = nullptr; + int msgid; + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int count = attributes.count(); + if (count > 0) { + attrs = static_cast(malloc((count + 1) * sizeof(char *))); + for (int i = 0; i < count; i++) { + attrs[i] = strdup(attributes.at(i).toUtf8().constData()); + } + attrs[count] = nullptr; + } + + int lscope = LDAP_SCOPE_BASE; + switch (scope) { + case LdapUrl::Base: + lscope = LDAP_SCOPE_BASE; + break; + case LdapUrl::One: + lscope = LDAP_SCOPE_ONELEVEL; + break; + case LdapUrl::Sub: + lscope = LDAP_SCOPE_SUBTREE; + break; + } + + qCDebug(LDAP_LOG) << "asyncSearch() base=\"" << base.toString() + << "\" scope=" << (int)scope + << "filter=\"" << filter + << "\" attrs=" << attributes; + int retval + = ldap_search_ext(ld, base.toString().toUtf8().data(), lscope, + filter.isEmpty() ? QByteArray("objectClass=*").data() + : filter.toUtf8().data(), + attrs, 0, serverctrls, clientctrls, nullptr, + d->mConnection->sizeLimit(), &msgid); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + // free the attributes list again + if (count > 0) { + for (int i = 0; i < count; i++) { + free(attrs[i]); + } + free(attrs); + } + + if (retval == 0) { + retval = msgid; + } + return retval; +} + +int LdapOperation::add(const LdapObject &object) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + int msgid; + LDAPMod **lmod = nullptr; + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + for (LdapAttrMap::ConstIterator it = object.attributes().begin(); + it != object.attributes().end(); ++it) { + QString attr = it.key(); + for (LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2) { + addModOp(&lmod, 0, attr, &(*it2)); + } + } + + int retval + = ldap_add_ext(ld, object.dn().toString().toUtf8().data(), lmod, serverctrls, + clientctrls, &msgid); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + ldap_mods_free(lmod, 1); + if (retval == 0) { + retval = msgid; + } + return retval; +} + +int LdapOperation::add_s(const LdapObject &object) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + LDAPMod **lmod = nullptr; + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + for (LdapAttrMap::ConstIterator it = object.attributes().begin(); + it != object.attributes().end(); ++it) { + QString attr = it.key(); + for (LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2) { + addModOp(&lmod, 0, attr, &(*it2)); + } + } + + int retval + = ldap_add_ext_s(ld, object.dn().toString().toUtf8().data(), lmod, serverctrls, + clientctrls); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + ldap_mods_free(lmod, 1); + return retval; +} + +int LdapOperation::add(const LdapDN &dn, const ModOps &ops) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + int msgid; + LDAPMod **lmod = nullptr; + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + for (int i = 0; i < ops.count(); ++i) { + for (int j = 0; j < ops[i].values.count(); ++j) { + addModOp(&lmod, 0, ops[i].attr, &ops[i].values[j]); + } + } + + int retval + = ldap_add_ext(ld, dn.toString().toUtf8().data(), lmod, serverctrls, + clientctrls, &msgid); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + ldap_mods_free(lmod, 1); + if (retval == 0) { + retval = msgid; + } + return retval; +} + +int LdapOperation::add_s(const LdapDN &dn, const ModOps &ops) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + LDAPMod **lmod = nullptr; + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + for (int i = 0; i < ops.count(); ++i) { + for (int j = 0; j < ops[i].values.count(); ++j) { + addModOp(&lmod, 0, ops[i].attr, &ops[i].values[j]); + } + } + qCDebug(LDAP_LOG) << dn.toString(); + int retval + = ldap_add_ext_s(ld, dn.toString().toUtf8().data(), lmod, serverctrls, + clientctrls); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + ldap_mods_free(lmod, 1); + return retval; +} + +int LdapOperation::rename(const LdapDN &dn, const QString &newRdn, const QString &newSuperior, bool deleteold) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + int msgid; + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int retval = ldap_rename(ld, dn.toString().toUtf8().data(), newRdn.toUtf8().data(), + newSuperior.isEmpty() ? (char *)nullptr : newSuperior.toUtf8().data(), + deleteold, serverctrls, clientctrls, &msgid); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + if (retval == 0) { + retval = msgid; + } + return retval; +} + +int LdapOperation::rename_s(const LdapDN &dn, const QString &newRdn, const QString &newSuperior, bool deleteold) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int retval = ldap_rename_s(ld, dn.toString().toUtf8().data(), newRdn.toUtf8().data(), + newSuperior.isEmpty() ? (char *)nullptr : newSuperior.toUtf8().data(), + deleteold, serverctrls, clientctrls); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + return retval; +} + +int LdapOperation::del(const LdapDN &dn) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + int msgid; + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int retval + = ldap_delete_ext(ld, dn.toString().toUtf8().data(), serverctrls, clientctrls, &msgid); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + if (retval == 0) { + retval = msgid; + } + return retval; +} + +int LdapOperation::del_s(const LdapDN &dn) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int retval = ldap_delete_ext_s(ld, dn.toString().toUtf8().data(), serverctrls, clientctrls); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + return retval; +} + +int LdapOperation::modify(const LdapDN &dn, const ModOps &ops) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + int msgid; + LDAPMod **lmod = nullptr; + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + for (int i = 0; i < ops.count(); ++i) { + int mtype = 0; + switch (ops[i].type) { + case Mod_None: + mtype = 0; + break; + case Mod_Add: + mtype = LDAP_MOD_ADD; + break; + case Mod_Replace: + mtype = LDAP_MOD_REPLACE; + break; + case Mod_Del: + mtype = LDAP_MOD_DELETE; + break; + } + addModOp(&lmod, mtype, ops[i].attr, nullptr); + for (int j = 0; j < ops[i].values.count(); ++j) { + addModOp(&lmod, mtype, ops[i].attr, &ops[i].values[j]); + } + } + + int retval + = ldap_modify_ext(ld, dn.toString().toUtf8().data(), lmod, serverctrls, clientctrls, &msgid); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + ldap_mods_free(lmod, 1); + if (retval == 0) { + retval = msgid; + } + return retval; +} + +int LdapOperation::modify_s(const LdapDN &dn, const ModOps &ops) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + LDAPMod **lmod = nullptr; + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + for (int i = 0; i < ops.count(); ++i) { + int mtype = 0; + switch (ops[i].type) { + case Mod_None: + mtype = 0; + break; + case Mod_Add: + mtype = LDAP_MOD_ADD; + break; + case Mod_Replace: + mtype = LDAP_MOD_REPLACE; + break; + case Mod_Del: + mtype = LDAP_MOD_DELETE; + break; + } + addModOp(&lmod, mtype, ops[i].attr, nullptr); + for (int j = 0; j < ops[i].values.count(); ++j) { + addModOp(&lmod, mtype, ops[i].attr, &ops[i].values[j]); + } + } + + int retval + = ldap_modify_ext_s(ld, dn.toString().toUtf8().data(), lmod, serverctrls, clientctrls); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + ldap_mods_free(lmod, 1); + return retval; +} + +int LdapOperation::compare(const LdapDN &dn, const QString &attr, const QByteArray &value) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + int msgid; + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int vallen = value.size(); + BerValue *berval; + berval = (BerValue *)malloc(sizeof(BerValue)); + berval->bv_val = (char *)malloc(vallen); + berval->bv_len = vallen; + memcpy(berval->bv_val, value.data(), vallen); + + int retval = ldap_compare_ext(ld, dn.toString().toUtf8().data(), attr.toUtf8().data(), berval, + serverctrls, clientctrls, &msgid); + + ber_bvfree(berval); + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + if (retval == 0) { + retval = msgid; + } + return retval; +} + +int LdapOperation::compare_s(const LdapDN &dn, const QString &attr, const QByteArray &value) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int vallen = value.size(); + BerValue *berval; + berval = (BerValue *)malloc(sizeof(BerValue)); + berval->bv_val = (char *)malloc(vallen); + berval->bv_len = vallen; + memcpy(berval->bv_val, value.data(), vallen); + + int retval = ldap_compare_ext_s(ld, dn.toString().toUtf8().data(), attr.toUtf8().data(), berval, + serverctrls, clientctrls); + + ber_bvfree(berval); + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + return retval; +} + +int LdapOperation::exop(const QString &oid, const QByteArray &data) +{ + Q_ASSERT(d->mConnection); +#if defined(HAVE_LDAP_EXTENDED_OPERATION) && defined(HAVE_LDAP_EXTENDED_OPERATION_PROTOTYPE) + LDAP *ld = (LDAP *)d->mConnection->handle(); + int msgid; + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int vallen = data.size(); + BerValue *berval; + berval = (BerValue *)malloc(sizeof(BerValue)); + berval->bv_val = (char *)malloc(vallen); + berval->bv_len = vallen; + memcpy(berval->bv_val, data.data(), vallen); + + int retval = ldap_extended_operation(ld, oid.toUtf8().data(), berval, + serverctrls, clientctrls, &msgid); + + ber_bvfree(berval); + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + if (retval == 0) { + retval = msgid; + } + return retval; +#else + qCritical() << "Your LDAP client libraries don't support extended operations."; + return -1; +#endif +} + +int LdapOperation::exop_s(const QString &oid, const QByteArray &data) +{ +#if defined(HAVE_LDAP_EXTENDED_OPERATION) && defined(HAVE_LDAP_EXTENDED_OPERATION_PROTOTYPE) + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + BerValue *retdata; + char *retoid; + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int vallen = data.size(); + BerValue *berval; + berval = (BerValue *)malloc(sizeof(BerValue)); + berval->bv_val = (char *)malloc(vallen); + berval->bv_len = vallen; + memcpy(berval->bv_val, data.data(), vallen); + + int retval = ldap_extended_operation_s(ld, oid.toUtf8().data(), berval, + serverctrls, clientctrls, &retoid, &retdata); + + ber_bvfree(berval); + ber_bvfree(retdata); + free(retoid); + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + return retval; +#else + qCritical() << "Your LDAP client libraries don't support extended operations."; + return -1; +#endif +} + +int LdapOperation::abandon(int id) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + LDAPControl **serverctrls = nullptr, **clientctrls = nullptr; + createControls(&serverctrls, d->mServerCtrls); + createControls(&serverctrls, d->mClientCtrls); + + int retval = ldap_abandon_ext(ld, id, serverctrls, clientctrls); + + ldap_controls_free(serverctrls); + ldap_controls_free(clientctrls); + + return retval; +} + +int LdapOperation::waitForResult(int id, int msecs) +{ + Q_ASSERT(d->mConnection); + LDAP *ld = (LDAP *)d->mConnection->handle(); + + LDAPMessage *msg; + + QElapsedTimer stopWatch; + stopWatch.start(); + int attempt(1); + int timeout(0); + + do { + // Calculate the timeout value to use and assign it to a timeval structure + // see man select (2) for details + timeout = kldap_timeout_value(msecs, stopWatch.elapsed()); + qCDebug(LDAP_LOG) << "(" << id << "," << msecs + << "): Waiting" << timeout + << "msecs for result. Attempt #" << attempt++; + struct timeval tv; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + // Wait for a result + int rescode = ldap_result(ld, id, 0, timeout < 0 ? nullptr : &tv, &msg); + if (rescode == -1) { + return -1; + } + // Act on the return code + if (rescode != 0) { + // Some kind of result is available for processing + return d->processResult(rescode, msg); + } + } while (msecs == -1 || stopWatch.elapsed() < msecs); + + return 0; //timeout +} + +#else + +int LdapOperation::bind(const QByteArray &creds, SASL_Callback_Proc *saslproc, void *data) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::bind_s(SASL_Callback_Proc *saslproc, void *data) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::search(const LdapDN &base, LdapUrl::Scope scope, const QString &filter, const QStringList &attributes) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::add(const LdapObject &object) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::add_s(const LdapObject &object) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::add(const LdapDN &dn, const ModOps &ops) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::add_s(const LdapDN &dn, const ModOps &ops) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::rename(const LdapDN &dn, const QString &newRdn, const QString &newSuperior, bool deleteold) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::rename_s(const LdapDN &dn, const QString &newRdn, const QString &newSuperior, bool deleteold) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::del(const LdapDN &dn) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::del_s(const LdapDN &dn) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::modify(const LdapDN &dn, const ModOps &ops) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::modify_s(const LdapDN &dn, const ModOps &ops) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::compare(const LdapDN &dn, const QString &attr, const QByteArray &value) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::exop(const QString &oid, const QByteArray &data) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::compare_s(const LdapDN &dn, const QString &attr, const QByteArray &value) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::exop_s(const QString &oid, const QByteArray &data) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::waitForResult(int id, int msecs) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +int LdapOperation::abandon(int id) +{ + qCritical() << "LDAP support not compiled"; + return -1; +} + +#endif diff --git a/3rdparty/kldap/src/core/ldapoperation.h b/3rdparty/kldap/src/core/ldapoperation.h new file mode 100644 index 0000000..6a99cc2 --- /dev/null +++ b/3rdparty/kldap/src/core/ldapoperation.h @@ -0,0 +1,280 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KLDAP_LDAPOPERATION_H +#define KLDAP_LDAPOPERATION_H + +#include "kldap_export.h" +#include "ldapconnection.h" +#include "ldapcontrol.h" +#include "ldapobject.h" +#include "ldapdn.h" +#include "ldapserver.h" +#include "ldapurl.h" + +#include +#include +#include + +namespace KLDAP { +/** + * @brief + * This class allows sending an ldap operation + * (search, rename, modify, delete, compare, exop) to an LDAP server. + */ +class KLDAP_EXPORT LdapOperation +{ +public: + typedef enum { + Mod_None, Mod_Add, Mod_Replace, Mod_Del + } ModType; + + typedef enum { + RES_BIND = 0x61, + RES_SEARCH_ENTRY = 0x64, + RES_SEARCH_REFERENCE = 0x73, + RES_SEARCH_RESULT = 0x65, + RES_MODIFY = 0x67, + RES_ADD = 0x69, + RES_DELETE = 0x69, + RES_MODDN = 0x6d, + RES_COMPARE = 0x6f, + RES_EXTENDED = 0x78, + RES_EXTENDED_PARTIAL = 0x79 + } ResultType; + + typedef struct { + ModType type; + QString attr; + QList values; + } ModOp; + + typedef QVector ModOps; + + enum SASL_Fields { + SASL_Authname = 0x1, + SASL_Authzid = 0x2, + SASL_Realm = 0x4, + SASL_Password = 0x8 + }; + + struct SASL_Credentials { + int fields; + QString authname; + QString authzid; + QString realm; + QString password; + }; + + typedef int (SASL_Callback_Proc)(SASL_Credentials &cred, void *data); + + struct SASL_Data { + SASL_Callback_Proc *proc; + void *data; + SASL_Credentials creds; + }; + + LdapOperation(); + LdapOperation(LdapConnection &conn); + ~LdapOperation(); + + /** + * Sets the connection object. Without living connection object, + * LDAP operations are not possible. + * @param the connection object to set + */ + void setConnection(LdapConnection &conn); + /** + * Returns the connection object. + */ + LdapConnection &connection(); + /** + * Sets the client controls which will sent with each operation. + */ + void setClientControls(const LdapControls &ctrls); + /** + * Sets the server controls which will sent with each operation. + */ + void setServerControls(const LdapControls &ctrls); + /** + * Returns the client controls (which set by setClientControls()). + */ + Q_REQUIRED_RESULT LdapControls clientControls() const; + /** + * Returns the server controls (which set by setServerControls()). + */ + Q_REQUIRED_RESULT LdapControls serverControls() const; + + /** + * Binds to the server which specified in the connection object. + * Can do simple or SASL bind. Returns a message id if successful, negative value if not. + */ + Q_REQUIRED_RESULT int bind(const QByteArray &creds = QByteArray(), SASL_Callback_Proc *saslproc = nullptr, void *data = nullptr); + + /** + * Binds to the server which specified in the connection object. + * Can do simple or SASL bind. This is the synchronous version. + * Returns KLDAP_SUCCESS id if successful, else an LDAP error code. + */ + Q_REQUIRED_RESULT int bind_s(SASL_Callback_Proc *saslproc = nullptr, void *data = nullptr); + + /** + * Starts a search operation with the given base DN, scope, filter and + * result attributes. Returns a message id if successful, -1 if not. + */ + Q_REQUIRED_RESULT int search(const LdapDN &base, LdapUrl::Scope scope, const QString &filter, const QStringList &attrs); + /** + * Starts an addition operation. + * Returns a message id if successful, -1 if not. + * @param object the additional operation to start + */ + Q_REQUIRED_RESULT int add(const LdapObject &object); + /** + * Adds the specified object to the LDAP database. + * Returns KLDAP_SUCCESS id if successful, else an LDAP error code. + * @param object the object to add to LDAP database + */ + Q_REQUIRED_RESULT int add_s(const LdapObject &object); + /** + * Starts an addition operation. This version accepts ModOps not LdapObject. + * Returns a message id if successful, -1 if not. + * @param dn the LdapDN operation to start + * @param ops the ModOps operation to start + */ + Q_REQUIRED_RESULT int add(const LdapDN &dn, const ModOps &ops); + /** + * Adds the specified object to the LDAP database. This version accepts ModOps not LdapObject. + * This is the synchronous version. + * Returns KLDAP_SUCCESS id if successful, else an LDAP error code. + * @param dn the LdapDN object to add + * @param ops the ModOps object to add + */ + Q_REQUIRED_RESULT int add_s(const LdapDN &dn, const ModOps &ops); + /** + * Starts a modrdn operation on given DN, changing its RDN to newRdn, + * changing its parent to newSuperior (if it's not empty), and deletes + * the old dn if deleteold is true. + * Returns a message id if successful, -1 if not. + */ + Q_REQUIRED_RESULT int rename(const LdapDN &dn, const QString &newRdn, const QString &newSuperior, bool deleteold = true); + /** + * Performs a modrdn operation on given DN, changing its RDN to newRdn, + * changing its parent to newSuperior (if it's not empty), and deletes + * the old dn if deleteold is true. This is the synchronous version. + * Returns KLDAP_SUCCESS id if successful, else an LDAP error code. + */ + Q_REQUIRED_RESULT int rename_s(const LdapDN &dn, const QString &newRdn, const QString &newSuperior, bool deleteold = true); + /** + * Starts a delete operation on the given DN. + * Returns a message id if successful, -1 if not. + */ + Q_REQUIRED_RESULT int del(const LdapDN &dn); + /** + * Deletes the given DN. This is the synchronous version. + * Returns KLDAP_SUCCESS id if successful, else an LDAP error code. + * @param dn the dn to delete + */ + Q_REQUIRED_RESULT int del_s(const LdapDN &dn); + /** + * Starts a modify operation on the given DN. + * Returns a message id if successful, -1 if not. + * @param dn the DN to start modify operation on + */ + Q_REQUIRED_RESULT int modify(const LdapDN &dn, const ModOps &ops); + /** + * Performs a modify operation on the given DN. + * This is the synchronous version. + * Returns KLDAP_SUCCESS id if successful, else an LDAP error code. + */ + Q_REQUIRED_RESULT int modify_s(const LdapDN &dn, const ModOps &ops); + /** + * Starts a compare operation on the given DN, compares the specified + * attribute with the given value. + * Returns a message id if successful, -1 if not. + */ + Q_REQUIRED_RESULT int compare(const LdapDN &dn, const QString &attr, const QByteArray &value); + /** + * Performs a compare operation on the given DN, compares the specified + * attribute with the given value. This is the synchronous version. + * Returns KLDAP_COMPARE_TRUE if the entry contains the attribute value + * and KLDAP_COMPARE_FALSE if it does not. Otherwise, some error code + * is returned. + */ + Q_REQUIRED_RESULT int compare_s(const LdapDN &dn, const QString &attr, const QByteArray &value); + /** + * Starts an extended operation specified with oid and data. + * Returns a message id if successful, -1 if not. + */ + Q_REQUIRED_RESULT int exop(const QString &oid, const QByteArray &data); + /** + * Performs an extended operation specified with oid and data. + * This is the synchronous version. + * Returns KLDAP_SUCCESS id if successful, else an LDAP error code. + */ + Q_REQUIRED_RESULT int exop_s(const QString &oid, const QByteArray &data); + /** + * Abandons a long-running operation. Requires the message id. + */ + Q_REQUIRED_RESULT int abandon(int id); + /** + * Waits for up to \p msecs milliseconds for a result message from the LDAP + * server. If \p msecs is -1, then this function will block indefinitely. + * If \p msecs is 0, then this function will return immediately, that is it + * will perform a poll for a result message. + * + * Returns the type of the result LDAP message (RES_XXX constants). + * -1 if error occurred, 0 if the timeout value elapsed. Note! + * Return code -1 means that fetching the message resulted in error, + * not the LDAP operation error. Call connection().ldapErrorCode() to + * determine if the operation succeeded. + */ + Q_REQUIRED_RESULT int waitForResult(int id, int msecs = -1); + /** + * Returns the result object if result() returned RES_SEARCH_ENTRY. + */ + Q_REQUIRED_RESULT LdapObject object() const; + /** + * Returns the server controls from the returned ldap message (grabbed + * by result()). + */ + Q_REQUIRED_RESULT LdapControls controls() const; + /** + * Returns the OID of the extended operation response (result + * returned RES_EXTENDED). + */ + Q_REQUIRED_RESULT QByteArray extendedOid() const; + /** + * Returns the data from the extended operation response (result + * returned RES_EXTENDED). + */ + Q_REQUIRED_RESULT QByteArray extendedData() const; + /** + * The server might supply a matched DN string in the message indicating + * how much of a name in a request was recognized. This can be grabbed by + * matchedDn(). + */ + Q_REQUIRED_RESULT QString matchedDn() const; + /** + * This function returns the referral strings from the parsed message + * (if any). + */ + Q_REQUIRED_RESULT QList referrals() const; + /** + * Returns the server response for a bind request (result + * returned RES_BIND). + */ + Q_REQUIRED_RESULT QByteArray serverCred() const; + +private: + class LdapOperationPrivate; + LdapOperationPrivate *const d; + + Q_DISABLE_COPY(LdapOperation) +}; +} + +#endif diff --git a/3rdparty/kldap/src/core/ldapsearch.cpp b/3rdparty/kldap/src/core/ldapsearch.cpp new file mode 100644 index 0000000..a1df903 --- /dev/null +++ b/3rdparty/kldap/src/core/ldapsearch.cpp @@ -0,0 +1,342 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "ldapsearch.h" +#include "ldapdn.h" +#include "ldapdefs.h" + +#include + +#include "ldap_debug.h" +#include +using namespace KLDAP; + +//blocking the GUI for xxx milliseconds +#define LDAPSEARCH_BLOCKING_TIMEOUT 10 + +class LdapSearchPrivate +{ +public: + LdapSearchPrivate(LdapSearch *parent) + : mParent(parent) + { + } + + void result(); + bool connect(); + void closeConnection(); + bool startSearch(const LdapDN &base, LdapUrl::Scope scope, const QString &filter, const QStringList &attributes, int pagesize, int count); + + LdapSearch *const mParent; + LdapConnection *mConn = nullptr; + LdapOperation mOp; + bool mOwnConnection = false; + bool mAbandoned = false; + int mId; + int mPageSize; + LdapDN mBase; + QString mFilter; + QStringList mAttributes; + LdapUrl::Scope mScope; + + QString mErrorString; + int mError; + int mCount; + int mMaxCount; + bool mFinished = false; +}; + +void LdapSearchPrivate::result() +{ + if (mAbandoned) { + mOp.abandon(mId); + return; + } + const int res = mOp.waitForResult(mId, LDAPSEARCH_BLOCKING_TIMEOUT); + + qCDebug(LDAP_LOG) << "LDAP result:" << res; + + if (res != 0 + && (res == -1 + || (mConn->ldapErrorCode() != KLDAP_SUCCESS + && mConn->ldapErrorCode() != KLDAP_SASL_BIND_IN_PROGRESS))) { + //error happened, but no timeout + mError = mConn->ldapErrorCode(); + mErrorString = mConn->ldapErrorString(); + Q_EMIT mParent->result(mParent); + return; + } + + //binding + if (res == LdapOperation::RES_BIND) { + const QByteArray servercc = mOp.serverCred(); + + qCDebug(LDAP_LOG) << "LdapSearch RES_BIND"; + if (mConn->ldapErrorCode() == KLDAP_SUCCESS) { //bind succeeded + qCDebug(LDAP_LOG) << "bind succeeded"; + LdapControls savedctrls = mOp.serverControls(); + if (mPageSize) { + LdapControls ctrls = savedctrls; + LdapControl::insert(ctrls, LdapControl::createPageControl(mPageSize)); + mOp.setServerControls(ctrls); + } + + mId = mOp.search(mBase, mScope, mFilter, mAttributes); + mOp.setServerControls(savedctrls); + } else { //next bind step + qCDebug(LDAP_LOG) << "bind next step"; + mId = mOp.bind(servercc); + } + if (mId < 0) { + if (mId == KLDAP_SASL_ERROR) { + mError = mId; + mErrorString = mConn->saslErrorString(); + } else { + mError = mConn->ldapErrorCode(); + mErrorString = mConn->ldapErrorString(); + } + Q_EMIT mParent->result(mParent); + return; + } + QTimer::singleShot(0, mParent, [this]() { + result(); + }); + return; + } + + //End of entries + if (res == LdapOperation::RES_SEARCH_RESULT) { + if (mPageSize) { + QByteArray cookie; + int estsize = -1; + const int numberOfControls(mOp.controls().count()); + for (int i = 0; i < numberOfControls; ++i) { + estsize = mOp.controls().at(i).parsePageControl(cookie); + if (estsize != -1) { + break; + } + } + qCDebug(LDAP_LOG) << " estimated size:" << estsize; + if (estsize != -1 && !cookie.isEmpty()) { + LdapControls ctrls, savedctrls; + savedctrls = mOp.serverControls(); + ctrls = savedctrls; + LdapControl::insert(ctrls, LdapControl::createPageControl(mPageSize, cookie)); + mOp.setServerControls(ctrls); + mId = mOp.search(mBase, mScope, mFilter, mAttributes); + mOp.setServerControls(savedctrls); + if (mId == -1) { + mError = mConn->ldapErrorCode(); + mErrorString = mConn->ldapErrorString(); + Q_EMIT mParent->result(mParent); + return; + } + //continue with the next page + QTimer::singleShot(0, mParent, [this]() { + result(); + }); + return; + } + } + mFinished = true; + Q_EMIT mParent->result(mParent); + return; + } + + //Found an entry + if (res == LdapOperation::RES_SEARCH_ENTRY) { + Q_EMIT mParent->data(mParent, mOp.object()); + mCount++; + } + + //If not reached the requested entries, continue + if (mMaxCount <= 0 || mCount < mMaxCount) { + QTimer::singleShot(0, mParent, [this]() { + result(); + }); + } + //If reached the requested entries, indicate it + if (mMaxCount > 0 && mCount == mMaxCount) { + qCDebug(LDAP_LOG) << mCount << " entries reached"; + Q_EMIT mParent->result(mParent); + } +} + +bool LdapSearchPrivate::connect() +{ + const int ret = mConn->connect(); + if (ret != KLDAP_SUCCESS) { + mError = ret; + mErrorString = mConn->connectionError(); + closeConnection(); + return false; + } + return true; +} + +void LdapSearchPrivate::closeConnection() +{ + if (mOwnConnection && mConn) { + delete mConn; + mConn = nullptr; + } +} + +//This starts the real job +bool LdapSearchPrivate::startSearch(const LdapDN &base, LdapUrl::Scope scope, const QString &filter, const QStringList &attributes, int pagesize, int count) +{ + qCDebug(LDAP_LOG) << "search: base=" << base.toString() << "scope=" << static_cast(scope) + << "filter=" << filter << "attributes=" << attributes + << "pagesize=" << pagesize; + mAbandoned = false; + mError = 0; + mErrorString.clear(); + mOp.setConnection(*mConn); + mPageSize = pagesize; + mBase = base; + mScope = scope; + mFilter = filter; + mAttributes = attributes; + mMaxCount = count; + mCount = 0; + mFinished = false; + + LdapControls savedctrls = mOp.serverControls(); + if (pagesize) { + LdapControls ctrls = savedctrls; + mConn->setOption(0x0008, nullptr); // Disable referals or paging won't work + LdapControl::insert(ctrls, LdapControl::createPageControl(pagesize)); + mOp.setServerControls(ctrls); + } + + mId = mOp.bind(); + if (mId < 0) { + if (mId == KLDAP_SASL_ERROR) { + mError = mId; + mErrorString = mConn->saslErrorString(); + } else { + mError = mConn->ldapErrorCode(); + mErrorString = mConn->ldapErrorString(); + if (mError == -1 && mErrorString.isEmpty()) { + mErrorString = i18n("Cannot access to server. Please reconfigure it."); + } + } + return false; + } + qCDebug(LDAP_LOG) << "startSearch msg id=" << mId; + + //maybe do this with threads?- need thread-safe client libs!!! + QTimer::singleShot(0, mParent, [this]() { + result(); + }); + + return true; +} + +/////////////////////////////////////////////// + +LdapSearch::LdapSearch() + : d(new LdapSearchPrivate(this)) +{ + d->mOwnConnection = true; + d->mConn = nullptr; +} + +LdapSearch::LdapSearch(LdapConnection &connection) + : d(new LdapSearchPrivate(this)) +{ + d->mOwnConnection = false; + d->mConn = &connection; +} + +LdapSearch::~LdapSearch() +{ + d->closeConnection(); + delete d; +} + +void LdapSearch::setConnection(LdapConnection &connection) +{ + d->closeConnection(); + d->mOwnConnection = false; + d->mConn = &connection; +} + +void LdapSearch::setClientControls(const LdapControls &ctrls) +{ + d->mOp.setClientControls(ctrls); +} + +void LdapSearch::setServerControls(const LdapControls &ctrls) +{ + d->mOp.setServerControls(ctrls); +} + +bool LdapSearch::search(const LdapServer &server, const QStringList &attributes, int count) +{ + if (d->mOwnConnection) { + d->closeConnection(); + d->mConn = new LdapConnection(server); + if (!d->connect()) { + return false; + } + } + return d->startSearch(server.baseDn(), server.scope(), server.filter(), + attributes, server.pageSize(), count); +} + +bool LdapSearch::search(const LdapUrl &url, int count) +{ + if (d->mOwnConnection) { + d->closeConnection(); + d->mConn = new LdapConnection(url); + if (!d->connect()) { + return false; + } + } + bool critical = true; + const int pagesize = url.extension(QStringLiteral("x-pagesize"), critical).toInt(); + return d->startSearch(url.dn(), url.scope(), url.filter(), + url.attributes(), pagesize, count); +} + +bool LdapSearch::search(const LdapDN &base, LdapUrl::Scope scope, const QString &filter, const QStringList &attributes, int pagesize, int count) +{ + Q_ASSERT(!d->mOwnConnection); + return d->startSearch(base, scope, filter, attributes, pagesize, count); +} + +void LdapSearch::continueSearch() +{ + Q_ASSERT(!d->mFinished); + d->mCount = 0; + QTimer::singleShot(0, this, [this]() { + d->result(); + }); +} + +bool LdapSearch::isFinished() +{ + return d->mFinished; +} + +void LdapSearch::abandon() +{ + d->mAbandoned = true; +} + +int LdapSearch::error() const +{ + return d->mError; +} + +QString LdapSearch::errorString() const +{ + return d->mErrorString; +} + +#include "moc_ldapsearch.cpp" diff --git a/3rdparty/kldap/src/core/ldapsearch.h b/3rdparty/kldap/src/core/ldapsearch.h new file mode 100644 index 0000000..5108300 --- /dev/null +++ b/3rdparty/kldap/src/core/ldapsearch.h @@ -0,0 +1,130 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KLDAP_LDAPSEARCH_H +#define KLDAP_LDAPSEARCH_H + +#include +#include +class LdapSearchPrivate; + +#include "kldap_export.h" + +#include "ldapconnection.h" +#include "ldapcontrol.h" +#include "ldapobject.h" +#include "ldapoperation.h" +#include "ldapserver.h" +#include "ldapurl.h" + +// clazy:excludeall=ctor-missing-parent-argument + +namespace KLDAP { +/** + * @brief + * This class starts a search operation on a LDAP server and returns the + * search values via a Qt signal. + */ +class KLDAP_EXPORT LdapSearch : public QObject +{ + Q_OBJECT + +public: + /** + * Constructs an LdapSearch object + */ + LdapSearch(); + + /** + * Constructs an LdapConnection object with the given connection. If this + * form of constructor used, then always this connection will be used + * regardless of the LDAP Url or LdapServer object passed to search(). + * @param connection the connection used to construct LdapConnection object + */ + explicit LdapSearch(LdapConnection &connection); + + ~LdapSearch(); + + /** + * Sets the connection for this object to use for searches from now + * onwards, regardless of the LDAP Url or LdapServer object passed to + * search(). + */ + void setConnection(LdapConnection &connection); + + /** + * Sets the client controls which will sent with each operation. + */ + void setClientControls(const LdapControls &ctrls); + + /** + * Sets the server controls which will sent with each operation. + */ + void setServerControls(const LdapControls &ctrls); + + /** + * Starts a search operation on the LDAP server @param server, + * returning the attributes specified with @param attributes. + * @param count means how many entries to list. If it's >0, then result() + * will be emitted when the number of entries is reached, but with + * isFinished() set to false. + */ + Q_REQUIRED_RESULT bool search(const LdapServer &server, const QStringList &attributes = QStringList(), int count = 0); + + /** + * Starts a search operation on the given LDAP URL. + */ + Q_REQUIRED_RESULT bool search(const LdapUrl &url, int count = 0); + + /** + * Starts a search operation if the LdapConnection object already set + * in the constructor. + */ + Q_REQUIRED_RESULT bool search(const LdapDN &base, LdapUrl::Scope scope = LdapUrl::Sub, const QString &filter = QString(), + const QStringList &attributes = QStringList(), int pagesize = 0, int count = 0); + + /** + * Continues the search (if you set count to non-zero in search(), and isFinished() is false) + */ + void continueSearch(); + /** + * Returns true if the search is finished else returns false. + */ + Q_REQUIRED_RESULT bool isFinished(); + /** + * Tries to abandon the search. + */ + void abandon(); + + /** + * Returns the error code of the search operation (0 if no error). + */ + Q_REQUIRED_RESULT int error() const; + + /** + * Returns the error description of the search operation. + */ + Q_REQUIRED_RESULT QString errorString() const; + +Q_SIGNALS: + /** + * Emitted for each result object. + */ + void data(KLDAP::LdapSearch *search, const KLDAP::LdapObject &obj); + + /** + * Emitted when the searching finished. + */ + void result(KLDAP::LdapSearch *search); + +private: + LdapSearchPrivate *const d; + Q_DISABLE_COPY(LdapSearch) +}; +} + +#endif diff --git a/3rdparty/kldap/src/core/ldapserver.cpp b/3rdparty/kldap/src/core/ldapserver.cpp new file mode 100644 index 0000000..242b8a6 --- /dev/null +++ b/3rdparty/kldap/src/core/ldapserver.cpp @@ -0,0 +1,429 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "ldapserver.h" + +#include "ldap_debug.h" + +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapServer::LdapServerPrivate +{ +public: + QString mHost; + int mPort; + LdapDN mBaseDn; + QString mUser; + QString mBindDn; + QString mRealm; + QString mPassword; + QString mMech; + QString mFilter; + int mTimeLimit; + int mSizeLimit; + int mVersion; + int mPageSize; + int mTimeout; + Security mSecurity; + Auth mAuth; + QString mTLSCACertFile; + TLSRequireCertificate mTLSRequireCertificate; + LdapUrl::Scope mScope; + int mCompletionWeight = -1; +}; + +LdapServer::LdapServer() + : d(new LdapServerPrivate) +{ + clear(); +} + +LdapServer::LdapServer(const LdapUrl &url) + : d(new LdapServerPrivate) +{ + clear(); + + setUrl(url); +} + +LdapServer::LdapServer(const LdapServer &that) + : d(new LdapServerPrivate) +{ + *d = *that.d; +} + +LdapServer &LdapServer::operator=(const LdapServer &that) +{ + if (this == &that) { + return *this; + } + + *d = *that.d; + + return *this; +} + +LdapServer::~LdapServer() +{ + delete d; +} + +void LdapServer::clear() +{ + d->mPort = 389; + d->mHost.clear(); + d->mUser.clear(); + d->mBindDn.clear(); + d->mMech.clear(); + d->mPassword.clear(); + d->mSecurity = None; + d->mAuth = Anonymous; + d->mTLSRequireCertificate = TLSReqCertDefault; + d->mTLSCACertFile.clear(); + d->mVersion = 3; + d->mTimeout = 0; + d->mSizeLimit = d->mTimeLimit = d->mPageSize = 0; + d->mCompletionWeight = -1; +} + +QString LdapServer::host() const +{ + return d->mHost; +} + +int LdapServer::port() const +{ + return d->mPort; +} + +LdapDN LdapServer::baseDn() const +{ + return d->mBaseDn; +} + +QString LdapServer::user() const +{ + return d->mUser; +} + +QString LdapServer::bindDn() const +{ + return d->mBindDn; +} + +QString LdapServer::realm() const +{ + return d->mRealm; +} + +QString LdapServer::password() const +{ + return d->mPassword; +} + +QString LdapServer::filter() const +{ + return d->mFilter; +} + +LdapUrl::Scope LdapServer::scope() const +{ + return d->mScope; +} + +int LdapServer::timeLimit() const +{ + return d->mTimeLimit; +} + +int LdapServer::sizeLimit() const +{ + return d->mSizeLimit; +} + +int LdapServer::pageSize() const +{ + return d->mPageSize; +} + +int LdapServer::version() const +{ + return d->mVersion; +} + +LdapServer::Security LdapServer::security() const +{ + return d->mSecurity; +} + +LdapServer::Auth LdapServer::auth() const +{ + return d->mAuth; +} + +LdapServer::TLSRequireCertificate LdapServer::tlsRequireCertificate() const +{ + return d->mTLSRequireCertificate; +} + +QString LdapServer::tlsCACertFile() const +{ + return d->mTLSCACertFile; +} + +QString LdapServer::mech() const +{ + return d->mMech; +} + +int LdapServer::timeout() const +{ + return d->mTimeout; +} + +void LdapServer::setHost(const QString &host) +{ + d->mHost = host; +} + +void LdapServer::setPort(int port) +{ + d->mPort = port; +} + +void LdapServer::setBaseDn(const LdapDN &baseDn) +{ + d->mBaseDn = baseDn; +} + +void LdapServer::setUser(const QString &user) +{ + d->mUser = user; +} + +void LdapServer::setBindDn(const QString &bindDn) +{ + d->mBindDn = bindDn; +} + +void LdapServer::setRealm(const QString &realm) +{ + d->mRealm = realm; +} + +void LdapServer::setPassword(const QString &password) +{ + d->mPassword = password; +} + +void LdapServer::setTimeLimit(int timelimit) +{ + d->mTimeLimit = timelimit; +} + +void LdapServer::setSizeLimit(int sizelimit) +{ + d->mSizeLimit = sizelimit; +} + +void LdapServer::setPageSize(int pagesize) +{ + d->mPageSize = pagesize; +} + +void LdapServer::setFilter(const QString &filter) +{ + d->mFilter = filter; +} + +void LdapServer::setScope(LdapUrl::Scope scope) +{ + d->mScope = scope; +} + +void LdapServer::setVersion(int version) +{ + d->mVersion = version; +} + +void LdapServer::setSecurity(Security security) +{ + d->mSecurity = security; +} + +void LdapServer::setAuth(Auth auth) +{ + d->mAuth = auth; +} + +void LdapServer::setTLSRequireCertificate(LdapServer::TLSRequireCertificate reqCert) +{ + d->mTLSRequireCertificate = reqCert; +} + +void LdapServer::setTLSCACertFile(const QString &caCertFile) +{ + d->mTLSCACertFile = caCertFile; +} + +void LdapServer::setMech(const QString &mech) +{ + d->mMech = mech; +} + +void LdapServer::setTimeout(int timeout) +{ + d->mTimeout = timeout; +} + +void LdapServer::setUrl(const LdapUrl &url) +{ + bool critical = true; + + d->mHost = url.host(); + const int port = url.port(); + if (port <= 0) { + d->mPort = 389; + } else { + d->mPort = port; + } + d->mBaseDn = url.dn(); + d->mScope = url.scope(); + + d->mFilter = url.filter(); + + d->mSecurity = None; + if (url.scheme() == QLatin1String("ldaps")) { + d->mSecurity = SSL; + } else if (url.hasExtension(QStringLiteral("x-tls"))) { + d->mSecurity = TLS; + } + qCDebug(LDAP_LOG) << "security:" << d->mSecurity; + + d->mMech.clear(); + d->mUser.clear(); + d->mBindDn.clear(); + if (url.hasExtension(QStringLiteral("x-sasl"))) { + d->mAuth = SASL; + if (url.hasExtension(QStringLiteral("x-mech"))) { + d->mMech = url.extension(QStringLiteral("x-mech"), critical); + } + if (url.hasExtension(QStringLiteral("x-realm"))) { + d->mRealm = url.extension(QStringLiteral("x-realm"), critical); + } + if (url.hasExtension(QStringLiteral("bindname"))) { + d->mBindDn = url.extension(QStringLiteral("bindname"), critical); + } + d->mUser = url.userName(); + } else if (url.hasExtension(QStringLiteral("bindname"))) { + d->mAuth = Simple; + d->mBindDn = url.extension(QStringLiteral("bindname"), critical); + } else { + QString user = url.userName(); + if (user.isEmpty()) { + d->mAuth = Anonymous; + } else { + d->mAuth = Simple; + d->mBindDn = user; + } + } + d->mPassword = url.password(); + if (url.hasExtension(QStringLiteral("x-version"))) { + d->mVersion = url.extension(QStringLiteral("x-version"), critical).toInt(); + } else { + d->mVersion = 3; + } + + if (url.hasExtension(QStringLiteral("x-timeout"))) { + d->mTimeout = url.extension(QStringLiteral("x-timeout"), critical).toInt(); + } else { + d->mTimeout = 0; + } + + if (url.hasExtension(QStringLiteral("x-timelimit"))) { + d->mTimeLimit = url.extension(QStringLiteral("x-timelimit"), critical).toInt(); + } else { + d->mTimeLimit = 0; + } + + if (url.hasExtension(QStringLiteral("x-sizelimit"))) { + d->mSizeLimit = url.extension(QStringLiteral("x-sizelimit"), critical).toInt(); + } else { + d->mSizeLimit = 0; + } + + if (url.hasExtension(QStringLiteral("x-pagesize"))) { + d->mPageSize = url.extension(QStringLiteral("x-pagesize"), critical).toInt(); + } else { + d->mPageSize = 0; + } +} + +LdapUrl LdapServer::url() const +{ + LdapUrl url; + url.setScheme(d->mSecurity == SSL ? QStringLiteral("ldaps") : QStringLiteral("ldap")); + url.setPort(d->mPort); + url.setHost(d->mHost); + url.setDn(d->mBaseDn); + url.setFilter(d->mFilter); + url.setScope(d->mScope); + if (d->mAuth == SASL) { + url.setUserName(d->mUser); + url.setPassword(d->mPassword); + url.setExtension(QStringLiteral("bindname"), d->mBindDn, true); + url.setExtension(QStringLiteral("x-sasl"), QString()); + if (!d->mMech.isEmpty()) { + url.setExtension(QStringLiteral("x-mech"), d->mMech); + } + if (!d->mRealm.isEmpty()) { + url.setExtension(QStringLiteral("x-realm"), d->mRealm); + } + } else if (d->mAuth == Simple) { + url.setUserName(d->mBindDn); + url.setPassword(d->mPassword); + } + if (d->mVersion == 2) { + url.setExtension(QStringLiteral("x-version"), d->mVersion); + } + if (d->mTimeout) { + url.setExtension(QStringLiteral("x-timeout"), d->mTimeout); + } + if (d->mTimeLimit != 0) { + url.setExtension(QStringLiteral("x-timelimit"), d->mTimeLimit); + } + if (d->mSizeLimit != 0) { + url.setExtension(QStringLiteral("x-sizelimit"), d->mSizeLimit); + } + if (d->mPageSize != 0) { + url.setExtension(QStringLiteral("x-pagesize"), d->mPageSize); + } + if (d->mSecurity == TLS) { + url.setExtension(QStringLiteral("x-tls"), 1, true); + } + return url; +} + +void LdapServer::setCompletionWeight(int value) +{ + d->mCompletionWeight = value; +} + +int LdapServer::completionWeight() const +{ + return d->mCompletionWeight; +} + +QDebug operator <<(QDebug d, const KLDAP::LdapServer &t) +{ + d << "completionWeight " << t.completionWeight(); + d << "timeout " << t.timeout(); + d << "timeLimit " << t.timeLimit(); + d << "sizeLimit " << t.sizeLimit(); + //TODO + return d; +} diff --git a/3rdparty/kldap/src/core/ldapserver.h b/3rdparty/kldap/src/core/ldapserver.h new file mode 100644 index 0000000..c5754de --- /dev/null +++ b/3rdparty/kldap/src/core/ldapserver.h @@ -0,0 +1,317 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KLDAP_LDAPSERVER_H +#define KLDAP_LDAPSERVER_H + +#include + +#include "ldapurl.h" +#include "ldapdn.h" +#include "kldap_export.h" + +// clazy:excludeall=copyable-polymorphic + +namespace KLDAP { +/** + * @short A class that contains LDAP server connection settings. + * + * This class holds various parameters that are needed to connect + * to an LDAP server. + */ +class KLDAP_EXPORT LdapServer +{ +public: + /** + * Creates an empty LDAP server object. + */ + LdapServer(); + + /** + * Creates a new LDAP server object. + * + * @param url The LDAP url of the server. + */ + LdapServer(const LdapUrl &url); + + /** + * Creates a new LDAP server object from an @p other object. + */ + LdapServer(const LdapServer &other); + + /** + * Overwrites the values of the LDAP server object with + * the values from an @p other object. + */ + LdapServer &operator=(const LdapServer &other); + + /** + * Destroys the LDAP server object. + */ + ~LdapServer(); + + /** + * Describes the encryption settings that can be used + * for the LDAP connection. + */ + typedef enum { + None, ///< Do not use any encryption. + TLS, ///< Use TLS encryption. + SSL ///< Use SSL encryption. + } Security; + + /** + * Describes the authentication method that can be used + * for the LDAP connection. + */ + typedef enum { + Anonymous, ///< Do no authentication. + Simple, ///< Authenticate via login and password. + SASL ///< Azthenticate with the SASL framework. + } Auth; + + /** + * Describes the certificate request and check behaviour + * for TLS/SSL connections. + */ + typedef enum { + TLSReqCertDefault, ///< Use system defaults + TLSReqCertNever, ///< Do not require any certificates. + TLSReqCertDemand, ///< Use LDAP_OPT_X_TLS_DEMAND. + TLSReqCertAllow, ///< Use LDAP_OPT_X_TLS_ALLOW. + TLSReqCertTry, ///< Use LDAP_OPT_X_TLS_TRY. + TLSReqCertHard, ///< Use LDAP_OPT_X_TLS_HARD. + } TLSRequireCertificate; + + /** + * Clears all server settings. + */ + void clear(); + + /** + * Sets the host of the LDAP connection. + */ + void setHost(const QString &host); + + /** + * Returns the host of the LDAP connection. + */ + Q_REQUIRED_RESULT QString host() const; + + /** + * Sets the port of the LDAP connection. + * If not port is set, 389 is used as default. + * @param port the LDAP port connection to set + */ + void setPort(int port); + + /** + * Returns the port of the LDAP connection. + */ + Q_REQUIRED_RESULT int port() const; + + /** + * Sets the @p baseDn of the LDAP connection. + */ + void setBaseDn(const LdapDN &baseDn); + + /** + * Returns the baseDn of the LDAP connection. + */ + Q_REQUIRED_RESULT LdapDN baseDn() const; + + /** + * Sets the @p user of the LDAP connection. + */ + void setUser(const QString &user); + + /** + * Returns the user of the LDAP connection. + */ + Q_REQUIRED_RESULT QString user() const; + + /** + * Sets the @p bindDn of the LDAP connection. + */ + void setBindDn(const QString &bindDn); + + /** + * Returns the bindDn of the LDAP connection. + */ + Q_REQUIRED_RESULT QString bindDn() const; + + /** + * Sets the @p realm of the LDAP connection. + */ + void setRealm(const QString &realm); + + /** + * Returns the realm of the LDAP connection. + */ + Q_REQUIRED_RESULT QString realm() const; + + /** + * Sets the @p password of the LDAP connection. + */ + void setPassword(const QString &password); + + /** + * Returns the password of the LDAP connection. + */ + QString password() const; + + /** + * Sets the protocol @p version of the LDAP connection. + * If no version is set, 3 is used as default. + * @param version the protocol version to set + */ + void setVersion(int version); + + /** + * Returns the protocol version of the LDAP connection. + */ + Q_REQUIRED_RESULT int version() const; + + /** + * Sets the security @p mode of the LDAP connection. + * If no security is set, None is used as default. + * @param mode the security mode to set + */ + void setSecurity(Security mode); + + /** + * Returns the security mode of the LDAP connection. + */ + Q_REQUIRED_RESULT Security security() const; + + /** + * Sets the @p authentication method of the LDAP connection. + * If no authentication method is set, Anonymous is used as default. + * @param authentication the authentication method to set + */ + void setAuth(Auth authentication); + + /** + * Returns the authentication method of the LDAP connection. + */ + Q_REQUIRED_RESULT Auth auth() const; + + /** + * Sets the certificate require mode for TLS/SSL connections + */ + void setTLSRequireCertificate(TLSRequireCertificate reqCert); + + /** + * Returns the certificate require mode for TLS/SSL connections + */ + Q_REQUIRED_RESULT TLSRequireCertificate tlsRequireCertificate() const; + + /** + * Sets the CA certificate file for TLS/SSL connections + */ + void setTLSCACertFile(const QString &caCertFile); + + /** + * Returns the CA certificate file used for TLS/SSL connections. + */ + Q_REQUIRED_RESULT QString tlsCACertFile() const; + + /** + * Sets the @p mech of the LDAP connection. + */ + void setMech(const QString &mech); + + /** + * Returns the mech of the LDAP connection. + */ + Q_REQUIRED_RESULT QString mech() const; + + /** + * Sets the @p timeout of the LDAP connection. + */ + void setTimeout(int timeout); + + /** + * Returns the timeout of the LDAP connection. + */ + Q_REQUIRED_RESULT int timeout() const; + + /** + * Sets the search @p scope of the LDAP connection. + */ + void setScope(LdapUrl::Scope scope); + + /** + * Returns the search scope of the LDAP connection. + */ + Q_REQUIRED_RESULT LdapUrl::Scope scope() const; + + /** + * Sets the time @p limit of the LDAP connection. + */ + void setTimeLimit(int limit); + + /** + * Returns the time limit of the LDAP connection. + */ + Q_REQUIRED_RESULT int timeLimit() const; + + /** + * Sets the size @p limit of the LDAP connection. + */ + void setSizeLimit(int sizelimit); + + /** + * Returns the size limit of the LDAP connection. + */ + Q_REQUIRED_RESULT int sizeLimit() const; + + /** + * Sets the page @p size of the LDAP connection. + */ + void setPageSize(int size); + + /** + * Returns the page size of the LDAP connection. + */ + Q_REQUIRED_RESULT int pageSize() const; + + /** + * Sets the @p filter string of the LDAP connection. + */ + void setFilter(const QString &filter); + + /** + * Returns the filter string of the LDAP connection. + */ + Q_REQUIRED_RESULT QString filter() const; + + /** + * Sets the server parameters from an RFC2255 compliant LDAP @p url. + */ + void setUrl(const LdapUrl &url); + + /** + * Returns the server parameters as an RFC2255 compliant LDAP Url. + * The URL extensions which are supported: + * Standard: bindname + * KLDAP extensions: x-tls, x-version, x-sasl, x-mech, x-realm, + * x-sizelimit, x-timelimit, x-pagesize, x-timeout + */ + Q_REQUIRED_RESULT LdapUrl url() const; + + void setCompletionWeight(int value); + Q_REQUIRED_RESULT int completionWeight() const; + +private: + class LdapServerPrivate; + LdapServerPrivate *const d; +}; +} +KLDAP_EXPORT QDebug operator <<(QDebug d, const KLDAP::LdapServer &t); + +#endif diff --git a/3rdparty/kldap/src/core/ldapurl.cpp b/3rdparty/kldap/src/core/ldapurl.cpp new file mode 100644 index 0000000..9cc1011 --- /dev/null +++ b/3rdparty/kldap/src/core/ldapurl.cpp @@ -0,0 +1,288 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "ldapurl.h" + +#include "ldap_debug.h" + + +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapUrl::LdapUrlPrivate +{ +public: + LdapUrlPrivate() + : m_scope(Base) + { + } + + QMap m_extensions; + QStringList m_attributes; + Scope m_scope; + QString m_filter; +}; + +LdapUrl::LdapUrl() + : d(new LdapUrlPrivate) +{ +} + +LdapUrl::LdapUrl(const QUrl &_url) + : QUrl(_url) + , d(new LdapUrlPrivate) +{ + parseQuery(); +} + +LdapUrl::LdapUrl(const LdapUrl &that) + : QUrl(that) + , d(new LdapUrlPrivate) +{ + *d = *that.d; +} + +LdapUrl &LdapUrl::operator=(const LdapUrl &that) +{ + if (this == &that) { + return *this; + } + + QUrl::operator=(that); + *d = *that.d; + + return *this; +} + +LdapUrl::~LdapUrl() +{ + delete d; +} + +void LdapUrl::setDn(const LdapDN &dn) +{ + const QString tmp = dn.toString(); + if (tmp.startsWith(QLatin1Char('/'))) { + setPath(tmp); + } else { + setPath(QLatin1Char('/') + tmp); + } +} + +LdapDN LdapUrl::dn() const +{ + QString tmp = path(); + if (tmp.startsWith(QLatin1Char('/'))) { + tmp = tmp.mid(1); + } + LdapDN tmpDN(tmp); + return tmpDN; +} + +QStringList LdapUrl::attributes() const +{ + return d->m_attributes; +} + +void LdapUrl::setAttributes(const QStringList &attributes) +{ + d->m_attributes = attributes; + updateQuery(); +} + +LdapUrl::Scope LdapUrl::scope() const +{ + return d->m_scope; +} + +void LdapUrl::setScope(Scope scope) +{ + d->m_scope = scope; + updateQuery(); +} + +QString LdapUrl::filter() const +{ + return d->m_filter; +} + +void LdapUrl::setFilter(const QString &filter) +{ + d->m_filter = filter; + updateQuery(); +} + +bool LdapUrl::hasExtension(const QString &key) const +{ + return d->m_extensions.contains(key); +} + +LdapUrl::Extension LdapUrl::extension(const QString &key) const +{ + QMap::const_iterator it; + + it = d->m_extensions.constFind(key); + if (it != d->m_extensions.constEnd()) { + return *it; + } else { + Extension ext; + ext.value = QLatin1String(""); + ext.critical = false; + return ext; + } +} + +QString LdapUrl::extension(const QString &key, bool &critical) const +{ + const Extension ext = extension(key); + critical = ext.critical; + return ext.value; +} + +void LdapUrl::setExtension(const QString &key, const LdapUrl::Extension &ext) +{ + d->m_extensions[ key ] = ext; + updateQuery(); +} + +void LdapUrl::setExtension(const QString &key, const QString &value, bool critical) +{ + Extension ext; + ext.value = value; + ext.critical = critical; + setExtension(key, ext); +} + +void LdapUrl::setExtension(const QString &key, int value, bool critical) +{ + Extension ext; + ext.value = QString::number(value); + ext.critical = critical; + setExtension(key, ext); +} + +void LdapUrl::removeExtension(const QString &key) +{ + d->m_extensions.remove(key); + updateQuery(); +} + +void LdapUrl::updateQuery() +{ + QMap::const_iterator it; + QString q(QLatin1Char('?')); + + // set the attributes to query + if (!d->m_attributes.isEmpty()) { + q += d->m_attributes.join(QLatin1Char(',')); + } + + // set the scope + q += QLatin1Char('?'); + switch (d->m_scope) { + case Sub: + q += QStringLiteral("sub"); + break; + case One: + q += QStringLiteral("one"); + break; + case Base: + q += QStringLiteral("base"); + break; + } + + // set the filter + q += QLatin1Char('?'); + if (d->m_filter != QLatin1String("(objectClass=*)") && !d->m_filter.isEmpty()) { + q += QLatin1String(toPercentEncoding(d->m_filter)); + } + + // set the extensions + q += QLatin1Char('?'); + for (it = d->m_extensions.constBegin(); it != d->m_extensions.constEnd(); ++it) { + if (it.value().critical) { + q += QLatin1Char('!'); + } + q += it.key(); + if (!it.value().value.isEmpty()) { + q += QLatin1Char('=') + QLatin1String(toPercentEncoding(it.value().value)); + } + q += QLatin1Char(','); + } + while (q.endsWith(QLatin1Char('?')) || q.endsWith(QLatin1Char(','))) { + q.remove(q.length() - 1, 1); + } + + setQuery(q); + qCDebug(LDAP_LOG) << "LDAP URL updateQuery():" << toDisplayString(); +} + +void LdapUrl::parseQuery() +{ + Extension ext; + QStringList extensions; + QString q = query(QUrl::FullyEncoded); + // remove first ? + if (q.startsWith(QLatin1Char('?'))) { + q.remove(0, 1); + } + + // split into a list + const QStringList url_items = q.split(QLatin1Char('?')); + + d->m_attributes.clear(); + d->m_scope = Base; + d->m_filter = QStringLiteral("(objectClass=*)"); + d->m_extensions.clear(); + + int i = 0; + QStringList::const_iterator end(url_items.constEnd()); + for (QStringList::const_iterator it = url_items.constBegin(); + it != end; ++it, i++) { + switch (i) { + case 0: +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + d->m_attributes = (*it).split(QLatin1Char(','), QString::SkipEmptyParts); +#else + d->m_attributes = (*it).split(QLatin1Char(','), Qt::SkipEmptyParts); +#endif + break; + case 1: + if ((*it) == QLatin1String("sub")) { + d->m_scope = Sub; + } else if ((*it) == QLatin1String("one")) { + d->m_scope = One; + } + break; + case 2: + d->m_filter = fromPercentEncoding((*it).toLatin1()); + break; + case 3: +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + extensions = (*it).split(QLatin1Char(','), QString::SkipEmptyParts); +#else + extensions = (*it).split(QLatin1Char(','), Qt::SkipEmptyParts); +#endif + break; + } + } + + QString name, value; + QStringList::const_iterator end2(extensions.constEnd()); + for (QStringList::const_iterator it = extensions.constBegin(); + it != end2; ++it) { + ext.critical = false; + name = fromPercentEncoding((*it).section(QLatin1Char('='), 0, 0).toLatin1()).toLower(); + value = fromPercentEncoding((*it).section(QLatin1Char('='), 1).toLatin1()); + if (name.startsWith(QLatin1Char('!'))) { + ext.critical = true; + name.remove(0, 1); + } + qCDebug(LDAP_LOG) << "LdapUrl extensions name=" << name << "value:" << value; + ext.value = value.replace(QLatin1String("%2"), QLatin1String(",")); + setExtension(name, ext); + } +} diff --git a/3rdparty/kldap/src/core/ldapurl.h b/3rdparty/kldap/src/core/ldapurl.h new file mode 100644 index 0000000..2e212ae --- /dev/null +++ b/3rdparty/kldap/src/core/ldapurl.h @@ -0,0 +1,170 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KLDAP_LDAPURL_H +#define KLDAP_LDAPURL_H + +#include + +#include + +#include "ldapdn.h" +#include "kldap_export.h" + +// clazy:excludeall=copyable-polymorphic + +namespace KLDAP { +/** + * @short A special url class for LDAP. + * + * LdapUrl implements an RFC 2255 compliant LDAP Url parser, with minimal + * differences. LDAP Urls implemented by this class has the following format: + * ldap[s]://[user[:password]@]hostname[:port]["/" [dn ["?" [attributes] + * ["?" [scope] ["?" [filter] ["?" extensions]]]]]] + */ +class KLDAP_EXPORT LdapUrl : public QUrl +{ +public: + + /** + * A class holding the extension name and state whether + * the extension is critical. + */ + typedef struct { + QString value; + bool critical; + } Extension; + + /** + * Describes the scope of the LDAP url. + */ + typedef enum { + Base, ///< Only the same level as the url. + One, ///< The level of the url and the one below. + Sub ///< All levels below the url's level. + } Scope; + + /** + * Constructs an empty LDAP url. + */ + LdapUrl(); + + /** + * Constructs a LDAP url from a KUrl @p url. + */ + explicit LdapUrl(const QUrl &url); + + /** + * Constructs a LDAP url from an other url. + */ + LdapUrl(const LdapUrl &other); + + /** + * Overwrites the values of the LDAP url with values + * from an @p other url. + */ + LdapUrl &operator=(const LdapUrl &other); + + /** + * Destroys the LDAP url. + */ + ~LdapUrl(); + + /** + * Sets the @p dn part of the LDAP url. + */ + void setDn(const LdapDN &dn); + + /** + * Returns the dn part of the LDAP url. + * This is equal to path() with the slash removed from the beginning. + */ + Q_REQUIRED_RESULT LdapDN dn() const; + + /** + * Sets the @p attributes part of the LDAP url. + */ + void setAttributes(const QStringList &attributes); + + /** + * Returns the attributes part of the LDAP url. + */ + Q_REQUIRED_RESULT QStringList attributes() const; + + /** + * Sets the scope part of the LDAP url. + */ + void setScope(Scope scope); + + /** + * Returns the scope part of the LDAP url. + */ + Q_REQUIRED_RESULT Scope scope() const; + + /** + * Sets the filter part of the LDAP url. + */ + void setFilter(const QString &filter); + + /** + * Returns the filter part of the LDAP url. + */ + Q_REQUIRED_RESULT QString filter() const; + + /** + * Returns whether the specified @p extension exists in the LDAP url. + */ + Q_REQUIRED_RESULT bool hasExtension(const QString &extension) const; + + /** + * Returns the specified @p extension. + */ + Q_REQUIRED_RESULT Extension extension(const QString &extension) const; + + /** + * Returns the specified @p extension. + */ + Q_REQUIRED_RESULT QString extension(const QString &extension, bool &critical) const; + + /** + * Sets the specified extension @p key with the value and criticality in @p extension. + */ + void setExtension(const QString &key, const Extension &extension); + + /** + * Sets the specified extension @p key with the @p value and criticality specified. + */ + void setExtension(const QString &key, const QString &value, bool critical = false); + + /** + * Sets the specified extension @p key with the @p value and criticality specified. + */ + void setExtension(const QString &key, int value, bool critical = false); + + /** + * Removes the specified @p extension. + */ + void removeExtension(const QString &extension); + + /** + * Updates the query component from the attributes, scope, filter and extensions. + */ + void updateQuery(); + + /** + * Parses the query argument of the URL and makes it available via the + * attributes(), extension(), filter() and scope() methods + */ + void parseQuery(); + +private: + class LdapUrlPrivate; + LdapUrlPrivate *const d; +}; +} + +#endif diff --git a/3rdparty/kldap/src/core/ldif.cpp b/3rdparty/kldap/src/core/ldif.cpp new file mode 100644 index 0000000..5e604e4 --- /dev/null +++ b/3rdparty/kldap/src/core/ldif.cpp @@ -0,0 +1,434 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "ldif.h" + +#include "ldap_debug.h" + +using namespace KLDAP; + +class Q_DECL_HIDDEN Ldif::LdifPrivate +{ +public: + int mModType; + bool mDelOldRdn, mUrl; + LdapDN mDn; + QString mAttr, mNewRdn, mNewSuperior, mOid; + QByteArray mLdif, mValue; + EntryType mEntryType; + + bool mIsNewLine, mIsComment, mCritical; + ParseValue mLastParseValue; + uint mPos, mLineNumber; + QByteArray mLine; +}; + +Ldif::Ldif() : d(new LdifPrivate) +{ + startParsing(); +} + +Ldif::Ldif(const Ldif &that) : d(new LdifPrivate) +{ + *d = *that.d; + + startParsing(); +} + +Ldif &Ldif::operator=(const Ldif &that) +{ + if (this == &that) { + return *this; + } + + *d = *that.d; + + return *this; +} + +Ldif::~Ldif() +{ + delete d; +} + +QByteArray Ldif::assembleLine(const QString &fieldname, const QByteArray &value, uint linelen, bool url) +{ + bool safe = false; + bool isDn; + QByteArray result; + + if (url) { + result = fieldname.toUtf8() + ":< " + value; + } else { + isDn = fieldname.toLower() == QLatin1String("dn"); + //SAFE-INIT-CHAR + if (!value.isEmpty() && value[0] > 0 && value[0] != '\n' + && value[0] != '\r' && value[0] != ':' && value[0] != '<') { + safe = true; + } + + //SAFE-CHAR + if (safe) { + for (int i = 1; i < value.size(); i++) { + //allow utf-8 in Distinguished Names + if ((isDn && value[i] == 0) + || (!isDn && value[i] <= 0) + || value[i] == '\r' || value[i] == '\n') { + safe = false; + break; + } + } + } + + if (value.isEmpty()) { + safe = true; + } + + if (safe) { + result = fieldname.toUtf8() + ": " + value; + } else { + result = fieldname.toUtf8() + ":: " + value.toBase64(); + } + + if (linelen > 0) { + int i = (uint)(fieldname.length() + 2) > linelen ? fieldname.length() + 2 : linelen; + while (i < result.length()) { + result.insert(i, "\n "); + i += linelen + 2; + } + } + } + return result; +} + +QByteArray Ldif::assembleLine(const QString &fieldname, const QString &value, uint linelen, bool url) +{ + return assembleLine(fieldname, value.toUtf8(), linelen, url); +} + +bool Ldif::splitLine(const QByteArray &line, QString &fieldname, QByteArray &value) +{ + int position; + int linelen; + +// qCDebug(LDAP_LOG) << "line:" << QString::fromUtf8(line); + + position = line.indexOf(":"); + if (position == -1) { + // strange: we did not find a fieldname + fieldname = QLatin1String(""); + value = line.trimmed(); +// qCDebug(LDAP_LOG) << "value :" << value[0]; + return false; + } + + linelen = line.size(); + fieldname = QString::fromUtf8(line.left(position).trimmed()); + + if (linelen > (position + 1) && line[ position + 1 ] == ':') { + // String is BASE64 encoded -> decode it now. + if (linelen <= (position + 3)) { + value.resize(0); + return false; + } + value = QByteArray::fromBase64(line.mid(position + 3)); + return false; + } + + if (linelen > (position + 1) && line[ position + 1 ] == '<') { + // String is an URL. + if (linelen <= (position + 3)) { + value.resize(0); + return false; + } + value = QByteArray::fromBase64(line.mid(position + 3)); + return true; + } + + if (linelen <= (position + 2)) { + value.resize(0); + return false; + } + value = line.mid(position + 2); + return false; +} + +bool Ldif::splitControl(const QByteArray &line, QString &oid, bool &critical, QByteArray &value) +{ + QString tmp; + critical = false; + bool url = splitLine(line, tmp, value); + + qCDebug(LDAP_LOG) << "value:" << QString::fromUtf8(value); + if (tmp.isEmpty()) { + tmp = QString::fromUtf8(value); + value.resize(0); + } + if (tmp.endsWith(QLatin1String("true"))) { + critical = true; + tmp.chop(5); + } else if (tmp.endsWith(QLatin1String("false"))) { + critical = false; + tmp.chop(6); + } + oid = tmp; + return url; +} + +Ldif::ParseValue Ldif::processLine() +{ + if (d->mIsComment) { + return None; + } + + ParseValue retval = None; + if (d->mLastParseValue == EndEntry) { + d->mEntryType = Entry_None; + } + + d->mUrl = splitLine(d->mLine, d->mAttr, d->mValue); + + const QString attrLower = d->mAttr.toLower(); + + switch (d->mEntryType) { + case Entry_None: + if (attrLower == QLatin1String("version")) { + if (!d->mDn.isEmpty()) { + retval = Err; + } + } else if (attrLower == QLatin1String("dn")) { + qCDebug(LDAP_LOG) << "ldapentry dn:" << QString::fromUtf8(d->mValue); + d->mDn = LdapDN(QString::fromUtf8(d->mValue)); + d->mModType = Mod_None; + retval = NewEntry; + } else if (attrLower == QLatin1String("changetype")) { + if (d->mDn.isEmpty()) { + retval = Err; + } else { + QString tmpval = QString::fromUtf8(d->mValue); + qCDebug(LDAP_LOG) << "changetype:" << tmpval; + if (tmpval == QLatin1String("add")) { + d->mEntryType = Entry_Add; + } else if (tmpval == QLatin1String("delete")) { + d->mEntryType = Entry_Del; + } else if (tmpval == QLatin1String("modrdn") || tmpval == QLatin1String("moddn")) { + d->mNewRdn.clear(); + d->mNewSuperior.clear(); + d->mDelOldRdn = true; + d->mEntryType = Entry_Modrdn; + } else if (tmpval == QLatin1String("modify")) { + d->mEntryType = Entry_Mod; + } else { + retval = Err; + } + } + } else if (attrLower == QLatin1String("control")) { + d->mUrl = splitControl(d->mValue, d->mOid, d->mCritical, d->mValue); + retval = Control; + } else if (!d->mAttr.isEmpty() && !d->mValue.isEmpty()) { + d->mEntryType = Entry_Add; + retval = Item; + } + break; + case Entry_Add: + if (d->mAttr.isEmpty() && d->mValue.isEmpty()) { + retval = EndEntry; + } else { + retval = Item; + } + break; + case Entry_Del: + if (d->mAttr.isEmpty() && d->mValue.isEmpty()) { + retval = EndEntry; + } else { + retval = Err; + } + break; + case Entry_Mod: + if (d->mModType == Mod_None) { + qCDebug(LDAP_LOG) << "new modtype" << d->mAttr; + if (d->mAttr.isEmpty() && d->mValue.isEmpty()) { + retval = EndEntry; + } else if (attrLower == QLatin1String("add")) { + d->mModType = Mod_Add; + } else if (attrLower == QLatin1String("replace")) { + d->mModType = Mod_Replace; + d->mAttr = QString::fromUtf8(d->mValue); + d->mValue = QByteArray(); + retval = Item; + } else if (attrLower == QLatin1String("delete")) { + d->mModType = Mod_Del; + d->mAttr = QString::fromUtf8(d->mValue); + d->mValue = QByteArray(); + retval = Item; + } else { + retval = Err; + } + } else { + if (d->mAttr.isEmpty()) { + if (QString::fromUtf8(d->mValue) == QLatin1String("-")) { + d->mModType = Mod_None; + } else if (d->mValue.isEmpty()) { + retval = EndEntry; + } else { + retval = Err; + } + } else { + retval = Item; + } + } + break; + case Entry_Modrdn: + if (d->mAttr.isEmpty() && d->mValue.isEmpty()) { + retval = EndEntry; + } else if (attrLower == QLatin1String("newrdn")) { + d->mNewRdn = QString::fromUtf8(d->mValue); + } else if (attrLower == QLatin1String("newsuperior")) { + d->mNewSuperior = QString::fromUtf8(d->mValue); + } else if (attrLower == QLatin1String("deleteoldrdn")) { + if (d->mValue.size() > 0 && d->mValue[0] == '0') { + d->mDelOldRdn = false; + } else if (d->mValue.size() > 0 && d->mValue[0] == '1') { + d->mDelOldRdn = true; + } else { + retval = Err; + } + } else { + retval = Err; + } + break; + } + return retval; +} + +Ldif::ParseValue Ldif::nextItem() +{ + ParseValue retval = None; + char c = 0; + + while (retval == None) { + if (d->mPos < (uint)d->mLdif.size()) { + c = d->mLdif.at(d->mPos); + d->mPos++; + if (d->mIsNewLine && c == '\r') { + continue; //handle \n\r line end + } + if (d->mIsNewLine && (c == ' ' || c == '\t')) { //line folding + d->mIsNewLine = false; + continue; + } + if (d->mIsNewLine) { + d->mIsNewLine = false; + retval = processLine(); + d->mLastParseValue = retval; + d->mLine.resize(0); + d->mIsComment = (c == '#'); + } + if (c == '\n' || c == '\r') { + d->mLineNumber++; + d->mIsNewLine = true; + continue; + } + } else { + retval = MoreData; + break; + } + + if (!d->mIsComment) { + d->mLine += c; + } + } + return retval; +} + +void Ldif::endLdif() +{ + QByteArray tmp(3, '\n'); + d->mLdif = tmp; + d->mPos = 0; +} + +void Ldif::startParsing() +{ + d->mPos = d->mLineNumber = 0; + d->mDelOldRdn = false; + d->mEntryType = Entry_None; + d->mModType = Mod_None; + d->mDn = LdapDN(); + d->mNewRdn.clear(); + d->mNewSuperior.clear(); + d->mLine = QByteArray(); + d->mIsNewLine = false; + d->mIsComment = false; + d->mLastParseValue = None; +} + +void Ldif::setLdif(const QByteArray &ldif) +{ + d->mLdif = ldif; + d->mPos = 0; +} + +Ldif::EntryType Ldif::entryType() const +{ + return d->mEntryType; +} + +int Ldif::modType() const +{ + return d->mModType; +} + +LdapDN Ldif::dn() const +{ + return d->mDn; +} + +QString Ldif::newRdn() const +{ + return d->mNewRdn; +} + +QString Ldif::newSuperior() const +{ + return d->mNewSuperior; +} + +bool Ldif::delOldRdn() const +{ + return d->mDelOldRdn; +} + +QString Ldif::attr() const +{ + return d->mAttr; +} + +QByteArray Ldif::value() const +{ + return d->mValue; +} + +bool Ldif::isUrl() const +{ + return d->mUrl; +} + +bool Ldif::isCritical() const +{ + return d->mCritical; +} + +QString Ldif::oid() const +{ + return d->mOid; +} + +uint Ldif::lineNumber() const +{ + return d->mLineNumber; +} diff --git a/3rdparty/kldap/src/core/ldif.h b/3rdparty/kldap/src/core/ldif.h new file mode 100644 index 0000000..2363066 --- /dev/null +++ b/3rdparty/kldap/src/core/ldif.h @@ -0,0 +1,183 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KLDAP_LDIF_H +#define KLDAP_LDIF_H + +#include +#include + +#include "ldapdn.h" +#include "kldap_export.h" + +// clazy:excludeall=copyable-polymorphic + +namespace KLDAP { +/** + * Ldif + * + * Ldif implements an RFC 2849 compliant Ldif parser. Ldif files are used to + * represent directory information on LDAP-based servers, or to describe a set + * of changes which are to be applied to a directory. + */ + +class KLDAP_EXPORT Ldif +{ +public: + + typedef enum { + None, NewEntry, EndEntry, Item, Control, Err, MoreData + } ParseValue; + + typedef enum { + Entry_None, Entry_Add, Entry_Del, Entry_Mod, Entry_Modrdn + } EntryType; + + typedef enum { + Mod_None, Mod_Add, Mod_Replace, Mod_Del + } ModType; + + Ldif(); + + Ldif(const Ldif &that); + Ldif &operator=(const Ldif &that); + + ~Ldif(); + + /** + * Assembles fieldname and value into a valid Ldif line, BASE64 encodes the + * value if necessary and optionally splits into more lines. + * @param fieldname The name of the entry. + * @param value The value of the entry. + * @param linelen Maximum length of the lines in the result. + * @param url If true, encode value as url ( use :< ). + */ + Q_REQUIRED_RESULT static QByteArray assembleLine(const QString &fieldname, const QByteArray &value, uint linelen = 0, bool url = false); + /** + * This is the same as the above function, the only difference that + * this accepts QString as the value. + */ + Q_REQUIRED_RESULT static QByteArray assembleLine(const QString &fieldname, const QString &value, uint linelen = 0, bool url = false); + + /** + * Splits one line from an Ldif file to attribute and value components. + * @return true if value is an URL, false otherwise + */ + Q_REQUIRED_RESULT static bool splitLine(const QByteArray &line, QString &fieldname, QByteArray &value); + + /** + * Splits a control specification (without the "control:" directive) + * @param line is the control directive + * @param oid will contain the OID + * @param critical will contain the criticality of control + * @param value is the control value + */ + Q_REQUIRED_RESULT static bool splitControl(const QByteArray &line, QString &oid, bool &critical, QByteArray &value); + + /** + * Starts the parsing of a new Ldif + */ + void startParsing(); + + /** + * Process one Ldif line + */ + Q_REQUIRED_RESULT ParseValue processLine(); + + /** + * Process the Ldif until a complete item can be returned + * @return NewEntry if a new DN encountered, Item if a new item returned, + * Err if the Ldif contains error, EndEntry if the parser reached the end + * of the current entry and MoreData if the parser encountered the end of + * the current chunk of the Ldif. + * + * If you want to finish the parsing after receiving MoreData, then call + * endLdif(), so the parser can safely flush the current entry. + */ + Q_REQUIRED_RESULT ParseValue nextItem(); + + /** + * Sets a chunk of Ldif. Call before startParsing(), or if nextItem() + * returned MoreData. + * @param ldif the Ldif chunk to set + */ + void setLdif(const QByteArray &ldif); + + /** + * Indicates the end of the Ldif file/stream. Call if nextItem() returned + * MoreData, but actually you don't have more data. + */ + void endLdif(); + + /** + * Returns the requested LDAP operation extracted from the current entry. + */ + Q_REQUIRED_RESULT EntryType entryType() const; + + /** + * Returns the LDAP modify request type if entryType() returned Entry_Mod. + */ + Q_REQUIRED_RESULT int modType() const; + + /** + * Returns the Distinguished Name of the current entry. + */ + Q_REQUIRED_RESULT LdapDN dn() const; + + /** + * Returns the new Relative Distinguished Name if modType() returned + * Entry_Modrdn. + */ + Q_REQUIRED_RESULT QString newRdn() const; + + /** + * Returns the new parent of the entry if modType() returned Entry_Modrdn. + */ + QString newSuperior() const; + + /** + * Returns if the delete of the old RDN is required. + */ + Q_REQUIRED_RESULT bool delOldRdn() const; + + /** + * Returns the attribute name. + */ + Q_REQUIRED_RESULT QString attr() const; + + /** + * Returns the attribute value. + */ + Q_REQUIRED_RESULT QByteArray value() const; + + /** + * Returns if val() is an url + */ + Q_REQUIRED_RESULT bool isUrl() const; + + /** + * Returns the criticality level when modType() returned Control. + */ + Q_REQUIRED_RESULT bool isCritical() const; + + /** + * Returns the OID when modType() returned Control. + */ + Q_REQUIRED_RESULT QString oid() const; + + /** + * Returns the line number which the parser processes. + */ + Q_REQUIRED_RESULT uint lineNumber() const; + +private: + class LdifPrivate; + LdifPrivate *const d; +}; +} + +#endif diff --git a/3rdparty/kldap/src/core/w32-ldap-help.h b/3rdparty/kldap/src/core/w32-ldap-help.h new file mode 100644 index 0000000..e3f6bfb --- /dev/null +++ b/3rdparty/kldap/src/core/w32-ldap-help.h @@ -0,0 +1,119 @@ +//krazy:excludeall=style +/* w32-ldap-help.h - Map utf8 based API into a wchar_t API. + + SPDX-FileCopyrightText: 2010 Andre Heinecke + + SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#ifndef W32_LDAP_HELP_H +#define W32_LDAP_HELP_H + +#include +#ifdef UNICODE +# undef UNICODE +# include +# include +# define UNICODE +#else +# include +# include +#endif // UNICODE + +/* + * From the openldap manpage: + * ber_len_t is an unsigned integer of at least 32 bits used to represent + * a length. It is commonly equivalent to a size_t. ber_slen_t is the + * signed variant to ber_len_t. + */ +typedef ULONG ber_len_t; + +#ifndef timeval +#define timeval l_timeval +#endif + +/* Redirect used ldap functions to functions with win_ prefix + * to further redirect those depending on the Windows Flavour */ +//#define ldap_err2string(a) win_ldap_err2string(a) +#define ldap_init(a, b) win_ldap_init(a, b) +#define ldap_sasl_bind(a, b, c, d, e, f, g) \ + win_ldap_sasl_bind(a, b, c, d, e, f, g) +#define ldap_sasl_bind_s(a, b, c, d, e, f, g) \ + win_ldap_sasl_bind_s(a, b, c, d, e, f, g) +#define ldap_parse_sasl_bind_result (a, b, c, d, e) \ + win_ldap_parse_sasl_bind_result((a), (b), (c), (d), (e)) +#define ldap_get_dn(a, b) win_ldap_get_dn(a, b) +#define ldap_memfree(a) win_ldap_memfree(a) +#define ldap_mods_free(a, b) win_ldap_mods_free(a, b) +#define ldap_first_attribute(a, b, c) \ + win_ldap_first_attribute(a, b, c) +#define ldap_get_values_len(a, b, c) \ + win_ldap_get_values_len(a, b, c) +#define ldap_next_attribute(a, b, c) \ + win_ldap_next_attribute(a, b, c) +#define ldap_parse_result(a, b, c, d, e, f, g, h) \ + win_ldap_parse_result(a, b, c, d, e, f, g, h) +#define ldap_parse_extended_result(a, b, c, d, e) \ + win_ldap_parse_extended_result(a, b, c, d, e) +#define ldap_add_ext(a, b, c, d, e, f) \ + win_ldap_add_ext((a), (b), (c), (d), (e), (f)) +#define ldap_add_ext_s(a, b, c, d, e) \ + win_ldap_add_ext_s((a), (b), (c), (d), (e)) +# define ldap_compare_ext_s(a, b, c, d, e, f) \ + win_ldap_compare_ext_s((a), (b), (c), (d), (e), (f)) +# define ldap_compare_ext(a, b, c, d, e, f, g) \ + win_ldap_compare_ext((a), (b), (c), (d), (e), (f), (g)) +# define ldap_modify_ext_s(a, b, c, d, e) \ + win_ldap_modify_ext_s((a), (b), (c), (d), (e)) +# define ldap_search_ext(a, b, c, d, e, f, g, h, i, j, k) \ + win_ldap_search_ext((a), (b), (c), (d), (e), (f), (g), (h), (i), (j), (k)) +#define ldap_rename_ext(a, b, c, d, e, f, g, h) \ + win_ldap_rename_ext((a), (b), (c), (d), (e), (f), (g), (h)) +#define ldap_rename(a, b, c, d, e, f, g, h) \ + ldap_rename_ext((a), (b), (c), (d), (e), (f), (g), (h)) +#define ldap_delete_ext(a, b, c, d, e) \ + win_ldap_delete_ext((a), (b), (c), (d), (e)) +#define ldap_modify_ext(a, b, c, d, e, f) \ + win_ldap_modify_ext((a), (b), (c), (d), (e), (f)) +#define ldap_abandon_ext(a, b, c, d) \ + win_ldap_abandon_ext((a), (b), (c), (d)) +#define ldap_controls_free(a) win_ldap_controls_free(a) + +// Use the functions that are available on the platform +// or redirect to wrapper functions + +/* Windows offers ASCII variants of most LDAP functions + * we only have to ensure that those are used */ +# define LDAPControl LDAPControlA +# define LDAPMod LDAPModA +# define win_ldap_init(a, b) ldap_initA((a), (b)) +# define win_ldap_simple_bind_s(a, b, c) ldap_simple_bind_sA((a), (b), (c)) +# define win_ldap_sasl_bind(a, b, c, d, e, f, g) \ + ldap_sasl_bindA(a, b, c, d, e, f, g) +# define win_ldap_sasl_bind_s(a, b, c, d, e, f, g) \ + ldap_sasl_bind_sA(a, b, c, d, e, f, g) +# define win_ldap_search_st(a, b, c, d, e, f, g, h) \ + ldap_search_stA((a), (b), (c), (d), (e), (f), (g), (h)) +# define win_ldap_search_ext(a, b, c, d, e, f, g, h, i, j, k) \ + my_win_ldap_search_ext((a), (b), (c), (d), (e), (f), (g), (h), (i), (j), (k)) +# define win_ldap_get_dn(a, b) ldap_get_dnA((a), (b)) +# define win_ldap_first_attribute(a, b, c) ldap_first_attributeA((a), (b), (c)) +# define win_ldap_next_attribute(a, b, c) ldap_next_attributeA((a), (b), (c)) +# define win_ldap_get_values_len(a, b, c) ldap_get_values_lenA((a), (b), (c)) +# define win_ldap_memfree(a) ldap_memfreeA((a)) +# define win_ldap_err2string(a) ldap_err2stringA((a)) +# define win_ldap_controls_free(a) ldap_controls_freeA((a)) +# define win_ldap_mods_free(a, b) ldap_mods_freeA((a), (b)) +# define win_ldap_add_ext(a, b, c, d, e, f) \ + ldap_add_extA((a), (b), (c), (d), (e), ((ulong *)f)) +# define win_ldap_add_ext_s(a, b, c, d, e) \ + ldap_add_ext_sA((a), (b), (c), (d), (e)) +# define win_ldap_parse_extended_result(a, b, c, d, e) \ + ldap_parse_extended_resultA((*a), (b), (c), (d), (e)) +# define win_ldap_parse_result(a, b, c, d, e, f, g, h) \ + ldap_parse_resultA((a), (b), ((ulong *)c), (d), (e), (f), (g), (h)) +# define win_ldap_modify_ext_s(a, b, c, d, e) \ + ldap_modify_ext_sW((a), (b), (c), (d), (e)) +# define win_ldap_compare_ext_s(a, b, c, d, e, f) \ + ldap_compare_ext_sA((a), (b), (c), (d), (e), (f)) +#endif /*W32_LDAP_HELP_H*/ diff --git a/3rdparty/kldap/src/kldap_config.h.cmake b/3rdparty/kldap/src/kldap_config.h.cmake new file mode 100644 index 0000000..4cd71b5 --- /dev/null +++ b/3rdparty/kldap/src/kldap_config.h.cmake @@ -0,0 +1,12 @@ +#cmakedefine LDAP_FOUND +#cmakedefine HAVE_WINLDAP_H +#cmakedefine HAVE_LDAP_H +#cmakedefine HAVE_SYS_TIME_H +#cmakedefine HAVE_LDAP_START_TLS_S +#cmakedefine HAVE_LDAP_INITIALIZE +#cmakedefine HAVE_BER_MEMFREE +#cmakedefine HAVE_LDAP_UNBIND_EXT +#cmakedefine HAVE_LDAP_EXTENDED_OPERATION +#cmakedefine HAVE_LDAP_EXTENDED_OPERATION_S +#cmakedefine HAVE_LDAP_EXTENDED_OPERATION_PROTOTYPE +#cmakedefine HAVE_LDAP_EXTENDED_OPERATION_S_PROTOTYPE diff --git a/3rdparty/kldap/src/widgets/addhostdialog.cpp b/3rdparty/kldap/src/widgets/addhostdialog.cpp new file mode 100644 index 0000000..2c08be5 --- /dev/null +++ b/3rdparty/kldap/src/widgets/addhostdialog.cpp @@ -0,0 +1,188 @@ +/* + This file is part of libkldap. + + SPDX-FileCopyrightText: 2002-2010 Tobias Koenig + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "addhostdialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace KLDAP; +class KLDAP::AddHostDialogPrivate +{ +public: + AddHostDialogPrivate(AddHostDialog *qq) + : q(qq) + { + } + + ~AddHostDialogPrivate() + { + writeConfig(); + } + + void readConfig(); + void writeConfig(); + KLDAP::LdapConfigWidget *mCfg = nullptr; + KLDAP::LdapServer *mServer = nullptr; + QPushButton *mOkButton = nullptr; + AddHostDialog *const q; +}; + +void AddHostDialogPrivate::readConfig() +{ + KConfigGroup group(KSharedConfig::openConfig(), "AddHostDialog"); + const QSize size = group.readEntry("Size", QSize(600, 400)); + if (size.isValid()) { + q->resize(size); + } +} + +void AddHostDialogPrivate::writeConfig() +{ + KConfigGroup group(KSharedConfig::openConfig(), "AddHostDialog"); + group.writeEntry("Size", q->size()); + group.sync(); +} + +AddHostDialog::AddHostDialog(KLDAP::LdapServer *server, QWidget *parent) + : QDialog(parent) + , d(new KLDAP::AddHostDialogPrivate(this)) +{ + setWindowTitle(i18nc("@title:window", "Add Host")); + auto *mainLayout = new QVBoxLayout(this); + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + d->mOkButton = buttonBox->button(QDialogButtonBox::Ok); + d->mOkButton->setDefault(true); + d->mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return); + connect(buttonBox, &QDialogButtonBox::rejected, this, &AddHostDialog::reject); + + setModal(true); + + d->mServer = server; + + QWidget *page = new QWidget(this); + mainLayout->addWidget(page); + mainLayout->addWidget(buttonBox); + auto *layout = new QHBoxLayout(page); + layout->setContentsMargins(0, 0, 0, 0); + + d->mCfg = new KLDAP::LdapConfigWidget( + KLDAP::LdapConfigWidget::W_USER + |KLDAP::LdapConfigWidget::W_PASS + |KLDAP::LdapConfigWidget::W_BINDDN + |KLDAP::LdapConfigWidget::W_REALM + |KLDAP::LdapConfigWidget::W_HOST + |KLDAP::LdapConfigWidget::W_PORT + |KLDAP::LdapConfigWidget::W_VER + |KLDAP::LdapConfigWidget::W_TIMELIMIT + |KLDAP::LdapConfigWidget::W_SIZELIMIT + |KLDAP::LdapConfigWidget::W_PAGESIZE + |KLDAP::LdapConfigWidget::W_DN + |KLDAP::LdapConfigWidget::W_FILTER + |KLDAP::LdapConfigWidget::W_SECBOX + |KLDAP::LdapConfigWidget::W_AUTHBOX, + page); + + layout->addWidget(d->mCfg); + d->mCfg->setHost(d->mServer->host()); + d->mCfg->setPort(d->mServer->port()); + d->mCfg->setDn(d->mServer->baseDn()); + d->mCfg->setUser(d->mServer->user()); + d->mCfg->setBindDn(d->mServer->bindDn()); + d->mCfg->setPassword(d->mServer->password()); + d->mCfg->setTimeLimit(d->mServer->timeLimit()); + d->mCfg->setSizeLimit(d->mServer->sizeLimit()); + d->mCfg->setPageSize(d->mServer->pageSize()); + d->mCfg->setVersion(d->mServer->version()); + d->mCfg->setFilter(d->mServer->filter()); + switch (d->mServer->security()) { + case KLDAP::LdapServer::TLS: + d->mCfg->setSecurity(KLDAP::LdapConfigWidget::TLS); + break; + case KLDAP::LdapServer::SSL: + d->mCfg->setSecurity(KLDAP::LdapConfigWidget::SSL); + break; + default: + d->mCfg->setSecurity(KLDAP::LdapConfigWidget::None); + } + + switch (d->mServer->auth()) { + case KLDAP::LdapServer::Simple: + d->mCfg->setAuth(KLDAP::LdapConfigWidget::Simple); + break; + case KLDAP::LdapServer::SASL: + d->mCfg->setAuth(KLDAP::LdapConfigWidget::SASL); + break; + default: + d->mCfg->setAuth(KLDAP::LdapConfigWidget::Anonymous); + } + d->mCfg->setMech(d->mServer->mech()); + + KAcceleratorManager::manage(this); + connect(d->mCfg, &KLDAP::LdapConfigWidget::hostNameChanged, this, &AddHostDialog::slotHostEditChanged); + connect(d->mOkButton, &QPushButton::clicked, this, &AddHostDialog::slotOk); + d->mOkButton->setEnabled(!d->mServer->host().isEmpty()); + d->readConfig(); +} + +AddHostDialog::~AddHostDialog() +{ + delete d; +} + +void AddHostDialog::slotHostEditChanged(const QString &text) +{ + d->mOkButton->setEnabled(!text.isEmpty()); +} + +void AddHostDialog::slotOk() +{ + d->mServer->setHost(d->mCfg->host()); + d->mServer->setPort(d->mCfg->port()); + d->mServer->setBaseDn(d->mCfg->dn()); + d->mServer->setUser(d->mCfg->user()); + d->mServer->setBindDn(d->mCfg->bindDn()); + d->mServer->setPassword(d->mCfg->password()); + d->mServer->setTimeLimit(d->mCfg->timeLimit()); + d->mServer->setSizeLimit(d->mCfg->sizeLimit()); + d->mServer->setPageSize(d->mCfg->pageSize()); + d->mServer->setVersion(d->mCfg->version()); + d->mServer->setFilter(d->mCfg->filter()); + switch (d->mCfg->security()) { + case KLDAP::LdapConfigWidget::TLS: + d->mServer->setSecurity(KLDAP::LdapServer::TLS); + break; + case KLDAP::LdapConfigWidget::SSL: + d->mServer->setSecurity(KLDAP::LdapServer::SSL); + break; + default: + d->mServer->setSecurity(KLDAP::LdapServer::None); + } + switch (d->mCfg->auth()) { + case KLDAP::LdapConfigWidget::Simple: + d->mServer->setAuth(KLDAP::LdapServer::Simple); + break; + case KLDAP::LdapConfigWidget::SASL: + d->mServer->setAuth(KLDAP::LdapServer::SASL); + break; + default: + d->mServer->setAuth(KLDAP::LdapServer::Anonymous); + } + d->mServer->setMech(d->mCfg->mech()); + QDialog::accept(); +} + +#include "moc_addhostdialog.cpp" diff --git a/3rdparty/kldap/src/widgets/addhostdialog.h b/3rdparty/kldap/src/widgets/addhostdialog.h new file mode 100644 index 0000000..c7c2e95 --- /dev/null +++ b/3rdparty/kldap/src/widgets/addhostdialog.h @@ -0,0 +1,42 @@ +/* + This file is part of libkldap. + + SPDX-FileCopyrightText: 2002-2010 Tobias Koenig + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef ADDHOSTDIALOG_H +#define ADDHOSTDIALOG_H + +#include "kldap_export.h" +#include + +namespace KLDAP { +class LdapServer; +class AddHostDialogPrivate; +/** + * @brief The AddHostDialog class + * @author Laurent Montel + */ +class KLDAP_EXPORT AddHostDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AddHostDialog(KLDAP::LdapServer *server, QWidget *parent = nullptr); + ~AddHostDialog(); + +Q_SIGNALS: + void changed(bool); + +private Q_SLOTS: + void slotHostEditChanged(const QString &); + void slotOk(); + +private: + AddHostDialogPrivate *const d; +}; +} + +#endif // ADDHOSTDIALOG_H diff --git a/3rdparty/kldap/src/widgets/ldapclient.cpp b/3rdparty/kldap/src/widgets/ldapclient.cpp new file mode 100644 index 0000000..75550bb --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapclient.cpp @@ -0,0 +1,283 @@ +/* kldapclient.cpp - LDAP access + * SPDX-FileCopyrightText: 2002 Klarälvdalens Datakonsult AB + * SPDX-FileContributor: Steffen Hansen + * + * Ported to KABC by Daniel Molkentin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "ldapclient.h" +#include "ldapclient_debug.h" + +#include +#include +#include +#include + +#include + +#include + +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapClient::Private +{ +public: + Private(LdapClient *qq) + : q(qq) + { + } + + ~Private() + { + q->cancelQuery(); + } + + void startParseLDIF(); + void parseLDIF(const QByteArray &data); + void endParseLDIF(); + void finishCurrentObject(); + + void slotData(KIO::Job *, const QByteArray &data); + void slotInfoMessage(KJob *, const QString &info, const QString &); + void slotDone(); + + LdapClient *const q; + + KLDAP::LdapServer mServer; + QString mScope; + QStringList mAttrs; + + QPointer mJob = nullptr; + bool mActive = false; + + KLDAP::LdapObject mCurrentObject; + KLDAP::Ldif mLdif; + int mClientNumber = 0; + int mCompletionWeight = 0; +}; + +LdapClient::LdapClient(int clientNumber, QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ + d->mClientNumber = clientNumber; + d->mCompletionWeight = 50 - d->mClientNumber; +} + +LdapClient::~LdapClient() +{ + delete d; +} + +bool LdapClient::isActive() const +{ + return d->mActive; +} + +void LdapClient::setServer(const KLDAP::LdapServer &server) +{ + d->mServer = server; +} + +const KLDAP::LdapServer LdapClient::server() const +{ + return d->mServer; +} + +void LdapClient::setAttributes(const QStringList &attrs) +{ + d->mAttrs = attrs; + d->mAttrs << QStringLiteral("objectClass"); // via objectClass we detect distribution lists +} + +QStringList LdapClient::attributes() const +{ + return d->mAttrs; +} + +void LdapClient::setScope(const QString &scope) +{ + d->mScope = scope; +} + +void LdapClient::startQuery(const QString &filter) +{ + cancelQuery(); + KLDAP::LdapUrl url{d->mServer.url()}; + + url.setAttributes(d->mAttrs); + url.setScope(d->mScope == QLatin1String("one") ? KLDAP::LdapUrl::One : KLDAP::LdapUrl::Sub); + const QString userFilter = url.filter(); + QString finalFilter = filter; + // combine the filter set by the user in the config dialog (url.filter()) and the filter from this query + if (!userFilter.isEmpty()) { + finalFilter = QLatin1String("&(") + finalFilter + QLatin1String(")(") + userFilter + QLatin1Char(')'); + } + url.setFilter(QLatin1Char('(') + finalFilter + QLatin1Char(')')); + + qCDebug(LDAPCLIENT_LOG) << "LdapClient: Doing query:" << url.toDisplayString(); + + d->startParseLDIF(); + d->mActive = true; + KIO::TransferJob *transfertJob = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo); + d->mJob = transfertJob; + connect(transfertJob, &KIO::TransferJob::data, this, [this](KIO::Job *job, const QByteArray &data) { + d->slotData(job, data); + }); + connect(d->mJob.data(), &KIO::TransferJob::infoMessage, this, + [this](KJob *job, const QString &str, const QString &val) { + d->slotInfoMessage(job, str, val); + }); + connect(d->mJob.data(), &KIO::TransferJob::result, + this, [this]() { + d->slotDone(); + }); +} + +void LdapClient::cancelQuery() +{ + if (d->mJob) { + d->mJob->kill(); + d->mJob = nullptr; + } + + d->mActive = false; +} + +void LdapClient::Private::slotData(KIO::Job *, const QByteArray &data) +{ + parseLDIF(data); +} + +void LdapClient::Private::slotInfoMessage(KJob *, const QString &info, const QString &) +{ + qCDebug(LDAPCLIENT_LOG) << "Job said :" << info; +} + +void LdapClient::Private::slotDone() +{ + endParseLDIF(); + mActive = false; + if (!mJob) { + return; + } + int err = mJob->error(); + if (err && err != KIO::ERR_USER_CANCELED) { + Q_EMIT q->error(mJob->errorString()); + } + Q_EMIT q->done(); +} + +void LdapClient::Private::startParseLDIF() +{ + mCurrentObject.clear(); + mLdif.startParsing(); +} + +void LdapClient::Private::endParseLDIF() +{ +} + +void LdapClient::Private::finishCurrentObject() +{ + mCurrentObject.setDn(mLdif.dn()); + KLDAP::LdapAttrValue objectclasses; + const KLDAP::LdapAttrMap::ConstIterator end = mCurrentObject.attributes().constEnd(); + for (KLDAP::LdapAttrMap::ConstIterator it = mCurrentObject.attributes().constBegin(); + it != end; ++it) { + if (it.key().toLower() == QLatin1String("objectclass")) { + objectclasses = it.value(); + break; + } + } + + bool groupofnames = false; + const KLDAP::LdapAttrValue::ConstIterator endValue(objectclasses.constEnd()); + for (KLDAP::LdapAttrValue::ConstIterator it = objectclasses.constBegin(); + it != endValue; ++it) { + const QByteArray sClass = (*it).toLower(); + if (sClass == "groupofnames" || sClass == "kolabgroupofnames") { + groupofnames = true; + } + } + + if (groupofnames) { + KLDAP::LdapAttrMap::ConstIterator it = mCurrentObject.attributes().find(QStringLiteral("mail")); + if (it == mCurrentObject.attributes().end()) { + // No explicit mail address found so far? + // Fine, then we use the address stored in the DN. + QString sMail; +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + const QStringList lMail = mCurrentObject.dn().toString().split(QStringLiteral(",dc="), QString::SkipEmptyParts); +#else + const QStringList lMail = mCurrentObject.dn().toString().split(QStringLiteral(",dc="), Qt::SkipEmptyParts); +#endif + const int n = lMail.count(); + if (n) { + if (lMail.first().startsWith(QLatin1String("cn="), Qt::CaseInsensitive)) { + sMail = lMail.first().simplified().mid(3); + if (1 < n) { + sMail.append(QLatin1Char('@')); + } + for (int i = 1; i < n; ++i) { + sMail.append(lMail.at(i)); + if (i < n - 1) { + sMail.append(QLatin1Char('.')); + } + } + mCurrentObject.addValue(QStringLiteral("mail"), sMail.toUtf8()); + } + } + } + } + Q_EMIT q->result(*q, mCurrentObject); + mCurrentObject.clear(); +} + +void LdapClient::Private::parseLDIF(const QByteArray &data) +{ + //qCDebug(LDAPCLIENT_LOG) <<"LdapClient::parseLDIF(" << QCString(data.data(), data.size()+1) <<" )"; + if (!data.isEmpty()) { + mLdif.setLdif(data); + } else { + mLdif.endLdif(); + } + KLDAP::Ldif::ParseValue ret; + QString name; + do { + ret = mLdif.nextItem(); + switch (ret) { + case KLDAP::Ldif::Item: + { + name = mLdif.attr(); + const QByteArray value = mLdif.value(); + mCurrentObject.addValue(name, value); + break; + } + case KLDAP::Ldif::EndEntry: + finishCurrentObject(); + break; + default: + break; + } + } while (ret != KLDAP::Ldif::MoreData); +} + +int LdapClient::clientNumber() const +{ + return d->mClientNumber; +} + +int LdapClient::completionWeight() const +{ + return d->mCompletionWeight; +} + +void LdapClient::setCompletionWeight(int weight) +{ + d->mCompletionWeight = weight; +} + +#include "moc_ldapclient.cpp" diff --git a/3rdparty/kldap/src/widgets/ldapclient.h b/3rdparty/kldap/src/widgets/ldapclient.h new file mode 100644 index 0000000..47cf65d --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapclient.h @@ -0,0 +1,141 @@ +/* kldapclient.h - LDAP access + * SPDX-FileCopyrightText: 2002 Klarälvdalens Datakonsult AB + * SPDX-FileContributor: Steffen Hansen + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#ifndef KLDAP_LDAPCLIENT_H +#define KLDAP_LDAPCLIENT_H + +#include "kldap_export.h" + +#include +#include + +namespace KLDAP { +class LdapObject; +class LdapServer; + +/** + * @short An object that represents a configured LDAP server. + * + * This class represents a client that to an LDAP server that + * can be used for LDAP lookups. Every client is identified by + * a unique numeric id. + * + * @since 4.5 + */ +class KLDAP_EXPORT LdapClient : public QObject +{ + Q_OBJECT + +public: + /** + * Creates a new ldap client. + * + * @param clientNumber The unique number of this client. + * @param parent The parent object. + */ + explicit LdapClient(int clientNumber, QObject *parent = nullptr); + + /** + * Destroys the ldap client. + */ + ~LdapClient() override; + + /** + * Returns the number of this client. + */ + int clientNumber() const; + + /** + * Returns whether this client is currently running + * a search query. + */ + bool isActive() const; + + /** + * Sets the completion @p weight of this client. + * + * This value will be used to sort the results of this + * client when used for auto completion. + */ + void setCompletionWeight(int weight); + + /** + * Returns the completion weight of this client. + */ + int completionWeight() const; + + /** + * Sets the LDAP @p server information that shall be + * used by this client. + */ + void setServer(const KLDAP::LdapServer &server); + + /** + * Returns the ldap server information that are used + * by this client. + */ + const KLDAP::LdapServer server() const; + + /** + * Sets the LDAP @p attributes that should be returned + * in the query result. + * + * Pass an empty list to include all available attributes. + */ + void setAttributes(const QStringList &attributes); + + /** + * Returns the LDAP attributes that should be returned + * in the query result. + */ + QStringList attributes() const; + + /** + * Sets the @p scope of the LDAP query. + * + * Valid values are 'one' or 'sub'. + */ + void setScope(const QString &scope); + + /** + * Starts the query with the given @p filter. + */ + void startQuery(const QString &filter); + + /** + * Cancels a running query. + */ + void cancelQuery(); + +Q_SIGNALS: + /** + * This signal is emitted when the query has finished. + */ + void done(); + + /** + * This signal is emitted in case of an error. + * + * @param message A message that describes the error. + */ + void error(const QString &message); + + /** + * This signal is emitted once for each object that is + * returned from the query + */ + void result(const KLDAP::LdapClient &client, const KLDAP::LdapObject &); + +private: + //@cond PRIVATE + class Private; + Private *const d; + //@endcond +}; +} + +#endif diff --git a/3rdparty/kldap/src/widgets/ldapclientsearch.cpp b/3rdparty/kldap/src/widgets/ldapclientsearch.cpp new file mode 100644 index 0000000..5e84a3e --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapclientsearch.cpp @@ -0,0 +1,431 @@ +/* kldapclient.cpp - LDAP access + * SPDX-FileCopyrightText: 2002 Klarälvdalens Datakonsult AB + * SPDX-FileContributor: Steffen Hansen + * + * Ported to KABC by Daniel Molkentin + * + * SPDX-FileCopyrightText: 2013-2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "ldapclientsearch.h" +#include "ldapclientsearchconfig.h" +#include "ldapclient_debug.h" +#include "ldapsearchclientreadconfigserverjob.h" + +#include "ldapclient.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapClientSearch::Private +{ +public: + Private(LdapClientSearch *qq) + : q(qq) + { + } + + ~Private() + { + } + + void readWeighForClient(LdapClient *client, const KConfigGroup &config, int clientNumber); + void readConfig(); + void finish(); + void makeSearchData(QStringList &ret, LdapResult::List &resList); + + void slotLDAPResult(const KLDAP::LdapClient &client, const KLDAP::LdapObject &); + void slotLDAPError(const QString &); + void slotLDAPDone(); + void slotDataTimer(); + void slotFileChanged(const QString &); + void init(const QStringList &attributes); + + LdapClientSearch *const q; + QList mClients; + QStringList mAttributes; + QString mSearchText; + QString mFilter; + QTimer mDataTimer; + int mActiveClients = 0; + bool mNoLDAPLookup = false; + LdapResultObject::List mResults; + QString mConfigFile; +}; + +LdapClientSearch::LdapClientSearch(QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ + d->init(LdapClientSearch::defaultAttributes()); +} + +LdapClientSearch::LdapClientSearch(const QStringList &attr, QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ + d->init(attr); +} + +LdapClientSearch::~LdapClientSearch() +{ + delete d; +} + +void LdapClientSearch::Private::init(const QStringList &attributes) +{ + Kdelibs4ConfigMigrator migrate(QStringLiteral("ldapsettings")); + migrate.setConfigFiles(QStringList() << QStringLiteral("kabldaprc")); + migrate.migrate(); + + if (!KProtocolInfo::isKnownProtocol(QUrl(QStringLiteral("ldap://localhost")))) { + mNoLDAPLookup = true; + return; + } + + mAttributes = attributes; + + // Set the filter, to make sure old usage (before 4.14) of this object still works. + mFilter = QStringLiteral("&(|(objectclass=person)(objectclass=groupOfNames)(mail=*))" + "(|(cn=%1*)(mail=%1*)(givenName=%1*)(sn=%1*))"); + + readConfig(); + q->connect(KDirWatch::self(), &KDirWatch::dirty, q, [this](const QString &filename) { + slotFileChanged(filename); + }); +} + +void LdapClientSearch::Private::readWeighForClient(LdapClient *client, const KConfigGroup &config, int clientNumber) +{ + const int completionWeight = config.readEntry(QStringLiteral("SelectedCompletionWeight%1").arg(clientNumber), -1); + if (completionWeight != -1) { + client->setCompletionWeight(completionWeight); + } +} + +void LdapClientSearch::updateCompletionWeights() +{ + KConfigGroup config(KLDAP::LdapClientSearchConfig::config(), "LDAP"); + for (int i = 0, total = d->mClients.size(); i < total; ++i) { + d->readWeighForClient(d->mClients[ i ], config, i); + } +} + +QList LdapClientSearch::clients() const +{ + return d->mClients; +} + +QString LdapClientSearch::filter() const +{ + return d->mFilter; +} + +void LdapClientSearch::setFilter(const QString &filter) +{ + d->mFilter = filter; +} + +QStringList LdapClientSearch::attributes() const +{ + return d->mAttributes; +} + +void LdapClientSearch::setAttributes(const QStringList &attrs) +{ + if (attrs != d->mAttributes) { + d->mAttributes = attrs; + d->readConfig(); + } +} + +QStringList LdapClientSearch::defaultAttributes() +{ + const QStringList attr{ QStringLiteral("cn") + , QStringLiteral("mail") + , QStringLiteral("givenname") + , QStringLiteral("sn")}; + return attr; +} + +void LdapClientSearch::Private::readConfig() +{ + q->cancelSearch(); + qDeleteAll(mClients); + mClients.clear(); + + // stolen from KAddressBook + KConfigGroup config(KLDAP::LdapClientSearchConfig::config(), "LDAP"); + const int numHosts = config.readEntry("NumSelectedHosts", 0); + if (!numHosts) { + mNoLDAPLookup = true; + } else { + for (int j = 0; j < numHosts; ++j) { + auto *ldapClient = new LdapClient(j, q); + auto job = new LdapSearchClientReadConfigServerJob; + job->setCurrentIndex(j); + job->setActive(true); + job->setConfig(config); + job->setLdapClient(ldapClient); + job->start(); + + mNoLDAPLookup = false; + readWeighForClient(ldapClient, config, j); + + ldapClient->setAttributes(mAttributes); + + q->connect(ldapClient, &LdapClient::result, + q, [this](const LdapClient &client, const KLDAP::LdapObject &obj) { + slotLDAPResult(client, obj); + }); + q->connect(ldapClient, &LdapClient::done, + q, [this]() { + slotLDAPDone(); + }); + q->connect(ldapClient, qOverload(&LdapClient::error), + q, [this](const QString &str) { + slotLDAPError(str); + }); + + mClients.append(ldapClient); + } + + q->connect(&mDataTimer, &QTimer::timeout, q, [this]() { + slotDataTimer(); + }); + } + mConfigFile = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/kabldaprc"); + KDirWatch::self()->addFile(mConfigFile); +} + +void LdapClientSearch::Private::slotFileChanged(const QString &file) +{ + if (file == mConfigFile) { + readConfig(); + } +} + +void LdapClientSearch::startSearch(const QString &txt) +{ + if (d->mNoLDAPLookup) { + QMetaObject::invokeMethod(this, &LdapClientSearch::searchDone, Qt::QueuedConnection); + return; + } + + cancelSearch(); + + int pos = txt.indexOf(QLatin1Char('\"')); + if (pos >= 0) { + ++pos; + const int pos2 = txt.indexOf(QLatin1Char('\"'), pos); + if (pos2 >= 0) { + d->mSearchText = txt.mid(pos, pos2 - pos); + } else { + d->mSearchText = txt.mid(pos); + } + } else { + d->mSearchText = txt; + } + + const QString filter = d->mFilter.arg(d->mSearchText); + + QList::Iterator it(d->mClients.begin()); + const QList::Iterator end(d->mClients.end()); + for (; it != end; ++it) { + (*it)->startQuery(filter); + qCDebug(LDAPCLIENT_LOG) << "LdapClientSearch::startSearch()" << filter; + ++d->mActiveClients; + } +} + +void LdapClientSearch::cancelSearch() +{ + QList::Iterator it(d->mClients.begin()); + const QList::Iterator end(d->mClients.end()); + for (; it != end; ++it) { + (*it)->cancelQuery(); + } + + d->mActiveClients = 0; + d->mResults.clear(); +} + +void LdapClientSearch::Private::slotLDAPResult(const LdapClient &client, const KLDAP::LdapObject &obj) +{ + LdapResultObject result; + result.client = &client; + result.object = obj; + + mResults.append(result); + if (!mDataTimer.isActive()) { + mDataTimer.setSingleShot(true); + mDataTimer.start(500); + } +} + +void LdapClientSearch::Private::slotLDAPError(const QString &) +{ + slotLDAPDone(); +} + +void LdapClientSearch::Private::slotLDAPDone() +{ + if (--mActiveClients > 0) { + return; + } + + finish(); +} + +void LdapClientSearch::Private::slotDataTimer() +{ + QStringList lst; + LdapResult::List reslist; + + Q_EMIT q->searchData(mResults); + + makeSearchData(lst, reslist); + if (!lst.isEmpty()) { + Q_EMIT q->searchData(lst); + } + if (!reslist.isEmpty()) { + Q_EMIT q->searchData(reslist); + } +} + +void LdapClientSearch::Private::finish() +{ + mDataTimer.stop(); + + slotDataTimer(); // Q_EMIT final bunch of data + Q_EMIT q->searchDone(); +} + +void LdapClientSearch::Private::makeSearchData(QStringList &ret, LdapResult::List &resList) +{ + LdapResultObject::List::ConstIterator it1(mResults.constBegin()); + const LdapResultObject::List::ConstIterator end1(mResults.constEnd()); + for (; it1 != end1; ++it1) { + QString name, mail, givenname, sn; + QStringList mails; + bool isDistributionList = false; + bool wasCN = false; + bool wasDC = false; + + //qCDebug(LDAPCLIENT_LOG) <<"\n\nLdapClientSearch::makeSearchData()"; + + KLDAP::LdapAttrMap::ConstIterator it2; + for (it2 = (*it1).object.attributes().constBegin(); + it2 != (*it1).object.attributes().constEnd(); ++it2) { + QByteArray val = (*it2).first(); + int len = val.size(); + if (len > 0 && '\0' == val[len - 1]) { + --len; + } + const QString tmp = QString::fromUtf8(val.constData(), len); + //qCDebug(LDAPCLIENT_LOG) <<" key: \"" << it2.key() <<"\" value: \"" << tmp <<"\""; + if (it2.key() == QLatin1String("cn")) { + name = tmp; + if (mail.isEmpty()) { + mail = tmp; + } else { + if (wasCN) { + mail.prepend(QLatin1Char('.')); + } else { + mail.prepend(QLatin1Char('@')); + } + mail.prepend(tmp); + } + wasCN = true; + } else if (it2.key() == QLatin1String("dc")) { + if (mail.isEmpty()) { + mail = tmp; + } else { + if (wasDC) { + mail.append(QLatin1Char('.')); + } else { + mail.append(QLatin1Char('@')); + } + mail.append(tmp); + } + wasDC = true; + } else if (it2.key() == QLatin1String("mail")) { + mail = tmp; + KLDAP::LdapAttrValue::ConstIterator it3 = it2.value().constBegin(); + for (; it3 != it2.value().constEnd(); ++it3) { + mails.append(QString::fromUtf8((*it3).data(), (*it3).size())); + } + } else if (it2.key() == QLatin1String("givenName")) { + givenname = tmp; + } else if (it2.key() == QLatin1String("sn")) { + sn = tmp; + } else if (it2.key() == QLatin1String("objectClass") + && (tmp == QLatin1String("groupOfNames") || tmp == QLatin1String("kolabGroupOfNames"))) { + isDistributionList = true; + } + } + + if (mails.isEmpty()) { + if (!mail.isEmpty()) { + mails.append(mail); + } + if (isDistributionList) { + //qCDebug(LDAPCLIENT_LOG) <<"\n\nLdapClientSearch::makeSearchData() found a list:" << name; + ret.append(name); + // following lines commented out for bugfixing kolab issue #177: + // + // Unlike we thought previously we may NOT append the server name here. + // + // The right server is found by the SMTP server instead: Kolab users + // must use the correct SMTP server, by definition. + // + //mail = (*it1).client->base().simplified(); + //mail.replace( ",dc=", ".", false ); + //if( mail.startsWith("dc=", false) ) + // mail.remove(0, 3); + //mail.prepend( '@' ); + //mail.prepend( name ); + //mail = name; + } else { + continue; // nothing, bad entry + } + } else if (name.isEmpty()) { + ret.append(mail); + } else { + ret.append(QStringLiteral("%1 <%2>").arg(name, mail)); + } + + LdapResult sr; + sr.dn = (*it1).object.dn(); + sr.clientNumber = (*it1).client->clientNumber(); + sr.completionWeight = (*it1).client->completionWeight(); + sr.name = name; + sr.email = mails; + resList.append(sr); + } + + mResults.clear(); +} + +bool LdapClientSearch::isAvailable() const +{ + return !d->mNoLDAPLookup; +} + +#include "moc_ldapclientsearch.cpp" diff --git a/3rdparty/kldap/src/widgets/ldapclientsearch.h b/3rdparty/kldap/src/widgets/ldapclientsearch.h new file mode 100644 index 0000000..4fb3878 --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapclientsearch.h @@ -0,0 +1,177 @@ +/* kldapclient.h - LDAP access + * SPDX-FileCopyrightText: 2002 Klarälvdalens Datakonsult AB + * SPDX-FileContributor: Steffen Hansen + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#ifndef LDAPCLIENTSEARCH_H +#define LDAPCLIENTSEARCH_H + +#include "kldap_export.h" + +#include +#include +#include + +namespace KLDAP { +class LdapClient; + +/** + * Describes the result returned by an LdapClientSearch query. + * + * @since 4.14 + */ +struct LdapResultObject { + typedef QVector List; + const LdapClient *client = nullptr; + KLDAP::LdapObject object; +}; + +/** + * Describes the result returned by an LdapClientSearch query. + * + * @since 4.5 + */ +struct LdapResult { + /** + * A list of LdapResult objects. + */ + typedef QVector List; + + LdapDN dn; + QString name; ///< The full name of the contact. + QStringList email; ///< The list of emails of the contact. + int clientNumber; ///< The client the contact comes from (used for sorting in a ldap-only lookup). + int completionWeight; ///< The weight of the contact (used for sorting in a completion list). +}; + +/** + * @since 4.5 + */ +class KLDAP_EXPORT LdapClientSearch : public QObject +{ + Q_OBJECT + +public: + /** + * Creates a new ldap client search object. + * + * @param parent The parent object. + */ + explicit LdapClientSearch(QObject *parent = nullptr); + + /** + * Creates a new ldap client search object. + * + * @param attr The attributes. + * @param parent The parent object. + */ + explicit LdapClientSearch(const QStringList &attr, QObject *parent = nullptr); + + /** + * Destroys the ldap client search object. + */ + ~LdapClientSearch() override; + + /** + * Starts the LDAP search on all configured LDAP clients with the given search @p query. + */ + void startSearch(const QString &query); + + /** + * Cancels the currently running search query. + */ + void cancelSearch(); + + /** + * Returns whether LDAP search is possible at all. + * + * @note This method can return @c false if either no LDAP is configured + * or the system does not support the KIO LDAP protocol. + */ + bool isAvailable() const; + + /** + * Updates the completion weights for the configured LDAP clients from + * the configuration file. + */ + void updateCompletionWeights(); + + /** + * Returns the list of configured LDAP clients. + */ + QList clients() const; + + /** + * Returns the filter for the Query + * + * @since 4.14 + */ + QString filter() const; + + /** + * Sets the filter for the Query + * + * @since 4.14 + */ + void setFilter(const QString &); + + /** + * Returns the attributes, that are queried the LDAP Server. + * + * @since 4.14 + */ + QStringList attributes() const; + + /** + * Sets the attributes, that are queried the LDAP Server. + * + * @since 4.14 + */ + void setAttributes(const QStringList &); + + static Q_REQUIRED_RESULT QStringList defaultAttributes(); + +Q_SIGNALS: + /** + * This signal is emitted whenever new contacts have been found + * during the lookup. + * + * @param results The contacts in the form "Full Name " + */ + void searchData(const QStringList &results); + + /** + * This signal is emitted whenever new contacts have been found + * during the lookup. + * + * @param results The list of found contacts. + */ + void searchData(const KLDAP::LdapResult::List &results); + + /** + * This signal is emitted whenever new contacts have been found + * during the lookup. + * + * @param results The list of found contacts. + */ + void searchData(const KLDAP::LdapResultObject::List &results); + + /** + * This signal is emitted whenever the lookup is complete or the + * user has canceled the query. + */ + void searchDone(); + +private: + //@cond PRIVATE + class Private; + Private *const d; + //@endcond +}; +} +Q_DECLARE_TYPEINFO(KLDAP::LdapResult, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(KLDAP::LdapResultObject, Q_MOVABLE_TYPE); + +#endif // LDAPCLIENTSEARCH_H diff --git a/3rdparty/kldap/src/widgets/ldapclientsearchconfig.cpp b/3rdparty/kldap/src/widgets/ldapclientsearchconfig.cpp new file mode 100644 index 0000000..ce6e432 --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapclientsearchconfig.cpp @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2013-2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "ldapclientsearchconfig.h" +#include "ldapclient_debug.h" +#include + +#include +#include +#include +#include +#include +using namespace QKeychain; +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapClientSearchConfig::Private +{ +public: + Private() + { + } + + ~Private() + { + } +}; + +Q_GLOBAL_STATIC_WITH_ARGS(KConfig, s_config, (QLatin1String("kabldaprc"), KConfig::NoGlobals)) + +KConfig *LdapClientSearchConfig::config() +{ + return s_config; +} + +LdapClientSearchConfig::LdapClientSearchConfig(QObject *parent) + : QObject(parent) + , d(new LdapClientSearchConfig::Private()) +{ +} + +LdapClientSearchConfig::~LdapClientSearchConfig() +{ + delete d; +} +#if 0 //Port it +void LdapClientSearchConfig::clearWalletPassword() +{ + if (!d->wallet) { + d->wallet = KWallet::Wallet::openWallet(KWallet::Wallet::LocalWallet(), 0); + } + if (d->wallet) { + d->useWallet = true; + if (d->wallet->hasFolder(QStringLiteral("ldapclient"))) { + //Recreate it. + d->wallet->removeFolder(QStringLiteral("ldapclient")); + d->wallet->createFolder(QStringLiteral("ldapclient")); + d->wallet->setFolder(QStringLiteral("ldapclient")); + } + } +} +#endif diff --git a/3rdparty/kldap/src/widgets/ldapclientsearchconfig.h b/3rdparty/kldap/src/widgets/ldapclientsearchconfig.h new file mode 100644 index 0000000..c9cc310 --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapclientsearchconfig.h @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2013-2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#ifndef LDAPCLIENTSEARCHCONFIG_H +#define LDAPCLIENTSEARCHCONFIG_H + +#include "kldap_export.h" + +#include + +class KConfigGroup; +class KConfig; + +namespace KLDAP { +class LdapServer; +class LdapClient; +/** + * @brief The LdapClientSearchConfig class + * @author Laurent Montel + */ +class KLDAP_EXPORT LdapClientSearchConfig : public QObject +{ + Q_OBJECT +public: + explicit LdapClientSearchConfig(QObject *parent = nullptr); + ~LdapClientSearchConfig(); + + /** + * Returns the global config object, which stores the LdapClient configurations. + */ + static KConfig *config(); + + +private: + //@cond PRIVATE + class Private; + Private *const d; +}; +} + +#endif // LDAPCLIENTSEARCHCONFIG_H diff --git a/3rdparty/kldap/src/widgets/ldapclientsearchconfigreadconfigjob.cpp b/3rdparty/kldap/src/widgets/ldapclientsearchconfigreadconfigjob.cpp new file mode 100644 index 0000000..0a93239 --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapclientsearchconfigreadconfigjob.cpp @@ -0,0 +1,199 @@ +/* + * SPDX-FileCopyrightText: 2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "ldapclientsearchconfigreadconfigjob.h" +#include "ldapclient_debug.h" + +#include +#include +#include +#include +#include +using namespace QKeychain; + +using namespace KLDAP; +LdapClientSearchConfigReadConfigJob::LdapClientSearchConfigReadConfigJob(QObject *parent) + : QObject(parent) +{ + +} + +LdapClientSearchConfigReadConfigJob::~LdapClientSearchConfigReadConfigJob() +{ + +} + +bool LdapClientSearchConfigReadConfigJob::canStart() const +{ + return mServerIndex != -1 && mConfig.isValid(); +} + +void LdapClientSearchConfigReadConfigJob::readLdapClientConfigFinished() +{ + Q_EMIT configLoaded(mServer); + deleteLater(); +} + +void LdapClientSearchConfigReadConfigJob::start() +{ + if (!canStart()) { + //Failed ! + readLdapClientConfigFinished(); + return; + } + readConfig(); +} + +bool LdapClientSearchConfigReadConfigJob::active() const +{ + return mActive; +} + +void LdapClientSearchConfigReadConfigJob::setActive(bool newActive) +{ + mActive = newActive; +} + +int LdapClientSearchConfigReadConfigJob::serverIndex() const +{ + return mServerIndex; +} + +void LdapClientSearchConfigReadConfigJob::setServerIndex(int newServerIndex) +{ + mServerIndex = newServerIndex; +} + +KConfigGroup LdapClientSearchConfigReadConfigJob::config() const +{ + return mConfig; +} + +void LdapClientSearchConfigReadConfigJob::setConfig(const KConfigGroup &newConfig) +{ + mConfig = newConfig; +} + +void LdapClientSearchConfigReadConfigJob::readConfig() +{ + QString prefix; + if (mActive) { + prefix = QStringLiteral("Selected"); + } + + const QString host = mConfig.readEntry(prefix + QStringLiteral("Host%1").arg(mServerIndex), + QString()).trimmed(); + if (!host.isEmpty()) { + mServer.setHost(host); + } + + const int port = mConfig.readEntry(prefix + QStringLiteral("Port%1").arg(mServerIndex), 389); + mServer.setPort(port); + + const QString base = mConfig.readEntry(prefix + QStringLiteral("Base%1").arg(mServerIndex), + QString()).trimmed(); + if (!base.isEmpty()) { + mServer.setBaseDn(KLDAP::LdapDN(base)); + } + + const QString user = mConfig.readEntry(prefix + QStringLiteral("User%1").arg(mServerIndex), + QString()).trimmed(); + if (!user.isEmpty()) { + mServer.setUser(user); + } + + const QString bindDN = mConfig.readEntry(prefix + QStringLiteral("Bind%1").arg(mServerIndex), QString()).trimmed(); + if (!bindDN.isEmpty()) { + mServer.setBindDn(bindDN); + } +#if 0 //Port + const QString pwdBindBNEntry = prefix + QStringLiteral("PwdBind%1").arg(mServerIndex); + QString pwdBindDN = mConfig.readEntry(pwdBindBNEntry, QString()); + if (!pwdBindDN.isEmpty()) { + if (d->askWallet && KMessageBox::Yes == KMessageBox::questionYesNo(nullptr, i18n("LDAP password is stored as clear text, do you want to store it in kwallet?"), + i18n("Store clear text password in Wallet"), + KStandardGuiItem::yes(), + KStandardGuiItem::no(), + QStringLiteral("DoAskToStoreToWallet"))) { + d->wallet = KWallet::Wallet::openWallet(KWallet::Wallet::LocalWallet(), 0); + if (d->wallet) { + connect(d->wallet, &KWallet::Wallet::walletClosed, this, &LdapClientSearchConfig::slotWalletClosed); + d->useWallet = true; + if (!d->wallet->hasFolder(QStringLiteral("ldapclient"))) { + d->wallet->createFolder(QStringLiteral("ldapclient")); + } + d->wallet->setFolder(QStringLiteral("ldapclient")); + d->wallet->writePassword(pwdBindBNEntry, pwdBindDN); + mConfig.deleteEntry(pwdBindBNEntry); + mConfig.sync(); + } + } + mServer.setPassword(pwdBindDN); + } else if (d->askWallet) { //Look at in Wallet + //Move as async here. + d->wallet = KWallet::Wallet::openWallet(KWallet::Wallet::LocalWallet(), 0); + if (d->wallet) { + d->useWallet = true; + if (!d->wallet->setFolder(QStringLiteral("ldapclient"))) { + d->wallet->createFolder(QStringLiteral("ldapclient")); + d->wallet->setFolder(QStringLiteral("ldapclient")); + } + d->wallet->readPassword(pwdBindBNEntry, pwdBindDN); + if (!pwdBindDN.isEmpty()) { + mServer.setPassword(pwdBindDN); + } + } else { + d->useWallet = false; + } + } +#endif + mServer.setTimeLimit(mConfig.readEntry(prefix + QStringLiteral("TimeLimit%1").arg(mServerIndex), 0)); + mServer.setSizeLimit(mConfig.readEntry(prefix + QStringLiteral("SizeLimit%1").arg(mServerIndex), 0)); + mServer.setPageSize(mConfig.readEntry(prefix + QStringLiteral("PageSize%1").arg(mServerIndex), 0)); + mServer.setVersion(mConfig.readEntry(prefix + QStringLiteral("Version%1").arg(mServerIndex), 3)); + + QString tmp = mConfig.readEntry(prefix + QStringLiteral("Security%1").arg(mServerIndex), + QStringLiteral("None")); + mServer.setSecurity(KLDAP::LdapServer::None); + if (tmp == QLatin1String("SSL")) { + mServer.setSecurity(KLDAP::LdapServer::SSL); + } else if (tmp == QLatin1String("TLS")) { + mServer.setSecurity(KLDAP::LdapServer::TLS); + } + + tmp = mConfig.readEntry(prefix + QStringLiteral("Auth%1").arg(mServerIndex), + QStringLiteral("Anonymous")); + mServer.setAuth(KLDAP::LdapServer::Anonymous); + if (tmp == QLatin1String("Simple")) { + mServer.setAuth(KLDAP::LdapServer::Simple); + } else if (tmp == QLatin1String("SASL")) { + mServer.setAuth(KLDAP::LdapServer::SASL); + } + + mServer.setMech(mConfig.readEntry(prefix + QStringLiteral("Mech%1").arg(mServerIndex), QString())); + mServer.setFilter(mConfig.readEntry(prefix + QStringLiteral("UserFilter%1").arg(mServerIndex), QString())); + mServer.setCompletionWeight(mConfig.readEntry(prefix + QStringLiteral("CompletionWeight%1").arg(mServerIndex), -1)); + + const QString pwdBindBNEntry = prefix + QStringLiteral("PwdBind%1").arg(mServerIndex); + + auto readJob = new ReadPasswordJob(QStringLiteral("ldapclient"), this); + connect(readJob, &Job::finished, this, &LdapClientSearchConfigReadConfigJob::readLdapPasswordFinished); + readJob->setKey(pwdBindBNEntry); + readJob->start(); +} + +void LdapClientSearchConfigReadConfigJob::readLdapPasswordFinished(QKeychain::Job *baseJob) +{ + auto *job = qobject_cast(baseJob); + Q_ASSERT(job); + if (!job->error()) { + mServer.setPassword(job->textData()); + } else { + qCWarning(LDAPCLIENT_LOG) << "We have an error during reading password " << job->errorString(); + } + readLdapClientConfigFinished(); +} + diff --git a/3rdparty/kldap/src/widgets/ldapclientsearchconfigreadconfigjob.h b/3rdparty/kldap/src/widgets/ldapclientsearchconfigreadconfigjob.h new file mode 100644 index 0000000..e5f2758 --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapclientsearchconfigreadconfigjob.h @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#ifndef LDAPCLIENTSEARCHCONFIGREADCONFIGJOB_H +#define LDAPCLIENTSEARCHCONFIGREADCONFIGJOB_H +#include +#include "kldap_export.h" +#include +#include +namespace QKeychain { +class Job; +} +namespace KLDAP { +class KLDAP_EXPORT LdapClientSearchConfigReadConfigJob : public QObject +{ + Q_OBJECT +public: + explicit LdapClientSearchConfigReadConfigJob(QObject *parent = nullptr); + ~LdapClientSearchConfigReadConfigJob() override; + + Q_REQUIRED_RESULT bool canStart() const; + void start(); + + Q_REQUIRED_RESULT bool active() const; + void setActive(bool newActive); + + Q_REQUIRED_RESULT int serverIndex() const; + void setServerIndex(int newServerIndex); + + Q_REQUIRED_RESULT KConfigGroup config() const; + void setConfig(const KConfigGroup &newConfig); + +Q_SIGNALS: + void configLoaded(const KLDAP::LdapServer &server); + +private: + void readLdapPasswordFinished(QKeychain::Job *baseJob); + void readLdapClientConfigFinished(); + void readConfig(); + int mServerIndex = -1; + KConfigGroup mConfig; + bool mActive = false; + KLDAP::LdapServer mServer; +}; + +} +#endif // LDAPCLIENTSEARCHCONFIGREADCONFIGJOB_H diff --git a/3rdparty/kldap/src/widgets/ldapclientsearchconfigwriteconfigjob.cpp b/3rdparty/kldap/src/widgets/ldapclientsearchconfigwriteconfigjob.cpp new file mode 100644 index 0000000..cc57848 --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapclientsearchconfigwriteconfigjob.cpp @@ -0,0 +1,149 @@ +/* + * SPDX-FileCopyrightText: 2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "ldapclientsearchconfigwriteconfigjob.h" +#include "ldapclient_debug.h" + +#include +#include +#include +#include +using namespace QKeychain; + +using namespace KLDAP; +LdapClientSearchConfigWriteConfigJob::LdapClientSearchConfigWriteConfigJob(QObject *parent) + : QObject(parent) +{ + +} + +LdapClientSearchConfigWriteConfigJob::~LdapClientSearchConfigWriteConfigJob() +{ + +} + +bool LdapClientSearchConfigWriteConfigJob::canStart() const +{ + return mServerIndex != -1 && mConfig.isValid(); +} + +void LdapClientSearchConfigWriteConfigJob::writeLdapClientConfigFinished() +{ + Q_EMIT configSaved(); + deleteLater(); +} + +void LdapClientSearchConfigWriteConfigJob::start() +{ + if (!canStart()) { + //Failed ! + writeLdapClientConfigFinished(); + return; + } + writeConfig(); +} + +bool LdapClientSearchConfigWriteConfigJob::active() const +{ + return mActive; +} + +void LdapClientSearchConfigWriteConfigJob::setActive(bool newActive) +{ + mActive = newActive; +} + +int LdapClientSearchConfigWriteConfigJob::serverIndex() const +{ + return mServerIndex; +} + +void LdapClientSearchConfigWriteConfigJob::setServerIndex(int newServerIndex) +{ + mServerIndex = newServerIndex; +} + +KConfigGroup LdapClientSearchConfigWriteConfigJob::config() const +{ + return mConfig; +} + +void LdapClientSearchConfigWriteConfigJob::setConfig(const KConfigGroup &newConfig) +{ + mConfig = newConfig; +} + +void LdapClientSearchConfigWriteConfigJob::writeConfig() +{ + QString prefix; + if (mActive) { + prefix = QStringLiteral("Selected"); + } + + mConfig.writeEntry(prefix + QStringLiteral("Host%1").arg(mServerIndex), mServer.host()); + mConfig.writeEntry(prefix + QStringLiteral("Port%1").arg(mServerIndex), mServer.port()); + mConfig.writeEntry(prefix + QStringLiteral("Base%1").arg(mServerIndex), mServer.baseDn().toString()); + mConfig.writeEntry(prefix + QStringLiteral("User%1").arg(mServerIndex), mServer.user()); + mConfig.writeEntry(prefix + QStringLiteral("Bind%1").arg(mServerIndex), mServer.bindDn()); + + const QString passwordEntry = prefix + QStringLiteral("PwdBind%1").arg(mServerIndex); + const QString password = mServer.password(); + if (!password.isEmpty()) { + auto writeJob = new WritePasswordJob(QStringLiteral("ldapclient"), this); + connect(writeJob, &Job::finished, this, [](QKeychain::Job *baseJob) { + if (baseJob->error()) { + qCWarning(LDAPCLIENT_LOG) << "Error writing password using QKeychain:" << baseJob->errorString(); + } + }); + writeJob->setKey(passwordEntry); + writeJob->setTextData(password); + writeJob->start(); + } + + mConfig.writeEntry(prefix + QStringLiteral("TimeLimit%1").arg(mServerIndex), mServer.timeLimit()); + mConfig.writeEntry(prefix + QStringLiteral("SizeLimit%1").arg(mServerIndex), mServer.sizeLimit()); + mConfig.writeEntry(prefix + QStringLiteral("PageSize%1").arg(mServerIndex), mServer.pageSize()); + mConfig.writeEntry(prefix + QStringLiteral("Version%1").arg(mServerIndex), mServer.version()); + QString tmp; + switch (mServer.security()) { + case KLDAP::LdapServer::TLS: + tmp = QStringLiteral("TLS"); + break; + case KLDAP::LdapServer::SSL: + tmp = QStringLiteral("SSL"); + break; + default: + tmp = QStringLiteral("None"); + } + mConfig.writeEntry(prefix + QStringLiteral("Security%1").arg(mServerIndex), tmp); + switch (mServer.auth()) { + case KLDAP::LdapServer::Simple: + tmp = QStringLiteral("Simple"); + break; + case KLDAP::LdapServer::SASL: + tmp = QStringLiteral("SASL"); + break; + default: + tmp = QStringLiteral("Anonymous"); + } + mConfig.writeEntry(prefix + QStringLiteral("Auth%1").arg(mServerIndex), tmp); + mConfig.writeEntry(prefix + QStringLiteral("Mech%1").arg(mServerIndex), mServer.mech()); + mConfig.writeEntry(prefix + QStringLiteral("UserFilter%1").arg(mServerIndex), mServer.filter().trimmed()); + if (mServer.completionWeight() > -1) { + mConfig.writeEntry(prefix + QStringLiteral("CompletionWeight%1").arg(mServerIndex), mServer.completionWeight()); + } +} + +KLDAP::LdapServer LdapClientSearchConfigWriteConfigJob::server() const +{ + return mServer; +} + +void LdapClientSearchConfigWriteConfigJob::setServer(const KLDAP::LdapServer &server) +{ + mServer = server; +} + diff --git a/3rdparty/kldap/src/widgets/ldapclientsearchconfigwriteconfigjob.h b/3rdparty/kldap/src/widgets/ldapclientsearchconfigwriteconfigjob.h new file mode 100644 index 0000000..1986401 --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapclientsearchconfigwriteconfigjob.h @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#ifndef LDAPCLIENTSEARCHCONFIGWRITECONFIGJOB_H +#define LDAPCLIENTSEARCHCONFIGWRITECONFIGJOB_H +#include +#include "kldap_export.h" +#include +#include +namespace QKeychain { +class Job; +} +namespace KLDAP { +class KLDAP_EXPORT LdapClientSearchConfigWriteConfigJob : public QObject +{ + Q_OBJECT +public: + explicit LdapClientSearchConfigWriteConfigJob(QObject *parent = nullptr); + ~LdapClientSearchConfigWriteConfigJob() override; + + Q_REQUIRED_RESULT bool canStart() const; + void start(); + + Q_REQUIRED_RESULT bool active() const; + void setActive(bool newActive); + + Q_REQUIRED_RESULT int serverIndex() const; + void setServerIndex(int newServerIndex); + + Q_REQUIRED_RESULT KConfigGroup config() const; + void setConfig(const KConfigGroup &newConfig); + + Q_REQUIRED_RESULT KLDAP::LdapServer server() const; + void setServer(const KLDAP::LdapServer &server); + +Q_SIGNALS: + void configSaved(); + +private: + void writeLdapClientConfigFinished(); + void writeConfig(); + int mServerIndex = -1; + KConfigGroup mConfig; + bool mActive = false; + KLDAP::LdapServer mServer; +}; + +} +#endif // LDAPCLIENTSEARCHCONFIGWRITECONFIGJOB_H diff --git a/3rdparty/kldap/src/widgets/ldapconfigurewidget.cpp b/3rdparty/kldap/src/widgets/ldapconfigurewidget.cpp new file mode 100644 index 0000000..c8067d1 --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapconfigurewidget.cpp @@ -0,0 +1,323 @@ +/* + * SPDX-FileCopyrightText: 2019-2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "ldapconfigurewidget.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ldapclientsearch.h" +#include "ldapclientsearchconfigwriteconfigjob.h" +#include "ldapwidgetitemreadconfigserverjob.h" +#include "ldapclientsearchconfig.h" +#include "ldapwidgetitem_p.h" +#include + +#include "addhostdialog.h" + +using namespace KLDAP; + +LdapConfigureWidget::LdapConfigureWidget(QWidget *parent) + : QWidget(parent) +{ + mClientSearchConfig = new KLDAP::LdapClientSearchConfig; + initGUI(); + + connect(mHostListView, &QListWidget::currentItemChanged, this, &LdapConfigureWidget::slotSelectionChanged); + connect(mHostListView, &QListWidget::itemDoubleClicked, this, &LdapConfigureWidget::slotEditHost); + connect(mHostListView, &QListWidget::itemClicked, this, &LdapConfigureWidget::slotItemClicked); + + connect(mUpButton, &QToolButton::clicked, this, &LdapConfigureWidget::slotMoveUp); + connect(mDownButton, &QToolButton::clicked, this, &LdapConfigureWidget::slotMoveDown); +} + +LdapConfigureWidget::~LdapConfigureWidget() +{ + delete mClientSearchConfig; +} + +void LdapConfigureWidget::slotSelectionChanged(QListWidgetItem *item) +{ + bool state = (item != nullptr); + mEditButton->setEnabled(state); + mRemoveButton->setEnabled(state); + mDownButton->setEnabled(item && (mHostListView->row(item) != (mHostListView->count() - 1))); + mUpButton->setEnabled(item && (mHostListView->row(item) != 0)); +} + +void LdapConfigureWidget::slotItemClicked(QListWidgetItem *item) +{ + auto *ldapItem = dynamic_cast(item); + if (!ldapItem) { + return; + } + + if ((ldapItem->checkState() == Qt::Checked) != ldapItem->isActive()) { + Q_EMIT changed(true); + ldapItem->setIsActive(ldapItem->checkState() == Qt::Checked); + } +} + +void LdapConfigureWidget::slotAddHost() +{ + KLDAP::LdapServer server; + KLDAP::AddHostDialog dlg(&server, this); + + if (dlg.exec() && !server.host().trimmed().isEmpty()) { //krazy:exclude=crashy + auto item = new LdapWidgetItem(mHostListView); + item->setServer(server); + + Q_EMIT changed(true); + } +} + +void LdapConfigureWidget::slotEditHost() +{ + auto *item = dynamic_cast(mHostListView->currentItem()); + if (!item) { + return; + } + + KLDAP::LdapServer server = item->server(); + KLDAP::AddHostDialog dlg(&server, this); + dlg.setWindowTitle(i18nc("@title:window", "Edit Host")); + + if (dlg.exec() && !server.host().isEmpty()) { //krazy:exclude=crashy + item->setServer(server); + + Q_EMIT changed(true); + } +} + +void LdapConfigureWidget::slotRemoveHost() +{ + QListWidgetItem *item = mHostListView->currentItem(); + if (!item) { + return; + } + auto *ldapItem = dynamic_cast(item); + if (KMessageBox::No == KMessageBox::questionYesNo(this, i18n("Do you want to remove setting for host \"%1\"?", ldapItem->server().host()), i18n("Remove Host"))) { + return; + } + + delete mHostListView->takeItem(mHostListView->currentRow()); + + slotSelectionChanged(mHostListView->currentItem()); + + Q_EMIT changed(true); +} + +static void swapItems(LdapWidgetItem *item, LdapWidgetItem *other) +{ + KLDAP::LdapServer server = item->server(); + bool isActive = item->isActive(); + item->setServer(other->server()); + item->setIsActive(other->isActive()); + item->setCheckState(other->isActive() ? Qt::Checked : Qt::Unchecked); + other->setServer(server); + other->setIsActive(isActive); + other->setCheckState(isActive ? Qt::Checked : Qt::Unchecked); +} + +void LdapConfigureWidget::slotMoveUp() +{ + const QList selectedItems = mHostListView->selectedItems(); + if (selectedItems.isEmpty()) { + return; + } + + LdapWidgetItem *item = static_cast(mHostListView->selectedItems().first()); + if (!item) { + return; + } + + auto *above = static_cast(mHostListView->item(mHostListView->row(item) - 1)); + if (!above) { + return; + } + + swapItems(item, above); + + mHostListView->setCurrentItem(above); + above->setSelected(true); + + Q_EMIT changed(true); +} + +void LdapConfigureWidget::slotMoveDown() +{ + const QList selectedItems = mHostListView->selectedItems(); + if (selectedItems.isEmpty()) { + return; + } + + LdapWidgetItem *item = static_cast(mHostListView->selectedItems().first()); + if (!item) { + return; + } + + auto *below = static_cast(mHostListView->item(mHostListView->row(item) + 1)); + if (!below) { + return; + } + + swapItems(item, below); + + mHostListView->setCurrentItem(below); + below->setSelected(true); + + Q_EMIT changed(true); +} + +void LdapConfigureWidget::load() +{ + mHostListView->clear(); + KConfig *config = KLDAP::LdapClientSearchConfig::config(); + KConfigGroup group(config, "LDAP"); + + int count = group.readEntry("NumSelectedHosts", 0); + for (int i = 0; i < count; ++i) { + auto *item = new LdapWidgetItem(mHostListView, true); + item->setCheckState(Qt::Checked); + auto job = new LdapWidgetItemReadConfigServerJob(this); + job->setCurrentIndex(i); + job->setActive(true); + job->setConfig(group); + job->setLdapWidgetItem(item); + job->start(); + } + + count = group.readEntry("NumHosts", 0); + for (int i = 0; i < count; ++i) { + auto item = new LdapWidgetItem(mHostListView); + auto job = new LdapWidgetItemReadConfigServerJob(this); + job->setCurrentIndex(i); + job->setActive(false); + job->setConfig(group); + job->setLdapWidgetItem(item); + job->start(); + + } + + Q_EMIT changed(false); +} + +void LdapConfigureWidget::save() +{ + //mClientSearchConfig->clearWalletPassword(); + KConfig *config = KLDAP::LdapClientSearchConfig::config(); + config->deleteGroup("LDAP"); + + KConfigGroup group(config, "LDAP"); + + int selected = 0; + int unselected = 0; + for (int i = 0; i < mHostListView->count(); ++i) { + auto *item = dynamic_cast(mHostListView->item(i)); + if (!item) { + continue; + } + + KLDAP::LdapServer server = item->server(); + if (item->checkState() == Qt::Checked) { + auto job = new LdapClientSearchConfigWriteConfigJob; + job->setActive(true); + job->setConfig(group); + job->setServerIndex(selected); + job->setServer(server); + job->start(); + selected++; + } else { + auto job = new LdapClientSearchConfigWriteConfigJob; + job->setActive(false); + job->setConfig(group); + job->setServerIndex(unselected); + job->setServer(server); + job->start(); + unselected++; + } + } + + group.writeEntry("NumSelectedHosts", selected); + group.writeEntry("NumHosts", unselected); + config->sync(); + + Q_EMIT changed(false); +} + +void LdapConfigureWidget::initGUI() +{ + auto *layout = new QVBoxLayout(this); + layout->setObjectName(QStringLiteral("layout")); + layout->setContentsMargins(0, 0, 0, 0); + + QGroupBox *groupBox = new QGroupBox(i18n("LDAP Servers")); + layout->addWidget(groupBox); + auto *mainLayout = new QVBoxLayout; + mainLayout->setObjectName(QStringLiteral("mainlayout")); + groupBox->setLayout(mainLayout); + + // Contents of the QVGroupBox: label and hbox + QLabel *label = new QLabel(i18n("Check all servers that should be used:")); + mainLayout->addWidget(label); + + QWidget *hBox = new QWidget(this); + mainLayout->addWidget(hBox); + + auto *hBoxHBoxLayout = new QHBoxLayout(hBox); + hBoxHBoxLayout->setContentsMargins(0, 0, 0, 0); + hBoxHBoxLayout->setSpacing(6); + // Contents of the hbox: listview and up/down buttons on the right (vbox) + mHostListView = new QListWidget(hBox); + hBoxHBoxLayout->addWidget(mHostListView); + mHostListView->setSortingEnabled(false); + + QWidget *upDownBox = new QWidget(hBox); + auto *upDownBoxVBoxLayout = new QVBoxLayout(upDownBox); + upDownBoxVBoxLayout->setContentsMargins(0, 0, 0, 0); + hBoxHBoxLayout->addWidget(upDownBox); + upDownBoxVBoxLayout->setSpacing(6); + mUpButton = new QToolButton(upDownBox); + upDownBoxVBoxLayout->addWidget(mUpButton); + mUpButton->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); + mUpButton->setEnabled(false); // b/c no item is selected yet + + mDownButton = new QToolButton(upDownBox); + upDownBoxVBoxLayout->addWidget(mDownButton); + mDownButton->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); + mDownButton->setEnabled(false); // b/c no item is selected yet + + QWidget *spacer = new QWidget(upDownBox); + upDownBoxVBoxLayout->addWidget(spacer); + upDownBoxVBoxLayout->setStretchFactor(spacer, 100); + + auto *buttons = new QDialogButtonBox(this); + QPushButton *add = buttons->addButton(i18n("&Add Host..."), + QDialogButtonBox::ActionRole); + connect(add, &QPushButton::clicked, this, &LdapConfigureWidget::slotAddHost); + mEditButton = buttons->addButton(i18n("&Edit Host..."), + QDialogButtonBox::ActionRole); + connect(mEditButton, &QPushButton::clicked, this, &LdapConfigureWidget::slotEditHost); + mEditButton->setEnabled(false); + mRemoveButton = buttons->addButton(i18n("&Remove Host"), + QDialogButtonBox::ActionRole); + connect(mRemoveButton, &QPushButton::clicked, this, &LdapConfigureWidget::slotRemoveHost); + mRemoveButton->setEnabled(false); + buttons->layout(); + + layout->addWidget(buttons); +} diff --git a/3rdparty/kldap/src/widgets/ldapconfigurewidget.h b/3rdparty/kldap/src/widgets/ldapconfigurewidget.h new file mode 100644 index 0000000..c71d029 --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapconfigurewidget.h @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2019-2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#ifndef LDAPCONFIGUREWIDGET_H +#define LDAPCONFIGUREWIDGET_H + + +#include "kldap_export.h" + +#include + +class QListWidget; +class QPushButton; +class QToolButton; +class QListWidgetItem; + +namespace KLDAP { +class LdapClientSearchConfig; +/** + * @brief The LdapConfigureWidget class + * @author Laurent Montel + */ +class KLDAP_EXPORT LdapConfigureWidget : public QWidget +{ + Q_OBJECT +public: + explicit LdapConfigureWidget(QWidget *parent = nullptr); + ~LdapConfigureWidget(); + + void load(); + void save(); + +private Q_SLOTS: + void slotAddHost(); + void slotEditHost(); + void slotRemoveHost(); + void slotSelectionChanged(QListWidgetItem *); + void slotItemClicked(QListWidgetItem *); + void slotMoveUp(); + void slotMoveDown(); + +Q_SIGNALS: + void changed(bool); + +private: + void initGUI(); + + QListWidget *mHostListView = nullptr; + + QPushButton *mAddButton = nullptr; + QPushButton *mEditButton = nullptr; + QPushButton *mRemoveButton = nullptr; + + QToolButton *mUpButton = nullptr; + QToolButton *mDownButton = nullptr; + KLDAP::LdapClientSearchConfig *mClientSearchConfig = nullptr; +}; +} + +#endif // LDAPCONFIGUREWIDGET_H diff --git a/3rdparty/kldap/src/widgets/ldapconfigwidget.cpp b/3rdparty/kldap/src/widgets/ldapconfigwidget.cpp new file mode 100644 index 0000000..c8b914c --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapconfigwidget.cpp @@ -0,0 +1,911 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "ldapconfigwidget.h" +#include "ldapsearch.h" + +#include +#include +#include "ldap_debug.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +using namespace KLDAP; + +class Q_DECL_HIDDEN LdapConfigWidget::Private +{ +public: + Private(LdapConfigWidget *parent) + : mParent(parent) + { + mainLayout = new QGridLayout(mParent); + mainLayout->setContentsMargins({}); + } + + void setLDAPPort(); + void setLDAPSPort(); + void setAnonymous(bool on); + void setSimple(bool on); + void setSASL(bool on); + void queryDNClicked(); + void queryMechClicked(); + void loadData(LdapSearch *search, const LdapObject &object); + void loadResult(LdapSearch *search); + void sendQuery(); + void initWidget(); + + LdapConfigWidget *const mParent; + QStringList mQResult; + QString mAttr; + + QLineEdit *mUser = nullptr; + KPasswordLineEdit *mPassword = nullptr; + QLineEdit *mHost = nullptr; + QSpinBox *mPort = nullptr; + QSpinBox *mVersion = nullptr; + QSpinBox *mSizeLimit = nullptr; + QSpinBox *mTimeLimit = nullptr; + QSpinBox *mPageSize = nullptr; + QLineEdit *mDn = nullptr; + QLineEdit *mBindDn = nullptr; + QLineEdit *mRealm = nullptr; + QLineEdit *mFilter = nullptr; + QRadioButton *mAnonymous = nullptr; + QRadioButton *mSimple = nullptr; + QRadioButton *mSASL = nullptr; + QCheckBox *mSubTree = nullptr; + QPushButton *mEditButton = nullptr; + QPushButton *mQueryMech = nullptr; + QRadioButton *mSecNo = nullptr; + QRadioButton *mSecTLS = nullptr; + QRadioButton *mSecSSL = nullptr; + QComboBox *mMech = nullptr; + + QProgressDialog *mProg = nullptr; + + QGridLayout *mainLayout = nullptr; + WinFlags mFeatures = W_ALL; + bool mCancelled = false; +}; + +void LdapConfigWidget::Private::initWidget() +{ + QLabel *label = nullptr; + + mUser = mHost = mDn = mBindDn = mRealm = mFilter = nullptr; + mPassword = nullptr; + mPort = mVersion = mTimeLimit = mSizeLimit = nullptr; + mAnonymous = mSimple = mSASL = mSecNo = mSecTLS = mSecSSL = nullptr; + mEditButton = mQueryMech = nullptr; + mPageSize = nullptr; + mMech = nullptr; + int row = 0; + int col; + + if (mFeatures & W_USER) { + label = new QLabel(i18n("User:"), mParent); + mUser = new QLineEdit(mParent); + mUser->setObjectName(QStringLiteral("kcfg_ldapuser")); + + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mUser, row, 1, 1, 3); + row++; + } + + if (mFeatures & W_BINDDN) { + label = new QLabel(i18n("Bind DN:"), mParent); + mBindDn = new QLineEdit(mParent); + mBindDn->setObjectName(QStringLiteral("kcfg_ldapbinddn")); + + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mBindDn, row, 1, 1, 3); + row++; + } + + if (mFeatures & W_REALM) { + label = new QLabel(i18n("Realm:"), mParent); + mRealm = new QLineEdit(mParent); + mRealm->setObjectName(QStringLiteral("kcfg_ldaprealm")); + + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mRealm, row, 1, 1, 3); + row++; + } + + if (mFeatures & W_PASS) { + label = new QLabel(i18n("Password:"), mParent); + mPassword = new KPasswordLineEdit(mParent); + mPassword->setObjectName(QStringLiteral("kcfg_ldappassword")); + + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mPassword, row, 1, 1, 3); + row++; + } + + if (mFeatures & W_HOST) { + label = new QLabel(i18n("Host:"), mParent); + mHost = new QLineEdit(mParent); + mHost->setObjectName(QStringLiteral("kcfg_ldaphost")); + mParent->connect(mHost, &QLineEdit::textChanged, mParent, &LdapConfigWidget::hostNameChanged); + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mHost, row, 1, 1, 3); + row++; + } + + col = 0; + if (mFeatures & W_PORT) { + label = new QLabel(i18n("Port:"), mParent); + mPort = new QSpinBox(mParent); + mPort->setRange(0, 65535); + mPort->setObjectName(QStringLiteral("kcfg_ldapport")); + mPort->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); + mPort->setValue(389); + + mainLayout->addWidget(label, row, col); + mainLayout->addWidget(mPort, row, col + 1); + col += 2; + } + + if (mFeatures & W_VER) { + label = new QLabel(i18n("LDAP version:"), mParent); + mVersion = new QSpinBox(mParent); + mVersion->setRange(2, 3); + mVersion->setObjectName(QStringLiteral("kcfg_ldapver")); + mVersion->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); + mVersion->setValue(3); + mainLayout->addWidget(label, row, col); + mainLayout->addWidget(mVersion, row, col + 1); + } + if (mFeatures & (W_PORT | W_VER)) { + row++; + } + + col = 0; + if (mFeatures & W_SIZELIMIT) { + label = new QLabel(i18n("Size limit:"), mParent); + mSizeLimit = new QSpinBox(mParent); + mSizeLimit->setRange(0, 9999999); + mSizeLimit->setObjectName(QStringLiteral("kcfg_ldapsizelimit")); + mSizeLimit->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); + mSizeLimit->setValue(0); + mSizeLimit->setSpecialValueText(i18nc("default ldap size limit", "Default")); + mainLayout->addWidget(label, row, col); + mainLayout->addWidget(mSizeLimit, row, col + 1); + col += 2; + } + + if (mFeatures & W_TIMELIMIT) { + label = new QLabel(i18n("Time limit:"), mParent); + mTimeLimit = new QSpinBox(mParent); + mTimeLimit->setRange(0, 9999999); + mTimeLimit->setObjectName(QStringLiteral("kcfg_ldaptimelimit")); + mTimeLimit->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); + mTimeLimit->setValue(0); + mTimeLimit->setSuffix(i18n(" sec")); + mTimeLimit->setSpecialValueText(i18nc("default ldap time limit", "Default")); + mainLayout->addWidget(label, row, col); + mainLayout->addWidget(mTimeLimit, row, col + 1); + } + if (mFeatures & (W_SIZELIMIT | W_TIMELIMIT)) { + row++; + } + + if (mFeatures & W_PAGESIZE) { + label = new QLabel(i18n("Page size:"), mParent); + mPageSize = new QSpinBox(mParent); + mPageSize->setRange(0, 9999999); + mPageSize->setObjectName(QStringLiteral("kcfg_ldappagesize")); + mPageSize->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); + mPageSize->setValue(0); + mPageSize->setSpecialValueText(i18n("No paging")); + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mPageSize, row++, 1); + } + + if (mFeatures & W_DN) { + label = new QLabel(i18nc("Distinguished Name", "DN:"), mParent); + mDn = new QLineEdit(mParent); + mDn->setObjectName(QStringLiteral("kcfg_ldapdn")); + + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mDn, row, 1, 1, 1); + //without host query doesn't make sense + if (mHost) { + QPushButton *dnquery = new QPushButton(i18n("Query Server"), mParent); + dnquery->setEnabled(false); + connect(dnquery, &QPushButton::clicked, mParent, [this]() { + queryDNClicked(); + }); + connect(mDn, &QLineEdit::textChanged, mParent, [dnquery](const QString &text) { + dnquery->setEnabled(!text.trimmed().isEmpty()); + }); + mainLayout->addWidget(dnquery, row, 2, 1, 1); + } + row++; + } + + if (mFeatures & W_FILTER) { + label = new QLabel(i18n("Filter:"), mParent); + mFilter = new QLineEdit(mParent); + mFilter->setObjectName(QStringLiteral("kcfg_ldapfilter")); + + mainLayout->addWidget(label, row, 0); + mainLayout->addWidget(mFilter, row, 1, 1, 3); + row++; + } + + if (mFeatures & W_SECBOX) { + QGroupBox *btgroup = new QGroupBox(i18n("Security"), mParent); + auto *hbox = new QHBoxLayout; + btgroup->setLayout(hbox); + mSecNo = new QRadioButton(i18nc("@option:radio set no security", "No"), btgroup); + mSecNo->setObjectName(QStringLiteral("kcfg_ldapnosec")); + hbox->addWidget(mSecNo); + mSecTLS = new QRadioButton(i18nc("@option:radio use TLS security", "TLS"), btgroup); + mSecTLS->setObjectName(QStringLiteral("kcfg_ldaptls")); + hbox->addWidget(mSecTLS); + mSecSSL = new QRadioButton(i18nc("@option:radio use SSL security", "SSL"), btgroup); + mSecSSL->setObjectName(QStringLiteral("kcfg_ldapssl")); + hbox->addWidget(mSecSSL); + mainLayout->addWidget(btgroup, row, 0, 1, 4); + + connect(mSecNo, &QRadioButton::clicked, mParent, [this]() { + setLDAPPort(); + }); + connect(mSecTLS, &QRadioButton::clicked, mParent, [this]() { + setLDAPPort(); + }); + connect(mSecSSL, &QRadioButton::clicked, mParent, [this]() { + setLDAPSPort(); + }); + + mSecNo->setChecked(true); + row++; + } + + if (mFeatures & W_AUTHBOX) { + QGroupBox *authbox + = new QGroupBox(i18n("Authentication"), mParent); + auto *vbox = new QVBoxLayout; + authbox->setLayout(vbox); + auto *hbox = new QHBoxLayout; + vbox->addLayout(hbox); + + mAnonymous + = new QRadioButton(i18nc("@option:radio anonymous authentication", "Anonymous"), authbox); + mAnonymous->setObjectName(QStringLiteral("kcfg_ldapanon")); + hbox->addWidget(mAnonymous); + mSimple + = new QRadioButton(i18nc("@option:radio simple authentication", "Simple"), authbox); + mSimple->setObjectName(QStringLiteral("kcfg_ldapsimple")); + hbox->addWidget(mSimple); + mSASL + = new QRadioButton(i18nc("@option:radio SASL authentication", "SASL"), authbox); + mSASL->setObjectName(QStringLiteral("kcfg_ldapsasl")); + hbox->addWidget(mSASL); + + hbox = new QHBoxLayout; + vbox->addLayout(hbox); + label = new QLabel(i18n("SASL mechanism:"), authbox); + hbox->addWidget(label); + mMech = new QComboBox(authbox); + mMech->setObjectName(QStringLiteral("kcfg_ldapsaslmech")); + mMech->addItem(QStringLiteral("DIGEST-MD5")); + mMech->addItem(QStringLiteral("GSSAPI")); + mMech->addItem(QStringLiteral("PLAIN")); + hbox->addWidget(mMech); + + //without host query doesn't make sense + if (mHost) { + mQueryMech = new QPushButton(i18n("Query Server"), authbox); + hbox->addWidget(mQueryMech); + connect(mQueryMech, &QPushButton::clicked, mParent, [this]() { + queryMechClicked(); + }); + } + + mainLayout->addWidget(authbox, row, 0, 2, 4); + + connect(mAnonymous, &QRadioButton::toggled, mParent, [this](bool b) { + setAnonymous(b); + }); + connect(mSimple, &QRadioButton::toggled, mParent, [this](bool b) { + setSimple(b); + }); + connect(mSASL, &QRadioButton::toggled, mParent, [this](bool b) { + setSASL(b); + }); + + mAnonymous->setChecked(true); + } +} + +void LdapConfigWidget::Private::sendQuery() +{ + LdapServer _server(mParent->server()); + + mQResult.clear(); + mCancelled = true; + + if (mAttr == QLatin1String("supportedsaslmechanisms")) { + _server.setAuth(LdapServer::Anonymous); + } + + LdapUrl _url(_server.url()); + + _url.setDn(LdapDN(QLatin1String(""))); + _url.setAttributes(QStringList(mAttr)); + _url.setScope(LdapUrl::Base); + + qCDebug(LDAP_LOG) << "sendQuery url:" << _url.toDisplayString(); + + LdapSearch search; + connect(&search, &LdapSearch::data, mParent, [this](KLDAP::LdapSearch *s, const KLDAP::LdapObject &obj) { + loadData(s, obj); + }); + connect(&search, &LdapSearch::result, mParent, [this](KLDAP::LdapSearch *s) { + loadResult(s); + }); + + if (!search.search(_url)) { + KMessageBox::error(mParent, search.errorString(), i18n("Check server")); + return; + } + + if (mProg == nullptr) { + mProg = new QProgressDialog(mParent); + mProg->setWindowTitle(i18nc("@title:window", "LDAP Query")); + mProg->setModal(true); + } + mProg->setLabelText(_url.toDisplayString()); + mProg->setMaximum(1); + mProg->setMinimum(0); + mProg->setValue(0); + mProg->exec(); + if (mCancelled) { + qCDebug(LDAP_LOG) << "query canceled!"; + search.abandon(); + } else { + if (search.error()) { + if (search.errorString().isEmpty()) { + KMessageBox::error(mParent, i18nc("%1 is a url to ldap server", "Unknown error connecting %1", _url.toDisplayString())); + } else { + KMessageBox::error(mParent, search.errorString()); + } + } + } +} + +void LdapConfigWidget::Private::queryMechClicked() +{ + mAttr = QStringLiteral("supportedsaslmechanisms"); + sendQuery(); + if (!mQResult.isEmpty()) { + mQResult.sort(); + mMech->clear(); + mMech->addItems(mQResult); + } +} + +void LdapConfigWidget::Private::queryDNClicked() +{ + mAttr = QStringLiteral("namingcontexts"); + sendQuery(); + if (!mQResult.isEmpty()) { + mDn->setText(mQResult.constFirst()); + } +} + +void LdapConfigWidget::Private::loadData(LdapSearch *, const LdapObject &object) +{ + qCDebug(LDAP_LOG) << "object:" << object.toString(); + mProg->setValue(mProg->value() + 1); + LdapAttrMap::ConstIterator end(object.attributes().constEnd()); + for (LdapAttrMap::ConstIterator it = object.attributes().constBegin(); + it != end; ++it) { + LdapAttrValue::ConstIterator end2((*it).constEnd()); + for (LdapAttrValue::ConstIterator it2 = (*it).constBegin(); + it2 != end2; ++it2) { + mQResult.push_back(QString::fromUtf8(*it2)); + } + } +} + +void LdapConfigWidget::Private::loadResult(LdapSearch *search) +{ + Q_UNUSED(search) + mCancelled = false; + mProg->close(); +} + +void LdapConfigWidget::Private::setAnonymous(bool on) +{ + if (!on) { + return; + } + if (mUser) { + mUser->setEnabled(false); + } + if (mPassword) { + mPassword->setEnabled(false); + } + if (mBindDn) { + mBindDn->setEnabled(false); + } + if (mRealm) { + mRealm->setEnabled(false); + } + if (mMech) { + mMech->setEnabled(false); + } + if (mQueryMech) { + mQueryMech->setEnabled(false); + } +} + +void LdapConfigWidget::Private::setSimple(bool on) +{ + if (!on) { + return; + } + if (mUser) { + mUser->setEnabled(false); + } + if (mPassword) { + mPassword->setEnabled(true); + } + if (mBindDn) { + mBindDn->setEnabled(true); + } + if (mRealm) { + mRealm->setEnabled(false); + } + if (mMech) { + mMech->setEnabled(false); + } + if (mQueryMech) { + mQueryMech->setEnabled(false); + } +} + +void LdapConfigWidget::Private::setSASL(bool on) +{ + if (!on) { + return; + } + if (mUser) { + mUser->setEnabled(true); + } + if (mPassword) { + mPassword->setEnabled(true); + } + if (mBindDn) { + mBindDn->setEnabled(true); + } + if (mRealm) { + mRealm->setEnabled(true); + } + if (mMech) { + mMech->setEnabled(true); + } + if (mQueryMech) { + mQueryMech->setEnabled(true); + } +} + +void LdapConfigWidget::Private::setLDAPPort() +{ + if (mPort) { + mPort->setValue(389); + } +} + +void LdapConfigWidget::Private::setLDAPSPort() +{ + if (mPort) { + mPort->setValue(636); + } +} + +LdapConfigWidget::LdapConfigWidget(QWidget *parent, Qt::WindowFlags fl) + : QWidget(parent, fl) + , d(new Private(this)) +{ +} + +LdapConfigWidget::LdapConfigWidget(LdapConfigWidget::WinFlags flags, QWidget *parent, Qt::WindowFlags fl) + : QWidget(parent, fl) + , d(new Private(this)) +{ + d->mFeatures = flags; + + d->initWidget(); +} + +LdapConfigWidget::~LdapConfigWidget() +{ + delete d; +} + +LdapUrl LdapConfigWidget::url() const +{ + return server().url(); +} + +void LdapConfigWidget::setUrl(const LdapUrl &url) +{ + LdapServer _server; + _server.setUrl(url); + setServer(_server); +} + +LdapServer LdapConfigWidget::server() const +{ + LdapServer _server; + if (d->mSecSSL && d->mSecSSL->isChecked()) { + _server.setSecurity(LdapServer::SSL); + } else if (d->mSecTLS && d->mSecTLS->isChecked()) { + _server.setSecurity(LdapServer::TLS); + } else { + _server.setSecurity(LdapServer::None); + } + + if (d->mUser) { + _server.setUser(d->mUser->text()); + } + if (d->mBindDn) { + _server.setBindDn(d->mBindDn->text()); + } + if (d->mPassword) { + _server.setPassword(d->mPassword->password()); + } + if (d->mRealm) { + _server.setRealm(d->mRealm->text()); + } + if (d->mHost) { + _server.setHost(d->mHost->text()); + } + if (d->mPort) { + _server.setPort(d->mPort->value()); + } + if (d->mDn) { + _server.setBaseDn(LdapDN(d->mDn->text())); + } + if (d->mFilter) { + _server.setFilter(d->mFilter->text()); + } + if (d->mVersion) { + _server.setVersion(d->mVersion->value()); + } + if (d->mSizeLimit && d->mSizeLimit->value() != 0) { + _server.setSizeLimit(d->mSizeLimit->value()); + } + if (d->mTimeLimit && d->mTimeLimit->value() != 0) { + _server.setTimeLimit(d->mTimeLimit->value()); + } + if (d->mPageSize && d->mPageSize->value() != 0) { + _server.setPageSize(d->mPageSize->value()); + } + if (d->mAnonymous && d->mAnonymous->isChecked()) { + _server.setAuth(LdapServer::Anonymous); + } else if (d->mSimple && d->mSimple->isChecked()) { + _server.setAuth(LdapServer::Simple); + } else if (d->mSASL && d->mSASL->isChecked()) { + _server.setAuth(LdapServer::SASL); + _server.setMech(d->mMech->currentText()); + } + return _server; +} + +void LdapConfigWidget::setServer(const LdapServer &server) +{ + switch (server.security()) { + case LdapServer::SSL: + if (d->mSecSSL) { + d->mSecSSL->setChecked(true); + } + break; + case LdapServer::TLS: + if (d->mSecTLS) { + d->mSecTLS->setChecked(true); + } + break; + case LdapServer::None: + if (d->mSecNo) { + d->mSecNo->setChecked(true); + } + break; + } + + switch (server.auth()) { + case LdapServer::Anonymous: + if (d->mAnonymous) { + d->mAnonymous->setChecked(true); + } + break; + case LdapServer::Simple: + if (d->mSimple) { + d->mSimple->setChecked(true); + } + break; + case LdapServer::SASL: + if (d->mSASL) { + d->mSASL->setChecked(true); + } + break; + } + + setUser(server.user()); + setBindDn(server.bindDn()); + setPassword(server.password()); + setRealm(server.realm()); + setHost(server.host()); + setPort(server.port()); + setFilter(server.filter()); + setDn(server.baseDn()); + setVersion(server.version()); + setSizeLimit(server.sizeLimit()); + setTimeLimit(server.timeLimit()); + setPageSize(server.pageSize()); + setMech(server.mech()); +} + +void LdapConfigWidget::setUser(const QString &user) +{ + if (d->mUser) { + d->mUser->setText(user); + } +} + +QString LdapConfigWidget::user() const +{ + return d->mUser ? d->mUser->text() : QString(); +} + +void LdapConfigWidget::setPassword(const QString &password) +{ + if (d->mPassword) { + d->mPassword->setPassword(password); + } +} + +QString LdapConfigWidget::password() const +{ + return d->mPassword ? d->mPassword->password() : QString(); +} + +void LdapConfigWidget::setBindDn(const QString &binddn) +{ + if (d->mBindDn) { + d->mBindDn->setText(binddn); + } +} + +QString LdapConfigWidget::bindDn() const +{ + return d->mBindDn ? d->mBindDn->text() : QString(); +} + +void LdapConfigWidget::setRealm(const QString &realm) +{ + if (d->mRealm) { + d->mRealm->setText(realm); + } +} + +QString LdapConfigWidget::realm() const +{ + return d->mRealm ? d->mRealm->text() : QString(); +} + +void LdapConfigWidget::setHost(const QString &host) +{ + if (d->mHost) { + d->mHost->setText(host); + } +} + +QString LdapConfigWidget::host() const +{ + return d->mHost ? d->mHost->text() : QString(); +} + +void LdapConfigWidget::setPort(int port) +{ + if (d->mPort) { + d->mPort->setValue(port); + } +} + +int LdapConfigWidget::port() const +{ + return d->mPort ? d->mPort->value() : 389; +} + +void LdapConfigWidget::setVersion(int version) +{ + if (d->mVersion) { + d->mVersion->setValue(version); + } +} + +int LdapConfigWidget::version() const +{ + return d->mVersion ? d->mVersion->value() : 3; +} + +void LdapConfigWidget::setDn(const LdapDN &dn) +{ + if (d->mDn) { + d->mDn->setText(dn.toString()); + } +} + +LdapDN LdapConfigWidget::dn() const +{ + return d->mDn ? LdapDN(d->mDn->text()) : LdapDN(); +} + +void LdapConfigWidget::setFilter(const QString &filter) +{ + if (d->mFilter) { + d->mFilter->setText(filter); + } +} + +QString LdapConfigWidget::filter() const +{ + return d->mFilter ? d->mFilter->text() : QString(); +} + +void LdapConfigWidget::setMech(const QString &mech) +{ + if (d->mMech == nullptr) { + return; + } + if (!mech.isEmpty()) { + int i = 0; + while (i < d->mMech->count()) { + if (d->mMech->itemText(i) == mech) { + break; + } + i++; + } + if (i == d->mMech->count()) { + d->mMech->addItem(mech); + } + d->mMech->setCurrentIndex(i); + } +} + +QString LdapConfigWidget::mech() const +{ + return d->mMech ? d->mMech->currentText() : QString(); +} + +void LdapConfigWidget::setSecurity(Security security) +{ + switch (security) { + case None: + d->mSecNo->setChecked(true); + break; + case SSL: + d->mSecSSL->setChecked(true); + break; + case TLS: + d->mSecTLS->setChecked(true); + break; + } +} + +LdapConfigWidget::Security LdapConfigWidget::security() const +{ + if (d->mSecTLS->isChecked()) { + return TLS; + } + if (d->mSecSSL->isChecked()) { + return SSL; + } + return None; +} + +void LdapConfigWidget::setAuth(Auth auth) +{ + switch (auth) { + case Anonymous: + d->mAnonymous->setChecked(true); + break; + case Simple: + d->mSimple->setChecked(true); + break; + case SASL: + d->mSASL->setChecked(true); + break; + } +} + +LdapConfigWidget::Auth LdapConfigWidget::auth() const +{ + if (d->mSimple->isChecked()) { + return Simple; + } + if (d->mSASL->isChecked()) { + return SASL; + } + return Anonymous; +} + +void LdapConfigWidget::setSizeLimit(int sizelimit) +{ + if (d->mSizeLimit) { + d->mSizeLimit->setValue(sizelimit); + } +} + +int LdapConfigWidget::sizeLimit() const +{ + return d->mSizeLimit ? d->mSizeLimit->value() : 0; +} + +void LdapConfigWidget::setTimeLimit(int timelimit) +{ + if (d->mTimeLimit) { + d->mTimeLimit->setValue(timelimit); + } +} + +int LdapConfigWidget::timeLimit() const +{ + return d->mTimeLimit ? d->mTimeLimit->value() : 0; +} + +void LdapConfigWidget::setPageSize(int pagesize) +{ + if (d->mPageSize) { + d->mPageSize->setValue(pagesize); + } +} + +int LdapConfigWidget::pageSize() const +{ + return d->mPageSize ? d->mPageSize->value() : 0; +} + +LdapConfigWidget::WinFlags LdapConfigWidget::features() const +{ + return d->mFeatures; +} + +void LdapConfigWidget::setFeatures(LdapConfigWidget::WinFlags features) +{ + d->mFeatures = features; + + // First delete all the child widgets. + // FIXME: I hope it's correct + QList ch = children(); + const int numberOfChild(ch.count()); + for (int i = 0; i < numberOfChild; ++i) { + QWidget *widget = qobject_cast(ch[ i ]); + if (widget && widget->parent() == this) { + delete(widget); + } + } + + // Re-create child widgets according to the new flags + d->initWidget(); +} + +#include "moc_ldapconfigwidget.cpp" diff --git a/3rdparty/kldap/src/widgets/ldapconfigwidget.h b/3rdparty/kldap/src/widgets/ldapconfigwidget.h new file mode 100644 index 0000000..a22b21b --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapconfigwidget.h @@ -0,0 +1,264 @@ +/* + This file is part of libkldap. + SPDX-FileCopyrightText: 2004-2006 Szombathelyi György + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KLDAP_LDAPCONFIGWIDGET_H +#define KLDAP_LDAPCONFIGWIDGET_H + +#include +#include + +#include "ldapdn.h" +#include "kldap_export.h" +#include "ldapobject.h" +#include "ldapserver.h" +#include "ldapurl.h" + +namespace KLDAP { + +/** + @brief LDAP Configuration widget + + This class can be used to query the user for LDAP connection parameters. + It's KConfigXT compatible, using widget names starting with kcfg_ +*/ + +class KLDAP_EXPORT LdapConfigWidget : public QWidget +{ + Q_OBJECT + Q_FLAGS(WinFlags) + Q_PROPERTY(WinFlags features READ features WRITE setFeatures) + Q_PROPERTY(QString user READ user WRITE setUser) + Q_PROPERTY(QString bindDn READ bindDn WRITE setBindDn) + Q_PROPERTY(QString realm READ realm WRITE setRealm) + Q_PROPERTY(QString password READ password WRITE setPassword) + Q_PROPERTY(QString host READ host WRITE setHost) + Q_PROPERTY(int port READ port WRITE setPort) + Q_PROPERTY(int version READ version WRITE setVersion) + Q_PROPERTY(LdapDN dn READ dn WRITE setDn) + Q_PROPERTY(QString filter READ filter WRITE setFilter) + Q_PROPERTY(QString mech READ mech WRITE setMech) + Q_PROPERTY(Security security READ security WRITE setSecurity) + Q_PROPERTY(Auth auth READ auth WRITE setAuth) + Q_PROPERTY(int sizeLimit READ sizeLimit WRITE setSizeLimit) + Q_PROPERTY(int timeLimit READ timeLimit WRITE setTimeLimit) + Q_PROPERTY(int pageSize READ pageSize WRITE setPageSize) + +public: + + enum WinFlag { + W_USER = 0x1, + W_BINDDN = 0x2, + W_REALM = 0x4, + W_PASS = 0x8, + W_HOST = 0x10, + W_PORT = 0x20, + W_VER = 0x40, + W_DN = 0x80, + W_FILTER = 0x100, + W_SECBOX = 0x200, + W_AUTHBOX = 0x400, + W_TIMELIMIT = 0x800, + W_SIZELIMIT = 0x1000, + W_PAGESIZE = 0x2000, + W_ALL = 0x2fff + }; + Q_DECLARE_FLAGS(WinFlags, WinFlag) + + enum Security { + None, SSL, TLS + }; + Q_ENUM(Security) + + enum Auth { + Anonymous, Simple, SASL + }; + Q_ENUM(Auth) + + /** Constructs an empty configuration widget. + * You need to call setFlags() after this. + * @param parent the QWidget parent + * @param fl the window flags to set + */ + explicit LdapConfigWidget(QWidget *parent = nullptr, Qt::WindowFlags fl = {}); + /** Constructs a configuration widget */ + explicit LdapConfigWidget(WinFlags flags, QWidget *parent = nullptr, Qt::WindowFlags fl = {}); + /** Destructs a configuration widget */ + ~LdapConfigWidget(); + + /** Sets the user name. Kconfig widget name: kcfg_ldapuser + * @param user the user name to set + */ + void setUser(const QString &user); + /** Gets the user name. Kconfig widget name: kcfg_ldapuser */ + Q_REQUIRED_RESULT QString user() const; + + /** Sets the password. Kconfig widget name: kcfg_ldappassword + * @param password the password to set + */ + void setPassword(const QString &password); + /** Gets the password. Kconfig widget name: kcfg_ldappassword */ + Q_REQUIRED_RESULT QString password() const; + + /** + * Sets the bind dn. + * Kconfig widget name: kcfg_ldapbinddn + * @param binddn the LDAP Bind DN to set + */ + void setBindDn(const QString &binddn); + /** Gets the bind dn. Kconfig widget name: kcfg_ldapbinddn*/ + Q_REQUIRED_RESULT QString bindDn() const; + + /** Sets the SASL realm. Kconfig widget name: kcfg_ldaprealm + * @param realm the SASL realm to set + */ + void setRealm(const QString &realm); + /** Gets the SASL realm. Kconfig widget name: kcfg_ldaprealm */ + Q_REQUIRED_RESULT QString realm() const; + + /** Sets the host name. Kconfig widget name: kcfg_ldaphost + * @param host the LDAP host to set + */ + void setHost(const QString &host); + /** Gets the host name. Kconfig widget name: kcfg_ldaphost */ + Q_REQUIRED_RESULT QString host() const; + + /** Sets the LDAP port. Kconfig widget name: kcfg_ldapport + * @param port the LDAP port to set + */ + void setPort(int port); + /** Gets the LDAP port. Kconfig widget name: kcfg_ldapport */ + Q_REQUIRED_RESULT int port() const; + + /** Sets the LDAP protocol version. Kconfig widget name: kcfg_ldapver + * @param version the LDAP protocol version to set + */ + void setVersion(int version); + /** Gets the LDAP protocol version. Kconfig widget name: kcfg_ldapver */ + Q_REQUIRED_RESULT int version() const; + + /** Sets the LDAP Base DN. Kconfig widget name: kcfg_ldapdn + * @param dn the LDAP Base DN to set + */ + void setDn(const LdapDN &dn); + /** Gets the LDAP Base DN. Kconfig widget name: kcfg_ldapdn */ + Q_REQUIRED_RESULT LdapDN dn() const; + + /** Sets the LDAP Filter. Kconfig widget name: kcfg_ldapfilter + * @param filter the LDAP Filter to set + */ + void setFilter(const QString &filter); + /** Gets the LDAP Filter. Kconfig widget name: kcfg_ldapfilter */ + Q_REQUIRED_RESULT QString filter() const; + + /** Sets the SASL Mechanism. Kconfig widget name: kcfg_ldapsaslmech + * @param mech the SASL Mechanism to set + */ + void setMech(const QString &mech); + /** Gets the SASL Mechanism. Kconfig widget name: kcfg_ldapsaslmech */ + Q_REQUIRED_RESULT QString mech() const; + + /** + * Sets the security type (None, SSL, TLS). + * Kconfig widget names: kcfg_ldapnosec, kcfg_ldaptls, kcfg_ldapssl + * @param security the security type to set + */ + void setSecurity(Security security); + /** + * Returns the security type. + * Kconfig widget names: kcfg_ldapnosec, kcfg_ldaptls, kcfg_ldapssl + * @param security the security type to set + */ + Q_REQUIRED_RESULT Security security() const; + + /** + * Sets the authentication type (Anonymous, Simple, SASL). + * Kconfig widget names: kcfg_ldapanon, kcfg_ldapsimple, kcfg_ldapsasl + * @param auth the authentication type to set + */ + void setAuth(Auth auth); + /** + * Returns the authentication type. + * Kconfig widget names: kcfg_ldapanon, kcfg_ldapsimple, kcfg_ldapsasl + * @param auth the authentication type to set + */ + Q_REQUIRED_RESULT Auth auth() const; + + /** + * Sets the size limit. + * KConfig widget name: kcfg_ldapsizelimit + * @param sizelimit the size limit to set + */ + void setSizeLimit(int sizelimit); + /** + * Returns the size limit. + * KConfig widget name: kcfg_ldapsizelimit + */ + Q_REQUIRED_RESULT int sizeLimit() const; + + /** + * Sets the time limit. + * KConfig widget name: kcfg_ldaptimelimit + * @param timelimit the time limit to set + */ + void setTimeLimit(int timelimit); + /** + * Returns the time limit. + * KConfig widget name: kcfg_ldaptimelimit + */ + Q_REQUIRED_RESULT int timeLimit() const; + + /** + * Sets the page size. + * KConfig widget name: kcfg_ldappagesize + * @param pagesize the page size to set + */ + void setPageSize(int pagesize); + /** + * Returns the page size. + * KConfig widget name: kcfg_ldappagesize + */ + Q_REQUIRED_RESULT int pageSize() const; + + Q_REQUIRED_RESULT WinFlags features() const; + void setFeatures(WinFlags features); + + /** + * Returns a LDAP Url constructed from the settings given. + * Extensions are filled for use in the LDAP ioslave + */ + Q_REQUIRED_RESULT LdapUrl url() const; + /** + * Set up the widget via an LDAP Url. + * @param url the LDAP Url to set + */ + void setUrl(const LdapUrl &url); + + /** + * Returns an LdapServer object constructed from the settings given. + */ + Q_REQUIRED_RESULT LdapServer server() const; + /** + * Set up the widget via an LdapServer object. + * @param server the LdapServer object to set + */ + void setServer(const LdapServer &server); + +Q_SIGNALS: + /** + * @since 4.13 + */ + void hostNameChanged(const QString &); + +private: + class Private; + Private *const d; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(LdapConfigWidget::WinFlags) +} + +#endif diff --git a/3rdparty/kldap/src/widgets/ldapsearchclientreadconfigserverjob.cpp b/3rdparty/kldap/src/widgets/ldapsearchclientreadconfigserverjob.cpp new file mode 100644 index 0000000..bee637d --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapsearchclientreadconfigserverjob.cpp @@ -0,0 +1,87 @@ +/* + * SPDX-FileCopyrightText: 2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "ldapsearchclientreadconfigserverjob.h" +#include "ldapclient.h" +#include "ldapclientsearchconfigreadconfigjob.h" +#include "ldapclient_debug.h" +using namespace KLDAP; +LdapSearchClientReadConfigServerJob::LdapSearchClientReadConfigServerJob(QObject *parent) + : QObject(parent) +{ + +} + +LdapSearchClientReadConfigServerJob::~LdapSearchClientReadConfigServerJob() +{ + +} + +void LdapSearchClientReadConfigServerJob::start() +{ + if (!canStart()) { + qCWarning(LDAPCLIENT_LOG) << " Impossible to start LdapSearchClientReadConfigServerJob"; + deleteLater(); + return; + } + auto job = new LdapClientSearchConfigReadConfigJob(this); + connect(job, &LdapClientSearchConfigReadConfigJob::configLoaded, this, &LdapSearchClientReadConfigServerJob::slotConfigLoaded); + job->setActive(mActive); + job->setConfig(mConfig); + job->setServerIndex(mCurrentIndex); + job->start(); +} + +bool LdapSearchClientReadConfigServerJob::canStart() const +{ + return mCurrentIndex != -1 && mConfig.isValid(); +} + +void LdapSearchClientReadConfigServerJob::slotConfigLoaded(const KLDAP::LdapServer &server) +{ + mLdapClient->setServer(server); + deleteLater(); +} + +LdapClient *LdapSearchClientReadConfigServerJob::ldapClient() const +{ + return mLdapClient; +} + +void LdapSearchClientReadConfigServerJob::setLdapClient(LdapClient *ldapClient) +{ + mLdapClient = ldapClient; +} + +int LdapSearchClientReadConfigServerJob::currentIndex() const +{ + return mCurrentIndex; +} + +void LdapSearchClientReadConfigServerJob::setCurrentIndex(int currentIndex) +{ + mCurrentIndex = currentIndex; +} + +bool LdapSearchClientReadConfigServerJob::active() const +{ + return mActive; +} + +void LdapSearchClientReadConfigServerJob::setActive(bool active) +{ + mActive = active; +} + +KConfigGroup LdapSearchClientReadConfigServerJob::config() const +{ + return mConfig; +} + +void LdapSearchClientReadConfigServerJob::setConfig(const KConfigGroup &config) +{ + mConfig = config; +} diff --git a/3rdparty/kldap/src/widgets/ldapsearchclientreadconfigserverjob.h b/3rdparty/kldap/src/widgets/ldapsearchclientreadconfigserverjob.h new file mode 100644 index 0000000..52ac956 --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapsearchclientreadconfigserverjob.h @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#ifndef LDAPSEARCHCLIENTREADCONFIGSERVERJOB_H +#define LDAPSEARCHCLIENTREADCONFIGSERVERJOB_H + +#include +#include +#include "kldap_export.h" +namespace KLDAP { +class LdapClient; +class LdapServer; +class KLDAP_EXPORT LdapSearchClientReadConfigServerJob : public QObject +{ + Q_OBJECT +public: + explicit LdapSearchClientReadConfigServerJob(QObject *parent = nullptr); + ~LdapSearchClientReadConfigServerJob() override; + + void start(); + Q_REQUIRED_RESULT bool canStart() const; + + Q_REQUIRED_RESULT int currentIndex() const; + void setCurrentIndex(int currentIndex); + + Q_REQUIRED_RESULT bool active() const; + void setActive(bool active); + + Q_REQUIRED_RESULT KConfigGroup config() const; + void setConfig(const KConfigGroup &config); + + LdapClient *ldapClient() const; + void setLdapClient(LdapClient *ldapClient); + +private: + void slotConfigLoaded(const KLDAP::LdapServer &server); + LdapClient *mLdapClient = nullptr; + KConfigGroup mConfig; + int mCurrentIndex = -1; + bool mActive = false; +}; +} + +#endif // LDAPSEARCHCLIENTREADCONFIGSERVERJOB_H diff --git a/3rdparty/kldap/src/widgets/ldapwidgetitem_p.cpp b/3rdparty/kldap/src/widgets/ldapwidgetitem_p.cpp new file mode 100644 index 0000000..73384c8 --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapwidgetitem_p.cpp @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "ldapwidgetitem_p.h" +using namespace KLDAP; + +LdapWidgetItem::LdapWidgetItem(QListWidget *parent, bool isActive) + : QListWidgetItem(parent, QListWidgetItem::UserType) + , mIsActive(isActive) +{ + setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); + setCheckState(isActive ? Qt::Checked : Qt::Unchecked); +} + +void LdapWidgetItem::setServer(const KLDAP::LdapServer &server) +{ + mServer = server; + setText(mServer.host()); +} + +const KLDAP::LdapServer &LdapWidgetItem::server() const +{ + return mServer; +} + +void LdapWidgetItem::setIsActive(bool isActive) +{ + mIsActive = isActive; +} + +bool LdapWidgetItem::isActive() const +{ + return mIsActive; +} diff --git a/3rdparty/kldap/src/widgets/ldapwidgetitem_p.h b/3rdparty/kldap/src/widgets/ldapwidgetitem_p.h new file mode 100644 index 0000000..4dcfa64 --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapwidgetitem_p.h @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#ifndef LDAPWIDGETITEM_H +#define LDAPWIDGETITEM_H + +#include +#include +namespace KLDAP { +class LdapWidgetItem : public QListWidgetItem +{ +public: + explicit LdapWidgetItem(QListWidget *parent, bool isActive = false); + + void setServer(const KLDAP::LdapServer &server); + + const KLDAP::LdapServer &server() const; + + void setIsActive(bool isActive); + + Q_REQUIRED_RESULT bool isActive() const; + +private: + KLDAP::LdapServer mServer; + bool mIsActive = false; +}; +} + +#endif // LDAPWIDGETITEM_H diff --git a/3rdparty/kldap/src/widgets/ldapwidgetitemreadconfigserverjob.cpp b/3rdparty/kldap/src/widgets/ldapwidgetitemreadconfigserverjob.cpp new file mode 100644 index 0000000..52844b7 --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapwidgetitemreadconfigserverjob.cpp @@ -0,0 +1,76 @@ +/* + * SPDX-FileCopyrightText: 2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "ldapwidgetitemreadconfigserverjob.h" +#include "ldapwidgetitem_p.h" +#include "ldapclientsearchconfigreadconfigjob.h" +using namespace KLDAP; +LdapWidgetItemReadConfigServerJob::LdapWidgetItemReadConfigServerJob(QObject *parent) + : QObject(parent) +{ + +} + +LdapWidgetItemReadConfigServerJob::~LdapWidgetItemReadConfigServerJob() +{ + +} + +void LdapWidgetItemReadConfigServerJob::start() +{ + auto job = new LdapClientSearchConfigReadConfigJob(this); + connect(job, &LdapClientSearchConfigReadConfigJob::configLoaded, this, &LdapWidgetItemReadConfigServerJob::slotConfigLoaded); + job->setActive(mActive); + job->setConfig(mConfig); + job->setServerIndex(mCurrentIndex); + job->start(); +} + +void LdapWidgetItemReadConfigServerJob::slotConfigLoaded(const KLDAP::LdapServer &server) +{ + mLdapWidgetItem->setServer(server); + deleteLater(); +} + +LdapWidgetItem *LdapWidgetItemReadConfigServerJob::ldapWidgetItem() const +{ + return mLdapWidgetItem; +} + +void LdapWidgetItemReadConfigServerJob::setLdapWidgetItem(LdapWidgetItem *ldapWidgetItem) +{ + mLdapWidgetItem = ldapWidgetItem; +} + +int LdapWidgetItemReadConfigServerJob::currentIndex() const +{ + return mCurrentIndex; +} + +void LdapWidgetItemReadConfigServerJob::setCurrentIndex(int currentIndex) +{ + mCurrentIndex = currentIndex; +} + +bool LdapWidgetItemReadConfigServerJob::active() const +{ + return mActive; +} + +void LdapWidgetItemReadConfigServerJob::setActive(bool active) +{ + mActive = active; +} + +KConfigGroup LdapWidgetItemReadConfigServerJob::config() const +{ + return mConfig; +} + +void LdapWidgetItemReadConfigServerJob::setConfig(const KConfigGroup &config) +{ + mConfig = config; +} diff --git a/3rdparty/kldap/src/widgets/ldapwidgetitemreadconfigserverjob.h b/3rdparty/kldap/src/widgets/ldapwidgetitemreadconfigserverjob.h new file mode 100644 index 0000000..a0a143f --- /dev/null +++ b/3rdparty/kldap/src/widgets/ldapwidgetitemreadconfigserverjob.h @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2020 Laurent Montel + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#ifndef LDAPWIDGETITEMREADCONFIGSERVERJOB_H +#define LDAPWIDGETITEMREADCONFIGSERVERJOB_H + +#include +#include +namespace KLDAP { +class LdapWidgetItem; +class LdapServer; +class LdapWidgetItemReadConfigServerJob : public QObject +{ + Q_OBJECT +public: + explicit LdapWidgetItemReadConfigServerJob(QObject *parent = nullptr); + ~LdapWidgetItemReadConfigServerJob() override; + + void start(); + + LdapWidgetItem *ldapWidgetItem() const; + void setLdapWidgetItem(LdapWidgetItem *ldapWidgetItem); + + Q_REQUIRED_RESULT int currentIndex() const; + void setCurrentIndex(int currentIndex); + + Q_REQUIRED_RESULT bool active() const; + void setActive(bool active); + + Q_REQUIRED_RESULT KConfigGroup config() const; + void setConfig(const KConfigGroup &config); + +private: + void slotConfigLoaded(const KLDAP::LdapServer &server); + LdapWidgetItem *mLdapWidgetItem = nullptr; + KConfigGroup mConfig; + int mCurrentIndex = -1; + bool mActive = false; +}; +} + +#endif // LDAPWIDGETITEMREADCONFIGSERVERJOB_H diff --git a/3rdparty/kldap/tests/CMakeLists.txt b/3rdparty/kldap/tests/CMakeLists.txt new file mode 100644 index 0000000..37e7ebf --- /dev/null +++ b/3rdparty/kldap/tests/CMakeLists.txt @@ -0,0 +1,8 @@ +########## next target ############### +set(testldapclient_SRCS testldapclient.cpp) + +add_executable(testldapclient ${testldapclient_SRCS}) + +target_link_libraries(testldapclient KF5::I18n KF5::Completion KF5::Ldap KF5::CoreAddons) + +#ldapclientsearchconfigreadconfigjobtest.cpp diff --git a/3rdparty/kldap/tests/testldapclient.cpp b/3rdparty/kldap/tests/testldapclient.cpp new file mode 100644 index 0000000..6a4ffc7 --- /dev/null +++ b/3rdparty/kldap/tests/testldapclient.cpp @@ -0,0 +1,176 @@ +/* This file is part of the KDE project + SPDX-FileCopyrightText: 2005 David Faure + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "testldapclient.h" + +#include + +#include + +#include + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QStandardPaths::setTestModeEnabled(true); + QCommandLineParser parser; + parser.addVersionOption(); + parser.addHelpOption(); + parser.process(app); + + TestLDAPClient test; + test.setup(); + test.runAll(); + test.cleanup(); + qDebug() << "All tests OK."; + return 0; +} + +TestLDAPClient::TestLDAPClient() +{ +} + +void TestLDAPClient::setup() +{ +} + +void TestLDAPClient::runAll() +{ + testIntevation(); +} + +bool TestLDAPClient::check(const QString &txt, QString a, QString b) +{ + if (a.isEmpty()) { + a.clear(); + } + + if (b.isEmpty()) { + b.clear(); + } + + if (a == b) { + qDebug() << txt << " : checking '" << a << "' against expected value '" << b << "'..." << "ok"; + } else { + qDebug() << txt << " : checking '" << a << "' against expected value '" << b << "'..." << "KO !"; + cleanup(); + exit(1); + } + + return true; +} + +void TestLDAPClient::cleanup() +{ + mClient = nullptr; +} + +void TestLDAPClient::testIntevation() +{ + qDebug(); + mClient = new KLDAP::LdapClient(0, this); + +#if 0 + mClient->setHost("ca.intevation.de"); + mClient->setPort("389"); + mClient->setBase("o=Intevation GmbH,c=de"); +#endif + + // Same list as in kaddressbook's ldapsearchdialog + QStringList attrs; + attrs << QStringLiteral("l") << QStringLiteral("Company") << QStringLiteral("co") << QStringLiteral("department") << QStringLiteral("description") << QStringLiteral("mail") + << QStringLiteral("facsimileTelephoneNumber") << QStringLiteral("cn") << QStringLiteral("homePhone") << QStringLiteral("mobile") << QStringLiteral("o") + << QStringLiteral("pager") << QStringLiteral("postalAddress") << QStringLiteral("st") << QStringLiteral("street") + << QStringLiteral("title") << QStringLiteral("uid") << QStringLiteral("telephoneNumber") << QStringLiteral("postalCode") << QStringLiteral("objectClass"); + // the list from ldapclient.cpp + //attrs << "cn" << "mail" << "givenname" << "sn" << "objectClass"; + mClient->setAttributes(attrs); + + // Taken from LdapSearch + /* + QString mSearchText = QString::fromUtf8( "Till" ); + QString filter = QString( "&(|(objectclass=person)(objectclass=groupOfNames)(mail=*))" + "(|(cn=%1*)(mail=%2*)(givenName=%3*)(sn=%4*))" ) + .arg( mSearchText ).arg( mSearchText ).arg( mSearchText ).arg( mSearchText ); + */ + + // For some reason a fromUtf8 broke the search for me (no results). + // But this certainly looks fishy, it might break on non-utf8 systems. + QString filter = QStringLiteral("&(|(objectclass=person)(objectclass=groupofnames)(mail=*))" + "(|(cn=*Ägypten MDK*)(sn=*Ägypten MDK*))"); + + connect(mClient, &KLDAP::LdapClient::result, this, &TestLDAPClient::slotLDAPResult); + connect(mClient, &KLDAP::LdapClient::done, this, &TestLDAPClient::slotLDAPDone); + connect(mClient, &KLDAP::LdapClient::error, this, &TestLDAPClient::slotLDAPError); + mClient->startQuery(filter); + + QEventLoop eventLoop; + connect(this, &TestLDAPClient::leaveModality, &eventLoop, &QEventLoop::quit); + eventLoop.exec(QEventLoop::ExcludeUserInputEvents); + + delete mClient; + mClient = nullptr; +} + +// from kaddressbook... ugly though... +static QString asUtf8(const QByteArray &val) +{ + if (val.isEmpty()) { + return QString(); + } + + const char *data = val.data(); + + //QString::fromUtf8() bug workaround + if (data[ val.size() - 1 ] == '\0') { + return QString::fromUtf8(data, val.size() - 1); + } else { + return QString::fromUtf8(data, val.size()); + } +} + +static QString join(const KLDAP::LdapAttrValue &lst, const QString &sep) +{ + QString res; + bool already = false; + for (KLDAP::LdapAttrValue::ConstIterator it = lst.begin(); it != lst.end(); ++it) { + if (already) { + res += sep; + } + + already = true; + res += asUtf8(*it); + } + + return res; +} + +void TestLDAPClient::slotLDAPResult(const KLDAP::LdapClient &, const KLDAP::LdapObject &obj) +{ + QString cn = join(obj.attributes()[ QStringLiteral("cn") ], QStringLiteral(", ")); + qDebug() << " cn:" << cn; + Q_ASSERT(!obj.attributes()[ QStringLiteral("mail") ].isEmpty()); + QString mail = join(obj.attributes()[ QStringLiteral("mail") ], QStringLiteral(", ")); + qDebug() << " mail:" << mail; + Q_ASSERT(mail.contains(QLatin1Char('@'))); +} + +void TestLDAPClient::slotLDAPError(const QString &err) +{ + qDebug() << err; + ::exit(1); +} + +void TestLDAPClient::slotLDAPDone() +{ + qDebug(); + Q_EMIT leaveModality(); +} diff --git a/3rdparty/kldap/tests/testldapclient.h b/3rdparty/kldap/tests/testldapclient.h new file mode 100644 index 0000000..cfb448b --- /dev/null +++ b/3rdparty/kldap/tests/testldapclient.h @@ -0,0 +1,46 @@ +/* This file is part of the KDE project + SPDX-FileCopyrightText: 2005 David Faure + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef TESTLDAPCLIENT_H +#define TESTLDAPCLIENT_H + +#include + +#include "widgets/ldapclient.h" + +namespace KLDAP { +class LdapClient; +class LdapObject; +} + +class TestLDAPClient : public QObject +{ + Q_OBJECT + +public: + TestLDAPClient(); + void setup(); + void runAll(); + void cleanup(); + + // tests + void testIntevation(); + +Q_SIGNALS: + void leaveModality(); + +private Q_SLOTS: + void slotLDAPResult(const KLDAP::LdapClient &, const KLDAP::LdapObject &); + void slotLDAPError(const QString &); + void slotLDAPDone(); + +private: + bool check(const QString &, QString, QString); + + KLDAP::LdapClient *mClient = nullptr; +}; + +#endif diff --git a/3rdparty/qthttpserver/.gitignore b/3rdparty/qthttpserver/.gitignore new file mode 100644 index 0000000..89c0081 --- /dev/null +++ b/3rdparty/qthttpserver/.gitignore @@ -0,0 +1,154 @@ +# This file is used to ignore files which are generated in the Qt build system +# ---------------------------------------------------------------------------- + +# Specific files/paths + +# qmake/configure stuff +/.qmake.cache +/.qmake.stash + +/mkspecs/modules/qt_*.pri +/mkspecs/modules-inst/ + +/include/ + +/lib/* +!/lib/README + +/doc/*.qch +/doc/activeqt +/doc/qdoc +/doc/qmake +/doc/qt*/* + +/translations/*.qm +/translations/*_en.ts +/translations/*_untranslated.ts + +# Unit tests libs/plugins/data +/tests/auto/cmake/build/ + +QObject.log +tst_* +!tst_*.* +tst_*.log +tst_*.debug +tst_*~ + +# Generic directories +.metadata/ +.pc/ +debug/ +release/ +tmp/ +tmp-debug/ +tmp-debug-shared/ +tmp-release/ +tmp-release-shared/ +qtc-qmldump/ +qtc-qmldbg/ +*.app/ +*.d/ + +# Generic files +.#* +.com.apple.timemachine.supported +.DS_Store +callgrind.out.* +core +Makefile* +!/qmake/Makefile.win32* +!/qmake/Makefile.unix +object_script.* +pcviewer.cfg +tags +*~ +*.a +*.la +*.core +*.dll +*.exe +*.dylib +*.gcov +*.gcda +*.gcno +*.lib +!Info.plist.lib +*.o +*.obj +*.orig +*.swp +*.rej +*.so +*.so.* +*.pbxuser +*.mode1 +*.mode1v3 +*_resource.rc +*.*# +*.debug + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.ncb +*.vcxproj +*.vcxproj.filters +*.vcxproj.user +*.exe.embed.manifest +*.exe_manifest.rc +*.exe_manifest.res +*.appxmanifest +.qmake.winrt_uuid_* + +# INTEGRITY generated files +*.ael +*.dla +*.dnm +*.dep +*.map + +# Precompiled headers +*.gch +*.pchi +*.pchi.cpp +*_pch.obj +*_pch.pch + +# Qt-specific files +codeattributions.qdoc +moc_*.cpp +qrc_*.cpp +ui_*.h +*.moc +*.prl +*.pro.user* +*.qmlproject.user* +*.rcc + +# Generated by qt_module.prf +*.version +*.version.in + +# Generated by qtPrepareTool() +wrapper.sh +wrapper.bat +*_wrapper.sh +*_wrapper.bat + +# Generated by dbusxml2cpp +*_interface.* +*_adaptor.* + +# Generated by qt.prf +*_plugin_import.cpp diff --git a/3rdparty/qthttpserver/.gitmodules b/3rdparty/qthttpserver/.gitmodules new file mode 100644 index 0000000..850f71d --- /dev/null +++ b/3rdparty/qthttpserver/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/3rdparty/http-parser"] + path = src/3rdparty/http-parser + url = https://github.com/nodejs/http-parser.git diff --git a/3rdparty/qthttpserver/.qmake.conf b/3rdparty/qthttpserver/.qmake.conf new file mode 100644 index 0000000..097d8b9 --- /dev/null +++ b/3rdparty/qthttpserver/.qmake.conf @@ -0,0 +1,3 @@ +load(qt_build_config) + +MODULE_VERSION = 5.12.0 diff --git a/3rdparty/qthttpserver/CMakeLists.txt b/3rdparty/qthttpserver/CMakeLists.txt new file mode 100644 index 0000000..e6e9931 --- /dev/null +++ b/3rdparty/qthttpserver/CMakeLists.txt @@ -0,0 +1,15 @@ +# Generated from qthttpserver.pro. + +cmake_minimum_required(VERSION 3.15.0) + +project(QtHttpServer + VERSION 6.0.0 + DESCRIPTION "Qt HTTP Server" + HOMEPAGE_URL "https://qt.io/" + LANGUAGES CXX C +) + +find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core Network) +find_package(Qt6 ${PROJECT_VERSION} CONFIG OPTIONAL_COMPONENTS Concurrent) + +qt_build_repo() diff --git a/3rdparty/qthttpserver/LICENSE.GPL3 b/3rdparty/qthttpserver/LICENSE.GPL3 new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/3rdparty/qthttpserver/LICENSE.GPL3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/3rdparty/qthttpserver/coin/product_dependencies.yaml b/3rdparty/qthttpserver/coin/product_dependencies.yaml new file mode 100644 index 0000000..6c93ee3 --- /dev/null +++ b/3rdparty/qthttpserver/coin/product_dependencies.yaml @@ -0,0 +1,3 @@ +dependencies: + ../../qt/qt5.git: + ref: "5.12" diff --git a/3rdparty/qthttpserver/dependencies.yaml b/3rdparty/qthttpserver/dependencies.yaml new file mode 100644 index 0000000..6251304 --- /dev/null +++ b/3rdparty/qthttpserver/dependencies.yaml @@ -0,0 +1,4 @@ +dependencies: + ../../qt/qtbase: + ref: "7d7afe1d2b1d85ea5221d1d2883c8bf80aa0e927" + required: true diff --git a/3rdparty/qthttpserver/examples/CMakeLists.txt b/3rdparty/qthttpserver/examples/CMakeLists.txt new file mode 100644 index 0000000..247cb4e --- /dev/null +++ b/3rdparty/qthttpserver/examples/CMakeLists.txt @@ -0,0 +1,7 @@ +# Generated from examples.pro. + +qt_examples_build_begin() + +add_subdirectory(httpserver) + +qt_examples_build_end() diff --git a/3rdparty/qthttpserver/examples/examples.pro b/3rdparty/qthttpserver/examples/examples.pro new file mode 100644 index 0000000..68d9ed3 --- /dev/null +++ b/3rdparty/qthttpserver/examples/examples.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +SUBDIRS = \ + httpserver diff --git a/3rdparty/qthttpserver/examples/httpserver/CMakeLists.txt b/3rdparty/qthttpserver/examples/httpserver/CMakeLists.txt new file mode 100644 index 0000000..5598230 --- /dev/null +++ b/3rdparty/qthttpserver/examples/httpserver/CMakeLists.txt @@ -0,0 +1,4 @@ +# Generated from httpserver.pro. + +add_subdirectory(afterrequest) +add_subdirectory(simple) diff --git a/3rdparty/qthttpserver/examples/httpserver/afterrequest/CMakeLists.txt b/3rdparty/qthttpserver/examples/httpserver/afterrequest/CMakeLists.txt new file mode 100644 index 0000000..4f38035 --- /dev/null +++ b/3rdparty/qthttpserver/examples/httpserver/afterrequest/CMakeLists.txt @@ -0,0 +1,27 @@ +# Generated from afterrequest.pro. + +cmake_minimum_required(VERSION 3.14) +project(afterrequest LANGUAGES CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +set(INSTALL_EXAMPLEDIR "examples/httpserver/afterrequest") + +find_package(Qt6 COMPONENTS HttpServer) + +add_executable(afterrequest + main.cpp +) +target_link_libraries(afterrequest PUBLIC + Qt::HttpServer +) + +install(TARGETS afterrequest + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/3rdparty/qthttpserver/examples/httpserver/afterrequest/afterrequest.pro b/3rdparty/qthttpserver/examples/httpserver/afterrequest/afterrequest.pro new file mode 100644 index 0000000..0602f10 --- /dev/null +++ b/3rdparty/qthttpserver/examples/httpserver/afterrequest/afterrequest.pro @@ -0,0 +1,13 @@ +requires(qtHaveModule(httpserver)) + +TEMPLATE = app + +QT = httpserver + +SOURCES += \ + main.cpp + +target.path = $$[QT_INSTALL_EXAMPLES]/httpserver/afterrequest +INSTALLS += target + +CONFIG += cmdline diff --git a/3rdparty/qthttpserver/examples/httpserver/afterrequest/main.cpp b/3rdparty/qthttpserver/examples/httpserver/afterrequest/main.cpp new file mode 100644 index 0000000..c31f0a1 --- /dev/null +++ b/3rdparty/qthttpserver/examples/httpserver/afterrequest/main.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Mikhail Svetkin +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + QHttpServer httpServer; + httpServer.route("/", []() { + return "Hello world"; + }); + + httpServer.afterRequest([](QHttpServerResponse &&resp) { + resp.setHeader("Server", "Super server!"); + return std::move(resp); + }); + + const auto port = httpServer.listen(QHostAddress::Any); + if (!port) { + qDebug() << QCoreApplication::translate( + "QHttpServerExample", "Server failed to listen on a port."); + return 0; + } + + qDebug() << QCoreApplication::translate( + "QHttpServerExample", "Running on http://127.0.0.1:%1/ (Press CTRL+C to quit)").arg(port); + + return app.exec(); +} diff --git a/3rdparty/qthttpserver/examples/httpserver/httpserver.pro b/3rdparty/qthttpserver/examples/httpserver/httpserver.pro new file mode 100644 index 0000000..7bd69d0 --- /dev/null +++ b/3rdparty/qthttpserver/examples/httpserver/httpserver.pro @@ -0,0 +1,6 @@ +TEMPLATE = subdirs + +SUBDIRS = \ + afterrequest \ + simple + diff --git a/3rdparty/qthttpserver/examples/httpserver/simple/CMakeLists.txt b/3rdparty/qthttpserver/examples/httpserver/simple/CMakeLists.txt new file mode 100644 index 0000000..07d21a7 --- /dev/null +++ b/3rdparty/qthttpserver/examples/httpserver/simple/CMakeLists.txt @@ -0,0 +1,40 @@ +# Generated from simple.pro. + +cmake_minimum_required(VERSION 3.14) +project(simple LANGUAGES CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +set(INSTALL_EXAMPLEDIR "examples/httpserver/simple") + +find_package(Qt6 COMPONENTS HttpServer) + +add_executable(simple + main.cpp +) +target_link_libraries(simple PUBLIC + Qt::HttpServer +) + + +# Resources: +set(assets_resource_files + "assets/qt-logo.png" +) + +qt6_add_resources(simple "assets" + PREFIX + "/" + FILES + ${assets_resource_files} +) + +install(TARGETS simple + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/3rdparty/qthttpserver/examples/httpserver/simple/assets.qrc b/3rdparty/qthttpserver/examples/httpserver/simple/assets.qrc new file mode 100644 index 0000000..e623543 --- /dev/null +++ b/3rdparty/qthttpserver/examples/httpserver/simple/assets.qrc @@ -0,0 +1,5 @@ + + + assets/qt-logo.png + + diff --git a/3rdparty/qthttpserver/examples/httpserver/simple/assets/qt-logo.png b/3rdparty/qthttpserver/examples/httpserver/simple/assets/qt-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..39a4a26f39658d393cbd26485c1aee5c8d8620ea GIT binary patch literal 1032 zcmV+j1o!)iP)Skq@&(`Z`LXj;^1VbyPC)^ldpbZ6IfXxDaY*m!K%d2QKx zZrOWp*?e%>eR0}-aoT=!+JAJ~fOXq}cH4q^+=YAGhkf0Nf8C0J-i(9Zj)mWlhTo8e z-;sykl8E4viQtoq;FXNwmyO|9VZpv#jZ~uIja~>b9`zx3cTFv+KIF>$|q>y}0bZyY0ce?ZUq9#K7;y z!SBby@5jUM$iwf+#PG?;@Xg8b&B^i3%JI+4@zBol($DhL((~5S^Vih$*wyse*Yw)h z_1oC>+}ics+V$Ss_TJp~-`)1$-uB_(_v7LB;{|Ns9+N*zuB000AY zQchCtpU*a4MmTeDvcsw060>hO0!MT0Era< z@{><9(;fg%q5;ML7SF~+L=vMOKxY&{OA*MmmCGT37f}Eg0ER22I|clSM*zG63h|)s z1vn*AwXYh0n*&}e#&&=ab3iM=Rn_?cJU0g%0ywWalR(xSa01}4>O25e{PTgqCiVfO zROb%hxyFD&fL*F{10ZG$7zXH2oofK=jRE}tdsOEZP&8k{8GwVTa~D`L-^618CspSe z@ZB8H4scO*W`K|8fS3o2t40C1Zw`<-;AcZncL5Cf=fnfng#ktYjt2ETfTaJt2y6}m zqyXkNmdpLWfv>J#%mVwu0PYmPv_H)o`<4ODm3ohWOfsI>*{Yj~2mAw<9ltr2Sq9*3 z=>ue-GU~Ns{Yv#zXJKo()0`_=FZCD8KwiyfHwB#@f6ntE3y*R@OCSmj@$3% zKTTXuhX6!mL$W)a?!FB0c}>*2F;L?Sz+mkFNqInihc>@T2k?Ky34LA*X#NFXdR%Q@ z3y}Ud$6fvVLjV%8emj7lcWm>HZQilX*HxF}IL=>M5GNSE0?ASU0000 +#include + +static inline QString host(const QHttpServerRequest &request) +{ + return request.headers()[QStringLiteral("Host")].toString(); +} + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + QHttpServer httpServer; + httpServer.route("/", []() { + return "Hello world"; + }); + + httpServer.route("/query", [] (const QHttpServerRequest &request) { + return QString("%1/query/").arg(host(request)); + }); + + httpServer.route("/query/", [] (qint32 id, const QHttpServerRequest &request) { + return QString("%1/query/%2").arg(host(request)).arg(id); + }); + + httpServer.route("/query//log", [] (qint32 id, const QHttpServerRequest &request) { + return QString("%1/query/%2/log").arg(host(request)).arg(id); + }); + + httpServer.route("/query//log/", [] (qint32 id, float threshold, + const QHttpServerRequest &request) { + return QString("%1/query/%2/log/%3").arg(host(request)).arg(id).arg(threshold); + }); + + httpServer.route("/user/", [] (const qint32 id) { + return QString("User %1").arg(id); + }); + + httpServer.route("/user//detail", [] (const qint32 id) { + return QString("User %1 detail").arg(id); + }); + + httpServer.route("/user//detail/", [] (const qint32 id, const qint32 year) { + return QString("User %1 detail year - %2").arg(id).arg(year); + }); + + httpServer.route("/json/", [] { + return QJsonObject{ + { + {"key1", "1"}, + {"key2", "2"}, + {"key3", "3"} + } + }; + }); + + httpServer.route("/assets/", [] (const QUrl &url) { + return QHttpServerResponse::fromFile(QStringLiteral(":/assets/%1").arg(url.path())); + }); + + httpServer.route("/remote_address", [](const QHttpServerRequest &request) { + return request.remoteAddress().toString(); + }); + + const auto port = httpServer.listen(QHostAddress::Any); + if (!port) { + qDebug() << QCoreApplication::translate( + "QHttpServerExample", "Server failed to listen on a port."); + return 0; + } + + qDebug() << QCoreApplication::translate( + "QHttpServerExample", "Running on http://127.0.0.1:%1/ (Press CTRL+C to quit)").arg(port); + + return app.exec(); +} diff --git a/3rdparty/qthttpserver/examples/httpserver/simple/simple.pro b/3rdparty/qthttpserver/examples/httpserver/simple/simple.pro new file mode 100644 index 0000000..a914e32 --- /dev/null +++ b/3rdparty/qthttpserver/examples/httpserver/simple/simple.pro @@ -0,0 +1,16 @@ +requires(qtHaveModule(httpserver)) + +TEMPLATE = app + +QT = httpserver + +SOURCES += \ + main.cpp + +target.path = $$[QT_INSTALL_EXAMPLES]/httpserver/simple +INSTALLS += target + +RESOURCES += \ + assets.qrc + +CONFIG += cmdline diff --git a/3rdparty/qthttpserver/qthttpserver.pro b/3rdparty/qthttpserver/qthttpserver.pro new file mode 100644 index 0000000..58c33f2 --- /dev/null +++ b/3rdparty/qthttpserver/qthttpserver.pro @@ -0,0 +1 @@ +load(qt_parts) diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser.pri b/3rdparty/qthttpserver/src/3rdparty/http-parser.pri new file mode 100644 index 0000000..51cd2b8 --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser.pri @@ -0,0 +1,6 @@ +NODEJS_HTTP_PARSER_PATH = $$PWD/http-parser + +INCLUDEPATH += $$NODEJS_HTTP_PARSER_PATH + +HEADERS += $$NODEJS_HTTP_PARSER_PATH/http_parser.h +SOURCES += $$NODEJS_HTTP_PARSER_PATH/http_parser.c diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser/.gitignore b/3rdparty/qthttpserver/src/3rdparty/http-parser/.gitignore new file mode 100644 index 0000000..c122e76 --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser/.gitignore @@ -0,0 +1,30 @@ +/out/ +core +tags +*.o +test +test_g +test_fast +bench +url_parser +parsertrace +parsertrace_g +*.mk +*.Makefile +*.so.* +*.exe.* +*.exe +*.a + + +# Visual Studio uglies +*.suo +*.sln +*.vcxproj +*.vcxproj.filters +*.vcxproj.user +*.opensdf +*.ncrunchsolution* +*.sdf +*.vsp +*.psess diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser/.mailmap b/3rdparty/qthttpserver/src/3rdparty/http-parser/.mailmap new file mode 100644 index 0000000..278d141 --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser/.mailmap @@ -0,0 +1,8 @@ +# update AUTHORS with: +# git log --all --reverse --format='%aN <%aE>' | perl -ne 'BEGIN{print "# Authors ordered by first contribution.\n"} print unless $h{$_}; $h{$_} = 1' > AUTHORS +Ryan Dahl +Salman Haq +Simon Zimmermann +Thomas LE ROUX LE ROUX Thomas +Thomas LE ROUX Thomas LE ROUX +Fedor Indutny diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser/.travis.yml b/3rdparty/qthttpserver/src/3rdparty/http-parser/.travis.yml new file mode 100644 index 0000000..4b038e6 --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser/.travis.yml @@ -0,0 +1,13 @@ +language: c + +compiler: + - clang + - gcc + +script: + - "make" + +notifications: + email: false + irc: + - "irc.freenode.net#node-ci" diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser/AUTHORS b/3rdparty/qthttpserver/src/3rdparty/http-parser/AUTHORS new file mode 100644 index 0000000..5323b68 --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser/AUTHORS @@ -0,0 +1,68 @@ +# Authors ordered by first contribution. +Ryan Dahl +Jeremy Hinegardner +Sergey Shepelev +Joe Damato +tomika +Phoenix Sol +Cliff Frey +Ewen Cheslack-Postava +Santiago Gala +Tim Becker +Jeff Terrace +Ben Noordhuis +Nathan Rajlich +Mark Nottingham +Aman Gupta +Tim Becker +Sean Cunningham +Peter Griess +Salman Haq +Cliff Frey +Jon Kolb +Fouad Mardini +Paul Querna +Felix Geisendörfer +koichik +Andre Caron +Ivo Raisr +James McLaughlin +David Gwynne +Thomas LE ROUX +Randy Rizun +Andre Louis Caron +Simon Zimmermann +Erik Dubbelboer +Martell Malone +Bertrand Paquet +BogDan Vatra +Peter Faiman +Corey Richardson +Tóth Tamás +Cam Swords +Chris Dickinson +Uli Köhler +Charlie Somerville +Patrik Stutz +Fedor Indutny +runner +Alexis Campailla +David Wragg +Vinnie Falco +Alex Butum +Rex Feng +Alex Kocharin +Mark Koopman +Helge Heß +Alexis La Goutte +George Miroshnykov +Maciej Małecki +Marc O'Morain +Jeff Pinner +Timothy J Fontaine +Akagi201 +Romain Giraud +Jay Satiro +Arne Steen +Kjell Schubert +Olivier Mengué diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser/LICENSE-MIT b/3rdparty/qthttpserver/src/3rdparty/http-parser/LICENSE-MIT new file mode 100644 index 0000000..1ec0ab4 --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright Joyent, Inc. and other Node contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser/Makefile b/3rdparty/qthttpserver/src/3rdparty/http-parser/Makefile new file mode 100644 index 0000000..d3f11b2 --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser/Makefile @@ -0,0 +1,160 @@ +# Copyright Joyent, Inc. and other Node contributors. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +PLATFORM ?= $(shell sh -c 'uname -s | tr "[A-Z]" "[a-z]"') +HELPER ?= +BINEXT ?= +SOLIBNAME = libhttp_parser +SOMAJOR = 2 +SOMINOR = 9 +SOREV = 0 +ifeq (darwin,$(PLATFORM)) +SOEXT ?= dylib +SONAME ?= $(SOLIBNAME).$(SOMAJOR).$(SOMINOR).$(SOEXT) +LIBNAME ?= $(SOLIBNAME).$(SOMAJOR).$(SOMINOR).$(SOREV).$(SOEXT) +else ifeq (wine,$(PLATFORM)) +CC = winegcc +BINEXT = .exe.so +HELPER = wine +else +SOEXT ?= so +SONAME ?= $(SOLIBNAME).$(SOEXT).$(SOMAJOR).$(SOMINOR) +LIBNAME ?= $(SOLIBNAME).$(SOEXT).$(SOMAJOR).$(SOMINOR).$(SOREV) +endif + +CC?=gcc +AR?=ar + +CPPFLAGS ?= +LDFLAGS ?= + +CPPFLAGS += -I. +CPPFLAGS_DEBUG = $(CPPFLAGS) -DHTTP_PARSER_STRICT=1 +CPPFLAGS_DEBUG += $(CPPFLAGS_DEBUG_EXTRA) +CPPFLAGS_FAST = $(CPPFLAGS) -DHTTP_PARSER_STRICT=0 +CPPFLAGS_FAST += $(CPPFLAGS_FAST_EXTRA) +CPPFLAGS_BENCH = $(CPPFLAGS_FAST) + +CFLAGS += -Wall -Wextra -Werror +CFLAGS_DEBUG = $(CFLAGS) -O0 -g $(CFLAGS_DEBUG_EXTRA) +CFLAGS_FAST = $(CFLAGS) -O3 $(CFLAGS_FAST_EXTRA) +CFLAGS_BENCH = $(CFLAGS_FAST) -Wno-unused-parameter +CFLAGS_LIB = $(CFLAGS_FAST) -fPIC + +LDFLAGS_LIB = $(LDFLAGS) -shared + +INSTALL ?= install +PREFIX ?= /usr/local +LIBDIR = $(PREFIX)/lib +INCLUDEDIR = $(PREFIX)/include + +ifeq (darwin,$(PLATFORM)) +LDFLAGS_LIB += -Wl,-install_name,$(LIBDIR)/$(SONAME) +else +# TODO(bnoordhuis) The native SunOS linker expects -h rather than -soname... +LDFLAGS_LIB += -Wl,-soname=$(SONAME) +endif + +test: test_g test_fast + $(HELPER) ./test_g$(BINEXT) + $(HELPER) ./test_fast$(BINEXT) + +test_g: http_parser_g.o test_g.o + $(CC) $(CFLAGS_DEBUG) $(LDFLAGS) http_parser_g.o test_g.o -o $@ + +test_g.o: test.c http_parser.h Makefile + $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) -c test.c -o $@ + +http_parser_g.o: http_parser.c http_parser.h Makefile + $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) -c http_parser.c -o $@ + +test_fast: http_parser.o test.o http_parser.h + $(CC) $(CFLAGS_FAST) $(LDFLAGS) http_parser.o test.o -o $@ + +test.o: test.c http_parser.h Makefile + $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c test.c -o $@ + +bench: http_parser.o bench.o + $(CC) $(CFLAGS_BENCH) $(LDFLAGS) http_parser.o bench.o -o $@ + +bench.o: bench.c http_parser.h Makefile + $(CC) $(CPPFLAGS_BENCH) $(CFLAGS_BENCH) -c bench.c -o $@ + +http_parser.o: http_parser.c http_parser.h Makefile + $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c http_parser.c + +test-run-timed: test_fast + while(true) do time $(HELPER) ./test_fast$(BINEXT) > /dev/null; done + +test-valgrind: test_g + valgrind ./test_g + +libhttp_parser.o: http_parser.c http_parser.h Makefile + $(CC) $(CPPFLAGS_FAST) $(CFLAGS_LIB) -c http_parser.c -o libhttp_parser.o + +library: libhttp_parser.o + $(CC) $(LDFLAGS_LIB) -o $(LIBNAME) $< + +package: http_parser.o + $(AR) rcs libhttp_parser.a http_parser.o + +url_parser: http_parser.o contrib/url_parser.c + $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) $^ -o $@ + +url_parser_g: http_parser_g.o contrib/url_parser.c + $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) $^ -o $@ + +parsertrace: http_parser.o contrib/parsertrace.c + $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) $^ -o parsertrace$(BINEXT) + +parsertrace_g: http_parser_g.o contrib/parsertrace.c + $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) $^ -o parsertrace_g$(BINEXT) + +tags: http_parser.c http_parser.h test.c + ctags $^ + +install: library + $(INSTALL) -D http_parser.h $(DESTDIR)$(INCLUDEDIR)/http_parser.h + $(INSTALL) -D $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(LIBNAME) + ln -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SONAME) + ln -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SOLIBNAME).$(SOEXT) + +install-strip: library + $(INSTALL) -D http_parser.h $(DESTDIR)$(INCLUDEDIR)/http_parser.h + $(INSTALL) -D -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(LIBNAME) + ln -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SONAME) + ln -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SOLIBNAME).$(SOEXT) + +uninstall: + rm $(DESTDIR)$(INCLUDEDIR)/http_parser.h + rm $(DESTDIR)$(LIBDIR)/$(SOLIBNAME).$(SOEXT) + rm $(DESTDIR)$(LIBDIR)/$(SONAME) + rm $(DESTDIR)$(LIBDIR)/$(LIBNAME) + +clean: + rm -f *.o *.a tags test test_fast test_g \ + http_parser.tar libhttp_parser.so.* \ + url_parser url_parser_g parsertrace parsertrace_g \ + *.exe *.exe.so + +contrib/url_parser.c: http_parser.h +contrib/parsertrace.c: http_parser.h + +.PHONY: clean package test-run test-run-timed test-valgrind install install-strip uninstall diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser/README.md b/3rdparty/qthttpserver/src/3rdparty/http-parser/README.md new file mode 100644 index 0000000..b265d71 --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser/README.md @@ -0,0 +1,246 @@ +HTTP Parser +=========== + +[![Build Status](https://api.travis-ci.org/nodejs/http-parser.svg?branch=master)](https://travis-ci.org/nodejs/http-parser) + +This is a parser for HTTP messages written in C. It parses both requests and +responses. The parser is designed to be used in performance HTTP +applications. It does not make any syscalls nor allocations, it does not +buffer data, it can be interrupted at anytime. Depending on your +architecture, it only requires about 40 bytes of data per message +stream (in a web server that is per connection). + +Features: + + * No dependencies + * Handles persistent streams (keep-alive). + * Decodes chunked encoding. + * Upgrade support + * Defends against buffer overflow attacks. + +The parser extracts the following information from HTTP messages: + + * Header fields and values + * Content-Length + * Request method + * Response status code + * Transfer-Encoding + * HTTP version + * Request URL + * Message body + + +Usage +----- + +One `http_parser` object is used per TCP connection. Initialize the struct +using `http_parser_init()` and set the callbacks. That might look something +like this for a request parser: +```c +http_parser_settings settings; +settings.on_url = my_url_callback; +settings.on_header_field = my_header_field_callback; +/* ... */ + +http_parser *parser = malloc(sizeof(http_parser)); +http_parser_init(parser, HTTP_REQUEST); +parser->data = my_socket; +``` + +When data is received on the socket execute the parser and check for errors. + +```c +size_t len = 80*1024, nparsed; +char buf[len]; +ssize_t recved; + +recved = recv(fd, buf, len, 0); + +if (recved < 0) { + /* Handle error. */ +} + +/* Start up / continue the parser. + * Note we pass recved==0 to signal that EOF has been received. + */ +nparsed = http_parser_execute(parser, &settings, buf, recved); + +if (parser->upgrade) { + /* handle new protocol */ +} else if (nparsed != recved) { + /* Handle error. Usually just close the connection. */ +} +``` + +`http_parser` needs to know where the end of the stream is. For example, sometimes +servers send responses without Content-Length and expect the client to +consume input (for the body) until EOF. To tell `http_parser` about EOF, give +`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors +can still be encountered during an EOF, so one must still be prepared +to receive them. + +Scalar valued message information such as `status_code`, `method`, and the +HTTP version are stored in the parser structure. This data is only +temporally stored in `http_parser` and gets reset on each new message. If +this information is needed later, copy it out of the structure during the +`headers_complete` callback. + +The parser decodes the transfer-encoding for both requests and responses +transparently. That is, a chunked encoding is decoded before being sent to +the on_body callback. + + +The Special Problem of Upgrade +------------------------------ + +`http_parser` supports upgrading the connection to a different protocol. An +increasingly common example of this is the WebSocket protocol which sends +a request like + + GET /demo HTTP/1.1 + Upgrade: WebSocket + Connection: Upgrade + Host: example.com + Origin: http://example.com + WebSocket-Protocol: sample + +followed by non-HTTP data. + +(See [RFC6455](https://tools.ietf.org/html/rfc6455) for more information the +WebSocket protocol.) + +To support this, the parser will treat this as a normal HTTP message without a +body, issuing both on_headers_complete and on_message_complete callbacks. However +http_parser_execute() will stop parsing at the end of the headers and return. + +The user is expected to check if `parser->upgrade` has been set to 1 after +`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied +offset by the return value of `http_parser_execute()`. + + +Callbacks +--------- + +During the `http_parser_execute()` call, the callbacks set in +`http_parser_settings` will be executed. The parser maintains state and +never looks behind, so buffering the data is not necessary. If you need to +save certain data for later usage, you can do that from the callbacks. + +There are two types of callbacks: + +* notification `typedef int (*http_cb) (http_parser*);` + Callbacks: on_message_begin, on_headers_complete, on_message_complete. +* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);` + Callbacks: (requests only) on_url, + (common) on_header_field, on_header_value, on_body; + +Callbacks must return 0 on success. Returning a non-zero value indicates +error to the parser, making it exit immediately. + +For cases where it is necessary to pass local information to/from a callback, +the `http_parser` object's `data` field can be used. +An example of such a case is when using threads to handle a socket connection, +parse a request, and then give a response over that socket. By instantiation +of a thread-local struct containing relevant data (e.g. accepted socket, +allocated memory for callbacks to write into, etc), a parser's callbacks are +able to communicate data between the scope of the thread and the scope of the +callback in a threadsafe manner. This allows `http_parser` to be used in +multi-threaded contexts. + +Example: +```c + typedef struct { + socket_t sock; + void* buffer; + int buf_len; + } custom_data_t; + + +int my_url_callback(http_parser* parser, const char *at, size_t length) { + /* access to thread local custom_data_t struct. + Use this access save parsed data for later use into thread local + buffer, or communicate over socket + */ + parser->data; + ... + return 0; +} + +... + +void http_parser_thread(socket_t sock) { + int nparsed = 0; + /* allocate memory for user data */ + custom_data_t *my_data = malloc(sizeof(custom_data_t)); + + /* some information for use by callbacks. + * achieves thread -> callback information flow */ + my_data->sock = sock; + + /* instantiate a thread-local parser */ + http_parser *parser = malloc(sizeof(http_parser)); + http_parser_init(parser, HTTP_REQUEST); /* initialise parser */ + /* this custom data reference is accessible through the reference to the + parser supplied to callback functions */ + parser->data = my_data; + + http_parser_settings settings; /* set up callbacks */ + settings.on_url = my_url_callback; + + /* execute parser */ + nparsed = http_parser_execute(parser, &settings, buf, recved); + + ... + /* parsed information copied from callback. + can now perform action on data copied into thread-local memory from callbacks. + achieves callback -> thread information flow */ + my_data->buffer; + ... +} + +``` + +In case you parse HTTP message in chunks (i.e. `read()` request line +from socket, parse, read half headers, parse, etc) your data callbacks +may be called more than once. `http_parser` guarantees that data pointer is only +valid for the lifetime of callback. You can also `read()` into a heap allocated +buffer to avoid copying memory around if this fits your application. + +Reading headers may be a tricky task if you read/parse headers partially. +Basically, you need to remember whether last header callback was field or value +and apply the following logic: + + (on_header_field and on_header_value shortened to on_h_*) + ------------------------ ------------ -------------------------------------------- + | State (prev. callback) | Callback | Description/action | + ------------------------ ------------ -------------------------------------------- + | nothing (first call) | on_h_field | Allocate new buffer and copy callback data | + | | | into it | + ------------------------ ------------ -------------------------------------------- + | value | on_h_field | New header started. | + | | | Copy current name,value buffers to headers | + | | | list and allocate new buffer for new name | + ------------------------ ------------ -------------------------------------------- + | field | on_h_field | Previous name continues. Reallocate name | + | | | buffer and append callback data to it | + ------------------------ ------------ -------------------------------------------- + | field | on_h_value | Value for current header started. Allocate | + | | | new buffer and copy callback data to it | + ------------------------ ------------ -------------------------------------------- + | value | on_h_value | Value continues. Reallocate value buffer | + | | | and append callback data to it | + ------------------------ ------------ -------------------------------------------- + + +Parsing URLs +------------ + +A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`. +Users of this library may wish to use it to parse URLs constructed from +consecutive `on_url` callbacks. + +See examples of reading in headers: + +* [partial example](http://gist.github.com/155877) in C +* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C +* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser/bench.c b/3rdparty/qthttpserver/src/3rdparty/http-parser/bench.c new file mode 100644 index 0000000..678f555 --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser/bench.c @@ -0,0 +1,128 @@ +/* Copyright Fedor Indutny. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "http_parser.h" +#include +#include +#include +#include +#include + +/* 8 gb */ +static const int64_t kBytes = 8LL << 30; + +static const char data[] = + "POST /joyent/http-parser HTTP/1.1\r\n" + "Host: github.com\r\n" + "DNT: 1\r\n" + "Accept-Encoding: gzip, deflate, sdch\r\n" + "Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\r\n" + "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/39.0.2171.65 Safari/537.36\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9," + "image/webp,*/*;q=0.8\r\n" + "Referer: https://github.com/joyent/http-parser\r\n" + "Connection: keep-alive\r\n" + "Transfer-Encoding: chunked\r\n" + "Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n"; +static const size_t data_len = sizeof(data) - 1; + +static int on_info(http_parser* p) { + return 0; +} + + +static int on_data(http_parser* p, const char *at, size_t length) { + return 0; +} + +static http_parser_settings settings = { + .on_message_begin = on_info, + .on_headers_complete = on_info, + .on_message_complete = on_info, + .on_header_field = on_data, + .on_header_value = on_data, + .on_url = on_data, + .on_status = on_data, + .on_body = on_data +}; + +int bench(int iter_count, int silent) { + struct http_parser parser; + int i; + int err; + struct timeval start; + struct timeval end; + + if (!silent) { + err = gettimeofday(&start, NULL); + assert(err == 0); + } + + fprintf(stderr, "req_len=%d\n", (int) data_len); + for (i = 0; i < iter_count; i++) { + size_t parsed; + http_parser_init(&parser, HTTP_REQUEST); + + parsed = http_parser_execute(&parser, &settings, data, data_len); + assert(parsed == data_len); + } + + if (!silent) { + double elapsed; + double bw; + double total; + + err = gettimeofday(&end, NULL); + assert(err == 0); + + fprintf(stdout, "Benchmark result:\n"); + + elapsed = (double) (end.tv_sec - start.tv_sec) + + (end.tv_usec - start.tv_usec) * 1e-6f; + + total = (double) iter_count * data_len; + bw = (double) total / elapsed; + + fprintf(stdout, "%.2f mb | %.2f mb/s | %.2f req/sec | %.2f s\n", + (double) total / (1024 * 1024), + bw / (1024 * 1024), + (double) iter_count / elapsed, + elapsed); + + fflush(stdout); + } + + return 0; +} + +int main(int argc, char** argv) { + int64_t iterations; + + iterations = kBytes / (int64_t) data_len; + if (argc == 2 && strcmp(argv[1], "infinite") == 0) { + for (;;) + bench(iterations, 1); + return 0; + } else { + return bench(iterations, 0); + } +} diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser/contrib/parsertrace.c b/3rdparty/qthttpserver/src/3rdparty/http-parser/contrib/parsertrace.c new file mode 100644 index 0000000..3daa7f4 --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser/contrib/parsertrace.c @@ -0,0 +1,157 @@ +/* Copyright Joyent, Inc. and other Node contributors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* Dump what the parser finds to stdout as it happen */ + +#include "http_parser.h" +#include +#include +#include + +int on_message_begin(http_parser* _) { + (void)_; + printf("\n***MESSAGE BEGIN***\n\n"); + return 0; +} + +int on_headers_complete(http_parser* _) { + (void)_; + printf("\n***HEADERS COMPLETE***\n\n"); + return 0; +} + +int on_message_complete(http_parser* _) { + (void)_; + printf("\n***MESSAGE COMPLETE***\n\n"); + return 0; +} + +int on_url(http_parser* _, const char* at, size_t length) { + (void)_; + printf("Url: %.*s\n", (int)length, at); + return 0; +} + +int on_header_field(http_parser* _, const char* at, size_t length) { + (void)_; + printf("Header field: %.*s\n", (int)length, at); + return 0; +} + +int on_header_value(http_parser* _, const char* at, size_t length) { + (void)_; + printf("Header value: %.*s\n", (int)length, at); + return 0; +} + +int on_body(http_parser* _, const char* at, size_t length) { + (void)_; + printf("Body: %.*s\n", (int)length, at); + return 0; +} + +void usage(const char* name) { + fprintf(stderr, + "Usage: %s $type $filename\n" + " type: -x, where x is one of {r,b,q}\n" + " parses file as a Response, reQuest, or Both\n", + name); + exit(EXIT_FAILURE); +} + +int main(int argc, char* argv[]) { + enum http_parser_type file_type; + + if (argc != 3) { + usage(argv[0]); + } + + char* type = argv[1]; + if (type[0] != '-') { + usage(argv[0]); + } + + switch (type[1]) { + /* in the case of "-", type[1] will be NUL */ + case 'r': + file_type = HTTP_RESPONSE; + break; + case 'q': + file_type = HTTP_REQUEST; + break; + case 'b': + file_type = HTTP_BOTH; + break; + default: + usage(argv[0]); + } + + char* filename = argv[2]; + FILE* file = fopen(filename, "r"); + if (file == NULL) { + perror("fopen"); + goto fail; + } + + fseek(file, 0, SEEK_END); + long file_length = ftell(file); + if (file_length == -1) { + perror("ftell"); + goto fail; + } + fseek(file, 0, SEEK_SET); + + char* data = malloc(file_length); + if (fread(data, 1, file_length, file) != (size_t)file_length) { + fprintf(stderr, "couldn't read entire file\n"); + free(data); + goto fail; + } + + http_parser_settings settings; + memset(&settings, 0, sizeof(settings)); + settings.on_message_begin = on_message_begin; + settings.on_url = on_url; + settings.on_header_field = on_header_field; + settings.on_header_value = on_header_value; + settings.on_headers_complete = on_headers_complete; + settings.on_body = on_body; + settings.on_message_complete = on_message_complete; + + http_parser parser; + http_parser_init(&parser, file_type); + size_t nparsed = http_parser_execute(&parser, &settings, data, file_length); + free(data); + + if (nparsed != (size_t)file_length) { + fprintf(stderr, + "Error: %s (%s)\n", + http_errno_description(HTTP_PARSER_ERRNO(&parser)), + http_errno_name(HTTP_PARSER_ERRNO(&parser))); + goto fail; + } + + return EXIT_SUCCESS; + +fail: + fclose(file); + return EXIT_FAILURE; +} diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser/contrib/url_parser.c b/3rdparty/qthttpserver/src/3rdparty/http-parser/contrib/url_parser.c new file mode 100644 index 0000000..f235bed --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser/contrib/url_parser.c @@ -0,0 +1,47 @@ +#include "http_parser.h" +#include +#include + +void +dump_url (const char *url, const struct http_parser_url *u) +{ + unsigned int i; + + printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port); + for (i = 0; i < UF_MAX; i++) { + if ((u->field_set & (1 << i)) == 0) { + printf("\tfield_data[%u]: unset\n", i); + continue; + } + + printf("\tfield_data[%u]: off: %u, len: %u, part: %.*s\n", + i, + u->field_data[i].off, + u->field_data[i].len, + u->field_data[i].len, + url + u->field_data[i].off); + } +} + +int main(int argc, char ** argv) { + struct http_parser_url u; + int len, connect, result; + + if (argc != 3) { + printf("Syntax : %s connect|get url\n", argv[0]); + return 1; + } + len = strlen(argv[2]); + connect = strcmp("connect", argv[1]) == 0 ? 1 : 0; + printf("Parsing %s, connect %d\n", argv[2], connect); + + http_parser_url_init(&u); + result = http_parser_parse_url(argv[2], len, connect, &u); + if (result != 0) { + printf("Parse error : %d\n", result); + return result; + } + printf("Parse ok, result : \n"); + dump_url(argv[2], &u); + return 0; +} diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser/http_parser.c b/3rdparty/qthttpserver/src/3rdparty/http-parser/http_parser.c new file mode 100644 index 0000000..e2fc5d2 --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser/http_parser.c @@ -0,0 +1,2501 @@ +/* Copyright Joyent, Inc. and other Node contributors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "http_parser.h" +#include +#include +#include +#include +#include + +static uint32_t max_header_size = HTTP_MAX_HEADER_SIZE; + +#ifndef ULLONG_MAX +# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ +#endif + +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +#ifndef BIT_AT +# define BIT_AT(a, i) \ + (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ + (1 << ((unsigned int) (i) & 7)))) +#endif + +#ifndef ELEM_AT +# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) +#endif + +#define SET_ERRNO(e) \ +do { \ + parser->nread = nread; \ + parser->http_errno = (e); \ +} while(0) + +#define CURRENT_STATE() p_state +#define UPDATE_STATE(V) p_state = (enum state) (V); +#define RETURN(V) \ +do { \ + parser->nread = nread; \ + parser->state = CURRENT_STATE(); \ + return (V); \ +} while (0); +#define REEXECUTE() \ + goto reexecute; \ + + +#ifdef __GNUC__ +# define LIKELY(X) __builtin_expect(!!(X), 1) +# define UNLIKELY(X) __builtin_expect(!!(X), 0) +#else +# define LIKELY(X) (X) +# define UNLIKELY(X) (X) +#endif + + +/* Run the notify callback FOR, returning ER if it fails */ +#define CALLBACK_NOTIFY_(FOR, ER) \ +do { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (LIKELY(settings->on_##FOR)) { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != settings->on_##FOR(parser))) { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + UPDATE_STATE(parser->state); \ + \ + /* We either errored above or got paused; get out */ \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ + return (ER); \ + } \ + } \ +} while (0) + +/* Run the notify callback FOR and consume the current byte */ +#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) + +/* Run the notify callback FOR and don't consume the current byte */ +#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) + +/* Run data callback FOR with LEN bytes, returning ER if it fails */ +#define CALLBACK_DATA_(FOR, LEN, ER) \ +do { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (FOR##_mark) { \ + if (LIKELY(settings->on_##FOR)) { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != \ + settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + UPDATE_STATE(parser->state); \ + \ + /* We either errored above or got paused; get out */ \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ + return (ER); \ + } \ + } \ + FOR##_mark = NULL; \ + } \ +} while (0) + +/* Run the data callback FOR and consume the current byte */ +#define CALLBACK_DATA(FOR) \ + CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) + +/* Run the data callback FOR and don't consume the current byte */ +#define CALLBACK_DATA_NOADVANCE(FOR) \ + CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) + +/* Set the mark FOR; non-destructive if mark is already set */ +#define MARK(FOR) \ +do { \ + if (!FOR##_mark) { \ + FOR##_mark = p; \ + } \ +} while (0) + +/* Don't allow the total size of the HTTP headers (including the status + * line) to exceed max_header_size. This check is here to protect + * embedders against denial-of-service attacks where the attacker feeds + * us a never-ending header that the embedder keeps buffering. + * + * This check is arguably the responsibility of embedders but we're doing + * it on the embedder's behalf because most won't bother and this way we + * make the web a little safer. max_header_size is still far bigger + * than any reasonable request or response so this should never affect + * day-to-day operation. + */ +#define COUNT_HEADER_SIZE(V) \ +do { \ + nread += (uint32_t)(V); \ + if (UNLIKELY(nread > max_header_size)) { \ + SET_ERRNO(HPE_HEADER_OVERFLOW); \ + goto error; \ + } \ +} while (0) + + +#define PROXY_CONNECTION "proxy-connection" +#define CONNECTION "connection" +#define CONTENT_LENGTH "content-length" +#define TRANSFER_ENCODING "transfer-encoding" +#define UPGRADE "upgrade" +#define CHUNKED "chunked" +#define KEEP_ALIVE "keep-alive" +#define CLOSE "close" + + +static const char *method_strings[] = + { +#define XX(num, name, string) #string, + HTTP_METHOD_MAP(XX) +#undef XX + }; + + +/* Tokens as defined by rfc 2616. Also lowercases them. + * token = 1* + * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + */ +static const char tokens[256] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + ' ', '!', 0, '#', '$', '%', '&', '\'', +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 0, 0, '*', '+', 0, '-', '.', 0, +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + '0', '1', '2', '3', '4', '5', '6', '7', +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + '8', '9', 0, 0, 0, 0, 0, 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 'x', 'y', 'z', 0, 0, 0, '^', '_', +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 'x', 'y', 'z', 0, '|', 0, '~', 0 }; + + +static const int8_t unhex[256] = + {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 + ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + }; + + +#if HTTP_PARSER_STRICT +# define T(v) 0 +#else +# define T(v) v +#endif + + +static const uint8_t normal_url_char[32] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; + +#undef T + +enum state + { s_dead = 1 /* important that this is > 0 */ + + , s_start_req_or_res + , s_res_or_resp_H + , s_start_res + , s_res_H + , s_res_HT + , s_res_HTT + , s_res_HTTP + , s_res_http_major + , s_res_http_dot + , s_res_http_minor + , s_res_http_end + , s_res_first_status_code + , s_res_status_code + , s_res_status_start + , s_res_status + , s_res_line_almost_done + + , s_start_req + + , s_req_method + , s_req_spaces_before_url + , s_req_schema + , s_req_schema_slash + , s_req_schema_slash_slash + , s_req_server_start + , s_req_server + , s_req_server_with_at + , s_req_path + , s_req_query_string_start + , s_req_query_string + , s_req_fragment_start + , s_req_fragment + , s_req_http_start + , s_req_http_H + , s_req_http_HT + , s_req_http_HTT + , s_req_http_HTTP + , s_req_http_I + , s_req_http_IC + , s_req_http_major + , s_req_http_dot + , s_req_http_minor + , s_req_http_end + , s_req_line_almost_done + + , s_header_field_start + , s_header_field + , s_header_value_discard_ws + , s_header_value_discard_ws_almost_done + , s_header_value_discard_lws + , s_header_value_start + , s_header_value + , s_header_value_lws + + , s_header_almost_done + + , s_chunk_size_start + , s_chunk_size + , s_chunk_parameters + , s_chunk_size_almost_done + + , s_headers_almost_done + , s_headers_done + + /* Important: 's_headers_done' must be the last 'header' state. All + * states beyond this must be 'body' states. It is used for overflow + * checking. See the PARSING_HEADER() macro. + */ + + , s_chunk_data + , s_chunk_data_almost_done + , s_chunk_data_done + + , s_body_identity + , s_body_identity_eof + + , s_message_done + }; + + +#define PARSING_HEADER(state) (state <= s_headers_done) + + +enum header_states + { h_general = 0 + , h_C + , h_CO + , h_CON + + , h_matching_connection + , h_matching_proxy_connection + , h_matching_content_length + , h_matching_transfer_encoding + , h_matching_upgrade + + , h_connection + , h_content_length + , h_content_length_num + , h_content_length_ws + , h_transfer_encoding + , h_upgrade + + , h_matching_transfer_encoding_chunked + , h_matching_connection_token_start + , h_matching_connection_keep_alive + , h_matching_connection_close + , h_matching_connection_upgrade + , h_matching_connection_token + + , h_transfer_encoding_chunked + , h_connection_keep_alive + , h_connection_close + , h_connection_upgrade + }; + +enum http_host_state + { + s_http_host_dead = 1 + , s_http_userinfo_start + , s_http_userinfo + , s_http_host_start + , s_http_host_v6_start + , s_http_host + , s_http_host_v6 + , s_http_host_v6_end + , s_http_host_v6_zone_start + , s_http_host_v6_zone + , s_http_host_port_start + , s_http_host_port +}; + +/* Macros for character classes; depends on strict-mode */ +#define CR '\r' +#define LF '\n' +#define LOWER(c) (unsigned char)(c | 0x20) +#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') +#define IS_NUM(c) ((c) >= '0' && (c) <= '9') +#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) +#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) +#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ + (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ + (c) == ')') +#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ + (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ + (c) == '$' || (c) == ',') + +#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c]) + +#if HTTP_PARSER_STRICT +#define TOKEN(c) STRICT_TOKEN(c) +#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) +#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') +#else +#define TOKEN(c) tokens[(unsigned char)c] +#define IS_URL_CHAR(c) \ + (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) +#define IS_HOST_CHAR(c) \ + (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') +#endif + +/** + * Verify that a char is a valid visible (printable) US-ASCII + * character or %x80-FF + **/ +#define IS_HEADER_CHAR(ch) \ + (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127)) + +#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) + + +#if HTTP_PARSER_STRICT +# define STRICT_CHECK(cond) \ +do { \ + if (cond) { \ + SET_ERRNO(HPE_STRICT); \ + goto error; \ + } \ +} while (0) +# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) +#else +# define STRICT_CHECK(cond) +# define NEW_MESSAGE() start_state +#endif + + +/* Map errno values to strings for human-readable output */ +#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s }, +static struct { + const char *name; + const char *description; +} http_strerror_tab[] = { + HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) +}; +#undef HTTP_STRERROR_GEN + +int http_message_needs_eof(const http_parser *parser); + +/* Our URL parser. + * + * This is designed to be shared by http_parser_execute() for URL validation, + * hence it has a state transition + byte-for-byte interface. In addition, it + * is meant to be embedded in http_parser_parse_url(), which does the dirty + * work of turning state transitions URL components for its API. + * + * This function should only be invoked with non-space characters. It is + * assumed that the caller cares about (and can detect) the transition between + * URL and non-URL states by looking for these. + */ +static enum state +parse_url_char(enum state s, const char ch) +{ + if (ch == ' ' || ch == '\r' || ch == '\n') { + return s_dead; + } + +#if HTTP_PARSER_STRICT + if (ch == '\t' || ch == '\f') { + return s_dead; + } +#endif + + switch (s) { + case s_req_spaces_before_url: + /* Proxied requests are followed by scheme of an absolute URI (alpha). + * All methods except CONNECT are followed by '/' or '*'. + */ + + if (ch == '/' || ch == '*') { + return s_req_path; + } + + if (IS_ALPHA(ch)) { + return s_req_schema; + } + + break; + + case s_req_schema: + if (IS_ALPHA(ch)) { + return s; + } + + if (ch == ':') { + return s_req_schema_slash; + } + + break; + + case s_req_schema_slash: + if (ch == '/') { + return s_req_schema_slash_slash; + } + + break; + + case s_req_schema_slash_slash: + if (ch == '/') { + return s_req_server_start; + } + + break; + + case s_req_server_with_at: + if (ch == '@') { + return s_dead; + } + + /* fall through */ + case s_req_server_start: + case s_req_server: + if (ch == '/') { + return s_req_path; + } + + if (ch == '?') { + return s_req_query_string_start; + } + + if (ch == '@') { + return s_req_server_with_at; + } + + if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { + return s_req_server; + } + + break; + + case s_req_path: + if (IS_URL_CHAR(ch)) { + return s; + } + + switch (ch) { + case '?': + return s_req_query_string_start; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_query_string_start: + case s_req_query_string: + if (IS_URL_CHAR(ch)) { + return s_req_query_string; + } + + switch (ch) { + case '?': + /* allow extra '?' in query string */ + return s_req_query_string; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_fragment_start: + if (IS_URL_CHAR(ch)) { + return s_req_fragment; + } + + switch (ch) { + case '?': + return s_req_fragment; + + case '#': + return s; + } + + break; + + case s_req_fragment: + if (IS_URL_CHAR(ch)) { + return s; + } + + switch (ch) { + case '?': + case '#': + return s; + } + + break; + + default: + break; + } + + /* We should never fall out of the switch above unless there's an error */ + return s_dead; +} + +size_t http_parser_execute (http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len) +{ + char c, ch; + int8_t unhex_val; + const char *p = data; + const char *header_field_mark = 0; + const char *header_value_mark = 0; + const char *url_mark = 0; + const char *body_mark = 0; + const char *status_mark = 0; + enum state p_state = (enum state) parser->state; + const unsigned int lenient = parser->lenient_http_headers; + uint32_t nread = parser->nread; + + /* We're in an error state. Don't bother doing anything. */ + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + return 0; + } + + if (len == 0) { + switch (CURRENT_STATE()) { + case s_body_identity_eof: + /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if + * we got paused. + */ + CALLBACK_NOTIFY_NOADVANCE(message_complete); + return 0; + + case s_dead: + case s_start_req_or_res: + case s_start_res: + case s_start_req: + return 0; + + default: + SET_ERRNO(HPE_INVALID_EOF_STATE); + return 1; + } + } + + + if (CURRENT_STATE() == s_header_field) + header_field_mark = data; + if (CURRENT_STATE() == s_header_value) + header_value_mark = data; + switch (CURRENT_STATE()) { + case s_req_path: + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_server: + case s_req_server_with_at: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + url_mark = data; + break; + case s_res_status: + status_mark = data; + break; + default: + break; + } + + for (p=data; p != data + len; p++) { + ch = *p; + + if (PARSING_HEADER(CURRENT_STATE())) + COUNT_HEADER_SIZE(1); + +reexecute: + switch (CURRENT_STATE()) { + + case s_dead: + /* this state is used after a 'Connection: close' message + * the parser will error out if it reads another message + */ + if (LIKELY(ch == CR || ch == LF)) + break; + + SET_ERRNO(HPE_CLOSED_CONNECTION); + goto error; + + case s_start_req_or_res: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (ch == 'H') { + UPDATE_STATE(s_res_or_resp_H); + + CALLBACK_NOTIFY(message_begin); + } else { + parser->type = HTTP_REQUEST; + UPDATE_STATE(s_start_req); + REEXECUTE(); + } + + break; + } + + case s_res_or_resp_H: + if (ch == 'T') { + parser->type = HTTP_RESPONSE; + UPDATE_STATE(s_res_HT); + } else { + if (UNLIKELY(ch != 'E')) { + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + parser->type = HTTP_REQUEST; + parser->method = HTTP_HEAD; + parser->index = 2; + UPDATE_STATE(s_req_method); + } + break; + + case s_start_res: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (ch == 'H') { + UPDATE_STATE(s_res_H); + } else { + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + CALLBACK_NOTIFY(message_begin); + break; + } + + case s_res_H: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_res_HT); + break; + + case s_res_HT: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_res_HTT); + break; + + case s_res_HTT: + STRICT_CHECK(ch != 'P'); + UPDATE_STATE(s_res_HTTP); + break; + + case s_res_HTTP: + STRICT_CHECK(ch != '/'); + UPDATE_STATE(s_res_http_major); + break; + + case s_res_http_major: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major = ch - '0'; + UPDATE_STATE(s_res_http_dot); + break; + + case s_res_http_dot: + { + if (UNLIKELY(ch != '.')) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + UPDATE_STATE(s_res_http_minor); + break; + } + + case s_res_http_minor: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor = ch - '0'; + UPDATE_STATE(s_res_http_end); + break; + + case s_res_http_end: + { + if (UNLIKELY(ch != ' ')) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + UPDATE_STATE(s_res_first_status_code); + break; + } + + case s_res_first_status_code: + { + if (!IS_NUM(ch)) { + if (ch == ' ') { + break; + } + + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + parser->status_code = ch - '0'; + UPDATE_STATE(s_res_status_code); + break; + } + + case s_res_status_code: + { + if (!IS_NUM(ch)) { + switch (ch) { + case ' ': + UPDATE_STATE(s_res_status_start); + break; + case CR: + case LF: + UPDATE_STATE(s_res_status_start); + REEXECUTE(); + break; + default: + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + break; + } + + parser->status_code *= 10; + parser->status_code += ch - '0'; + + if (UNLIKELY(parser->status_code > 999)) { + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + + break; + } + + case s_res_status_start: + { + MARK(status); + UPDATE_STATE(s_res_status); + parser->index = 0; + + if (ch == CR || ch == LF) + REEXECUTE(); + + break; + } + + case s_res_status: + if (ch == CR) { + UPDATE_STATE(s_res_line_almost_done); + CALLBACK_DATA(status); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + CALLBACK_DATA(status); + break; + } + + break; + + case s_res_line_almost_done: + STRICT_CHECK(ch != LF); + UPDATE_STATE(s_header_field_start); + break; + + case s_start_req: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (UNLIKELY(!IS_ALPHA(ch))) { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + parser->method = (enum http_method) 0; + parser->index = 1; + switch (ch) { + case 'A': parser->method = HTTP_ACL; break; + case 'B': parser->method = HTTP_BIND; break; + case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; + case 'D': parser->method = HTTP_DELETE; break; + case 'G': parser->method = HTTP_GET; break; + case 'H': parser->method = HTTP_HEAD; break; + case 'L': parser->method = HTTP_LOCK; /* or LINK */ break; + case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; + case 'N': parser->method = HTTP_NOTIFY; break; + case 'O': parser->method = HTTP_OPTIONS; break; + case 'P': parser->method = HTTP_POST; + /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ + break; + case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break; + case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */ break; + case 'T': parser->method = HTTP_TRACE; break; + case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break; + default: + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + UPDATE_STATE(s_req_method); + + CALLBACK_NOTIFY(message_begin); + + break; + } + + case s_req_method: + { + const char *matcher; + if (UNLIKELY(ch == '\0')) { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + matcher = method_strings[parser->method]; + if (ch == ' ' && matcher[parser->index] == '\0') { + UPDATE_STATE(s_req_spaces_before_url); + } else if (ch == matcher[parser->index]) { + ; /* nada */ + } else if ((ch >= 'A' && ch <= 'Z') || ch == '-') { + + switch (parser->method << 16 | parser->index << 8 | ch) { +#define XX(meth, pos, ch, new_meth) \ + case (HTTP_##meth << 16 | pos << 8 | ch): \ + parser->method = HTTP_##new_meth; break; + + XX(POST, 1, 'U', PUT) + XX(POST, 1, 'A', PATCH) + XX(POST, 1, 'R', PROPFIND) + XX(PUT, 2, 'R', PURGE) + XX(CONNECT, 1, 'H', CHECKOUT) + XX(CONNECT, 2, 'P', COPY) + XX(MKCOL, 1, 'O', MOVE) + XX(MKCOL, 1, 'E', MERGE) + XX(MKCOL, 1, '-', MSEARCH) + XX(MKCOL, 2, 'A', MKACTIVITY) + XX(MKCOL, 3, 'A', MKCALENDAR) + XX(SUBSCRIBE, 1, 'E', SEARCH) + XX(SUBSCRIBE, 1, 'O', SOURCE) + XX(REPORT, 2, 'B', REBIND) + XX(PROPFIND, 4, 'P', PROPPATCH) + XX(LOCK, 1, 'I', LINK) + XX(UNLOCK, 2, 'S', UNSUBSCRIBE) + XX(UNLOCK, 2, 'B', UNBIND) + XX(UNLOCK, 3, 'I', UNLINK) +#undef XX + default: + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + ++parser->index; + break; + } + + case s_req_spaces_before_url: + { + if (ch == ' ') break; + + MARK(url); + if (parser->method == HTTP_CONNECT) { + UPDATE_STATE(s_req_server_start); + } + + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + + break; + } + + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + { + switch (ch) { + /* No whitespace allowed here */ + case ' ': + case CR: + case LF: + SET_ERRNO(HPE_INVALID_URL); + goto error; + default: + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + + break; + } + + case s_req_server: + case s_req_server_with_at: + case s_req_path: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + { + switch (ch) { + case ' ': + UPDATE_STATE(s_req_http_start); + CALLBACK_DATA(url); + break; + case CR: + case LF: + parser->http_major = 0; + parser->http_minor = 9; + UPDATE_STATE((ch == CR) ? + s_req_line_almost_done : + s_header_field_start); + CALLBACK_DATA(url); + break; + default: + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + break; + } + + case s_req_http_start: + switch (ch) { + case ' ': + break; + case 'H': + UPDATE_STATE(s_req_http_H); + break; + case 'I': + if (parser->method == HTTP_SOURCE) { + UPDATE_STATE(s_req_http_I); + break; + } + /* fall through */ + default: + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + break; + + case s_req_http_H: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_req_http_HT); + break; + + case s_req_http_HT: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_req_http_HTT); + break; + + case s_req_http_HTT: + STRICT_CHECK(ch != 'P'); + UPDATE_STATE(s_req_http_HTTP); + break; + + case s_req_http_I: + STRICT_CHECK(ch != 'C'); + UPDATE_STATE(s_req_http_IC); + break; + + case s_req_http_IC: + STRICT_CHECK(ch != 'E'); + UPDATE_STATE(s_req_http_HTTP); /* Treat "ICE" as "HTTP". */ + break; + + case s_req_http_HTTP: + STRICT_CHECK(ch != '/'); + UPDATE_STATE(s_req_http_major); + break; + + case s_req_http_major: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major = ch - '0'; + UPDATE_STATE(s_req_http_dot); + break; + + case s_req_http_dot: + { + if (UNLIKELY(ch != '.')) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + UPDATE_STATE(s_req_http_minor); + break; + } + + case s_req_http_minor: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor = ch - '0'; + UPDATE_STATE(s_req_http_end); + break; + + case s_req_http_end: + { + if (ch == CR) { + UPDATE_STATE(s_req_line_almost_done); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + break; + } + + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + break; + } + + /* end of request line */ + case s_req_line_almost_done: + { + if (UNLIKELY(ch != LF)) { + SET_ERRNO(HPE_LF_EXPECTED); + goto error; + } + + UPDATE_STATE(s_header_field_start); + break; + } + + case s_header_field_start: + { + if (ch == CR) { + UPDATE_STATE(s_headers_almost_done); + break; + } + + if (ch == LF) { + /* they might be just sending \n instead of \r\n so this would be + * the second \n to denote the end of headers*/ + UPDATE_STATE(s_headers_almost_done); + REEXECUTE(); + } + + c = TOKEN(ch); + + if (UNLIKELY(!c)) { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + MARK(header_field); + + parser->index = 0; + UPDATE_STATE(s_header_field); + + switch (c) { + case 'c': + parser->header_state = h_C; + break; + + case 'p': + parser->header_state = h_matching_proxy_connection; + break; + + case 't': + parser->header_state = h_matching_transfer_encoding; + break; + + case 'u': + parser->header_state = h_matching_upgrade; + break; + + default: + parser->header_state = h_general; + break; + } + break; + } + + case s_header_field: + { + const char* start = p; + for (; p != data + len; p++) { + ch = *p; + c = TOKEN(ch); + + if (!c) + break; + + switch (parser->header_state) { + case h_general: { + size_t limit = data + len - p; + limit = MIN(limit, max_header_size); + while (p+1 < data + limit && TOKEN(p[1])) { + p++; + } + break; + } + + case h_C: + parser->index++; + parser->header_state = (c == 'o' ? h_CO : h_general); + break; + + case h_CO: + parser->index++; + parser->header_state = (c == 'n' ? h_CON : h_general); + break; + + case h_CON: + parser->index++; + switch (c) { + case 'n': + parser->header_state = h_matching_connection; + break; + case 't': + parser->header_state = h_matching_content_length; + break; + default: + parser->header_state = h_general; + break; + } + break; + + /* connection */ + + case h_matching_connection: + parser->index++; + if (parser->index > sizeof(CONNECTION)-1 + || c != CONNECTION[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CONNECTION)-2) { + parser->header_state = h_connection; + } + break; + + /* proxy-connection */ + + case h_matching_proxy_connection: + parser->index++; + if (parser->index > sizeof(PROXY_CONNECTION)-1 + || c != PROXY_CONNECTION[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { + parser->header_state = h_connection; + } + break; + + /* content-length */ + + case h_matching_content_length: + parser->index++; + if (parser->index > sizeof(CONTENT_LENGTH)-1 + || c != CONTENT_LENGTH[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { + parser->header_state = h_content_length; + } + break; + + /* transfer-encoding */ + + case h_matching_transfer_encoding: + parser->index++; + if (parser->index > sizeof(TRANSFER_ENCODING)-1 + || c != TRANSFER_ENCODING[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { + parser->header_state = h_transfer_encoding; + } + break; + + /* upgrade */ + + case h_matching_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE)-1 + || c != UPGRADE[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(UPGRADE)-2) { + parser->header_state = h_upgrade; + } + break; + + case h_connection: + case h_content_length: + case h_transfer_encoding: + case h_upgrade: + if (ch != ' ') parser->header_state = h_general; + break; + + default: + assert(0 && "Unknown header_state"); + break; + } + } + + if (p == data + len) { + --p; + COUNT_HEADER_SIZE(p - start); + break; + } + + COUNT_HEADER_SIZE(p - start); + + if (ch == ':') { + UPDATE_STATE(s_header_value_discard_ws); + CALLBACK_DATA(header_field); + break; + } + + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + case s_header_value_discard_ws: + if (ch == ' ' || ch == '\t') break; + + if (ch == CR) { + UPDATE_STATE(s_header_value_discard_ws_almost_done); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_value_discard_lws); + break; + } + + /* fall through */ + + case s_header_value_start: + { + MARK(header_value); + + UPDATE_STATE(s_header_value); + parser->index = 0; + + c = LOWER(ch); + + switch (parser->header_state) { + case h_upgrade: + parser->flags |= F_UPGRADE; + parser->header_state = h_general; + break; + + case h_transfer_encoding: + /* looking for 'Transfer-Encoding: chunked' */ + if ('c' == c) { + parser->header_state = h_matching_transfer_encoding_chunked; + } else { + parser->header_state = h_general; + } + break; + + case h_content_length: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + if (parser->flags & F_CONTENTLENGTH) { + SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); + goto error; + } + + parser->flags |= F_CONTENTLENGTH; + parser->content_length = ch - '0'; + parser->header_state = h_content_length_num; + break; + + /* when obsolete line folding is encountered for content length + * continue to the s_header_value state */ + case h_content_length_ws: + break; + + case h_connection: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + parser->header_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + parser->header_state = h_matching_connection_close; + } else if (c == 'u') { + parser->header_state = h_matching_connection_upgrade; + } else { + parser->header_state = h_matching_connection_token; + } + break; + + /* Multi-value `Connection` header */ + case h_matching_connection_token_start: + break; + + default: + parser->header_state = h_general; + break; + } + break; + } + + case s_header_value: + { + const char* start = p; + enum header_states h_state = (enum header_states) parser->header_state; + for (; p != data + len; p++) { + ch = *p; + if (ch == CR) { + UPDATE_STATE(s_header_almost_done); + parser->header_state = h_state; + CALLBACK_DATA(header_value); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_almost_done); + COUNT_HEADER_SIZE(p - start); + parser->header_state = h_state; + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } + + if (!lenient && !IS_HEADER_CHAR(ch)) { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + c = LOWER(ch); + + switch (h_state) { + case h_general: + { + const char* p_cr; + const char* p_lf; + size_t limit = data + len - p; + + limit = MIN(limit, max_header_size); + + p_cr = (const char*) memchr(p, CR, limit); + p_lf = (const char*) memchr(p, LF, limit); + if (p_cr != NULL) { + if (p_lf != NULL && p_cr >= p_lf) + p = p_lf; + else + p = p_cr; + } else if (UNLIKELY(p_lf != NULL)) { + p = p_lf; + } else { + p = data + len; + } + --p; + break; + } + + case h_connection: + case h_transfer_encoding: + assert(0 && "Shouldn't get here."); + break; + + case h_content_length: + if (ch == ' ') break; + h_state = h_content_length_num; + /* fall through */ + + case h_content_length_num: + { + uint64_t t; + + if (ch == ' ') { + h_state = h_content_length_ws; + break; + } + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } + + t = parser->content_length; + t *= 10; + t += ch - '0'; + + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } + + parser->content_length = t; + break; + } + + case h_content_length_ws: + if (ch == ' ') break; + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + + /* Transfer-Encoding: chunked */ + case h_matching_transfer_encoding_chunked: + parser->index++; + if (parser->index > sizeof(CHUNKED)-1 + || c != CHUNKED[parser->index]) { + h_state = h_general; + } else if (parser->index == sizeof(CHUNKED)-2) { + h_state = h_transfer_encoding_chunked; + } + break; + + case h_matching_connection_token_start: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + h_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + h_state = h_matching_connection_close; + } else if (c == 'u') { + h_state = h_matching_connection_upgrade; + } else if (STRICT_TOKEN(c)) { + h_state = h_matching_connection_token; + } else if (c == ' ' || c == '\t') { + /* Skip lws */ + } else { + h_state = h_general; + } + break; + + /* looking for 'Connection: keep-alive' */ + case h_matching_connection_keep_alive: + parser->index++; + if (parser->index > sizeof(KEEP_ALIVE)-1 + || c != KEEP_ALIVE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(KEEP_ALIVE)-2) { + h_state = h_connection_keep_alive; + } + break; + + /* looking for 'Connection: close' */ + case h_matching_connection_close: + parser->index++; + if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(CLOSE)-2) { + h_state = h_connection_close; + } + break; + + /* looking for 'Connection: upgrade' */ + case h_matching_connection_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE) - 1 || + c != UPGRADE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(UPGRADE)-2) { + h_state = h_connection_upgrade; + } + break; + + case h_matching_connection_token: + if (ch == ',') { + h_state = h_matching_connection_token_start; + parser->index = 0; + } + break; + + case h_transfer_encoding_chunked: + if (ch != ' ') h_state = h_general; + break; + + case h_connection_keep_alive: + case h_connection_close: + case h_connection_upgrade: + if (ch == ',') { + if (h_state == h_connection_keep_alive) { + parser->flags |= F_CONNECTION_KEEP_ALIVE; + } else if (h_state == h_connection_close) { + parser->flags |= F_CONNECTION_CLOSE; + } else if (h_state == h_connection_upgrade) { + parser->flags |= F_CONNECTION_UPGRADE; + } + h_state = h_matching_connection_token_start; + parser->index = 0; + } else if (ch != ' ') { + h_state = h_matching_connection_token; + } + break; + + default: + UPDATE_STATE(s_header_value); + h_state = h_general; + break; + } + } + parser->header_state = h_state; + + if (p == data + len) + --p; + + COUNT_HEADER_SIZE(p - start); + break; + } + + case s_header_almost_done: + { + if (UNLIKELY(ch != LF)) { + SET_ERRNO(HPE_LF_EXPECTED); + goto error; + } + + UPDATE_STATE(s_header_value_lws); + break; + } + + case s_header_value_lws: + { + if (ch == ' ' || ch == '\t') { + if (parser->header_state == h_content_length_num) { + /* treat obsolete line folding as space */ + parser->header_state = h_content_length_ws; + } + UPDATE_STATE(s_header_value_start); + REEXECUTE(); + } + + /* finished the header */ + switch (parser->header_state) { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; + default: + break; + } + + UPDATE_STATE(s_header_field_start); + REEXECUTE(); + } + + case s_header_value_discard_ws_almost_done: + { + STRICT_CHECK(ch != LF); + UPDATE_STATE(s_header_value_discard_lws); + break; + } + + case s_header_value_discard_lws: + { + if (ch == ' ' || ch == '\t') { + UPDATE_STATE(s_header_value_discard_ws); + break; + } else { + switch (parser->header_state) { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + case h_content_length: + /* do not allow empty content length */ + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + break; + default: + break; + } + + /* header value was empty */ + MARK(header_value); + UPDATE_STATE(s_header_field_start); + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } + } + + case s_headers_almost_done: + { + STRICT_CHECK(ch != LF); + + if (parser->flags & F_TRAILING) { + /* End of a chunked request */ + UPDATE_STATE(s_message_done); + CALLBACK_NOTIFY_NOADVANCE(chunk_complete); + REEXECUTE(); + } + + /* Cannot use chunked encoding and a content-length header together + per the HTTP specification. */ + if ((parser->flags & F_CHUNKED) && + (parser->flags & F_CONTENTLENGTH)) { + SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); + goto error; + } + + UPDATE_STATE(s_headers_done); + + /* Set this here so that on_headers_complete() callbacks can see it */ + if ((parser->flags & F_UPGRADE) && + (parser->flags & F_CONNECTION_UPGRADE)) { + /* For responses, "Upgrade: foo" and "Connection: upgrade" are + * mandatory only when it is a 101 Switching Protocols response, + * otherwise it is purely informational, to announce support. + */ + parser->upgrade = + (parser->type == HTTP_REQUEST || parser->status_code == 101); + } else { + parser->upgrade = (parser->method == HTTP_CONNECT); + } + + /* Here we call the headers_complete callback. This is somewhat + * different than other callbacks because if the user returns 1, we + * will interpret that as saying that this message has no body. This + * is needed for the annoying case of recieving a response to a HEAD + * request. + * + * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so + * we have to simulate it by handling a change in errno below. + */ + if (settings->on_headers_complete) { + switch (settings->on_headers_complete(parser)) { + case 0: + break; + + case 2: + parser->upgrade = 1; + + /* fall through */ + case 1: + parser->flags |= F_SKIPBODY; + break; + + default: + SET_ERRNO(HPE_CB_headers_complete); + RETURN(p - data); /* Error */ + } + } + + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + RETURN(p - data); + } + + REEXECUTE(); + } + + case s_headers_done: + { + int hasBody; + STRICT_CHECK(ch != LF); + + parser->nread = 0; + nread = 0; + + hasBody = parser->flags & F_CHUNKED || + (parser->content_length > 0 && parser->content_length != ULLONG_MAX); + if (parser->upgrade && (parser->method == HTTP_CONNECT || + (parser->flags & F_SKIPBODY) || !hasBody)) { + /* Exit, the rest of the message is in a different protocol. */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + RETURN((p - data) + 1); + } + + if (parser->flags & F_SKIPBODY) { + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else if (parser->flags & F_CHUNKED) { + /* chunked encoding - ignore Content-Length header */ + UPDATE_STATE(s_chunk_size_start); + } else { + if (parser->content_length == 0) { + /* Content-Length header given but zero: Content-Length: 0\r\n */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else if (parser->content_length != ULLONG_MAX) { + /* Content-Length header given and non-zero */ + UPDATE_STATE(s_body_identity); + } else { + if (!http_message_needs_eof(parser)) { + /* Assume content-length 0 - read the next */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else { + /* Read body until EOF */ + UPDATE_STATE(s_body_identity_eof); + } + } + } + + break; + } + + case s_body_identity: + { + uint64_t to_read = MIN(parser->content_length, + (uint64_t) ((data + len) - p)); + + assert(parser->content_length != 0 + && parser->content_length != ULLONG_MAX); + + /* The difference between advancing content_length and p is because + * the latter will automaticaly advance on the next loop iteration. + * Further, if content_length ends up at 0, we want to see the last + * byte again for our message complete callback. + */ + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; + + if (parser->content_length == 0) { + UPDATE_STATE(s_message_done); + + /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. + * + * The alternative to doing this is to wait for the next byte to + * trigger the data callback, just as in every other case. The + * problem with this is that this makes it difficult for the test + * harness to distinguish between complete-on-EOF and + * complete-on-length. It's not clear that this distinction is + * important for applications, but let's keep it for now. + */ + CALLBACK_DATA_(body, p - body_mark + 1, p - data); + REEXECUTE(); + } + + break; + } + + /* read until EOF */ + case s_body_identity_eof: + MARK(body); + p = data + len - 1; + + break; + + case s_message_done: + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + if (parser->upgrade) { + /* Exit, the rest of the message is in a different protocol. */ + RETURN((p - data) + 1); + } + break; + + case s_chunk_size_start: + { + assert(nread == 1); + assert(parser->flags & F_CHUNKED); + + unhex_val = unhex[(unsigned char)ch]; + if (UNLIKELY(unhex_val == -1)) { + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } + + parser->content_length = unhex_val; + UPDATE_STATE(s_chunk_size); + break; + } + + case s_chunk_size: + { + uint64_t t; + + assert(parser->flags & F_CHUNKED); + + if (ch == CR) { + UPDATE_STATE(s_chunk_size_almost_done); + break; + } + + unhex_val = unhex[(unsigned char)ch]; + + if (unhex_val == -1) { + if (ch == ';' || ch == ' ') { + UPDATE_STATE(s_chunk_parameters); + break; + } + + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } + + t = parser->content_length; + t *= 16; + t += unhex_val; + + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + parser->content_length = t; + break; + } + + case s_chunk_parameters: + { + assert(parser->flags & F_CHUNKED); + /* just ignore this shit. TODO check for overflow */ + if (ch == CR) { + UPDATE_STATE(s_chunk_size_almost_done); + break; + } + break; + } + + case s_chunk_size_almost_done: + { + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + + parser->nread = 0; + nread = 0; + + if (parser->content_length == 0) { + parser->flags |= F_TRAILING; + UPDATE_STATE(s_header_field_start); + } else { + UPDATE_STATE(s_chunk_data); + } + CALLBACK_NOTIFY(chunk_header); + break; + } + + case s_chunk_data: + { + uint64_t to_read = MIN(parser->content_length, + (uint64_t) ((data + len) - p)); + + assert(parser->flags & F_CHUNKED); + assert(parser->content_length != 0 + && parser->content_length != ULLONG_MAX); + + /* See the explanation in s_body_identity for why the content + * length and data pointers are managed this way. + */ + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; + + if (parser->content_length == 0) { + UPDATE_STATE(s_chunk_data_almost_done); + } + + break; + } + + case s_chunk_data_almost_done: + assert(parser->flags & F_CHUNKED); + assert(parser->content_length == 0); + STRICT_CHECK(ch != CR); + UPDATE_STATE(s_chunk_data_done); + CALLBACK_DATA(body); + break; + + case s_chunk_data_done: + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + parser->nread = 0; + nread = 0; + UPDATE_STATE(s_chunk_size_start); + CALLBACK_NOTIFY(chunk_complete); + break; + + default: + assert(0 && "unhandled state"); + SET_ERRNO(HPE_INVALID_INTERNAL_STATE); + goto error; + } + } + + /* Run callbacks for any marks that we have leftover after we ran out of + * bytes. There should be at most one of these set, so it's OK to invoke + * them in series (unset marks will not result in callbacks). + * + * We use the NOADVANCE() variety of callbacks here because 'p' has already + * overflowed 'data' and this allows us to correct for the off-by-one that + * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' + * value that's in-bounds). + */ + + assert(((header_field_mark ? 1 : 0) + + (header_value_mark ? 1 : 0) + + (url_mark ? 1 : 0) + + (body_mark ? 1 : 0) + + (status_mark ? 1 : 0)) <= 1); + + CALLBACK_DATA_NOADVANCE(header_field); + CALLBACK_DATA_NOADVANCE(header_value); + CALLBACK_DATA_NOADVANCE(url); + CALLBACK_DATA_NOADVANCE(body); + CALLBACK_DATA_NOADVANCE(status); + + RETURN(len); + +error: + if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { + SET_ERRNO(HPE_UNKNOWN); + } + + RETURN(p - data); +} + + +/* Does the parser need to see an EOF to find the end of the message? */ +int +http_message_needs_eof (const http_parser *parser) +{ + if (parser->type == HTTP_REQUEST) { + return 0; + } + + /* See RFC 2616 section 4.4 */ + if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 || /* Not Modified */ + parser->flags & F_SKIPBODY) { /* response to a HEAD request */ + return 0; + } + + if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { + return 0; + } + + return 1; +} + + +int +http_should_keep_alive (const http_parser *parser) +{ + if (parser->http_major > 0 && parser->http_minor > 0) { + /* HTTP/1.1 */ + if (parser->flags & F_CONNECTION_CLOSE) { + return 0; + } + } else { + /* HTTP/1.0 or earlier */ + if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { + return 0; + } + } + + return !http_message_needs_eof(parser); +} + + +const char * +http_method_str (enum http_method m) +{ + return ELEM_AT(method_strings, m, ""); +} + +const char * +http_status_str (enum http_status s) +{ + switch (s) { +#define XX(num, name, string) case HTTP_STATUS_##name: return #string; + HTTP_STATUS_MAP(XX) +#undef XX + default: return ""; + } +} + +void +http_parser_init (http_parser *parser, enum http_parser_type t) +{ + void *data = parser->data; /* preserve application data */ + memset(parser, 0, sizeof(*parser)); + parser->data = data; + parser->type = t; + parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); + parser->http_errno = HPE_OK; +} + +void +http_parser_settings_init(http_parser_settings *settings) +{ + memset(settings, 0, sizeof(*settings)); +} + +const char * +http_errno_name(enum http_errno err) { + assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab)); + return http_strerror_tab[err].name; +} + +const char * +http_errno_description(enum http_errno err) { + assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab)); + return http_strerror_tab[err].description; +} + +static enum http_host_state +http_parse_host_char(enum http_host_state s, const char ch) { + switch(s) { + case s_http_userinfo: + case s_http_userinfo_start: + if (ch == '@') { + return s_http_host_start; + } + + if (IS_USERINFO_CHAR(ch)) { + return s_http_userinfo; + } + break; + + case s_http_host_start: + if (ch == '[') { + return s_http_host_v6_start; + } + + if (IS_HOST_CHAR(ch)) { + return s_http_host; + } + + break; + + case s_http_host: + if (IS_HOST_CHAR(ch)) { + return s_http_host; + } + + /* fall through */ + case s_http_host_v6_end: + if (ch == ':') { + return s_http_host_port_start; + } + + break; + + case s_http_host_v6: + if (ch == ']') { + return s_http_host_v6_end; + } + + /* fall through */ + case s_http_host_v6_start: + if (IS_HEX(ch) || ch == ':' || ch == '.') { + return s_http_host_v6; + } + + if (s == s_http_host_v6 && ch == '%') { + return s_http_host_v6_zone_start; + } + break; + + case s_http_host_v6_zone: + if (ch == ']') { + return s_http_host_v6_end; + } + + /* fall through */ + case s_http_host_v6_zone_start: + /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */ + if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' || + ch == '~') { + return s_http_host_v6_zone; + } + break; + + case s_http_host_port: + case s_http_host_port_start: + if (IS_NUM(ch)) { + return s_http_host_port; + } + + break; + + default: + break; + } + return s_http_host_dead; +} + +static int +http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { + enum http_host_state s; + + const char *p; + size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; + + assert(u->field_set & (1 << UF_HOST)); + + u->field_data[UF_HOST].len = 0; + + s = found_at ? s_http_userinfo_start : s_http_host_start; + + for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { + enum http_host_state new_s = http_parse_host_char(s, *p); + + if (new_s == s_http_host_dead) { + return 1; + } + + switch(new_s) { + case s_http_host: + if (s != s_http_host) { + u->field_data[UF_HOST].off = (uint16_t)(p - buf); + } + u->field_data[UF_HOST].len++; + break; + + case s_http_host_v6: + if (s != s_http_host_v6) { + u->field_data[UF_HOST].off = (uint16_t)(p - buf); + } + u->field_data[UF_HOST].len++; + break; + + case s_http_host_v6_zone_start: + case s_http_host_v6_zone: + u->field_data[UF_HOST].len++; + break; + + case s_http_host_port: + if (s != s_http_host_port) { + u->field_data[UF_PORT].off = (uint16_t)(p - buf); + u->field_data[UF_PORT].len = 0; + u->field_set |= (1 << UF_PORT); + } + u->field_data[UF_PORT].len++; + break; + + case s_http_userinfo: + if (s != s_http_userinfo) { + u->field_data[UF_USERINFO].off = (uint16_t)(p - buf); + u->field_data[UF_USERINFO].len = 0; + u->field_set |= (1 << UF_USERINFO); + } + u->field_data[UF_USERINFO].len++; + break; + + default: + break; + } + s = new_s; + } + + /* Make sure we don't end somewhere unexpected */ + switch (s) { + case s_http_host_start: + case s_http_host_v6_start: + case s_http_host_v6: + case s_http_host_v6_zone_start: + case s_http_host_v6_zone: + case s_http_host_port_start: + case s_http_userinfo: + case s_http_userinfo_start: + return 1; + default: + break; + } + + return 0; +} + +void +http_parser_url_init(struct http_parser_url *u) { + memset(u, 0, sizeof(*u)); +} + +int +http_parser_parse_url(const char *buf, size_t buflen, int is_connect, + struct http_parser_url *u) +{ + enum state s; + const char *p; + enum http_parser_url_fields uf, old_uf; + int found_at = 0; + + if (buflen == 0) { + return 1; + } + + u->port = u->field_set = 0; + s = is_connect ? s_req_server_start : s_req_spaces_before_url; + old_uf = UF_MAX; + + for (p = buf; p < buf + buflen; p++) { + s = parse_url_char(s, *p); + + /* Figure out the next field that we're operating on */ + switch (s) { + case s_dead: + return 1; + + /* Skip delimeters */ + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_query_string_start: + case s_req_fragment_start: + continue; + + case s_req_schema: + uf = UF_SCHEMA; + break; + + case s_req_server_with_at: + found_at = 1; + + /* fall through */ + case s_req_server: + uf = UF_HOST; + break; + + case s_req_path: + uf = UF_PATH; + break; + + case s_req_query_string: + uf = UF_QUERY; + break; + + case s_req_fragment: + uf = UF_FRAGMENT; + break; + + default: + assert(!"Unexpected state"); + return 1; + } + + /* Nothing's changed; soldier on */ + if (uf == old_uf) { + u->field_data[uf].len++; + continue; + } + + u->field_data[uf].off = (uint16_t)(p - buf); + u->field_data[uf].len = 1; + + u->field_set |= (1 << uf); + old_uf = uf; + } + + /* host must be present if there is a schema */ + /* parsing http:///toto will fail */ + if ((u->field_set & (1 << UF_SCHEMA)) && + (u->field_set & (1 << UF_HOST)) == 0) { + return 1; + } + + if (u->field_set & (1 << UF_HOST)) { + if (http_parse_host(buf, u, found_at) != 0) { + return 1; + } + } + + /* CONNECT requests can only contain "hostname:port" */ + if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { + return 1; + } + + if (u->field_set & (1 << UF_PORT)) { + uint16_t off; + uint16_t len; + const char* p; + const char* end; + unsigned long v; + + off = u->field_data[UF_PORT].off; + len = u->field_data[UF_PORT].len; + end = buf + off + len; + + /* NOTE: The characters are already validated and are in the [0-9] range */ + assert(off + len <= buflen && "Port number overflow"); + v = 0; + for (p = buf + off; p < end; p++) { + v *= 10; + v += *p - '0'; + + /* Ports have a max value of 2^16 */ + if (v > 0xffff) { + return 1; + } + } + + u->port = (uint16_t) v; + } + + return 0; +} + +void +http_parser_pause(http_parser *parser, int paused) { + /* Users should only be pausing/unpausing a parser that is not in an error + * state. In non-debug builds, there's not much that we can do about this + * other than ignore it. + */ + if (HTTP_PARSER_ERRNO(parser) == HPE_OK || + HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { + uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */ + SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); + } else { + assert(0 && "Attempting to pause parser in error state"); + } +} + +int +http_body_is_final(const struct http_parser *parser) { + return parser->state == s_message_done; +} + +unsigned long +http_parser_version(void) { + return HTTP_PARSER_VERSION_MAJOR * 0x10000 | + HTTP_PARSER_VERSION_MINOR * 0x00100 | + HTTP_PARSER_VERSION_PATCH * 0x00001; +} + +void +http_parser_set_max_header_size(uint32_t size) { + max_header_size = size; +} diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser/http_parser.gyp b/3rdparty/qthttpserver/src/3rdparty/http-parser/http_parser.gyp new file mode 100644 index 0000000..ef34eca --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser/http_parser.gyp @@ -0,0 +1,111 @@ +# This file is used with the GYP meta build system. +# http://code.google.com/p/gyp/ +# To build try this: +# svn co http://gyp.googlecode.com/svn/trunk gyp +# ./gyp/gyp -f make --depth=`pwd` http_parser.gyp +# ./out/Debug/test +{ + 'target_defaults': { + 'default_configuration': 'Debug', + 'configurations': { + # TODO: hoist these out and put them somewhere common, because + # RuntimeLibrary MUST MATCH across the entire project + 'Debug': { + 'defines': [ 'DEBUG', '_DEBUG' ], + 'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'RuntimeLibrary': 1, # static debug + }, + }, + }, + 'Release': { + 'defines': [ 'NDEBUG' ], + 'cflags': [ '-Wall', '-Wextra', '-O3' ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'RuntimeLibrary': 0, # static release + }, + }, + } + }, + 'msvs_settings': { + 'VCCLCompilerTool': { + }, + 'VCLibrarianTool': { + }, + 'VCLinkerTool': { + 'GenerateDebugInformation': 'true', + }, + }, + 'conditions': [ + ['OS == "win"', { + 'defines': [ + 'WIN32' + ], + }] + ], + }, + + 'targets': [ + { + 'target_name': 'http_parser', + 'type': 'static_library', + 'include_dirs': [ '.' ], + 'direct_dependent_settings': { + 'defines': [ 'HTTP_PARSER_STRICT=0' ], + 'include_dirs': [ '.' ], + }, + 'defines': [ 'HTTP_PARSER_STRICT=0' ], + 'sources': [ './http_parser.c', ], + 'conditions': [ + ['OS=="win"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + # Compile as C++. http_parser.c is actually C99, but C++ is + # close enough in this case. + 'CompileAs': 2, + }, + }, + }] + ], + }, + + { + 'target_name': 'http_parser_strict', + 'type': 'static_library', + 'include_dirs': [ '.' ], + 'direct_dependent_settings': { + 'defines': [ 'HTTP_PARSER_STRICT=1' ], + 'include_dirs': [ '.' ], + }, + 'defines': [ 'HTTP_PARSER_STRICT=1' ], + 'sources': [ './http_parser.c', ], + 'conditions': [ + ['OS=="win"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + # Compile as C++. http_parser.c is actually C99, but C++ is + # close enough in this case. + 'CompileAs': 2, + }, + }, + }] + ], + }, + + { + 'target_name': 'test-nonstrict', + 'type': 'executable', + 'dependencies': [ 'http_parser' ], + 'sources': [ 'test.c' ] + }, + + { + 'target_name': 'test-strict', + 'type': 'executable', + 'dependencies': [ 'http_parser_strict' ], + 'sources': [ 'test.c' ] + } + ] +} diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser/http_parser.h b/3rdparty/qthttpserver/src/3rdparty/http-parser/http_parser.h new file mode 100644 index 0000000..880ed27 --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser/http_parser.h @@ -0,0 +1,439 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef http_parser_h +#define http_parser_h +#ifdef __cplusplus +extern "C" { +#endif + +/* Also update SONAME in the Makefile whenever you change these. */ +#define HTTP_PARSER_VERSION_MAJOR 2 +#define HTTP_PARSER_VERSION_MINOR 9 +#define HTTP_PARSER_VERSION_PATCH 0 + +#include +#if defined(_WIN32) && !defined(__MINGW32__) && \ + (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__) +#include +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif + +/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run + * faster + */ +#ifndef HTTP_PARSER_STRICT +# define HTTP_PARSER_STRICT 1 +#endif + +/* Maximium header size allowed. If the macro is not defined + * before including this header then the default is used. To + * change the maximum header size, define the macro in the build + * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove + * the effective limit on the size of the header, define the macro + * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) + */ +#ifndef HTTP_MAX_HEADER_SIZE +# define HTTP_MAX_HEADER_SIZE (80*1024) +#endif + +typedef struct http_parser http_parser; +typedef struct http_parser_settings http_parser_settings; + + +/* Callbacks should return non-zero to indicate an error. The parser will + * then halt execution. + * + * The one exception is on_headers_complete. In a HTTP_RESPONSE parser + * returning '1' from on_headers_complete will tell the parser that it + * should not expect a body. This is used when receiving a response to a + * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: + * chunked' headers that indicate the presence of a body. + * + * Returning `2` from on_headers_complete will tell parser that it should not + * expect neither a body nor any futher responses on this connection. This is + * useful for handling responses to a CONNECT request which may not contain + * `Upgrade` or `Connection: upgrade` headers. + * + * http_data_cb does not return data chunks. It will be called arbitrarily + * many times for each string. E.G. you might get 10 callbacks for "on_url" + * each providing just a few characters more data. + */ +typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); +typedef int (*http_cb) (http_parser*); + + +/* Status Codes */ +#define HTTP_STATUS_MAP(XX) \ + XX(100, CONTINUE, Continue) \ + XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \ + XX(102, PROCESSING, Processing) \ + XX(200, OK, OK) \ + XX(201, CREATED, Created) \ + XX(202, ACCEPTED, Accepted) \ + XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \ + XX(204, NO_CONTENT, No Content) \ + XX(205, RESET_CONTENT, Reset Content) \ + XX(206, PARTIAL_CONTENT, Partial Content) \ + XX(207, MULTI_STATUS, Multi-Status) \ + XX(208, ALREADY_REPORTED, Already Reported) \ + XX(226, IM_USED, IM Used) \ + XX(300, MULTIPLE_CHOICES, Multiple Choices) \ + XX(301, MOVED_PERMANENTLY, Moved Permanently) \ + XX(302, FOUND, Found) \ + XX(303, SEE_OTHER, See Other) \ + XX(304, NOT_MODIFIED, Not Modified) \ + XX(305, USE_PROXY, Use Proxy) \ + XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \ + XX(308, PERMANENT_REDIRECT, Permanent Redirect) \ + XX(400, BAD_REQUEST, Bad Request) \ + XX(401, UNAUTHORIZED, Unauthorized) \ + XX(402, PAYMENT_REQUIRED, Payment Required) \ + XX(403, FORBIDDEN, Forbidden) \ + XX(404, NOT_FOUND, Not Found) \ + XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \ + XX(406, NOT_ACCEPTABLE, Not Acceptable) \ + XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \ + XX(408, REQUEST_TIMEOUT, Request Timeout) \ + XX(409, CONFLICT, Conflict) \ + XX(410, GONE, Gone) \ + XX(411, LENGTH_REQUIRED, Length Required) \ + XX(412, PRECONDITION_FAILED, Precondition Failed) \ + XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \ + XX(414, URI_TOO_LONG, URI Too Long) \ + XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \ + XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \ + XX(417, EXPECTATION_FAILED, Expectation Failed) \ + XX(421, MISDIRECTED_REQUEST, Misdirected Request) \ + XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \ + XX(423, LOCKED, Locked) \ + XX(424, FAILED_DEPENDENCY, Failed Dependency) \ + XX(426, UPGRADE_REQUIRED, Upgrade Required) \ + XX(428, PRECONDITION_REQUIRED, Precondition Required) \ + XX(429, TOO_MANY_REQUESTS, Too Many Requests) \ + XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \ + XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \ + XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \ + XX(501, NOT_IMPLEMENTED, Not Implemented) \ + XX(502, BAD_GATEWAY, Bad Gateway) \ + XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \ + XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \ + XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \ + XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \ + XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \ + XX(508, LOOP_DETECTED, Loop Detected) \ + XX(510, NOT_EXTENDED, Not Extended) \ + XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \ + +enum http_status + { +#define XX(num, name, string) HTTP_STATUS_##name = num, + HTTP_STATUS_MAP(XX) +#undef XX + }; + + +/* Request Methods */ +#define HTTP_METHOD_MAP(XX) \ + XX(0, DELETE, DELETE) \ + XX(1, GET, GET) \ + XX(2, HEAD, HEAD) \ + XX(3, POST, POST) \ + XX(4, PUT, PUT) \ + /* pathological */ \ + XX(5, CONNECT, CONNECT) \ + XX(6, OPTIONS, OPTIONS) \ + XX(7, TRACE, TRACE) \ + /* WebDAV */ \ + XX(8, COPY, COPY) \ + XX(9, LOCK, LOCK) \ + XX(10, MKCOL, MKCOL) \ + XX(11, MOVE, MOVE) \ + XX(12, PROPFIND, PROPFIND) \ + XX(13, PROPPATCH, PROPPATCH) \ + XX(14, SEARCH, SEARCH) \ + XX(15, UNLOCK, UNLOCK) \ + XX(16, BIND, BIND) \ + XX(17, REBIND, REBIND) \ + XX(18, UNBIND, UNBIND) \ + XX(19, ACL, ACL) \ + /* subversion */ \ + XX(20, REPORT, REPORT) \ + XX(21, MKACTIVITY, MKACTIVITY) \ + XX(22, CHECKOUT, CHECKOUT) \ + XX(23, MERGE, MERGE) \ + /* upnp */ \ + XX(24, MSEARCH, M-SEARCH) \ + XX(25, NOTIFY, NOTIFY) \ + XX(26, SUBSCRIBE, SUBSCRIBE) \ + XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ + /* RFC-5789 */ \ + XX(28, PATCH, PATCH) \ + XX(29, PURGE, PURGE) \ + /* CalDAV */ \ + XX(30, MKCALENDAR, MKCALENDAR) \ + /* RFC-2068, section 19.6.1.2 */ \ + XX(31, LINK, LINK) \ + XX(32, UNLINK, UNLINK) \ + /* icecast */ \ + XX(33, SOURCE, SOURCE) \ + +enum http_method + { +#define XX(num, name, string) HTTP_##name = num, + HTTP_METHOD_MAP(XX) +#undef XX + }; + + +enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; + + +/* Flag values for http_parser.flags field */ +enum flags + { F_CHUNKED = 1 << 0 + , F_CONNECTION_KEEP_ALIVE = 1 << 1 + , F_CONNECTION_CLOSE = 1 << 2 + , F_CONNECTION_UPGRADE = 1 << 3 + , F_TRAILING = 1 << 4 + , F_UPGRADE = 1 << 5 + , F_SKIPBODY = 1 << 6 + , F_CONTENTLENGTH = 1 << 7 + }; + + +/* Map for errno-related constants + * + * The provided argument should be a macro that takes 2 arguments. + */ +#define HTTP_ERRNO_MAP(XX) \ + /* No error */ \ + XX(OK, "success") \ + \ + /* Callback-related errors */ \ + XX(CB_message_begin, "the on_message_begin callback failed") \ + XX(CB_url, "the on_url callback failed") \ + XX(CB_header_field, "the on_header_field callback failed") \ + XX(CB_header_value, "the on_header_value callback failed") \ + XX(CB_headers_complete, "the on_headers_complete callback failed") \ + XX(CB_body, "the on_body callback failed") \ + XX(CB_message_complete, "the on_message_complete callback failed") \ + XX(CB_status, "the on_status callback failed") \ + XX(CB_chunk_header, "the on_chunk_header callback failed") \ + XX(CB_chunk_complete, "the on_chunk_complete callback failed") \ + \ + /* Parsing-related errors */ \ + XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ + XX(HEADER_OVERFLOW, \ + "too many header bytes seen; overflow detected") \ + XX(CLOSED_CONNECTION, \ + "data received after completed connection: close message") \ + XX(INVALID_VERSION, "invalid HTTP version") \ + XX(INVALID_STATUS, "invalid HTTP status code") \ + XX(INVALID_METHOD, "invalid HTTP method") \ + XX(INVALID_URL, "invalid URL") \ + XX(INVALID_HOST, "invalid host") \ + XX(INVALID_PORT, "invalid port") \ + XX(INVALID_PATH, "invalid path") \ + XX(INVALID_QUERY_STRING, "invalid query string") \ + XX(INVALID_FRAGMENT, "invalid fragment") \ + XX(LF_EXPECTED, "LF character expected") \ + XX(INVALID_HEADER_TOKEN, "invalid character in header") \ + XX(INVALID_CONTENT_LENGTH, \ + "invalid character in content-length header") \ + XX(UNEXPECTED_CONTENT_LENGTH, \ + "unexpected content-length header") \ + XX(INVALID_CHUNK_SIZE, \ + "invalid character in chunk size header") \ + XX(INVALID_CONSTANT, "invalid constant string") \ + XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ + XX(STRICT, "strict mode assertion failed") \ + XX(PAUSED, "parser is paused") \ + XX(UNKNOWN, "an unknown error occurred") + + +/* Define HPE_* values for each errno value above */ +#define HTTP_ERRNO_GEN(n, s) HPE_##n, +enum http_errno { + HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) +}; +#undef HTTP_ERRNO_GEN + + +/* Get an http_errno value from an http_parser */ +#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) + + +struct http_parser { + /** PRIVATE **/ + unsigned int type : 2; /* enum http_parser_type */ + unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */ + unsigned int state : 7; /* enum state from http_parser.c */ + unsigned int header_state : 7; /* enum header_state from http_parser.c */ + unsigned int index : 7; /* index into current matcher */ + unsigned int lenient_http_headers : 1; + + uint32_t nread; /* # bytes read in various scenarios */ + uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ + + /** READ-ONLY **/ + unsigned short http_major; + unsigned short http_minor; + unsigned int status_code : 16; /* responses only */ + unsigned int method : 8; /* requests only */ + unsigned int http_errno : 7; + + /* 1 = Upgrade header was present and the parser has exited because of that. + * 0 = No upgrade header present. + * Should be checked when http_parser_execute() returns in addition to + * error checking. + */ + unsigned int upgrade : 1; + + /** PUBLIC **/ + void *data; /* A pointer to get hook to the "connection" or "socket" object */ +}; + + +struct http_parser_settings { + http_cb on_message_begin; + http_data_cb on_url; + http_data_cb on_status; + http_data_cb on_header_field; + http_data_cb on_header_value; + http_cb on_headers_complete; + http_data_cb on_body; + http_cb on_message_complete; + /* When on_chunk_header is called, the current chunk length is stored + * in parser->content_length. + */ + http_cb on_chunk_header; + http_cb on_chunk_complete; +}; + + +enum http_parser_url_fields + { UF_SCHEMA = 0 + , UF_HOST = 1 + , UF_PORT = 2 + , UF_PATH = 3 + , UF_QUERY = 4 + , UF_FRAGMENT = 5 + , UF_USERINFO = 6 + , UF_MAX = 7 + }; + + +/* Result structure for http_parser_parse_url(). + * + * Callers should index into field_data[] with UF_* values iff field_set + * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and + * because we probably have padding left over), we convert any port to + * a uint16_t. + */ +struct http_parser_url { + uint16_t field_set; /* Bitmask of (1 << UF_*) values */ + uint16_t port; /* Converted UF_PORT string */ + + struct { + uint16_t off; /* Offset into buffer in which field starts */ + uint16_t len; /* Length of run in buffer */ + } field_data[UF_MAX]; +}; + + +/* Returns the library version. Bits 16-23 contain the major version number, + * bits 8-15 the minor version number and bits 0-7 the patch level. + * Usage example: + * + * unsigned long version = http_parser_version(); + * unsigned major = (version >> 16) & 255; + * unsigned minor = (version >> 8) & 255; + * unsigned patch = version & 255; + * printf("http_parser v%u.%u.%u\n", major, minor, patch); + */ +unsigned long http_parser_version(void); + +void http_parser_init(http_parser *parser, enum http_parser_type type); + + +/* Initialize http_parser_settings members to 0 + */ +void http_parser_settings_init(http_parser_settings *settings); + + +/* Executes the parser. Returns number of parsed bytes. Sets + * `parser->http_errno` on error. */ +size_t http_parser_execute(http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len); + + +/* If http_should_keep_alive() in the on_headers_complete or + * on_message_complete callback returns 0, then this should be + * the last message on the connection. + * If you are the server, respond with the "Connection: close" header. + * If you are the client, close the connection. + */ +int http_should_keep_alive(const http_parser *parser); + +/* Returns a string version of the HTTP method. */ +const char *http_method_str(enum http_method m); + +/* Returns a string version of the HTTP status code. */ +const char *http_status_str(enum http_status s); + +/* Return a string name of the given error */ +const char *http_errno_name(enum http_errno err); + +/* Return a string description of the given error */ +const char *http_errno_description(enum http_errno err); + +/* Initialize all http_parser_url members to 0 */ +void http_parser_url_init(struct http_parser_url *u); + +/* Parse a URL; return nonzero on failure */ +int http_parser_parse_url(const char *buf, size_t buflen, + int is_connect, + struct http_parser_url *u); + +/* Pause or un-pause the parser; a nonzero value pauses */ +void http_parser_pause(http_parser *parser, int paused); + +/* Checks if this is the final chunk of the body. */ +int http_body_is_final(const http_parser *parser); + +/* Change the maximum header size provided at compile time. */ +void http_parser_set_max_header_size(uint32_t size); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/3rdparty/qthttpserver/src/3rdparty/http-parser/test.c b/3rdparty/qthttpserver/src/3rdparty/http-parser/test.c new file mode 100644 index 0000000..c3fddd5 --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/http-parser/test.c @@ -0,0 +1,4515 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "http_parser.h" +#include +#include +#include +#include /* rand */ +#include +#include + +#if defined(__APPLE__) +# undef strlncpy +#endif /* defined(__APPLE__) */ + +#undef TRUE +#define TRUE 1 +#undef FALSE +#define FALSE 0 + +#define MAX_HEADERS 13 +#define MAX_ELEMENT_SIZE 2048 +#define MAX_CHUNKS 16 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +static http_parser parser; + +struct message { + const char *name; // for debugging purposes + const char *raw; + enum http_parser_type type; + enum http_method method; + int status_code; + char response_status[MAX_ELEMENT_SIZE]; + char request_path[MAX_ELEMENT_SIZE]; + char request_url[MAX_ELEMENT_SIZE]; + char fragment[MAX_ELEMENT_SIZE]; + char query_string[MAX_ELEMENT_SIZE]; + char body[MAX_ELEMENT_SIZE]; + size_t body_size; + const char *host; + const char *userinfo; + uint16_t port; + int num_headers; + enum { NONE=0, FIELD, VALUE } last_header_element; + char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE]; + int should_keep_alive; + + int num_chunks; + int num_chunks_complete; + int chunk_lengths[MAX_CHUNKS]; + + const char *upgrade; // upgraded body + + unsigned short http_major; + unsigned short http_minor; + + int message_begin_cb_called; + int headers_complete_cb_called; + int message_complete_cb_called; + int status_cb_called; + int message_complete_on_eof; + int body_is_final; +}; + +static int currently_parsing_eof; + +static struct message messages[5]; +static int num_messages; +static http_parser_settings *current_pause_parser; + +/* * R E Q U E S T S * */ +const struct message requests[] = +#define CURL_GET 0 +{ {.name= "curl get" + ,.type= HTTP_REQUEST + ,.raw= "GET /test HTTP/1.1\r\n" + "User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\n" + "Host: 0.0.0.0=5000\r\n" + "Accept: */*\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/test" + ,.request_url= "/test" + ,.num_headers= 3 + ,.headers= + { { "User-Agent", "curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1" } + , { "Host", "0.0.0.0=5000" } + , { "Accept", "*/*" } + } + ,.body= "" + } + +#define FIREFOX_GET 1 +, {.name= "firefox get" + ,.type= HTTP_REQUEST + ,.raw= "GET /favicon.ico HTTP/1.1\r\n" + "Host: 0.0.0.0=5000\r\n" + "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" + "Accept-Language: en-us,en;q=0.5\r\n" + "Accept-Encoding: gzip,deflate\r\n" + "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" + "Keep-Alive: 300\r\n" + "Connection: keep-alive\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/favicon.ico" + ,.request_url= "/favicon.ico" + ,.num_headers= 8 + ,.headers= + { { "Host", "0.0.0.0=5000" } + , { "User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0" } + , { "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" } + , { "Accept-Language", "en-us,en;q=0.5" } + , { "Accept-Encoding", "gzip,deflate" } + , { "Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7" } + , { "Keep-Alive", "300" } + , { "Connection", "keep-alive" } + } + ,.body= "" + } + +#define DUMBLUCK 2 +, {.name= "dumbluck" + ,.type= HTTP_REQUEST + ,.raw= "GET /dumbluck HTTP/1.1\r\n" + "aaaaaaaaaaaaa:++++++++++\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/dumbluck" + ,.request_url= "/dumbluck" + ,.num_headers= 1 + ,.headers= + { { "aaaaaaaaaaaaa", "++++++++++" } + } + ,.body= "" + } + +#define FRAGMENT_IN_URI 3 +, {.name= "fragment in url" + ,.type= HTTP_REQUEST + ,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "page=1" + ,.fragment= "posts-17408" + ,.request_path= "/forums/1/topics/2375" + /* XXX request url does include fragment? */ + ,.request_url= "/forums/1/topics/2375?page=1#posts-17408" + ,.num_headers= 0 + ,.body= "" + } + +#define GET_NO_HEADERS_NO_BODY 4 +, {.name= "get no headers no body" + ,.type= HTTP_REQUEST + ,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE /* would need Connection: close */ + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/get_no_headers_no_body/world" + ,.request_url= "/get_no_headers_no_body/world" + ,.num_headers= 0 + ,.body= "" + } + +#define GET_ONE_HEADER_NO_BODY 5 +, {.name= "get one header no body" + ,.type= HTTP_REQUEST + ,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n" + "Accept: */*\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE /* would need Connection: close */ + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/get_one_header_no_body" + ,.request_url= "/get_one_header_no_body" + ,.num_headers= 1 + ,.headers= + { { "Accept" , "*/*" } + } + ,.body= "" + } + +#define GET_FUNKY_CONTENT_LENGTH 6 +, {.name= "get funky content length body hello" + ,.type= HTTP_REQUEST + ,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n" + "conTENT-Length: 5\r\n" + "\r\n" + "HELLO" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/get_funky_content_length_body_hello" + ,.request_url= "/get_funky_content_length_body_hello" + ,.num_headers= 1 + ,.headers= + { { "conTENT-Length" , "5" } + } + ,.body= "HELLO" + } + +#define POST_IDENTITY_BODY_WORLD 7 +, {.name= "post identity body world" + ,.type= HTTP_REQUEST + ,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n" + "Accept: */*\r\n" + "Transfer-Encoding: identity\r\n" + "Content-Length: 5\r\n" + "\r\n" + "World" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.query_string= "q=search" + ,.fragment= "hey" + ,.request_path= "/post_identity_body_world" + ,.request_url= "/post_identity_body_world?q=search#hey" + ,.num_headers= 3 + ,.headers= + { { "Accept", "*/*" } + , { "Transfer-Encoding", "identity" } + , { "Content-Length", "5" } + } + ,.body= "World" + } + +#define POST_CHUNKED_ALL_YOUR_BASE 8 +, {.name= "post - chunked body: all your base are belong to us" + ,.type= HTTP_REQUEST + ,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "1e\r\nall your base are belong to us\r\n" + "0\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/post_chunked_all_your_base" + ,.request_url= "/post_chunked_all_your_base" + ,.num_headers= 1 + ,.headers= + { { "Transfer-Encoding" , "chunked" } + } + ,.body= "all your base are belong to us" + ,.num_chunks_complete= 2 + ,.chunk_lengths= { 0x1e } + } + +#define TWO_CHUNKS_MULT_ZERO_END 9 +, {.name= "two chunks ; triple zero ending" + ,.type= HTTP_REQUEST + ,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "5\r\nhello\r\n" + "6\r\n world\r\n" + "000\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/two_chunks_mult_zero_end" + ,.request_url= "/two_chunks_mult_zero_end" + ,.num_headers= 1 + ,.headers= + { { "Transfer-Encoding", "chunked" } + } + ,.body= "hello world" + ,.num_chunks_complete= 3 + ,.chunk_lengths= { 5, 6 } + } + +#define CHUNKED_W_TRAILING_HEADERS 10 +, {.name= "chunked with trailing headers. blech." + ,.type= HTTP_REQUEST + ,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "5\r\nhello\r\n" + "6\r\n world\r\n" + "0\r\n" + "Vary: *\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/chunked_w_trailing_headers" + ,.request_url= "/chunked_w_trailing_headers" + ,.num_headers= 3 + ,.headers= + { { "Transfer-Encoding", "chunked" } + , { "Vary", "*" } + , { "Content-Type", "text/plain" } + } + ,.body= "hello world" + ,.num_chunks_complete= 3 + ,.chunk_lengths= { 5, 6 } + } + +#define CHUNKED_W_NONSENSE_AFTER_LENGTH 11 +, {.name= "with nonsense after the length" + ,.type= HTTP_REQUEST + ,.raw= "POST /chunked_w_nonsense_after_length HTTP/1.1\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "5; ilovew3;whattheluck=aretheseparametersfor\r\nhello\r\n" + "6; blahblah; blah\r\n world\r\n" + "0\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/chunked_w_nonsense_after_length" + ,.request_url= "/chunked_w_nonsense_after_length" + ,.num_headers= 1 + ,.headers= + { { "Transfer-Encoding", "chunked" } + } + ,.body= "hello world" + ,.num_chunks_complete= 3 + ,.chunk_lengths= { 5, 6 } + } + +#define WITH_QUOTES 12 +, {.name= "with quotes" + ,.type= HTTP_REQUEST + ,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "foo=\"bar\"" + ,.fragment= "" + ,.request_path= "/with_\"stupid\"_quotes" + ,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\"" + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#define APACHEBENCH_GET 13 +/* The server receiving this request SHOULD NOT wait for EOF + * to know that content-length == 0. + * How to represent this in a unit test? message_complete_on_eof + * Compare with NO_CONTENT_LENGTH_RESPONSE. + */ +, {.name = "apachebench get" + ,.type= HTTP_REQUEST + ,.raw= "GET /test HTTP/1.0\r\n" + "Host: 0.0.0.0:5000\r\n" + "User-Agent: ApacheBench/2.3\r\n" + "Accept: */*\r\n\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/test" + ,.request_url= "/test" + ,.num_headers= 3 + ,.headers= { { "Host", "0.0.0.0:5000" } + , { "User-Agent", "ApacheBench/2.3" } + , { "Accept", "*/*" } + } + ,.body= "" + } + +#define QUERY_URL_WITH_QUESTION_MARK_GET 14 +/* Some clients include '?' characters in query strings. + */ +, {.name = "query url with question mark" + ,.type= HTTP_REQUEST + ,.raw= "GET /test.cgi?foo=bar?baz HTTP/1.1\r\n\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "foo=bar?baz" + ,.fragment= "" + ,.request_path= "/test.cgi" + ,.request_url= "/test.cgi?foo=bar?baz" + ,.num_headers= 0 + ,.headers= {} + ,.body= "" + } + +#define PREFIX_NEWLINE_GET 15 +/* Some clients, especially after a POST in a keep-alive connection, + * will send an extra CRLF before the next request + */ +, {.name = "newline prefix get" + ,.type= HTTP_REQUEST + ,.raw= "\r\nGET /test HTTP/1.1\r\n\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/test" + ,.request_url= "/test" + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#define UPGRADE_REQUEST 16 +, {.name = "upgrade request" + ,.type= HTTP_REQUEST + ,.raw= "GET /demo HTTP/1.1\r\n" + "Host: example.com\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" + "Sec-WebSocket-Protocol: sample\r\n" + "Upgrade: WebSocket\r\n" + "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" + "Origin: http://example.com\r\n" + "\r\n" + "Hot diggity dogg" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/demo" + ,.request_url= "/demo" + ,.num_headers= 7 + ,.upgrade="Hot diggity dogg" + ,.headers= { { "Host", "example.com" } + , { "Connection", "Upgrade" } + , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" } + , { "Sec-WebSocket-Protocol", "sample" } + , { "Upgrade", "WebSocket" } + , { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" } + , { "Origin", "http://example.com" } + } + ,.body= "" + } + +#define CONNECT_REQUEST 17 +, {.name = "connect request" + ,.type= HTTP_REQUEST + ,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n" + "User-agent: Mozilla/1.1N\r\n" + "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n" + "\r\n" + "some data\r\n" + "and yet even more data" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.method= HTTP_CONNECT + ,.query_string= "" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "0-home0.netscape.com:443" + ,.num_headers= 2 + ,.upgrade="some data\r\nand yet even more data" + ,.headers= { { "User-agent", "Mozilla/1.1N" } + , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" } + } + ,.body= "" + } + +#define REPORT_REQ 18 +, {.name= "report request" + ,.type= HTTP_REQUEST + ,.raw= "REPORT /test HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_REPORT + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/test" + ,.request_url= "/test" + ,.num_headers= 0 + ,.headers= {} + ,.body= "" + } + +#define NO_HTTP_VERSION 19 +, {.name= "request with no http version" + ,.type= HTTP_REQUEST + ,.raw= "GET /\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 0 + ,.http_minor= 9 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/" + ,.request_url= "/" + ,.num_headers= 0 + ,.headers= {} + ,.body= "" + } + +#define MSEARCH_REQ 20 +, {.name= "m-search request" + ,.type= HTTP_REQUEST + ,.raw= "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "MAN: \"ssdp:discover\"\r\n" + "ST: \"ssdp:all\"\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_MSEARCH + ,.query_string= "" + ,.fragment= "" + ,.request_path= "*" + ,.request_url= "*" + ,.num_headers= 3 + ,.headers= { { "HOST", "239.255.255.250:1900" } + , { "MAN", "\"ssdp:discover\"" } + , { "ST", "\"ssdp:all\"" } + } + ,.body= "" + } + +#define LINE_FOLDING_IN_HEADER 21 +, {.name= "line folding in header value" + ,.type= HTTP_REQUEST + ,.raw= "GET / HTTP/1.1\r\n" + "Line1: abc\r\n" + "\tdef\r\n" + " ghi\r\n" + "\t\tjkl\r\n" + " mno \r\n" + "\t \tqrs\r\n" + "Line2: \t line2\t\r\n" + "Line3:\r\n" + " line3\r\n" + "Line4: \r\n" + " \r\n" + "Connection:\r\n" + " close\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/" + ,.request_url= "/" + ,.num_headers= 5 + ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" } + , { "Line2", "line2\t" } + , { "Line3", "line3" } + , { "Line4", "" } + , { "Connection", "close" }, + } + ,.body= "" + } + + +#define QUERY_TERMINATED_HOST 22 +, {.name= "host terminated by a query string" + ,.type= HTTP_REQUEST + ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "hail=all" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "http://hypnotoad.org?hail=all" + ,.host= "hypnotoad.org" + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#define QUERY_TERMINATED_HOSTPORT 23 +, {.name= "host:port terminated by a query string" + ,.type= HTTP_REQUEST + ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "hail=all" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "http://hypnotoad.org:1234?hail=all" + ,.host= "hypnotoad.org" + ,.port= 1234 + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#define SPACE_TERMINATED_HOSTPORT 24 +, {.name= "host:port terminated by a space" + ,.type= HTTP_REQUEST + ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "http://hypnotoad.org:1234" + ,.host= "hypnotoad.org" + ,.port= 1234 + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#define PATCH_REQ 25 +, {.name = "PATCH request" + ,.type= HTTP_REQUEST + ,.raw= "PATCH /file.txt HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Content-Type: application/example\r\n" + "If-Match: \"e0023aa4e\"\r\n" + "Content-Length: 10\r\n" + "\r\n" + "cccccccccc" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_PATCH + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/file.txt" + ,.request_url= "/file.txt" + ,.num_headers= 4 + ,.headers= { { "Host", "www.example.com" } + , { "Content-Type", "application/example" } + , { "If-Match", "\"e0023aa4e\"" } + , { "Content-Length", "10" } + } + ,.body= "cccccccccc" + } + +#define CONNECT_CAPS_REQUEST 26 +, {.name = "connect caps request" + ,.type= HTTP_REQUEST + ,.raw= "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\n" + "User-agent: Mozilla/1.1N\r\n" + "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.method= HTTP_CONNECT + ,.query_string= "" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "HOME0.NETSCAPE.COM:443" + ,.num_headers= 2 + ,.upgrade="" + ,.headers= { { "User-agent", "Mozilla/1.1N" } + , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" } + } + ,.body= "" + } + +#if !HTTP_PARSER_STRICT +#define UTF8_PATH_REQ 27 +, {.name= "utf-8 path request" + ,.type= HTTP_REQUEST + ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n" + "Host: github.com\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "q=1" + ,.fragment= "narf" + ,.request_path= "/δ¶/δt/pope" + ,.request_url= "/δ¶/δt/pope?q=1#narf" + ,.num_headers= 1 + ,.headers= { {"Host", "github.com" } + } + ,.body= "" + } + +#define HOSTNAME_UNDERSCORE 28 +, {.name = "hostname underscore" + ,.type= HTTP_REQUEST + ,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n" + "User-agent: Mozilla/1.1N\r\n" + "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.method= HTTP_CONNECT + ,.query_string= "" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "home_0.netscape.com:443" + ,.num_headers= 2 + ,.upgrade="" + ,.headers= { { "User-agent", "Mozilla/1.1N" } + , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" } + } + ,.body= "" + } +#endif /* !HTTP_PARSER_STRICT */ + +/* see https://github.com/ry/http-parser/issues/47 */ +#define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 29 +, {.name = "eat CRLF between requests, no \"Connection: close\" header" + ,.raw= "POST / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: 4\r\n" + "\r\n" + "q=42\r\n" /* note the trailing CRLF */ + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/" + ,.request_url= "/" + ,.num_headers= 3 + ,.upgrade= 0 + ,.headers= { { "Host", "www.example.com" } + , { "Content-Type", "application/x-www-form-urlencoded" } + , { "Content-Length", "4" } + } + ,.body= "q=42" + } + +/* see https://github.com/ry/http-parser/issues/47 */ +#define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 30 +, {.name = "eat CRLF between requests even if \"Connection: close\" is set" + ,.raw= "POST / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: 4\r\n" + "Connection: close\r\n" + "\r\n" + "q=42\r\n" /* note the trailing CRLF */ + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */ + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/" + ,.request_url= "/" + ,.num_headers= 4 + ,.upgrade= 0 + ,.headers= { { "Host", "www.example.com" } + , { "Content-Type", "application/x-www-form-urlencoded" } + , { "Content-Length", "4" } + , { "Connection", "close" } + } + ,.body= "q=42" + } + +#define PURGE_REQ 31 +, {.name = "PURGE request" + ,.type= HTTP_REQUEST + ,.raw= "PURGE /file.txt HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_PURGE + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/file.txt" + ,.request_url= "/file.txt" + ,.num_headers= 1 + ,.headers= { { "Host", "www.example.com" } } + ,.body= "" + } + +#define SEARCH_REQ 32 +, {.name = "SEARCH request" + ,.type= HTTP_REQUEST + ,.raw= "SEARCH / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_SEARCH + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/" + ,.request_url= "/" + ,.num_headers= 1 + ,.headers= { { "Host", "www.example.com" } } + ,.body= "" + } + +#define PROXY_WITH_BASIC_AUTH 33 +, {.name= "host:port and basic_auth" + ,.type= HTTP_REQUEST + ,.raw= "GET http://a%12:b!&*$@hypnotoad.org:1234/toto HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.fragment= "" + ,.request_path= "/toto" + ,.request_url= "http://a%12:b!&*$@hypnotoad.org:1234/toto" + ,.host= "hypnotoad.org" + ,.userinfo= "a%12:b!&*$" + ,.port= 1234 + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#define LINE_FOLDING_IN_HEADER_WITH_LF 34 +, {.name= "line folding in header value" + ,.type= HTTP_REQUEST + ,.raw= "GET / HTTP/1.1\n" + "Line1: abc\n" + "\tdef\n" + " ghi\n" + "\t\tjkl\n" + " mno \n" + "\t \tqrs\n" + "Line2: \t line2\t\n" + "Line3:\n" + " line3\n" + "Line4: \n" + " \n" + "Connection:\n" + " close\n" + "\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/" + ,.request_url= "/" + ,.num_headers= 5 + ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" } + , { "Line2", "line2\t" } + , { "Line3", "line3" } + , { "Line4", "" } + , { "Connection", "close" }, + } + ,.body= "" + } + +#define CONNECTION_MULTI 35 +, {.name = "multiple connection header values with folding" + ,.type= HTTP_REQUEST + ,.raw= "GET /demo HTTP/1.1\r\n" + "Host: example.com\r\n" + "Connection: Something,\r\n" + " Upgrade, ,Keep-Alive\r\n" + "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" + "Sec-WebSocket-Protocol: sample\r\n" + "Upgrade: WebSocket\r\n" + "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" + "Origin: http://example.com\r\n" + "\r\n" + "Hot diggity dogg" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/demo" + ,.request_url= "/demo" + ,.num_headers= 7 + ,.upgrade="Hot diggity dogg" + ,.headers= { { "Host", "example.com" } + , { "Connection", "Something, Upgrade, ,Keep-Alive" } + , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" } + , { "Sec-WebSocket-Protocol", "sample" } + , { "Upgrade", "WebSocket" } + , { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" } + , { "Origin", "http://example.com" } + } + ,.body= "" + } + +#define CONNECTION_MULTI_LWS 36 +, {.name = "multiple connection header values with folding and lws" + ,.type= HTTP_REQUEST + ,.raw= "GET /demo HTTP/1.1\r\n" + "Connection: keep-alive, upgrade\r\n" + "Upgrade: WebSocket\r\n" + "\r\n" + "Hot diggity dogg" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/demo" + ,.request_url= "/demo" + ,.num_headers= 2 + ,.upgrade="Hot diggity dogg" + ,.headers= { { "Connection", "keep-alive, upgrade" } + , { "Upgrade", "WebSocket" } + } + ,.body= "" + } + +#define CONNECTION_MULTI_LWS_CRLF 37 +, {.name = "multiple connection header values with folding and lws" + ,.type= HTTP_REQUEST + ,.raw= "GET /demo HTTP/1.1\r\n" + "Connection: keep-alive, \r\n upgrade\r\n" + "Upgrade: WebSocket\r\n" + "\r\n" + "Hot diggity dogg" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/demo" + ,.request_url= "/demo" + ,.num_headers= 2 + ,.upgrade="Hot diggity dogg" + ,.headers= { { "Connection", "keep-alive, upgrade" } + , { "Upgrade", "WebSocket" } + } + ,.body= "" + } + +#define UPGRADE_POST_REQUEST 38 +, {.name = "upgrade post request" + ,.type= HTTP_REQUEST + ,.raw= "POST /demo HTTP/1.1\r\n" + "Host: example.com\r\n" + "Connection: Upgrade\r\n" + "Upgrade: HTTP/2.0\r\n" + "Content-Length: 15\r\n" + "\r\n" + "sweet post body" + "Hot diggity dogg" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.request_path= "/demo" + ,.request_url= "/demo" + ,.num_headers= 4 + ,.upgrade="Hot diggity dogg" + ,.headers= { { "Host", "example.com" } + , { "Connection", "Upgrade" } + , { "Upgrade", "HTTP/2.0" } + , { "Content-Length", "15" } + } + ,.body= "sweet post body" + } + +#define CONNECT_WITH_BODY_REQUEST 39 +, {.name = "connect with body request" + ,.type= HTTP_REQUEST + ,.raw= "CONNECT foo.bar.com:443 HTTP/1.0\r\n" + "User-agent: Mozilla/1.1N\r\n" + "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n" + "Content-Length: 10\r\n" + "\r\n" + "blarfcicle" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.method= HTTP_CONNECT + ,.request_url= "foo.bar.com:443" + ,.num_headers= 3 + ,.upgrade="blarfcicle" + ,.headers= { { "User-agent", "Mozilla/1.1N" } + , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" } + , { "Content-Length", "10" } + } + ,.body= "" + } + +/* Examples from the Internet draft for LINK/UNLINK methods: + * https://tools.ietf.org/id/draft-snell-link-method-01.html#rfc.section.5 + */ + +#define LINK_REQUEST 40 +, {.name = "link request" + ,.type= HTTP_REQUEST + ,.raw= "LINK /images/my_dog.jpg HTTP/1.1\r\n" + "Host: example.com\r\n" + "Link: ; rel=\"tag\"\r\n" + "Link: ; rel=\"tag\"\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_LINK + ,.request_path= "/images/my_dog.jpg" + ,.request_url= "/images/my_dog.jpg" + ,.query_string= "" + ,.fragment= "" + ,.num_headers= 3 + ,.headers= { { "Host", "example.com" } + , { "Link", "; rel=\"tag\"" } + , { "Link", "; rel=\"tag\"" } + } + ,.body= "" + } + +#define UNLINK_REQUEST 41 +, {.name = "unlink request" + ,.type= HTTP_REQUEST + ,.raw= "UNLINK /images/my_dog.jpg HTTP/1.1\r\n" + "Host: example.com\r\n" + "Link: ; rel=\"tag\"\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_UNLINK + ,.request_path= "/images/my_dog.jpg" + ,.request_url= "/images/my_dog.jpg" + ,.query_string= "" + ,.fragment= "" + ,.num_headers= 2 + ,.headers= { { "Host", "example.com" } + , { "Link", "; rel=\"tag\"" } + } + ,.body= "" + } + +#define SOURCE_REQUEST 42 +, {.name = "source request" + ,.type= HTTP_REQUEST + ,.raw= "SOURCE /music/sweet/music HTTP/1.1\r\n" + "Host: example.com\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_SOURCE + ,.request_path= "/music/sweet/music" + ,.request_url= "/music/sweet/music" + ,.query_string= "" + ,.fragment= "" + ,.num_headers= 1 + ,.headers= { { "Host", "example.com" } } + ,.body= "" + } + +#define SOURCE_ICE_REQUEST 42 +, {.name = "source request" + ,.type= HTTP_REQUEST + ,.raw= "SOURCE /music/sweet/music ICE/1.0\r\n" + "Host: example.com\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.method= HTTP_SOURCE + ,.request_path= "/music/sweet/music" + ,.request_url= "/music/sweet/music" + ,.query_string= "" + ,.fragment= "" + ,.num_headers= 1 + ,.headers= { { "Host", "example.com" } } + ,.body= "" + } +}; + +/* * R E S P O N S E S * */ +const struct message responses[] = +#define GOOGLE_301 0 +{ {.name= "google 301" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 301 Moved Permanently\r\n" + "Location: http://www.google.com/\r\n" + "Content-Type: text/html; charset=UTF-8\r\n" + "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n" + "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n" + "X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */ + "Cache-Control: public, max-age=2592000\r\n" + "Server: gws\r\n" + "Content-Length: 219 \r\n" + "\r\n" + "\n" + "301 Moved\n" + "

301 Moved

\n" + "The document has moved\n" + "here.\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 301 + ,.response_status= "Moved Permanently" + ,.num_headers= 8 + ,.headers= + { { "Location", "http://www.google.com/" } + , { "Content-Type", "text/html; charset=UTF-8" } + , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" } + , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" } + , { "X-$PrototypeBI-Version", "1.6.0.3" } + , { "Cache-Control", "public, max-age=2592000" } + , { "Server", "gws" } + , { "Content-Length", "219 " } + } + ,.body= "\n" + "301 Moved\n" + "

301 Moved

\n" + "The document has moved\n" + "here.\r\n" + "\r\n" + } + +#define NO_CONTENT_LENGTH_RESPONSE 1 +/* The client should wait for the server's EOF. That is, when content-length + * is not specified, and "Connection: close", the end of body is specified + * by the EOF. + * Compare with APACHEBENCH_GET + */ +, {.name= "no content-length response" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n" + "Server: Apache\r\n" + "X-Powered-By: Servlet/2.5 JSP/2.1\r\n" + "Content-Type: text/xml; charset=utf-8\r\n" + "Connection: close\r\n" + "\r\n" + "\n" + "\n" + " \n" + " \n" + " SOAP-ENV:Client\n" + " Client Error\n" + " \n" + " \n" + "" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 5 + ,.headers= + { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" } + , { "Server", "Apache" } + , { "X-Powered-By", "Servlet/2.5 JSP/2.1" } + , { "Content-Type", "text/xml; charset=utf-8" } + , { "Connection", "close" } + } + ,.body= "\n" + "\n" + " \n" + " \n" + " SOAP-ENV:Client\n" + " Client Error\n" + " \n" + " \n" + "" + } + +#define NO_HEADERS_NO_BODY_404 2 +, {.name= "404 no headers no body" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 404 + ,.response_status= "Not Found" + ,.num_headers= 0 + ,.headers= {} + ,.body_size= 0 + ,.body= "" + } + +#define NO_REASON_PHRASE 3 +, {.name= "301 no response phrase" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 301\r\n\r\n" + ,.should_keep_alive = FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 301 + ,.response_status= "" + ,.num_headers= 0 + ,.headers= {} + ,.body= "" + } + +#define TRAILING_SPACE_ON_CHUNKED_BODY 4 +, {.name="200 trailing space on chunked body" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "25 \r\n" + "This is the data in the first chunk\r\n" + "\r\n" + "1C\r\n" + "and this is the second one\r\n" + "\r\n" + "0 \r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 2 + ,.headers= + { {"Content-Type", "text/plain" } + , {"Transfer-Encoding", "chunked" } + } + ,.body_size = 37+28 + ,.body = + "This is the data in the first chunk\r\n" + "and this is the second one\r\n" + ,.num_chunks_complete= 3 + ,.chunk_lengths= { 0x25, 0x1c } + } + +#define NO_CARRIAGE_RET 5 +, {.name="no carriage ret" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\n" + "Content-Type: text/html; charset=utf-8\n" + "Connection: close\n" + "\n" + "these headers are from http://news.ycombinator.com/" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 2 + ,.headers= + { {"Content-Type", "text/html; charset=utf-8" } + , {"Connection", "close" } + } + ,.body= "these headers are from http://news.ycombinator.com/" + } + +#define PROXY_CONNECTION 6 +, {.name="proxy connection" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=UTF-8\r\n" + "Content-Length: 11\r\n" + "Proxy-Connection: close\r\n" + "Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n" + "\r\n" + "hello world" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 4 + ,.headers= + { {"Content-Type", "text/html; charset=UTF-8" } + , {"Content-Length", "11" } + , {"Proxy-Connection", "close" } + , {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"} + } + ,.body= "hello world" + } + +#define UNDERSTORE_HEADER_KEY 7 + // shown by + // curl -o /dev/null -v "http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;" +, {.name="underscore header key" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Server: DCLK-AdSvr\r\n" + "Content-Type: text/xml\r\n" + "Content-Length: 0\r\n" + "DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\r\n\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 4 + ,.headers= + { {"Server", "DCLK-AdSvr" } + , {"Content-Type", "text/xml" } + , {"Content-Length", "0" } + , {"DCLK_imp", "v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o" } + } + ,.body= "" + } + +#define BONJOUR_MADAME_FR 8 +/* The client should not merge two headers fields when the first one doesn't + * have a value. + */ +, {.name= "bonjourmadame.fr" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.0 301 Moved Permanently\r\n" + "Date: Thu, 03 Jun 2010 09:56:32 GMT\r\n" + "Server: Apache/2.2.3 (Red Hat)\r\n" + "Cache-Control: public\r\n" + "Pragma: \r\n" + "Location: http://www.bonjourmadame.fr/\r\n" + "Vary: Accept-Encoding\r\n" + "Content-Length: 0\r\n" + "Content-Type: text/html; charset=UTF-8\r\n" + "Connection: keep-alive\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.status_code= 301 + ,.response_status= "Moved Permanently" + ,.num_headers= 9 + ,.headers= + { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" } + , { "Server", "Apache/2.2.3 (Red Hat)" } + , { "Cache-Control", "public" } + , { "Pragma", "" } + , { "Location", "http://www.bonjourmadame.fr/" } + , { "Vary", "Accept-Encoding" } + , { "Content-Length", "0" } + , { "Content-Type", "text/html; charset=UTF-8" } + , { "Connection", "keep-alive" } + } + ,.body= "" + } + +#define RES_FIELD_UNDERSCORE 9 +/* Should handle spaces in header fields */ +, {.name= "field underscore" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n" + "Server: Apache\r\n" + "Cache-Control: no-cache, must-revalidate\r\n" + "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" + ".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n" + "Vary: Accept-Encoding\r\n" + "_eep-Alive: timeout=45\r\n" /* semantic value ignored */ + "_onnection: Keep-Alive\r\n" /* semantic value ignored */ + "Transfer-Encoding: chunked\r\n" + "Content-Type: text/html\r\n" + "Connection: close\r\n" + "\r\n" + "0\r\n\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 11 + ,.headers= + { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" } + , { "Server", "Apache" } + , { "Cache-Control", "no-cache, must-revalidate" } + , { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" } + , { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" } + , { "Vary", "Accept-Encoding" } + , { "_eep-Alive", "timeout=45" } + , { "_onnection", "Keep-Alive" } + , { "Transfer-Encoding", "chunked" } + , { "Content-Type", "text/html" } + , { "Connection", "close" } + } + ,.body= "" + ,.num_chunks_complete= 1 + ,.chunk_lengths= {} + } + +#define NON_ASCII_IN_STATUS_LINE 10 +/* Should handle non-ASCII in status line */ +, {.name= "non-ASCII in status line" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n" + "Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n" + "Content-Length: 0\r\n" + "Connection: close\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 500 + ,.response_status= "Oriëntatieprobleem" + ,.num_headers= 3 + ,.headers= + { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" } + , { "Content-Length", "0" } + , { "Connection", "close" } + } + ,.body= "" + } + +#define HTTP_VERSION_0_9 11 +/* Should handle HTTP/0.9 */ +, {.name= "http version 0.9" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/0.9 200 OK\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 0 + ,.http_minor= 9 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 0 + ,.headers= + {} + ,.body= "" + } + +#define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12 +/* The client should wait for the server's EOF. That is, when neither + * content-length nor transfer-encoding is specified, the end of body + * is specified by the EOF. + */ +, {.name= "neither content-length nor transfer-encoding response" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "hello world" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 1 + ,.headers= + { { "Content-Type", "text/plain" } + } + ,.body= "hello world" + } + +#define NO_BODY_HTTP10_KA_200 13 +, {.name= "HTTP/1.0 with keep-alive and EOF-terminated 200 status" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.0 200 OK\r\n" + "Connection: keep-alive\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 0 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 1 + ,.headers= + { { "Connection", "keep-alive" } + } + ,.body_size= 0 + ,.body= "" + } + +#define NO_BODY_HTTP10_KA_204 14 +, {.name= "HTTP/1.0 with keep-alive and a 204 status" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.0 204 No content\r\n" + "Connection: keep-alive\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.status_code= 204 + ,.response_status= "No content" + ,.num_headers= 1 + ,.headers= + { { "Connection", "keep-alive" } + } + ,.body_size= 0 + ,.body= "" + } + +#define NO_BODY_HTTP11_KA_200 15 +, {.name= "HTTP/1.1 with an EOF-terminated 200 status" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 0 + ,.headers={} + ,.body_size= 0 + ,.body= "" + } + +#define NO_BODY_HTTP11_KA_204 16 +, {.name= "HTTP/1.1 with a 204 status" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 204 No content\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 204 + ,.response_status= "No content" + ,.num_headers= 0 + ,.headers={} + ,.body_size= 0 + ,.body= "" + } + +#define NO_BODY_HTTP11_NOKA_204 17 +, {.name= "HTTP/1.1 with a 204 status and keep-alive disabled" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 204 No content\r\n" + "Connection: close\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 204 + ,.response_status= "No content" + ,.num_headers= 1 + ,.headers= + { { "Connection", "close" } + } + ,.body_size= 0 + ,.body= "" + } + +#define NO_BODY_HTTP11_KA_CHUNKED_200 18 +, {.name= "HTTP/1.1 with chunked endocing and a 200 response" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "0\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 1 + ,.headers= + { { "Transfer-Encoding", "chunked" } + } + ,.body_size= 0 + ,.body= "" + ,.num_chunks_complete= 1 + } + +#if !HTTP_PARSER_STRICT +#define SPACE_IN_FIELD_RES 19 +/* Should handle spaces in header fields */ +, {.name= "field space" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Server: Microsoft-IIS/6.0\r\n" + "X-Powered-By: ASP.NET\r\n" + "en-US Content-Type: text/xml\r\n" /* this is the problem */ + "Content-Type: text/xml\r\n" + "Content-Length: 16\r\n" + "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n" + "Connection: keep-alive\r\n" + "\r\n" + "hello" /* fake body */ + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 7 + ,.headers= + { { "Server", "Microsoft-IIS/6.0" } + , { "X-Powered-By", "ASP.NET" } + , { "en-US Content-Type", "text/xml" } + , { "Content-Type", "text/xml" } + , { "Content-Length", "16" } + , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" } + , { "Connection", "keep-alive" } + } + ,.body= "hello" + } +#endif /* !HTTP_PARSER_STRICT */ + +#define AMAZON_COM 20 +, {.name= "amazon.com" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 301 MovedPermanently\r\n" + "Date: Wed, 15 May 2013 17:06:33 GMT\r\n" + "Server: Server\r\n" + "x-amz-id-1: 0GPHKXSJQ826RK7GZEB2\r\n" + "p3p: policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \"\r\n" + "x-amz-id-2: STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD\r\n" + "Location: http://www.amazon.com/Dan-Brown/e/B000AP9DSU/ref=s9_pop_gw_al1?_encoding=UTF8&refinementId=618073011&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=center-2&pf_rd_r=0SHYY5BZXN3KR20BNFAY&pf_rd_t=101&pf_rd_p=1263340922&pf_rd_i=507846\r\n" + "Vary: Accept-Encoding,User-Agent\r\n" + "Content-Type: text/html; charset=ISO-8859-1\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "1\r\n" + "\n\r\n" + "0\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 301 + ,.response_status= "MovedPermanently" + ,.num_headers= 9 + ,.headers= { { "Date", "Wed, 15 May 2013 17:06:33 GMT" } + , { "Server", "Server" } + , { "x-amz-id-1", "0GPHKXSJQ826RK7GZEB2" } + , { "p3p", "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \"" } + , { "x-amz-id-2", "STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD" } + , { "Location", "http://www.amazon.com/Dan-Brown/e/B000AP9DSU/ref=s9_pop_gw_al1?_encoding=UTF8&refinementId=618073011&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=center-2&pf_rd_r=0SHYY5BZXN3KR20BNFAY&pf_rd_t=101&pf_rd_p=1263340922&pf_rd_i=507846" } + , { "Vary", "Accept-Encoding,User-Agent" } + , { "Content-Type", "text/html; charset=ISO-8859-1" } + , { "Transfer-Encoding", "chunked" } + } + ,.body= "\n" + ,.num_chunks_complete= 2 + ,.chunk_lengths= { 1 } + } + +#define EMPTY_REASON_PHRASE_AFTER_SPACE 20 +, {.name= "empty reason phrase after space" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 \r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "" + ,.num_headers= 0 + ,.headers= {} + ,.body= "" + } + +#define CONTENT_LENGTH_X 21 +, {.name= "Content-Length-X" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Content-Length-X: 0\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "2\r\n" + "OK\r\n" + "0\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 2 + ,.headers= { { "Content-Length-X", "0" } + , { "Transfer-Encoding", "chunked" } + } + ,.body= "OK" + ,.num_chunks_complete= 2 + ,.chunk_lengths= { 2 } + } + +#define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER 22 +, {.name= "HTTP 101 response with Upgrade header" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 101 Switching Protocols\r\n" + "Connection: upgrade\r\n" + "Upgrade: h2c\r\n" + "\r\n" + "proto" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 101 + ,.response_status= "Switching Protocols" + ,.upgrade= "proto" + ,.num_headers= 2 + ,.headers= + { { "Connection", "upgrade" } + , { "Upgrade", "h2c" } + } + } + +#define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER_AND_CONTENT_LENGTH 23 +, {.name= "HTTP 101 response with Upgrade and Content-Length header" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 101 Switching Protocols\r\n" + "Connection: upgrade\r\n" + "Upgrade: h2c\r\n" + "Content-Length: 4\r\n" + "\r\n" + "body" + "proto" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 101 + ,.response_status= "Switching Protocols" + ,.body= "body" + ,.upgrade= "proto" + ,.num_headers= 3 + ,.headers= + { { "Connection", "upgrade" } + , { "Upgrade", "h2c" } + , { "Content-Length", "4" } + } + } + +#define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER_AND_TRANSFER_ENCODING 24 +, {.name= "HTTP 101 response with Upgrade and Transfer-Encoding header" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 101 Switching Protocols\r\n" + "Connection: upgrade\r\n" + "Upgrade: h2c\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "2\r\n" + "bo\r\n" + "2\r\n" + "dy\r\n" + "0\r\n" + "\r\n" + "proto" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 101 + ,.response_status= "Switching Protocols" + ,.body= "body" + ,.upgrade= "proto" + ,.num_headers= 3 + ,.headers= + { { "Connection", "upgrade" } + , { "Upgrade", "h2c" } + , { "Transfer-Encoding", "chunked" } + } + ,.num_chunks_complete= 3 + ,.chunk_lengths= { 2, 2 } + } + +#define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER 25 +, {.name= "HTTP 200 response with Upgrade header" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Connection: upgrade\r\n" + "Upgrade: h2c\r\n" + "\r\n" + "body" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.body= "body" + ,.upgrade= NULL + ,.num_headers= 2 + ,.headers= + { { "Connection", "upgrade" } + , { "Upgrade", "h2c" } + } + } + +#define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER_AND_CONTENT_LENGTH 26 +, {.name= "HTTP 200 response with Upgrade and Content-Length header" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Connection: upgrade\r\n" + "Upgrade: h2c\r\n" + "Content-Length: 4\r\n" + "\r\n" + "body" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 3 + ,.body= "body" + ,.upgrade= NULL + ,.headers= + { { "Connection", "upgrade" } + , { "Upgrade", "h2c" } + , { "Content-Length", "4" } + } + } + +#define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER_AND_TRANSFER_ENCODING 27 +, {.name= "HTTP 200 response with Upgrade and Transfer-Encoding header" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Connection: upgrade\r\n" + "Upgrade: h2c\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "2\r\n" + "bo\r\n" + "2\r\n" + "dy\r\n" + "0\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 3 + ,.body= "body" + ,.upgrade= NULL + ,.headers= + { { "Connection", "upgrade" } + , { "Upgrade", "h2c" } + , { "Transfer-Encoding", "chunked" } + } + ,.num_chunks_complete= 3 + ,.chunk_lengths= { 2, 2 } + } +}; + +/* strnlen() is a POSIX.2008 addition. Can't rely on it being available so + * define it ourselves. + */ +size_t +strnlen(const char *s, size_t maxlen) +{ + const char *p; + + p = memchr(s, '\0', maxlen); + if (p == NULL) + return maxlen; + + return p - s; +} + +size_t +strlncat(char *dst, size_t len, const char *src, size_t n) +{ + size_t slen; + size_t dlen; + size_t rlen; + size_t ncpy; + + slen = strnlen(src, n); + dlen = strnlen(dst, len); + + if (dlen < len) { + rlen = len - dlen; + ncpy = slen < rlen ? slen : (rlen - 1); + memcpy(dst + dlen, src, ncpy); + dst[dlen + ncpy] = '\0'; + } + + assert(len > slen + dlen); + return slen + dlen; +} + +size_t +strlncpy(char *dst, size_t len, const char *src, size_t n) +{ + size_t slen; + size_t ncpy; + + slen = strnlen(src, n); + + if (len > 0) { + ncpy = slen < len ? slen : (len - 1); + memcpy(dst, src, ncpy); + dst[ncpy] = '\0'; + } + + assert(len > slen); + return slen; +} + +int +request_url_cb (http_parser *p, const char *buf, size_t len) +{ + assert(p == &parser); + strlncat(messages[num_messages].request_url, + sizeof(messages[num_messages].request_url), + buf, + len); + return 0; +} + +int +header_field_cb (http_parser *p, const char *buf, size_t len) +{ + assert(p == &parser); + struct message *m = &messages[num_messages]; + + if (m->last_header_element != FIELD) + m->num_headers++; + + strlncat(m->headers[m->num_headers-1][0], + sizeof(m->headers[m->num_headers-1][0]), + buf, + len); + + m->last_header_element = FIELD; + + return 0; +} + +int +header_value_cb (http_parser *p, const char *buf, size_t len) +{ + assert(p == &parser); + struct message *m = &messages[num_messages]; + + strlncat(m->headers[m->num_headers-1][1], + sizeof(m->headers[m->num_headers-1][1]), + buf, + len); + + m->last_header_element = VALUE; + + return 0; +} + +void +check_body_is_final (const http_parser *p) +{ + if (messages[num_messages].body_is_final) { + fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 " + "on last on_body callback call " + "but it doesn't! ***\n\n"); + assert(0); + abort(); + } + messages[num_messages].body_is_final = http_body_is_final(p); +} + +int +body_cb (http_parser *p, const char *buf, size_t len) +{ + assert(p == &parser); + strlncat(messages[num_messages].body, + sizeof(messages[num_messages].body), + buf, + len); + messages[num_messages].body_size += len; + check_body_is_final(p); + // printf("body_cb: '%s'\n", requests[num_messages].body); + return 0; +} + +int +count_body_cb (http_parser *p, const char *buf, size_t len) +{ + assert(p == &parser); + assert(buf); + messages[num_messages].body_size += len; + check_body_is_final(p); + return 0; +} + +int +message_begin_cb (http_parser *p) +{ + assert(p == &parser); + assert(!messages[num_messages].message_begin_cb_called); + messages[num_messages].message_begin_cb_called = TRUE; + return 0; +} + +int +headers_complete_cb (http_parser *p) +{ + assert(p == &parser); + messages[num_messages].method = parser.method; + messages[num_messages].status_code = parser.status_code; + messages[num_messages].http_major = parser.http_major; + messages[num_messages].http_minor = parser.http_minor; + messages[num_messages].headers_complete_cb_called = TRUE; + messages[num_messages].should_keep_alive = http_should_keep_alive(&parser); + return 0; +} + +int +message_complete_cb (http_parser *p) +{ + assert(p == &parser); + if (messages[num_messages].should_keep_alive != + http_should_keep_alive(&parser)) + { + fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same " + "value in both on_message_complete and on_headers_complete " + "but it doesn't! ***\n\n"); + assert(0); + abort(); + } + + if (messages[num_messages].body_size && + http_body_is_final(p) && + !messages[num_messages].body_is_final) + { + fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 " + "on last on_body callback call " + "but it doesn't! ***\n\n"); + assert(0); + abort(); + } + + messages[num_messages].message_complete_cb_called = TRUE; + + messages[num_messages].message_complete_on_eof = currently_parsing_eof; + + num_messages++; + return 0; +} + +int +response_status_cb (http_parser *p, const char *buf, size_t len) +{ + assert(p == &parser); + + messages[num_messages].status_cb_called = TRUE; + + strlncat(messages[num_messages].response_status, + sizeof(messages[num_messages].response_status), + buf, + len); + return 0; +} + +int +chunk_header_cb (http_parser *p) +{ + assert(p == &parser); + int chunk_idx = messages[num_messages].num_chunks; + messages[num_messages].num_chunks++; + if (chunk_idx < MAX_CHUNKS) { + messages[num_messages].chunk_lengths[chunk_idx] = p->content_length; + } + + return 0; +} + +int +chunk_complete_cb (http_parser *p) +{ + assert(p == &parser); + + /* Here we want to verify that each chunk_header_cb is matched by a + * chunk_complete_cb, so not only should the total number of calls to + * both callbacks be the same, but they also should be interleaved + * properly */ + assert(messages[num_messages].num_chunks == + messages[num_messages].num_chunks_complete + 1); + + messages[num_messages].num_chunks_complete++; + return 0; +} + +/* These dontcall_* callbacks exist so that we can verify that when we're + * paused, no additional callbacks are invoked */ +int +dontcall_message_begin_cb (http_parser *p) +{ + if (p) { } // gcc + fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n"); + abort(); +} + +int +dontcall_header_field_cb (http_parser *p, const char *buf, size_t len) +{ + if (p || buf || len) { } // gcc + fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n"); + abort(); +} + +int +dontcall_header_value_cb (http_parser *p, const char *buf, size_t len) +{ + if (p || buf || len) { } // gcc + fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n"); + abort(); +} + +int +dontcall_request_url_cb (http_parser *p, const char *buf, size_t len) +{ + if (p || buf || len) { } // gcc + fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n"); + abort(); +} + +int +dontcall_body_cb (http_parser *p, const char *buf, size_t len) +{ + if (p || buf || len) { } // gcc + fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n"); + abort(); +} + +int +dontcall_headers_complete_cb (http_parser *p) +{ + if (p) { } // gcc + fprintf(stderr, "\n\n*** on_headers_complete() called on paused " + "parser ***\n\n"); + abort(); +} + +int +dontcall_message_complete_cb (http_parser *p) +{ + if (p) { } // gcc + fprintf(stderr, "\n\n*** on_message_complete() called on paused " + "parser ***\n\n"); + abort(); +} + +int +dontcall_response_status_cb (http_parser *p, const char *buf, size_t len) +{ + if (p || buf || len) { } // gcc + fprintf(stderr, "\n\n*** on_status() called on paused parser ***\n\n"); + abort(); +} + +int +dontcall_chunk_header_cb (http_parser *p) +{ + if (p) { } // gcc + fprintf(stderr, "\n\n*** on_chunk_header() called on paused parser ***\n\n"); + exit(1); +} + +int +dontcall_chunk_complete_cb (http_parser *p) +{ + if (p) { } // gcc + fprintf(stderr, "\n\n*** on_chunk_complete() " + "called on paused parser ***\n\n"); + exit(1); +} + +static http_parser_settings settings_dontcall = + {.on_message_begin = dontcall_message_begin_cb + ,.on_header_field = dontcall_header_field_cb + ,.on_header_value = dontcall_header_value_cb + ,.on_url = dontcall_request_url_cb + ,.on_status = dontcall_response_status_cb + ,.on_body = dontcall_body_cb + ,.on_headers_complete = dontcall_headers_complete_cb + ,.on_message_complete = dontcall_message_complete_cb + ,.on_chunk_header = dontcall_chunk_header_cb + ,.on_chunk_complete = dontcall_chunk_complete_cb + }; + +/* These pause_* callbacks always pause the parser and just invoke the regular + * callback that tracks content. Before returning, we overwrite the parser + * settings to point to the _dontcall variety so that we can verify that + * the pause actually did, you know, pause. */ +int +pause_message_begin_cb (http_parser *p) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return message_begin_cb(p); +} + +int +pause_header_field_cb (http_parser *p, const char *buf, size_t len) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return header_field_cb(p, buf, len); +} + +int +pause_header_value_cb (http_parser *p, const char *buf, size_t len) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return header_value_cb(p, buf, len); +} + +int +pause_request_url_cb (http_parser *p, const char *buf, size_t len) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return request_url_cb(p, buf, len); +} + +int +pause_body_cb (http_parser *p, const char *buf, size_t len) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return body_cb(p, buf, len); +} + +int +pause_headers_complete_cb (http_parser *p) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return headers_complete_cb(p); +} + +int +pause_message_complete_cb (http_parser *p) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return message_complete_cb(p); +} + +int +pause_response_status_cb (http_parser *p, const char *buf, size_t len) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return response_status_cb(p, buf, len); +} + +int +pause_chunk_header_cb (http_parser *p) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return chunk_header_cb(p); +} + +int +pause_chunk_complete_cb (http_parser *p) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return chunk_complete_cb(p); +} + +int +connect_headers_complete_cb (http_parser *p) +{ + headers_complete_cb(p); + return 1; +} + +int +connect_message_complete_cb (http_parser *p) +{ + messages[num_messages].should_keep_alive = http_should_keep_alive(&parser); + return message_complete_cb(p); +} + +static http_parser_settings settings_pause = + {.on_message_begin = pause_message_begin_cb + ,.on_header_field = pause_header_field_cb + ,.on_header_value = pause_header_value_cb + ,.on_url = pause_request_url_cb + ,.on_status = pause_response_status_cb + ,.on_body = pause_body_cb + ,.on_headers_complete = pause_headers_complete_cb + ,.on_message_complete = pause_message_complete_cb + ,.on_chunk_header = pause_chunk_header_cb + ,.on_chunk_complete = pause_chunk_complete_cb + }; + +static http_parser_settings settings = + {.on_message_begin = message_begin_cb + ,.on_header_field = header_field_cb + ,.on_header_value = header_value_cb + ,.on_url = request_url_cb + ,.on_status = response_status_cb + ,.on_body = body_cb + ,.on_headers_complete = headers_complete_cb + ,.on_message_complete = message_complete_cb + ,.on_chunk_header = chunk_header_cb + ,.on_chunk_complete = chunk_complete_cb + }; + +static http_parser_settings settings_count_body = + {.on_message_begin = message_begin_cb + ,.on_header_field = header_field_cb + ,.on_header_value = header_value_cb + ,.on_url = request_url_cb + ,.on_status = response_status_cb + ,.on_body = count_body_cb + ,.on_headers_complete = headers_complete_cb + ,.on_message_complete = message_complete_cb + ,.on_chunk_header = chunk_header_cb + ,.on_chunk_complete = chunk_complete_cb + }; + +static http_parser_settings settings_connect = + {.on_message_begin = message_begin_cb + ,.on_header_field = header_field_cb + ,.on_header_value = header_value_cb + ,.on_url = request_url_cb + ,.on_status = response_status_cb + ,.on_body = dontcall_body_cb + ,.on_headers_complete = connect_headers_complete_cb + ,.on_message_complete = connect_message_complete_cb + ,.on_chunk_header = chunk_header_cb + ,.on_chunk_complete = chunk_complete_cb + }; + +static http_parser_settings settings_null = + {.on_message_begin = 0 + ,.on_header_field = 0 + ,.on_header_value = 0 + ,.on_url = 0 + ,.on_status = 0 + ,.on_body = 0 + ,.on_headers_complete = 0 + ,.on_message_complete = 0 + ,.on_chunk_header = 0 + ,.on_chunk_complete = 0 + }; + +void +parser_init (enum http_parser_type type) +{ + num_messages = 0; + http_parser_init(&parser, type); + memset(&messages, 0, sizeof messages); +} + +size_t parse (const char *buf, size_t len) +{ + size_t nparsed; + currently_parsing_eof = (len == 0); + nparsed = http_parser_execute(&parser, &settings, buf, len); + return nparsed; +} + +size_t parse_count_body (const char *buf, size_t len) +{ + size_t nparsed; + currently_parsing_eof = (len == 0); + nparsed = http_parser_execute(&parser, &settings_count_body, buf, len); + return nparsed; +} + +size_t parse_pause (const char *buf, size_t len) +{ + size_t nparsed; + http_parser_settings s = settings_pause; + + currently_parsing_eof = (len == 0); + current_pause_parser = &s; + nparsed = http_parser_execute(&parser, current_pause_parser, buf, len); + return nparsed; +} + +size_t parse_connect (const char *buf, size_t len) +{ + size_t nparsed; + currently_parsing_eof = (len == 0); + nparsed = http_parser_execute(&parser, &settings_connect, buf, len); + return nparsed; +} + +static inline int +check_str_eq (const struct message *m, + const char *prop, + const char *expected, + const char *found) { + if ((expected == NULL) != (found == NULL)) { + printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name); + printf("expected %s\n", (expected == NULL) ? "NULL" : expected); + printf(" found %s\n", (found == NULL) ? "NULL" : found); + return 0; + } + if (expected != NULL && 0 != strcmp(expected, found)) { + printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name); + printf("expected '%s'\n", expected); + printf(" found '%s'\n", found); + return 0; + } + return 1; +} + +static inline int +check_num_eq (const struct message *m, + const char *prop, + int expected, + int found) { + if (expected != found) { + printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name); + printf("expected %d\n", expected); + printf(" found %d\n", found); + return 0; + } + return 1; +} + +#define MESSAGE_CHECK_STR_EQ(expected, found, prop) \ + if (!check_str_eq(expected, #prop, expected->prop, found->prop)) return 0 + +#define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \ + if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0 + +#define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn) \ +do { \ + char ubuf[256]; \ + \ + if ((u)->field_set & (1 << (fn))) { \ + memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off, \ + (u)->field_data[(fn)].len); \ + ubuf[(u)->field_data[(fn)].len] = '\0'; \ + } else { \ + ubuf[0] = '\0'; \ + } \ + \ + check_str_eq(expected, #prop, expected->prop, ubuf); \ +} while(0) + +int +message_eq (int index, int connect, const struct message *expected) +{ + int i; + struct message *m = &messages[index]; + + MESSAGE_CHECK_NUM_EQ(expected, m, http_major); + MESSAGE_CHECK_NUM_EQ(expected, m, http_minor); + + if (expected->type == HTTP_REQUEST) { + MESSAGE_CHECK_NUM_EQ(expected, m, method); + } else { + MESSAGE_CHECK_NUM_EQ(expected, m, status_code); + MESSAGE_CHECK_STR_EQ(expected, m, response_status); + assert(m->status_cb_called); + } + + if (!connect) { + MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive); + MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof); + } + + assert(m->message_begin_cb_called); + assert(m->headers_complete_cb_called); + assert(m->message_complete_cb_called); + + + MESSAGE_CHECK_STR_EQ(expected, m, request_url); + + /* Check URL components; we can't do this w/ CONNECT since it doesn't + * send us a well-formed URL. + */ + if (*m->request_url && m->method != HTTP_CONNECT) { + struct http_parser_url u; + + if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) { + fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n", + m->request_url); + abort(); + } + + if (expected->host) { + MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST); + } + + if (expected->userinfo) { + MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO); + } + + m->port = (u.field_set & (1 << UF_PORT)) ? + u.port : 0; + + MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY); + MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT); + MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH); + MESSAGE_CHECK_NUM_EQ(expected, m, port); + } + + if (connect) { + check_num_eq(m, "body_size", 0, m->body_size); + } else if (expected->body_size) { + MESSAGE_CHECK_NUM_EQ(expected, m, body_size); + } else { + MESSAGE_CHECK_STR_EQ(expected, m, body); + } + + if (connect) { + check_num_eq(m, "num_chunks_complete", 0, m->num_chunks_complete); + } else { + assert(m->num_chunks == m->num_chunks_complete); + MESSAGE_CHECK_NUM_EQ(expected, m, num_chunks_complete); + for (i = 0; i < m->num_chunks && i < MAX_CHUNKS; i++) { + MESSAGE_CHECK_NUM_EQ(expected, m, chunk_lengths[i]); + } + } + + MESSAGE_CHECK_NUM_EQ(expected, m, num_headers); + + int r; + for (i = 0; i < m->num_headers; i++) { + r = check_str_eq(expected, "header field", expected->headers[i][0], m->headers[i][0]); + if (!r) return 0; + r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]); + if (!r) return 0; + } + + if (!connect) { + MESSAGE_CHECK_STR_EQ(expected, m, upgrade); + } + + return 1; +} + +/* Given a sequence of varargs messages, return the number of them that the + * parser should successfully parse, taking into account that upgraded + * messages prevent all subsequent messages from being parsed. + */ +size_t +count_parsed_messages(const size_t nmsgs, ...) { + size_t i; + va_list ap; + + va_start(ap, nmsgs); + + for (i = 0; i < nmsgs; i++) { + struct message *m = va_arg(ap, struct message *); + + if (m->upgrade) { + va_end(ap); + return i + 1; + } + } + + va_end(ap); + return nmsgs; +} + +/* Given a sequence of bytes and the number of these that we were able to + * parse, verify that upgrade bodies are correct. + */ +void +upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) { + va_list ap; + size_t i; + size_t off = 0; + + va_start(ap, nmsgs); + + for (i = 0; i < nmsgs; i++) { + struct message *m = va_arg(ap, struct message *); + + off += strlen(m->raw); + + if (m->upgrade) { + off -= strlen(m->upgrade); + + /* Check the portion of the response after its specified upgrade */ + if (!check_str_eq(m, "upgrade", body + off, body + nread)) { + abort(); + } + + /* Fix up the response so that message_eq() will verify the beginning + * of the upgrade */ + *(body + nread + strlen(m->upgrade)) = '\0'; + messages[num_messages -1 ].upgrade = body + nread; + + va_end(ap); + return; + } + } + + va_end(ap); + printf("\n\n*** Error: expected a message with upgrade ***\n"); + + abort(); +} + +static void +print_error (const char *raw, size_t error_location) +{ + fprintf(stderr, "\n*** %s ***\n\n", + http_errno_description(HTTP_PARSER_ERRNO(&parser))); + + int this_line = 0, char_len = 0; + size_t i, j, len = strlen(raw), error_location_line = 0; + for (i = 0; i < len; i++) { + if (i == error_location) this_line = 1; + switch (raw[i]) { + case '\r': + char_len = 2; + fprintf(stderr, "\\r"); + break; + + case '\n': + fprintf(stderr, "\\n\n"); + + if (this_line) goto print; + + error_location_line = 0; + continue; + + default: + char_len = 1; + fputc(raw[i], stderr); + break; + } + if (!this_line) error_location_line += char_len; + } + + fprintf(stderr, "[eof]\n"); + + print: + for (j = 0; j < error_location_line; j++) { + fputc(' ', stderr); + } + fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location); +} + +void +test_preserve_data (void) +{ + char my_data[] = "application-specific data"; + http_parser parser; + parser.data = my_data; + http_parser_init(&parser, HTTP_REQUEST); + if (parser.data != my_data) { + printf("\n*** parser.data not preserved accross http_parser_init ***\n\n"); + abort(); + } +} + +struct url_test { + const char *name; + const char *url; + int is_connect; + struct http_parser_url u; + int rv; +}; + +const struct url_test url_tests[] = +{ {.name="proxy request" + ,.url="http://hostname/" + ,.is_connect=0 + ,.u= + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH) + ,.port=0 + ,.field_data= + {{ 0, 4 } /* UF_SCHEMA */ + ,{ 7, 8 } /* UF_HOST */ + ,{ 0, 0 } /* UF_PORT */ + ,{ 15, 1 } /* UF_PATH */ + ,{ 0, 0 } /* UF_QUERY */ + ,{ 0, 0 } /* UF_FRAGMENT */ + ,{ 0, 0 } /* UF_USERINFO */ + } + } + ,.rv=0 + } + +, {.name="proxy request with port" + ,.url="http://hostname:444/" + ,.is_connect=0 + ,.u= + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH) + ,.port=444 + ,.field_data= + {{ 0, 4 } /* UF_SCHEMA */ + ,{ 7, 8 } /* UF_HOST */ + ,{ 16, 3 } /* UF_PORT */ + ,{ 19, 1 } /* UF_PATH */ + ,{ 0, 0 } /* UF_QUERY */ + ,{ 0, 0 } /* UF_FRAGMENT */ + ,{ 0, 0 } /* UF_USERINFO */ + } + } + ,.rv=0 + } + +, {.name="CONNECT request" + ,.url="hostname:443" + ,.is_connect=1 + ,.u= + {.field_set=(1 << UF_HOST) | (1 << UF_PORT) + ,.port=443 + ,.field_data= + {{ 0, 0 } /* UF_SCHEMA */ + ,{ 0, 8 } /* UF_HOST */ + ,{ 9, 3 } /* UF_PORT */ + ,{ 0, 0 } /* UF_PATH */ + ,{ 0, 0 } /* UF_QUERY */ + ,{ 0, 0 } /* UF_FRAGMENT */ + ,{ 0, 0 } /* UF_USERINFO */ + } + } + ,.rv=0 + } + +, {.name="CONNECT request but not connect" + ,.url="hostname:443" + ,.is_connect=0 + ,.rv=1 + } + +, {.name="proxy ipv6 request" + ,.url="http://[1:2::3:4]/" + ,.is_connect=0 + ,.u= + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH) + ,.port=0 + ,.field_data= + {{ 0, 4 } /* UF_SCHEMA */ + ,{ 8, 8 } /* UF_HOST */ + ,{ 0, 0 } /* UF_PORT */ + ,{ 17, 1 } /* UF_PATH */ + ,{ 0, 0 } /* UF_QUERY */ + ,{ 0, 0 } /* UF_FRAGMENT */ + ,{ 0, 0 } /* UF_USERINFO */ + } + } + ,.rv=0 + } + +, {.name="proxy ipv6 request with port" + ,.url="http://[1:2::3:4]:67/" + ,.is_connect=0 + ,.u= + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH) + ,.port=67 + ,.field_data= + {{ 0, 4 } /* UF_SCHEMA */ + ,{ 8, 8 } /* UF_HOST */ + ,{ 18, 2 } /* UF_PORT */ + ,{ 20, 1 } /* UF_PATH */ + ,{ 0, 0 } /* UF_QUERY */ + ,{ 0, 0 } /* UF_FRAGMENT */ + ,{ 0, 0 } /* UF_USERINFO */ + } + } + ,.rv=0 + } + +, {.name="CONNECT ipv6 address" + ,.url="[1:2::3:4]:443" + ,.is_connect=1 + ,.u= + {.field_set=(1 << UF_HOST) | (1 << UF_PORT) + ,.port=443 + ,.field_data= + {{ 0, 0 } /* UF_SCHEMA */ + ,{ 1, 8 } /* UF_HOST */ + ,{ 11, 3 } /* UF_PORT */ + ,{ 0, 0 } /* UF_PATH */ + ,{ 0, 0 } /* UF_QUERY */ + ,{ 0, 0 } /* UF_FRAGMENT */ + ,{ 0, 0 } /* UF_USERINFO */ + } + } + ,.rv=0 + } + +, {.name="ipv4 in ipv6 address" + ,.url="http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/" + ,.is_connect=0 + ,.u= + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH) + ,.port=0 + ,.field_data= + {{ 0, 4 } /* UF_SCHEMA */ + ,{ 8, 37 } /* UF_HOST */ + ,{ 0, 0 } /* UF_PORT */ + ,{ 46, 1 } /* UF_PATH */ + ,{ 0, 0 } /* UF_QUERY */ + ,{ 0, 0 } /* UF_FRAGMENT */ + ,{ 0, 0 } /* UF_USERINFO */ + } + } + ,.rv=0 + } + +, {.name="extra ? in query string" + ,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css," + "fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css," + "fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css" + ,.is_connect=0 + ,.u= + {.field_set=(1<field_set, u->port); + for (i = 0; i < UF_MAX; i++) { + if ((u->field_set & (1 << i)) == 0) { + printf("\tfield_data[%u]: unset\n", i); + continue; + } + + printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n\"", + i, + u->field_data[i].off, + u->field_data[i].len, + u->field_data[i].len, + url + u->field_data[i].off); + } +} + +void +test_parse_url (void) +{ + struct http_parser_url u; + const struct url_test *test; + unsigned int i; + int rv; + + for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) { + test = &url_tests[i]; + memset(&u, 0, sizeof(u)); + + rv = http_parser_parse_url(test->url, + test->url ? strlen(test->url) : 0, + test->is_connect, + &u); + + if (test->rv == 0) { + if (rv != 0) { + printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, " + "unexpected rv %d ***\n\n", test->url, test->name, rv); + abort(); + } + + if (memcmp(&u, &test->u, sizeof(u)) != 0) { + printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n", + test->url, test->name); + + printf("target http_parser_url:\n"); + dump_url(test->url, &test->u); + printf("result http_parser_url:\n"); + dump_url(test->url, &u); + + abort(); + } + } else { + /* test->rv != 0 */ + if (rv == 0) { + printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, " + "unexpected rv %d ***\n\n", test->url, test->name, rv); + abort(); + } + } + } +} + +void +test_method_str (void) +{ + assert(0 == strcmp("GET", http_method_str(HTTP_GET))); + assert(0 == strcmp("", http_method_str(1337))); +} + +void +test_status_str (void) +{ + assert(0 == strcmp("OK", http_status_str(HTTP_STATUS_OK))); + assert(0 == strcmp("Not Found", http_status_str(HTTP_STATUS_NOT_FOUND))); + assert(0 == strcmp("", http_status_str(1337))); +} + +void +test_message (const struct message *message) +{ + size_t raw_len = strlen(message->raw); + size_t msg1len; + for (msg1len = 0; msg1len < raw_len; msg1len++) { + parser_init(message->type); + + size_t read; + const char *msg1 = message->raw; + const char *msg2 = msg1 + msg1len; + size_t msg2len = raw_len - msg1len; + + if (msg1len) { + assert(num_messages == 0); + messages[0].headers_complete_cb_called = FALSE; + + read = parse(msg1, msg1len); + + if (!messages[0].headers_complete_cb_called && parser.nread != read) { + assert(parser.nread == read); + print_error(msg1, read); + abort(); + } + + if (message->upgrade && parser.upgrade && num_messages > 0) { + messages[num_messages - 1].upgrade = msg1 + read; + goto test; + } + + if (read != msg1len) { + print_error(msg1, read); + abort(); + } + } + + + read = parse(msg2, msg2len); + + if (message->upgrade && parser.upgrade) { + messages[num_messages - 1].upgrade = msg2 + read; + goto test; + } + + if (read != msg2len) { + print_error(msg2, read); + abort(); + } + + read = parse(NULL, 0); + + if (read != 0) { + print_error(message->raw, read); + abort(); + } + + test: + + if (num_messages != 1) { + printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name); + abort(); + } + + if(!message_eq(0, 0, message)) abort(); + } +} + +void +test_message_count_body (const struct message *message) +{ + parser_init(message->type); + + size_t read; + size_t l = strlen(message->raw); + size_t i, toread; + size_t chunk = 4024; + + for (i = 0; i < l; i+= chunk) { + toread = MIN(l-i, chunk); + read = parse_count_body(message->raw + i, toread); + if (read != toread) { + print_error(message->raw, read); + abort(); + } + } + + + read = parse_count_body(NULL, 0); + if (read != 0) { + print_error(message->raw, read); + abort(); + } + + if (num_messages != 1) { + printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name); + abort(); + } + + if(!message_eq(0, 0, message)) abort(); +} + +void +test_simple_type (const char *buf, + enum http_errno err_expected, + enum http_parser_type type) +{ + parser_init(type); + + enum http_errno err; + + parse(buf, strlen(buf)); + err = HTTP_PARSER_ERRNO(&parser); + parse(NULL, 0); + + /* In strict mode, allow us to pass with an unexpected HPE_STRICT as + * long as the caller isn't expecting success. + */ +#if HTTP_PARSER_STRICT + if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) { +#else + if (err_expected != err) { +#endif + fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n", + http_errno_name(err_expected), http_errno_name(err), buf); + abort(); + } +} + +void +test_simple (const char *buf, enum http_errno err_expected) +{ + test_simple_type(buf, err_expected, HTTP_REQUEST); +} + +void +test_invalid_header_content (int req, const char* str) +{ + http_parser parser; + http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); + size_t parsed; + const char *buf; + buf = req ? + "GET / HTTP/1.1\r\n" : + "HTTP/1.1 200 OK\r\n"; + parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); + assert(parsed == strlen(buf)); + + buf = str; + size_t buflen = strlen(buf); + + parsed = http_parser_execute(&parser, &settings_null, buf, buflen); + if (parsed != buflen) { + assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_HEADER_TOKEN); + return; + } + + fprintf(stderr, + "\n*** Error expected but none in invalid header content test ***\n"); + abort(); +} + +void +test_invalid_header_field_content_error (int req) +{ + test_invalid_header_content(req, "Foo: F\01ailure"); + test_invalid_header_content(req, "Foo: B\02ar"); +} + +void +test_invalid_header_field (int req, const char* str) +{ + http_parser parser; + http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); + size_t parsed; + const char *buf; + buf = req ? + "GET / HTTP/1.1\r\n" : + "HTTP/1.1 200 OK\r\n"; + parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); + assert(parsed == strlen(buf)); + + buf = str; + size_t buflen = strlen(buf); + + parsed = http_parser_execute(&parser, &settings_null, buf, buflen); + if (parsed != buflen) { + assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_HEADER_TOKEN); + return; + } + + fprintf(stderr, + "\n*** Error expected but none in invalid header token test ***\n"); + abort(); +} + +void +test_invalid_header_field_token_error (int req) +{ + test_invalid_header_field(req, "Fo@: Failure"); + test_invalid_header_field(req, "Foo\01\test: Bar"); +} + +void +test_double_content_length_error (int req) +{ + http_parser parser; + http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); + size_t parsed; + const char *buf; + buf = req ? + "GET / HTTP/1.1\r\n" : + "HTTP/1.1 200 OK\r\n"; + parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); + assert(parsed == strlen(buf)); + + buf = "Content-Length: 0\r\nContent-Length: 1\r\n\r\n"; + size_t buflen = strlen(buf); + + parsed = http_parser_execute(&parser, &settings_null, buf, buflen); + if (parsed != buflen) { + assert(HTTP_PARSER_ERRNO(&parser) == HPE_UNEXPECTED_CONTENT_LENGTH); + return; + } + + fprintf(stderr, + "\n*** Error expected but none in double content-length test ***\n"); + abort(); +} + +void +test_chunked_content_length_error (int req) +{ + http_parser parser; + http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); + size_t parsed; + const char *buf; + buf = req ? + "GET / HTTP/1.1\r\n" : + "HTTP/1.1 200 OK\r\n"; + parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); + assert(parsed == strlen(buf)); + + buf = "Transfer-Encoding: chunked\r\nContent-Length: 1\r\n\r\n"; + size_t buflen = strlen(buf); + + parsed = http_parser_execute(&parser, &settings_null, buf, buflen); + if (parsed != buflen) { + assert(HTTP_PARSER_ERRNO(&parser) == HPE_UNEXPECTED_CONTENT_LENGTH); + return; + } + + fprintf(stderr, + "\n*** Error expected but none in chunked content-length test ***\n"); + abort(); +} + +void +test_header_cr_no_lf_error (int req) +{ + http_parser parser; + http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); + size_t parsed; + const char *buf; + buf = req ? + "GET / HTTP/1.1\r\n" : + "HTTP/1.1 200 OK\r\n"; + parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); + assert(parsed == strlen(buf)); + + buf = "Foo: 1\rBar: 1\r\n\r\n"; + size_t buflen = strlen(buf); + + parsed = http_parser_execute(&parser, &settings_null, buf, buflen); + if (parsed != buflen) { + assert(HTTP_PARSER_ERRNO(&parser) == HPE_LF_EXPECTED); + return; + } + + fprintf(stderr, + "\n*** Error expected but none in header whitespace test ***\n"); + abort(); +} + +void +test_no_overflow_parse_url (void) +{ + int rv; + struct http_parser_url u; + + http_parser_url_init(&u); + rv = http_parser_parse_url("http://example.com:8001", 22, 0, &u); + + if (rv != 0) { + fprintf(stderr, + "\n*** test_no_overflow_parse_url invalid return value=%d\n", + rv); + abort(); + } + + if (u.port != 800) { + fprintf(stderr, + "\n*** test_no_overflow_parse_url invalid port number=%d\n", + u.port); + abort(); + } +} + +void +test_header_overflow_error (int req) +{ + http_parser parser; + http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); + size_t parsed; + const char *buf; + buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.0 200 OK\r\n"; + parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); + assert(parsed == strlen(buf)); + + buf = "header-key: header-value\r\n"; + size_t buflen = strlen(buf); + + int i; + for (i = 0; i < 10000; i++) { + parsed = http_parser_execute(&parser, &settings_null, buf, buflen); + if (parsed != buflen) { + //fprintf(stderr, "error found on iter %d\n", i); + assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW); + return; + } + } + + fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n"); + abort(); +} + + +void +test_header_nread_value () +{ + http_parser parser; + http_parser_init(&parser, HTTP_REQUEST); + size_t parsed; + const char *buf; + buf = "GET / HTTP/1.1\r\nheader: value\nhdr: value\r\n"; + parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); + assert(parsed == strlen(buf)); + + assert(parser.nread == strlen(buf)); +} + + +static void +test_content_length_overflow (const char *buf, size_t buflen, int expect_ok) +{ + http_parser parser; + http_parser_init(&parser, HTTP_RESPONSE); + http_parser_execute(&parser, &settings_null, buf, buflen); + + if (expect_ok) + assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK); + else + assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH); +} + +void +test_header_content_length_overflow_error (void) +{ +#define X(size) \ + "HTTP/1.1 200 OK\r\n" \ + "Content-Length: " #size "\r\n" \ + "\r\n" + const char a[] = X(1844674407370955160); /* 2^64 / 10 - 1 */ + const char b[] = X(18446744073709551615); /* 2^64-1 */ + const char c[] = X(18446744073709551616); /* 2^64 */ +#undef X + test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */ + test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */ + test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */ +} + +void +test_chunk_content_length_overflow_error (void) +{ +#define X(size) \ + "HTTP/1.1 200 OK\r\n" \ + "Transfer-Encoding: chunked\r\n" \ + "\r\n" \ + #size "\r\n" \ + "..." + const char a[] = X(FFFFFFFFFFFFFFE); /* 2^64 / 16 - 1 */ + const char b[] = X(FFFFFFFFFFFFFFFF); /* 2^64-1 */ + const char c[] = X(10000000000000000); /* 2^64 */ +#undef X + test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */ + test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */ + test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */ +} + +void +test_no_overflow_long_body (int req, size_t length) +{ + http_parser parser; + http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); + size_t parsed; + size_t i; + char buf1[3000]; + size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %lu\r\n\r\n", + req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", (unsigned long)length); + parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len); + if (parsed != buf1len) + goto err; + + for (i = 0; i < length; i++) { + char foo = 'a'; + parsed = http_parser_execute(&parser, &settings_null, &foo, 1); + if (parsed != 1) + goto err; + } + + parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len); + if (parsed != buf1len) goto err; + return; + + err: + fprintf(stderr, + "\n*** error in test_no_overflow_long_body %s of length %lu ***\n", + req ? "REQUEST" : "RESPONSE", + (unsigned long)length); + abort(); +} + +void +test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3) +{ + int message_count = count_parsed_messages(3, r1, r2, r3); + + char total[ strlen(r1->raw) + + strlen(r2->raw) + + strlen(r3->raw) + + 1 + ]; + total[0] = '\0'; + + strcat(total, r1->raw); + strcat(total, r2->raw); + strcat(total, r3->raw); + + parser_init(r1->type); + + size_t read; + + read = parse(total, strlen(total)); + + if (parser.upgrade) { + upgrade_message_fix(total, read, 3, r1, r2, r3); + goto test; + } + + if (read != strlen(total)) { + print_error(total, read); + abort(); + } + + read = parse(NULL, 0); + + if (read != 0) { + print_error(total, read); + abort(); + } + +test: + + if (message_count != num_messages) { + fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages); + abort(); + } + + if (!message_eq(0, 0, r1)) abort(); + if (message_count > 1 && !message_eq(1, 0, r2)) abort(); + if (message_count > 2 && !message_eq(2, 0, r3)) abort(); +} + +/* SCAN through every possible breaking to make sure the + * parser can handle getting the content in any chunks that + * might come from the socket + */ +void +test_scan (const struct message *r1, const struct message *r2, const struct message *r3) +{ + char total[80*1024] = "\0"; + char buf1[80*1024] = "\0"; + char buf2[80*1024] = "\0"; + char buf3[80*1024] = "\0"; + + strcat(total, r1->raw); + strcat(total, r2->raw); + strcat(total, r3->raw); + + size_t read; + + int total_len = strlen(total); + + int total_ops = 2 * (total_len - 1) * (total_len - 2) / 2; + int ops = 0 ; + + size_t buf1_len, buf2_len, buf3_len; + int message_count = count_parsed_messages(3, r1, r2, r3); + + int i,j,type_both; + for (type_both = 0; type_both < 2; type_both ++ ) { + for (j = 2; j < total_len; j ++ ) { + for (i = 1; i < j; i ++ ) { + + if (ops % 1000 == 0) { + printf("\b\b\b\b%3.0f%%", 100 * (float)ops /(float)total_ops); + fflush(stdout); + } + ops += 1; + + parser_init(type_both ? HTTP_BOTH : r1->type); + + buf1_len = i; + strlncpy(buf1, sizeof(buf1), total, buf1_len); + buf1[buf1_len] = 0; + + buf2_len = j - i; + strlncpy(buf2, sizeof(buf1), total+i, buf2_len); + buf2[buf2_len] = 0; + + buf3_len = total_len - j; + strlncpy(buf3, sizeof(buf1), total+j, buf3_len); + buf3[buf3_len] = 0; + + assert(num_messages == 0); + messages[0].headers_complete_cb_called = FALSE; + + read = parse(buf1, buf1_len); + + if (!messages[0].headers_complete_cb_called && parser.nread != read) { + print_error(buf1, read); + goto error; + } + + if (parser.upgrade) goto test; + + if (read != buf1_len) { + print_error(buf1, read); + goto error; + } + + read += parse(buf2, buf2_len); + + if (parser.upgrade) goto test; + + if (read != buf1_len + buf2_len) { + print_error(buf2, read); + goto error; + } + + read += parse(buf3, buf3_len); + + if (parser.upgrade) goto test; + + if (read != buf1_len + buf2_len + buf3_len) { + print_error(buf3, read); + goto error; + } + + parse(NULL, 0); + +test: + if (parser.upgrade) { + upgrade_message_fix(total, read, 3, r1, r2, r3); + } + + if (message_count != num_messages) { + fprintf(stderr, "\n\nParser didn't see %d messages only %d\n", + message_count, num_messages); + goto error; + } + + if (!message_eq(0, 0, r1)) { + fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n"); + goto error; + } + + if (message_count > 1 && !message_eq(1, 0, r2)) { + fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n"); + goto error; + } + + if (message_count > 2 && !message_eq(2, 0, r3)) { + fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n"); + goto error; + } + } + } + } + puts("\b\b\b\b100%"); + return; + + error: + fprintf(stderr, "i=%d j=%d\n", i, j); + fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1); + fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2); + fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3); + abort(); +} + +// user required to free the result +// string terminated by \0 +char * +create_large_chunked_message (int body_size_in_kb, const char* headers) +{ + int i; + size_t wrote = 0; + size_t headers_len = strlen(headers); + size_t bufsize = headers_len + (5+1024+2)*body_size_in_kb + 6; + char * buf = malloc(bufsize); + + memcpy(buf, headers, headers_len); + wrote += headers_len; + + for (i = 0; i < body_size_in_kb; i++) { + // write 1kb chunk into the body. + memcpy(buf + wrote, "400\r\n", 5); + wrote += 5; + memset(buf + wrote, 'C', 1024); + wrote += 1024; + strcpy(buf + wrote, "\r\n"); + wrote += 2; + } + + memcpy(buf + wrote, "0\r\n\r\n", 6); + wrote += 6; + assert(wrote == bufsize); + + return buf; +} + +/* Verify that we can pause parsing at any of the bytes in the + * message and still get the result that we're expecting. */ +void +test_message_pause (const struct message *msg) +{ + char *buf = (char*) msg->raw; + size_t buflen = strlen(msg->raw); + size_t nread; + + parser_init(msg->type); + + do { + nread = parse_pause(buf, buflen); + + // We can only set the upgrade buffer once we've gotten our message + // completion callback. + if (messages[0].message_complete_cb_called && + msg->upgrade && + parser.upgrade) { + messages[0].upgrade = buf + nread; + goto test; + } + + if (nread < buflen) { + + // Not much do to if we failed a strict-mode check + if (HTTP_PARSER_ERRNO(&parser) == HPE_STRICT) { + return; + } + + assert (HTTP_PARSER_ERRNO(&parser) == HPE_PAUSED); + } + + buf += nread; + buflen -= nread; + http_parser_pause(&parser, 0); + } while (buflen > 0); + + nread = parse_pause(NULL, 0); + assert (nread == 0); + +test: + if (num_messages != 1) { + printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name); + abort(); + } + + if(!message_eq(0, 0, msg)) abort(); +} + +/* Verify that body and next message won't be parsed in responses to CONNECT */ +void +test_message_connect (const struct message *msg) +{ + char *buf = (char*) msg->raw; + size_t buflen = strlen(msg->raw); + + parser_init(msg->type); + + parse_connect(buf, buflen); + + if (num_messages != 1) { + printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name); + abort(); + } + + if(!message_eq(0, 1, msg)) abort(); +} + +int +main (void) +{ + unsigned i, j, k; + unsigned long version; + unsigned major; + unsigned minor; + unsigned patch; + + version = http_parser_version(); + major = (version >> 16) & 255; + minor = (version >> 8) & 255; + patch = version & 255; + printf("http_parser v%u.%u.%u (0x%06lx)\n", major, minor, patch, version); + + printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser)); + + //// API + test_preserve_data(); + test_parse_url(); + test_method_str(); + test_status_str(); + + //// NREAD + test_header_nread_value(); + + //// OVERFLOW CONDITIONS + test_no_overflow_parse_url(); + + test_header_overflow_error(HTTP_REQUEST); + test_no_overflow_long_body(HTTP_REQUEST, 1000); + test_no_overflow_long_body(HTTP_REQUEST, 100000); + + test_header_overflow_error(HTTP_RESPONSE); + test_no_overflow_long_body(HTTP_RESPONSE, 1000); + test_no_overflow_long_body(HTTP_RESPONSE, 100000); + + test_header_content_length_overflow_error(); + test_chunk_content_length_overflow_error(); + + //// HEADER FIELD CONDITIONS + test_double_content_length_error(HTTP_REQUEST); + test_chunked_content_length_error(HTTP_REQUEST); + test_header_cr_no_lf_error(HTTP_REQUEST); + test_invalid_header_field_token_error(HTTP_REQUEST); + test_invalid_header_field_content_error(HTTP_REQUEST); + test_double_content_length_error(HTTP_RESPONSE); + test_chunked_content_length_error(HTTP_RESPONSE); + test_header_cr_no_lf_error(HTTP_RESPONSE); + test_invalid_header_field_token_error(HTTP_RESPONSE); + test_invalid_header_field_content_error(HTTP_RESPONSE); + + test_simple_type( + "POST / HTTP/1.1\r\n" + "Content-Length:\r\n" // empty + "\r\n", + HPE_INVALID_CONTENT_LENGTH, + HTTP_REQUEST); + + test_simple_type( + "POST / HTTP/1.1\r\n" + "Content-Length: 42 \r\n" // Note the surrounding whitespace. + "\r\n", + HPE_OK, + HTTP_REQUEST); + + test_simple_type( + "POST / HTTP/1.1\r\n" + "Content-Length: 4 2\r\n" + "\r\n", + HPE_INVALID_CONTENT_LENGTH, + HTTP_REQUEST); + + test_simple_type( + "POST / HTTP/1.1\r\n" + "Content-Length: 13 37\r\n" + "\r\n", + HPE_INVALID_CONTENT_LENGTH, + HTTP_REQUEST); + + test_simple_type( + "POST / HTTP/1.1\r\n" + "Content-Length: 42\r\n" + " Hello world!\r\n", + HPE_INVALID_CONTENT_LENGTH, + HTTP_REQUEST); + + test_simple_type( + "POST / HTTP/1.1\r\n" + "Content-Length: 42\r\n" + " \r\n", + HPE_OK, + HTTP_REQUEST); + + //// RESPONSES + + test_simple_type("HTP/1.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE); + test_simple_type("HTTP/01.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE); + test_simple_type("HTTP/11.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE); + test_simple_type("HTTP/1.01 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE); + test_simple_type("HTTP/1.1\t200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE); + test_simple_type("\rHTTP/1.1\t200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE); + + for (i = 0; i < ARRAY_SIZE(responses); i++) { + test_message(&responses[i]); + } + + for (i = 0; i < ARRAY_SIZE(responses); i++) { + test_message_pause(&responses[i]); + } + + for (i = 0; i < ARRAY_SIZE(responses); i++) { + test_message_connect(&responses[i]); + } + + for (i = 0; i < ARRAY_SIZE(responses); i++) { + if (!responses[i].should_keep_alive) continue; + for (j = 0; j < ARRAY_SIZE(responses); j++) { + if (!responses[j].should_keep_alive) continue; + for (k = 0; k < ARRAY_SIZE(responses); k++) { + test_multiple3(&responses[i], &responses[j], &responses[k]); + } + } + } + + test_message_count_body(&responses[NO_HEADERS_NO_BODY_404]); + test_message_count_body(&responses[TRAILING_SPACE_ON_CHUNKED_BODY]); + + // test very large chunked response + { + char * msg = create_large_chunked_message(31337, + "HTTP/1.0 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "Content-Type: text/plain\r\n" + "\r\n"); + struct message large_chunked = + {.name= "large chunked" + ,.type= HTTP_RESPONSE + ,.raw= msg + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 2 + ,.headers= + { { "Transfer-Encoding", "chunked" } + , { "Content-Type", "text/plain" } + } + ,.body_size= 31337*1024 + ,.num_chunks_complete= 31338 + }; + for (i = 0; i < MAX_CHUNKS; i++) { + large_chunked.chunk_lengths[i] = 1024; + } + test_message_count_body(&large_chunked); + free(msg); + } + + + + printf("response scan 1/2 "); + test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY] + , &responses[NO_BODY_HTTP10_KA_204] + , &responses[NO_REASON_PHRASE] + ); + + printf("response scan 2/2 "); + test_scan( &responses[BONJOUR_MADAME_FR] + , &responses[UNDERSTORE_HEADER_KEY] + , &responses[NO_CARRIAGE_RET] + ); + + puts("responses okay"); + + + /// REQUESTS + + test_simple("GET / IHTTP/1.0\r\n\r\n", HPE_INVALID_CONSTANT); + test_simple("GET / ICE/1.0\r\n\r\n", HPE_INVALID_CONSTANT); + test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION); + test_simple("GET / HTTP/01.1\r\n\r\n", HPE_INVALID_VERSION); + test_simple("GET / HTTP/11.1\r\n\r\n", HPE_INVALID_VERSION); + test_simple("GET / HTTP/1.01\r\n\r\n", HPE_INVALID_VERSION); + + // Extended characters - see nodejs/test/parallel/test-http-headers-obstext.js + test_simple("GET / HTTP/1.1\r\n" + "Test: Düsseldorf\r\n", + HPE_OK); + + // Well-formed but incomplete + test_simple("GET / HTTP/1.1\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 6\r\n" + "\r\n" + "fooba", + HPE_OK); + + static const char *all_methods[] = { + "DELETE", + "GET", + "HEAD", + "POST", + "PUT", + //"CONNECT", //CONNECT can't be tested like other methods, it's a tunnel + "OPTIONS", + "TRACE", + "COPY", + "LOCK", + "MKCOL", + "MOVE", + "PROPFIND", + "PROPPATCH", + "SEARCH", + "UNLOCK", + "BIND", + "REBIND", + "UNBIND", + "ACL", + "REPORT", + "MKACTIVITY", + "CHECKOUT", + "MERGE", + "M-SEARCH", + "NOTIFY", + "SUBSCRIBE", + "UNSUBSCRIBE", + "PATCH", + "PURGE", + "MKCALENDAR", + "LINK", + "UNLINK", + 0 }; + const char **this_method; + for (this_method = all_methods; *this_method; this_method++) { + char buf[200]; + sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method); + test_simple(buf, HPE_OK); + } + + static const char *bad_methods[] = { + "ASDF", + "C******", + "COLA", + "GEM", + "GETA", + "M****", + "MKCOLA", + "PROPPATCHA", + "PUN", + "PX", + "SA", + "hello world", + 0 }; + for (this_method = bad_methods; *this_method; this_method++) { + char buf[200]; + sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method); + test_simple(buf, HPE_INVALID_METHOD); + } + + // illegal header field name line folding + test_simple("GET / HTTP/1.1\r\n" + "name\r\n" + " : value\r\n" + "\r\n", + HPE_INVALID_HEADER_TOKEN); + + const char *dumbluck2 = + "GET / HTTP/1.1\r\n" + "X-SSL-Nonsense: -----BEGIN CERTIFICATE-----\r\n" + "\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n" + "\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n" + "\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n" + "\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n" + "\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n" + "\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n" + "\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n" + "\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n" + "\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n" + "\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n" + "\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n" + "\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n" + "\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n" + "\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n" + "\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n" + "\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n" + "\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n" + "\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n" + "\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n" + "\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n" + "\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n" + "\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n" + "\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n" + "\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n" + "\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n" + "\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n" + "\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n" + "\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n" + "\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n" + "\tRA==\r\n" + "\t-----END CERTIFICATE-----\r\n" + "\r\n"; + test_simple(dumbluck2, HPE_OK); + + const char *corrupted_connection = + "GET / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Connection\r\033\065\325eep-Alive\r\n" + "Accept-Encoding: gzip\r\n" + "\r\n"; + test_simple(corrupted_connection, HPE_INVALID_HEADER_TOKEN); + + const char *corrupted_header_name = + "GET / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "X-Some-Header\r\033\065\325eep-Alive\r\n" + "Accept-Encoding: gzip\r\n" + "\r\n"; + test_simple(corrupted_header_name, HPE_INVALID_HEADER_TOKEN); + +#if 0 + // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body + // until EOF. + // + // no content-length + // error if there is a body without content length + const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\n" + "Accept: */*\r\n" + "\r\n" + "HELLO"; + test_simple(bad_get_no_headers_no_body, 0); +#endif + /* TODO sending junk and large headers gets rejected */ + + + /* check to make sure our predefined requests are okay */ + for (i = 0; i < ARRAY_SIZE(requests); i++) { + test_message(&requests[i]); + } + + for (i = 0; i < ARRAY_SIZE(requests); i++) { + test_message_pause(&requests[i]); + } + + for (i = 0; i < ARRAY_SIZE(requests); i++) { + if (!requests[i].should_keep_alive) continue; + for (j = 0; j < ARRAY_SIZE(requests); j++) { + if (!requests[j].should_keep_alive) continue; + for (k = 0; k < ARRAY_SIZE(requests); k++) { + test_multiple3(&requests[i], &requests[j], &requests[k]); + } + } + } + + printf("request scan 1/4 "); + test_scan( &requests[GET_NO_HEADERS_NO_BODY] + , &requests[GET_ONE_HEADER_NO_BODY] + , &requests[GET_NO_HEADERS_NO_BODY] + ); + + printf("request scan 2/4 "); + test_scan( &requests[POST_CHUNKED_ALL_YOUR_BASE] + , &requests[POST_IDENTITY_BODY_WORLD] + , &requests[GET_FUNKY_CONTENT_LENGTH] + ); + + printf("request scan 3/4 "); + test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END] + , &requests[CHUNKED_W_TRAILING_HEADERS] + , &requests[CHUNKED_W_NONSENSE_AFTER_LENGTH] + ); + + printf("request scan 4/4 "); + test_scan( &requests[QUERY_URL_WITH_QUESTION_MARK_GET] + , &requests[PREFIX_NEWLINE_GET ] + , &requests[CONNECT_REQUEST] + ); + + puts("requests okay"); + + return 0; +} diff --git a/3rdparty/qthttpserver/src/3rdparty/qt_attribution.json b/3rdparty/qthttpserver/src/3rdparty/qt_attribution.json new file mode 100644 index 0000000..45f6a18 --- /dev/null +++ b/3rdparty/qthttpserver/src/3rdparty/qt_attribution.json @@ -0,0 +1,12 @@ +{ + "Id": "http-parser", + "Name": "HTTP request/response parser for C", + "QDocModule": "qthttpserver", + "QtUsage": "Used in Qt HttpServer.", + "Version": "2.9.0" + + "License": "MIT License", + "LicenseId": "MIT", + "LicenseFile": "http-parser/LICENSE-MIT", + "Copyright": "Copyright Joyent, Inc. and other Node contributors. All rights reserved." +} diff --git a/3rdparty/qthttpserver/src/CMakeLists.txt b/3rdparty/qthttpserver/src/CMakeLists.txt new file mode 100644 index 0000000..588815f --- /dev/null +++ b/3rdparty/qthttpserver/src/CMakeLists.txt @@ -0,0 +1,6 @@ +# Generated from src.pro. + +if(QT_FEATURE_ssl) + add_subdirectory(sslserver) +endif() +add_subdirectory(httpserver) diff --git a/3rdparty/qthttpserver/src/httpserver/CMakeLists.txt b/3rdparty/qthttpserver/src/httpserver/CMakeLists.txt new file mode 100644 index 0000000..a552ca2 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/CMakeLists.txt @@ -0,0 +1,56 @@ +# Generated from httpserver.pro. + +##################################################################### +## HttpServer Module: +##################################################################### + +qt_add_module(HttpServer + SOURCES + ../3rdparty/http-parser/http_parser.c ../3rdparty/http-parser/http_parser.h + qabstracthttpserver.cpp qabstracthttpserver.h qabstracthttpserver_p.h + qhttpserver.cpp qhttpserver.h qhttpserver_p.h + qhttpserverliterals.cpp qhttpserverliterals_p.h + qhttpserverrequest.cpp qhttpserverrequest.h qhttpserverrequest_p.h + qhttpserverresponder.cpp qhttpserverresponder.h qhttpserverresponder_p.h + qhttpserverresponse.cpp qhttpserverresponse.h qhttpserverresponse_p.h + qhttpserverrouter.cpp qhttpserverrouter.h qhttpserverrouter_p.h + qhttpserverrouterrule.cpp qhttpserverrouterrule.h qhttpserverrouterrule_p.h + qhttpserverrouterviewtraits.h + qhttpserverviewtraits.h + qhttpserverviewtraits_impl.h + qthttpserverglobal.h + INCLUDE_DIRECTORIES + . + ../3rdparty/http-parser + LIBRARIES + Qt::CorePrivate + PUBLIC_LIBRARIES + Qt::Core + Qt::Network + PRIVATE_MODULE_INTERFACE + Qt::CorePrivate +) + +## Scopes: +##################################################################### + +qt_extend_target(HttpServer CONDITION TARGET Qt::WebSockets + LIBRARIES + Qt::WebSocketsPrivate + PUBLIC_LIBRARIES + Qt::WebSockets + PRIVATE_MODULE_INTERFACE + Qt::WebSocketsPrivate +) + +qt_extend_target(HttpServer CONDITION QT_FEATURE_ssl + PUBLIC_LIBRARIES + Qt::SslServer +) + +qt_extend_target(HttpServer CONDITION TARGET Qt::Concurrent + SOURCES + qhttpserverfutureresponse.cpp qhttpserverfutureresponse.h + PUBLIC_LIBRARIES + Qt::Concurrent +) diff --git a/3rdparty/qthttpserver/src/httpserver/httpserver.pro b/3rdparty/qthttpserver/src/httpserver/httpserver.pro new file mode 100644 index 0000000..9e9d17a --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/httpserver.pro @@ -0,0 +1,48 @@ +TARGET = QtHttpServer +INCLUDEPATH += . + +QT = network core-private + +qtHaveModule(websockets): QT += websockets-private +qtConfig(ssl): QT += sslserver + +HEADERS += \ + qthttpserverglobal.h \ + qabstracthttpserver.h \ + qabstracthttpserver_p.h \ + qhttpserver.h \ + qhttpserver_p.h \ + qhttpserverliterals_p.h \ + qhttpserverrequest.h \ + qhttpserverrequest_p.h \ + qhttpserverresponder.h \ + qhttpserverresponder_p.h \ + qhttpserverresponse.h \ + qhttpserverresponse_p.h \ + qhttpserverrouter.h \ + qhttpserverrouter_p.h \ + qhttpserverrouterrule.h \ + qhttpserverrouterrule_p.h \ + qhttpserverrouterviewtraits.h \ + qhttpserverviewtraits.h \ + qhttpserverviewtraits_impl.h + +SOURCES += \ + qabstracthttpserver.cpp \ + qhttpserver.cpp \ + qhttpserverliterals.cpp \ + qhttpserverrequest.cpp \ + qhttpserverresponder.cpp \ + qhttpserverresponse.cpp \ + qhttpserverrouter.cpp \ + qhttpserverrouterrule.cpp + +qtHaveModule(concurrent) { + QT += concurrent + HEADERS += qhttpserverfutureresponse.h + SOURCES += qhttpserverfutureresponse.cpp +} + +include(../3rdparty/http-parser.pri) + +load(qt_module) diff --git a/3rdparty/qthttpserver/src/httpserver/qabstracthttpserver.cpp b/3rdparty/qthttpserver/src/httpserver/qabstracthttpserver.cpp new file mode 100644 index 0000000..5a6e1e5 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qabstracthttpserver.cpp @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "http_parser.h" + +#include + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcHttpServer, "qt.httpserver") + +QAbstractHttpServerPrivate::QAbstractHttpServerPrivate() +{ +} + +void QAbstractHttpServerPrivate::handleNewConnections() +{ + Q_Q(QAbstractHttpServer); + auto tcpServer = qobject_cast(q->sender()); + Q_ASSERT(tcpServer); + while (auto socket = tcpServer->nextPendingConnection()) { + auto request = new QHttpServerRequest(socket->peerAddress()); // TODO own tcp server could pre-allocate it + QObject::connect(socket, &QTcpSocket::readyRead, q_ptr, + [this, request, socket] () { + handleReadyRead(socket, request); + }); + + QObject::connect(socket, &QTcpSocket::disconnected, socket, [request, socket] () { + if (!request->d->handling) + socket->deleteLater(); + }); + + QObject::connect(socket, &QObject::destroyed, socket, [request] () { + delete request; + }); + } +} + +void QAbstractHttpServerPrivate::handleReadyRead(QTcpSocket *socket, + QHttpServerRequest *request) +{ + Q_Q(QAbstractHttpServer); + Q_ASSERT(socket); + Q_ASSERT(request); + + if (!socket->isTransactionStarted()) + socket->startTransaction(); + + if (request->d->state == QHttpServerRequestPrivate::State::OnMessageComplete) + request->d->clear(); + + if (!request->d->parse(socket)) { + socket->disconnect(); + return; + } + + if (!request->d->httpParser.upgrade && + request->d->state != QHttpServerRequestPrivate::State::OnMessageComplete) + return; // Partial read + + if (request->d->httpParser.upgrade && + request->d->httpParser.method != HTTP_CONNECT) { // Upgrade + const auto &upgradeValue = request->value(QByteArrayLiteral("upgrade")); +#if defined(QT_WEBSOCKETS_LIB) + if (upgradeValue.compare(QByteArrayLiteral("websocket"), Qt::CaseInsensitive) == 0) { + static const auto signal = QMetaMethod::fromSignal( + &QAbstractHttpServer::newWebSocketConnection); + if (q->isSignalConnected(signal)) { + QObject::disconnect(socket, &QTcpSocket::readyRead, nullptr, nullptr); + socket->rollbackTransaction(); + websocketServer.handleConnection(socket); + Q_EMIT socket->readyRead(); + } else { + qWarning(lcHttpServer, "WebSocket received but no slots connected to " + "QWebSocketServer::newConnection"); + socket->disconnectFromHost(); + } + return; + } +#endif + qCWarning(lcHttpServer, "Upgrade to %s not supported", upgradeValue.constData()); + socket->disconnectFromHost(); + return; + } + + socket->commitTransaction(); + request->d->handling = true; + if (!q->handleRequest(*request, socket)) + Q_EMIT q->missingHandler(*request, socket); + request->d->handling = false; + if (socket->state() == QAbstractSocket::UnconnectedState) + socket->deleteLater(); +} + +QAbstractHttpServer::QAbstractHttpServer(QObject *parent) + : QAbstractHttpServer(*new QAbstractHttpServerPrivate, parent) +{} + +QAbstractHttpServer::QAbstractHttpServer(QAbstractHttpServerPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +#if defined(QT_WEBSOCKETS_LIB) + Q_D(QAbstractHttpServer); + connect(&d->websocketServer, &QWebSocketServer::newConnection, + this, &QAbstractHttpServer::newWebSocketConnection); +#endif +} + +/*! + Tries to bind a \c QTcpServer to \a address and \a port. + + Returns the server port upon success, 0 otherwise. +*/ +quint16 QAbstractHttpServer::listen(const QHostAddress &address, quint16 port) +{ +#if QT_CONFIG(ssl) + Q_D(QAbstractHttpServer); + QTcpServer *tcpServer = d->sslEnabled ? new QSslServer(d->sslConfiguration, this) + : new QTcpServer(this); +#else + auto tcpServer = new QTcpServer(this); +#endif + const auto listening = tcpServer->listen(address, port); + if (listening) { + bind(tcpServer); + return tcpServer->serverPort(); + } else { + qCCritical(lcHttpServer, "listen failed: %s", + tcpServer->errorString().toStdString().c_str()); + } + + delete tcpServer; + return 0; +} + +/*! + Returns the list of ports this instance of QAbstractHttpServer + is listening to. + + This function has the same guarantee as QObject::children, + the latest server added is the last entry in the vector. + + \sa servers() +*/ +QVector QAbstractHttpServer::serverPorts() +{ + QVector ports; + auto children = findChildren(); + ports.reserve(children.count()); + std::transform(children.cbegin(), children.cend(), std::back_inserter(ports), + [](const QTcpServer *server) { return server->serverPort(); }); + return ports; +} + +/*! + Bind the HTTP server to given TCP \a server over which + the transmission happens. It is possible to call this function + multiple times with different instances of TCP \a server to + handle multiple connections and ports, for example both SSL and + non-encrypted connections. + + After calling this function, every _new_ connection will be + handled and forwarded by the HTTP server. + + It is the user's responsibility to call QTcpServer::listen() on + the \a server. + + If the \a server is null, then a new, default-constructed TCP + server will be constructed, which will be listening on a random + port and all interfaces. + + The \a server will be parented to this HTTP server. + + \sa QTcpServer, QTcpServer::listen() +*/ +void QAbstractHttpServer::bind(QTcpServer *server) +{ + Q_D(QAbstractHttpServer); + if (!server) { + server = new QTcpServer(this); + if (!server->listen()) { + qCCritical(lcHttpServer, "QTcpServer listen failed (%s)", + qPrintable(server->errorString())); + } + } else { + if (!server->isListening()) + qCWarning(lcHttpServer) << "The TCP server" << server << "is not listening."; + server->setParent(this); + } + QObjectPrivate::connect(server, &QTcpServer::newConnection, + d, &QAbstractHttpServerPrivate::handleNewConnections, + Qt::UniqueConnection); +} + +/*! + Returns list of child TCP servers of this HTTP server. + + \sa serverPorts() + */ +QVector QAbstractHttpServer::servers() const +{ + return findChildren().toVector(); +} + +#if defined(QT_WEBSOCKETS_LIB) +/*! + \fn QAbstractHttpServer::newConnection + This signal is emitted every time a new WebSocket connection is + available. + + \sa hasPendingWebSocketConnections(), nextPendingWebSocketConnection() +*/ + +/*! + Returns \c true if the server has pending WebSocket connections; + otherwise returns \c false. + + \sa newWebSocketConnection(), nextPendingWebSocketConnection() +*/ +bool QAbstractHttpServer::hasPendingWebSocketConnections() const +{ + Q_D(const QAbstractHttpServer); + return d->websocketServer.hasPendingConnections(); +} + +/*! + Returns the next pending connection as a connected QWebSocket + object. QAbstractHttpServer does not take ownership of the + returned QWebSocket object. It is up to the caller to delete the + object explicitly when it will no longer be used, otherwise a + memory leak will occur. \c nullptr is returned if this function + is called when there are no pending connections. + + \note The returned QWebSocket object cannot be used from another + thread. + + \sa newWebSocketConnection(), hasPendingWebSocketConnections() +*/ +QWebSocket *QAbstractHttpServer::nextPendingWebSocketConnection() +{ + Q_D(QAbstractHttpServer); + return d->websocketServer.nextPendingConnection(); +} +#endif + +QHttpServerResponder QAbstractHttpServer::makeResponder(const QHttpServerRequest &request, + QTcpSocket *socket) +{ + return QHttpServerResponder(request, socket); +} + +#if QT_CONFIG(ssl) +void QAbstractHttpServer::sslSetup(const QSslCertificate &certificate, + const QSslKey &privateKey, + QSsl::SslProtocol protocol) +{ + QSslConfiguration conf; + conf.setLocalCertificate(certificate); + conf.setPrivateKey(privateKey); + conf.setProtocol(protocol); + sslSetup(conf); +} + +void QAbstractHttpServer::sslSetup(const QSslConfiguration &sslConfiguration) +{ + Q_D(QAbstractHttpServer); + d->sslConfiguration = sslConfiguration; + d->sslEnabled = true; +} +#endif + +QT_END_NAMESPACE diff --git a/3rdparty/qthttpserver/src/httpserver/qabstracthttpserver.h b/3rdparty/qthttpserver/src/httpserver/qabstracthttpserver.h new file mode 100644 index 0000000..df0af73 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qabstracthttpserver.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTHTTPSERVER_H +#define QABSTRACTHTTPSERVER_H + +#include + +#include + +#include + +#if QT_CONFIG(ssl) +#include +#include +#include +#endif + +QT_BEGIN_NAMESPACE + +class QHttpServerRequest; +class QHttpServerResponder; +class QTcpServer; +class QTcpSocket; +class QWebSocket; + +class QAbstractHttpServerPrivate; +class Q_HTTPSERVER_EXPORT QAbstractHttpServer : public QObject +{ + Q_OBJECT + +public: + QAbstractHttpServer(QObject *parent = nullptr); + + quint16 listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0); + QVector serverPorts(); + + void bind(QTcpServer *server = nullptr); + QVector servers() const; + +#if QT_CONFIG(ssl) + void sslSetup(const QSslCertificate &certificate, const QSslKey &privateKey, + QSsl::SslProtocol protocol = QSsl::SecureProtocols); + void sslSetup(const QSslConfiguration &sslConfiguration); +#endif + +Q_SIGNALS: + void missingHandler(const QHttpServerRequest &request, QTcpSocket *socket); + +#if defined(QT_WEBSOCKETS_LIB) + void newWebSocketConnection(); + +public: + bool hasPendingWebSocketConnections() const; + QWebSocket *nextPendingWebSocketConnection(); +#endif // defined(QT_WEBSOCKETS_LIB) + +protected: + QAbstractHttpServer(QAbstractHttpServerPrivate &dd, QObject *parent = nullptr); + + virtual bool handleRequest(const QHttpServerRequest &request, QTcpSocket *socket) = 0; + static QHttpServerResponder makeResponder(const QHttpServerRequest &request, + QTcpSocket *socket); + +private: + Q_DECLARE_PRIVATE(QAbstractHttpServer) +}; + +QT_END_NAMESPACE + +#endif // QABSTRACTHTTPSERVER_H diff --git a/3rdparty/qthttpserver/src/httpserver/qabstracthttpserver_p.h b/3rdparty/qthttpserver/src/httpserver/qabstracthttpserver_p.h new file mode 100644 index 0000000..2c02b19 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qabstracthttpserver_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTHTTPSERVER_P_H +#define QABSTRACTHTTPSERVER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QHttpServer. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#include +#include + +#include + +#if defined(QT_WEBSOCKETS_LIB) +#include +#endif // defined(QT_WEBSOCKETS_LIB) + +QT_BEGIN_NAMESPACE + +class QHttpServerRequest; + +class Q_HTTPSERVER_EXPORT QAbstractHttpServerPrivate: public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QAbstractHttpServer) + +public: + QAbstractHttpServerPrivate(); + +#if defined(QT_WEBSOCKETS_LIB) + QWebSocketServer websocketServer { + QLatin1String("QtHttpServer"), + QWebSocketServer::NonSecureMode + }; +#endif // defined(QT_WEBSOCKETS_LIB) + + void handleNewConnections(); + void handleReadyRead(QTcpSocket *socket, + QHttpServerRequest *request); + +#if QT_CONFIG(ssl) + QSslConfiguration sslConfiguration; + bool sslEnabled = false; +#endif +}; + +QT_END_NAMESPACE + +#endif // QABSTRACTHTTPSERVER_P_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserver.cpp b/3rdparty/qthttpserver/src/httpserver/qhttpserver.cpp new file mode 100644 index 0000000..fb0b67b --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserver.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + + +#include +#include +#include + +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcHS, "qt.httpserver"); + +/*! + \class QHttpServer + \brief QHttpServer is a simplified API for QAbstractHttpServer and QHttpServerRouter. + + \code + + QHttpServer server; + + server.route("/", [] () { + return "hello world"; + }); + server.listen(); + + \endcode +*/ + +QHttpServer::QHttpServer(QObject *parent) + : QAbstractHttpServer(*new QHttpServerPrivate, parent) +{ + connect(this, &QAbstractHttpServer::missingHandler, this, + [=] (const QHttpServerRequest &request, QTcpSocket *socket) { + qCDebug(lcHS) << tr("missing handler:") << request.url().path(); + sendResponse(QHttpServerResponder::StatusCode::NotFound, request, socket); + }); +} + +/*! \fn template bool route(Args && ... args) + This function is just a wrapper to simplify the router API. + + This function takes variadic arguments. The last argument is \c a callback (ViewHandler). + The remaining arguments are used to create a new \a Rule (the default is QHttpServerRouterRule). + This is in turn added to the QHttpServerRouter. + + \c ViewHandler can only be a lambda. The lambda definition can take two optional special + arguments: \c {const QHttpServerRequest&} and \c {QHttpServerResponder&&}. + These special arguments must be the last in the parameter list. + + Examples: + + \code + + QHttpServer server; + + // Valid: + server.route("test", [] (const int page) { return ""; }); + server.route("test", [] (const int page, const QHttpServerRequest &request) { return ""; }); + server.route("test", [] (QHttpServerResponder &&responder) { return ""; }); + + // Invalid (compile time error): + server.route("test", [] (const QHttpServerRequest &request, const int page) { return ""; }); // request must be last + server.route("test", [] (QHttpServerRequest &request) { return ""; }); // request must be passed by const reference + server.route("test", [] (QHttpServerResponder &responder) { return ""; }); // responder must be passed by universal reference + + \endcode + + \sa QHttpServerRouter::addRule +*/ + +/*! \fn template void afterRequest(ViewHandler &&viewHandler) + Register a function to be run after each request. + + \c ViewHandler can only be a lambda. The lambda definition can take two + arguments: \c {QHttpServerResponse &&} and \c {const QHttpServerRequest&} (optional). + + Examples: + + \code + + QHttpServer server; + + // Valid: + server.afterRequest([] (QHttpServerResponse &&resp, const QHttpServerRequest &request) { + return std::move(resp); + } + server.afterRequest([] (const QHttpServerRequest &request, QHttpServerResponse &&resp) { + return std::move(resp); + } + server.afterRequest([] (QHttpServerResponse &&resp) { return std::move(resp); } + + // Invalid (compile time error): + // resp must be passed by universal reference + server.afterRequest([] (QHttpServerResponse &resp, const QHttpServerRequest &request) { + return std::move(resp); + } + // request must be passed by const reference + server.afterRequest([] (QHttpServerResponse &&resp, QHttpServerRequest &request) { + return std::move(resp); + } + + \endcode +*/ + +/*! + Destroys a QHttpServer. +*/ +QHttpServer::~QHttpServer() +{ +} + +/*! + Returns the router object. +*/ +QHttpServerRouter *QHttpServer::router() +{ + Q_D(QHttpServer); + return &d->router; +} + +void QHttpServer::afterRequestImpl(AfterRequestHandler &&afterRequestHandler) +{ + Q_D(QHttpServer); + d->afterRequestHandlers.push_back(std::move(afterRequestHandler)); +} + +/*! + \internal +*/ +void QHttpServer::sendResponse(QHttpServerResponse &&response, + const QHttpServerRequest &request, + QTcpSocket *socket) +{ + Q_D(QHttpServer); + for (auto afterRequestHandler : d->afterRequestHandlers) + response = std::move(afterRequestHandler(std::move(response), request)); + response.write(makeResponder(request, socket)); +} + +/*! + \internal +*/ +bool QHttpServer::handleRequest(const QHttpServerRequest &request, QTcpSocket *socket) +{ + Q_D(QHttpServer); + return d->router.handleRequest(request, socket); +} + +QT_END_NAMESPACE diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserver.h b/3rdparty/qthttpserver/src/httpserver/qhttpserver.h new file mode 100644 index 0000000..5274d0f --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserver.h @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Mikhail Svetkin +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVER_H +#define QHTTPSERVER_H + +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QTcpSocket; +class QHttpServerRequest; + +class QHttpServerPrivate; +class Q_HTTPSERVER_EXPORT QHttpServer final : public QAbstractHttpServer +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QHttpServer) + + template + struct VariadicTypeAt { using Type = typename std::tuple_element>::type; }; + + template + struct VariadicTypeLast { + using Type = typename VariadicTypeAt::Type; + }; + + + template + using ResponseType = + typename std::conditional< + std::is_base_of::value, + T, + QHttpServerResponse + >::type; + +public: + explicit QHttpServer(QObject *parent = nullptr); + ~QHttpServer(); + + QHttpServerRouter *router(); + + template + bool route(Args && ... args) + { + using ViewHandler = typename VariadicTypeLast::Type; + using ViewTraits = QHttpServerRouterViewTraits; + static_assert(ViewTraits::Arguments::StaticAssert, + "ViewHandler arguments are in the wrong order or not supported"); + return routeHelper( + QtPrivate::makeIndexSequence{}, + std::forward(args)...); + } + + template + void afterRequest(ViewHandler &&viewHandler) + { + using ViewTraits = QHttpServerAfterRequestViewTraits; + static_assert(ViewTraits::Arguments::StaticAssert, + "ViewHandler arguments are in the wrong order or not supported"); + afterRequestHelper(std::move(viewHandler)); + } + + using AfterRequestHandler = + std::function; +private: + template + typename std::enable_if::type + afterRequestHelper(ViewHandler &&viewHandler) { + auto handler = [viewHandler](QHttpServerResponse &&resp, + const QHttpServerRequest &request) { + return std::move(viewHandler(std::move(resp), request)); + }; + + afterRequestImpl(std::move(handler)); + } + + template + typename std::enable_if::type + afterRequestHelper(ViewHandler &&viewHandler) { + auto handler = [viewHandler](QHttpServerResponse &&resp, + const QHttpServerRequest &) { + return std::move(viewHandler(std::move(resp))); + }; + + afterRequestImpl(std::move(handler)); + } + + template + typename std::enable_if::type + afterRequestHelper(ViewHandler &&viewHandler) { + auto handler = [viewHandler](QHttpServerResponse &&resp, + const QHttpServerRequest &request) { + return std::move(viewHandler(request, std::move(resp))); + }; + + afterRequestImpl(std::move(handler)); + } + + void afterRequestImpl(AfterRequestHandler &&afterRequestHandler); + +private: + template + bool routeHelper(QtPrivate::IndexesList, Args &&... args) + { + return routeImpl::Type...>(std::forward(args)...); + } + + template + bool routeImpl(Args &&...args, ViewHandler &&viewHandler) + { + auto routerHandler = [this, viewHandler] ( + const QRegularExpressionMatch &match, + const QHttpServerRequest &request, + QTcpSocket *socket) mutable { + auto boundViewHandler = router()->bindCaptured( + std::move(viewHandler), match); + responseImpl(boundViewHandler, request, socket); + }; + + return router()->addRule( + new Rule(std::forward(args)..., std::move(routerHandler))); + } + + template + typename std::enable_if::type + responseImpl(T &boundViewHandler, + const QHttpServerRequest &request, + QTcpSocket *socket) + { + ResponseType response(boundViewHandler()); + sendResponse(std::move(response), request, socket); + } + + template + typename std::enable_if::type + responseImpl(T &boundViewHandler, const QHttpServerRequest &request, QTcpSocket *socket) + { + ResponseType response(boundViewHandler(request)); + sendResponse(std::move(response), request, socket); + } + + template + typename std::enable_if::type + responseImpl(T &boundViewHandler, const QHttpServerRequest &request, QTcpSocket *socket) + { + boundViewHandler(makeResponder(request, socket), request); + } + + template + typename std::enable_if::type + responseImpl(T &boundViewHandler, + const QHttpServerRequest &request, + QTcpSocket *socket) + { + boundViewHandler(request, makeResponder(request, socket)); + } + + template + typename std::enable_if::type + responseImpl(T &boundViewHandler, + const QHttpServerRequest &request, + QTcpSocket *socket) + { + boundViewHandler(makeResponder(request, socket)); + } + + bool handleRequest(const QHttpServerRequest &request, QTcpSocket *socket) override final; + + void sendResponse(QHttpServerResponse &&response, + const QHttpServerRequest &request, + QTcpSocket *socket); +}; + +QT_END_NAMESPACE + +#endif // QHTTPSERVER_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserver_p.h b/3rdparty/qthttpserver/src/httpserver/qhttpserver_p.h new file mode 100644 index 0000000..eb39a9e --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserver_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVER_P_H +#define QHTTPSERVER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QHttpServer. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#include + +#include +#include +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QHttpServerPrivate: public QAbstractHttpServerPrivate +{ + Q_DECLARE_PUBLIC(QHttpServer) + +public: + QHttpServerPrivate() = default; + + QHttpServerRouter router; + std::list afterRequestHandlers; +}; + +QT_END_NAMESPACE + +#endif // QHTTPSERVER_P_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverfutureresponse.cpp b/3rdparty/qthttpserver/src/httpserver/qhttpserverfutureresponse.cpp new file mode 100644 index 0000000..2409e6a --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverfutureresponse.cpp @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Mikhail Svetkin +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhttpserverfutureresponse.h" + +#include +#include + +#include + +#include + +#include + + +QT_BEGIN_NAMESPACE + +/*! + \class QHttpServerFutureResponse + \brief QHttpServerFutureResponse is a simplified API for asynchronous responses. + + \code + + QHttpServer server; + + server.route("/feature/", [] (int id) -> QHttpServerFutureResponse { + auto future = QtConcurrent::run([] () { + return QHttpServerResponse("the future is coming"); + }); + + return future; + }); + server.listen(); + + \endcode +*/ + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + +QHttpServerResponse QFutureInterface::takeResult() +{ + if (isCanceled()) { + exceptionStore().throwPossibleException(); + return QHttpServerResponse::StatusCode::NotFound; + } + // Note: we wait for all, this is intentional, + // not to mess with other unready results. + waitForResult(-1); + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + std::lock_guard locker{*mutex()}; +#else + std::lock_guard locker{mutex(0)}; +#endif + QtPrivate::ResultIteratorBase position = resultStoreBase().resultAt(0); + auto ret = std::move_if_noexcept( + *const_cast(position.pointer())); + resultStoreBase().template clear(); + + return ret; +} + +#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + +struct QResponseWatcher : public QFutureWatcher +{ + Q_OBJECT + +public: + QResponseWatcher(QHttpServerResponder &&_responder) + : QFutureWatcher(), + responder(std::move(_responder)) { + } + + QHttpServerResponder responder; +}; + +class QHttpServerFutureResponsePrivate : public QHttpServerResponsePrivate +{ +public: + QHttpServerFutureResponsePrivate(const QFuture &futureResponse) + : QHttpServerResponsePrivate(), + futureResp(futureResponse) + { + } + + QFuture futureResp; +}; + +/*! + Constructs a new QHttpServerFutureResponse with the \a future response. +*/ +QHttpServerFutureResponse::QHttpServerFutureResponse(const QFuture &futureResp) + : QHttpServerFutureResponse(new QHttpServerFutureResponsePrivate{futureResp}) +{ +} + +/*! + \internal +*/ +QHttpServerFutureResponse::QHttpServerFutureResponse(QHttpServerFutureResponsePrivate *d) + : QHttpServerResponse(d) +{ +} + +/*! + \reimp +*/ +void QHttpServerFutureResponse::write(QHttpServerResponder &&responder) const +{ + if (!d_ptr->derived) { + QHttpServerResponse::write(std::move(responder)); + return; + } + + Q_D(const QHttpServerFutureResponse); + + auto socket = responder.socket(); + auto futureWatcher = new QResponseWatcher(std::move(responder)); + + QObject::connect(socket, &QObject::destroyed, + futureWatcher, &QObject::deleteLater); + QObject::connect(futureWatcher, &QFutureWatcherBase::finished, + socket, + [futureWatcher] () mutable { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + auto resp = futureWatcher->future().d.takeResult(); +#else + auto resp = futureWatcher->future().takeResult(); +#endif + resp.write(std::move(futureWatcher->responder)); + futureWatcher->deleteLater(); + }); + + futureWatcher->setFuture(d->futureResp); +} + +QT_END_NAMESPACE + +#include "qhttpserverfutureresponse.moc" diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverfutureresponse.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverfutureresponse.h new file mode 100644 index 0000000..d5c8fe1 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverfutureresponse.h @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Mikhail Svetkin +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERFUTURERESPONSE_H +#define QHTTPSERVERFUTURERESPONSE_H + +#include + +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + +template <> +class QFutureInterface : public QFutureInterfaceBase +{ +public: + QFutureInterface(State initialState = NoState) + : QFutureInterfaceBase(initialState) + { + refT(); + } + QFutureInterface(const QFutureInterface &other) + : QFutureInterfaceBase(other) + { + refT(); + } + ~QFutureInterface() + { + if (!derefT()) + resultStoreBase().template clear(); + } + + static QFutureInterface canceledResult() + { return QFutureInterface(State(Started | Finished | Canceled)); } + + QFutureInterface &operator=(const QFutureInterface &other) + { + other.refT(); + if (!derefT()) + resultStoreBase().template clear(); + QFutureInterfaceBase::operator=(other); + return *this; + } + + inline QFuture future() + { + return QFuture(this); + } + + void reportAndMoveResult(QHttpServerResponse &&result, int index = -1) + { +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + std::lock_guard locker{*mutex()}; +#else + std::lock_guard locker{mutex(0)}; +#endif + if (queryState(Canceled) || queryState(Finished)) + return; + + QtPrivate::ResultStoreBase &store = resultStoreBase(); + + const int oldResultCount = store.count(); + const int insertIndex = store.addResult( + index, static_cast(new QHttpServerResponse(std::move_if_noexcept(result)))); + if (!store.filterMode() || oldResultCount < store.count()) // Let's make sure it's not in pending results. + reportResultsReady(insertIndex, store.count()); + } + + void reportFinished() + { + QFutureInterfaceBase::reportFinished(); + } + + QHttpServerResponse takeResult(); +}; + +#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + +namespace QtConcurrent { + +template <> +class RunFunctionTask : public RunFunctionTaskBase +{ +public: + void run() override + { + if (this->isCanceled()) { + this->reportFinished(); + return; + } +#ifndef QT_NO_EXCEPTIONS + try { +#endif + this->runFunctor(); +#ifndef QT_NO_EXCEPTIONS + } catch (QException &e) { + QFutureInterface::reportException(e); + } catch (...) { + QFutureInterface::reportException(QUnhandledException()); + } +#endif + this->reportAndMoveResult(std::move_if_noexcept(result)); + this->reportFinished(); + } + + QHttpServerResponse result{QHttpServerResponse::StatusCode::NotFound}; +}; + +} + +class QHttpServerFutureResponsePrivate; +class Q_HTTPSERVER_EXPORT QHttpServerFutureResponse : public QHttpServerResponse +{ + Q_DECLARE_PRIVATE(QHttpServerFutureResponse) + +public: + using QHttpServerResponse::QHttpServerResponse; + + QHttpServerFutureResponse(const QFuture &futureResponse); + + virtual void write(QHttpServerResponder &&responder) const override; + +protected: + QHttpServerFutureResponse(QHttpServerFutureResponsePrivate *d); +}; + +QT_END_NAMESPACE + +#endif // QHTTPSERVERFUTURERESPONSE_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverliterals.cpp b/3rdparty/qthttpserver/src/httpserver/qhttpserverliterals.cpp new file mode 100644 index 0000000..3fc7627 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverliterals.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Mikhail Svetkin +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhttpserverliterals_p.h" + +QT_BEGIN_NAMESPACE + +QByteArray QHttpServerLiterals::contentTypeHeader() +{ + return QByteArrayLiteral("Content-Type"); +} + +QByteArray QHttpServerLiterals::contentTypeXEmpty() +{ + return QByteArrayLiteral("application/x-empty"); +} + +QByteArray QHttpServerLiterals::contentTypeTextHtml() +{ + return QByteArrayLiteral("text/html"); +} + +QByteArray QHttpServerLiterals::contentTypeJson() +{ + return QByteArrayLiteral("application/json"); +} + +QByteArray QHttpServerLiterals::contentLengthHeader() +{ + return QByteArrayLiteral("Content-Length"); +} + +QT_END_NAMESPACE diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverliterals_p.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverliterals_p.h new file mode 100644 index 0000000..a6ac05a --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverliterals_p.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Mikhail Svetkin +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERLITERALS_P_H +#define QHTTPSERVERLITERALS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QHttpServer. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#include + +#include + +QT_BEGIN_NAMESPACE + +class Q_HTTPSERVER_EXPORT QHttpServerLiterals +{ + +public: + static QByteArray contentTypeHeader(); + static QByteArray contentTypeXEmpty(); + static QByteArray contentTypeTextHtml(); + static QByteArray contentTypeJson(); + static QByteArray contentLengthHeader(); +}; + +QT_END_NAMESPACE + +#endif // QHTTPSERVERLITERALS_P_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverrequest.cpp b/3rdparty/qthttpserver/src/httpserver/qhttpserverrequest.cpp new file mode 100644 index 0000000..5c0ac5f --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverrequest.cpp @@ -0,0 +1,319 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhttpserverrequest_p.h" + +#include + +#include +#include +#include +#if QT_CONFIG(ssl) +#include +#endif + +#include + +Q_LOGGING_CATEGORY(lc, "qt.httpserver.request") + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_DEBUG_STREAM) +Q_HTTPSERVER_EXPORT QDebug operator<<(QDebug debug, const QHttpServerRequest &request) +{ + const auto oldSetting = debug.autoInsertSpaces(); + debug.nospace() << "QHttpServerRequest("; + debug << "(Url: " << request.url() << ")"; + debug << "(Headers: " << request.headers() << ")"; + debug << ')'; + debug.setAutoInsertSpaces(oldSetting); + return debug.maybeSpace(); +} + +QDebug operator<<(QDebug debug, const http_parser *const httpParser) +{ + const auto oldSetting = debug.autoInsertSpaces(); + debug.nospace() << "http_parser(" << static_cast(httpParser) << ": "; + debug << "HTTP " << httpParser->http_major << "." << httpParser->http_minor << " " + << http_method_str(http_method(httpParser->method)) << ')'; + debug.setAutoInsertSpaces(oldSetting); + return debug.maybeSpace(); +} +#endif + +static const std::array parseUrlFunctions { + [](const QString &string, QUrl *url) { url->setScheme(string); }, + [](const QString &string, QUrl *url) { url->setHost(string); }, + [](const QString &string, QUrl *url) { url->setPort(string.toInt()); }, + [](const QString &string, QUrl *url) { url->setPath(string, QUrl::TolerantMode); }, + [](const QString &string, QUrl *url) { url->setQuery(string); }, + [](const QString &string, QUrl *url) { url->setFragment(string); }, + [](const QString &string, QUrl *url) { url->setUserInfo(string); }, +}; + +http_parser_settings QHttpServerRequestPrivate::httpParserSettings { + &QHttpServerRequestPrivate::onMessageBegin, + &QHttpServerRequestPrivate::onUrl, + &QHttpServerRequestPrivate::onStatus, + &QHttpServerRequestPrivate::onHeaderField, + &QHttpServerRequestPrivate::onHeaderValue, + &QHttpServerRequestPrivate::onHeadersComplete, + &QHttpServerRequestPrivate::onBody, + &QHttpServerRequestPrivate::onMessageComplete, + &QHttpServerRequestPrivate::onChunkHeader, + &QHttpServerRequestPrivate::onChunkComplete +}; + +QHttpServerRequestPrivate::QHttpServerRequestPrivate(const QHostAddress &remoteAddress) + : remoteAddress(remoteAddress) +{ + httpParser.data = this; + http_parser_init(&httpParser, HTTP_REQUEST); +} + +QByteArray QHttpServerRequestPrivate::header(const QByteArray &key) const +{ + return headers.value(headerHash(key)).second; +} + +bool QHttpServerRequestPrivate::parse(QIODevice *socket) +{ + const auto fragment = socket->readAll(); + if (fragment.size()) { +#if QT_CONFIG(ssl) + auto sslSocket = qobject_cast(socket); + url.setScheme(sslSocket && sslSocket->isEncrypted() ? QStringLiteral("https") + : QStringLiteral("http")); +#else + url.setScheme(QStringLiteral("http")); +#endif + const auto parsed = http_parser_execute(&httpParser, + &httpParserSettings, + fragment.constData(), + size_t(fragment.size())); + if (int(parsed) < fragment.size()) { + qCDebug(lc, "Parse error: %d", httpParser.http_errno); + return false; + } + } + return true; +} + +uint QHttpServerRequestPrivate::headerHash(const QByteArray &key) const +{ + return qHash(key.toLower(), headersSeed); +} + +void QHttpServerRequestPrivate::clear() +{ + url.clear(); + lastHeader.clear(); + headers.clear(); + body.clear(); +} + +bool QHttpServerRequestPrivate::parseUrl(const char *at, size_t length, bool connect, QUrl *url) +{ + struct http_parser_url u; + if (http_parser_parse_url(at, length, connect ? 1 : 0, &u) != 0) + return false; + + for (auto i = 0u; i < UF_MAX; i++) { + if (u.field_set & (1 << i)) { + parseUrlFunctions[i](QString::fromUtf8(at + u.field_data[i].off, + u.field_data[i].len), + url); + } + } + return true; +} + +QHttpServerRequestPrivate *QHttpServerRequestPrivate::instance(http_parser *httpParser) +{ + return static_cast(httpParser->data); +} + +int QHttpServerRequestPrivate::onMessageBegin(http_parser *httpParser) +{ + qCDebug(lc) << static_cast(httpParser); + instance(httpParser)->state = State::OnMessageBegin; + return 0; +} + +int QHttpServerRequestPrivate::onUrl(http_parser *httpParser, const char *at, size_t length) +{ + qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length)); + auto instance = static_cast(httpParser->data); + instance->state = State::OnUrl; + parseUrl(at, length, false, &instance->url); + return 0; +} + +int QHttpServerRequestPrivate::onStatus(http_parser *httpParser, const char *at, size_t length) +{ + qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length)); + instance(httpParser)->state = State::OnStatus; + return 0; +} + +int QHttpServerRequestPrivate::onHeaderField(http_parser *httpParser, const char *at, size_t length) +{ + qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length)); + auto i = instance(httpParser); + i->state = State::OnHeaders; + const auto key = QByteArray(at, int(length)); + i->headers.insert(i->headerHash(key), qMakePair(key, QByteArray())); + i->lastHeader = key; + return 0; +} + +int QHttpServerRequestPrivate::onHeaderValue(http_parser *httpParser, const char *at, size_t length) +{ + qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length)); + auto i = instance(httpParser); + i->state = State::OnHeaders; + Q_ASSERT(!i->lastHeader.isEmpty()); + const auto value = QByteArray(at, int(length)); + i->headers[i->headerHash(i->lastHeader)] = qMakePair(i->lastHeader, value); + if (i->lastHeader.compare(QByteArrayLiteral("host"), Qt::CaseInsensitive) == 0) + parseUrl(at, length, true, &i->url); +#if defined(QT_DEBUG) + i->lastHeader.clear(); +#endif + return 0; +} + +int QHttpServerRequestPrivate::onHeadersComplete(http_parser *httpParser) +{ + qCDebug(lc) << httpParser; + instance(httpParser)->state = State::OnHeadersComplete; + return 0; +} + +int QHttpServerRequestPrivate::onBody(http_parser *httpParser, const char *at, size_t length) +{ + qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length)); + auto i = instance(httpParser); + i->state = State::OnBody; + if (i->body.isEmpty()) { + i->body.reserve( + static_cast(httpParser->content_length) + + static_cast(length)); + } + + i->body.append(at, int(length)); + return 0; +} + +int QHttpServerRequestPrivate::onMessageComplete(http_parser *httpParser) +{ + qCDebug(lc) << httpParser; + instance(httpParser)->state = State::OnMessageComplete; + return 0; +} + +int QHttpServerRequestPrivate::onChunkHeader(http_parser *httpParser) +{ + qCDebug(lc) << httpParser; + instance(httpParser)->state = State::OnChunkHeader; + return 0; +} + +int QHttpServerRequestPrivate::onChunkComplete(http_parser *httpParser) +{ + qCDebug(lc) << httpParser; + instance(httpParser)->state = State::OnChunkComplete; + return 0; +} + +QHttpServerRequest::QHttpServerRequest(const QHostAddress &remoteAddress) : + d(new QHttpServerRequestPrivate(remoteAddress)) +{} + +QHttpServerRequest::~QHttpServerRequest() +{} + +QByteArray QHttpServerRequest::value(const QByteArray &key) const +{ + return d->headers.value(d->headerHash(key)).second; +} + +QUrl QHttpServerRequest::url() const +{ + return d->url; +} + +QUrlQuery QHttpServerRequest::query() const +{ + return QUrlQuery(d->url.query()); +} + +QHttpServerRequest::Method QHttpServerRequest::method() const +{ + switch (d->httpParser.method) { + case HTTP_GET: + return QHttpServerRequest::Method::Get; + case HTTP_PUT: + return QHttpServerRequest::Method::Put; + case HTTP_DELETE: + return QHttpServerRequest::Method::Delete; + case HTTP_POST: + return QHttpServerRequest::Method::Post; + case HTTP_HEAD: + return QHttpServerRequest::Method::Head; + case HTTP_OPTIONS: + return QHttpServerRequest::Method::Options; + case HTTP_PATCH: + return QHttpServerRequest::Method::Patch; + case HTTP_CONNECT: + return QHttpServerRequest::Method::Connect; + default: + return QHttpServerRequest::Method::Unknown; + } +} + +QVariantMap QHttpServerRequest::headers() const +{ + QVariantMap ret; + for (auto it : d->headers) + ret.insert(QString::fromUtf8(it.first), it.second); + return ret; +} + +QByteArray QHttpServerRequest::body() const +{ + return d->body; +} + +QHostAddress QHttpServerRequest::remoteAddress() const +{ + return d->remoteAddress; +} + +QT_END_NAMESPACE diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverrequest.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverrequest.h new file mode 100644 index 0000000..4ace46a --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverrequest.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERREQUEST_H +#define QHTTPSERVERREQUEST_H + +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QRegularExpression; +class QString; +class QTcpSocket; + +class QHttpServerRequestPrivate; +class Q_HTTPSERVER_EXPORT QHttpServerRequest +{ + friend class QAbstractHttpServerPrivate; + friend class QHttpServerResponse; + + Q_GADGET + +public: + virtual ~QHttpServerRequest(); + + enum class Method + { + Unknown = 0x0000, + Get = 0x0001, + Put = 0x0002, + Delete = 0x0004, + Post = 0x0008, + Head = 0x0010, + Options = 0x0020, + Patch = 0x0040, + Connect = 0x0080, + + All = Get | Put | Delete | Post | Head | Options | Patch | Connect, + + // Include upper-case aliases for the sake of parsing from strings: + GET = Get, + PUT = Put, + DELETE = Delete, + POST = Post, + HEAD = Head, + OPTIONS = Options, + PATCH = Patch, + CONNECT = Connect + }; + Q_ENUM(Method) + Q_DECLARE_FLAGS(Methods, Method) + Q_FLAG(Methods) + + QByteArray value(const QByteArray &key) const; + QUrl url() const; + QUrlQuery query() const; + Method method() const; + QVariantMap headers() const; + QByteArray body() const; + QHostAddress remoteAddress() const; + +private: + Q_DISABLE_COPY(QHttpServerRequest) + +#if !defined(QT_NO_DEBUG_STREAM) + friend Q_HTTPSERVER_EXPORT QDebug operator<<(QDebug debug, const QHttpServerRequest &request); +#endif + + explicit QHttpServerRequest(const QHostAddress &remoteAddress); + + QScopedPointer d; +}; + +QT_END_NAMESPACE + +#endif // QHTTPSERVERREQUEST_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverrequest_p.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverrequest_p.h new file mode 100644 index 0000000..6459cc4 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverrequest_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERREQUEST_P_H +#define QHTTPSERVERREQUEST_P_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../3rdparty/http-parser/http_parser.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QHttpServer. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +QT_BEGIN_NAMESPACE + +class QHttpServerRequestPrivate : public QSharedData +{ +public: + QHttpServerRequestPrivate(const QHostAddress &remoteAddress); + + quint16 port = 0; + enum class State { + NotStarted, + OnMessageBegin, + OnUrl, + OnStatus, + OnHeaders, + OnHeadersComplete, + OnBody, + OnMessageComplete, + OnChunkHeader, + OnChunkComplete + } state = State::NotStarted; + QByteArray body; + + QUrl url; + + http_parser httpParser; + + QByteArray header(const QByteArray &key) const; + bool parse(QIODevice *socket); + + QByteArray lastHeader; + QMap> headers; + const uint headersSeed = uint(qGlobalQHashSeed()); + uint headerHash(const QByteArray &key) const; + + void clear(); + QHostAddress remoteAddress; + bool handling{false}; + +private: + static http_parser_settings httpParserSettings; + static bool parseUrl(const char *at, size_t length, bool connect, QUrl *url); + + static QHttpServerRequestPrivate *instance(http_parser *httpParser); + + static int onMessageBegin(http_parser *httpParser); + static int onUrl(http_parser *httpParser, const char *at, size_t length); + static int onStatus(http_parser *httpParser, const char *at, size_t length); + static int onHeaderField(http_parser *httpParser, const char *at, size_t length); + static int onHeaderValue(http_parser *httpParser, const char *at, size_t length); + static int onHeadersComplete(http_parser *httpParser); + static int onBody(http_parser *httpParser, const char *at, size_t length); + static int onMessageComplete(http_parser *httpParser); + static int onChunkHeader(http_parser *httpParser); + static int onChunkComplete(http_parser *httpParser); +}; + +QT_END_NAMESPACE + +#endif // QHTTPSERVERREQUEST_P_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverresponder.cpp b/3rdparty/qthttpserver/src/httpserver/qhttpserverresponder.cpp new file mode 100644 index 0000000..2cfc974 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverresponder.cpp @@ -0,0 +1,413 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../3rdparty/http-parser/http_parser.h" + +QT_BEGIN_NAMESPACE + +static const QLoggingCategory &lc() +{ + static const QLoggingCategory category("qt.httpserver.response"); + return category; +} + +// https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html +static const std::map statusString { +#define XX(num, name, string) { static_cast(num), QByteArrayLiteral(#string) }, + HTTP_STATUS_MAP(XX) +#undef XX +}; + +template +struct IOChunkedTransfer +{ + // TODO This is not the fastest implementation, as it does read & write + // in a sequential fashion, but these operation could potentially overlap. + // TODO Can we implement it without the buffer? Direct write to the target buffer + // would be great. + + const qint64 bufferSize = BUFFERSIZE; + char buffer[BUFFERSIZE]; + qint64 beginIndex = -1; + qint64 endIndex = -1; + QPointer source; + const QPointer sink; + const QMetaObject::Connection bytesWrittenConnection; + const QMetaObject::Connection readyReadConnection; + IOChunkedTransfer(QIODevice *input, QIODevice *output) : + source(input), + sink(output), + bytesWrittenConnection(QObject::connect(sink.data(), &QIODevice::bytesWritten, [this] () { + writeToOutput(); + })), + readyReadConnection(QObject::connect(source.data(), &QIODevice::readyRead, [this] () { + readFromInput(); + })) + { + Q_ASSERT(!source->atEnd()); // TODO error out + QObject::connect(sink.data(), &QObject::destroyed, source.data(), &QObject::deleteLater); + QObject::connect(source.data(), &QObject::destroyed, [this] () { + delete this; + }); + readFromInput(); + } + + ~IOChunkedTransfer() + { + QObject::disconnect(bytesWrittenConnection); + QObject::disconnect(readyReadConnection); + } + + inline bool isBufferEmpty() + { + Q_ASSERT(beginIndex <= endIndex); + return beginIndex == endIndex; + } + + void readFromInput() + { + if (!isBufferEmpty()) // We haven't consumed all the data yet. + return; + beginIndex = 0; + endIndex = source->read(buffer, bufferSize); + if (endIndex < 0) { + endIndex = beginIndex; // Mark the buffer as empty + qCWarning(lc, "Error reading chunk: %s", qPrintable(source->errorString())); + } else if (endIndex) { + memset(buffer + endIndex, 0, sizeof(buffer) - std::size_t(endIndex)); + writeToOutput(); + } + } + + void writeToOutput() + { + if (isBufferEmpty()) + return; + + const auto writtenBytes = sink->write(buffer + beginIndex, endIndex); + if (writtenBytes < 0) { + qCWarning(lc, "Error writing chunk: %s", qPrintable(sink->errorString())); + return; + } + beginIndex += writtenBytes; + if (isBufferEmpty()) { + if (source->bytesAvailable()) + QTimer::singleShot(0, source.data(), [this]() { readFromInput(); }); + else if (source->atEnd()) // Finishing + source->deleteLater(); + } + } +}; + +/*! + Constructs a QHttpServerResponder using the request \a request + and the socket \a socket. +*/ +QHttpServerResponder::QHttpServerResponder(const QHttpServerRequest &request, + QTcpSocket *socket) : + d_ptr(new QHttpServerResponderPrivate(request, socket)) +{ + Q_ASSERT(socket); +} + +/*! + Move-constructs a QHttpServerResponder instance, making it point + at the same object that \a other was pointing to. +*/ +QHttpServerResponder::QHttpServerResponder(QHttpServerResponder &&other) : + d_ptr(other.d_ptr.take()) +{} + +/*! + Destroys a QHttpServerResponder. +*/ +QHttpServerResponder::~QHttpServerResponder() +{} + +/*! + Answers a request with an HTTP status code \a status and + HTTP headers \a headers. The I/O device \a data provides the body + of the response. If \a data is sequential, the body of the + message is sent in chunks: otherwise, the function assumes all + the content is available and sends it all at once but the read + is done in chunks. + + \note This function takes the ownership of \a data. +*/ +void QHttpServerResponder::write(QIODevice *data, + HeaderList headers, + StatusCode status) +{ + Q_D(QHttpServerResponder); + Q_ASSERT(d->socket); + QScopedPointer input(data); + + input->setParent(nullptr); + if (!input->isOpen()) { + if (!input->open(QIODevice::ReadOnly)) { + // TODO Add developer error handling + qCDebug(lc, "500: Could not open device %s", qPrintable(input->errorString())); + write(StatusCode::InternalServerError); + return; + } + } else if (!(input->openMode() & QIODevice::ReadOnly)) { + // TODO Add developer error handling + qCDebug(lc) << "500: Device is opened in a wrong mode" << input->openMode(); + write(StatusCode::InternalServerError); + return; + } + + if (!d->socket->isOpen()) { + qCWarning(lc, "Cannot write to socket. It's disconnected"); + return; + } + + writeStatusLine(status); + + if (!input->isSequential()) { // Non-sequential QIODevice should know its data size + writeHeader(QHttpServerLiterals::contentLengthHeader(), + QByteArray::number(input->size())); + } + + for (auto &&header : headers) + writeHeader(header.first, header.second); + + d->socket->write("\r\n"); + + if (input->atEnd()) { + qCDebug(lc, "No more data available."); + return; + } + + // input takes ownership of the IOChunkedTransfer pointer inside his constructor + new IOChunkedTransfer<>(input.take(), d->socket); +} + +/*! + Answers a request with an HTTP status code \a status and a + MIME type \a mimeType. The I/O device \a data provides the body + of the response. If \a data is sequential, the body of the + message is sent in chunks: otherwise, the function assumes all + the content is available and sends it all at once but the read + is done in chunks. + + \note This function takes the ownership of \a data. +*/ +void QHttpServerResponder::write(QIODevice *data, + const QByteArray &mimeType, + StatusCode status) +{ + write(data, + {{ QHttpServerLiterals::contentTypeHeader(), mimeType }}, + status); +} + +/*! + Answers a request with an HTTP status code \a status, JSON + document \a document and HTTP headers \a headers. + + Note: This function sets HTTP Content-Type header as "application/json". +*/ +void QHttpServerResponder::write(const QJsonDocument &document, + HeaderList headers, + StatusCode status) +{ + const QByteArray &json = document.toJson(); + + writeStatusLine(status); + writeHeader(QHttpServerLiterals::contentTypeHeader(), + QHttpServerLiterals::contentTypeJson()); + writeHeader(QHttpServerLiterals::contentLengthHeader(), + QByteArray::number(json.size())); + writeHeaders(std::move(headers)); + writeBody(document.toJson()); +} + +/*! + Answers a request with an HTTP status code \a status, and JSON + document \a document. + + Note: This function sets HTTP Content-Type header as "application/json". +*/ +void QHttpServerResponder::write(const QJsonDocument &document, + StatusCode status) +{ + write(document, {}, status); +} + +/*! + Answers a request with an HTTP status code \a status, + HTTP Headers \a headers and a body \a data. + + Note: This function sets HTTP Content-Length header. +*/ +void QHttpServerResponder::write(const QByteArray &data, + HeaderList headers, + StatusCode status) +{ + writeStatusLine(status); + + for (auto &&header : headers) + writeHeader(header.first, header.second); + + writeHeader(QHttpServerLiterals::contentLengthHeader(), + QByteArray::number(data.size())); + writeBody(data); +} + +/*! + Answers a request with an HTTP status code \a status, a + MIME type \a mimeType and a body \a data. +*/ +void QHttpServerResponder::write(const QByteArray &data, + const QByteArray &mimeType, + StatusCode status) +{ + write(data, + {{ QHttpServerLiterals::contentTypeHeader(), mimeType }}, + status); +} + +/*! + Answers a request with an HTTP status code \a status. + + Note: This function sets HTTP Content-Type header as "application/x-empty". +*/ +void QHttpServerResponder::write(StatusCode status) +{ + write(QByteArray(), QHttpServerLiterals::contentTypeXEmpty(), status); +} + +/*! + Answers a request with an HTTP status code \a status and + HTTP Headers \a headers. +*/ +void QHttpServerResponder::write(HeaderList headers, StatusCode status) +{ + write(QByteArray(), std::move(headers), status); +} + +/*! + This function writes HTTP status line with an HTTP status code \a status + and an HTTP version \a version. +*/ +void QHttpServerResponder::writeStatusLine(StatusCode status, + const QPair &version) +{ + Q_D(const QHttpServerResponder); + Q_ASSERT(d->socket->isOpen()); + d->socket->write("HTTP/"); + d->socket->write(QByteArray::number(version.first)); + d->socket->write("."); + d->socket->write(QByteArray::number(version.second)); + d->socket->write(" "); + d->socket->write(QByteArray::number(quint32(status))); + d->socket->write(" "); + d->socket->write(statusString.at(status)); + d->socket->write("\r\n"); +} + +/*! + This function writes an HTTP header \a header + with \a value. +*/ +void QHttpServerResponder::writeHeader(const QByteArray &header, + const QByteArray &value) +{ + Q_D(const QHttpServerResponder); + Q_ASSERT(d->socket->isOpen()); + d->socket->write(header); + d->socket->write(": "); + d->socket->write(value); + d->socket->write("\r\n"); +} + +/*! + This function writes HTTP headers \a headers. +*/ +void QHttpServerResponder::writeHeaders(HeaderList headers) +{ + for (auto &&header : headers) + writeHeader(header.first, header.second); +} + +/*! + This function writes HTTP body \a body with size \a size. +*/ +void QHttpServerResponder::writeBody(const char *body, qint64 size) +{ + Q_D(QHttpServerResponder); + Q_ASSERT(d->socket->isOpen()); + + if (!d->bodyStarted) { + d->socket->write("\r\n"); + d->bodyStarted = true; + } + + d->socket->write(body, size); +} + +/*! + This function writes HTTP body \a body. +*/ +void QHttpServerResponder::writeBody(const char *body) +{ + writeBody(body, qstrlen(body)); +} + +/*! + This function writes HTTP body \a body. +*/ +void QHttpServerResponder::writeBody(const QByteArray &body) +{ + writeBody(body.constData(), body.size()); +} + +/*! + Returns the socket used. +*/ +QTcpSocket *QHttpServerResponder::socket() const +{ + Q_D(const QHttpServerResponder); + return d->socket; +} + +QT_END_NAMESPACE diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverresponder.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverresponder.h new file mode 100644 index 0000000..f19f263 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverresponder.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERRESPONDER_H +#define QHTTPSERVERRESPONDER_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QTcpSocket; +class QHttpServerRequest; + +class QHttpServerResponderPrivate; +class Q_HTTPSERVER_EXPORT QHttpServerResponder final +{ + Q_DECLARE_PRIVATE(QHttpServerResponder) + + friend class QAbstractHttpServer; + +public: + enum class StatusCode { + // 1xx: Informational + Continue = 100, + SwitchingProtocols, + Processing, + + // 2xx: Success + Ok = 200, + Created, + Accepted, + NonAuthoritativeInformation, + NoContent, + ResetContent, + PartialContent, + MultiStatus, + AlreadyReported, + IMUsed = 226, + + // 3xx: Redirection + MultipleChoices = 300, + MovedPermanently, + Found, + SeeOther, + NotModified, + UseProxy, + // 306: not used, was proposed as "Switch Proxy" but never standardized + TemporaryRedirect = 307, + PermanentRedirect, + + // 4xx: Client Error + BadRequest = 400, + Unauthorized, + PaymentRequired, + Forbidden, + NotFound, + MethodNotAllowed, + NotAcceptable, + ProxyAuthenticationRequired, + RequestTimeout, + Conflict, + Gone, + LengthRequired, + PreconditionFailed, + PayloadTooLarge, + UriTooLong, + UnsupportedMediaType, + RequestRangeNotSatisfiable, + ExpectationFailed, + ImATeapot, + MisdirectedRequest = 421, + UnprocessableEntity, + Locked, + FailedDependency, + UpgradeRequired = 426, + PreconditionRequired = 428, + TooManyRequests, + RequestHeaderFieldsTooLarge = 431, + UnavailableForLegalReasons = 451, + + // 5xx: Server Error + InternalServerError = 500, + NotImplemented, + BadGateway, + ServiceUnavailable, + GatewayTimeout, + HttpVersionNotSupported, + VariantAlsoNegotiates, + InsufficientStorage, + LoopDetected, + NotExtended = 510, + NetworkAuthenticationRequired, + NetworkConnectTimeoutError = 599, + }; + + using HeaderList = std::initializer_list>; + + QHttpServerResponder(QHttpServerResponder &&other); + ~QHttpServerResponder(); + + void write(QIODevice *data, + HeaderList headers, + StatusCode status = StatusCode::Ok); + + void write(QIODevice *data, + const QByteArray &mimeType, + StatusCode status = StatusCode::Ok); + + void write(const QJsonDocument &document, + HeaderList headers, + StatusCode status = StatusCode::Ok); + + void write(const QJsonDocument &document, + StatusCode status = StatusCode::Ok); + + void write(const QByteArray &data, + HeaderList headers, + StatusCode status = StatusCode::Ok); + + void write(const QByteArray &data, + const QByteArray &mimeType, + StatusCode status = StatusCode::Ok); + + void write(HeaderList headers, StatusCode status = StatusCode::Ok); + void write(StatusCode status = StatusCode::Ok); + + + void writeStatusLine(StatusCode status = StatusCode::Ok, + const QPair &version = qMakePair(1u, 1u)); + + void writeHeader(const QByteArray &key, const QByteArray &value); + void writeHeaders(HeaderList headers); + + void writeBody(const char *body, qint64 size); + void writeBody(const char *body); + void writeBody(const QByteArray &body); + + QTcpSocket *socket() const; + +private: + QHttpServerResponder(const QHttpServerRequest &request, QTcpSocket *socket); + + QScopedPointer d_ptr; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QHttpServerResponder::StatusCode) + +#endif // QHTTPSERVERRESPONDER_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverresponder_p.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverresponder_p.h new file mode 100644 index 0000000..f8b1fb0 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverresponder_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERRESPONDER_P_H +#define QHTTPSERVERRESPONDER_P_H + +#include +#include +#include + +#include +#include +#include +#include + +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QHttpServer. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +QT_BEGIN_NAMESPACE + +class QHttpServerResponderPrivate +{ +public: + QHttpServerResponderPrivate(const QHttpServerRequest &request, QTcpSocket *const socket) + : request(request), socket(socket) {} + + const QHttpServerRequest &request; +#if defined(QT_DEBUG) + const QPointer socket; +#else + QTcpSocket *const socket; +#endif + bool bodyStarted{false}; +}; + +QT_END_NAMESPACE + +#endif // QHTTPSERVERRESPONDER_P_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverresponse.cpp b/3rdparty/qthttpserver/src/httpserver/qhttpserverresponse.cpp new file mode 100644 index 0000000..47c3b49 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverresponse.cpp @@ -0,0 +1,370 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QHttpServerResponsePrivate::QHttpServerResponsePrivate( + QByteArray &&d, const QHttpServerResponse::StatusCode sc) + : data(std::move(d)), + statusCode(sc) +{ } + +QHttpServerResponsePrivate::QHttpServerResponsePrivate(const QHttpServerResponse::StatusCode sc) + : statusCode(sc) +{ } + +QHttpServerResponse::QHttpServerResponse(QHttpServerResponse &&other) noexcept + : d_ptr(other.d_ptr.take()) +{ +} + +QHttpServerResponse& QHttpServerResponse::operator=(QHttpServerResponse &&other) noexcept +{ + if (this == &other) + return *this; + + qSwap(d_ptr, other.d_ptr); + return *this; +} + +QHttpServerResponse::QHttpServerResponse( + const QHttpServerResponse::StatusCode statusCode) + : QHttpServerResponse(QHttpServerLiterals::contentTypeXEmpty(), + QByteArray(), + statusCode) +{ +} + +QHttpServerResponse::QHttpServerResponse(const char *data) + : QHttpServerResponse(QByteArray::fromRawData(data, qstrlen(data))) +{ +} + +QHttpServerResponse::QHttpServerResponse(const QString &data) + : QHttpServerResponse(data.toUtf8()) +{ +} + +QHttpServerResponse::QHttpServerResponse(const QByteArray &data) + : QHttpServerResponse(QMimeDatabase().mimeTypeForData(data).name().toLocal8Bit(), data) +{ +} + +QHttpServerResponse::QHttpServerResponse(QByteArray &&data) + : QHttpServerResponse( + QMimeDatabase().mimeTypeForData(data).name().toLocal8Bit(), + std::move(data)) +{ +} + +QHttpServerResponse::QHttpServerResponse(const QJsonObject &data) + : QHttpServerResponse(QHttpServerLiterals::contentTypeJson(), + QJsonDocument(data).toJson(QJsonDocument::Compact)) +{ +} + +QHttpServerResponse::QHttpServerResponse(const QJsonArray &data) + : QHttpServerResponse(QHttpServerLiterals::contentTypeJson(), + QJsonDocument(data).toJson(QJsonDocument::Compact)) +{ +} + +QHttpServerResponse::QHttpServerResponse(const QByteArray &mimeType, + const QByteArray &data, + const StatusCode status) + : d_ptr(new QHttpServerResponsePrivate(QByteArray(data), status)) +{ + setHeader(QHttpServerLiterals::contentTypeHeader(), mimeType); +} + +QHttpServerResponse::QHttpServerResponse(QByteArray &&mimeType, + const QByteArray &data, + const StatusCode status) + : d_ptr(new QHttpServerResponsePrivate(QByteArray(data), status)) +{ + setHeader(QHttpServerLiterals::contentTypeHeader(), std::move(mimeType)); +} + +QHttpServerResponse::QHttpServerResponse(const QByteArray &mimeType, + QByteArray &&data, + const StatusCode status) + : d_ptr(new QHttpServerResponsePrivate(std::move(data), status)) +{ + setHeader(QHttpServerLiterals::contentTypeHeader(), mimeType); +} + +QHttpServerResponse::QHttpServerResponse(QByteArray &&mimeType, + QByteArray &&data, + const StatusCode status) + : d_ptr(new QHttpServerResponsePrivate(std::move(data), status)) +{ + setHeader(QHttpServerLiterals::contentTypeHeader(), std::move(mimeType)); +} + +QHttpServerResponse::~QHttpServerResponse() +{ +} + +QHttpServerResponse QHttpServerResponse::fromFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly)) + return QHttpServerResponse(StatusCode::NotFound); + const QByteArray data = file.readAll(); + file.close(); + const QByteArray mimeType = QMimeDatabase().mimeTypeForFileNameAndData(fileName, data).name().toLocal8Bit(); + return QHttpServerResponse(mimeType, data); +} + +QHttpServerResponse::QHttpServerResponse(QHttpServerResponsePrivate *d) + : d_ptr(d) +{ + d->derived = true; +} + +/*! + Returns response body. +*/ +QByteArray QHttpServerResponse::data() const +{ + Q_D(const QHttpServerResponse); + return d->data; +} + +QHttpServerResponse::StatusCode QHttpServerResponse::statusCode() const +{ + Q_D(const QHttpServerResponse); + return d->statusCode; +} + +/*! + Returns HTTP "Content-Type" header. + + \note Default value is "text/html" +*/ +QByteArray QHttpServerResponse::mimeType() const +{ + Q_D(const QHttpServerResponse); + const auto res = d->headers.find( + QHttpServerLiterals::contentTypeHeader()); + if (res == d->headers.end()) + return QHttpServerLiterals::contentTypeTextHtml(); + + return res->second; +} + +/*! + Adds the HTTP header with name \a name and value \a value, + does not override any previously set headers. +*/ +void QHttpServerResponse::addHeader(QByteArray &&name, QByteArray &&value) +{ + Q_D(QHttpServerResponse); + d->headers.emplace(std::move(name), std::move(value)); +} + +/*! + Adds the HTTP header with name \a name and value \a value, + does not override any previously set headers. +*/ +void QHttpServerResponse::addHeader(QByteArray &&name, const QByteArray &value) +{ + Q_D(QHttpServerResponse); + d->headers.emplace(std::move(name), value); +} + +/*! + Adds the HTTP header with name \a name and value \a value, + does not override any previously set headers. +*/ +void QHttpServerResponse::addHeader(const QByteArray &name, QByteArray &&value) +{ + Q_D(QHttpServerResponse); + d->headers.emplace(name, std::move(value)); +} + +/*! + Adds the HTTP header with name \a name and value \a value, + does not override any previously set headers. +*/ +void QHttpServerResponse::addHeader(const QByteArray &name, const QByteArray &value) +{ + Q_D(QHttpServerResponse); + d->headers.emplace(name, value); +} + +void QHttpServerResponse::addHeaders(QHttpServerResponder::HeaderList headers) +{ + for (auto &&header : headers) + addHeader(header.first, header.second); +} + +/*! + Removes the HTTP header with name \a name. +*/ +void QHttpServerResponse::clearHeader(const QByteArray &name) +{ + Q_D(QHttpServerResponse); + d->headers.erase(name); +} + +/*! + Removes all HTTP headers. +*/ +void QHttpServerResponse::clearHeaders() +{ + Q_D(QHttpServerResponse); + d->headers.clear(); +} + +/*! + Sets the HTTP header with name \a name and value \a value, + overriding any previously set headers. +*/ +void QHttpServerResponse::setHeader(QByteArray &&name, QByteArray &&value) +{ + clearHeader(name); + addHeader(std::move(name), std::move(value)); +} + +/*! + Sets the HTTP header with name \a name and value \a value, + overriding any previously set headers. +*/ +void QHttpServerResponse::setHeader(QByteArray &&name, const QByteArray &value) +{ + clearHeader(name); + addHeader(std::move(name), value); +} + +/*! + Sets the HTTP header with name \a name and value \a value, + overriding any previously set headers. +*/ +void QHttpServerResponse::setHeader(const QByteArray &name, QByteArray &&value) +{ + clearHeader(name); + addHeader(name, std::move(value)); +} + +/*! + Sets the HTTP header with name \a name and value \a value, + overriding any previously set headers. +*/ +void QHttpServerResponse::setHeader(const QByteArray &name, const QByteArray &value) +{ + clearHeader(name); + addHeader(name, value); +} + +/*! + Sets the headers \a headers, overriding any previously set headers. +*/ +void QHttpServerResponse::setHeaders(QHttpServerResponder::HeaderList headers) +{ + for (auto &&header : headers) + setHeader(header.first, header.second); +} + +/*! + Returns true if the response contains an HTTP header with name \a name, + otherwise returns false. +*/ +bool QHttpServerResponse::hasHeader(const QByteArray &header) const +{ + Q_D(const QHttpServerResponse); + return d->headers.find(header) != d->headers.end(); +} + +/*! + Returns true if the response contains an HTTP header with name \a name and + with value \a value, otherwise returns false. +*/ +bool QHttpServerResponse::hasHeader(const QByteArray &name, + const QByteArray &value) const +{ + Q_D(const QHttpServerResponse); + auto range = d->headers.equal_range(name); + + auto condition = [&value] (const std::pair &pair) { + return pair.second == value; + }; + + return std::find_if(range.first, range.second, condition) != range.second; +} + +/*! + Returns values of the HTTP header with name \a name +*/ +QVector QHttpServerResponse::headers(const QByteArray &name) const +{ + Q_D(const QHttpServerResponse); + + QVector results; + auto range = d->headers.equal_range(name); + + for (auto it = range.first; it != range.second; ++it) + results.append(it->second); + + return results; +} + +/*! + Writes HTTP response into QHttpServerResponder \a responder. +*/ +void QHttpServerResponse::write(QHttpServerResponder &&responder) const +{ + Q_D(const QHttpServerResponse); + if (responder.socket()->state() != QAbstractSocket::ConnectedState) + return; + + responder.writeStatusLine(d->statusCode); + + for (auto &&header : d->headers) + responder.writeHeader(header.first, header.second); + + responder.writeHeader(QHttpServerLiterals::contentLengthHeader(), + QByteArray::number(d->data.size())); + + responder.writeBody(d->data); +} + +QT_END_NAMESPACE diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverresponse.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverresponse.h new file mode 100644 index 0000000..6d2bc1d --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverresponse.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERRESPONSE_H +#define QHTTPSERVERRESPONSE_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QJsonObject; + +class QHttpServerResponsePrivate; +class Q_HTTPSERVER_EXPORT QHttpServerResponse +{ + Q_DECLARE_PRIVATE(QHttpServerResponse) + +public: + using StatusCode = QHttpServerResponder::StatusCode; + + QHttpServerResponse() = delete; + QHttpServerResponse(const QHttpServerResponse &other) = delete; + QHttpServerResponse& operator=(const QHttpServerResponse &other) = delete; + + QHttpServerResponse(QHttpServerResponse &&other) noexcept; + QHttpServerResponse& operator=(QHttpServerResponse &&other) noexcept; + + QHttpServerResponse(const StatusCode statusCode); + + QHttpServerResponse(const char *data); + + QHttpServerResponse(const QString &data); + + explicit QHttpServerResponse(const QByteArray &data); + explicit QHttpServerResponse(QByteArray &&data); + + QHttpServerResponse(const QJsonObject &data); + QHttpServerResponse(const QJsonArray &data); + + QHttpServerResponse(const QByteArray &mimeType, + const QByteArray &data, + const StatusCode status = StatusCode::Ok); + QHttpServerResponse(QByteArray &&mimeType, + const QByteArray &data, + const StatusCode status = StatusCode::Ok); + QHttpServerResponse(const QByteArray &mimeType, + QByteArray &&data, + const StatusCode status = StatusCode::Ok); + QHttpServerResponse(QByteArray &&mimeType, + QByteArray &&data, + const StatusCode status = StatusCode::Ok); + + virtual ~QHttpServerResponse(); + static QHttpServerResponse fromFile(const QString &fileName); + + QByteArray data() const; + + QByteArray mimeType() const; + + StatusCode statusCode() const; + + void addHeader(QByteArray &&name, QByteArray &&value); + void addHeader(QByteArray &&name, const QByteArray &value); + void addHeader(const QByteArray &name, QByteArray &&value); + void addHeader(const QByteArray &name, const QByteArray &value); + + void addHeaders(QHttpServerResponder::HeaderList headers); + + template + void addHeaders(const Container &headerList) + { + for (const auto &header : headerList) + addHeader(header.first, header.second); + } + + void clearHeader(const QByteArray &name); + void clearHeaders(); + + void setHeader(QByteArray &&name, QByteArray &&value); + void setHeader(QByteArray &&name, const QByteArray &value); + void setHeader(const QByteArray &name, QByteArray &&value); + void setHeader(const QByteArray &name, const QByteArray &value); + + void setHeaders(QHttpServerResponder::HeaderList headers); + + bool hasHeader(const QByteArray &name) const; + bool hasHeader(const QByteArray &name, const QByteArray &value) const; + + QVector headers(const QByteArray &name) const; + + virtual void write(QHttpServerResponder &&responder) const; + +protected: + QHttpServerResponse(QHttpServerResponsePrivate *d); + + QScopedPointer d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QHTTPSERVERRESPONSE_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverresponse_p.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverresponse_p.h new file mode 100644 index 0000000..5011552 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverresponse_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERRESPONSE_P_H +#define QHTTPSERVERRESPONSE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QHttpServerResponse. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QHttpServerResponsePrivate +{ + struct HashHelper { + std::size_t operator()(const QByteArray& key) const + { + return qHash(key.toLower()); + } + }; + +public: + explicit QHttpServerResponsePrivate() = default; + virtual ~QHttpServerResponsePrivate() = default; + + QHttpServerResponsePrivate(QByteArray &&d, const QHttpServerResponse::StatusCode sc); + QHttpServerResponsePrivate(const QHttpServerResponse::StatusCode sc); + + QByteArray data; + QHttpServerResponse::StatusCode statusCode; + std::unordered_multimap headers; + bool derived{false}; +}; + +QT_END_NAMESPACE + +#endif // QHTTPSERVERRESPONSE_P_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverrouter.cpp b/3rdparty/qthttpserver/src/httpserver/qhttpserverrouter.cpp new file mode 100644 index 0000000..e28d5cf --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverrouter.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhttpserverrouter_p.h" + +#include +#include +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcRouter, "qt.httpserver.router") + +static const QMap defaultConverters = { + { QMetaType::Int, QLatin1String("[+-]?\\d+") }, + { QMetaType::Long, QLatin1String("[+-]?\\d+") }, + { QMetaType::LongLong, QLatin1String("[+-]?\\d+") }, + { QMetaType::Short, QLatin1String("[+-]?\\d+") }, + + { QMetaType::UInt, QLatin1String("[+]?\\d+") }, + { QMetaType::ULong, QLatin1String("[+]?\\d+") }, + { QMetaType::ULongLong, QLatin1String("[+]?\\d+") }, + { QMetaType::UShort, QLatin1String("[+]?\\d+") }, + + { QMetaType::Double, QLatin1String("[+-]?(?:[0-9]+(?:[.][0-9]*)?|[.][0-9]+)") }, + { QMetaType::Float, QLatin1String("[+-]?(?:[0-9]+(?:[.][0-9]*)?|[.][0-9]+)") }, + + { QMetaType::QString, QLatin1String("[^/]+") }, + { QMetaType::QByteArray, QLatin1String("[^/]+") }, + + { QMetaType::QUrl, QLatin1String(".*") }, + + { QMetaType::Void, QLatin1String("") }, +}; + +/*! + \class QHttpServerRouter + \brief Provides functions to bind a URL to a \c ViewHandler. + + You can register \c ViewHandler as a callback for requests to a specific URL. + Variable parts in the route can be specified by the arguments in ViewHandler. + + \note This is a low-level routing API for an HTTP server. + + See the following example: + + \code + auto pageView = [] (const quint64 page) { + qDebug() << "page" << page; + }; + using ViewHandler = decltype(pageView); + + QHttpServerRouter router; + + // register callback pageView on request "/page/" + // for example: "/page/10", "/page/15" + router.addRoute( + new QHttpServerRouterRule("/page/", [=] (QRegularExpressionMatch &match, + const QHttpServerRequest &, + QTcpSocket *) { + auto boundView = router.bindCaptured(pageView, match); + + // it calls pageView + boundView(); + })); + \endcode +*/ + +/*! \fn template bool QHttpServerRouter::addConverter(const QLatin1String ®exp) + + Adds a new converter for type \e Type matching regular expression \a regexp. + + Automatically try to register an implicit converter from QString to \e Type. + If there is already a converter of type \e Type, that converter's regexp + is replaced with \a regexp. + + \code + struct CustomArg { + int data = 10; + + CustomArg() {} ; + CustomArg(const QString &urlArg) : data(urlArg.toInt()) {} + }; + Q_DECLARE_METATYPE(CustomArg); + + QHttpServerRouter router; + router.addConverter(QLatin1String("[+-]?\\d+")); + + auto pageView = [] (const CustomArg &customArg) { + qDebug("data: %d", customArg.data); + }; + using ViewHandler = decltype(pageView); + + auto rule = new QHttpServerRouterRule( + "///log", + [&router, &pageView] (QRegularExpressionMatch &match, + const QHttpServerRequest &request, + QTcpSocket *socket) { + // Bind and call viewHandler with match's captured string and quint32: + router.bindCaptured(pageView, match)(); + }); + + router.addRule(rule); + \endcode +*/ + +/*! \fn template > bool QHttpServerRouter::addRule(QHttpServerRouterRule *rule) + + Adds a new \a rule. + + Inside addRule, we determine ViewHandler arguments and generate a list of + their QMetaType::Type ids. Then we parse the URL and replace each \c + with a regexp for its type from the list. + + \code + QHttpServerRouter router; + + using ViewHandler = decltype([] (const QString &page, const quint32 num) { }); + + auto rule = new QHttpServerRouterRule( + "///log", + [] (QRegularExpressionMatch &match, + const QHttpServerRequest &request, + QTcpSocket *socket) { + }); + + router.addRule(rule); + \endcode + + \note This function takes over ownership of \a rule. +*/ + +/*! \fn template> auto bindCaptured(ViewHandler &&handler, QRegularExpressionMatch &match) const -> typename ViewTraits::BindableType + + Supplies the \a handler with arguments derived from a URL. + Returns the bound function that accepts whatever remaining arguments the handler may take, + supplying them to the handler after the URL-derived values. + Each match of the regex applied to the URL (as a string) is converted to the type + of the handler's parameter at its position, so that passing it works. + + \code + QHttpServerRouter router; + + auto pageView = [] (const QString &page, const quint32 num) { + qDebug("page: %s, num: %d", qPrintable(page), num); + }; + using ViewHandler = decltype(pageView); + + auto rule = new QHttpServerRouterRule( + "///log", + [&router, &pageView] (QRegularExpressionMatch &match, + const QHttpServerRequest &request, + QTcpSocket *socket) { + // Bind and call viewHandler with match's captured string and quint32: + router.bindCaptured(pageView, match)(); + }); + + router.addRule(rule); + \endcode +*/ + +QHttpServerRouterPrivate::QHttpServerRouterPrivate() + : converters(defaultConverters) +{} + +/*! + Creates a QHttpServerRouter object with \c defaultConverters. + + \sa defaultConverters() +*/ +QHttpServerRouter::QHttpServerRouter() + : d_ptr(new QHttpServerRouterPrivate) +{} + +/*! + Destroys a QHttpServerRouter. +*/ +QHttpServerRouter::~QHttpServerRouter() +{} + +/*! + Adds a new converter for type \a type matching regular expression \a regexp. + + If there is already a converter of type \a type, that converter's regexp + is replaced with \a regexp. +*/ +void QHttpServerRouter::addConverter(const int type, const QLatin1String ®exp) +{ + Q_D(QHttpServerRouter); + d->converters[type] = regexp; +} + +/*! + Removes the converter for type \a type. +*/ +void QHttpServerRouter::removeConverter(const int type) +{ + Q_D(QHttpServerRouter); + d->converters.remove(type); +} + +/*! + Removes all converters. + + \note clearConverters() does not set up \c defaultConverters. + + \sa defaultConverters() +*/ +void QHttpServerRouter::clearConverters() +{ + Q_D(QHttpServerRouter); + d->converters.clear(); +} + +/*! + Returns a map of converter type and regexp. +*/ +const QMap &QHttpServerRouter::converters() const +{ + Q_D(const QHttpServerRouter); + return d->converters; +} + +/*! + Returns a map of default converter type and regexp. + The following converters are available by default: + + \value QMetaType::Int + \value QMetaType::Long + \value QMetaType::LongLong + \value QMetaType::Short + \value QMetaType::UInt + \value QMetaType::ULong + \value QMetaType::ULongLong + \value QMetaType::UShort + \value QMetaType::Double + \value QMetaType::Float + \value QMetaType::QString + \value QMetaType::QByteArray + \value QMetaType::QUrl + \value QMetaType::Void An empty converter. +*/ +const QMap &QHttpServerRouter::defaultConverters() +{ + return ::defaultConverters; +} + +bool QHttpServerRouter::addRuleImpl(QHttpServerRouterRule *rule, + const std::initializer_list &types) +{ + Q_D(QHttpServerRouter); + + if (!rule->hasValidMethods() || !rule->createPathRegexp(types, d->converters)) { + delete rule; + return false; + } + + d->rules.emplace_back(rule); + return true; +} + +/*! + Handles each new request for the HTTP server. + + Iterates through the list of rules to find the first that matches, + then executes this rule, returning \c true. Returns \c false if no rule + matches the request. +*/ +bool QHttpServerRouter::handleRequest(const QHttpServerRequest &request, + QTcpSocket *socket) const +{ + Q_D(const QHttpServerRouter); + for (const auto &rule : qAsConst(d->rules)) { + if (rule->exec(request, socket)) + return true; + } + + return false; +} + +QT_END_NAMESPACE diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverrouter.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverrouter.h new file mode 100644 index 0000000..8e4b79f --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverrouter.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERROUTER_H +#define QHTTPSERVERROUTER_H + +#include +#include + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QtPrivate { + template struct QHttpServerRouterPlaceholder {}; +} + +QT_END_NAMESPACE + +namespace std { + +template +struct is_placeholder)> : + integral_constant +{}; + +} + +QT_BEGIN_NAMESPACE + +class QTcpSocket; +class QHttpServerRequest; +class QHttpServerRouterRule; + +class QHttpServerRouterPrivate; +class Q_HTTPSERVER_EXPORT QHttpServerRouter +{ + Q_DECLARE_PRIVATE(QHttpServerRouter) + +public: + QHttpServerRouter(); + ~QHttpServerRouter(); + + template + bool addConverter(const QLatin1String ®exp) { + static_assert(QMetaTypeId2::Defined, + "Type is not registered with Qt's meta-object system: " + "please apply Q_DECLARE_METATYPE() to it"); + + if (!QMetaType::registerConverter()) + return false; + + addConverter(qMetaTypeId(), regexp); + return true; + } + + void addConverter(const int type, const QLatin1String ®exp); + void removeConverter(const int); + void clearConverters(); + const QMap &converters() const; + + static const QMap &defaultConverters(); + + template> + bool addRule(QHttpServerRouterRule *rule) + { + return addRuleHelper( + rule, + typename ViewTraits::Arguments::Indexes{}); + } + + template> + typename ViewTraits::BindableType bindCaptured(ViewHandler &&handler, + const QRegularExpressionMatch &match) const + { + return bindCapturedImpl( + std::forward(handler), + match, + typename ViewTraits::Arguments::CapturableIndexes{}, + typename ViewTraits::Arguments::PlaceholdersIndexes{}); + } + + bool handleRequest(const QHttpServerRequest &request, + QTcpSocket *socket) const; + +private: + template + bool addRuleHelper(QHttpServerRouterRule *rule, + QtPrivate::IndexesList) + { + const std::initializer_list types = { + ViewTraits::Arguments::template metaTypeId()...}; + return addRuleImpl(rule, types); + } + + bool addRuleImpl(QHttpServerRouterRule *rule, + const std::initializer_list &metaTypes); + + template + typename std::enable_if::type + bindCapturedImpl(ViewHandler &&handler, + const QRegularExpressionMatch &match, + QtPrivate::IndexesList, + QtPrivate::IndexesList) const + { + return std::bind( + std::forward(handler), + QVariant(match.captured(Cx + 1)) + .value::CleanType>()..., + QtPrivate::QHttpServerRouterPlaceholder{}...); + } + + template + typename std::enable_if::type + bindCapturedImpl(ViewHandler &&handler, + const QRegularExpressionMatch &, + QtPrivate::IndexesList, + QtPrivate::IndexesList) const + { + return std::bind( + std::forward(handler), + QtPrivate::QHttpServerRouterPlaceholder{}...); + } + + QScopedPointer d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QHTTPSERVERROUTER_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverrouter_p.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverrouter_p.h new file mode 100644 index 0000000..71078a6 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverrouter_p.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERROUTER_P_H +#define QHTTPSERVERROUTER_P_H + +#include +#include + +#include +#include +#include + +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QHttpServer. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +QT_BEGIN_NAMESPACE + +class QHttpServerRouterPrivate +{ +public: + QHttpServerRouterPrivate(); + + QMap converters; + std::list> rules; +}; + +QT_END_NAMESPACE + +#endif // QHTTPSERVERROUTER_P_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverrouterrule.cpp b/3rdparty/qthttpserver/src/httpserver/qhttpserverrouterrule.cpp new file mode 100644 index 0000000..e09bedd --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverrouterrule.cpp @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcRouterRule, "qt.httpserver.router.rule") + +static const auto methodEnum = QMetaEnum::fromType(); + +static QHttpServerRequest::Methods strToMethods(const char *strMethods) +{ + QHttpServerRequest::Methods methods; + + bool ok = false; + const int val = methodEnum.keysToValue(strMethods, &ok); + if (ok) + methods = static_cast(val); + else + qCWarning(lcRouterRule, "Can not convert %s to QHttpServerRequest::Method", strMethods); + + return methods; +} + +/*! + \class QHttpServerRouterRule + \brief The QHttpServerRouterRule is the base class for QHttpServerRouter rules. + + Use QHttpServerRouterRule to specify expected request parameters: + + \value path QUrl::path() + \value HTTP methods QHttpServerRequest::Methods + \value callback User-defined response callback + + \note This is a low level API, see QHttpServer for higher level alternatives. + + Example of QHttpServerRouterRule and QHttpServerRouter usage: + + \code + template + void route(const char *path, const QHttpServerRequest::Methods methods, ViewHandler &&viewHandler) + { + auto rule = new QHttpServerRouterRule( + path, methods, [this, &viewHandler] (QRegularExpressionMatch &match, + const QHttpServerRequest &request, + QTcpSocket *const socket) { + auto boundViewHandler = router.bindCaptured( + std::forward(viewHandler), match); + // call viewHandler + boundViewHandler(); + }); + + // QHttpServerRouter + router.addRule(rule); + } + + // Valid: + route("/user/", [] (qint64 id) { } ); // "/user/1" + // "/user/3" + // + route("/user//history", [] (qint64 id) { } ); // "/user/1/history" + // "/user/2/history" + // + route("/user//history/", [] (qint64 id, qint64 page) { } ); // "/user/1/history/1" + // "/user/2/history/2" + + // Invalid: + route("/user/", [] () { } ); // ERROR: path pattern has , but ViewHandler does not have any arguments + route("/user/\\d+", [] () { } ); // ERROR: path pattern does not support manual regexp + \endcode + + \note Regular expressions in the path pattern are not supported, but + can be registered (to match a use of "" to a specific type) using + QHttpServerRouter::addConverter(). +*/ + +/*! + Constructs a rule with pathPattern \a pathPattern, and routerHandler \a routerHandler. + + The rule accepts all HTTP methods by default. + + \sq QHttpServerRequest::Method +*/ +QHttpServerRouterRule::QHttpServerRouterRule(const QString &pathPattern, + RouterHandler &&routerHandler) + : QHttpServerRouterRule(pathPattern, + QHttpServerRequest::Method::All, + std::forward(routerHandler)) +{ +} + +/*! + Constructs a rule with pathPattern \a pathPattern, methods \a methods + and routerHandler \a routerHandler. + + The rule accepts any combinations of available HTTP methods. + + \sa QHttpServerRequest::Method +*/ +QHttpServerRouterRule::QHttpServerRouterRule(const QString &pathPattern, + const QHttpServerRequest::Methods methods, + RouterHandler &&routerHandler) + : QHttpServerRouterRule( + new QHttpServerRouterRulePrivate{pathPattern, + methods, + std::forward(routerHandler), {}}) +{ +} + +/*! + Constructs a rule with pathPattern \a pathPattern, methods \a methods + and routerHandler \a routerHandler. + + \note \a methods shall be joined with | as separator (not spaces or commas) + and that either the upper-case or the capitalised form may be used. + + \sa QMetaEnum::keysToValue +*/ +QHttpServerRouterRule::QHttpServerRouterRule(const QString &pathPattern, + const char *methods, + RouterHandler &&routerHandler) + : QHttpServerRouterRule(pathPattern, + strToMethods(methods), + std::forward(routerHandler)) +{ +} + +/*! + \internal + */ +QHttpServerRouterRule::QHttpServerRouterRule(QHttpServerRouterRulePrivate *d) + : d_ptr(d) +{ +} + +/*! + Destroys a QHttpServerRouterRule. +*/ +QHttpServerRouterRule::~QHttpServerRouterRule() +{ +} + +/*! + Returns true if the methods is valid +*/ +bool QHttpServerRouterRule::hasValidMethods() const +{ + Q_D(const QHttpServerRouterRule); + return d->methods & QHttpServerRequest::Method::All; +} + +/*! + This function is called by QHttpServerRouter when a new request is received. +*/ +bool QHttpServerRouterRule::exec(const QHttpServerRequest &request, + QTcpSocket *socket) const +{ + Q_D(const QHttpServerRouterRule); + + QRegularExpressionMatch match; + if (!matches(request, &match)) + return false; + + d->routerHandler(match, request, socket); + return true; +} + +/*! + This virtual function is called by exec() to check if request matches the rule. +*/ +bool QHttpServerRouterRule::matches(const QHttpServerRequest &request, + QRegularExpressionMatch *match) const +{ + Q_D(const QHttpServerRouterRule); + + if (d->methods && !(d->methods & request.method())) + return false; + + *match = d->pathRegexp.match(request.url().path()); + return (match->hasMatch() && d->pathRegexp.captureCount() == match->lastCapturedIndex()); +} + +/*! + \internal +*/ +bool QHttpServerRouterRule::createPathRegexp(const std::initializer_list &metaTypes, + const QMap &converters) +{ + Q_D(QHttpServerRouterRule); + + QString pathRegexp = d->pathPattern; + const QLatin1String arg(""); + for (auto type : metaTypes) { + if (type >= QMetaType::User + && !QMetaType::hasRegisteredConverterFunction(qMetaTypeId(), type)) { + qCWarning(lcRouterRule) << QMetaType::typeName(type) + << "has not registered a converter to QString." + << "Use QHttpServerRouter::addConveter(converter)."; + return false; + } + + auto it = converters.constFind(type); + if (it == converters.end()) { + qCWarning(lcRouterRule) << "Can not find converter for type:" + << QMetaType::typeName(type); + return false; + } + + if (it->isEmpty()) + continue; + + const auto index = pathRegexp.indexOf(arg); + const QString ®exp = QLatin1Char('(') % *it % QLatin1Char(')'); + if (index == -1) + pathRegexp.append(regexp); + else + pathRegexp.replace(index, arg.size(), regexp); + } + + if (pathRegexp.indexOf(arg) != -1) { + qCWarning(lcRouterRule) << "not enough types or one of the types is not supported, regexp:" + << pathRegexp + << ", pattern:" << d->pathPattern + << ", types:" << std::list(metaTypes); + return false; + } + + if (!pathRegexp.startsWith(QLatin1Char('^'))) + pathRegexp = QLatin1Char('^') % pathRegexp; + if (!pathRegexp.endsWith(QLatin1Char('$'))) + pathRegexp += QLatin1String("$"); + + qCDebug(lcRouterRule) << "url pathRegexp:" << pathRegexp; + + d->pathRegexp.setPattern(pathRegexp); + d->pathRegexp.optimize(); + return true; +} + +QT_END_NAMESPACE diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverrouterrule.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverrouterrule.h new file mode 100644 index 0000000..20ad2a2 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverrouterrule.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERROUTERRULE_H +#define QHTTPSERVERROUTERRULE_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QString; +class QHttpServerRequest; +class QTcpSocket; +class QRegularExpressionMatch; +class QHttpServerRouter; + +class QHttpServerRouterRulePrivate; +class Q_HTTPSERVER_EXPORT QHttpServerRouterRule +{ + Q_DECLARE_PRIVATE(QHttpServerRouterRule) + +public: + using RouterHandler = std::function; + + explicit QHttpServerRouterRule(const QString &pathPattern, RouterHandler &&routerHandler); + explicit QHttpServerRouterRule(const QString &pathPattern, + const QHttpServerRequest::Methods methods, + RouterHandler &&routerHandler); + explicit QHttpServerRouterRule(const QString &pathPattern, + const char * methods, + RouterHandler &&routerHandler); + + QHttpServerRouterRule(QHttpServerRouterRule &&other) = delete; + QHttpServerRouterRule &operator=(QHttpServerRouterRule &&other) = delete; + + virtual ~QHttpServerRouterRule(); + +protected: + bool exec(const QHttpServerRequest &request, QTcpSocket *socket) const; + + bool hasValidMethods() const; + + bool createPathRegexp(const std::initializer_list &metaTypes, + const QMap &converters); + + virtual bool matches(const QHttpServerRequest &request, + QRegularExpressionMatch *match) const; + + QHttpServerRouterRule(QHttpServerRouterRulePrivate *d); + +private: + Q_DISABLE_COPY(QHttpServerRouterRule) + QScopedPointer d_ptr; + + friend class QHttpServerRouter; +}; + +QT_END_NAMESPACE + +#endif // QHTTPSERVERROUTERRULE_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverrouterrule_p.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverrouterrule_p.h new file mode 100644 index 0000000..149c1c6 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverrouterrule_p.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERROUTERRULE_P_H +#define QHTTPSERVERROUTERRULE_P_H + +#include + +#include +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QHttpServer. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +QT_BEGIN_NAMESPACE + +class Q_HTTPSERVER_EXPORT QHttpServerRouterRulePrivate +{ +public: + QString pathPattern; + QHttpServerRequest::Methods methods; + QHttpServerRouterRule::RouterHandler routerHandler; + + QRegularExpression pathRegexp; +}; + +QT_END_NAMESPACE + +#endif // QHTTPSERVERROUTERRULE_P_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverrouterviewtraits.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverrouterviewtraits.h new file mode 100644 index 0000000..b572675 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverrouterviewtraits.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Mikhail Svetkin +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERROUTERVIEWTRAITS_H +#define QHTTPSERVERROUTERVIEWTRAITS_H + +#include + +QT_BEGIN_NAMESPACE + +class QHttpServerRequest; +class QHttpServerResponder; + +namespace QtPrivate { + +template +struct RouterViewTraitsHelper : ViewTraits { + using VTraits = ViewTraits; + using FunctionTraits = typename VTraits::FTraits; + + template + struct ArgumentChecker : FunctionTraits::template Arg { + using IsRequest = typename VTraits::template Special; + static_assert(IsRequest::AssertCondition, + "ViewHandler arguments error: " + "QHttpServerRequest can only be passed as a const reference"); + + using IsResponder = typename VTraits::template Special; + static_assert(IsResponder::AssertCondition, + "ViewHandler arguments error: " + "QHttpServerResponder can only be passed as a universal reference"); + + using IsSpecial = CheckAny; + + struct IsSimple { + static constexpr bool Value = !IsSpecial::Value && + I < FunctionTraits::ArgumentCount && + FunctionTraits::ArgumentIndexMax != -1; + static constexpr bool Valid = FunctionTraits::template Arg::Defined; + + static constexpr bool StaticAssert = + DisableStaticAssert || !Value || Valid; + + + static_assert(StaticAssert, + "ViewHandler arguments error: " + "Type is not registered, please use the Q_DECLARE_METATYPE macro " + "to make it known to Qt's meta-object system"); + }; + + using CheckOk = CheckAny; + + static constexpr bool Valid = CheckOk::Valid; + static constexpr bool StaticAssert = CheckOk::StaticAssert; + }; + + + struct Arguments { + template + struct ArgumentsReturn { + template + using Arg = ArgumentChecker; + + template + static constexpr int metaTypeId() noexcept + { + using Type = typename FunctionTraits::template Arg::CleanType; + + return qMetaTypeId< + typename std::conditional< + QMetaTypeId2::Defined, + Type, + void>::type>(); + } + + static constexpr std::size_t Count = FunctionTraits::ArgumentCount; + static constexpr std::size_t CapturableCount = + StaticMath::Sum::eval( + static_cast(FunctionTraits::template Arg::Defined)...); + static constexpr std::size_t PlaceholdersCount = Count - CapturableCount; + + static constexpr bool Valid = StaticMath::And::eval(Arg::Valid...); + static constexpr bool StaticAssert = + StaticMath::And::eval(Arg::StaticAssert...); + + using Indexes = typename QtPrivate::IndexesList; + + using CapturableIndexes = + typename QtPrivate::Indexes::Value; + + using PlaceholdersIndexes = + typename QtPrivate::Indexes::Value; + + using Last = Arg; + }; + + template + static constexpr ArgumentsReturn eval(QtPrivate::IndexesList) noexcept + { + return ArgumentsReturn{}; + } + }; + + template + struct BindType { + template + struct FunctionWrapper { + using Type = std::function; + }; + + template + using OffsetArg = typename FunctionTraits::template Arg::Type; + + template + static constexpr typename FunctionWrapper...>::Type + eval(QtPrivate::IndexesList) noexcept; + }; +}; + + +} // namespace QtPrivate + +template +struct QHttpServerRouterViewTraits +{ + using Helpers = typename QtPrivate::RouterViewTraitsHelper; + using ReturnType = typename Helpers::FunctionTraits::ReturnType; + using Arguments = decltype(Helpers::Arguments::eval(typename Helpers::ArgumentIndexes{})); + using BindableType = decltype( + Helpers::template BindType::eval( + typename Arguments::PlaceholdersIndexes{})); +}; + + +QT_END_NAMESPACE + +#endif // QHTTPSERVERROUTERVIEWTRAITS_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverviewtraits.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverviewtraits.h new file mode 100644 index 0000000..0469ffb --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverviewtraits.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Mikhail Svetkin +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERVIEWTRAITS_H +#define QHTTPSERVERVIEWTRAITS_H + +#include + +QT_BEGIN_NAMESPACE + +class QHttpServerRequest; +class QHttpServerResponse; + +namespace QtPrivate { + +template +struct AfterRequestViewTraitsHelper : ViewTraits { + using VTraits = ViewTraits; + using FunctionTraits = typename VTraits::FTraits; + + static_assert(DisableStaticAssert || + FunctionTraits::ArgumentCount == 2 || + FunctionTraits::ArgumentCount == 1, + "ViewHandler arguments error: " + "afterRequest can only accept QHttpServerResponse and QHttpServerRequest"); + + static_assert(DisableStaticAssert || + std::is_same::value, + "ViewHandler return type error: " + "Return type can only be QHttpServerResponse"); + + template + struct ArgumentChecker { + using IsRequest = typename VTraits::template Special; + static_assert(IsRequest::AssertCondition, + "ViewHandler arguments error: " + "QHttpServerRequest can only be passed as a const reference"); + + using IsResponse = typename VTraits::template Special; + static_assert(IsResponse::AssertCondition, + "ViewHandler arguments error: " + "QHttpServerResponse can only be passed as a universal reference"); + + using IsSpecial = CheckAny; + + static constexpr bool Valid = IsSpecial::Valid; + static constexpr bool StaticAssert = IsSpecial::StaticAssert; + }; + + struct Arguments { + template + struct ArgumentsReturn { + template + using Arg = ArgumentChecker; + static constexpr bool Valid = QtPrivate::StaticMath::And::eval(Arg::Valid...); + static constexpr bool StaticAssert = QtPrivate::StaticMath::And::eval( + Arg::StaticAssert...); + using Last = Arg; + static constexpr std::size_t Count = FunctionTraits::ArgumentCount; + }; + + template + static constexpr ArgumentsReturn eval(QtPrivate::IndexesList) noexcept + { + return ArgumentsReturn{}; + } + }; +}; + +} // namespace QtPrivate + +template +struct QHttpServerAfterRequestViewTraits +{ + using Helpers = typename QtPrivate::AfterRequestViewTraitsHelper; + using Arguments = decltype(Helpers::Arguments::eval(typename Helpers::ArgumentIndexes{})); +}; + +QT_END_NAMESPACE + +#endif // QHTTPSERVERVIEWTRAITS_H diff --git a/3rdparty/qthttpserver/src/httpserver/qhttpserverviewtraits_impl.h b/3rdparty/qthttpserver/src/httpserver/qhttpserverviewtraits_impl.h new file mode 100644 index 0000000..45c46d7 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qhttpserverviewtraits_impl.h @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Mikhail Svetkin +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPSERVERVIEWTRAITS_IMPL_H +#define QHTTPSERVERVIEWTRAITS_IMPL_H + +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QtPrivate { + +template +struct RemoveCVRef +{ + using Type = typename std::remove_cv::type>::type; +}; + + +template +struct FunctionTraitsHelper +{ + static constexpr const int ArgumentCount = sizeof ... (Args); + static constexpr const int ArgumentIndexMax = ArgumentCount - 1; + static constexpr const bool IsClassMember = classMember; + using ReturnType = ReturnT; + + template + struct Arg { + using Type = typename std::tuple_element>::type; + + using CleanType = typename QtPrivate::RemoveCVRef::Type; + + static constexpr bool Defined = QMetaTypeId2::Defined; + }; +}; + +template +struct FunctionTraitsHelper +{ + static constexpr const int ArgumentCount = 0; + static constexpr const int ArgumentIndexMax = -1; + static constexpr const bool IsClassMember = classMember; + using ReturnType = ReturnT; + + template + struct Arg { + using Type = std::false_type; + using CleanType = Type; + static constexpr bool Defined = QMetaTypeId2::Defined; + }; +}; + +template +struct FunctionTraits; + +template +struct FunctionTraits : public FunctionTraits{}; + +template +struct FunctionTraits + : public FunctionTraitsHelper +{ +}; + +template +struct FunctionTraits + : public FunctionTraitsHelper +{ + using classType = ClassT; +}; + +struct StaticMath { + template class Predicate, bool defaultValue> + struct Loop { + static constexpr bool eval() noexcept { + return defaultValue; + } + + template + static constexpr T eval(const T it, N ...n) noexcept { + return Predicate::eval(it, eval(n...)); + } + }; + + template + struct SumPredicate { + static constexpr T eval(const T rs, const T ls) noexcept + { + return rs + ls; + } + }; + + template + struct AndPredicate { + static constexpr T eval(const T rs, const T ls) noexcept + { + return rs && ls; + } + }; + + using Sum = Loop; + using And = Loop; + using Or = Sum; +}; + +template +struct CheckAny { + static constexpr bool Value = StaticMath::Or::eval(T::Value...); + static constexpr bool Valid = StaticMath::Or::eval(T::Valid...); + static constexpr bool StaticAssert = StaticMath::Or::eval(T::StaticAssert...); +}; + +template +struct ViewTraits { + using FTraits = FunctionTraits; + using ArgumentIndexes = typename Indexes::Value; + + template + struct SpecialHelper { + using Arg = typename FTraits::template Arg; + using CleanSpecialT = typename RemoveCVRef::Type; + + static constexpr bool TypeMatched = std::is_same::value; + static constexpr bool TypeCVRefMatched = std::is_same::value; + + static constexpr bool ValidPosition = + (I == FTraits::ArgumentIndexMax || + I == FTraits::ArgumentIndexMax - 1); + static constexpr bool ValidAll = TypeCVRefMatched && ValidPosition; + + static constexpr bool AssertCondition = + DisableStaticAssert || !TypeMatched || TypeCVRefMatched; + + static constexpr bool AssertConditionOrder = + DisableStaticAssert || !TypeMatched || ValidPosition; + + static constexpr bool StaticAssert = AssertCondition && AssertConditionOrder; + + static_assert(AssertConditionOrder, + "ViewHandler arguments error: " + "QHttpServerRequest or QHttpServerResponder" + " can only be the last argument"); + }; + + template + struct Special { + using Helper = SpecialHelper; + static constexpr bool Value = Helper::TypeMatched; + static constexpr bool Valid = Helper::ValidAll; + static constexpr bool StaticAssert = Helper::StaticAssert; + static constexpr bool AssertCondition = Helper::AssertCondition; + }; +}; + +} // namespace QtPrivate + +QT_END_NAMESPACE + +#endif // QHTTPSERVERVIEWTRAITS_IMPL_H diff --git a/3rdparty/qthttpserver/src/httpserver/qthttpserverglobal.h b/3rdparty/qthttpserver/src/httpserver/qthttpserverglobal.h new file mode 100644 index 0000000..8190256 --- /dev/null +++ b/3rdparty/qthttpserver/src/httpserver/qthttpserverglobal.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTHTTPSERVERGLOBAL_H +#define QTHTTPSERVERGLOBAL_H + +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_STATIC +# if defined(QT_BUILD_HTTPSERVER_LIB) +# define Q_HTTPSERVER_EXPORT Q_DECL_EXPORT +# else +# define Q_HTTPSERVER_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_HTTPSERVER_EXPORT +#endif + +QT_END_NAMESPACE + +#endif // QTHTTPSERVERGLOBAL_H + diff --git a/3rdparty/qthttpserver/src/src.pro b/3rdparty/qthttpserver/src/src.pro new file mode 100644 index 0000000..a78d859 --- /dev/null +++ b/3rdparty/qthttpserver/src/src.pro @@ -0,0 +1,10 @@ +TEMPLATE = subdirs + +QT = network + +qtConfig(ssl) { + SUBDIRS += sslserver + httpserver.depends = sslserver +} + +SUBDIRS += httpserver diff --git a/3rdparty/qthttpserver/src/sslserver/CMakeLists.txt b/3rdparty/qthttpserver/src/sslserver/CMakeLists.txt new file mode 100644 index 0000000..580d824 --- /dev/null +++ b/3rdparty/qthttpserver/src/sslserver/CMakeLists.txt @@ -0,0 +1,16 @@ +# Generated from sslserver.pro. + +##################################################################### +## SslServer Module: +##################################################################### + +qt_add_module(SslServer + SOURCES + qsslserver.cpp qsslserver.h qsslserver_p.h + qtsslserverglobal.h + INCLUDE_DIRECTORIES + . + PUBLIC_LIBRARIES + Qt::Core + Qt::Network +) diff --git a/3rdparty/qthttpserver/src/sslserver/qsslserver.cpp b/3rdparty/qthttpserver/src/sslserver/qsslserver.cpp new file mode 100644 index 0000000..b07f04b --- /dev/null +++ b/3rdparty/qthttpserver/src/sslserver/qsslserver.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Sylvain Garcia . +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcSS, "qt.sslserver"); + +QSslServer::QSslServer(QObject *parent): + QTcpServer (parent), d(new QSslServerPrivate) +{ +} + +QSslServer::QSslServer(const QSslConfiguration &sslConfiguration, + QObject *parent): + QTcpServer (parent), d(new QSslServerPrivate) +{ + d->sslConfiguration = sslConfiguration; +} + +QSslServer::~QSslServer() = default; + +void QSslServer::incomingConnection(qintptr handle) +{ + QSslSocket *socket = new QSslSocket(this); + connect(socket, QOverload&>::of(&QSslSocket::sslErrors), + [this, socket](const QList &errors) { + for (auto &err: errors) + qCCritical(lcSS) << err; + Q_EMIT sslErrors(socket, errors); + }); + socket->setSocketDescriptor(handle); + socket->setSslConfiguration(d->sslConfiguration); + socket->startServerEncryption(); + + addPendingConnection(socket); +} + +void QSslServer::setSslConfiguration(const QSslConfiguration &sslConfiguration) +{ + d->sslConfiguration = sslConfiguration; +} +QT_END_NAMESPACE diff --git a/3rdparty/qthttpserver/src/sslserver/qsslserver.h b/3rdparty/qthttpserver/src/sslserver/qsslserver.h new file mode 100644 index 0000000..841fc4f --- /dev/null +++ b/3rdparty/qthttpserver/src/sslserver/qsslserver.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Sylvain Garcia . +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSSLSERVER_H +#define QSSLSERVER_H + +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QSslServerPrivate; +class Q_SSLSERVER_EXPORT QSslServer : public QTcpServer +{ + Q_OBJECT +public: + QSslServer(QObject *parent = nullptr); + QSslServer(const QSslConfiguration &sslConfiguration, QObject *parent = nullptr); + ~QSslServer(); + + void setSslConfiguration(const QSslConfiguration &sslConfiguration); + +Q_SIGNALS: + void sslErrors(QSslSocket *socket, const QList &errors); + +protected: + void incomingConnection(qintptr handle) override final; + +private: + QScopedPointer d; +}; + +QT_END_NAMESPACE + +#endif // QSSLSERVER_HPP diff --git a/3rdparty/qthttpserver/src/sslserver/qsslserver_p.h b/3rdparty/qthttpserver/src/sslserver/qsslserver_p.h new file mode 100644 index 0000000..26d0ad1 --- /dev/null +++ b/3rdparty/qthttpserver/src/sslserver/qsslserver_p.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Sylvain Garcia . +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSSLSERVER_P_H +#define QSSLSERVER_P_H + +#include + +QT_BEGIN_NAMESPACE + +class QSslServerPrivate +{ +public: + QSslConfiguration sslConfiguration; +}; + +QT_END_NAMESPACE + +#endif // QSSLSERVER_P_H diff --git a/3rdparty/qthttpserver/src/sslserver/qtsslserverglobal.h b/3rdparty/qthttpserver/src/sslserver/qtsslserverglobal.h new file mode 100644 index 0000000..8f9b55d --- /dev/null +++ b/3rdparty/qthttpserver/src/sslserver/qtsslserverglobal.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Sylvain Garcia . +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTSSLSERVERGLOBAL_H +#define QTSSLSERVERGLOBAL_H + +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_STATIC +# if defined(QT_BUILD_SSLSERVER_LIB) +# define Q_SSLSERVER_EXPORT Q_DECL_EXPORT +# else +# define Q_SSLSERVER_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_SSLSERVER_EXPORT +#endif + +QT_END_NAMESPACE + +#endif // QTSSLSERVERGLOBAL_H diff --git a/3rdparty/qthttpserver/src/sslserver/sslserver.pro b/3rdparty/qthttpserver/src/sslserver/sslserver.pro new file mode 100644 index 0000000..0956a8c --- /dev/null +++ b/3rdparty/qthttpserver/src/sslserver/sslserver.pro @@ -0,0 +1,14 @@ +TARGET = QtSslServer +INCLUDEPATH += . + +QT = network core + +HEADERS += \ + qsslserver.h \ + qtsslserverglobal.h \ + qsslserver_p.h + +SOURCES += \ + qsslserver.cpp + +load(qt_module) diff --git a/3rdparty/qthttpserver/sync.profile b/3rdparty/qthttpserver/sync.profile new file mode 100644 index 0000000..80eeaf9 --- /dev/null +++ b/3rdparty/qthttpserver/sync.profile @@ -0,0 +1,4 @@ +%modules = ( # path to module name map + "QtHttpServer" => "$basedir/src/httpserver", + "QtSslServer" => "$basedir/src/sslserver", +); diff --git a/3rdparty/qthttpserver/tests/CMakeLists.txt b/3rdparty/qthttpserver/tests/CMakeLists.txt new file mode 100644 index 0000000..2214137 --- /dev/null +++ b/3rdparty/qthttpserver/tests/CMakeLists.txt @@ -0,0 +1,7 @@ +# Generated from tests.pro. + +if(QT_BUILD_STANDALONE_TESTS) + # Add qt_find_package calls for extra dependencies that need to be found when building + # the standalone tests here. +endif() +qt_build_tests() diff --git a/3rdparty/qthttpserver/tests/auto/CMakeLists.txt b/3rdparty/qthttpserver/tests/auto/CMakeLists.txt new file mode 100644 index 0000000..a7e0cb5 --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/CMakeLists.txt @@ -0,0 +1,7 @@ +# Generated from auto.pro. + +add_subdirectory(qabstracthttpserver) +add_subdirectory(qhttpserver) +add_subdirectory(qhttpserverresponder) +add_subdirectory(qhttpserverrouter) +add_subdirectory(qhttpserverresponse) diff --git a/3rdparty/qthttpserver/tests/auto/auto.pro b/3rdparty/qthttpserver/tests/auto/auto.pro new file mode 100644 index 0000000..af38d7a --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/auto.pro @@ -0,0 +1,11 @@ +TEMPLATE = subdirs + +QT = network + +SUBDIRS = \ + cmake \ + qabstracthttpserver \ + qhttpserver \ + qhttpserverresponder \ + qhttpserverrouter \ + qhttpserverresponse diff --git a/3rdparty/qthttpserver/tests/auto/cmake/CMakeLists.txt b/3rdparty/qthttpserver/tests/auto/cmake/CMakeLists.txt new file mode 100644 index 0000000..3fd2899 --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/cmake/CMakeLists.txt @@ -0,0 +1,12 @@ + +cmake_minimum_required(VERSION 2.8) + +project(qmake_cmake_files) + +enable_testing() + +find_package(Qt5Core REQUIRED) + +include("${_Qt5CTestMacros}") + +expect_pass(abstracthttpserver) diff --git a/3rdparty/qthttpserver/tests/auto/cmake/abstracthttpserver/CMakeLists.txt b/3rdparty/qthttpserver/tests/auto/cmake/abstracthttpserver/CMakeLists.txt new file mode 100644 index 0000000..0d49823 --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/cmake/abstracthttpserver/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8) +project(qabstracthttpserver_test) + +find_package(Qt5 COMPONENTS HttpServer REQUIRED) + +add_executable(qabstracthttpserver_test "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp") +target_link_libraries(qabstracthttpserver_test Qt5::HttpServer) diff --git a/3rdparty/qthttpserver/tests/auto/cmake/abstracthttpserver/main.cpp b/3rdparty/qthttpserver/tests/auto/cmake/abstracthttpserver/main.cpp new file mode 100644 index 0000000..da5e497 --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/cmake/abstracthttpserver/main.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() +{ + struct HttpServer : QAbstractHttpServer { + bool handleRequest(const QHttpServerRequest &, QTcpSocket *) override + { + return false; + } + } httpServer; + Q_UNUSED(httpServer); + return 0; +} diff --git a/3rdparty/qthttpserver/tests/auto/cmake/cmake.pro b/3rdparty/qthttpserver/tests/auto/cmake/cmake.pro new file mode 100644 index 0000000..9cef14e --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/cmake/cmake.pro @@ -0,0 +1,4 @@ +# Cause make to do nothing. +TEMPLATE = subdirs +CMAKE_QT_MODULES_UNDER_TEST = httpserver +CONFIG += ctest_testcase diff --git a/3rdparty/qthttpserver/tests/auto/qabstracthttpserver/CMakeLists.txt b/3rdparty/qthttpserver/tests/auto/qabstracthttpserver/CMakeLists.txt new file mode 100644 index 0000000..f7de801 --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qabstracthttpserver/CMakeLists.txt @@ -0,0 +1,12 @@ +# Generated from qabstracthttpserver.pro. + +##################################################################### +## tst_qabstracthttpserver Test: +##################################################################### + +qt_add_test(tst_qabstracthttpserver + SOURCES + tst_qabstracthttpserver.cpp + PUBLIC_LIBRARIES + Qt::HttpServer +) diff --git a/3rdparty/qthttpserver/tests/auto/qabstracthttpserver/qabstracthttpserver.pro b/3rdparty/qthttpserver/tests/auto/qabstracthttpserver/qabstracthttpserver.pro new file mode 100644 index 0000000..ca20055 --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qabstracthttpserver/qabstracthttpserver.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +TARGET = tst_qabstracthttpserver +SOURCES += tst_qabstracthttpserver.cpp + +QT = httpserver testlib diff --git a/3rdparty/qthttpserver/tests/auto/qabstracthttpserver/tst_qabstracthttpserver.cpp b/3rdparty/qthttpserver/tests/auto/qabstracthttpserver/tst_qabstracthttpserver.cpp new file mode 100644 index 0000000..48c637c --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qabstracthttpserver/tst_qabstracthttpserver.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#if defined(QT_WEBSOCKETS_LIB) +# include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if defined(Q_OS_UNIX) +# include +# include +#endif + +QT_BEGIN_NAMESPACE + +class tst_QAbstractHttpServer : public QObject +{ + Q_OBJECT + +private slots: + void request_data(); + void request(); + void checkListenWarns(); + void websocket(); + void servers(); + void fork(); + void qtbug82053(); +}; + +void tst_QAbstractHttpServer::request_data() +{ + QTest::addColumn("host"); + QTest::addColumn("path"); + QTest::addColumn("query"); + + QTest::addRow("127.0.0.1") << "127.0.0.1" << "/" << QString(); + QTest::addRow("0.0.0.0") << "0.0.0.0" << "/" << QString(); + QTest::addRow("localhost") << "localhost" << "/" << QString(); + QTest::addRow("localhost with query") << "localhost" << "/" << QString("key=value"); + QTest::addRow("0.0.0.0 path with spaces") << "0.0.0.0" << "/test test" << QString(); + QTest::addRow("0.0.0.0 path with spec spaces") << "0.0.0.0" << "/test%20test" << QString(); + QTest::addRow("127.0.0.1 path with spaces") << "127.0.0.1" << "/test test" << QString(); + QTest::addRow("127.0.0.1 path with spec spaces") << "127.0.0.1" << "/test%20test" << QString(); +} + +void tst_QAbstractHttpServer::request() +{ + QFETCH(QString, host); + QFETCH(QString, path); + QFETCH(QString, query); + +#if defined(Q_OS_WIN) + if (host == QLatin1String("0.0.0.0")) + QSKIP("Windows has problems with 0.0.0.0"); +#endif + + struct HttpServer : QAbstractHttpServer + { + QUrl url; + QByteArray body; + QHttpServerRequest::Method method = QHttpServerRequest::Method::Unknown; + quint8 padding[4]; + + bool handleRequest(const QHttpServerRequest &request, QTcpSocket *) override + { + method = request.method(); + url = request.url(); + body = request.body(); + return true; + } + } server; + auto tcpServer = new QTcpServer; + QVERIFY(tcpServer->listen()); + server.bind(tcpServer); + QNetworkAccessManager networkAccessManager; + QUrl url(QStringLiteral("http://%1:%2%3") + .arg(host) + .arg(tcpServer->serverPort()) + .arg(path)); + if (!query.isEmpty()) + url.setQuery(query); + const QNetworkRequest request(url); + networkAccessManager.get(request); + QTRY_COMPARE(server.method, QHttpServerRequest::Method::Get); + QCOMPARE(server.url, url); + QCOMPARE(server.body, QByteArray()); +} + +void tst_QAbstractHttpServer::checkListenWarns() +{ + struct HttpServer : QAbstractHttpServer + { + bool handleRequest(const QHttpServerRequest &, QTcpSocket *) override { return true; } + } server; + auto tcpServer = new QTcpServer; + QTest::ignoreMessage(QtWarningMsg, + QRegularExpression(QStringLiteral("The TCP server .* is not listening."))); + server.bind(tcpServer); +} + +void tst_QAbstractHttpServer::websocket() +{ +#if !defined(QT_WEBSOCKETS_LIB) + QSKIP("This test requires WebSocket support"); +#else + struct HttpServer : QAbstractHttpServer + { + bool handleRequest(const QHttpServerRequest &, QTcpSocket *) override { return false; } + } server; + auto tcpServer = new QTcpServer; + tcpServer->listen(); + server.bind(tcpServer); + auto makeWebSocket = [this, tcpServer] () mutable { + auto s = new QWebSocket(QString::fromUtf8(""), + QWebSocketProtocol::VersionLatest, + this); + const QUrl url(QString::fromLatin1("ws://localhost:%1").arg(tcpServer->serverPort())); + s->open(url); + return s; + }; + + // We have to send two requests to make sure that swapping between + // QTcpSocket and QWebSockets works correctly + auto s1 = makeWebSocket(); + auto s2 = makeWebSocket(); + + QSignalSpy newConnectionSpy(&server, &HttpServer::newWebSocketConnection); + QTRY_COMPARE(newConnectionSpy.count(), 2); + delete server.nextPendingWebSocketConnection(); + delete server.nextPendingWebSocketConnection(); + delete s1; + delete s2; +#endif // defined(QT_WEBSOCKETS_LIB) +} + +void tst_QAbstractHttpServer::servers() +{ + struct HttpServer : QAbstractHttpServer + { + bool handleRequest(const QHttpServerRequest &, QTcpSocket *) override { return true; } + } server; + auto tcpServer = new QTcpServer; + tcpServer->listen(); + server.bind(tcpServer); + auto tcpServer2 = new QTcpServer; + tcpServer2->listen(); + server.bind(tcpServer2); + QTRY_COMPARE(server.servers().count(), 2); + QTRY_COMPARE(server.serverPorts().count(), 2); + QTRY_COMPARE(server.servers().first(), tcpServer); + QTRY_COMPARE(server.serverPorts().first(), tcpServer->serverPort()); + QTRY_COMPARE(server.servers().last(), tcpServer2); + QTRY_COMPARE(server.serverPorts().last(), tcpServer2->serverPort()); +} + +void tst_QAbstractHttpServer::fork() +{ +#if defined(Q_OS_UNIX) + const auto message = QByteArrayLiteral("Hello world!"); + struct HttpServer : QAbstractHttpServer + { + const QByteArray &message; + HttpServer(const QByteArray &message) : message(message) {} + bool handleRequest(const QHttpServerRequest &, QTcpSocket *socket) override + { + socket->write(QByteArrayLiteral("HTTP/1.1 200 OK")); + socket->write(QByteArrayLiteral("\r\n")); + socket->write(QByteArrayLiteral("Content-Length: ")); + socket->write(QByteArray::number(message.size())); + socket->write(QByteArrayLiteral("\r\n")); + socket->write(QByteArrayLiteral("Connection: close")); + socket->write(QByteArrayLiteral("\r\n")); + socket->write(QByteArrayLiteral("Content-Type: text/html")); + socket->write(QByteArrayLiteral("\r\n\r\n")); + socket->write(message); + socket->flush(); + ::kill(::getpid(), SIGKILL); // Avoids continuing running tests in the child process + return true; + } + } server = { message }; + + struct TcpServer : QTcpServer + { + void incomingConnection(qintptr socketDescriptor) override + { + if (::fork() != 0) { + // Parent process: Create a QTcpSocket with the descriptor to close it properly + QTcpSocket socket; + socket.setSocketDescriptor(socketDescriptor); + socket.close(); + } else { + // Child process: It will parse the request and call HttpServer::handleRequest + QTcpServer::incomingConnection(socketDescriptor); + } + } + }; + auto tcpServer = new TcpServer; + tcpServer->listen(); + server.bind(tcpServer); + QNetworkAccessManager networkAccessManager; + const QUrl url(QString::fromLatin1("http://localhost:%1").arg(tcpServer->serverPort())); + auto reply = networkAccessManager.get(QNetworkRequest(url)); + QSignalSpy finishedSpy(reply, &QNetworkReply::finished); + QTRY_VERIFY(finishedSpy.count()); + QCOMPARE(reply->readAll(), message); + reply->close(); + reply->deleteLater(); +#else + QSKIP("fork() not supported by this platform"); +#endif +} + +void tst_QAbstractHttpServer::qtbug82053() +{ + struct HttpServer : QAbstractHttpServer + { + bool wasConnectRequest{false}; + bool handleRequest(const QHttpServerRequest &req, QTcpSocket *) override + { + wasConnectRequest = (req.method() == QHttpServerRequest::Method::Connect); + return false; + } + } server; + auto tcpServer = new QTcpServer; + tcpServer->listen(); + server.bind(tcpServer); + + QTcpSocket client; + client.connectToHost(QHostAddress::LocalHost, tcpServer->serverPort()); + client.waitForConnected(); + client.write("CONNECT / HTTP/1.1\n\n"); + client.waitForBytesWritten(); + QTest::qWait(0); + QCOMPARE(client.state(), QAbstractSocket::ConnectedState); + QTRY_VERIFY(server.wasConnectRequest); +} + +QT_END_NAMESPACE + +QTEST_MAIN(tst_QAbstractHttpServer) + +#include "tst_qabstracthttpserver.moc" diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserver/CMakeLists.txt b/3rdparty/qthttpserver/tests/auto/qhttpserver/CMakeLists.txt new file mode 100644 index 0000000..806494e --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserver/CMakeLists.txt @@ -0,0 +1,17 @@ +# Generated from qhttpserver.pro. + +##################################################################### +## tst_qhttpserver Test: +##################################################################### + +# Collect test data +list(APPEND test_data "data/") + +qt_add_test(tst_qhttpserver + SOURCES + tst_qhttpserver.cpp + PUBLIC_LIBRARIES + Qt::HttpServer + Qt::HttpServerPrivate + TESTDATA ${test_data} +) diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserver/data/application.json b/3rdparty/qthttpserver/tests/auto/qhttpserver/data/application.json new file mode 100644 index 0000000..7ba1b79 --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserver/data/application.json @@ -0,0 +1 @@ +{ "key": "value" } diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserver/data/text.html b/3rdparty/qthttpserver/tests/auto/qhttpserver/data/text.html new file mode 100644 index 0000000..18ecdcb --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserver/data/text.html @@ -0,0 +1 @@ + diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserver/qhttpserver.pro b/3rdparty/qthttpserver/tests/auto/qhttpserver/qhttpserver.pro new file mode 100644 index 0000000..d496ebb --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserver/qhttpserver.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +TARGET = tst_qhttpserver +SOURCES += tst_qhttpserver.cpp + +QT = httpserver httpserver-private testlib + +TESTDATA += data/ diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserver/tst_qhttpserver.cpp b/3rdparty/qthttpserver/tests/auto/qhttpserver/tst_qhttpserver.cpp new file mode 100644 index 0000000..cf5c43d --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserver/tst_qhttpserver.cpp @@ -0,0 +1,977 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#if QT_CONFIG(concurrent) +# include +#endif + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +static const char g_privateKey[] = R"(-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDykG51ZjNJra8iS27g3DJojH1qG8C3Z+Avo5U6Qz6NkOsjvr22 +gXqOS4uwVUXdCAKxsP0Wwn2zGz5vxGpLPVKtbAmaqHYZuipMG/Qun3t+QYBgR+9t +lmHdI8TNP2Om8stDO5uQyBH7DcMjPyIgpfc8fBoNLhCn4oC2n6JK9EMuhQIDAQAB +AoGAUHTLzrEJjgTINI3kxz0Ck18WMl3mPG9+Ew8lbl/jnb1V4VNhReoIpq40NVbz +h28ixaG5MRVt8Dy3Jwd1YmOCylHSujdFQ2u0pcHFmERgDS2bOMwMTRoFOj2qgMGS +9SM+iXlPY5AQY8nEg7rLjMSfaC/8Hq4RXpkj4PeHh6N7AzkCQQD++HzM3xBr+Gvh +zco9Kt8IiKNlfeiA5gUQq1UPJzcWIEgW1Tgr5UzMUOcZ0HfYwhqL3+wMhzN4sba+ +1plB1QRXAkEA84sfM0jm9BRSqtYTPlhsYAmuPjeo24Pxel8ijEkToAu0ppEC6AQ3 +zfwZD0ISgaWQ7af5TN/RCsoNVX79twP6gwJBANbtB+Z6shERm38ARdZB6Tf8ViAb +fn4JZ4OhqVXYrKrOE3aLzYnTBGXGXMh53kytcksuOoBlB5JZ274Kj63arokCQFPo +9xMAZzJpXiImJ/MvHAfqzfH501/ukeCLrqeO9ggKgG9zPwEZkvCRj0DGjwHEPa7k +VOy7oJaLDxUJ7/iCkmkCQQCtTLsvDbGH4tyFK5VIPJbUcccIib+dTzSTeONdUxKL +Yk+C6o7OpaUWX+ikp4Ow/6iHOAgXaeA2OolDer/NspUy +-----END RSA PRIVATE KEY-----)"; + +static const char g_certificate[] = R"(-----BEGIN CERTIFICATE----- +MIICrjCCAhegAwIBAgIUcuXjCSkJ2+v/Rqv/UHThTRGFlpswDQYJKoZIhvcNAQEL +BQAwaDELMAkGA1UEBhMCRlIxDzANBgNVBAgMBkZyYW5jZTERMA8GA1UEBwwIR3Jl +bm9ibGUxFjAUBgNVBAoMDVF0Q29udHJpYnV0b3IxHTAbBgNVBAMMFHFodHRwc3Nl +cnZlcnRlc3QuY29tMCAXDTE5MDkyNjA4NTc1MloYDzIyNTUwMzEzMDg1NzUyWjBo +MQswCQYDVQQGEwJGUjEPMA0GA1UECAwGRnJhbmNlMREwDwYDVQQHDAhHcmVub2Js +ZTEWMBQGA1UECgwNUXRDb250cmlidXRvcjEdMBsGA1UEAwwUcWh0dHBzc2VydmVy +dGVzdC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPKQbnVmM0mtryJL +buDcMmiMfWobwLdn4C+jlTpDPo2Q6yO+vbaBeo5Li7BVRd0IArGw/RbCfbMbPm/E +aks9Uq1sCZqodhm6Kkwb9C6fe35BgGBH722WYd0jxM0/Y6byy0M7m5DIEfsNwyM/ +IiCl9zx8Gg0uEKfigLafokr0Qy6FAgMBAAGjUzBRMB0GA1UdDgQWBBTDMYCcl2jz +UUWByEzTj5Ew/LWkeDAfBgNVHSMEGDAWgBTDMYCcl2jzUUWByEzTj5Ew/LWkeDAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAMNupAOXoBih6RvuAn3w +W8jOIZfkn5CMYdbUSndY/Wrt4p07M8r9uFPWG4bXSwG6n9Nzl75X9b0ka/jqPjQ3 +X769simPygCblBp2xwE6w14aHEBx4kcF1p2QbC1vHynszJxyVLvHqUjuJwVAoPrM +Imy6LOiw2tRTHPsj7UH16M6C +-----END CERTIFICATE-----)"; + +QT_BEGIN_NAMESPACE + +class QueryRequireRouterRule : public QHttpServerRouterRule +{ +public: + QueryRequireRouterRule(const QString &pathPattern, + const char *queryKey, + RouterHandler &&routerHandler) + : QHttpServerRouterRule(pathPattern, std::forward(routerHandler)), + m_queryKey(queryKey) + { + } + + bool matches(const QHttpServerRequest &request, QRegularExpressionMatch *match) const override + { + if (QHttpServerRouterRule::matches(request, match)) { + if (request.query().hasQueryItem(m_queryKey)) + return true; + } + + return false; + } + +private: + const char * m_queryKey; +}; + +class tst_QHttpServer final : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void routeGet_data(); + void routeGet(); + void routeKeepAlive(); + void routePost_data(); + void routePost(); + void routeDelete_data(); + void routeDelete(); + void routeExtraHeaders(); + void invalidRouterArguments(); + void checkRouteLambdaCapture(); + void afterRequest(); + void disconnectedInEventLoop(); + +private: + void checkReply(QNetworkReply *reply, const QString &response); + +private: + QHttpServer httpserver; + QString urlBase; + QString sslUrlBase; + QNetworkAccessManager networkAccessManager; +}; + +struct CustomArg { + int data = 10; + + CustomArg() {} ; + CustomArg(const QString &urlArg) : data(urlArg.toInt()) {} +}; + +void tst_QHttpServer::initTestCase() +{ + + httpserver.route("/req-and-resp", [] (QHttpServerResponder &&resp, + const QHttpServerRequest &req) { + resp.write(req.body(), + QHttpServerLiterals::contentTypeTextHtml()); + }); + + httpserver.route("/resp-and-req", [] (const QHttpServerRequest &req, + QHttpServerResponder &&resp) { + resp.write(req.body(), + QHttpServerLiterals::contentTypeTextHtml()); + }); + + httpserver.route("/test", [] (QHttpServerResponder &&responder) { + responder.write("test msg", + QHttpServerLiterals::contentTypeTextHtml()); + }); + + httpserver.route("/", QHttpServerRequest::Method::Get, [] () { + return "Hello world get"; + }); + + httpserver.route("/", QHttpServerRequest::Method::Post, [] () { + return "Hello world post"; + }); + + httpserver.route("/post-and-get", "GET|POST", [] (const QHttpServerRequest &request) { + if (request.method() == QHttpServerRequest::Method::Get) + return "Hello world get"; + else if (request.method() == QHttpServerRequest::Method::Post) + return "Hello world post"; + + return "This should not work"; + }); + + httpserver.route("/any", "All", [] (const QHttpServerRequest &request) { + static const auto metaEnum = QMetaEnum::fromType(); + return metaEnum.valueToKey(static_cast(request.method())); + }); + + httpserver.route("/page/", [] (const qint32 number) { + return QString("page: %1").arg(number); + }); + + httpserver.route("/page//detail", [] (const quint32 number) { + return QString("page: %1 detail").arg(number); + }); + + httpserver.route("/user/", [] (const QString &name) { + return QString("%1").arg(name); + }); + + httpserver.route("/user//", [] (const QString &name, const QByteArray &ba) { + return QString("%1-%2").arg(name).arg(QString::fromLatin1(ba)); + }); + + httpserver.route("/test/", [] (const QUrl &url) { + return QString("path: %1").arg(url.path()); + }); + + httpserver.route("/api/v", [] (const float api) { + return QString("api %1v").arg(api); + }); + + httpserver.route("/api/v/user/", [] (const float api, const quint64 user) { + return QString("api %1v, user id - %2").arg(api).arg(user); + }); + + httpserver.route("/api/v/user//settings", [] (const float api, const quint64 user, + const QHttpServerRequest &request) { + const auto &role = request.query().queryItemValue(QString::fromLatin1("role")); + const auto &fragment = request.url().fragment(); + + return QString("api %1v, user id - %2, set settings role=%3#'%4'") + .arg(api).arg(user).arg(role, fragment); + }); + + httpserver.route( + "/custom/", + "key", + [] (const quint64 num, const QHttpServerRequest &request) { + return QString("Custom router rule: %1, key=%2") + .arg(num) + .arg(request.query().queryItemValue("key")); + }); + + httpserver.router()->addConverter(QLatin1String("[+-]?\\d+")); + httpserver.route("/check-custom-type/", [] (const CustomArg &customArg) { + return QString("data = %1").arg(customArg.data); + }); + + httpserver.route("/post-body", "POST", [] (const QHttpServerRequest &request) { + return request.body(); + }); + + httpserver.route("/file/", [] (const QString &file) { + return QHttpServerResponse::fromFile(QFINDTESTDATA(QLatin1String("data/") + file)); + }); + + httpserver.route("/json-object/", [] () { + return QJsonObject{ + {"property", "test"}, + {"value", 1} + }; + }); + + httpserver.route("/json-array/", [] () { + return QJsonArray{ + 1, "2", + QJsonObject{ + {"name", "test"} + } + }; + }); + + httpserver.route("/chunked/", [] (QHttpServerResponder &&responder) { + responder.writeStatusLine(QHttpServerResponder::StatusCode::Ok); + responder.writeHeaders({ + {"Content-Type", "text/plain"}, + {"Transfer-Encoding", "chunked"} }); + + auto writeChunk = [&responder] (const char *message) { + responder.writeBody(QByteArray::number(qstrlen(message), 16)); + responder.writeBody("\r\n"); + responder.writeBody(message); + responder.writeBody("\r\n"); + }; + + writeChunk("part 1 of the message, "); + writeChunk("part 2 of the message"); + writeChunk(""); + }); + + httpserver.route("/extra-headers", [] () { + QHttpServerResponse resp(""); + resp.setHeader("Content-Type", "application/x-empty"); + resp.setHeader("Server", "test server"); + return resp; + }); + + httpserver.afterRequest([] (QHttpServerResponse &&resp) { + return std::move(resp); + }); + +#if QT_CONFIG(concurrent) + httpserver.route("/future/", [] (int id) -> QHttpServerFutureResponse { + if (id == 0) + return QHttpServerResponse::StatusCode::NotFound; + + auto future = QtConcurrent::run([] () { + QTest::qSleep(500); + return QHttpServerResponse("future is coming"); + }); + + return future; + }); +#endif + + quint16 port = httpserver.listen(); + if (!port) + qCritical() << "Http server listen failed"; + + urlBase = QStringLiteral("http://localhost:%1%2").arg(port); + +#if QT_CONFIG(ssl) + httpserver.sslSetup(QSslCertificate(g_certificate), + QSslKey(g_privateKey, QSsl::Rsa)); + + port = httpserver.listen(); + if (!port) + qCritical() << "Http server listen failed"; + + sslUrlBase = QStringLiteral("https://localhost:%1%2").arg(port); + + QList expectedSslErrors; + +// Non-OpenSSL backends are not able to report a specific error code +// for self-signed certificates. +#ifndef QT_NO_OPENSSL +# define FLUKE_CERTIFICATE_ERROR QSslError::SelfSignedCertificate +#else +# define FLUKE_CERTIFICATE_ERROR QSslError::CertificateUntrusted +#endif + + expectedSslErrors.append(QSslError(FLUKE_CERTIFICATE_ERROR, + QSslCertificate(g_certificate))); + expectedSslErrors.append(QSslError(QSslError::HostNameMismatch, + QSslCertificate(g_certificate))); + + connect(&networkAccessManager, &QNetworkAccessManager::sslErrors, + [expectedSslErrors](QNetworkReply *reply, + const QList &errors) { + for (const auto &error: errors) { + for (const auto &expectedError: expectedSslErrors) { + if (error.error() != expectedError.error() || + error.certificate() != expectedError.certificate()) { + qCritical() << "Got unexpected ssl error:" + << error << error.certificate(); + } + } + } + reply->ignoreSslErrors(expectedSslErrors); + }); +#endif +} + +void tst_QHttpServer::routeGet_data() +{ + QTest::addColumn("url"); + QTest::addColumn("code"); + QTest::addColumn("type"); + QTest::addColumn("body"); + + QTest::addRow("hello world") + << urlBase.arg("/") + << 200 + << "text/plain" + << "Hello world get"; + + QTest::addRow("test msg") + << urlBase.arg("/test") + << 200 + << "text/html" + << "test msg"; + + QTest::addRow("not found") + << urlBase.arg("/not-found") + << 404 + << "application/x-empty" + << ""; + + QTest::addRow("arg:int") + << urlBase.arg("/page/10") + << 200 + << "text/plain" + << "page: 10"; + + QTest::addRow("arg:-int") + << urlBase.arg("/page/-10") + << 200 + << "text/plain" + << "page: -10"; + + QTest::addRow("arg:uint") + << urlBase.arg("/page/10/detail") + << 200 + << "text/plain" + << "page: 10 detail"; + + QTest::addRow("arg:-uint") + << urlBase.arg("/page/-10/detail") + << 404 + << "application/x-empty" + << ""; + + QTest::addRow("arg:string") + << urlBase.arg("/user/test") + << 200 + << "text/plain" + << "test"; + + QTest::addRow("arg:string") + << urlBase.arg("/user/test test ,!a+.") + << 200 + << "text/plain" + << "test test ,!a+."; + + QTest::addRow("arg:string,ba") + << urlBase.arg("/user/james/bond") + << 200 + << "text/plain" + << "james-bond"; + + QTest::addRow("arg:url") + << urlBase.arg("/test/api/v0/cmds?val=1") + << 200 + << "text/plain" + << "path: api/v0/cmds"; + + QTest::addRow("arg:float 5.1") + << urlBase.arg("/api/v5.1") + << 200 + << "text/plain" + << "api 5.1v"; + + QTest::addRow("arg:float 5.") + << urlBase.arg("/api/v5.") + << 200 + << "text/plain" + << "api 5v"; + + QTest::addRow("arg:float 6.0") + << urlBase.arg("/api/v6.0") + << 200 + << "text/plain" + << "api 6v"; + + QTest::addRow("arg:float,uint") + << urlBase.arg("/api/v5.1/user/10") + << 200 + << "text/plain" + << "api 5.1v, user id - 10"; + + QTest::addRow("arg:float,uint,query") + << urlBase.arg("/api/v5.2/user/11/settings?role=admin") + << 200 + << "text/plain" + << "api 5.2v, user id - 11, set settings role=admin#''"; + + // The fragment isn't actually sent via HTTP (it's information for the user agent) + QTest::addRow("arg:float,uint, query+fragment") + << urlBase.arg("/api/v5.2/user/11/settings?role=admin#tag") + << 200 + << "text/plain" + << "api 5.2v, user id - 11, set settings role=admin#''"; + + QTest::addRow("custom route rule") + << urlBase.arg("/custom/15") + << 404 + << "application/x-empty" + << ""; + + QTest::addRow("custom route rule + query") + << urlBase.arg("/custom/10?key=11&g=1") + << 200 + << "text/plain" + << "Custom router rule: 10, key=11"; + + QTest::addRow("custom route rule + query key req") + << urlBase.arg("/custom/10?g=1&key=12") + << 200 + << "text/plain" + << "Custom router rule: 10, key=12"; + + QTest::addRow("post-and-get, get") + << urlBase.arg("/post-and-get") + << 200 + << "text/plain" + << "Hello world get"; + + QTest::addRow("invalid-rule-method, get") + << urlBase.arg("/invalid-rule-method") + << 404 + << "application/x-empty" + << ""; + + QTest::addRow("check custom type, data=1") + << urlBase.arg("/check-custom-type/1") + << 200 + << "text/plain" + << "data = 1"; + + QTest::addRow("any, get") + << urlBase.arg("/any") + << 200 + << "text/plain" + << "Get"; + + QTest::addRow("response from html file") + << urlBase.arg("/file/text.html") + << 200 + << "text/html" + << ""; + + QTest::addRow("response from json file") + << urlBase.arg("/file/application.json") + << 200 + << "application/json" + << "{ \"key\": \"value\" }"; + + QTest::addRow("json-object") + << urlBase.arg("/json-object/") + << 200 + << "application/json" + << "{\"property\":\"test\",\"value\":1}"; + + QTest::addRow("json-array") + << urlBase.arg("/json-array/") + << 200 + << "application/json" + << "[1,\"2\",{\"name\":\"test\"}]"; + + QTest::addRow("chunked") + << urlBase.arg("/chunked/") + << 200 + << "text/plain" + << "part 1 of the message, part 2 of the message"; + +#if QT_CONFIG(concurrent) + QTest::addRow("future") + << urlBase.arg("/future/1") + << 200 + << "text/plain" + << "future is coming"; + + QTest::addRow("future-not-found") + << urlBase.arg("/future/0") + << 404 + << "application/x-empty" + << ""; +#endif + +#if QT_CONFIG(ssl) + + QTest::addRow("hello world, ssl") + << sslUrlBase.arg("/") + << 200 + << "text/plain" + << "Hello world get"; + + QTest::addRow("post-and-get, get, ssl") + << sslUrlBase.arg("/post-and-get") + << 200 + << "text/plain" + << "Hello world get"; + + QTest::addRow("invalid-rule-method, get, ssl") + << sslUrlBase.arg("/invalid-rule-method") + << 404 + << "application/x-empty" + << ""; + + QTest::addRow("check custom type, data=1, ssl") + << sslUrlBase.arg("/check-custom-type/1") + << 200 + << "text/plain" + << "data = 1"; + +#endif // QT_CONFIG(ssl) +} + +void tst_QHttpServer::routeGet() +{ + QFETCH(QString, url); + QFETCH(int, code); + QFETCH(QString, type); + QFETCH(QString, body); + + auto reply = networkAccessManager.get(QNetworkRequest(url)); + + QTRY_VERIFY(reply->isFinished()); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code); + QCOMPARE(reply->readAll().trimmed(), body); + + reply->deleteLater(); +} + +void tst_QHttpServer::routeKeepAlive() +{ + httpserver.route("/keep-alive", [] (const QHttpServerRequest &req) -> QHttpServerResponse { + if (req.headers()["Connection"] != "keep-alive") + return QHttpServerResponse::StatusCode::NotFound; + + return QString("header: %1, query: %2, body: %3, method: %4") + .arg(req.value("CustomHeader"), + req.url().query(), + req.body()) + .arg(static_cast(req.method())); + }); + + QNetworkRequest request(urlBase.arg("/keep-alive")); + request.setRawHeader(QByteArray("Connection"), QByteArray("keep-alive")); + + checkReply(networkAccessManager.get(request), + QString("header: , query: , body: , method: %1") + .arg(static_cast(QHttpServerRequest::Method::Get))); + if (QTest::currentTestFailed()) + return; + + request.setUrl(urlBase.arg("/keep-alive?po=98")); + request.setRawHeader("CustomHeader", "1"); + request.setHeader(QNetworkRequest::ContentTypeHeader, + QHttpServerLiterals::contentTypeTextHtml()); + + checkReply(networkAccessManager.post(request, QByteArray("test")), + QString("header: 1, query: po=98, body: test, method: %1") + .arg(static_cast(QHttpServerRequest::Method::Post))); + if (QTest::currentTestFailed()) + return; + + request = QNetworkRequest(urlBase.arg("/keep-alive")); + request.setRawHeader(QByteArray("Connection"), QByteArray("keep-alive")); + request.setHeader(QNetworkRequest::ContentTypeHeader, + QHttpServerLiterals::contentTypeTextHtml()); + + checkReply(networkAccessManager.post(request, QByteArray("")), + QString("header: , query: , body: , method: %1") + .arg(static_cast(QHttpServerRequest::Method::Post))); + if (QTest::currentTestFailed()) + return; + + checkReply(networkAccessManager.get(request), + QString("header: , query: , body: , method: %1") + .arg(static_cast(QHttpServerRequest::Method::Get))); + if (QTest::currentTestFailed()) + return; +} + +void tst_QHttpServer::routePost_data() +{ + QTest::addColumn("url"); + QTest::addColumn("code"); + QTest::addColumn("type"); + QTest::addColumn("data"); + QTest::addColumn("body"); + + QTest::addRow("hello world") + << urlBase.arg("/") + << 200 + << "text/plain" + << "" + << "Hello world post"; + + QTest::addRow("post-and-get, post") + << urlBase.arg("/post-and-get") + << 200 + << "text/plain" + << "" + << "Hello world post"; + + QTest::addRow("any, post") + << urlBase.arg("/any") + << 200 + << "text/plain" + << "" + << "Post"; + + QTest::addRow("post-body") + << urlBase.arg("/post-body") + << 200 + << "text/plain" + << "some post data" + << "some post data"; + + QString body; + for (int i = 0; i < 10000; i++) + body.append(QString::number(i)); + + QTest::addRow("post-body - huge body, chunk test") + << urlBase.arg("/post-body") + << 200 + << "text/plain" + << body + << body; + + QTest::addRow("req-and-resp") + << urlBase.arg("/req-and-resp") + << 200 + << "text/html" + << "test" + << "test"; + + QTest::addRow("resp-and-req") + << urlBase.arg("/resp-and-req") + << 200 + << "text/html" + << "test" + << "test"; + +#if QT_CONFIG(ssl) + + QTest::addRow("post-and-get, post, ssl") + << sslUrlBase.arg("/post-and-get") + << 200 + << "text/plain" + << "" + << "Hello world post"; + + QTest::addRow("any, post, ssl") + << sslUrlBase.arg("/any") + << 200 + << "text/plain" + << "" + << "Post"; + + QTest::addRow("post-body, ssl") + << sslUrlBase.arg("/post-body") + << 200 + << "text/plain" + << "some post data" + << "some post data"; + + QTest::addRow("post-body - huge body, chunk test, ssl") + << sslUrlBase.arg("/post-body") + << 200 + << "text/plain" + << body + << body; + +#endif // QT_CONFIG(ssl) +} + +void tst_QHttpServer::routePost() +{ + QFETCH(QString, url); + QFETCH(int, code); + QFETCH(QString, type); + QFETCH(QString, data); + QFETCH(QString, body); + + QNetworkRequest request(url); + if (data.size()) { + request.setHeader(QNetworkRequest::ContentTypeHeader, + QHttpServerLiterals::contentTypeTextHtml()); + } + + auto reply = networkAccessManager.post(request, data.toUtf8()); + + QTRY_VERIFY(reply->isFinished()); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code); + QCOMPARE(reply->readAll(), body); + + reply->deleteLater(); +} + +void tst_QHttpServer::routeDelete_data() +{ + QTest::addColumn("url"); + QTest::addColumn("code"); + QTest::addColumn("type"); + QTest::addColumn("data"); + + QTest::addRow("post-and-get, delete") + << urlBase.arg("/post-and-get") + << 404 + << "application/x-empty" + << ""; + + QTest::addRow("any, delete") + << urlBase.arg("/any") + << 200 + << "text/plain" + << "Delete"; + +#if QT_CONFIG(ssl) + + QTest::addRow("post-and-get, delete, ssl") + << sslUrlBase.arg("/post-and-get") + << 404 + << "application/x-empty" + << ""; + + QTest::addRow("any, delete, ssl") + << sslUrlBase.arg("/any") + << 200 + << "text/plain" + << "Delete"; + +#endif // QT_CONFIG(ssl) +} + +void tst_QHttpServer::routeDelete() +{ + QFETCH(QString, url); + QFETCH(int, code); + QFETCH(QString, type); + QFETCH(QString, data); + + auto reply = networkAccessManager.deleteResource(QNetworkRequest(url)); + + QTRY_VERIFY(reply->isFinished()); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code); + + reply->deleteLater(); +} + +void tst_QHttpServer::routeExtraHeaders() +{ + const QUrl requestUrl(urlBase.arg("/extra-headers")); + auto reply = networkAccessManager.get(QNetworkRequest(requestUrl)); + + QTRY_VERIFY(reply->isFinished()); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), "application/x-empty"); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(reply->header(QNetworkRequest::ServerHeader), "test server"); +} + +struct CustomType { + CustomType() {} + CustomType(const QString &) {} +}; + +void tst_QHttpServer::invalidRouterArguments() +{ + QTest::ignoreMessage(QtWarningMsg, "Can not find converter for type: QDateTime"); + QCOMPARE( + httpserver.route("/datetime/", [] (const QDateTime &datetime) { + return QString("datetime: %1").arg(datetime.toString()); + }), + false); + + QTest::ignoreMessage(QtWarningMsg, "Can not convert GeT to QHttpServerRequest::Method"); + QCOMPARE( + httpserver.route("/invalid-rule-method", "GeT", [] () { + return ""; + }), + false); + + QTest::ignoreMessage(QtWarningMsg, "Can not convert Garbage to QHttpServerRequest::Method"); + QCOMPARE( + httpserver.route("/invalid-rule-method", "Garbage", [] () { + return ""; + }), + false); + + QCOMPARE( + httpserver.route("/invalid-rule-method", "Unknown", [] () { + return ""; + }), + false); + + QTest::ignoreMessage(QtWarningMsg, + "CustomType has not registered a converter to QString. " + "Use QHttpServerRouter::addConveter(converter)."); + QCOMPARE( + httpserver.route("/implicit-conversion-to-qstring-has-no-registered/", + [] (const CustomType &) { + return ""; + }), + false); +} + +void tst_QHttpServer::checkRouteLambdaCapture() +{ + httpserver.route("/capture-this/", [this] () { + return urlBase; + }); + + QString msg = urlBase + "/pod"; + httpserver.route("/capture-non-pod-data/", [&msg] () { + return msg; + }); + + checkReply(networkAccessManager.get( + QNetworkRequest(QUrl(urlBase.arg("/capture-this/")))), + urlBase); + if (QTest::currentTestFailed()) + return; + + checkReply(networkAccessManager.get( + QNetworkRequest(QUrl(urlBase.arg("/capture-non-pod-data/")))), + msg); + if (QTest::currentTestFailed()) + return; +} + +void tst_QHttpServer::afterRequest() +{ + httpserver.afterRequest([] (QHttpServerResponse &&resp, + const QHttpServerRequest &request) { + if (request.url().path() == "/test-after-request") + resp.setHeader("Arguments-Order-1", "resp, request"); + + return std::move(resp); + }); + + httpserver.afterRequest([] (const QHttpServerRequest &request, + QHttpServerResponse &&resp) { + if (request.url().path() == "/test-after-request") + resp.setHeader("Arguments-Order-2", "request, resp"); + + return std::move(resp); + }); + + const QUrl requestUrl(urlBase.arg("/test-after-request")); + auto reply = networkAccessManager.get(QNetworkRequest(requestUrl)); + + QTRY_VERIFY(reply->isFinished()); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), "application/x-empty"); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 404); + QCOMPARE(reply->rawHeader("Arguments-Order-1"), "resp, request"); + QCOMPARE(reply->rawHeader("Arguments-Order-2"), "request, resp"); + reply->deleteLater(); +} + +void tst_QHttpServer::checkReply(QNetworkReply *reply, const QString &response) { + QTRY_VERIFY(reply->isFinished()); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), "text/plain"); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(reply->readAll(), response); + + reply->deleteLater(); +}; + +void tst_QHttpServer::disconnectedInEventLoop() +{ + httpserver.route("/event-loop/", [] () { + QEventLoop loop; + QTimer::singleShot(1000, &loop, &QEventLoop::quit); + loop.exec(); + return QHttpServerResponse::StatusCode::Ok; + }); + + const QUrl requestUrl(urlBase.arg("/event-loop/")); + auto reply = networkAccessManager.get(QNetworkRequest(requestUrl)); + QTimer::singleShot(500, reply, &QNetworkReply::abort); // cancel connection + QEventLoop loop; + connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); + reply->deleteLater(); +} + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(CustomArg); +Q_DECLARE_METATYPE(CustomType); + +QTEST_MAIN(tst_QHttpServer) + +#include "tst_qhttpserver.moc" diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverresponder/CMakeLists.txt b/3rdparty/qthttpserver/tests/auto/qhttpserverresponder/CMakeLists.txt new file mode 100644 index 0000000..51a2a33 --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserverresponder/CMakeLists.txt @@ -0,0 +1,17 @@ +# Generated from qhttpserverresponder.pro. + +##################################################################### +## tst_qhttpserverresponder Test: +##################################################################### + +# Collect test data +list(APPEND test_data "data/") + +qt_add_test(tst_qhttpserverresponder + SOURCES + tst_qhttpserverresponder.cpp + PUBLIC_LIBRARIES + Qt::HttpServer + Qt::HttpServerPrivate + TESTDATA ${test_data} +) diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverresponder/data/index.html b/3rdparty/qthttpserver/tests/auto/qhttpserverresponder/data/index.html new file mode 100644 index 0000000..18ecdcb --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserverresponder/data/index.html @@ -0,0 +1 @@ + diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverresponder/qhttpserverresponder.pro b/3rdparty/qthttpserver/tests/auto/qhttpserverresponder/qhttpserverresponder.pro new file mode 100644 index 0000000..c170060 --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserverresponder/qhttpserverresponder.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +TARGET = tst_qhttpserverresponder +SOURCES += tst_qhttpserverresponder.cpp + +QT = httpserver httpserver-private testlib + +TESTDATA += data/ diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverresponder/tst_qhttpserverresponder.cpp b/3rdparty/qthttpserver/tests/auto/qhttpserverresponder/tst_qhttpserverresponder.cpp new file mode 100644 index 0000000..20b9480 --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserverresponder/tst_qhttpserverresponder.cpp @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +static const QByteArray headerServerString(QByteArrayLiteral("Server")); +static const QByteArray headerServerValue(QByteArrayLiteral("Test server")); + +class tst_QHttpServerResponder : public QObject +{ + Q_OBJECT + + std::unique_ptr networkAccessManager; + +private slots: + void init() { networkAccessManager.reset(new QNetworkAccessManager); } + void cleanup() { networkAccessManager.reset(); } + + void defaultStatusCodeNoParameters(); + void defaultStatusCodeByteArray(); + void defaultStatusCodeJson(); + void writeStatusCode_data(); + void writeStatusCode(); + void writeStatusCodeExtraHeader(); + void writeJson(); + void writeJsonExtraHeader(); + void writeFile_data(); + void writeFile(); + void writeFileExtraHeader(); + void writeByteArrayExtraHeader(); +}; + +#define qWaitForFinished(REPLY) QVERIFY(QSignalSpy(REPLY, &QNetworkReply::finished).wait()) + +struct HttpServer : QAbstractHttpServer { + std::function handleRequestFunction; + QUrl url { QStringLiteral("http://localhost:%1").arg(listen()) }; + + HttpServer(decltype(handleRequestFunction) function) : handleRequestFunction(function) {} + bool handleRequest(const QHttpServerRequest &request, QTcpSocket *socket) override; +}; + +bool HttpServer::handleRequest(const QHttpServerRequest &request, QTcpSocket *socket) +{ + handleRequestFunction(makeResponder(request, socket)); + return true; +} + +void tst_QHttpServerResponder::defaultStatusCodeNoParameters() +{ + HttpServer server([](QHttpServerResponder responder) { responder.write(); }); + auto reply = networkAccessManager->get(QNetworkRequest(server.url)); + qWaitForFinished(reply); + QCOMPARE(reply->error(), QNetworkReply::NoError); +} + +void tst_QHttpServerResponder::defaultStatusCodeByteArray() +{ + HttpServer server([](QHttpServerResponder responder) { + responder.write(QByteArray(), QHttpServerLiterals::contentTypeXEmpty()); + }); + auto reply = networkAccessManager->get(QNetworkRequest(server.url)); + qWaitForFinished(reply); + QCOMPARE(reply->error(), QNetworkReply::NoError); +} + +void tst_QHttpServerResponder::defaultStatusCodeJson() +{ + const auto json = QJsonDocument::fromJson(QByteArrayLiteral("{}")); + HttpServer server([json](QHttpServerResponder responder) { responder.write(json); }); + auto reply = networkAccessManager->get(QNetworkRequest(server.url)); + qWaitForFinished(reply); + QCOMPARE(reply->error(), QNetworkReply::NoError); +} + +void tst_QHttpServerResponder::writeStatusCode_data() +{ + using StatusCode = QHttpServerResponder::StatusCode; + + QTest::addColumn("statusCode"); + QTest::addColumn("networkError"); + + QTest::addRow("OK") << StatusCode::Ok << QNetworkReply::NoError; + QTest::addRow("Content Access Denied") << StatusCode::Forbidden + << QNetworkReply::ContentAccessDenied; + QTest::addRow("Connection Refused") << StatusCode::NotFound + << QNetworkReply::ContentNotFoundError; +} + +void tst_QHttpServerResponder::writeStatusCode() +{ + QFETCH(QHttpServerResponder::StatusCode, statusCode); + QFETCH(QNetworkReply::NetworkError, networkError); + HttpServer server([statusCode](QHttpServerResponder responder) { + responder.write(statusCode); + }); + auto reply = networkAccessManager->get(QNetworkRequest(server.url)); + qWaitForFinished(reply); + QCOMPARE(reply->bytesAvailable(), 0); + QCOMPARE(reply->error(), networkError); + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), + QByteArrayLiteral("application/x-empty")); +} + +void tst_QHttpServerResponder::writeStatusCodeExtraHeader() +{ + HttpServer server([=](QHttpServerResponder responder) { + responder.write({{ headerServerString, headerServerValue }}); + }); + auto reply = networkAccessManager->get(QNetworkRequest(server.url)); + qWaitForFinished(reply); + QCOMPARE(reply->bytesAvailable(), 0); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->header(QNetworkRequest::ServerHeader), headerServerValue); +} + +void tst_QHttpServerResponder::writeJson() +{ + const auto json = QJsonDocument::fromJson(QByteArrayLiteral(R"JSON({ "key" : "value" })JSON")); + HttpServer server([json](QHttpServerResponder responder) { responder.write(json); }); + auto reply = networkAccessManager->get(QNetworkRequest(server.url)); + qWaitForFinished(reply); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), + QHttpServerLiterals::contentTypeJson()); + QCOMPARE(QJsonDocument::fromJson(reply->readAll()), json); +} + +void tst_QHttpServerResponder::writeJsonExtraHeader() +{ + const auto json = QJsonDocument::fromJson(QByteArrayLiteral(R"JSON({ "key" : "value" })JSON")); + HttpServer server([json](QHttpServerResponder responder) { + responder.write(json, {{ headerServerString, headerServerValue }}); + }); + auto reply = networkAccessManager->get(QNetworkRequest(server.url)); + qWaitForFinished(reply); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), + QHttpServerLiterals::contentTypeJson()); + QCOMPARE(reply->header(QNetworkRequest::ServerHeader), headerServerValue); + QCOMPARE(QJsonDocument::fromJson(reply->readAll()), json); +} + +void tst_QHttpServerResponder::writeFile_data() +{ + QTest::addColumn("iodevice"); + QTest::addColumn("code"); + QTest::addColumn("type"); + QTest::addColumn("data"); + + QTest::addRow("index.html") + << qobject_cast(new QFile(QFINDTESTDATA("data/index.html"), this)) + << 200 + << "text/html" + << ""; + + QTest::addRow("index1.html - not found") + << qobject_cast(new QFile("./index1.html", this)) + << 500 + << "application/x-empty" + << QString(); + + QTest::addRow("temporary file") + << qobject_cast(new QTemporaryFile(this)) + << 200 + << "text/html" + << QString(); +} + +void tst_QHttpServerResponder::writeFile() +{ + QFETCH(QIODevice *, iodevice); + QFETCH(int, code); + QFETCH(QString, type); + QFETCH(QString, data); + + QSignalSpy spyDestroyIoDevice(iodevice, &QObject::destroyed); + + HttpServer server([&iodevice](QHttpServerResponder responder) { + responder.write(iodevice, QHttpServerLiterals::contentTypeTextHtml()); + }); + auto reply = networkAccessManager->get(QNetworkRequest(server.url)); + QTRY_VERIFY(reply->isFinished()); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code); + QCOMPARE(reply->readAll().trimmed(), data); + + QCOMPARE(spyDestroyIoDevice.count(), 1); +} + +void tst_QHttpServerResponder::writeFileExtraHeader() +{ + auto file = new QFile(QFINDTESTDATA("data/index.html"), this); + QSignalSpy spyDestroyIoDevice(file, &QObject::destroyed); + + HttpServer server([=](QHttpServerResponder responder) { + responder.write( + file, + { + { + QHttpServerLiterals::contentTypeHeader(), + QHttpServerLiterals::contentTypeTextHtml() + }, + { headerServerString, headerServerValue } + }); + }); + auto reply = networkAccessManager->get(QNetworkRequest(server.url)); + QTRY_VERIFY(reply->isFinished()); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), + QHttpServerLiterals::contentTypeTextHtml()); + QCOMPARE(reply->header(QNetworkRequest::ServerHeader), headerServerValue); + QCOMPARE(reply->readAll().trimmed(), ""); + + QCOMPARE(spyDestroyIoDevice.count(), 1); +} + +void tst_QHttpServerResponder::writeByteArrayExtraHeader() +{ + const QByteArray data("test data"); + const QByteArray contentType("text/plain"); + + HttpServer server([=](QHttpServerResponder responder) { + responder.write( + data, + { + { "Content-Type", contentType }, + { headerServerString, headerServerValue } + }); + }); + auto reply = networkAccessManager->get(QNetworkRequest(server.url)); + QTRY_VERIFY(reply->isFinished()); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), contentType); + QCOMPARE(reply->header(QNetworkRequest::ServerHeader), headerServerValue); + QCOMPARE(reply->readAll(), data); +} + +QT_END_NAMESPACE + +QTEST_MAIN(tst_QHttpServerResponder) + +#include "tst_qhttpserverresponder.moc" diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/CMakeLists.txt b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/CMakeLists.txt new file mode 100644 index 0000000..7ab6d6c --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/CMakeLists.txt @@ -0,0 +1,17 @@ +# Generated from qhttpserverresponse.pro. + +##################################################################### +## tst_qhttpserverresponse Test: +##################################################################### + +# Collect test data +list(APPEND test_data "data/") + +qt_add_test(tst_qhttpserverresponse + SOURCES + tst_qhttpserverresponse.cpp + PUBLIC_LIBRARIES + Qt::HttpServer + Qt::HttpServerPrivate + TESTDATA ${test_data} +) diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/application.json b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/application.json new file mode 100644 index 0000000..7ba1b79 --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/application.json @@ -0,0 +1 @@ +{ "key": "value" } diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/empty b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/empty new file mode 100644 index 0000000..e69de29 diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/image.jpeg b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/image.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..a5ee3e1cd6e864abc3155d3379375ada989c14d2 GIT binary patch literal 5514 zcmbuDcT`i)w#N@5Qq&(J9Th~R1qJCH6a^&+NJ)T55fG42LKA`@QF_N8B!E;AVt%2B zl+Z*XU8&MUnvftMAex9kfRMcSyZ7C@)_ecF_3q4CGvBr5%$)t%YtNZ8oB4^k0Gt4u zn3@1=YyfbBRRJamus23}djf#DIRF9xfD1Upb`oG`DK=IG61;)Ke`^4cVf6sO{*3M4 zqt6cgYxMFn_J8Z&4>C6Zty}jHA&B66h=8-IikAQ_BUAIkzgJ_GzqRz=^2GR=)Ts=h z_VDT>?%Z!QqbTNEfR78f3|wYs;{y)yv9a^9F}nZ>mPH(Ff0w_fu^P5R?1wpyaB^|; z9A!N~IKeWVo&C^Zb`Fl;uVH)08V3&Zah#M?zH)@$%9Zn6pn%HbKTEizjB45itw$*5 zRo#N3xp{**UpS^PZQMVPD|>!k5?9)e{;To0?lXI=i}idf&b88yy>; z_&E7#YMMk|{IayX@^y8M`g8Nw*7go@FWSM#K zgi{v>`v}y=#uN%sfNqn(+tJ8NIk-1I3^MK%O^vaMx8lC&dZa$vU5*&4xXx8vkALv; zM8;3UQT|nKvlw}|Ce;{awe>(hxPP`+o;4b7O=^KiRp}tIZx!9QKG}M9w=}1_e8Q$n zC!~~qFgrW@d3M&>+0imAEKILa)6P!iz5d|fz)!WhSw~9+wK_|+m!oDEQb$dtR9_3Z z-QE=nE_4{en)YGAndmj!f`S$8!A)>5x>_D=?Qe}PoCyo_Wda&&6BW)?;H@>=Dj30Z zq6${IWX)3NSFEw%EdTo;3%|bLtRQqgI^X|`pp5Hf4nP37#dcYOIkMNbyu7@f^HOt5OLMmAcYlAcKe0t*a<~IFFB}`1>2TLG(^RzZQ{Bn=<O_@q8N7xbTP;;&WuawzUuh9=gUp+vOtf4F z7=0=k53^m}RE+4|(9>E-MI;t@WKxR$z)diLdY?Y~0WT$;w@Xd1psqz~WL1x9;RSW= za)Bi|lLna(xy@>CB%_aR`jO}=@d>>u&fZEhc3qr5Ws04ickEP3P~#OtW-EA`UC_ZL z5}1Inx!_BT;tQ&|9xUof&68+t_mOWQtG;EZ)zjMUA0yvU=_HtzK4nxNRV>qn!PLcbIQgj^aek=G?r4&qq5z~?xsLr1n_#xS8s zXY5xqqAS$k%!gJ%a({%ShAwJj5U!ihVYw5KkmK zGoIK-_DjDFND~(&pVl~uOm{SJ$hoUMsG~>49h5%q_7DUuY>FY_1iVSrXL~0`d zjxN=XlB3_Hxl~1p(25t8;$id@gA$Uyv)TDKHwLWW4}2-zs8aaIHHQJ^wHD)v^k=VB zU$<{(NX`{$D$x78o(jpv*A4VbbrW=^q9jKl)=Oy*R8!%k8ou#MJ~|gm_RJfh1JA#3p0V~VoD|*MCD9UC(1jXQRBLq|EL(l*q5a*xPJ3K&R>=(;-Uf9kJ0!^IMKULWyhPc z5)H@aHzND@%{03X5c_s9br&Wj7`-V2$|5`x_PwO9lT5&)UZcv69igCw9jhve+gw zAS({fRTRt?&qBE_20OOdWNsKZN=0W*^A|*o{FL3uf9XVv*)F5aJq%aqmu^hIfBX-X zZ?`>g@y{D6TWGT}?lUi*@_-&c5~ekK(l@&bAU9pkQ1xMHeYXY`T;0PWY}!pkz^0+# zLas96LU9UmH5WS2>a_UHIJ|i%=Y(4jt#LFR!V~Vnr6gz7VWi;K`a>~)o9pBEdZp=p zl0d^~iwqXO4wIgR2e6`_7_XkMk!&@Bkea;fcXz^@V+44hVQ z(Reh3DstLfQ+Q5ZYY^dWbTKTW;#cs1g2!hSitVatk5;9#p;EQLh8Q}q8LAVLItdZW z#6cApQ8qDOGd>hgrz{QLx>Qg?`?JQaAkgI{h4q zlFbC}G67d6(BZS-XV-)Jd4{zIylfa_i~&|1WCBk#inoUiH;TU&xlDN+uqQxKoXXZZkSRMM&&Ca2_k3j>!I!^Q|>(5wV9M zWgMYp{65sZnLrhBZHF#}@;xxyH{6d%s0sf;Pes)RqHgcuCZZ}Bi}BlZ#{7<6QIE^8 z*9IRoh9NxPqa+gPz_Kok!DmSWGXatW6G-CRCT{=uWcaG};&Y@A!-zPQXE@z9ageb+ zzk6h6SK{8j=8x9mdNX=VkDf83AyC}COTuO^VUmGoyx+>GijXK@xYVSyxPRKPe!CdD zO>9q4GL(JOKdiKA+4Wh22_&ZV?9Pj#JW=i_Uz8`yONdESy+PGMG5U^5_0DklorjGL zdl`*P;6C=-L3QoUu6`LE+IE+w${hReEon(yK)1=&k472LaGg0pWtQam1RXT&o@b}y z+5?%oQaH*ZEuv=B&hE9WHn->874`j`N60l~)WzO~HB%FZcynhprR{Z6jTW3SsL1lY zeR82MzHKLC3-{rNW@$9_lxE+$#ii`v4%%NN|E-FYx9+dBi0*KM2Ek-JoQ*5dBjvvv=DitP{i)j~bce~1r)mA4|9M`tUVsOtbys_*i z5-cAfsxErw+@##q*-74-DKk-afX&1`44N?u`URC`Lly0ux)2gVk|G)gxJw-7C(Q0+!FEi4GtA z^Q!WYD$httK<5ZHhaB+9OWUDrZCRv1TI9tTJP1y&)itDnc$56pe@sXD>3sCS(9bOJ z@*v^Ar2P4u%{6MsS$w@b{eEO0xAiPSZcO~KidEW5Ixc1LsTEFIHhW3r*zwzHyKpeF z0Hv6(ek|marR~h7{yet9=c+Olj)bY&WMA(LH-IG8H=`AXUg&CldB|^D2RHA%t?{F3 zJSz8~w!MW3M2AH6^WSD{DB4^FNk{n~FJE#lJF<^BDO^wS zd9=j);1Uyn6iiQ;%&MlB{UB6X{k6ARHD(B*zSkj|@N%wL&ZkL}Hvjrq&ZEbje+#n@ ztHkL0{m{K%PZU(x&1L+-12D(`8K=6u0CK&Hc&ycnG%SHM+TYjhiV_-Mm=~& zzBY5lsk515Fm0QS}WFIk`7JWXqy zwNCJ8g?S(5o~HL`!NRwE{kBu&)01(kv(Zosi#ds4q@&&8%**h zVnMl)=0c-Y2^G++(VGV`5|mrSDa4}qW9%o!NHM$O_pJ)9o+&8`6G)(Ymrl>#5cFRe zA)GN{0$}PNPjY-yq+nP5q%PdKqP|5+^2pg!Vn}fw3Y0k+r>H`LW0cjP=~_3%z2al% z-juzXSo>I#(pjD+v)XfVYz_k}D8Ifvy=O)OCf404BT#hEY@`0$5xfFu z;*wS?RF?bcol=Kx^N~32X~Wm}#7dIr@P^^kSTE`@9YtyEWgXJBhVgm|3mfj{`%wjI zRhAvwMG_au#ozG$k*W{L6;9&o`z(8}wH8?_9$g`3x^uLZ`uV?CFZiIeT5u|lGK6@t zJQ1n?c0py6M|86!>_k1jkMoDPNFXm; zpafT-xKw*2PWBzO$)(a2$}5bv>PO@sSYRD1_zJArp~wP@LvKuj#=6Y-LFt8h5Kv@R ziH_H%1663VCyb#_#jJEei3o8^vXe)vv$~r7k5Ven{7_z*Cf`~6XP2BRUtwE$vOio) zI$kT?8hDNg2&|~9f|?sVY_#q>`{?;gFB{ASP1q0nNIR=0*So*B0)tL~Pb6DKrwUjx z>;m;8clf))m;7asIb9G{$KWAGqu{Y?!Rm?l(@r{1(n`$7DzO%7cq|w#B^HAA?%_H( zcTkL4cxK2ODMfL=PE(@JkeuebE}rV-J9S2!Jl)w@Bv~^alKax5W$eOMH81V?Z8O29 zk%8j7^>3mh9!nQMo9pAr+_EhPxL7^M_dmajbp}id-c?sk)i|P9MX#NL|tq zsL{F^f*-Z%beS5%3+s-h>9$*(#JH?k9&gKZRL`Wy9DS^JbzyJT>~Y^63JrvQHRbG) z(0zvlg#@6Lcm4fz)C6zxYSxrG7p&o};?95V!D^3JP%}2Psn)t1YYjhFc)tIGW-kNbGD!17bPyvE{%hjVVtO?Q!Q*48x4ogd)gpiic&&gLfu zV>8~>BJbvkEPQok;CQqgbuh5tD*4H6d##>SryJw3PR(Vzic95IbfxZeMN*VHId|o> zWT#q_ja ztdu8&B+R-rM%Khio$9kj=cT#}hR$JT3^lOest5I7Z{e&7@Ciw9n%FH2m;fKojME8O zZP#YqGzlU`@_p8DW%Jh(%!z+~f>>97_L(Tos1GhDiC%>$2dZ$VA9!eJ(|JVZ13|&h zdU)T=JWm)STmMFb_d`)0_k6t1?bo@|dr?AU`g#l%S>R~~E7WOf zz3_@q;#}NNBG)WGR=%oj@&|$YC0sQ}B|~jt5p*A95q$-MoF3yA$Q2KdkC6Jyp6u%AkumuX>t~CAY3-q0i_lg5xFrOmyrP8h%c5kPhMKwt;zCy(eyMc8Sr^%==1Mn z0J(ucXh{A?GQchR^Jgy1?_|LJbh%O}D;Z!VPzOwKSyYlN3Z$*ErX~Wb9g4vP%e(Ge zCYorhyt+saXydg)gEtrh+w%TUVu6KElpA5h)Ji}Y_yk^>CuDUjIv8&q48Ig>gDx9) z%MUetX8FlwTeyK{?uUzn(FiZv!>9(|IhrXy68x>TH`*j-EXAQ@k|}bT8Zu%ajS1^5 z=^6C%9gOr>JjZCE)OEZIpvzG?Pgteh{8?s8L26^QJWgbe23wkXs-*0cy?m9luM<|j zqQwNtimww(=M;}&;}#86J8i1E3Hb76PdlhG-YkCAHXQ>}^fn5gxB(u^TTXWo`Z|g@ Pu?ERte*9nQrm6n|&LWuv literal 0 HcmV?d00001 diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/image.png b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/image.png new file mode 100644 index 0000000000000000000000000000000000000000..39a4a26f39658d393cbd26485c1aee5c8d8620ea GIT binary patch literal 1032 zcmV+j1o!)iP)Skq@&(`Z`LXj;^1VbyPC)^ldpbZ6IfXxDaY*m!K%d2QKx zZrOWp*?e%>eR0}-aoT=!+JAJ~fOXq}cH4q^+=YAGhkf0Nf8C0J-i(9Zj)mWlhTo8e z-;sykl8E4viQtoq;FXNwmyO|9VZpv#jZ~uIja~>b9`zx3cTFv+KIF>$|q>y}0bZyY0ce?ZUq9#K7;y z!SBby@5jUM$iwf+#PG?;@Xg8b&B^i3%JI+4@zBol($DhL((~5S^Vih$*wyse*Yw)h z_1oC>+}ics+V$Ss_TJp~-`)1$-uB_(_v7LB;{|Ns9+N*zuB000AY zQchCtpU*a4MmTeDvcsw060>hO0!MT0Era< z@{><9(;fg%q5;ML7SF~+L=vMOKxY&{OA*MmmCGT37f}Eg0ER22I|clSM*zG63h|)s z1vn*AwXYh0n*&}e#&&=ab3iM=Rn_?cJU0g%0ywWalR(xSa01}4>O25e{PTgqCiVfO zROb%hxyFD&fL*F{10ZG$7zXH2oofK=jRE}tdsOEZP&8k{8GwVTa~D`L-^618CspSe z@ZB8H4scO*W`K|8fS3o2t40C1Zw`<-;AcZncL5Cf=fnfng#ktYjt2ETfTaJt2y6}m zqyXkNmdpLWfv>J#%mVwu0PYmPv_H)o`<4ODm3ohWOfsI>*{Yj~2mAw<9ltr2Sq9*3 z=>ue-GU~Ns{Yv#zXJKo()0`_=FZCD8KwiyfHwB#@f6ntE3y*R@OCSmj@$3% zKTTXuhX6!mL$W)a?!FB0c}>*2F;L?Sz+mkFNqInihc>@T2k?Ky34LA*X#NFXdR%Q@ z3y}Ud$6fvVLjV%8emj7lcWm>HZQilX*HxF}IL=>M5GNSE0?ASU0000 + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/text.html b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/text.html new file mode 100644 index 0000000..18ecdcb --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/text.html @@ -0,0 +1 @@ + diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/text.plain b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/text.plain new file mode 100644 index 0000000..980a0d5 --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/data/text.plain @@ -0,0 +1 @@ +Hello World! diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/qhttpserverresponse.pro b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/qhttpserverresponse.pro new file mode 100644 index 0000000..97ac43e --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/qhttpserverresponse.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +TARGET = tst_qhttpserverresponse +SOURCES += tst_qhttpserverresponse.cpp + +QT = httpserver httpserver-private testlib + +TESTDATA += data/ diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/tst_qhttpserverresponse.cpp b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/tst_qhttpserverresponse.cpp new file mode 100644 index 0000000..cf2c86b --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserverresponse/tst_qhttpserverresponse.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Tasuku Suzuki +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class tst_QHttpServerResponse : public QObject +{ + Q_OBJECT + +private slots: + void mimeTypeDetection_data(); + void mimeTypeDetection(); + void mimeTypeDetectionFromFile_data(); + void mimeTypeDetectionFromFile(); + void headers(); +}; + +void tst_QHttpServerResponse::mimeTypeDetection_data() +{ + QTest::addColumn("content"); + QTest::addColumn("mimeType"); + + QTest::addRow("application/x-zerosize") + << QFINDTESTDATA("data/empty") + << QByteArrayLiteral("application/x-zerosize"); + + QTest::addRow("text/plain") + << QFINDTESTDATA("data/text.plain") + << QByteArrayLiteral("text/plain"); + + QTest::addRow("text/html") + << QFINDTESTDATA("data/text.html") + << QHttpServerLiterals::contentTypeTextHtml(); + + QTest::addRow("image/png") + << QFINDTESTDATA("data/image.png") + << QByteArrayLiteral("image/png"); + + QTest::addRow("image/jpeg") + << QFINDTESTDATA("data/image.jpeg") + << QByteArrayLiteral("image/jpeg"); + + QTest::addRow("image/svg+xml") + << QFINDTESTDATA("data/image.svg") + << QByteArrayLiteral("image/svg+xml"); +} + +void tst_QHttpServerResponse::mimeTypeDetection() +{ + QFETCH(QString, content); + QFETCH(QByteArray, mimeType); + + QFile file(content); + file.open(QFile::ReadOnly); + QHttpServerResponse response(file.readAll()); + file.close(); + + QCOMPARE(response.mimeType(), mimeType); +} + +void tst_QHttpServerResponse::mimeTypeDetectionFromFile_data() +{ + QTest::addColumn("content"); + QTest::addColumn("mimeType"); + + QTest::addRow("application/x-zerosize") + << QFINDTESTDATA("data/empty") + << QByteArrayLiteral("application/x-zerosize"); + + QTest::addRow("text/plain") + << QFINDTESTDATA("data/text.plain") + << QByteArrayLiteral("text/plain"); + + QTest::addRow("text/html") + << QFINDTESTDATA("data/text.html") + << QHttpServerLiterals::contentTypeTextHtml(); + + QTest::addRow("image/png") + << QFINDTESTDATA("data/image.png") + << QByteArrayLiteral("image/png"); + + QTest::addRow("image/jpeg") + << QFINDTESTDATA("data/image.jpeg") + << QByteArrayLiteral("image/jpeg"); + + QTest::addRow("image/svg+xml") + << QFINDTESTDATA("data/image.svg") + << QByteArrayLiteral("image/svg+xml"); + + QTest::addRow("application/json") + << QFINDTESTDATA("data/application.json") + << QByteArrayLiteral("application/json"); +} + +void tst_QHttpServerResponse::mimeTypeDetectionFromFile() +{ + QFETCH(QString, content); + QFETCH(QByteArray, mimeType); + + QCOMPARE(QHttpServerResponse::fromFile(content).mimeType(), mimeType); +} + +void tst_QHttpServerResponse::headers() +{ + QHttpServerResponse resp(""); + + const QByteArray test1 = QByteArrayLiteral("test1"); + const QByteArray test2 = QByteArrayLiteral("test2"); + const QByteArray zero = QByteArrayLiteral("application/x-zerosize"); + const auto &contentTypeHeader = QHttpServerLiterals::contentTypeHeader(); + const auto &contentLengthHeader = QHttpServerLiterals::contentLengthHeader(); + + QVERIFY(!resp.hasHeader(contentLengthHeader)); + QVERIFY(resp.hasHeader(contentTypeHeader, zero)); + QVERIFY(!resp.hasHeader(contentTypeHeader, test1)); + QVERIFY(!resp.hasHeader(contentTypeHeader, test2)); + + resp.addHeader(contentTypeHeader, test1); + resp.addHeader(contentLengthHeader, test2); + QVERIFY(resp.hasHeader(contentLengthHeader, test2)); + QVERIFY(resp.hasHeader(contentTypeHeader, zero)); + QVERIFY(resp.hasHeader(contentTypeHeader, test1)); + QVERIFY(!resp.hasHeader(contentTypeHeader, test2)); + + const auto &typeHeaders = resp.headers(contentTypeHeader); + QCOMPARE(typeHeaders.size(), 2); + QVERIFY(typeHeaders.contains(zero)); + QVERIFY(typeHeaders.contains(test1)); + + const auto &lengthHeaders = resp.headers(contentLengthHeader); + QCOMPARE(lengthHeaders.size(), 1); + QVERIFY(lengthHeaders.contains(test2)); + + resp.setHeader(contentTypeHeader, test2); + + QVERIFY(resp.hasHeader(contentLengthHeader, test2)); + QVERIFY(!resp.hasHeader(contentTypeHeader, zero)); + QVERIFY(!resp.hasHeader(contentTypeHeader, test1)); + QVERIFY(resp.hasHeader(contentTypeHeader, test2)); + + resp.clearHeader(contentTypeHeader); + + QVERIFY(resp.hasHeader(contentLengthHeader, test2)); + + resp.clearHeader(contentLengthHeader); + + QVERIFY(!resp.hasHeader(contentLengthHeader)); + QVERIFY(!resp.hasHeader(contentTypeHeader)); + + resp.addHeaders({ {contentTypeHeader, zero}, {contentLengthHeader, test1} }); + + QVERIFY(resp.hasHeader(contentTypeHeader, zero)); + QVERIFY(resp.hasHeader(contentLengthHeader, test1)); + + resp.clearHeaders(); + + QVERIFY(!resp.hasHeader(contentLengthHeader)); + QVERIFY(!resp.hasHeader(contentTypeHeader)); + + const QList> headers = { + {contentTypeHeader, zero}, {contentLengthHeader, test2} + }; + + resp.addHeaders(headers); + + QVERIFY(resp.hasHeader(contentTypeHeader, zero)); + QVERIFY(resp.hasHeader(contentLengthHeader, test2)); + + resp.clearHeaders(); + + QVERIFY(!resp.hasHeader(contentLengthHeader)); + QVERIFY(!resp.hasHeader(contentTypeHeader)); +} + +QT_END_NAMESPACE + +QTEST_MAIN(tst_QHttpServerResponse) + +#include "tst_qhttpserverresponse.moc" diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverrouter/CMakeLists.txt b/3rdparty/qthttpserver/tests/auto/qhttpserverrouter/CMakeLists.txt new file mode 100644 index 0000000..27901fc --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserverrouter/CMakeLists.txt @@ -0,0 +1,12 @@ +# Generated from qhttpserverrouter.pro. + +##################################################################### +## tst_qhttpserverrouter Test: +##################################################################### + +qt_add_test(tst_qhttpserverrouter + SOURCES + tst_qhttpserverrouter.cpp + PUBLIC_LIBRARIES + Qt::HttpServer +) diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverrouter/qhttpserverrouter.pro b/3rdparty/qthttpserver/tests/auto/qhttpserverrouter/qhttpserverrouter.pro new file mode 100644 index 0000000..c928c6a --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserverrouter/qhttpserverrouter.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +TARGET = tst_qhttpserverrouter +SOURCES += tst_qhttpserverrouter.cpp + +QT = httpserver testlib diff --git a/3rdparty/qthttpserver/tests/auto/qhttpserverrouter/tst_qhttpserverrouter.cpp b/3rdparty/qthttpserver/tests/auto/qhttpserverrouter/tst_qhttpserverrouter.cpp new file mode 100644 index 0000000..d68221c --- /dev/null +++ b/3rdparty/qthttpserver/tests/auto/qhttpserverrouter/tst_qhttpserverrouter.cpp @@ -0,0 +1,544 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtHttpServer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QNetworkAccessManager::Operation); + +QT_BEGIN_NAMESPACE + +struct HttpServer : QAbstractHttpServer { + QHttpServerRouter router; + + HttpServer() + : QAbstractHttpServer() + { + connect(this, &QAbstractHttpServer::missingHandler, + [] (const QHttpServerRequest &request, QTcpSocket *socket) { + makeResponder(request, socket).write(QHttpServerResponder::StatusCode::NotFound); + }); + } + + template + void route(const char *path, const QHttpServerRequest::Methods methods, ViewHandler &&viewHandler) + { + auto rule = new QHttpServerRouterRule( + path, methods, [this, &viewHandler] (const QRegularExpressionMatch &match, + const QHttpServerRequest &request, + QTcpSocket *socket) { + auto boundViewHandler = router.bindCaptured( + std::forward(viewHandler), match); + boundViewHandler(makeResponder(request, socket)); + }); + + router.addRule(rule); + } + + template + void route(const char *path, ViewHandler &&viewHandler) + { + route(path, QHttpServerRequest::Method::All, std::forward(viewHandler)); + } + + bool handleRequest(const QHttpServerRequest &request, QTcpSocket *socket) override { + return router.handleRequest(request, socket); + } +}; + +class tst_QHttpServerRouter : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void routerRule_data(); + void routerRule(); + void viewHandlerNoArg(); + void viewHandlerOneArg(); + void viewHandlerTwoArgs(); + void viewHandlerResponder(); + void viewHandlerRequest(); + void viewHandlerLastTwoSpecials(); + +private: + HttpServer httpserver; + QString urlBase; +}; + +void tst_QHttpServerRouter::initTestCase() +{ + httpserver.route("/page/", [] (const quint64 &page, QHttpServerResponder &&responder) { + responder.write(QString("page: %1").arg(page).toUtf8(), "text/plain"); + }); + + httpserver.route("/post-only", QHttpServerRequest::Method::Post, + [] (QHttpServerResponder &&responder) { + responder.write(QString("post-test").toUtf8(), "text/plain"); + }); + + httpserver.route("/get-only", QHttpServerRequest::Method::Get, + [] (QHttpServerResponder &&responder) { + responder.write(QString("get-test").toUtf8(), "text/plain"); + }); + + urlBase = QStringLiteral("http://localhost:%1%2").arg(httpserver.listen()); +} + +void tst_QHttpServerRouter::routerRule_data() +{ + QTest::addColumn("url"); + QTest::addColumn("code"); + QTest::addColumn("type"); + QTest::addColumn("body"); + QTest::addColumn("replyType"); + + QTest::addRow("/page/1") + << "/page/1" + << 200 + << "text/plain" + << "page: 1" + << QNetworkAccessManager::GetOperation; + + QTest::addRow("/page/-1") + << "/page/-1" + << 404 + << "application/x-empty" + << "" + << QNetworkAccessManager::GetOperation; + + QTest::addRow("/post-only [GET]") + << "/post-only" + << 404 + << "application/x-empty" + << "" + << QNetworkAccessManager::GetOperation; + + QTest::addRow("/post-only [DELETE]") + << "/post-only" + << 404 + << "application/x-empty" + << "" + << QNetworkAccessManager::DeleteOperation; + + QTest::addRow("/post-only [POST]") + << "/post-only" + << 200 + << "text/plain" + << "post-test" + << QNetworkAccessManager::PostOperation; + + QTest::addRow("/get-only [GET]") + << "/get-only" + << 200 + << "text/plain" + << "get-test" + << QNetworkAccessManager::GetOperation; + + QTest::addRow("/get-only [POST]") + << "/get-only" + << 404 + << "application/x-empty" + << "" + << QNetworkAccessManager::PostOperation; + + QTest::addRow("/get-only [DELETE]") + << "/get-only" + << 404 + << "application/x-empty" + << "" + << QNetworkAccessManager::DeleteOperation; +} + +void tst_QHttpServerRouter::routerRule() +{ + QFETCH(QString, url); + QFETCH(int, code); + QFETCH(QString, type); + QFETCH(QString, body); + QFETCH(QNetworkAccessManager::Operation, replyType); + + QNetworkAccessManager networkAccessManager; + QNetworkReply *reply; + QNetworkRequest request(QUrl(urlBase.arg(url))); + + switch (replyType) { + case QNetworkAccessManager::GetOperation: + reply = networkAccessManager.get(request); + break; + case QNetworkAccessManager::PostOperation: + request.setHeader(QNetworkRequest::ContentTypeHeader, type); + reply = networkAccessManager.post(request, QByteArray("post body")); + break; + case QNetworkAccessManager::DeleteOperation: + reply = networkAccessManager.deleteResource(request); + break; + default: + QFAIL("The replyType does not supported"); + } + + QTRY_VERIFY(reply->isFinished()); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code); + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type); + QCOMPARE(reply->readAll(), body); +} + +void tst_QHttpServerRouter::viewHandlerNoArg() +{ + auto viewNonArg = [] () { + }; + + using ViewTraits = QHttpServerRouterViewTraits; + using Args = typename ViewTraits::Arguments; + + static_assert(Args::Count == 0, + "viewNonArg: Args::Count == 0"); + static_assert(Args::CapturableCount == 0, + "viewNonArg: Args::CapturableCount == 0"); + static_assert(Args::PlaceholdersCount == 0, + "viewNonArg: Args::PlaceholdersCount == 0"); + + static_assert(Args::Last::IsRequest::Value == 0, + "viewNonArg: Args::Last::IsRequest::Value == 0"); + static_assert(Args::Last::IsRequest::Valid == 0, + "viewNonArg: Args::Last::IsRequest::Valid == 0"); + static_assert(Args::Last::IsResponder::Value == 0, + "viewNonArg: Args::Last::IsResponder::Value == 0"); + static_assert(Args::Last::IsResponder::Valid == 0, + "viewNonArg: Args::Last::IsResponder::Valid == 0"); + static_assert(Args::Last::IsSpecial::Value == 0, + "viewNonArg: Args::Last::IsSpecial::Value == 0"); + static_assert(Args::Last::IsSpecial::Valid == 0, + "viewNonArg: Args::Last::IsSpecial::Valid == 0"); + static_assert(Args::Last::IsSimple::Value == 0, + "viewNonArg: Args::Last::IsSimple::Value == 0"); + static_assert(Args::Last::IsSimple::Valid == 0, + "viewNonArg: Args::Last::IsSimple::Valid == 0"); + static_assert(Args::Last::Valid == 0, + "viewOneArg: Args::Last::Valid"); + static_assert(Args::Last::StaticAssert, + "viewOneArg: Args::Last::StaticAssert"); + static_assert(std::is_same::value, + "viewNonArg: std::is_same"); + + using Arg1 = typename Args::template Arg<1>; + static_assert(Arg1::IsRequest::Value == 0, + "viewNonArg: Args::Arg<1>::isRequest::Value == 0"); + static_assert(Arg1::IsRequest::Valid == 0, + "viewNonArg: Args::Arg<1>::IsRequest::Valid == 0"); + static_assert(Arg1::IsResponder::Value == 0, + "viewNonArg: Args::Arg<1>::IsResponder::Value == 0"); + static_assert(Arg1::IsResponder::Valid == 0, + "viewNonArg: Args::Arg<1>::IsResponder::Valid == 0"); + static_assert(Arg1::IsSpecial::Value == 0, + "viewNonArg: Args::Arg<1>::IsSpecial::Value == 0"); + static_assert(Arg1::IsSpecial::Valid == 0, + "viewNonArg: Args::Arg<1>::IsSpecial::Valid == 0"); + static_assert(Arg1::Valid == 0, + "viewOneArg: Args::Arg<1>::Valid"); + static_assert(Arg1::StaticAssert, + "viewOneArg: Args::Arg<1>::Valid::StaticAssert"); + static_assert(std::is_same::value, + "viewNonArg: std::is_same::Type, std::false_type>"); + + static_assert(Args::Valid, "viewNonArg: Args::Valid"); + static_assert(Args::StaticAssert, "viewNonArg: Args::StaticAssert"); +} + +void tst_QHttpServerRouter::viewHandlerOneArg() +{ + auto view = [] (const quint64 &) { + }; + + using ViewTraits = QHttpServerRouterViewTraits; + using Args = typename ViewTraits::Arguments; + + static_assert(Args::Count == 1, + "viewOneArg: Args::Count == 1"); + static_assert(Args::CapturableCount == 1, + "viewOneArg: Args::CapturableCount == 1"); + static_assert(Args::PlaceholdersCount == 0, + "viewOneArg: Args::PlaceholdersCount == 0"); + static_assert(Args::Last::IsRequest::Value == 0, + "viewOneArg: Args::Last::IsRequest::Value == 0"); + static_assert(Args::Last::IsRequest::Valid == 0, + "viewOneArg: Args::Last::IsRequest::Valid == 0"); + static_assert(Args::Last::IsResponder::Value == 0, + "viewOneArg: Args::Last::IsResponder::Value == 0"); + static_assert(Args::Last::IsResponder::Valid == 0, + "viewOneArg: Args::Last::IsResponder::Valid == 0"); + static_assert(Args::Last::IsSpecial::Value == 0, + "viewOneArg: Args::Last::IsSpecial::Value == 0"); + static_assert(Args::Last::IsSpecial::Valid == 0, + "viewOneArg: Args::Last::IsSpecial::Valid == 0"); + static_assert(Args::Last::IsSimple::Value, + "viewOneArg: Args::Last::IsSimple::Value"); + static_assert(Args::Last::IsSimple::Valid, + "viewOneArg: Args::Last::IsSimple::Valid"); + static_assert(Args::Last::Valid, + "viewOneArg: Args::Last::Valid"); + static_assert(Args::Last::StaticAssert, + "viewOneArg: Args::Last::StaticAssert"); + static_assert(std::is_same::value, + "viewNonArg: std::is_same"); + static_assert(Args::Valid, "viewOneArg: Args::Valid"); + static_assert(Args::StaticAssert, "viewOneArg: Args::StaticAssert"); +} + +void tst_QHttpServerRouter::viewHandlerTwoArgs() +{ + auto view = [] (const quint64 &, const QHttpServerResponder &) { + }; + + using ViewTraits = QHttpServerRouterViewTraits; + using Args = typename ViewTraits::Arguments; + + static_assert(Args::Count == 2, + "viewTwoArgs: Args::Count == 2"); + static_assert(Args::CapturableCount == 1, + "viewTwoArgs: Args::CapturableCount == 1"); + static_assert(Args::PlaceholdersCount == 1, + "viewTwoArgs: Args::PlaceholdersCount == 0"); + + using Arg0 = typename Args::template Arg<0>; + static_assert(Arg0::IsRequest::Value == 0, + "viewTwoArgs: Args::Arg0::IsRequest::Value == 0"); + static_assert(Arg0::IsRequest::Valid == 0, + "viewTwoArgs: Args::Arg0::IsRequest::Valid == 0"); + static_assert(Arg0::IsResponder::Value == 0, + "viewTwoArgs: Args::Arg0::IsResponder::Value == 0"); + static_assert(Arg0::IsResponder::Valid == 0, + "viewTwoArgs: Args::Arg0::IsResponder::Valid == 0"); + static_assert(Arg0::IsSpecial::Value == 0, + "viewTwoArgs: Args::Arg0::IsSpecial::Value == 0"); + static_assert(Arg0::IsSpecial::Valid == 0, + "viewTwoArgs: Args::Arg0::IsSpecial::Valid == 0"); + static_assert(Arg0::IsSimple::Value, + "viewTwoArgs: Args::Arg0::IsSimple::Value"); + static_assert(Arg0::IsSimple::Valid, + "viewTwoArgs: Args::Arg0::IsSimple::Valid"); + static_assert(Arg0::Valid, + "viewTwoArgs: Args::Arg0::Valid"); + static_assert(Arg0::StaticAssert, + "viewTwoArgs: Args::Arg0::StaticAssert"); + static_assert(std::is_same::value, + "viewNonArg: std::is_same"); + + using Arg1 = typename Args::template Arg<1>; + static_assert(Arg1::IsRequest::Value == 0, + "viewTwoArgs: Args::Arg1::IsRequest::Value == 0"); + static_assert(Arg1::IsRequest::Valid == 0, + "viewTwoArgs: Args::Arg1::IsRequest::Valid == 0"); + static_assert(Arg1::IsResponder::Value, + "viewTwoArgs: Args::Arg1::IsResponder::Value"); + static_assert(Arg1::IsResponder::Valid == 0, + "viewTwoArgs: Args::Arg1::IsResponder::Valid == 0"); + static_assert(Arg1::IsSpecial::Value == 1, + "viewTwoArgs: Args::Arg1::IsSpecial::Value"); + static_assert(Arg1::IsSpecial::Valid == 0, + "viewTwoArgs: Args::Arg1::IsSpecial::Valid == 0"); + static_assert(Arg1::IsSimple::Value == 0, + "viewTwoArgs: Args::Arg1::IsSimple::Value == 0"); + static_assert(Arg1::IsSimple::Valid == 0, + "viewTwoArgs: Args::Arg1::IsSimple::Valid == 0"); + static_assert(Arg1::Valid == 0, + "viewTwoArgs: Args::Arg1::Valid"); + // StaticAssert is disabled in tests + static_assert(Arg1::StaticAssert, + "viewOneArg: Args::Arg1::StaticAssert"); + static_assert(std::is_same::value, + "viewTwoArgs: std::is_same)"); + + static_assert(Args::Valid == 0, "viewTwoArgs: Args::Valid == 0"); + // StaticAssert is disabled in tests + static_assert(Args::StaticAssert, "viewTwoArgs: Args::StaticAssert"); +} + +void tst_QHttpServerRouter::viewHandlerResponder() +{ + auto view = [] (QHttpServerResponder &&) { + }; + + using ViewTraits = QHttpServerRouterViewTraits; + using Args = typename ViewTraits::Arguments; + + static_assert(Args::Count == 1, + "viewResponder: Args::Count == 1"); + static_assert(Args::CapturableCount == 0, + "viewResponder: Args::CapturableCount == 0"); + static_assert(Args::PlaceholdersCount == 1, + "viewResponder: Args::PlaceholdersCount == 1"); + static_assert(Args::Last::IsRequest::Value == 0, + "viewResponder: Args::Last::IsRequest::Value == 0"); + static_assert(Args::Last::IsRequest::Valid == 0, + "viewResponder: Args::Last::IsRequest::Valid == 0"); + static_assert(Args::Last::IsResponder::Value, + "viewResponder: Args::Last::IsResponder::Value"); + static_assert(Args::Last::IsResponder::Valid, + "viewResponder: Args::Last::IsResponder::Valid"); + static_assert(Args::Last::IsSpecial::Value, + "viewResponder: Args::Last::IsSpecial::Value"); + static_assert(Args::Last::IsSpecial::Valid, + "viewResponder: Args::Last::IsSpecial::Valid"); + static_assert(Args::Last::IsSimple::Value == 0, + "viewResponder: Args::Last::IsSimple::Value == 0"); + static_assert(Args::Last::IsSimple::Valid == 0, + "viewResponder: Args::Last::IsSimple::Valid == 0"); + static_assert(Args::Last::Valid, + "viewResponder: Args::Last::Valid"); + static_assert(Args::Last::StaticAssert, + "viewResponder: Args::Last::StaticAssert"); + static_assert(std::is_same::value, + "viewNonArg: std::is_same"); + static_assert(Args::Valid, "viewResponder: Args::Valid"); + static_assert(Args::StaticAssert, "viewResponder: Args::StaticAssert"); +} + +void tst_QHttpServerRouter::viewHandlerRequest() +{ + auto view = [] (const QHttpServerRequest &) { + }; + + using ViewTraits = QHttpServerRouterViewTraits; + using Args = typename ViewTraits::Arguments; + + static_assert(Args::Count == 1, + "viewResponder: Args::Count == 1"); + static_assert(Args::CapturableCount == 0, + "viewResponder: Args::CapturableCount == 0"); + static_assert(Args::PlaceholdersCount == 1, + "viewResponder: Args::PlaceholdersCount == 1"); + static_assert(Args::Last::IsRequest::Value, + "viewResponder: Args::Last::IsRequest::Value"); + static_assert(Args::Last::IsRequest::Valid, + "viewResponder: Args::Last::IsRequest::Valid"); + static_assert(Args::Last::IsResponder::Value == 0, + "viewResponder: Args::Last::IsResponder::Value == 0"); + static_assert(Args::Last::IsResponder::Valid == 0, + "viewResponder: Args::Last::IsResponder::Valid == 0"); + static_assert(Args::Last::IsSpecial::Value, + "viewResponder: Args::Last::IsSpecial::Value"); + static_assert(Args::Last::IsSpecial::Valid, + "viewResponder: Args::Last::IsSpecial::Valid"); + static_assert(Args::Last::IsSimple::Value == 0, + "viewResponder: Args::Last::IsSimple::Value == 0"); + static_assert(Args::Last::IsSimple::Valid == 0, + "viewResponder: Args::Last::IsSimple::Valid == 0"); + static_assert(Args::Last::Valid, + "viewResponder: Args::Last::Valid"); + static_assert(Args::Last::StaticAssert, + "viewResponder: Args::Last::StaticAssert"); + static_assert(std::is_same::value, + "viewNonArg: std::is_same"); + static_assert(Args::Valid, "viewResponder: Args::Valid"); + static_assert(Args::StaticAssert, "viewResponder: Args::StaticAssert"); +} + +void tst_QHttpServerRouter::viewHandlerLastTwoSpecials() +{ + auto view = [] (const QHttpServerRequest &, QHttpServerResponder &&) { + }; + + using ViewTraits = QHttpServerRouterViewTraits; + using Args = typename ViewTraits::Arguments; + + static_assert(Args::Count == 2, + "viewTwoSpecialArgs: Args::Count == 2"); + static_assert(Args::CapturableCount == 0, + "viewTwoSpecialArgs: Args::CapturableCount == 1"); + static_assert(Args::PlaceholdersCount == 2, + "viewTwoSpecialArgs: Args::PlaceholdersCount == 0"); + + using Arg0 = typename Args::template Arg<0>; + static_assert(Arg0::IsRequest::Value, + "viewTwoSpecialArgs: Args::Arg0::IsRequest::Value"); + static_assert(Arg0::IsRequest::Valid, + "viewTwoSpecialArgs: Args::Arg0::IsRequest::Valid"); + static_assert(Arg0::IsResponder::Value == 0, + "viewTwoSpecialArgs: Args::Arg0::IsResponder::Value == 0"); + static_assert(Arg0::IsResponder::Valid == 0, + "viewTwoSpecialArgs: Args::Arg0::IsResponder::Valid == 0"); + static_assert(Arg0::IsSpecial::Value, + "viewTwoSpecialArgs: Args::Arg0::IsSpecial::Value"); + static_assert(Arg0::IsSpecial::Valid, + "viewTwoSpecialArgs: Args::Arg0::IsSpecial::Valid"); + static_assert(Arg0::IsSimple::Value == 0, + "viewTwoSpecialArgs: Args::Arg0::IsSimple::Value == 0"); + static_assert(Arg0::IsSimple::Valid == 0, + "viewTwoSpecialArgs: Args::Arg0::IsSimple::Valid == 0"); + static_assert(Arg0::Valid, + "viewTwoSpecialArgs: Args::Arg0::Valid"); + // StaticAssert is disabled in tests + static_assert(Arg0::StaticAssert, + "viewTwoSpecialArgs: Args::Arg0::StaticAssert"); + static_assert(std::is_same::value, + "viewNonArg: std::is_same"); + + using Arg1 = typename Args::template Arg<1>; + static_assert(Arg1::IsRequest::Value == 0, + "viewTwoSpecialArgs: Args::Arg1::IsRequest::Value == 0"); + static_assert(Arg1::IsRequest::Valid == 0, + "viewTwoSpecialArgs: Args::Arg1::IsRequest::Valid == 0"); + static_assert(Arg1::IsResponder::Value, + "viewTwoSpecialArgs: Args::Arg1::IsResponder::Value"); + static_assert(Arg1::IsResponder::Valid, + "viewTwoSpecialArgs: Args::Arg1::IsResponder::Valid"); + static_assert(Arg1::IsSpecial::Value, + "viewTwoSpecialArgs: Args::Arg1::IsSpecial::Value"); + static_assert(Arg1::IsSpecial::Valid, + "viewTwoSpecialArgs: Args::Arg1::IsSpecial::Valid"); + static_assert(Arg1::IsSimple::Value == 0, + "viewTwoSpecialArgs: Args::Arg1::IsSimple::Value == 0"); + static_assert(Arg1::IsSimple::Valid == 0, + "viewTwoSpecialArgs: Args::Arg1::IsSimple::Valid == 0"); + static_assert(Arg1::Valid, + "viewTwoSpecialArgs: Args::Arg1::Valid"); + static_assert(Arg1::StaticAssert, + "viewTwoSpecialArgs: Args::Arg1::StaticAssert"); + static_assert(std::is_same::value, + "viewTwoSpecialArgs: std::is_same"); + + static_assert(Args::Valid, "viewTwoSpecialArgs: Args::Valid"); + // StaticAssert is disabled in tests + static_assert(Args::StaticAssert, "viewTwoSpecialArgs: Args::StaticAssert"); +} + +QT_END_NAMESPACE + +QTEST_MAIN(tst_QHttpServerRouter) + +#include "tst_qhttpserverrouter.moc" diff --git a/3rdparty/qthttpserver/tests/tests.pro b/3rdparty/qthttpserver/tests/tests.pro new file mode 100644 index 0000000..804b740 --- /dev/null +++ b/3rdparty/qthttpserver/tests/tests.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +SUBDIRS = \ + auto diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c0775e9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,324 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.1.0) + +PROJECT(veyon) + +SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH}) +IF(CMAKE_BUILD_TYPE STREQUAL "Debug") + SET(VEYON_DEBUG TRUE) +ELSEIF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE relwithdebinfo) +ENDIF() + +IF(VEYON_DEBUG) + ADD_DEFINITIONS(-DVEYON_DEBUG) +ENDIF() + +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) + +IF(COMMAND CMAKE_POLICY) + CMAKE_POLICY(SET CMP0009 NEW) + CMAKE_POLICY(SET CMP0020 NEW) + cmake_policy(SET CMP0058 NEW) + CMAKE_POLICY(SET CMP0063 NEW) + if(${CMAKE_VERSION} VERSION_GREATER "3.12.0") + cmake_policy(SET CMP0075 NEW) + endif() + if(${CMAKE_VERSION} VERSION_GREATER "3.14.0") + cmake_policy(SET CMP0083 NEW) + endif() +ENDIF() + +INCLUDE(AddFileDependencies) +INCLUDE(CheckCSourceCompiles) +INCLUDE(CheckIncludeFiles) +INCLUDE(CheckFunctionExists) +INCLUDE(CheckSymbolExists) +INCLUDE(CheckTypeSize) +INCLUDE(GNUInstallDirs) +INCLUDE(ConfigureFiles) +include(SetDefaultTargetProperties) + +FIND_PACKAGE(Git) + +IF(GIT_FOUND) + EXECUTE_PROCESS(COMMAND "${GIT_EXECUTABLE}" describe --tags + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE VERSION_STRING) + STRING(REGEX REPLACE "^v([0-9]+)\\..*" "\\1" VERSION_MAJOR "${VERSION_STRING}") + STRING(REGEX REPLACE "^v[0-9]+\\.([0-9]+).*" "\\1" VERSION_MINOR "${VERSION_STRING}") + STRING(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" VERSION_PATCH "${VERSION_STRING}") + + # determine build number to use in NSIS installer and resource files + EXECUTE_PROCESS(COMMAND "${GIT_EXECUTABLE}" describe --tags + COMMAND cut -d "-" -f2 + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE VERSION_BUILD) + IF(NOT VERSION_BUILD GREATER 0) + SET(VERSION_BUILD 0) + ENDIF() + + # Get list of all committers from git history, ordered by number of commits. + # The CONTRIBUTORS file is used by AboutDialog. This information can be provided + # with -DCONTRIBUTORS=/path/to/CONTRIBUTORS instead. For instance, to generate + # this file for version 3.0.2, the command is: + # git shortlog -sne v3.0.2 | cut -c8- + SET(CONTRIBUTORS "${CMAKE_BINARY_DIR}/CONTRIBUTORS") + IF(NOT EXISTS "${CONTRIBUTORS}") + EXECUTE_PROCESS(COMMAND "${GIT_EXECUTABLE}" shortlog -s d160d147165271516589c304cb1b8f5e48f8527d..HEAD + COMMAND cut -c8- + COMMAND sort -f + OUTPUT_FILE "${CONTRIBUTORS}" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + TIMEOUT 10) + ENDIF() + +ENDIF() + +# can't retrieve version information as not building from Git repository? +IF(NOT VERSION_STRING) + SET(VERSION_MAJOR 4) + SET(VERSION_MINOR 5) + SET(VERSION_PATCH 3) + SET(VERSION_BUILD 0) + SET(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") +ELSE() + # remove leading character from tag name + STRING(REPLACE "v" "" VERSION_STRING "${VERSION_STRING}") +ENDIF() + +# set up compiler version variable +STRING(REGEX REPLACE "\\.[0-9]$" "" COMPILER_VERSION_MAJOR_MINOR ${CMAKE_CXX_COMPILER_VERSION}) + + +# set up basic platform variables +IF(WIN32) + SET(VEYON_BUILD_WIN32 1) +ENDIF() +IF(APPLE) + SET(VEYON_BUILD_APPLE 1) +ENDIF() +IF(UNIX AND NOT ANDROID) + SET(VEYON_BUILD_LINUX 1) +ENDIF() +IF(ANDROID) + SET(VEYON_BUILD_ANDROID 1) +ENDIF() + +IF(WIN64) + SET(VEYON_BUILD_WIN64 TRUE) +ENDIF(WIN64) + +# set up library and plugin path variables +IF(VEYON_BUILD_ANDROID) + SET(CMAKE_INSTALL_PREFIX "/") + SET(VEYON_LIB_DIR "libs/${ANDROID_ABI}") + SET(VEYON_INSTALL_PLUGIN_DIR "${VEYON_LIB_DIR}/veyon") + SET(VEYON_INSTALL_DATA_DIR "${CMAKE_INSTALL_DATADIR}/veyon") + SET(VEYON_PLUGIN_DIR "") + SET(VEYON_TRANSLATIONS_DIR "/translations") +ELSE() + IF(CMAKE_INSTALL_LIBDIR) + SET(VEYON_LIB_DIR "${CMAKE_INSTALL_LIBDIR}/veyon" CACHE INTERNAL "Veyon library directory") + ELSE() + SET(VEYON_LIB_DIR lib/veyon CACHE INTERNAL "Veyon library directory") + ENDIF() + + SET(VEYON_INSTALL_PLUGIN_DIR "${VEYON_LIB_DIR}") + SET(VEYON_INSTALL_DATA_DIR "${CMAKE_INSTALL_DATADIR}/veyon") + + IF(WIN32) + SET(VEYON_PLUGIN_DIR "plugins") + SET(VEYON_TRANSLATIONS_DIR "translations") + ELSE() + SET(VEYON_PLUGIN_DIR "../${VEYON_LIB_DIR}") + SET(VEYON_TRANSLATIONS_DIR "../share/veyon/translations") + ENDIF() +ENDIF() + + +SET(VEYON_CORE_INCLUDE_DIR core/include) + +# find required Qt5 modules +FIND_PACKAGE(Qt5Core REQUIRED) +FIND_PACKAGE(Qt5Concurrent REQUIRED) +FIND_PACKAGE(Qt5Gui REQUIRED) +FIND_PACKAGE(Qt5Widgets REQUIRED) +FIND_PACKAGE(Qt5Network REQUIRED) +FIND_PACKAGE(Qt5LinguistTools REQUIRED) + +# find required libraries +find_package(QCA REQUIRED) +find_package(OpenSSL REQUIRED) + +# find Linux-specific packages +IF(VEYON_BUILD_LINUX) + INCLUDE(XdgInstall) +ENDIF() + +find_package(LibVNCClient 0.9.13) + +if(LibVNCClient_FOUND) + include(CheckCSourceCompiles) + set(CMAKE_REQUIRED_LIBRARIES LibVNC::LibVNCClient) + check_c_source_compiles(" +#include + +int main() +{ + rfbClient* client = rfbGetClient( 8, 3, 4 ); + client->connectTimeout = 1; + client->readTimeout = 1; + return 0; +} +" + LIBVNCCLIENT_SUPPORTS_TIMEOUTS) + if(NOT LIBVNCCLIENT_SUPPORTS_TIMEOUTS) + message(FATAL_ERROR "Outdated development version of LibVNCClient found") + endif() + unset(CMAKE_REQUIRED_LIBRARIES) +else() + message(WARNING "Performing internal build of LibVNCClient which requires additional development packages") + find_package(ZLIB REQUIRED) + find_package(PNG REQUIRED) + find_package(JPEG REQUIRED) + find_package(LZO REQUIRED) + set(CMAKE_THREAD_PREFER_PTHREAD TRUE) + find_package(Threads REQUIRED) +endif() + +option(WITH_CORE_ONLY "Build core library only" OFF) +option(WITH_ADDONS "Build add-ons" OFF) +option(WITH_PCH "Reduce compile time by using precompiled headers (requires CMake >= 3.16)" ON) +option(WITH_UNITY_BUILD "Reduce compile time by using cmake unity builds (requires CMake >= 3.16)" ON) + +option(WITH_MODEL_TESTERS "Build with model testers (turn on for debugging only)" OFF) + +if(${CMAKE_VERSION} VERSION_LESS "3.16.0") + set(WITH_PCH OFF) + set(WITH_UNITY_BUILD OFF) +elseif(WITH_UNITY_BUILD) + set(CMAKE_UNITY_BUILD ON) +endif() + +if(WITH_MODEL_TESTERS) +find_package(Qt5Test REQUIRED) +set(VEYON_DEBUG_LIBRARIES Qt5::Test) +endif() + +IF(SANITIZE) +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread -fsanitize=undefined") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread -fsanitize=undefined") +ENDIF() + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fno-exceptions -std=c++11 -fstack-protector-strong ${CXXFLAGS}") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fstack-protector-strong ${CFLAGS}") + +IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) +MESSAGE(WARNING "Not using -fvisibility=hidden as it is broken with GCC < 6.0") +ELSE() +SET(CMAKE_C_VISIBILITY_PRESET hidden) +SET(CMAKE_CXX_VISIBILITY_PRESET hidden) +SET(CMAKE_VISIBILITY_INLINES_HIDDEN 1) +IF(LTO) +SET(LTO_FLAGS "-flto=4") +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LTO_FLAGS}") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LTO_FLAGS}") +SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LTO_FLAGS}") +SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LTO_FLAGS}") +SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LTO_FLAGS}") +ENDIF() +ENDIF() + +add_definitions(-DQT_DEPRECATED_WARNINGS -DQT_DISABLE_DEPRECATED_BEFORE=0x050e00 -D_FORTIFY_SOURCE=2 -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_KEYWORDS) + +FILE(GLOB_RECURSE IN_FILES RELATIVE ${CMAKE_SOURCE_DIR} "*config.h.in" "*.rc.in" "*.desktop.in" "*.policy.in" "*.service.in" "*.manifest.in" "*.nsi.in") +CONFIGURE_FILES(${IN_FILES}) + +SET(CMAKE_AUTOMOC TRUE) +SET(CMAKE_AUTOUIC TRUE) +SET(CMAKE_AUTORCC TRUE) + +SET(3rdparty_DIR ${CMAKE_SOURCE_DIR}/3rdparty) +SET(ultravnc_DIR ${3rdparty_DIR}/ultravnc) +SET(libvncserver_DIR ${3rdparty_DIR}/libvncserver) +SET(x11vnc_DIR ${3rdparty_DIR}/x11vnc) +SET(libfakekey_DIR ${3rdparty_DIR}/libfakekey) +set(qthttpserver_DIR ${3rdparty_DIR}/qthttpserver) + +SET(CMAKE_SKIP_BUILD_RPATH FALSE) +SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) +SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${VEYON_LIB_DIR}") +SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + +# make sub-directories +add_subdirectory(core) +if(NOT WITH_CORE_ONLY) + add_subdirectory(server) + add_subdirectory(service) + add_subdirectory(master) + add_subdirectory(configurator) + add_subdirectory(cli) + add_subdirectory(worker) + add_subdirectory(plugins) + add_subdirectory(translations) +endif() +if(WITH_ADDONS) + add_subdirectory(addons) +endif() + +# +# add Windows installer related targets +# +if(WIN32) + include(WindowsInstaller) +endif() + +# +# package generation +# +INCLUDE(cmake/CPackDefinitions.cmake) + + + +# +# display configuration information +# + +MESSAGE("\n" +"Veyon build summary\n" +"--------------------\n" +"* Version : ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_BUILD} (${VERSION_STRING})\n" +"* Install prefix : ${CMAKE_INSTALL_PREFIX}\n" +"* Library directory : ${CMAKE_INSTALL_PREFIX}/${VEYON_LIB_DIR}\n" +"* Plugin directory : ${CMAKE_INSTALL_PREFIX}/${VEYON_INSTALL_PLUGIN_DIR}\n" +"* Build type : ${CMAKE_BUILD_TYPE}\n" +"* Build platform : ${CMAKE_SYSTEM_PROCESSOR}\n" +"* Compile flags : ${CMAKE_C_FLAGS} (CXX: ${CMAKE_CXX_FLAGS})\n" +"* Use precompiled headers : ${WITH_PCH}\n" +"* Use unity build : ${WITH_UNITY_BUILD}\n" +) + +IF(VEYON_BUILD_ANDROID) + INCLUDE(AndroidDeployQt) + SET(CMAKE_ANDROID_DIR "${CMAKE_SOURCE_DIR}/android") + SET(_CMAKE_ANDROID_DIR "${CMAKE_ANDROID_DIR}") + SET(ANDROID_INSTALL_DIR "${CMAKE_BINARY_DIR}/install") + SET(ANDROID_EXTRA_PLUGINS ${ANDROID_INSTALL_DIR}/${VEYON_LIB_DIR}/veyon/ ${QT_DIR}/lib/qca-qt5/crypto) + FILE(GLOB ANDROID_EXTRA_LIBS ${ANDROID_INSTALL_DIR}/${VEYON_LIB_DIR}/*.so) + LIST(APPEND ANDROID_EXTRA_LIBS "${ANDROID_SYSROOT_GENERIC}/libc++_shared.so") + LIST(APPEND ANDROID_EXTRA_LIBS "${QT_DIR}/lib/libldap.so" + "${QT_DIR}/lib/liblber.so" + "${QT_DIR}/lib/libsasl2.so") + androiddeployqt("veyon-master" "${ANDROID_ADDITIONAL_FIND_ROOT_PATH};${CMAKE_BINARY_DIR}/core") + SET_TARGET_PROPERTIES(create-apk-veyon-master PROPERTIES ANDROID_APK_DIR "${CMAKE_ANDROID_DIR}") + + add_custom_target(prepare-apk + COMMAND rm -rf ${ANDROID_INSTALL_DIR} + COMMAND cd ${CMAKE_BINARY_DIR}/core && make DESTDIR=${ANDROID_INSTALL_DIR} install + COMMAND cd ${CMAKE_BINARY_DIR}/plugins && make DESTDIR=${ANDROID_INSTALL_DIR} install + ) + + add_dependencies(create-apk-veyon-master prepare-apk) +ENDIF() diff --git a/CONTRIBUTORS b/CONTRIBUTORS new file mode 100644 index 0000000..516145a --- /dev/null +++ b/CONTRIBUTORS @@ -0,0 +1,26 @@ +1JackBlack1 +Aleksey Avdeev +Babycasèny Gavèrnmènt +barteekelder +Denis Medvedev +Eric Shamow +Extremas Saruncis +Florian Best +Gihun Ham +José Antonio Muñoz Jiménez +KHALDOUN Mohsen +Madan +Marco Bakera +Michael Gajda +Michael Wehr +miharix +Mike Gabriel +Paulo Henrique +Peter Gagarinov +Raphaël RIGNIER +Robert Marmorstein +Ryan Tandy +Sergio Garnica +SlrG +swhaat +Tobias Junghans diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..1168b48 --- /dev/null +++ b/COPYING @@ -0,0 +1,352 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + +------------------------------------------------------------------------- + +In addition, as a special exception, Tobias Junghans gives permission to link the +code of its release of Veyon with the OpenSSL project's "OpenSSL" library (or +modified versions of the "OpenSSL" library that use the same license as the +original version), and distribute the linked executables. + +You must comply with the GNU General Public License version 2 in all respects +for all of the code used other than the "OpenSSL" code. If you modify this +file, you may extend this exception to your version of the file, but you are +not obligated to do so. If you do not wish to do so, delete this exception +statement from your version of this file. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..34b6c81 --- /dev/null +++ b/INSTALL @@ -0,0 +1,91 @@ +INSTALLING AND SETTING UP VEYON FOR USE +======================================= + +Please note that all instructions in this file apply to the Linux version of +Veyon. Users of the Windows version of Veyon should run the installer which will +guide you through the installation. Afterwards the graphical Veyon Configurator +helps to set up Veyon. + +For further information you can also take a look at Veyon administrator manual: + + http://docs.veyon.io/en/latest/admin/index.html + + +Installing Veyon +---------------- + +Requirements: + +First you should download the necessary packages depending on your +Linux distribution. If there're no packages for your distribution, +try to build Veyon yourself. See according section in README file +for details. + +If there're unfulfilled requirements, your package management system (dpkg, +RPM etc.) will complain about it. + + +Installing: + +First you have to install the necessary packages on your computer(s). +On clients you only have to install the packages veyon: + +on DPKG-based systems: + + dpkg -i veyon...deb + +on RPM-based systems: + + rpm -i veyon...rpm + +Of course, if your linux distribution has available such package you can install it from repositories: + + apt install veyon + + dnf install veyon + + +Essentially, there are 3 main apps in package: + +- veyon-configurator +- veyon-service +- veyon-master + + +On the master computer you must be sure to have installed veyon-master. +On clients computers remove veyon-master if you installed it. + + + +Setup a Veyon client +-------------------- + +On clients you have to make sure that the Veyon Service is being started when +either X is started or a user logs on. There're several methods to achieve this +(this is why it's not done automatically when installing the package): + +1. Add the appropriate command ("veyon-service &") to /etc/X11/xinit/xinitrc or + add an according desktop file to /etc/xdg/autostart. + This method isn't recommend, because ICA then runs with the user's + privileges -> the user is able to kill ICA and get out of teacher's + control. + +2. add "veyon-service &" to /etc/X11/xdm/Xsetup. On some systems, this + doesn't take effect, so either edit /etc/kde4/kdm/Xsetup or + /etc/X11/gdm/Xsetup + +You can easily test whether all is fine by running telnet on port 11100 of the +according compter. + +example: + + telnet 192.168.1.2 11100 + + +You should see something like "RFB 003.008". + + +Setting up and configuring Veyon +--------------------------------- + +Please refer to the official Veyon Administrator Manual at http://docs.veyon.io/en/latest/admin/index.html diff --git a/README.md b/README.md new file mode 100644 index 0000000..e650084 --- /dev/null +++ b/README.md @@ -0,0 +1,162 @@ +# Veyon - Virtual Eye On Networks + +[![Build status](https://travis-ci.com/veyon/veyon.svg?branch=4.5)](https://travis-ci.com/veyon/veyon) +[![Latest stable release](https://img.shields.io/github/release/veyon/veyon.svg?maxAge=3600)](https://github.com/veyon/veyon/releases) +[![Overall downloads on Github](https://img.shields.io/github/downloads/veyon/veyon/total.svg?maxAge=3600)](https://github.com/veyon/veyon/releases) +[![Documentation Status](https://readthedocs.org/projects/veyon/badge/?version=latest)](https://docs.veyon.io/) +[![Localise on Transifex](https://img.shields.io/badge/localise-on_transifex-green.svg)](https://www.transifex.com/veyon-solutions/veyon/) +[![license](https://img.shields.io/badge/license-GPLv2-green.svg)](LICENSE) + + +## What is Veyon? + +Veyon is a free and open source software for computer monitoring and classroom +management supporting Windows and Linux. It enables teachers to view and control +computer labs and interact with students. Veyon is available in many different +languages and provides numerous features supporting teachers and administrators +at their daily work: + + * Overview: monitor all computers in one or multiple locations or classrooms + * Remote access: view or control computers to watch and support users + * Demo: broadcast the teacher's screen in realtime (fullscreen/window) + * Screen lock: draw attention to what matters right now + * Communication: send text messages to students + * Start and end lessons: log in and log out users all at once + * Screenshots: record learning progress and document infringements + * Programs & websites: launch programs and open website URLs remotely + * Teaching material: distribute and open documents, images and videos easily + * Administration: power on/off and reboot computers remotely + + +## License + +Copyright (c) 2004-2021 Tobias Junghans / Veyon Solutions. + +See the file COPYING for the GNU GENERAL PUBLIC LICENSE. + + +## Installation and configuration + +Please refer to the official Veyon Administrator Manual at https://docs.veyon.io/en/latest/admin/index.html +for information on the installation and configuration of Veyon. + + +## Usage + +Please refer to the official Veyon User Manual at https://docs.veyon.io/en/latest/user/index.html +for information on how to use Veyon. + + +## Veyon on Linux + +### Downloading sources + +First grab the latest sources by cloning the Git repository and fetching all submodules: + + git clone --recursive https://github.com/veyon/veyon.git && cd veyon + + +### Installing dependencies + +Requirements for Debian-based distributions: + +- Build tools: g++ libc6-dev make cmake dpkg-dev +- Qt5: qtbase5-dev qtbase5-private-dev qtbase5-dev-tools qttools5-dev qttools5-dev-tools +- X11: xorg-dev libxtst-dev libfakekey-dev +- libjpeg: libjpeg-dev provided by libjpeg-turbo8-dev or libjpeg62-turbo-dev +- zlib: zlib1g-dev +- OpenSSL: libssl-dev +- PAM: libpam0g-dev +- procps: libprocps-dev +- LZO: liblzo2-dev +- QCA: libqca-qt5-2-dev +- LDAP: libldap2-dev +- SASL: libsasl2-dev + +As root you can run + + apt install g++ libc6-dev make cmake qtbase5-dev qtbase5-private-dev \ + qtbase5-dev-tools qttools5-dev qttools5-dev-tools \ + xorg-dev libxtst-dev libfakekey-dev libjpeg-dev zlib1g-dev libssl-dev libpam0g-dev \ + libprocps-dev liblzo2-dev libqca-qt5-2-dev libldap2-dev \ + libsasl2-dev + + + +Requirements for RedHat-based distributions: + +- Build tools: gcc-c++ make cmake rpm-build +- Qt5: qt5-devel qt5-qtbase-private-devel +- X11: libXtst-devel libXrandr-devel libXinerama-devel libXcursor-devel libXrandr-devel libXdamage-devel libXcomposite-devel libXfixes-devel libfakekey-devel +- libjpeg: libjpeg-turbo-devel +- zlib: zlib-devel +- OpenSSL: openssl-devel +- PAM: pam-devel +- procps: procps-devel +- LZO: lzo-devel +- QCA: qca-devel qca-qt5-devel +- LDAP: openldap-devel +- SASL: cyrus-sasl-devel + +As root you can run + + dnf install gcc-c++ make cmake rpm-build qt5-devel libXtst-devel libXrandr-devel libXinerama-devel libXcursor-devel \ + libXrandr-devel libXdamage-devel libXcomposite-devel libXfixes-devel libjpeg-turbo-devel zlib-devel \ + openssl-devel pam-devel procps-devel lzo-devel qca-devel qca-qt5-devel openldap-devel cyrus-sasl-devel + + +### Configuring and building sources + +Run the following commands: + + mkdir build + cd build + cmake .. + make -j4 + +NOTE: If you want to build a .deb or .rpm package for this software, instead of the provided cmake command, you should use: + + cmake -DCMAKE_INSTALL_PREFIX=/usr .. + +to install package files in /usr instead of /usr/local. + +If some requirements are not fullfilled, CMake will inform you about it and +you will have to install the missing software before continuing. + +You can now generate a package (.deb or .rpm depending what system you are in). + +For generating a package you can run + + fakeroot make package + +Then you'll get something like veyon_x.y.z_arch.deb or veyon-x.y.z.arch.rpm + +Alternatively you can install the built binaries directly (not recommended for +production systems) by running the following command as root: + + make install + +### Arch linux + +A PKGBUILD can be found in the [AUR](https://aur.archlinux.org/packages/veyon/). + +### PPA + +This PPA contains official Veyon packages for Ubuntu suitable for use both on desktop computers and ARM boards (e.g. Raspberry Pi). Even though only packages for LTS releases are available they should work for subsequent non-LTS releases as well. + + sudo add-apt-repository ppa:veyon/stable + sudo apt-get update + +## Join development + +If you are interested in Veyon, its programming, artwork, testing or something like that, you're welcome to participate in the development of Veyon! + +Before starting the implementation of a new feature, please always open an issue at https://github.com/veyon/veyon/issues to start a discussion about your intended implementation. There may be different ideas, improvements, hints or maybe an already ongoing work on this feature. + + +## More information + +* https://veyon.io/ +* https://docs.veyon.io/ +* https://facebook.com/veyon.io/ +* https://twitter.com/veyon_io diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt new file mode 100644 index 0000000..7e13bf4 --- /dev/null +++ b/cli/CMakeLists.txt @@ -0,0 +1,19 @@ +INCLUDE(BuildVeyonApplication) +INCLUDE(WindowsBuildHelpers) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src) +SET(cli_SOURCES src/main.cpp) + +build_veyon_application(veyon-cli ${cli_SOURCES}) +TARGET_LINK_LIBRARIES(veyon-cli veyon-core Qt5::Network) + +ADD_WINDOWS_RESOURCE(veyon-cli ${CMAKE_CURRENT_BINARY_DIR}/veyon-cli.rc) +MAKE_CONSOLE_APP(veyon-cli) + +IF(VEYON_BUILD_WIN32) +build_veyon_application(veyon-wcli ${cli_SOURCES}) +TARGET_LINK_LIBRARIES(veyon-wcli veyon-core Qt5::Network) + +ADD_WINDOWS_RESOURCE(veyon-wcli ${CMAKE_CURRENT_BINARY_DIR}/veyon-wcli.rc) +MAKE_GRAPHICAL_APP(veyon-wcli) +ENDIF() diff --git a/cli/data/veyon-cli.exe.manifest.in b/cli/data/veyon-cli.exe.manifest.in new file mode 100644 index 0000000..d9b1f3a --- /dev/null +++ b/cli/data/veyon-cli.exe.manifest.in @@ -0,0 +1,14 @@ + + + + Veyon CLI (console version) + + + + + + + + + + diff --git a/cli/data/veyon-wcli.exe.manifest.in b/cli/data/veyon-wcli.exe.manifest.in new file mode 100644 index 0000000..10b1b9c --- /dev/null +++ b/cli/data/veyon-wcli.exe.manifest.in @@ -0,0 +1,14 @@ + + + + Veyon CLI (non-console version) + + + + + + + + + + diff --git a/cli/src/main.cpp b/cli/src/main.cpp new file mode 100644 index 0000000..2740c6d --- /dev/null +++ b/cli/src/main.cpp @@ -0,0 +1,203 @@ +/* + * main.cpp - main file for Veyon CLI + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include + +#include "CommandLineIO.h" +#include "CommandLinePluginInterface.h" +#include "Logger.h" +#include "PluginManager.h" + + +int main( int argc, char **argv ) +{ + QCoreApplication* app = nullptr; + +#ifdef Q_OS_LINUX + // do not create graphical application if DISPLAY is not available + if( qEnvironmentVariableIsSet( "DISPLAY" ) == false ) + { + app = new QCoreApplication( argc, argv ); + } + else + { + app = new QApplication( argc, argv ); + } +#else + app = new QApplication( argc, argv ); +#endif + + const auto arguments = app->arguments(); + + if( arguments.count() == 2 ) + { + if( arguments.last() == QLatin1String("-v") || arguments.last() == QLatin1String("--version") ) + { + CommandLineIO::print( VeyonCore::version() ); + delete app; + return 0; + } + else if( arguments.last() == QLatin1String("about") ) + { + CommandLineIO::print( QStringLiteral("Veyon: %1 (%2)").arg( VeyonCore::version() ).arg( QLatin1String(__DATE__) ) ); + CommandLineIO::print( QStringLiteral("Qt: %1 (built against %2/%3)"). + arg( QLatin1String(qVersion() ) ). + arg( QLatin1String(QT_VERSION_STR) ). + arg( QSysInfo::buildAbi() ) ); + CommandLineIO::print( QStringLiteral("OpenSSL: %1").arg( QLatin1String(SSLeay_version(SSLEAY_VERSION)) ) ); + delete app; + return 0; + } + } + + // disable logging at all in order to avoid clobbering + if( qEnvironmentVariableIsEmpty( Logger::logLevelEnvironmentVariable() ) ) + { + qputenv( Logger::logLevelEnvironmentVariable(), QByteArray::number( static_cast(Logger::LogLevel::Nothing) ) ); + } + + auto core = new VeyonCore( app, VeyonCore::Component::CLI, QStringLiteral("CLI") ); + + QHash commandLinePluginInterfaces; + const auto pluginObjects = core->pluginManager().pluginObjects(); + for( auto pluginObject : pluginObjects ) + { + auto commandLinePluginInterface = qobject_cast( pluginObject ); + if( commandLinePluginInterface ) + { + commandLinePluginInterfaces[commandLinePluginInterface] = pluginObject; + } + } + + const auto module = arguments.value( 1 ); + + for( auto it = commandLinePluginInterfaces.constBegin(), end = commandLinePluginInterfaces.constEnd(); it != end; ++it ) + { + if( it.key()->commandLineModuleName() == module ) + { + auto runResult = CommandLinePluginInterface::Unknown; + + if( arguments.count() > 2 ) + { + const auto handler = QStringLiteral( "handle_%1(QStringList)" ).arg( arguments[2] ); + if( it.value()->metaObject()->indexOfMethod( handler.toUtf8().constData() ) >= 0 ) + { + QMetaObject::invokeMethod( it.value(), + QStringLiteral( "handle_%1" ).arg( arguments[2] ).toUtf8().constData(), + Qt::DirectConnection, + Q_RETURN_ARG(CommandLinePluginInterface::RunResult, runResult), + Q_ARG( QStringList, arguments.mid( 3 ) ) ); + } + else if( arguments[2] != QLatin1String("help") ) + { + runResult = CommandLinePluginInterface::InvalidCommand; + } + } + else if( it.value()->metaObject()->indexOfMethod("handle_main()") >= 0 ) + { + QMetaObject::invokeMethod( it.value(), + "handle_main", + Qt::DirectConnection, + Q_RETURN_ARG(CommandLinePluginInterface::RunResult, runResult) ); + } + else + { + runResult = CommandLinePluginInterface::NotEnoughArguments; + } + + switch( runResult ) + { + case CommandLinePluginInterface::NoResult: + return 0; + case CommandLinePluginInterface::Successful: + CommandLineIO::print( VeyonCore::tr( "[OK]" ) ); + return 0; + case CommandLinePluginInterface::Failed: + CommandLineIO::print( VeyonCore::tr( "[FAIL]" ) ); + return -1; + case CommandLinePluginInterface::InvalidCommand: + CommandLineIO::error( VeyonCore::tr( "Invalid command!" ) ); + break; + case CommandLinePluginInterface::InvalidArguments: + CommandLineIO::error( VeyonCore::tr( "Invalid arguments given" ) ); + return -1; + case CommandLinePluginInterface::NotEnoughArguments: + CommandLineIO::error( VeyonCore::tr( "Not enough arguments given - " + "use \"%1 help\" for more information" ).arg( module ) ); + return -1; + case CommandLinePluginInterface::NotLicensed: + CommandLineIO::error( VeyonCore::tr( "Plugin not licensed" ) ); + return -1; + case CommandLinePluginInterface::Unknown: + break; + default: + CommandLineIO::error( VeyonCore::tr( "Unknown result!" ) ); + return -1; + } + + auto commands = it.key()->commands(); + std::sort( commands.begin(), commands.end() ); + + CommandLineIO::print( VeyonCore::tr( "Available commands:" ) ); + for( const auto& command : commands ) + { + CommandLineIO::print( QStringLiteral(" %1 - %2").arg( command, it.key()->commandHelp( command ) ) ); + } + + delete core; + delete app; + return -1; + } + } + + int rc = -1; + + if( module == QLatin1String("help") ) + { + CommandLineIO::print( VeyonCore::tr( "Available modules:" ) ); + rc = 0; + } + else + { + CommandLineIO::error( VeyonCore::tr( "No module specified or module not found - available modules are:" ) ); + } + + QStringList modulesHelpStrings; + for( auto it = commandLinePluginInterfaces.constBegin(), end = commandLinePluginInterfaces.constEnd(); it != end; ++it ) + { + modulesHelpStrings.append( QStringLiteral( "%1 - %2" ).arg( it.key()->commandLineModuleName(), + it.key()->commandLineModuleHelp() ) ); + } + + std::sort( modulesHelpStrings.begin(), modulesHelpStrings.end() ); + std::for_each( modulesHelpStrings.begin(), modulesHelpStrings.end(), [](const QString& s) { + CommandLineIO::print( QStringLiteral( " " ) + s ); } ); + + delete core; + delete app; + + return rc; +} diff --git a/cli/veyon-cli.1 b/cli/veyon-cli.1 new file mode 100644 index 0000000..75251bf --- /dev/null +++ b/cli/veyon-cli.1 @@ -0,0 +1,371 @@ +.TH VEYON-CLI 1 2018-12-07 Veyon +.SH NAME +veyon-cli \- Veyon Command Line Interface +.SH SYNOPSIS +\fBveyon-cli\fP \fIhelp\fR + +\fBveyon-cli\fP \fI\fR \fIhelp\fR + +\fBveyon-cli\fP \fI\fR \fIhelp\fR \fI\fR + +\fBveyon-cli\fP \fI\fR \fI\fR [\fIparameters\fP] + +.SH DESCRIPTION + +\fBVEYON-CLI\fR is a command line tool that in addition to the Veyon +Configurator allows various configuration adjustments, automated tasks +and the use of some Veyon functions without graphical interaction. The +program is run either interactively on the command line or script +controlled with usually elevated privileges. + +For administrative tasks, the Veyon Configurator and the command line +tool Veyon Control are available. Veyon Control can be started via the +command \fBveyon-cli\fR in the command line. If the Veyon installation +directory is not in the $PATH environment variable, you must first change +to the installation directory or prepend the directory to the program +name. + +Veyon Control falls into various control modules. For each module a +set of commands is available. + +Available modules are: + +.TP +\fIauthkeys\fR +Commands for managing authentication keys + +.TP +\fIconfig\fR +Commands for managing the configuration of Veyon + +.TP +\fIldap\fR +Commands for configuring and testing LDAP/AD integration + +.TP +\fInetworkobjects\fR +Commands for managing the builtin network object directory + +.TP +\fIremoteaccess\fR +Remote view or control a computer + +.TP +\fIservice\fR +Commands for configuring and controlling Veyon Service + +.TP +\fIshell\fR +Commands for shell functionalities + +If the \fBveyon-cli\fR command is called with the \fIhelp\fR parameter, a +list of all available modules is displayed. The list can vary depending +on the installed Veyon plugins. + +Each module supports the \fIhelp\fR command, so that a list of all available +commands can be displayed for each module. + +.SH AUTHKEYS MODULE + +The authkeys module allows the management of authentication keys so that +common operations such as importing an authentication key or assigning a +user group can be easily automated. + +.TP +\fIcreate \fR +This command creates a new pair of authentication keys and stores the +private and public keys in the configured key directory. The parameter +must be a name for the key, which may only contain letters. + +.TP +\fIdelete \fR +This command deletes the authentication key from the configured key +directory. Please note that a key cannot be recovered once it has been +deleted. + +.TP +\fIexport []\fR +This command exports the to authentication key. If is +not specified, the file name is derived from the name and type of . +.TP +\fIextract \fR +This command extracts the public key part from the private key and +saves it as the associated public key. When setting up another master +computer, it is therefore sufficient to transfer the private key. The +public key can then be extracted. +.TP +\fIimport []\fR +This command imports the authentication key from . If +is not specified, the file name is derived from the name and type of +. +.TP +\fIlist [details]\fR +This command lists all available authentication keys in the configured +key directory. If the details option is specified, a table with key +details is output instead. Some details may be missing if a key cannot be +accessed, e.g. due to missing read permissions. +.TP +\fIsetaccessgroup \fR +This command adjusts the file access permissions on the so that +only the user group has read access to it. + +.SH CONFIG MODULE + +Available commands for the config module are: + +.TP +.I clear +Clear system-wide Veyon configuration. This command resets the entire +local configuration by deleting all configuration keys. Use this command +to recreate a defined state before importing another configuration: + + \fBveyon-cli\fR config clear + +.TP +.I export +Export configuration to given file.This command exports the local +configuration to a file. The name of the destination file must be +specified as an additional parameter: + + \fBveyon-cli\fR config export myconfig.json + +.TP +.I get +Read and output configuration value for given key. This command allows +reading a single configuration key. The name of the key must be supplied +as a parameter. + + \fBveyon-cli\fR config get Network/PrimaryServicePort + +.TP +.I import +Import configuration from given file. This command imports a previously +exported configuration file into the local configuration. The name of the +configuration file to be imported must be specified as an additional +argument: + + \fBveyon-cli\fR config import myconfig.json + +.TP +.I list +List all configuration keys and values. This command shows a list of all +configuration keys and their corresponding values. + + \fBveyon-cli\fR config list + +Using this command you can find the names of configuration keys in order +to get oder set them one by one. + +.TP +.I set +Write given value to given configuration key. With this command a single +configuration key can be written. The name of the key and the desired +value must be passed as additional arguments: + + \fBveyon-cli\fR config set Network/PrimaryServicePort 12345 + + \fBveyon-cli\fR config set Service/Autostart true + + \fBveyon-cli\fR config set UI/Language de_DE + +.TP +.I unset +Unset (remove) given configuration key. This command deletes a single +configuration key resulting in Veyon using the internal index:`default +value for this key. The name of the key must be passed as an additional +argument: + + \fBveyon-cli\fR config unset Directories/Screenshots + +.TP +.I upgrade +Upgrade and save configuration of program and plugins. With this command +the configuration of Veyon and all plugins can be updated and saved. This +may be necessary if settings or configuration formats have changed due to +program or plugin updates. + +.SH LDAP MODULE +There are several LDAP specific operations provided through Veyon Control +All operations are provided through the LDAP module. All lists of all +supported commands is printed on entering + + \fBveyon-cli\fR ldap help + +whereas command specific help texts can be shown via + + \fBveyon-cli\fR ldap help + +The available commands are: + +.TP +.I autoconfigurebasedn +This command can be used to automatically determine the used Base DN and +permanently write it to the configuration. An LDAP server URL and +optionally a naming context attribute have to be supplied as parameters: + + \fBveyon-cli\fR ldap autoconfigurebasedn ldap://192.168.1.2/ namingContexts + + \fBveyon-cli\fR ldap autoconfigurebasedn ldap://Administrator:MYPASSWORD@192.168.1.2:389/ + +.TP +.I query +This command allows querying LDAP objects (rooms, computers, groups, +users) and is designed mainly for debugging purposes. However, the +function can also be used for developing scripts that may be helpful for +system integration. + + \fBveyon-cli\fR ldap query users + + \fBveyon-cli\fR ldap query computers + +.SH NETWORKOBJECTS MODULE + +Veyon provides a built-in network object directory that can be used when +no LDAP server is available. This network object directory can be managed +in the Veyon Configurator as well as on the command line. Certain +operations such as CSV import are currently only available on the command +line. For most commands, a detailed description with examples is +available in the command-specific help. The following commands can be +used in the NETWORKOBJECTS module: + + +.TP +.I add [ ] +This command adds an object, where can be room or computer. + can be specified as name or UUID. + +.TP +.I clear +This command resets the entire network object directory, i.e. all rooms +and computers are removed. This operation is particularly useful before +any automated import. + +.TP +.I dump +This command outputs the complete network object directory as a flat +table. Each property such as object UID, type or name is displayed as a +separate column. + +.TP +.I export [room ] [format ] +This command can be used to export either the complete network object +dictionary or only the specified room to a text file. The formatting can +be controlled via a format string and the variables it contains, so that, +for example, a CSV file can be generated. Valid variables are %type%, +%name%, %host%, %mac% and %room%. Various examples are given in the +command help (\fBveyon-cli\fR networkobjects help export). + +.TP +.I import [room < SPACE>] [format ] [regex ] +This command can be used to import a text file into the network object +directory. The processing of the input data can be controlled via a +format string or a regular expression and contained variables. This way +both CSV files and otherwise structured data can be imported. Valid +variables are %name%, %host%, %mac% and %room%. Various examples are +given in the command help (\fBveyon-cli\fR networkobjects help import). + +.TP +.I list +This command prints the complete network object directory as a formatted +list. Unlike the dump command, the hierarchy of rooms and computers is +represented by appropriate formatting. + +.TP +.I remove +This command removes the specified object from the directory. +can be specified as name or UUID. When a room is removed, all computers +in it are also removed. + +.SH REMOTEACCESS MODULE + +The remoteaccess module provides functions for a graphical remote access +to computers. These are the same function that can be accessed from the +Veyon Master. For example, the function provided by the command line tool +can be used to create a program shortcut for direct access to a +particular computer. + +.TP +.I control +This command opens a window with the remote control function that can be +used to control a remote computer. The computer name or IP address (and +optionally the TCP port) must be passed as an argument: + + \fBveyon-cli\fR remoteaccess control 192.168.1.2 +.TP +.I view +This command opens a window with the remote view function to monitor a +remote computer. In this mode the screen content is displayed in real +time, but interaction with the computer is not possible until the +corresponding button on the tool bar has been clicked. The computer or IP +address (and optionally the TCP port) has to be passed as an argument: + + \fBveyon-cli\fR remoteaccess view pc5:5900 + +.SH SERVICE MODULE + +The local Veyon Service can be controlled using the service module. + + +.TP +.I register +This command registers the Veyon Service in the operating system as a +service so that it starts automatically when the computer starts up. + + \fBveyon-cli\fR service register +.TP +.I unregister +This command removes the service registration in the operating system so +that the Veyon Service will not start automatically on startup. + + \fBveyon-cli\fR service unregister +.TP +.I start +This command starts the Veyon Service. + + \fBveyon-cli\fR service start +.TP +.I stop +This command stops the Veyon Service. + + \fBveyon-cli\fR service stop +.TP +.I restart +This command restarts the Veyon Service. + + \fBveyon-cli\fR service restart +.TP +.I status +This command queries and displays the status of the Veyon Service. + + \fBveyon-cli\fR service status + +.SH SHELL MODULE +Simple shell functionalities are provided by the shell module. If this +module is called without further arguments, an interactive mode is +started. In this mode, all CLI commands can be entered direcliy without +having to specify and call the \fBveyon-cli\fR program for each command. The +mode can be exited by entering the keyword exit. + +Additionally the module can be used for automated processing of commands +in a text file in order to implement simple batch processing: + +.TP +.I run +This command executes the commands specified in the text file line by +line. Operations are executed independently of the result of previous +operations, i.e. an error does not lead to termination. + +.SH FURTHER INFORMATION +For more information about the \fB\fBveyon-cli\fR\fR command, point your browser to +file:///usr/share/doc/veyon-cli/ or https://veyon.io/. + +.SH SEE ALSO +veyon-service(1), veyon-master(1), veyon-configurator(1) + +.PP +https://veyon.io/ + +.SH AUTHOR +Veyon has been written by Tobias Junghans. +.PP +This manual page has been written by Tobias Junghans and Mike Gabriel. diff --git a/cli/veyon-cli.rc.in b/cli/veyon-cli.rc.in new file mode 100644 index 0000000..b8bc9bc --- /dev/null +++ b/cli/veyon-cli.rc.in @@ -0,0 +1,31 @@ +#include + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST data/veyon-cli.exe.manifest + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@,@VERSION_BUILD@ + FILEFLAGSMASK 0x0L + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, charset = Windows, Multilingual + BEGIN + VALUE "Comments", "Virtual Eye On Networks (https://veyon.io)\0" + VALUE "CompanyName", "Veyon Solutions\0" + VALUE "ProductName", "Veyon\0" + VALUE "ProductVersion", "@VERSION_STRING@\0" + VALUE "FileDescription", "Veyon Command Line Interface (console version)\0" + VALUE "FileVersion", "@VERSION_STRING@\0" + VALUE "LegalCopyright", "Copyright (c) 2017-2021 Veyon Solutions / Tobias Junghans\0" + VALUE "OriginalFilename", "veyon-cli.exe\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04E4 + END +END diff --git a/cli/veyon-wcli.rc.in b/cli/veyon-wcli.rc.in new file mode 100644 index 0000000..c3e45a3 --- /dev/null +++ b/cli/veyon-wcli.rc.in @@ -0,0 +1,31 @@ +#include + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST data/veyon-wcli.exe.manifest + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@,@VERSION_BUILD@ + FILEFLAGSMASK 0x0L + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, charset = Windows, Multilingual + BEGIN + VALUE "Comments", "Virtual Eye On Networks (https://veyon.io)\0" + VALUE "CompanyName", "Veyon Solutions\0" + VALUE "ProductName", "Veyon\0" + VALUE "ProductVersion", "@VERSION_STRING@\0" + VALUE "FileDescription", "Veyon Command Line Interface (non-console version)\0" + VALUE "FileVersion", "@VERSION_STRING@\0" + VALUE "LegalCopyright", "Copyright (c) 2017-2021 Veyon Solutions / Tobias Junghans\0" + VALUE "OriginalFilename", "veyon-wcli.exe\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04E4 + END +END diff --git a/cmake/CPackDefinitions.cmake b/cmake/CPackDefinitions.cmake new file mode 100644 index 0000000..f006714 --- /dev/null +++ b/cmake/CPackDefinitions.cmake @@ -0,0 +1,115 @@ +# +# generate packages +# +# Environment +if (NOT CPACK_SYSTEM_NAME) + set(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_PROCESSOR}") +endif () + + +# Basic information +if(NOT CPACK_PACKAGE_NAME) + set(CPACK_PACKAGE_NAME "veyon") +endif() +SET(CPACK_PACKAGE_VERSION_MAJOR "${VERSION_MAJOR}") +SET(CPACK_PACKAGE_VERSION_MINOR "${VERSION_MINOR}") +SET(CPACK_PACKAGE_VERSION_PATCH "${VERSION_PATCH}") + +SET(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") +SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_BUILD}-${CPACK_SYSTEM_NAME}") +SET(CPACK_PACKAGE_CONTACT "Tobias Junghans ") +SET(CPACK_PACKAGE_HOMEPAGE "https://veyon.io") +SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Open source computer monitoring and classroom management") +SET(CPACK_PACKAGE_VENDOR "Veyon Solutions") +SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING") +SET(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md") +SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY TRUE) +SET(CPACK_SOURCE_IGNORE_FILES "${CMAKE_SOURCE_DIR}/build/;${CMAKE_SOURCE_DIR}/.git/;") +SET(CPACK_STRIP_FILES TRUE) + +# DEB package +SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Open source computer monitoring and classroom management software + Veyon is a free and open source software for computer monitoring and classroom + management supporting Windows and Linux. It enables teachers to view and control + computer labs and interact with students. Veyon is available in many different + languages and provides numerous features supporting teachers and administrators + at their daily work: + . + * Overview: monitor all computers in one or multiple locations or classrooms + * Remote access: view or control computers to watch and support users + * Demo: broadcast the teacher's screen in realtime (fullscreen/window) + * Screen lock: draw attention to what matters right now + * Communication: send text messages to students + * Start and end lessons: log in and log out users all at once + * Screenshots: record learning progress and document infringements + * Programs & websites: launch programs and open website URLs remotely + * Teaching material: distribute and open documents, images and videos easily + * Administration: power on/off and reboot computers remotely") +SET(CPACK_DEBIAN_PACKAGE_SECTION "Education") +SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libqca-qt5-2-plugins") +SET(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) +SET(CPACK_DEBIAN_COMPRESSION_TYPE "xz") + +FUNCTION(ReadRelease valuename FROM filename INTO varname) + file (STRINGS ${filename} _distrib + REGEX "^${valuename}=" + ) + string (REGEX REPLACE + "^${valuename}=\"?\(.*\)" "\\1" ${varname} "${_distrib}" + ) + # remove trailing quote that got globbed by the wildcard (greedy match) + string (REGEX REPLACE + "\"$" "" ${varname} "${${varname}}" + ) + set (${varname} "${${varname}}" PARENT_SCOPE) +ENDFUNCTION() + +# RPM package +IF(EXISTS /etc/os-release) +ReadRelease("NAME" FROM /etc/os-release INTO OS_NAME) +IF(OS_NAME MATCHES ".*openSUSE.*") + SET(OS_OPENSUSE TRUE) +ENDIF() +ENDIF() + +IF(OS_OPENSUSE) +SET(CPACK_RPM_PACKAGE_REQUIRES ${CPACK_RPM_PACKAGE_REQUIRES} "libqca-qt5-plugins") +ELSE() +SET(CPACK_RPM_PACKAGE_REQUIRES ${CPACK_RPM_PACKAGE_REQUIRES} "qca-qt5-ossl") +ENDIF() +SET(CPACK_RPM_PACKAGE_LICENSE "GPLv2") +SET(CPACK_RPM_PACKAGE_DESCRIPTION ${CPACK_DEBIAN_PACKAGE_DESCRIPTION}) +SET(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /lib) + + +# Generators +IF (WIN32) # TODO + IF (USE_WIX_TOOLSET) + SET(CPACK_GENERATOR "WIX") # this need WiX Tooset installed and a path to candle.exe + ELSE () + SET(CPACK_GENERATOR "NSIS") # this needs NSIS installed, and available + ENDIF () + SET(CPACK_SOURCE_GENERATOR "ZIP") +ELSEIF ( ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # TODO + SET(CPACK_GENERATOR "PackageMake") +ELSE () + IF(EXISTS /etc/redhat-release OR EXISTS /etc/fedora-release OR OS_OPENSUSE) + SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_BUILD}.${CPACK_SYSTEM_NAME}") + SET(CPACK_GENERATOR "RPM") + ENDIF () + IF(EXISTS /etc/debian_version) + if (CPACK_SYSTEM_NAME STREQUAL "x86_64") + set(CPACK_SYSTEM_NAME "amd64") + endif () + SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-${VERSION_BUILD}_${CPACK_SYSTEM_NAME}") + SET(CPACK_GENERATOR "DEB") + ENDIF () + SET(CPACK_SOURCE_GENERATOR "TGZ") +ENDIF () + + +INCLUDE(CPack) +# To generate packages use: +# make package +# make package_source + diff --git a/cmake/modules/BuildVeyonApplication.cmake b/cmake/modules/BuildVeyonApplication.cmake new file mode 100644 index 0000000..3c571a7 --- /dev/null +++ b/cmake/modules/BuildVeyonApplication.cmake @@ -0,0 +1,20 @@ +# BuildVeyonApplication.cmake - Copyright (c) 2019-2021 Tobias Junghans +# +# description: build Veyon application +# usage: build_veyon_application( ) + +MACRO(build_veyon_application APPLICATION_NAME) + IF(VEYON_BUILD_ANDROID) + ADD_LIBRARY(${APPLICATION_NAME} SHARED ${ARGN}) + ELSE() + ADD_EXECUTABLE(${APPLICATION_NAME} ${ARGN}) + INSTALL(TARGETS ${APPLICATION_NAME} RUNTIME DESTINATION bin) + ENDIF() + set_property(TARGET ${APPLICATION_NAME} PROPERTY POSITION_INDEPENDENT_CODE TRUE) + set_default_target_properties(${APPLICATION_NAME}) + + if(WITH_PCH) + target_precompile_headers(${APPLICATION_NAME} REUSE_FROM veyon-pch) + endif() +ENDMACRO() + diff --git a/cmake/modules/BuildVeyonPlugin.cmake b/cmake/modules/BuildVeyonPlugin.cmake new file mode 100644 index 0000000..fffa05a --- /dev/null +++ b/cmake/modules/BuildVeyonPlugin.cmake @@ -0,0 +1,22 @@ +# BuildVeyonPlugin.cmake - Copyright (c) 2017-2021 Tobias Junghans +# +# description: build Veyon plugin +# usage: build_veyon_plugin( ) + +MACRO(build_veyon_plugin PLUGIN_NAME) + ADD_LIBRARY(${PLUGIN_NAME} MODULE ${ARGN}) + + TARGET_INCLUDE_DIRECTORIES(${PLUGIN_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) + TARGET_LINK_LIBRARIES(${PLUGIN_NAME} veyon-core) + + SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES PREFIX "") + SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES LINK_FLAGS "-Wl,-no-undefined") + INSTALL(TARGETS ${PLUGIN_NAME} LIBRARY DESTINATION ${VEYON_INSTALL_PLUGIN_DIR}) + + set_default_target_properties(${PLUGIN_NAME}) + + if(WITH_PCH) + target_precompile_headers(${PLUGIN_NAME} REUSE_FROM veyon-pch) + endif() +ENDMACRO() + diff --git a/cmake/modules/COPYING-CMAKE-SCRIPTS b/cmake/modules/COPYING-CMAKE-SCRIPTS new file mode 100644 index 0000000..4b41776 --- /dev/null +++ b/cmake/modules/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,22 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cmake/modules/ConfigureFiles.cmake b/cmake/modules/ConfigureFiles.cmake new file mode 100644 index 0000000..4757a79 --- /dev/null +++ b/cmake/modules/ConfigureFiles.cmake @@ -0,0 +1,7 @@ +MACRO(CONFIGURE_FILES) + FOREACH(f ${ARGN}) + STRING(REPLACE ".in" "" OUT_FILE "${f}") + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${f} ${CMAKE_CURRENT_BINARY_DIR}/${OUT_FILE} @ONLY) + ENDFOREACH(f ${IN_FILES}) +ENDMACRO() + diff --git a/cmake/modules/CreateTranslations.cmake b/cmake/modules/CreateTranslations.cmake new file mode 100644 index 0000000..7d6d1d0 --- /dev/null +++ b/cmake/modules/CreateTranslations.cmake @@ -0,0 +1,32 @@ +# CreateTranslations.cmake - Copyright (c) 2020-2021 Tobias Junghans +# +# description: create Qt translation files +# usage: create_translations( ) + +function(create_translations name ts_files source_files) + + set(qm_targets "") + foreach(ts_file ${ts_files}) + string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" ts_filename "${ts_file}") + string(REPLACE ".ts" "" basename "${ts_filename}") + set(ts_target "${basename}_ts") + set(qm_target "${basename}_qm") + set(qm_file "${CMAKE_CURRENT_BINARY_DIR}/${basename}.qm") + add_custom_command(OUTPUT ${ts_file} + COMMAND ${Qt5_LUPDATE_EXECUTABLE} -I${CMAKE_SOURCE_DIR}/core/include -locations none -no-obsolete ${source_files} -ts ${ts_file} + DEPENDS ${source_files}) + add_custom_target(${ts_target} DEPENDS ${ts_file}) + # add command and target for generating/updating QM file if TS file is newer or no QM file exists yet + add_custom_command(OUTPUT ${qm_file} + COMMAND ${Qt5_LRELEASE_EXECUTABLE} ${ts_file} -qm ${qm_file} + DEPENDS ${ts_file}) + add_custom_target(${qm_target} DEPENDS ${qm_file}) + + list(APPEND qm_targets "${qm_target}") + + install(FILES ${qm_file} DESTINATION ${VEYON_INSTALL_DATA_DIR}/translations) + endforeach() + + add_custom_target("${name}-translations" ALL DEPENDS "${qm_targets}") + +endfunction() diff --git a/cmake/modules/FindLZO.cmake b/cmake/modules/FindLZO.cmake new file mode 100644 index 0000000..9a899d8 --- /dev/null +++ b/cmake/modules/FindLZO.cmake @@ -0,0 +1,29 @@ +# Find liblzo2 +# LZO_FOUND - system has the LZO library +# LZO_INCLUDE_DIR - the LZO include directory +# LZO_LIBRARIES - The libraries needed to use LZO + +if (LZO_INCLUDE_DIR AND LZO_LIBRARIES) + # in cache already + SET(LZO_FOUND TRUE) +else (LZO_INCLUDE_DIR AND LZO_LIBRARIES) + FIND_PATH(LZO_INCLUDE_DIR NAMES lzo/lzo1x.h) + + FIND_LIBRARY(LZO_LIBRARIES NAMES lzo2) + + if (LZO_INCLUDE_DIR AND LZO_LIBRARIES) + set(LZO_FOUND TRUE) + endif (LZO_INCLUDE_DIR AND LZO_LIBRARIES) + + if (LZO_FOUND) + if (NOT LZO_FIND_QUIETLY) + message(STATUS "Found LZO: ${LZO_LIBRARIES}") + endif (NOT LZO_FIND_QUIETLY) + else (LZO_FOUND) + if (LZO_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find LZO") + endif (LZO_FIND_REQUIRED) + endif (LZO_FOUND) + + MARK_AS_ADVANCED(LZO_INCLUDE_DIR LZO_LIBRARIES) +endif (LZO_INCLUDE_DIR AND LZO_LIBRARIES) diff --git a/cmake/modules/FindLdap.cmake b/cmake/modules/FindLdap.cmake new file mode 100644 index 0000000..7f8ef04 --- /dev/null +++ b/cmake/modules/FindLdap.cmake @@ -0,0 +1,114 @@ +#.rst: +# FindLdap +# -------- +# +# Try to find the LDAP client libraries. +# +# This will define the following variables: +# +# ``Ldap_FOUND`` +# True if libldap is available. +# +# ``Ldap_VERSION`` +# The version of libldap +# +# ``Ldap_INCLUDE_DIRS`` +# This should be passed to target_include_directories() if +# the target is not used for linking +# +# ``Ldap_LIBRARIES`` +# The LDAP libraries (libldap + liblber if available) +# This can be passed to target_link_libraries() instead of +# the ``Ldap::Ldap`` target +# +# If ``Ldap_FOUND`` is TRUE, the following imported target +# will be available: +# +# ``Ldap::Ldap`` +# The LDAP library +# +# Since pre-5.0.0. +# +# Imported target since 5.1.41 +# +#============================================================================= +# Copyright 2006 Szombathelyi György +# Copyright 2007-2016 Laurent Montel +# +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +find_path(Ldap_INCLUDE_DIRS NAMES ldap.h) + +if(APPLE) + find_library(Ldap_LIBRARIES NAMES LDAP + PATHS + /System/Library/Frameworks + /Library/Frameworks + ) +else() + find_library(Ldap_LIBRARIES NAMES ldap) + find_library(Lber_LIBRARIES NAMES lber) +endif() + +if(Ldap_LIBRARIES AND Lber_LIBRARIES) + set(Ldap_LIBRARIES ${Ldap_LIBRARIES} ${Lber_LIBRARIES}) +endif() + +if(EXISTS ${Ldap_INCLUDE_DIRS}/ldap_features.h) + file(READ ${Ldap_INCLUDE_DIRS}/ldap_features.h LDAP_FEATURES_H_CONTENT) + string(REGEX MATCH "#define LDAP_VENDOR_VERSION_MAJOR[ ]+[0-9]+" _LDAP_VERSION_MAJOR_MATCH ${LDAP_FEATURES_H_CONTENT}) + string(REGEX MATCH "#define LDAP_VENDOR_VERSION_MINOR[ ]+[0-9]+" _LDAP_VERSION_MINOR_MATCH ${LDAP_FEATURES_H_CONTENT}) + string(REGEX MATCH "#define LDAP_VENDOR_VERSION_PATCH[ ]+[0-9]+" _LDAP_VERSION_PATCH_MATCH ${LDAP_FEATURES_H_CONTENT}) + + string(REGEX REPLACE ".*_MAJOR[ ]+(.*)" "\\1" LDAP_VERSION_MAJOR ${_LDAP_VERSION_MAJOR_MATCH}) + string(REGEX REPLACE ".*_MINOR[ ]+(.*)" "\\1" LDAP_VERSION_MINOR ${_LDAP_VERSION_MINOR_MATCH}) + string(REGEX REPLACE ".*_PATCH[ ]+(.*)" "\\1" LDAP_VERSION_PATCH ${_LDAP_VERSION_PATCH_MATCH}) + + set(Ldap_VERSION "${LDAP_VERSION_MAJOR}.${LDAP_VERSION_MINOR}.${LDAP_VERSION_PATCH}") +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Ldap + FOUND_VAR Ldap_FOUND + REQUIRED_VARS Ldap_LIBRARIES Ldap_INCLUDE_DIRS + VERSION_VAR Ldap_VERSION +) + +if(Ldap_FOUND AND NOT TARGET Ldap::Ldap) + add_library(Ldap::Ldap UNKNOWN IMPORTED) + set_target_properties(Ldap::Ldap PROPERTIES + IMPORTED_LOCATION "${Ldap_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${Ldap_INCLUDE_DIRS}") +endif() + +mark_as_advanced(Ldap_INCLUDE_DIRS Ldap_LIBRARIES Lber_LIBRARIES Ldap_VERSION) + +include(FeatureSummary) +set_package_properties(Ldap PROPERTIES + URL "http://www.openldap.org/" + DESCRIPTION "LDAP (Lightweight Directory Access Protocol) libraries." +) diff --git a/cmake/modules/FindLibVNCClient.cmake b/cmake/modules/FindLibVNCClient.cmake new file mode 100644 index 0000000..195d8ad --- /dev/null +++ b/cmake/modules/FindLibVNCClient.cmake @@ -0,0 +1,61 @@ +#.rst: +# FindLibVNCClient +# -------- +# +# Try to find the LibVNCClient library, once done this will define: +# +# ``LibVNCClient_FOUND`` +# System has LibVNCClient. +# +# ``LibVNCClient_INCLUDE_DIRS`` +# The LibVNCClient include directory. +# +# ``LibVNCClient_LIBRARIES`` +# The LibVNCClient libraries. +# +# ``LibVNCClient_VERSION`` +# The LibVNCClient version. +# +# If ``LibVNCClient_FOUND`` is TRUE, the following imported target +# will be available: +# +# ``LibVNC::LibVNCClient`` +# The LibVNCClient library + +#============================================================================= +# SPDX-FileCopyrightText: 2020-2021 Tobias Junghans +# +# SPDX-License-Identifier: BSD-3-Clause +#============================================================================= + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_LIBVNCCLIENT QUIET libvncclient) + +find_path(LibVNCClient_INCLUDE_DIRS NAMES rfb/rfbclient.h HINTS ${PC_LIBVNCCLIENT_INCLUDE_DIRS}) +find_library(LibVNCClient_LIBRARIES NAMES vncclient HINTS ${PC_LIBVNCCLIENT_LIBRARY_DIRS}) + +set(LibVNCClient_VERSION ${PC_LIBVNCCLIENT_VERSION}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibVNCClient + FOUND_VAR LibVNCClient_FOUND + REQUIRED_VARS LibVNCClient_INCLUDE_DIRS LibVNCClient_LIBRARIES + VERSION_VAR LibVNCClient_VERSION +) + +mark_as_advanced(LibVNCClient_INCLUDE_DIRS LibVNCClient_LIBRARIES) + +if(LibVNCClient_FOUND AND NOT TARGET LibVNCClient::LibVNCClient) + add_library(LibVNC::LibVNCClient UNKNOWN IMPORTED) + set_target_properties(LibVNC::LibVNCClient PROPERTIES + IMPORTED_LOCATION "${LibVNCClient_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${LibVNCClient_INCLUDE_DIRS}" + INTERFACE_COMPILE_DEFINITIONS "${PC_LIBVNCCLIENT_CFLAGS_OTHER}" + ) +endif() + +include(FeatureSummary) +set_package_properties(LibVNCClient PROPERTIES + DESCRIPTION "cross-platform C library which allows you to easily implement VNC client functionality" + URL "https://libvnc.github.io/" +) diff --git a/cmake/modules/FindLibVNCServer.cmake b/cmake/modules/FindLibVNCServer.cmake new file mode 100644 index 0000000..dfe21df --- /dev/null +++ b/cmake/modules/FindLibVNCServer.cmake @@ -0,0 +1,61 @@ +#.rst: +# FindLibVNCServer +# -------- +# +# Try to find the LibVNCServer library, once done this will define: +# +# ``LibVNCServer_FOUND`` +# System has LibVNCServer. +# +# ``LibVNCServer_INCLUDE_DIRS`` +# The LibVNCServer include directory. +# +# ``LibVNCServer_LIBRARIES`` +# The LibVNCServer libraries. +# +# ``LibVNCServer_VERSION`` +# The LibVNCServer version. +# +# If ``LibVNCServer_FOUND`` is TRUE, the following imported target +# will be available: +# +# ``LibVNC::LibVNCServer`` +# The LibVNCServer library + +#============================================================================= +# SPDX-FileCopyrightText: 2020-2021 Tobias Junghans +# +# SPDX-License-Identifier: BSD-3-Clause +#============================================================================= + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_LIBVNCCLIENT QUIET libvncserver) + +find_path(LibVNCServer_INCLUDE_DIRS NAMES rfb/rfb.h HINTS ${PC_LIBVNCCLIENT_INCLUDE_DIRS}) +find_library(LibVNCServer_LIBRARIES NAMES vncserver HINTS ${PC_LIBVNCCLIENT_LIBRARY_DIRS}) + +set(LibVNCServer_VERSION ${PC_LIBVNCCLIENT_VERSION}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibVNCServer + FOUND_VAR LibVNCServer_FOUND + REQUIRED_VARS LibVNCServer_INCLUDE_DIRS LibVNCServer_LIBRARIES + VERSION_VAR LibVNCServer_VERSION +) + +mark_as_advanced(LibVNCServer_INCLUDE_DIRS LibVNCServer_LIBRARIES) + +if(LibVNCServer_FOUND AND NOT TARGET LibVNCServer::LibVNCServer) + add_library(LibVNC::LibVNCServer UNKNOWN IMPORTED) + set_target_properties(LibVNC::LibVNCServer PROPERTIES + IMPORTED_LOCATION "${LibVNCServer_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${LibVNCServer_INCLUDE_DIRS}" + INTERFACE_COMPILE_DEFINITIONS "${PC_LIBVNCCLIENT_CFLAGS_OTHER}" + ) +endif() + +include(FeatureSummary) +set_package_properties(LibVNCServer PROPERTIES + DESCRIPTION "cross-platform C library which allows you to easily implement VNC server functionality" + URL "https://libvnc.github.io/" +) diff --git a/cmake/modules/FindPAM.cmake b/cmake/modules/FindPAM.cmake new file mode 100644 index 0000000..3499836 --- /dev/null +++ b/cmake/modules/FindPAM.cmake @@ -0,0 +1,74 @@ +# - Try to find the PAM libraries +# Once done this will define +# +# PAM_FOUND - system has pam +# PAM_INCLUDE_DIR - the pam include directory +# PAM_LIBRARIES - libpam library + +if (PAM_INCLUDE_DIR AND PAM_LIBRARY) + # Already in cache, be silent + set(PAM_FIND_QUIETLY TRUE) +endif (PAM_INCLUDE_DIR AND PAM_LIBRARY) + +find_path(PAM_INCLUDE_DIR NAMES security/pam_appl.h pam/pam_appl.h) +find_library(PAM_LIBRARY pam) +find_library(DL_LIBRARY dl) + +if (PAM_INCLUDE_DIR AND PAM_LIBRARY) + set(PAM_FOUND TRUE) + if (DL_LIBRARY) + set(PAM_LIBRARIES ${PAM_LIBRARY} ${DL_LIBRARY}) + else (DL_LIBRARY) + set(PAM_LIBRARIES ${PAM_LIBRARY}) + endif (DL_LIBRARY) + + if (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h) + # darwin claims to be something special + set(HAVE_PAM_PAM_APPL_H 1) + endif (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h) + + if (NOT DEFINED PAM_MESSAGE_CONST) + include(CheckCXXSourceCompiles) + # XXX does this work with plain c? + check_cxx_source_compiles(" +#if ${HAVE_PAM_PAM_APPL_H}+0 +# include +#else +# include +#endif + +static int PAM_conv( + int num_msg, + const struct pam_message **msg, /* this is the culprit */ + struct pam_response **resp, + void *ctx) +{ + return 0; +} + +int main(void) +{ + struct pam_conv PAM_conversation = { + &PAM_conv, /* this bombs out if the above does not match */ + 0 + }; + + return 0; +} +" PAM_MESSAGE_CONST) + endif (NOT DEFINED PAM_MESSAGE_CONST) + set(PAM_MESSAGE_CONST ${PAM_MESSAGE_CONST} CACHE BOOL "PAM expects a conversation function with const pam_message") + +endif (PAM_INCLUDE_DIR AND PAM_LIBRARY) + +if (PAM_FOUND) + if (NOT PAM_FIND_QUIETLY) + message(STATUS "Found PAM: ${PAM_LIBRARIES}") + endif (NOT PAM_FIND_QUIETLY) +else (PAM_FOUND) + if (PAM_FIND_REQUIRED) + message(FATAL_ERROR "PAM was not found") + endif(PAM_FIND_REQUIRED) +endif (PAM_FOUND) + +mark_as_advanced(PAM_INCLUDE_DIR PAM_LIBRARY DL_LIBRARY PAM_MESSAGE_CONST) diff --git a/cmake/modules/FindQCA.cmake b/cmake/modules/FindQCA.cmake new file mode 100644 index 0000000..5b14af9 --- /dev/null +++ b/cmake/modules/FindQCA.cmake @@ -0,0 +1,99 @@ +# Find QCA (Qt Cryptography Architecture 2+) +# ~~~~~~~~~~~~~~~~ +# When run this will define +# +# QCA_FOUND - system has QCA +# QCA_LIBRARY - the QCA library or framework +# QCA_INCLUDE_DIR - the QCA include directory +# QCA_VERSION_STR - e.g. "2.0.3" +# +# Copyright (c) 2006, Michael Larouche, +# Copyright (c) 2014, Larry Shaffer, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +if(QCA_INCLUDE_DIR AND QCA_LIBRARY) + + set(QCA_FOUND TRUE) + +else(QCA_INCLUDE_DIR AND QCA_LIBRARY) + + set(QCA_LIBRARY_NAMES qca-qt5 qca2-qt5) + + find_library(QCA_LIBRARY + NAMES ${QCA_LIBRARY_NAMES} + PATHS + ${LIB_DIR} + $ENV{LIB} + "$ENV{LIB_DIR}" + /usr/local/lib + ) + + set(_qca_fw) + if(QCA_LIBRARY MATCHES "/qca.*\\.framework") + string(REGEX REPLACE "^(.*/qca.*\\.framework).*$" "\\1" _qca_fw "${QCA_LIBRARY}") + endif() + + find_path(QCA_INCLUDE_DIR + NAMES QtCrypto + PATHS + "${_qca_fw}/Headers" + ${LIB_DIR}/include + "$ENV{LIB_DIR}/include" + $ENV{INCLUDE} + /usr/local/include + PATH_SUFFIXES QtCrypto qt5/QtCrypto Qca-qt5/QtCrypto qt/Qca-qt5/QtCrypto qt5/Qca-qt5/QtCrypto + ) + + if(QCA_LIBRARY AND QCA_INCLUDE_DIR) + set(QCA_FOUND TRUE) + endif() + +endif(QCA_INCLUDE_DIR AND QCA_LIBRARY) + +if(NOT QCA_FOUND) + + if(QCA_FIND_REQUIRED) + message(FATAL_ERROR "Could not find QCA") + else() + message(STATUS "Could not find QCA") + endif() + +else(NOT QCA_FOUND) + + # Check version is valid (>= 2.0.3) + # find_package(QCA 2.0.3) works with 2.1.0+, which has a QcaConfigVersion.cmake, but 2.0.3 does not + + # qca_version.h header only available with 2.1.0+ + set(_qca_version_h "${QCA_INCLUDE_DIR}/qca_version.h") + if(EXISTS "${_qca_version_h}") + file(STRINGS "${_qca_version_h}" _qca_version_str REGEX "^.*QCA_VERSION_STR +\"[^\"]+\".*$") + string(REGEX REPLACE "^.*QCA_VERSION_STR +\"([^\"]+)\".*$" "\\1" QCA_VERSION_STR "${_qca_version_str}") + else() + # qca_core.h contains hexadecimal version in <= 2.0.3 + set(_qca_core_h "${QCA_INCLUDE_DIR}/qca_core.h") + if(EXISTS "${_qca_core_h}") + file(STRINGS "${_qca_core_h}" _qca_version_str REGEX "^#define +QCA_VERSION +0x[0-9a-fA-F]+.*") + string(REGEX REPLACE "^#define +QCA_VERSION +0x([0-9a-fA-F]+)$" "\\1" _qca_version_int "${_qca_version_str}") + if("${_qca_version_int}" STREQUAL "020003") + set(QCA_VERSION_STR "2.0.3") + endif() + endif() + endif() + + if(NOT QCA_VERSION_STR) + set(QCA_FOUND FALSE) + if(QCA_FIND_REQUIRED) + message(FATAL_ERROR "Could not find QCA >= 2.0.3") + else() + message(STATUS "Could not find QCA >= 2.0.3") + endif() + else() + if(NOT QCA_FIND_QUIETLY) + message(STATUS "Found QCA: ${QCA_LIBRARY} (${QCA_VERSION_STR})") + endif() + endif() + +endif(NOT QCA_FOUND) diff --git a/cmake/modules/FindQtTranslations.cmake b/cmake/modules/FindQtTranslations.cmake new file mode 100644 index 0000000..cf9377d --- /dev/null +++ b/cmake/modules/FindQtTranslations.cmake @@ -0,0 +1,37 @@ +# FindQtTranslations.cmake - Copyright (c) 2020-2021 Tobias Junghans +# +# description: find translation files of Qt and copy them on Windows to current binary dir +# usage: find_qt_translations() + + +function(find_qt_translations) + # find Qt's translation files + set(QT_TRANSLATIONS_STAMP ${CMAKE_CURRENT_BINARY_DIR}/qttranslations.stamp) + if(NOT EXISTS "${QT_TRANSLATIONS_STAMP}") + get_target_property(QT_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) + execute_process(COMMAND "${QT_QMAKE_EXECUTABLE}" -query QT_INSTALL_TRANSLATIONS + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE QT_INSTALL_TRANSLATIONS) + message(STATUS "Found Qt translations: ${QT_INSTALL_TRANSLATIONS}") + set(${QT_TRANSLATIONS_DIR} "${QT_INSTALL_TRANSLATIONS}" PARENT_SCOPE) + file(WRITE "${QT_TRANSLATIONS_STAMP}" "1") + if(WIN32) + file(GLOB QT_TRANSLATIONS "${QT_INSTALL_TRANSLATIONS}/qt_*.qm") + foreach(QT_TRANSLATION ${QT_TRANSLATIONS}) + if(NOT QT_TRANSLATION MATCHES "help") + string(REPLACE "${QT_INSTALL_TRANSLATIONS}/" "" QT_TRANSLATION_FILE_NAME "${QT_TRANSLATION}") + string(REPLACE "qt_" "qtbase_" QTBASE_TRANSLATION_FILE_NAME "${QT_TRANSLATION_FILE_NAME}") + # is there qtbase-specific QM file? + if(EXISTS "${QT_INSTALL_TRANSLATIONS}/${QTBASE_TRANSLATION_FILE_NAME}") + # then use it instead of (deprecated) QM file for all Qt modules + file(COPY "${QT_INSTALL_TRANSLATIONS}/${QTBASE_TRANSLATION_FILE_NAME}" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + file(RENAME "${CMAKE_CURRENT_BINARY_DIR}/${QTBASE_TRANSLATION_FILE_NAME}" "${CMAKE_CURRENT_BINARY_DIR}/${QT_TRANSLATION_FILE_NAME}") + else() + file(COPY ${QT_TRANSLATION} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + endif() + message(STATUS "Imported Qt translation file: ${QT_TRANSLATION}") + endif() + endforeach() + endif() + endif() +endfunction() diff --git a/cmake/modules/FindSasl2.cmake b/cmake/modules/FindSasl2.cmake new file mode 100644 index 0000000..95dd8f6 --- /dev/null +++ b/cmake/modules/FindSasl2.cmake @@ -0,0 +1,114 @@ +#.rst: +# FindSasl2 +# --------- +# +# Try to find the SASL2 library. +# +# This will define the following variables: +# +# ``Sasl2_FOUND`` +# System has SASL2. +# +# ``Sasl2_VERSION`` +# The version of SASL2. +# +# ``Sasl2_INCLUDE_DIRS`` +# This should be passed to target_include_directories() if +# the target is not used for linking. +# +# ``Sasl2_LIBRARIES`` +# The SASL2 library. +# This can be passed to target_link_libraries() instead of +# the ``Sasl2::Sasl2`` target +# +# If ``Sasl2_FOUND`` is TRUE, the following imported target +# will be available: +# +# ``Sasl2::Sasl2`` +# The SASL2 library +# +# Since 5.41.0. +# +#============================================================================= +# Copyright 2006, 2007 Laurent Montel +# +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +# NOTE: libsasl2.pc doesn't export the include dir. +find_package(PkgConfig QUIET) +pkg_check_modules(PC_Sasl2 libsasl2) + +find_path(Sasl2_INCLUDE_DIRS NAMES sasl/sasl.h) + +# libsasl2 add for windows, because the windows package of cyrus-sasl2 +# contains a libsasl2 also for msvc which is not standard conform +find_library(Sasl2_LIBRARIES + NAMES sasl2 libsasl2 + HINTS ${PC_Sasl2_LIBRARY_DIRS} +) + +set(Sasl2_VERSION "${PC_Sasl2_VERSION}") + +if(NOT Sasl2_VERSION) + if(EXISTS "${Sasl2_INCLUDE_DIRS}/sasl/sasl.h") + file(READ "${Sasl2_INCLUDE_DIRS}/sasl/sasl.h" SASL2_H_CONTENT) + string(REGEX MATCH "#define SASL_VERSION_MAJOR[ ]+[0-9]+" SASL2_VERSION_MAJOR_MATCH ${SASL2_H_CONTENT}) + string(REGEX MATCH "#define SASL_VERSION_MINOR[ ]+[0-9]+" SASL2_VERSION_MINOR_MATCH ${SASL2_H_CONTENT}) + string(REGEX MATCH "#define SASL_VERSION_STEP[ ]+[0-9]+" SASL2_VERSION_STEP_MATCH ${SASL2_H_CONTENT}) + + string(REGEX REPLACE ".*_MAJOR[ ]+(.*)" "\\1" SASL2_VERSION_MAJOR ${SASL2_VERSION_MAJOR_MATCH}) + string(REGEX REPLACE ".*_MINOR[ ]+(.*)" "\\1" SASL2_VERSION_MINOR ${SASL2_VERSION_MINOR_MATCH}) + string(REGEX REPLACE ".*_STEP[ ]+(.*)" "\\1" SASL2_VERSION_STEP ${SASL2_VERSION_STEP_MATCH}) + + set(Sasl2_VERSION "${SASL2_VERSION_MAJOR}.${SASL2_VERSION_MINOR}.${SASL2_VERSION_STEP}") + else() + # Could not find the version + set(Sasl2_VERSION "0.0.0") + endif() +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Sasl2 + FOUND_VAR Sasl2_FOUND + REQUIRED_VARS Sasl2_LIBRARIES Sasl2_INCLUDE_DIRS + VERSION_VAR Sasl2_VERSION +) +if(Sasl2_FOUND AND NOT TARGET Sasl2::Sasl2) + add_library(Sasl2::Sasl2 UNKNOWN IMPORTED) + set_target_properties(Sasl2::Sasl2 PROPERTIES + IMPORTED_LOCATION "${Sasl2_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${Sasl2_INCLUDE_DIRS}") +endif() + +mark_as_advanced(Sasl2_LIBRARIES Sasl2_INCLUDE_DIRS Sasl2_VERSION) + +include(FeatureSummary) +set_package_properties(Sasl2 PROPERTIES + URL "https://www.cyrusimap.org/sasl/" + DESCRIPTION "The Cyrus-sasl library." +) + diff --git a/cmake/modules/LibVNCServerIntegration.cmake b/cmake/modules/LibVNCServerIntegration.cmake new file mode 100644 index 0000000..06f0122 --- /dev/null +++ b/cmake/modules/LibVNCServerIntegration.cmake @@ -0,0 +1,76 @@ +include(CheckFunctionExists) +include(CheckSymbolExists) +include(CheckIncludeFile) +include(CheckTypeSize) +include(TestBigEndian) +include(CheckCSourceCompiles) +include(CheckCSourceRuns) + +check_include_file("endian.h" LIBVNCSERVER_HAVE_ENDIAN_H) +check_include_file("fcntl.h" LIBVNCSERVER_HAVE_FCNTL_H) +check_include_file("netinet/in.h" LIBVNCSERVER_HAVE_NETINET_IN_H) +check_include_file("sys/endian.h" LIBVNCSERVER_HAVE_SYS_ENDIAN_H) +check_include_file("sys/socket.h" LIBVNCSERVER_HAVE_SYS_SOCKET_H) +check_include_file("sys/stat.h" LIBVNCSERVER_HAVE_SYS_STAT_H) +check_include_file("sys/time.h" LIBVNCSERVER_HAVE_SYS_TIME_H) +check_include_file("sys/types.h" LIBVNCSERVER_HAVE_SYS_TYPES_H) +check_include_file("sys/wait.h" LIBVNCSERVER_HAVE_SYS_WAIT_H) +check_include_file("unistd.h" LIBVNCSERVER_HAVE_UNISTD_H) +check_include_file("sys/uio.h" LIBVNCSERVER_HAVE_SYS_UIO_H) +check_include_file("sys/resource.h" LIBVNCSERVER_HAVE_SYS_RESOURCE_H) + + +# headers needed for check_type_size() +check_include_file("vfork.h" LIBVNCSERVER_HAVE_VFORK_H) +check_include_file("ws2tcpip.h" LIBVNCSERVER_HAVE_WS2TCPIP_H) +check_include_file("arpa/inet.h" HAVE_ARPA_INET_H) +check_include_file("stdint.h" HAVE_STDINT_H) +check_include_file("stddef.h" HAVE_STDDEF_H) +check_include_file("sys/types.h" HAVE_SYS_TYPES_H) + +# error out if required headers not found +if(NOT HAVE_STDINT_H) + message(FATAL_ERROR "Could NOT find required header stdint.h") +endif() + +check_function_exists(gettimeofday LIBVNCSERVER_HAVE_GETTIMEOFDAY) +check_function_exists(vfork LIBVNCSERVER_HAVE_VFORK) +check_function_exists(vprintf LIBVNCSERVER_HAVE_VPRINTF) +check_function_exists(mmap LIBVNCSERVER_HAVE_MMAP) +check_function_exists(fork LIBVNCSERVER_HAVE_FORK) +check_function_exists(ftime LIBVNCSERVER_HAVE_FTIME) +check_function_exists(gethostbyname LIBVNCSERVER_HAVE_GETHOSTBYNAME) +check_function_exists(gethostname LIBVNCSERVER_HAVE_GETHOSTNAME) +check_function_exists(inet_ntoa LIBVNCSERVER_HAVE_INET_NTOA) +check_function_exists(memmove LIBVNCSERVER_HAVE_MEMMOVE) +check_function_exists(memset LIBVNCSERVER_HAVE_MEMSET) +check_function_exists(mkfifo LIBVNCSERVER_HAVE_MKFIFO) +check_function_exists(select LIBVNCSERVER_HAVE_SELECT) +check_function_exists(socket LIBVNCSERVER_HAVE_SOCKET) +check_function_exists(strchr LIBVNCSERVER_HAVE_STRCHR) +check_function_exists(strcspn LIBVNCSERVER_HAVE_STRCSPN) +check_function_exists(strdup LIBVNCSERVER_HAVE_STRDUP) +check_function_exists(strerror LIBVNCSERVER_HAVE_STRERROR) +check_function_exists(strstr LIBVNCSERVER_HAVE_STRSTR) + +check_symbol_exists(htobe64 "endian.h" LIBVNCSERVER_HAVE_HTOBE64) +check_symbol_exists(OSSwapHostToBigInt64 "libkern/OSByteOrder.h" LIBVNCSERVER_HAVE_OSSWAPHOSTTOBIGINT64) + +if(LIBVNCSERVER_HAVE_SYS_SOCKET_H) + # socklen_t + list(APPEND CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") +endif() +if(HAVE_ARPA_INET_H) + # in_addr_t + list(APPEND CMAKE_EXTRA_INCLUDE_FILES "arpa/inet.h") +endif() + +check_type_size(pid_t LIBVNCSERVER_PID_T) +check_type_size(size_t LIBVNCSERVER_SIZE_T) +check_type_size(socklen_t LIBVNCSERVER_SOCKLEN_T) +check_type_size(in_addr_t LIBVNCSERVER_IN_ADDR_T) +if(NOT HAVE_LIBVNCSERVER_IN_ADDR_T) + set(LIBVNCSERVER_NEED_INADDR_T 1) +endif() + +test_big_endian(LIBVNCSERVER_WORDS_BIGENDIAN) diff --git a/cmake/modules/SetDefaultTargetProperties.cmake b/cmake/modules/SetDefaultTargetProperties.cmake new file mode 100644 index 0000000..6530918 --- /dev/null +++ b/cmake/modules/SetDefaultTargetProperties.cmake @@ -0,0 +1,3 @@ +macro(SET_DEFAULT_TARGET_PROPERTIES TARGET_NAME) + set_property(TARGET ${TARGET_NAME} PROPERTY NO_SYSTEM_FROM_IMPORTED ON) +endmacro() diff --git a/cmake/modules/WindowsBuildHelpers.cmake b/cmake/modules/WindowsBuildHelpers.cmake new file mode 100644 index 0000000..2538fa7 --- /dev/null +++ b/cmake/modules/WindowsBuildHelpers.cmake @@ -0,0 +1,26 @@ +MACRO(ADD_WINDOWS_RESOURCE TARGET) + IF(VEYON_BUILD_WIN32) + SET(WINRC "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.rc") + SET(RCOBJ "${CMAKE_CURRENT_BINARY_DIR}/winrc-${TARGET}.obj") + ADD_CUSTOM_COMMAND(OUTPUT ${RCOBJ} + COMMAND ${WINDRES} + -I${CMAKE_CURRENT_SOURCE_DIR} + -o${RCOBJ} + -i${WINRC} + DEPENDS ${WINRC}) + TARGET_SOURCES(${TARGET} PUBLIC ${RCOBJ}) + ENDIF(VEYON_BUILD_WIN32) +ENDMACRO() + +MACRO(MAKE_GRAPHICAL_APP TARGET) + IF(VEYON_BUILD_WIN32) + SET_TARGET_PROPERTIES(${TARGET} PROPERTIES LINK_FLAGS -mwindows) + ENDIF() +ENDMACRO() + +MACRO(MAKE_CONSOLE_APP TARGET) + IF(VEYON_BUILD_WIN32) + SET_TARGET_PROPERTIES(${TARGET} PROPERTIES LINK_FLAGS -mconsole) + ENDIF() +ENDMACRO() + diff --git a/cmake/modules/WindowsInstaller.cmake b/cmake/modules/WindowsInstaller.cmake new file mode 100644 index 0000000..0ba4c16 --- /dev/null +++ b/cmake/modules/WindowsInstaller.cmake @@ -0,0 +1,75 @@ +set(WINDOWS_INSTALL_FILES "veyon-${MINGW_PLATFORM}-${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_BUILD}") + +set(DLLDIR "${MINGW_PREFIX}/bin") +set(DLLDIR_LIB "${MINGW_PREFIX}/lib") +set(DLLDIR_GCC "/usr/lib/gcc/${MINGW_TARGET}/${COMPILER_VERSION_MAJOR_MINOR}-posix") +if(VEYON_BUILD_WIN64) + set(DLL_GCC "libgcc_s_seh-1.dll") + set(DLL_DDENGINE "ddengine64.dll") +else() + set(DLL_GCC "libgcc_s_sjlj-1.dll") + set(DLL_DDENGINE "ddengine.dll") +endif() + +add_custom_target(windows-binaries + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --config $ + COMMAND rm -rf ${WINDOWS_INSTALL_FILES}* + COMMAND mkdir -p ${WINDOWS_INSTALL_FILES}/interception + COMMAND cp ${CMAKE_SOURCE_DIR}/3rdparty/interception/* ${WINDOWS_INSTALL_FILES}/interception + COMMAND cp ${CMAKE_SOURCE_DIR}/3rdparty/ddengine/${DLL_DDENGINE} ${WINDOWS_INSTALL_FILES} + COMMAND cp core/veyon-core.dll ${WINDOWS_INSTALL_FILES} + COMMAND find . -mindepth 2 -name 'veyon-*.exe' -exec cp '{}' ${WINDOWS_INSTALL_FILES}/ '\;' + COMMAND mkdir -p ${WINDOWS_INSTALL_FILES}/plugins + COMMAND find plugins/ -name '*.dll' -exec cp '{}' ${WINDOWS_INSTALL_FILES}/plugins/ '\;' + COMMAND mv ${WINDOWS_INSTALL_FILES}/plugins/lib*.dll ${WINDOWS_INSTALL_FILES} + COMMAND mv ${WINDOWS_INSTALL_FILES}/plugins/vnchooks.dll ${WINDOWS_INSTALL_FILES} + COMMAND mkdir -p ${WINDOWS_INSTALL_FILES}/translations + COMMAND cp translations/*qm ${WINDOWS_INSTALL_FILES}/translations/ + COMMAND cp ${DLLDIR}/libjpeg-62.dll ${WINDOWS_INSTALL_FILES} + COMMAND cp ${DLLDIR}/libpng16-16.dll ${WINDOWS_INSTALL_FILES} + COMMAND cp ${DLLDIR}/libcrypto-1_1*.dll ${DLLDIR}/libssl-1_1*.dll ${WINDOWS_INSTALL_FILES} + COMMAND cp ${DLLDIR}/libqca-qt5.dll ${WINDOWS_INSTALL_FILES} + COMMAND cp ${DLLDIR}/libsasl2-3.dll ${WINDOWS_INSTALL_FILES} + COMMAND cp ${DLLDIR}/libldap.dll ${DLLDIR}/liblber.dll ${WINDOWS_INSTALL_FILES} + COMMAND cp ${DLLDIR}/interception.dll ${WINDOWS_INSTALL_FILES} + COMMAND cp ${DLLDIR}/liblzo2-2.dll ${WINDOWS_INSTALL_FILES} + COMMAND cp ${DLLDIR}/libvncclient.dll ${WINDOWS_INSTALL_FILES} + COMMAND cp ${DLLDIR_LIB}/zlib1.dll ${WINDOWS_INSTALL_FILES} + COMMAND cp ${DLLDIR_LIB}/libwinpthread-1.dll ${WINDOWS_INSTALL_FILES} + COMMAND cp ${DLLDIR_GCC}/libstdc++-6.dll ${WINDOWS_INSTALL_FILES} + COMMAND cp ${DLLDIR_GCC}/libssp-0.dll ${WINDOWS_INSTALL_FILES} + COMMAND cp ${DLLDIR_GCC}/${DLL_GCC} ${WINDOWS_INSTALL_FILES} + COMMAND mkdir -p ${WINDOWS_INSTALL_FILES}/crypto + COMMAND cp ${DLLDIR_LIB}/qca-qt5/crypto/libqca-ossl.dll ${WINDOWS_INSTALL_FILES}/crypto + COMMAND cp ${DLLDIR}/Qt5Core.dll ${DLLDIR}/Qt5Gui.dll ${DLLDIR}/Qt5Widgets.dll ${DLLDIR}/Qt5Network.dll ${DLLDIR}/Qt5Concurrent.dll ${WINDOWS_INSTALL_FILES} + COMMAND mkdir -p ${WINDOWS_INSTALL_FILES}/imageformats + COMMAND cp ${DLLDIR_LIB}/qt5/plugins/imageformats/qjpeg.dll ${WINDOWS_INSTALL_FILES}/imageformats + COMMAND mkdir -p ${WINDOWS_INSTALL_FILES}/platforms + COMMAND cp ${DLLDIR_LIB}/qt5/plugins/platforms/qwindows.dll ${WINDOWS_INSTALL_FILES}/platforms + COMMAND mkdir -p ${WINDOWS_INSTALL_FILES}/styles + COMMAND cp ${DLLDIR_LIB}/qt5/plugins/styles/*.dll ${WINDOWS_INSTALL_FILES}/styles + COMMAND ${STRIP} ${WINDOWS_INSTALL_FILES}/*.dll ${WINDOWS_INSTALL_FILES}/*.exe ${WINDOWS_INSTALL_FILES}/plugins/*.dll ${WINDOWS_INSTALL_FILES}/platforms/*.dll ${WINDOWS_INSTALL_FILES}/styles/*.dll ${WINDOWS_INSTALL_FILES}/crypto/*.dll + COMMAND cp ${CMAKE_SOURCE_DIR}/COPYING ${WINDOWS_INSTALL_FILES} + COMMAND cp ${CMAKE_SOURCE_DIR}/COPYING ${WINDOWS_INSTALL_FILES}/LICENSE.TXT + COMMAND cp ${CMAKE_SOURCE_DIR}/README.md ${WINDOWS_INSTALL_FILES}/README.TXT + COMMAND todos ${WINDOWS_INSTALL_FILES}/*.TXT + COMMAND cp -ra ${CMAKE_SOURCE_DIR}/nsis ${WINDOWS_INSTALL_FILES} + COMMAND cp ${CMAKE_BINARY_DIR}/nsis/veyon.nsi ${WINDOWS_INSTALL_FILES} + COMMAND find ${WINDOWS_INSTALL_FILES} -ls +) + +add_custom_target(create-windows-installer + COMMAND makensis ${WINDOWS_INSTALL_FILES}/veyon.nsi + COMMAND mv ${WINDOWS_INSTALL_FILES}/veyon-*setup.exe . + COMMAND rm -rf ${WINDOWS_INSTALL_FILES} + DEPENDS windows-binaries +) + +add_custom_target(prepare-dev-nsi + COMMAND sed -i ${WINDOWS_INSTALL_FILES}/veyon.nsi -e "s,/SOLID lzma,zlib,g" + DEPENDS windows-binaries) + +add_custom_target(dev-nsi + DEPENDS prepare-dev-nsi create-windows-installer +) + diff --git a/cmake/modules/XdgInstall.cmake b/cmake/modules/XdgInstall.cmake new file mode 100644 index 0000000..275ce25 --- /dev/null +++ b/cmake/modules/XdgInstall.cmake @@ -0,0 +1,12 @@ +FIND_PROGRAM(XDG_DESKTOP_MENU_EXECUTABLE xdg-desktop-menu) +SET(XDG_APPS_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/applications) + +MACRO(XDG_INSTALL DESKTOP_FILE ICON_XPM ICON_PNG ICON_SVG) + INSTALL(FILES ${DESKTOP_FILE} DESTINATION ${XDG_APPS_INSTALL_DIR}) + INSTALL(FILES ${ICON_XPM} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps) + INSTALL(FILES ${ICON_PNG} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/48x48/apps) + INSTALL(FILES ${ICON_SVG} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps) + #IF(XDG_DESKTOP_MENU_EXECUTABLE) + # INSTALL(CODE "EXECUTE_PROCESS(COMMAND ${XDG_DESKTOP_MENU_EXECUTABLE} install --novendor ${XDG_APPS_INSTALL_DIR}/${DESKTOP_FILE})") + #ENDIF() +ENDMACRO() diff --git a/configurator/CMakeLists.txt b/configurator/CMakeLists.txt new file mode 100644 index 0000000..9ae05ab --- /dev/null +++ b/configurator/CMakeLists.txt @@ -0,0 +1,18 @@ +INCLUDE(BuildVeyonApplication) +INCLUDE(WindowsBuildHelpers) + +FILE(GLOB configurator_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h) +FILE(GLOB configurator_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/*.ui) +SET(configurator_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/resources/configurator.qrc) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src) +build_veyon_application(veyon-configurator ${configurator_SOURCES} ${configurator_INCLUDES} ${configurator_RESOURCES}) +TARGET_LINK_LIBRARIES(veyon-configurator veyon-core) + +ADD_WINDOWS_RESOURCE(veyon-configurator) +MAKE_GRAPHICAL_APP(veyon-configurator) + +IF(VEYON_BUILD_LINUX) + XDG_INSTALL(${CMAKE_CURRENT_BINARY_DIR}/data/veyon-configurator.desktop ${CMAKE_CURRENT_SOURCE_DIR}/data/veyon-configurator.xpm ${CMAKE_CURRENT_SOURCE_DIR}/data/veyon-configurator.png ${CMAKE_CURRENT_SOURCE_DIR}/data/veyon-configurator.svg) + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/data/io.veyon.veyon-configurator.policy DESTINATION ${CMAKE_INSTALL_PREFIX}/share/polkit-1/actions) +ENDIF() diff --git a/configurator/data/io.veyon.veyon-configurator.policy.in b/configurator/data/io.veyon.veyon-configurator.policy.in new file mode 100644 index 0000000..90aeaf9 --- /dev/null +++ b/configurator/data/io.veyon.veyon-configurator.policy.in @@ -0,0 +1,20 @@ + + + + + + Veyon Configurator + Authentication is required to run the Veyon Configurator + veyon-configurator + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + @CMAKE_INSTALL_PREFIX@/bin/veyon-configurator + true + + + diff --git a/configurator/data/veyon-configurator.desktop.in b/configurator/data/veyon-configurator.desktop.in new file mode 100644 index 0000000..28d9246 --- /dev/null +++ b/configurator/data/veyon-configurator.desktop.in @@ -0,0 +1,11 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Exec=@CMAKE_INSTALL_PREFIX@/bin/veyon-configurator +Icon=veyon-configurator +Terminal=false +Name=Veyon Configurator +Comment=Configuration tool for Veyon (Virtual Eye On Networks) +Comment[de]=Einrichtungswerkzeug für Veyon (Virtual Eye On Networks) +Categories=Qt;Education;Network;Settings;System;RemoteAccess; +Keywords=classroom,control,computer,room,lab,monitoring,teacher,student,settings,configuration diff --git a/configurator/data/veyon-configurator.exe.manifest.in b/configurator/data/veyon-configurator.exe.manifest.in new file mode 100644 index 0000000..e4e7447 --- /dev/null +++ b/configurator/data/veyon-configurator.exe.manifest.in @@ -0,0 +1,21 @@ + + + + Veyon Configurator + + + + + + + + + + + + + + + + + diff --git a/configurator/data/veyon-configurator.png b/configurator/data/veyon-configurator.png new file mode 100644 index 0000000000000000000000000000000000000000..884a8ba171d471c100c031417aa08f5165e41f5c GIT binary patch literal 781 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-s%*9TgAsieWw;%dH0CG7CJR*x3 z7+4KKm~s1C4Ui_u64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1V42l#}z z0%=cA&xC}8P%sD%4h|0w2Qq<-kdTm|pdcU{$N+L73LtC<7s!UFgp&|iAc>U$F$yRJ zQ4dx{6aiEp5fKp?85tcNotBn1VZwxo6DLlZG-=9|DO0CToik_7+_`h-&6~Gk#fq(4 zw{F|EZU6rL2M!##dGqG2TeoiCzJ34x{pZi0zj*QD-Me@1-@pIx;luau-+%o0@&EsS z2TOqiz#uRy3GxeOVCPU&QB~8`v$b<_c5(Ia^a=})h|S9{ESfQM*6cY87A{)6ci;X4 z2d`bf@$l)hSFhi``~2n4zYA+k0vH$=?L1u^Lp+YZotzuYm?+XF@2a{aNa^xymx|9w7@ys-?_KTNGnU^gTW)Mf+t#<}qO-Vt{U;WNn*!$b zZ0!d6`@`pO9gUq<(44=nu8w*3R?huJe~ + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/configurator/data/veyon-configurator.xpm b/configurator/data/veyon-configurator.xpm new file mode 100644 index 0000000..8dcb10c --- /dev/null +++ b/configurator/data/veyon-configurator.xpm @@ -0,0 +1,62 @@ +/* XPM */ +static char *veyon_configurator[] = { +/* columns rows colors chars-per-pixel */ +"32 32 24 1 ", +" c gray33", +". c #555555", +"X c gray37", +"o c #676767", +"O c gray43", +"+ c gray58", +"@ c #959595", +"# c #B6B6B6", +"$ c #B7B7B7", +"% c #BBBBBB", +"& c #C5C5C5", +"* c #C6C6C6", +"= c gray78", +"- c gray79", +"; c gray84", +": c #D7D7D7", +"> c #D8D8D8", +", c gray95", +"< c #F3F3F3", +"1 c #F4F4F4", +"2 c gray96", +"3 c #F6F6F6", +"4 c white", +"5 c None", +/* pixels */ +"55555555555555555555555555555555", +"55555555555555555555555555555555", +"55555555555555555555555555555555", +"55555555555555555555555555555555", +"55555555555555555555555555555555", +"555555555555555555 5555555555", +"55555555555555555 555555555", +"5555555555555555 55555555", +"55 55", +"55 55", +"5555555555555555 55555555", +"55555555555555555 555555555", +"555555555555555555 5555555555", +"55555555555555555555555555555555", +"55555555555555555555555555555555", +"55555555555555555555555555555555", +"555555 5555555555555555555555", +"5555 55555555555555555555", +"555 o=33-O 5555555555555555555", +"555 o,44443o 5555555555555555555", +"55 =3#@@#3= 555555555555555555", +"55 %X :: X% 55", +"55 %X :: X% 55", +"55 -3#@@#,& 555555555555555555", +"555 O34444,o 5555555555555555555", +"555 o=33=o 5555555555555555555", +"5555 55555555555555555555", +"555555 5555555555555555555555", +"55555555555555555555555555555555", +"55555555555555555555555555555555", +"55555555555555555555555555555555", +"55555555555555555555555555555555" +}; diff --git a/configurator/resources/access-rule-ask.png b/configurator/resources/access-rule-ask.png new file mode 100644 index 0000000000000000000000000000000000000000..4ddd3fc465cdc3c27a2c02f2337c312a3140c298 GIT binary patch literal 1600 zcmaKrdpOit9L5L5j6y3bGijfcCznu(Mq7F`N;Af-T&GlKUAt_=L{TaRC7W2K(rj(% zLXt*E=HgN=JCd!?D1|}p!yvag=j?B)?b*NfdEWD$&pF@se4q2jIcMEmwrQx7)NweR z293I92R7R&23`eoPRZ#(Y*LA!&^+*X{JRdNZS`Tf zhX%2tnBjrAsHiB5eTNQ2_%p)-EkeVCvxYa4a5!bAo0G>@ER1qJk=zvcLWsZR71b7&HaSZrQ04rg@!%CPtDPou_l3(}#&GYGy zM~7cr&cpg+o#|io?I4!_*6QoQ@BZJ;-#7msuxns)27DL5cLf4WZa}ad2;D)s7by1z z6+WPX0V;QbN?%a58&vHD&oD8;bARwW08|Hpnh;R457h1lwFf}$K_H3%A{MAW0_vl{ zi=(h18aBkhhU4&MEPQzqHlBiwr{Svv_$m>;N`kM?z&Gb$Qz~pugDn~GZ6*|7hOJq! zl>^`3fNi&6+imy((;fIR7q;Jr?Rl`{A?z%GosXd8F_aX+u433#0=u5T?o!ypgP-{D zlMwb*!M&B)N~7@8eNvlB=@iNG`hGYHHgh)IqhKoBB?3C;U3tay>TSvy?Z zaVm4>s;c1$>htDnFVy*wNLsAFWZCi+M#gKctjRWXXBStu?K{E_hew<`9T(3|NIa94 zo{@R^O4hZTIk^w=h2@o1q8AN~@7}k4_}JdjDe3DU7#tdwj>^Wyr>18#=k1HZUUe^R z%VrNf;!wq;y)#o^tv$5#wsBz4mb4%0A`9Jn_%}J>C-O2f4sxToYbl>U#DtbMH%&gY zY&a&hmDp~Z7LPTaY>x9^=)U5qtS4ILyU<feg-u)&@MnZBG+r;eqx^tUSC3o0@?+i2pJ z5yKcZW77-*de~uCyJHWsS6_3|Zujm8EZs12Zh8D_vU2})iEByf(7m0HVq0=j^JctM z4VGUUPqe#LDH808q2I_oUY%<@LSMl-LkZ#V3P_}PK1cAeThp?w@sYZ~@A=_5^OtID z(;UoL$GxemB-T{#)!3EgvVP$LOjDuGjM~fpcOo_WKi!tLFC;jm|+scPAdo>|b#Ju1bw^%pQs$j&=B*oXT zi*jgSk*#;;oowwIUlX#DHghRC+g`Khb!QS})^WqIJZ0 zqWXjMzjo-^*w^lPKmTpm#0{Z3-GZGUrkUrMEKBDaq%{extuHQ*J-CS_tA18=BZ$!7 r+;Zh-+ZIV3XH$jv?tuj$xX(!Gm@VO^8ub`esTW*M2mNH literal 0 HcmV?d00001 diff --git a/configurator/resources/application-x-ms-dos-executable.png b/configurator/resources/application-x-ms-dos-executable.png new file mode 100644 index 0000000000000000000000000000000000000000..c8fdeb41a9f5e0e300e05af63edaa477f9a23cd8 GIT binary patch literal 349 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!n2Vh}LpV4%Za?&Y0OWEOctjR6 zFqp@JFr#FH8<4>uS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjTBIkeSlAh zE07KbgV4}WARPh*KsFGBg@plm5E3GSNiJ|ar4KYhq$J2Mm_bQH%h1X#wxws*?8U3M z>^gb(#ml|Z_S^u<7kIijhD02Gd(D!s!GMSLV&eVsclBXXOClT`Cunf|pQ^WPqcW>Y z*=uL67V#PE`kyr~{Ja|{UY1Z8)o_X@kM+#Fs)k=#gQHL_*N{(t-ttRXM4k+du;3Kt2OO+Y$*lW&EVuS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjT8d|xzG>{t<6ogDhL_`3&!NI}d;o(Fw zAj*IwL`z6WNLW}Hkc-BEaFGR()gWtusDXS9lLh#*|-0|!NVs`o<4u!@|CMM zZr-|m=kC4x4<0^x{N(Ag=PzEpe*6Bz$FJYM|M>aq_n*K2HW#fIXJBAj;_2cTA|d(q zV7hZqAOqV2@x}!rQf4Nb=ccAs&&b`naAjuo`%G_z!)nVW3NG^X<`ukef9qkvLG6pS z;nW=wO9?Fm{V_VZ)lW5>EfS8AN%-V|m%DtoecPG!b^_Mb{cP%9+HzNYFQyLk5hT= z9IZ=NO%JS$N_b-Z)h1gZq+;cFQ8&K+qM*&Jla}^QcH=vE;gmvfp7L9};Hv4)3tt#c zUZl5VXR=S0e#@k%va0$eR+@VoCHGFzb9&CTG;hX5=a)hy>w>rr^lYE9=fcY+U)Zu* z`)7!hDxQ^1zVtUTAmU@A=Jtu-8Z>Ra6JK25P&bWMh`eQy`9qUxJZ8ZUrMsAA#=1J~)EV5`RL)qUiGV8ZIsy{cG zdF{~^oWbfLy+4<;oxChyrO8v>J#p^mBj=bnwoUfQ-sBYV*7h~ShK!~lUXfEX!G&eC@PP6hCYAoLPm^4XyDScWSM?&MK{FS^1MA;aqxJf%8*KQ`v^C(`Pc}XgSuX{OAuCdvZWFVc7!Fodt%@|81V6Fq+oSvt96EEz8cx<4abTJvbmxujkzNEnt>L wqrvxQT2EHpFn2oG|L5j4^IvVN40U(!pZMwiju#=D1wnmdKI;Vst03upcT>t<8 literal 0 HcmV?d00001 diff --git a/configurator/resources/configurator.qrc b/configurator/resources/configurator.qrc new file mode 100644 index 0000000..10e2ee2 --- /dev/null +++ b/configurator/resources/configurator.qrc @@ -0,0 +1,16 @@ + + + veyon-configurator.png + help-about.png + media-playback-start.png + media-playback-stop.png + network-vpn.png + configure-shortcuts.png + application-x-ms-dos-executable.png + application-x-sharedlib.png + vcs-conflicting.png + vcs-normal.png + vcs-removed.png + access-rule-ask.png + + diff --git a/configurator/resources/configure-shortcuts.png b/configurator/resources/configure-shortcuts.png new file mode 100644 index 0000000000000000000000000000000000000000..63c74a20e1c2a75e7d787f32bf038758947ed4b6 GIT binary patch literal 574 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!n2Vh}LpV4%Za?&Y0OWEOctjR6 zFi5WlVa7PAi84Sz$r9IylHmNblJdl&REF~Ma=pyF?Be9af>gcyqV(DCY@~pSCI|S0 zxB_W6H@DExP(MFEPcR4!3hyu5sUeSLghE1EdoIZ2*-1!Ud z-hcS`>GPLAfB${3y66ei`_R+HF(l&f+o`w38UsXJwOd+>x=RFA?{4}3|FuNPm1f5< zx$iYm{BKR&mfxK@>4<@kbz9k#=L>CJo}X=zQ(-gPzaz+%(X>bJi;~;DBzIn&1rM(1 zaM;;-*?;=q^j&7Qar?UV9gf!w);&M3wTa{NqpcpnIik<6Y8^HHk}i?cY{S2XF0g&TQ= Rn}D8W@O1TaS?83{1OUgA*Af5# literal 0 HcmV?d00001 diff --git a/configurator/resources/help-about.png b/configurator/resources/help-about.png new file mode 100644 index 0000000000000000000000000000000000000000..d4127f2e9abc399a9fb6343ce713eb3920506309 GIT binary patch literal 1168 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!n2Vh}LpV4%Za?&Y0OWEOctjR6 zFqp@JFr#FH8<4>uS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjT8d|V?cmU zh$~Pr5TvE0g@%TDdV2c$`T`k#etz!m?ty`U5D{;0?|^^+prDVBkC&GhkO5Q%6!iD^ zN9FuA!-AU}$7)YGG+* z?ds<4;pycU7!(>286A_DoSNR)(%RPEF=gts=`$8BUb1ZYij}KYZ`inL^S13fcJA7< z_w2dz7cO19e&gotJNNFtc=_t}o44;ieEjtJ>$mSee*XIXr@>^6JOcxhxu=U`NW|f{ z({6_g1&XwP_lO9QDs#LdaFAO(*!iOyXVj7N<7pgp0lPPiFAR zp0z0ZF1O&yzXitsZ{B|PyLIY{_kr>AkBdkDy=bF(bdJk>mIeRnwtOwgSrfRdl%pb!4_apzH(Fgk4jY83bflXZtVeC}d-q+K`$M$~~bp!ISd_V^Woa%#S53HSBRO z84n-m6#BsZ;RR!V<9sg%-h!8m{0*=EGyW;&Sl-~z|ADzuHQ{!oWP0P8rzXW0JTwym zIXM_voo}Cdeqe{tg?PqeB@GM#Vex7=naoNWd=+vFgPk1?XY*Iw+_0xd>DcwYi8mR4 z@W@2{i%Da6_J)C*A^6(kGG@c!r3JzPW}%3?`QvrzJ@?`Y@~C7Z;qEv2y2y zIZGcMmYaV1asTpjv(?Q73zsz7ew{S6TFK9}_QuP9xn8>_7)~^d-g~xW!)n)Hmm{(< pr=G3P+Pb=?tniMm?bnp`_7^8Hu&m5}Z4N3AJYD@<);T3K0RXC5(FOnj literal 0 HcmV?d00001 diff --git a/configurator/resources/media-playback-start.png b/configurator/resources/media-playback-start.png new file mode 100644 index 0000000000000000000000000000000000000000..1a5aa9e3b0fb4dd92695f1489225ad73416c552f GIT binary patch literal 297 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0J3?w7mbKU|e=3*z$5DpHG+YkL80J)q69+AZi z4Cb*Q%qZF524pZumbgZg1m~xflqVLYGL)B>>t*I;7bhncr0V4trO$q6BL!3>9pDq< z3Z&iK-2DCh{rvoVQ329 zg+}HJ;f!U|dqY?oTST^K3ItpT^zm?XS5RaFVdQ&MBb@0H(-W-2eap literal 0 HcmV?d00001 diff --git a/configurator/resources/media-playback-stop.png b/configurator/resources/media-playback-stop.png new file mode 100644 index 0000000000000000000000000000000000000000..5d0113ad1ab5ba77e36d2b56eb854680720e030d GIT binary patch literal 198 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0P3?wHke>@jRF&8^|hH!9j+(yEr+qAXP8FD1G)j8!4b7wg8_H zS0L@{>pNloYD*xCu_VYZn8D%MjWi%f)zif>L}Oxd!UC}${A@oY+9?0<_6^Y`S}4wfQ-PvKp+Fi_44upl0H5@KrWCS z5D?()?G5BY#DN-tTz`LmI0GmJR1Z-D6o*Iw1%V2HY={)ZK%fW^K-6Qhfogz)5E3E_ zR|8}~41_ZvT)1MmEV353Vl+Vr7orzUBSZ>L!p(<^5XlJt5V#E(=+z}be!&ckOe}2d z99-NyynOt^qT&)#(lYW2N~&t=8k$<#I=cFX#%AVLHV#fMuI?Vb{sDoZVUcn1$th`h z`GqCrRn;|h^{pM9{S&55pD}yx{KZR`Enm56H7g6%@}i76(BGdKD! z^;I&SJVk899j~<=EK|iaH#1GSvgyW`f1fK0do9jeGoQH-e$Fv^HgoX#kM>(4i^7~w zS52~+^2V{-t!T}yeeGdij`L?&bbLRoTf5O^@})(5;x^^ex52|V(WN2#*e1Ug^$J|kH07Q{MjF9sWOX=+917-g-f+n*bK2)aORBrmW=rMh z1axGtxOgvcM*Ccbr8~sig1x6OZFqb#=u}If=()gCRSDU%x=dEt1sk$f*Q$Ey2Yj6r z6zML@>GgPp)bcGYRczlhzbriEo_N21*R;%Ojf&ozRbEN!TvlOpZc=;dXtKd!vbupw zhEc!UuFp1Q!fnwu+lr-lZ4-PKGPm_HWjkd&((SKtJZsf|i3!|`QYrQ!5TD7+Xavt7zF@DPrV>azopr0Kq-L A3jhEB literal 0 HcmV?d00001 diff --git a/configurator/resources/vcs-conflicting.png b/configurator/resources/vcs-conflicting.png new file mode 100644 index 0000000000000000000000000000000000000000..0d7885c5beef37f28aebfa68da2f9b3d9e9adb75 GIT binary patch literal 1171 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!n2Vh}LpV4%Za?&Y0OWEOctjR6 zFqp@JFr#FH8<4>uS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjT8d|qf3BK zh$~R>|IpC=4i0CG)2@bw-uCpo?~w4=GW40R??t!Rs}7-8-9xWC2i|muyzLo!%{}Ob ztKUth(Ay3{cijAMxdh*K3INL9a16ca7JSDo^oDEjP3NFnZo#)*f^NA4-3BsTgKoQo z+;k4U=?WAH0gB&t0jdFUZ-T{dfyF^aIS1W#4FNK4xdww&Is-`{8>kB;brWbZP$N(T zf}FuBLAC%zKxV?kVGO7;BrPCgAqb)i&c)8S?HzT;H~L{_>7$&AN4b@skDT~?U7-l zyKwu?-Fx>RJbd){<*V0k-oAVP@zduoKYsq=yba4!cIQ({cWb`cu5r^ik zMT;&Sebn?QNJDJpwQCD@tQXxP$|8PbHOpGBPyg?K4W4;M<$D@i?N_^}%;!93oQZY2 ze6P7@nupTfdAhM7UN^s%C|$ZFbJuKV5tq{(9sezwkN2)rc6gMr%boN6EL~>7r!)O5 z?LUg!JW7aZ+Ww!#Q-^KOkA7=~-nRm0zyD`!y}=*+$3bq&qYvEY>Q6K#{rTOp?mnN! z@oDmcFW$U9{aVT`^nd)u*YSCEm!9S83LA#JzBV_v!|JC)-(%e)A5KKid3a!_dOGX9 z+=@q)&R$QN)0N(v?D;Tfp-$Sh{$u+2YuskEot0Mm{bSk3o8^lCBmafH&viaqS|1nv z;Pv6~cZqk|rm#yqX3>6-s8A8{=+%AB3v6BO$@0QI7IBjK3Y86tY91D5yxU(a^ZX=` z-o$!++vfR=&m7a6cqd5Tu~}ot7{oD$t)*Q@Si~{yK!t{ZBdZGAl=kAz1$m0QrNl*4 zZ4xyq^pfsw>@Z#LdS}9?6$k5jGcEfipUr%5`}MY>9!{kve|?$P8k{%uSh{&e{gOuS z^xT=xGM(ApYDN52;0fHbBIzr$>67T6n{4jZM9!aND#m%7G>Y_+PloQz1QOQE9G-)W{)xypDJqKZtu^0 zczAnn<*aX&;jQOeUj~|PS^DAs?>%j)`%7+I`#Y)9cGvrzHH$wnJ1dmBJzRd0Z>h1j zLF9oCe?qqZT9}>lGGdAM%#7?}cJuoMG4Z#x!?cd>4Yf6UQf<$1`6I)n*>jqd_D)X$ PCT0dtS3j3^P6uS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjT8d|V_1Ms zh$~QW=&DeM{SC%v(zLHOsNBv~*xz7ztPsf6yI7)qHB0+gp~lH<<1@vE=W>nD6d0Z> z)4x!tcOqBkRFUrKLY*_E`X`I@PUq{MDb_t(taGl=;8ecenS8yoMFwXI^v)LQodzk> zJyW26wov~pPz1!)KU1WC1}t?3C0hIy8A%driK#a2m`XF%-8)%yD8K7RE zMi2vtK<0zgL-c~!Kv}Q}1z>TAECCW^4mM3X!j z=kdk~Pqs{dwqx$|Jqus#Kl}OaxzG2`f4+C=%Y$oQAHVGPK#KY#IYXI3*X zFj;uIIEF+VemnhUxUiu}d+>rNktI7q7X*}bFKyxq{^shS*?pmP={0U83$8As|Npu9 z=cG-VSv}eM{hxVNMW1RXd#a!875J`rdCEDlu&%55U$Y9APVhB%T^!ve*6(oo;h_X> z>H10f%#%Jx9P|IdOvCV z&sO)#&OEa9m&*Ees_OI;(ZkZYf2Z%cja$j~s%@?7S=bd>t_%Gedgx&%WAb!`IUIB=Jn$h8=2l1sggz*G%^QJRw!oM?U+@)M>j; zUv<*-*zfWgT~Bvxy;1gPT9xhQ+&TN~ezz)4ROeo|_>S|nxl47!ovy#Xk^FDz>iT~U z%U2iAe3l`;J>vX|&3htOUe})Oea@rY=T7?>-Ij;5w;40vl?NtP22WQ%mvv4FO#u3l BAbtP< literal 0 HcmV?d00001 diff --git a/configurator/resources/vcs-removed.png b/configurator/resources/vcs-removed.png new file mode 100644 index 0000000000000000000000000000000000000000..733cc76218744f66437fa0c730edd2b3fed095e6 GIT binary patch literal 961 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!n2Vh}LpV4%Za?&Y0OWEOctjR6 zFqp@JFr#FH8<4>uS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjT8d|qildr zh$~R>s#U8R8X9KKoCzc+O`0@+{`|haJ|MTdyL;-?sX+FWDN}%4pj3Z<|K!P&r%#^_ zWCNw9O`8T}0A(jkm;e-?IB_D71d0PifZ`AU6ajJ}Bv1sI4HN{5AS-}~K-3^hLD)bN z!T>5m23Q3l3Lr)yD+aPL)nirm|Nnnx2ji2#0E#UM@(X5QW?^Mx=iubx<`EJW5tWpZ zmXTFbR#Vq7HZe7~@b>ld4+sn@C@d;&Xl!b3X>IG6IBClC88c_kS+sK1+I9O696EgZ z?70iK@7%q2|G~pYk6*re{pRhv_a8rf{_^AJFFx+fYG914^>lFzi8%ardSvh+0}0o| z%T{RYSk$qmNkL(UXsVz{fQE*3$JfvQ|MSGp@Zt8H-C|cHC;4t><}Ll5jv2h7tL-PQ z-{!Xe)QZrv>VLxaZLy7-*VN{znUvo5K9$WOVZy!CuKed|4vEw58l7{ub8Pp!p|G!z zo6W}h>nCp3<|@CpzU6bb!(-^ImX zTf#wBj)#YvT4pHpvHB-5adO8!-~Y$Z=zldUcZ#7SH+Nf7lbx7GQj`2=7H#`Or@D81 z%0AOw>(L)kpsLo9*ck6qkao{v@uZFg3U#Y==IbiW&N{#B-n7eqnwIQj=RWm`D=1BV zoyHk;4ju6p_IZC~S3bIO^K<@^u4bDvr!N#u&YrdX^t9zyZCd*7MSj{eJucMNwC#EM z>#3>ji8rUou+`e$xE8yw;bZH;nG4SyHQ9QyB)xFyuOmNw-))HB<#cqTnXMK}ZjHG4 r>B^W@Chc8DIr}^e=d-R|_=$hHOkMQ((=lPd6vE)?>gTe~DWM4f56{HK literal 0 HcmV?d00001 diff --git a/configurator/resources/veyon-configurator.png b/configurator/resources/veyon-configurator.png new file mode 100644 index 0000000000000000000000000000000000000000..b79b457ac129138ad8b7633ef5d1851320e1a813 GIT binary patch literal 1828 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&F&8^|hH!9j+(yEr+qAXP8FD1G)j8z}|`CaVCS z5LcjL2M33=w6xIBP)|?KgoFej0|){G10y3Nkr_cjK@kxVK#}n9@ZjKJkfP8~2qPpU zBrGfp$VDR|;y@B81t%eD;95YYfHguy;IcqLxEeSEmmtUpx&WZ_fPnxEnW(6!=;-K} zn3&ku*tobjV7MhFCMG2%rKF?)12ZEdBP%N_J3BikCnq;IH!m-*prD|zu&|`0q_niO ztgNh|p`od%simc*t*x!Sy}hrmufM;4!h{JECr+F)Wy;j4Q>RUvHfz?bxpU{vpFe-W zf(45gFJ886*@_h_R;^mKX3d(lYuBz{zkb7p4I4LZ+_Y)a)~#E2?AWnu*RI{WckkJ= zXYby<`}XZSaNxj+6DQ7|J$vrlxl5NWUAc1Q+O=ypZrr$a>(=etx9{A!bN~MR2M-=R zeE9IuqeoAkJbCu)+3VM@-@JMA{{8z8A3l8i`0>-HPhY=&{qf_+&!0bk{rdI$_wPS{ z{`~#>_us#N|NsAIn05IoF!iLA1o;Isu&}bRb8rd?i-?MeOGrvdE32yO>bbhPd-(bV z1O|tOMI@x8rDtSjW#{Aa^)Iw(Zz=;Na0?$4{I*b>`fK zYu9hwy!GhulQ(bQefaq4^OvtbfBpXRZ{OGV62R2|#?!?yL_+fI!Pm~gsWR*z=JPdi zv?a|D@tiR;N&Jckr;aJJr;nGGr1gY1(T1_Ju0|xKa?L6WeiUS}#hUR_@5Ppb@9VP* z=50QB^W2-GmGi=%e^zWi{II}6hOhmwU8L$(_E{!xPjH*Qj#(1-uvku|;@b|-wQpo| zBiwG!^X1yfujIOS_7u%LH>tBnmvsC#U2^q}n(DjEhSpy>L5b`-xyr0uw-ZiAJAAV3 z=gIQirNeKOQ8+P2^+bm3-}r!@GYceUIvN@^C#A3*J;Bp8QBp+NSi`MM)6p&4(`-7s zRn*Lle?{(v>zwUePP5*Ac`)MC>qcSkf0XDXmwWbK&fA3q>m-r>5l{ zDuuFb2kk{3rzSPmNl1lHlJP$*FzboP_T32;la@p*b^Kv*DAQ+d^MlgKJi8TJl;&Iu z5pRiAamY~A9Ff;e>*nQ5f8YF0 z?e(+zzt1jT?y&xR^Q)oQp$k7hoydE7fpyWI2k+OPPv}gzZ=&D9wm@5Xcc^oD=i`67 z{+RjviT5dt;V%hIG3Q8pHZyy!fF4hMy_{2m!m%1=Geu*LWSeh-Jbz`%dE4G`%C-8Q z;5qa~*PbW+xn-Unhx7At!5b}S6l(gGXzy7e?irpaY}8z^Tbfh6;-b2Tg*D&o36d=@ zUx-TlsV}{wes#(h@wqzU$+n7oIOpoyhzC zbAfc?;X6k>?oW@et$y8KY1Xpv^V5lM`JBYNHZHxXP*R$*Usr(bW{`c$@so9yQYHU5 zw;9~yoNL~$vZCpN>CsGIRz(|UBatkDbH0UA|4W=RbQLt`H7Qyg)I8Q~VB=V{Ca2|~ zy};wtwBOc=7F(KBG_zct3RF&UUMvvc)Lrd4tKE8n$cvpHt5eF%jI8by3wg@&PjZni zG~eg$xr+Dr#K7apDv_DmMjvckE{U2Ry0q}>k^^09<(4cB-Q~gi-H%suk2SZ)+&O2z z+UY!8eS3~`*Hhb|t=nJre*1QP_P2LhyQhP)A}~j${N#UhrM7Y99nNZCIm6)T>gTe~ HDWM4fwIo29 literal 0 HcmV?d00001 diff --git a/configurator/src/AccessControlPage.cpp b/configurator/src/AccessControlPage.cpp new file mode 100644 index 0000000..b9cb632 --- /dev/null +++ b/configurator/src/AccessControlPage.cpp @@ -0,0 +1,293 @@ +/* + * AccessControlPage.cpp - implementation of the access control page + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "AccessControlPage.h" +#include "AccessControlRule.h" +#include "VeyonCore.h" +#include "VeyonConfiguration.h" +#include "AccessControlProvider.h" +#include "Configuration/UiMapping.h" +#include "AccessControlRuleEditDialog.h" +#include "UserGroupsBackendManager.h" + +#include "ui_AccessControlPage.h" + +AccessControlPage::AccessControlPage() : + ConfigurationPage(), + ui(new Ui::AccessControlPage), + m_accessControlRulesModel( this ), + m_accessControlRulesTestDialog( this ) +{ + ui->setupUi(this); + + if( VeyonCore::userGroupsBackendManager().accessControlBackend() == nullptr ) + { + QMessageBox::critical( this, + tr( "Missing user groups backend" ), + tr( "No default user groups plugin was found. " + "Please check your installation!" ) ); + qFatal( "AccessControlPage: missing default user groups backend" ); + } + + const auto backends = VeyonCore::userGroupsBackendManager().availableBackends(); + for( auto it = backends.constBegin(), end = backends.constEnd(); it != end; ++it ) + { + ui->accessControlUserGroupsBackend->addItem( it.value(), it.key() ); + } + + ui->accessControlRulesView->setModel( &m_accessControlRulesModel ); + + updateAccessGroupsLists(); + updateAccessControlRules(); +} + + + +AccessControlPage::~AccessControlPage() +{ + delete ui; +} + + + +void AccessControlPage::resetWidgets() +{ + FOREACH_VEYON_ACCESS_CONTROL_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); + + m_accessGroups = VeyonCore::config().authorizedUserGroups(); + + updateAccessGroupsLists(); + updateAccessControlRules(); +} + + + +void AccessControlPage::connectWidgetsToProperties() +{ + FOREACH_VEYON_ACCESS_CONTROL_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY) +} + + + +void AccessControlPage::applyConfiguration() +{ + VeyonCore::userGroupsBackendManager().reloadConfiguration(); + + resetWidgets(); +} + + + +void AccessControlPage::addAccessGroup() +{ + const auto items = ui->allGroupsList->selectedItems(); + + for( auto item : items ) + { + m_accessGroups.removeAll( item->text() ); + m_accessGroups += item->text(); + } + + VeyonCore::config().setAuthorizedUserGroups( m_accessGroups ); + + updateAccessGroupsLists(); +} + + + +void AccessControlPage::removeAccessGroup() +{ + const auto items = ui->accessGroupsList->selectedItems(); + + for( auto item : items ) + { + m_accessGroups.removeAll( item->text() ); + } + + VeyonCore::config().setAuthorizedUserGroups( m_accessGroups ); + + updateAccessGroupsLists(); +} + + + +void AccessControlPage::updateAccessGroupsLists() +{ + ui->accessGroupsList->setUpdatesEnabled( false ); + + ui->allGroupsList->clear(); + ui->accessGroupsList->clear(); + + const auto backend = VeyonCore::userGroupsBackendManager().accessControlBackend(); + const auto groups = backend->userGroups( VeyonCore::config().domainGroupsForAccessControlEnabled() ); + + for( const auto& group : groups ) + { + if( m_accessGroups.contains( group ) ) + { + ui->accessGroupsList->addItem( group ); + } + else + { + ui->allGroupsList->addItem( group ); + } + } + + ui->accessGroupsList->setUpdatesEnabled( true ); +} + + +void AccessControlPage::addAccessControlRule() +{ + AccessControlRule newRule; + newRule.setAction( AccessControlRule::Action::Allow ); + + if( AccessControlRuleEditDialog( newRule, this ).exec() ) + { + VeyonCore::config().setAccessControlRules( VeyonCore::config().accessControlRules() << newRule.toJson() ); + + updateAccessControlRules(); + } +} + + + +void AccessControlPage::removeAccessControlRule() +{ + QJsonArray accessControlRules = VeyonCore::config().accessControlRules(); + + accessControlRules.removeAt( ui->accessControlRulesView->currentIndex().row() ); + + VeyonCore::config().setAccessControlRules( accessControlRules ); + + updateAccessControlRules(); +} + + + +void AccessControlPage::editAccessControlRule() +{ + QJsonArray accessControlRules = VeyonCore::config().accessControlRules(); + + int row = ui->accessControlRulesView->currentIndex().row(); + + if( row >= accessControlRules.count() ) + { + return; + } + + AccessControlRule rule( accessControlRules[row] ); + + if( AccessControlRuleEditDialog( rule, this ).exec() ) + { + accessControlRules.replace( row, rule.toJson() ); + + modifyAccessControlRules( accessControlRules, row ); + } +} + + + +void AccessControlPage::moveAccessControlRuleDown() +{ + QJsonArray accessControlRules = VeyonCore::config().accessControlRules(); + + int row = ui->accessControlRulesView->currentIndex().row(); + int newRow = row + 1; + + if( newRow < accessControlRules.size() ) + { + const QJsonValue swapValue = accessControlRules[row]; + accessControlRules[row] = accessControlRules[newRow]; + accessControlRules[newRow] = swapValue; + + modifyAccessControlRules( accessControlRules, newRow ); + } +} + + + +void AccessControlPage::moveAccessControlRuleUp() +{ + QJsonArray accessControlRules = VeyonCore::config().accessControlRules(); + + int row = ui->accessControlRulesView->currentIndex().row(); + int newRow = row - 1; + + if( newRow >= 0 ) + { + const QJsonValue swapValue = accessControlRules[row]; + accessControlRules[row] = accessControlRules[newRow]; + accessControlRules[newRow] = swapValue; + + modifyAccessControlRules( accessControlRules, newRow ); + } +} + + + +void AccessControlPage::testUserGroupsAccessControl() +{ + QString username = QInputDialog::getText( this, tr( "Enter username" ), + tr( "Please enter a user login name whose access permissions to test:" ) ); + + if( AccessControlProvider().processAuthorizedGroups( username ) ) + { + QMessageBox::information( this, tr( "Access allowed" ), + tr( "The specified user is allowed to access computers with this configuration." ) ); + } + else + { + QMessageBox::warning( this, tr( "Access denied" ), + tr( "The specified user is not allowed to access computers with this configuration." ) ); + } +} + + + +void AccessControlPage::testAccessControlRules() +{ + m_accessControlRulesTestDialog.exec(); +} + + + +void AccessControlPage::modifyAccessControlRules(const QJsonArray &accessControlRules, int selectedRow) +{ + VeyonCore::config().setAccessControlRules( accessControlRules ); + + updateAccessControlRules(); + + ui->accessControlRulesView->setCurrentIndex( m_accessControlRulesModel.index( selectedRow ) ); +} + + + +void AccessControlPage::updateAccessControlRules() +{ + m_accessControlRulesModel.reload(); +} diff --git a/configurator/src/AccessControlPage.h b/configurator/src/AccessControlPage.h new file mode 100644 index 0000000..8bb48f5 --- /dev/null +++ b/configurator/src/AccessControlPage.h @@ -0,0 +1,71 @@ +/* + * AccessControlPage.h - header for the AccessControlPage class + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "AccessControlRuleListModel.h" +#include "AccessControlRulesTestDialog.h" +#include "ConfigurationPage.h" + +namespace Ui { +class AccessControlPage; +} + +class AccessControlPage : public ConfigurationPage +{ + Q_OBJECT +public: + AccessControlPage(); + ~AccessControlPage() override; + + void resetWidgets() override; + void connectWidgetsToProperties() override; + void applyConfiguration() override; + +private Q_SLOTS: + void addAccessGroup(); + void removeAccessGroup(); + void updateAccessGroupsLists(); + + void addAccessControlRule(); + void removeAccessControlRule(); + void editAccessControlRule(); + void moveAccessControlRuleDown(); + void moveAccessControlRuleUp(); + + void testUserGroupsAccessControl(); + void testAccessControlRules(); + +private: + void modifyAccessControlRules( const QJsonArray& accessControlRules, int selectedRow ); + void updateAccessControlRules(); + + Ui::AccessControlPage *ui; + + QStringList m_accessGroups; + AccessControlRuleListModel m_accessControlRulesModel; + + AccessControlRulesTestDialog m_accessControlRulesTestDialog; + +}; diff --git a/configurator/src/AccessControlPage.ui b/configurator/src/AccessControlPage.ui new file mode 100644 index 0000000..8113f93 --- /dev/null +++ b/configurator/src/AccessControlPage.ui @@ -0,0 +1,608 @@ + + + AccessControlPage + + + Form + + + + 0 + + + 0 + + + + + Computer access control + + + + + + Restrict access to members of specific user groups + + + accessControlModeGroup + + + + + + + + + User groups backend: + + + + + + + + + + + + false + + + Test + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + Process access control rules + + + accessControlModeGroup + + + + + + + false + + + Test + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + Grant access to every authenticated user (default) + + + true + + + accessControlModeGroup + + + + + + + Enable usage of domain groups + + + + + + + + + + false + + + User groups authorized for computer access + + + + + + QAbstractScrollArea::AdjustToContents + + + true + + + + + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + + + true + + + + + + + Authorized user groups + + + + + + + QAbstractScrollArea::AdjustToContents + + + true + + + + + + + All groups + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + :/core/go-next.png:/core/go-next.png + + + + + + + + :/core/go-previous.png:/core/go-previous.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + false + + + Access control rules + + + + + + Qt::Vertical + + + + 20 + 88 + + + + + + + + Move selected rule up + + + + :/core/go-up.png:/core/go-up.png + + + + + + + Edit selected rule + + + + + + + :/core/document-edit.png:/core/document-edit.png + + + + + + + Add access control rule + + + + + + + :/core/list-add.png:/core/list-add.png + + + + + + + Remove access control rule + + + + + + + :/core/edit-delete.png:/core/edit-delete.png + + + + + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::NoEditTriggers + + + + + + + Move selected rule down + + + + :/core/go-down.png:/core/go-down.png + + + + + + + + + + + + + + + + + skipAccessControl + isAccessRestrictedToUserGroups + isAccessControlRulesProcessingEnabled + testUserGroupsAccessControlButton + testAccessControlRulesButton + allGroupsList + addAccessGroupButton + removeAccessGroupButton + accessGroupsList + accessControlRulesView + addAccessControlRuleButton + removeAccessControlRule + editAccessControlRuleButton + moveAccessControlRuleUpButton + moveAccessControlRuleDownButton + + + + + + + addAccessGroupButton + clicked() + AccessControlPage + addAccessGroup() + + + 494 + 412 + + + 494 + 404 + + + + + removeAccessGroupButton + clicked() + AccessControlPage + removeAccessGroup() + + + 494 + 455 + + + 494 + 404 + + + + + addAccessControlRuleButton + clicked() + AccessControlPage + addAccessControlRule() + + + 935 + 580 + + + 494 + 404 + + + + + removeAccessControlRule + clicked() + AccessControlPage + removeAccessControlRule() + + + 935 + 622 + + + 494 + 404 + + + + + editAccessControlRuleButton + clicked() + AccessControlPage + editAccessControlRule() + + + 935 + 665 + + + 494 + 404 + + + + + moveAccessControlRuleDownButton + clicked() + AccessControlPage + moveAccessControlRuleDown() + + + 935 + 749 + + + 494 + 404 + + + + + moveAccessControlRuleUpButton + clicked() + AccessControlPage + moveAccessControlRuleUp() + + + 935 + 707 + + + 494 + 404 + + + + + isAccessRestrictedToUserGroups + toggled(bool) + groupBox + setEnabled(bool) + + + 494 + 114 + + + 494 + 354 + + + + + isAccessControlRulesProcessingEnabled + toggled(bool) + groupBox_2 + setEnabled(bool) + + + 494 + 160 + + + 494 + 660 + + + + + accessControlRulesView + doubleClicked(QModelIndex) + AccessControlPage + editAccessControlRule() + + + 331 + 791 + + + 983 + 527 + + + + + testUserGroupsAccessControlButton + clicked() + AccessControlPage + testUserGroupsAccessControl() + + + 920 + 118 + + + 986 + 114 + + + + + testAccessControlRulesButton + clicked() + AccessControlPage + testAccessControlRules() + + + 933 + 167 + + + 984 + 162 + + + + + isAccessRestrictedToUserGroups + toggled(bool) + testUserGroupsAccessControlButton + setEnabled(bool) + + + 447 + 116 + + + 914 + 116 + + + + + isAccessControlRulesProcessingEnabled + toggled(bool) + testAccessControlRulesButton + setEnabled(bool) + + + 447 + 166 + + + 914 + 166 + + + + + allGroupsList + doubleClicked(QModelIndex) + AccessControlPage + addAccessGroup() + + + 181 + 464 + + + 357 + 484 + + + + + accessGroupsList + doubleClicked(QModelIndex) + AccessControlPage + removeAccessGroup() + + + 534 + 464 + + + 357 + 484 + + + + + + addAccessGroup() + removeAccessGroup() + addAccessControlRule() + removeAccessControlRule() + editAccessControlRule() + moveAccessControlRuleUp() + moveAccessControlRuleDown() + testAccessControlRules() + testUserGroupsAccessControl() + + + + + diff --git a/configurator/src/AccessControlRuleEditDialog.cpp b/configurator/src/AccessControlRuleEditDialog.cpp new file mode 100644 index 0000000..3d090df --- /dev/null +++ b/configurator/src/AccessControlRuleEditDialog.cpp @@ -0,0 +1,171 @@ +/* + * AccessControlRuleEditDialog.cpp - dialog for editing an AccessControlRule + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "AccessControlRuleEditDialog.h" +#include "AccessControlProvider.h" + +#include "ui_AccessControlRuleEditDialog.h" + +AccessControlRuleEditDialog::AccessControlRuleEditDialog(AccessControlRule &rule, QWidget *parent) : + QDialog(parent), + ui(new Ui::AccessControlRuleEditDialog), + m_rule( rule ), + m_subjectNameMap( { +{ AccessControlRule::Subject::AccessingUser, tr( "Accessing user" ) }, +{ AccessControlRule::Subject::AccessingComputer, tr( "Accessing computer" ) }, +{ AccessControlRule::Subject::LocalUser, tr( "Local (logged on) user" ) }, +{ AccessControlRule::Subject::LocalComputer, tr( "Local computer" ) }, + } ) +{ + ui->setupUi(this); + + AccessControlProvider accessControlProvider; + + // populate user subject combobox + ui->isMemberOfGroupSubjectComboBox->addItem( m_subjectNameMap[AccessControlRule::Subject::AccessingUser], + static_cast( AccessControlRule::Subject::AccessingUser ) ); + ui->isMemberOfGroupSubjectComboBox->addItem( m_subjectNameMap[AccessControlRule::Subject::LocalUser], + static_cast( AccessControlRule::Subject::LocalUser ) ); + + // populate computer subject comboboxes + ui->isAtLocationSubjectComboBox->addItem( m_subjectNameMap[AccessControlRule::Subject::AccessingComputer], + static_cast( AccessControlRule::Subject::AccessingComputer ) ); + ui->isAtLocationSubjectComboBox->addItem( m_subjectNameMap[AccessControlRule::Subject::LocalComputer], + static_cast( AccessControlRule::Subject::LocalComputer ) ); + + // populate groups and locations comboboxes + ui->groupsComboBox->addItems( accessControlProvider.userGroups() ); + ui->locationsComboBox->addItems( accessControlProvider.locations() ); + + // load general settings + ui->ruleNameLineEdit->setText( rule.name() ); + ui->ruleDescriptionLineEdit->setText( rule.description() ); + + // load general condition processing settings + ui->ignoreConditionsCheckBox->setChecked( rule.areConditionsIgnored() ); + ui->invertConditionsCheckBox->setChecked( rule.areConditionsInverted() ); + + // load condition states + ui->isMemberOfGroupCheckBox->setChecked( rule.isConditionEnabled( AccessControlRule::Condition::MemberOfUserGroup ) ); + ui->hasCommonGroupsCheckBox->setChecked( rule.isConditionEnabled( AccessControlRule::Condition::GroupsInCommon ) ); + ui->isAtLocationCheckBox->setChecked( rule.isConditionEnabled( AccessControlRule::Condition::LocatedAt ) ); + ui->hasCommonLocationsCheckBox->setChecked( rule.isConditionEnabled( AccessControlRule::Condition::SameLocation ) ); + ui->isLocalHostAccessCheckBox->setChecked( rule.isConditionEnabled( AccessControlRule::Condition::AccessFromLocalHost ) ); + ui->isLocalUserAccessCheckBox->setChecked( rule.isConditionEnabled( AccessControlRule::Condition::AccessFromLocalUser ) ); + ui->isSameUserAccessCheckBox->setChecked( rule.isConditionEnabled( AccessControlRule::Condition::AccessFromAlreadyConnectedUser ) ); + ui->noUserLoggedOnCheckBox->setChecked( rule.isConditionEnabled( AccessControlRule::Condition::NoUserLoggedOn ) ); + + // load selected condition subjects + ui->isMemberOfGroupSubjectComboBox->setCurrentText( m_subjectNameMap.value( rule.subject( AccessControlRule::Condition::MemberOfUserGroup ) ) ); + ui->isAtLocationSubjectComboBox->setCurrentText( m_subjectNameMap.value( rule.subject( AccessControlRule::Condition::LocatedAt ) ) ); + + // load condition arguments + ui->groupsComboBox->setCurrentText( rule.argument( AccessControlRule::Condition::MemberOfUserGroup ) ); + ui->locationsComboBox->setCurrentText( rule.argument( AccessControlRule::Condition::LocatedAt ) ); + + // load action + ui->actionNoneRadioButton->setChecked( rule.action() == AccessControlRule::Action::None ); + ui->actionAllowRadioButton->setChecked( rule.action() == AccessControlRule::Action::Allow ); + ui->actionDenyRadioButton->setChecked( rule.action() == AccessControlRule::Action::Deny ); + ui->actionAskForPermissionRadioButton->setChecked( rule.action() == AccessControlRule::Action::AskForPermission ); +} + + + +AccessControlRuleEditDialog::~AccessControlRuleEditDialog() +{ + delete ui; +} + + + +void AccessControlRuleEditDialog::accept() +{ + // save general settings + m_rule.setName( ui->ruleNameLineEdit->text() ); + m_rule.setDescription( ui->ruleDescriptionLineEdit->text() ); + + // save general condition processing settings + m_rule.setConditionsIgnored( ui->ignoreConditionsCheckBox->isChecked() ); + m_rule.setConditionsInverted( ui->invertConditionsCheckBox->isChecked() ); + + // save conditions + m_rule.clearParameters(); + + // member of user group + m_rule.setConditionEnabled( AccessControlRule::Condition::MemberOfUserGroup, + ui->isMemberOfGroupCheckBox->isChecked() ); + m_rule.setSubject( AccessControlRule::Condition::MemberOfUserGroup, + m_subjectNameMap.key( ui->isMemberOfGroupSubjectComboBox->currentText() ) ); + m_rule.setArgument( AccessControlRule::Condition::MemberOfUserGroup, + ui->groupsComboBox->currentText() ); + + // common groups + m_rule.setConditionEnabled( AccessControlRule::Condition::GroupsInCommon, + ui->hasCommonGroupsCheckBox->isChecked() ); + + // located at + m_rule.setConditionEnabled( AccessControlRule::Condition::LocatedAt, + ui->isAtLocationCheckBox->isChecked() ); + m_rule.setSubject( AccessControlRule::Condition::LocatedAt, + m_subjectNameMap.key( ui->isAtLocationSubjectComboBox->currentText() ) ); + m_rule.setArgument( AccessControlRule::Condition::LocatedAt, + ui->locationsComboBox->currentText() ); + + // same location + m_rule.setConditionEnabled( AccessControlRule::Condition::SameLocation, + ui->hasCommonLocationsCheckBox->isChecked() ); + + m_rule.setConditionEnabled( AccessControlRule::Condition::AccessFromLocalHost, + ui->isLocalHostAccessCheckBox->isChecked() ); + + m_rule.setConditionEnabled( AccessControlRule::Condition::AccessFromLocalUser, + ui->isLocalUserAccessCheckBox->isChecked() ); + + m_rule.setConditionEnabled( AccessControlRule::Condition::AccessFromAlreadyConnectedUser, + ui->isSameUserAccessCheckBox->isChecked() ); + + m_rule.setConditionEnabled( AccessControlRule::Condition::NoUserLoggedOn, + ui->noUserLoggedOnCheckBox->isChecked() ); + + // save action + if( ui->actionAllowRadioButton->isChecked() ) + { + m_rule.setAction( AccessControlRule::Action::Allow ); + } + else if( ui->actionDenyRadioButton->isChecked() ) + { + m_rule.setAction( AccessControlRule::Action::Deny ); + } + else if( ui->actionAskForPermissionRadioButton->isChecked() ) + { + m_rule.setAction( AccessControlRule::Action::AskForPermission ); + } + else + { + m_rule.setAction( AccessControlRule::Action::None ); + } + + QDialog::accept(); +} diff --git a/configurator/src/AccessControlRuleEditDialog.h b/configurator/src/AccessControlRuleEditDialog.h new file mode 100644 index 0000000..7450538 --- /dev/null +++ b/configurator/src/AccessControlRuleEditDialog.h @@ -0,0 +1,50 @@ +/* + * AccessControlRuleEditDialog.h - dialog for editing an AccessControlRule + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "AccessControlRule.h" + +namespace Ui { +class AccessControlRuleEditDialog; +} + +class AccessControlRuleEditDialog : public QDialog +{ + Q_OBJECT +public: + explicit AccessControlRuleEditDialog(AccessControlRule& rule, QWidget *parent = nullptr); + ~AccessControlRuleEditDialog() override; + + void accept() override; + + +private: + Ui::AccessControlRuleEditDialog *ui; + AccessControlRule& m_rule; + QMap m_subjectNameMap; + +}; diff --git a/configurator/src/AccessControlRuleEditDialog.ui b/configurator/src/AccessControlRuleEditDialog.ui new file mode 100644 index 0000000..50da4cc --- /dev/null +++ b/configurator/src/AccessControlRuleEditDialog.ui @@ -0,0 +1,429 @@ + + + AccessControlRuleEditDialog + + + Edit access control rule + + + + + + General + + + + + + enter a short name for the rule here + + + + + + + Rule name: + + + + + + + enter a description for the rule here + + + + + + + Rule description: + + + + + + + Invert all conditions ("is/has" interpreted as "is/has not") + + + + + + + Always process rule and ignore conditions + + + + + + + + + + Conditions + + + + + + false + + + true + + + QComboBox::AdjustToContents + + + + + + + is member of group + + + + + + + + + + + + + + false + + + QComboBox::AdjustToContents + + + + + + + + true + + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + + + true + + + + + + + + 0 + 0 + + + + Accessing computer and local computer are at the same location + + + + + + + No user logged on + + + + + + + is located at + + + + + + + false + + + QComboBox::AdjustToContents + + + + + + + false + + + true + + + QComboBox::AdjustToContents + + + + + + + + + + + + + + Accessing computer is localhost + + + + + + + Accessing user has one or more groups in common with local (logged on) user + + + + + + + Accessing user is logged on user + + + + + + + Accessing user is already connected + + + + + + + + + + Action + + + + 20 + + + + + Allow access + + + true + + + actionButtonGroup + + + + + + + Deny access + + + actionButtonGroup + + + + + + + Ask logged on user for permission + + + actionButtonGroup + + + + + + + None (rule disabled) + + + actionButtonGroup + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + ruleNameLineEdit + ruleDescriptionLineEdit + ignoreConditionsCheckBox + invertConditionsCheckBox + isMemberOfGroupCheckBox + isMemberOfGroupSubjectComboBox + groupsComboBox + isAtLocationCheckBox + isAtLocationSubjectComboBox + locationsComboBox + hasCommonLocationsCheckBox + isLocalHostAccessCheckBox + hasCommonGroupsCheckBox + isLocalUserAccessCheckBox + isSameUserAccessCheckBox + noUserLoggedOnCheckBox + actionAllowRadioButton + actionDenyRadioButton + actionAskForPermissionRadioButton + actionNoneRadioButton + + + + + buttonBox + accepted() + AccessControlRuleEditDialog + accept() + + + 233 + 407 + + + 157 + 274 + + + + + buttonBox + rejected() + AccessControlRuleEditDialog + reject() + + + 301 + 413 + + + 286 + 274 + + + + + isMemberOfGroupCheckBox + toggled(bool) + groupsComboBox + setEnabled(bool) + + + 514 + 89 + + + 876 + 88 + + + + + isAtLocationCheckBox + toggled(bool) + locationsComboBox + setEnabled(bool) + + + 628 + 131 + + + 891 + 133 + + + + + isMemberOfGroupCheckBox + toggled(bool) + isMemberOfGroupSubjectComboBox + setEnabled(bool) + + + 44 + 295 + + + 236 + 295 + + + + + isAtLocationCheckBox + toggled(bool) + isAtLocationSubjectComboBox + setEnabled(bool) + + + 44 + 391 + + + 236 + 391 + + + + + ignoreConditionsCheckBox + toggled(bool) + conditionsGroupBox + setDisabled(bool) + + + 548 + 179 + + + 548 + 516 + + + + + ignoreConditionsCheckBox + toggled(bool) + invertConditionsCheckBox + setDisabled(bool) + + + 548 + 179 + + + 548 + 225 + + + + + + + + diff --git a/configurator/src/AccessControlRuleListModel.cpp b/configurator/src/AccessControlRuleListModel.cpp new file mode 100644 index 0000000..bf1abfa --- /dev/null +++ b/configurator/src/AccessControlRuleListModel.cpp @@ -0,0 +1,101 @@ +/* + * AccessControlRuleListModel.cpp - data model for access control rules + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "AccessControlRuleListModel.h" +#include "VeyonConfiguration.h" +#include "VeyonCore.h" + + +AccessControlRuleListModel::AccessControlRuleListModel(QObject *parent) : + QAbstractListModel(parent) +{ + reload(); +} + + + +void AccessControlRuleListModel::reload() +{ + beginResetModel(); + + m_accessControlRules.clear(); + + const auto accessControlRules = VeyonCore::config().accessControlRules(); + + for( const auto& accessControlRule : accessControlRules ) + { + m_accessControlRules.append( AccessControlRule( accessControlRule ) ); + } + + endResetModel(); +} + + + +int AccessControlRuleListModel::rowCount(const QModelIndex &parent) const +{ + // For list models only the root node (an invalid parent) should return the list's size. For all + // other (valid) parents, rowCount() should return 0 so that it does not become a tree model. + if( parent.isValid() ) + { + return 0; + } + + return m_accessControlRules.count(); +} + + + +QVariant AccessControlRuleListModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() > rowCount() ) + { + return QVariant(); + } + + const AccessControlRule& rule = m_accessControlRules[index.row()]; + + if( role == Qt::DecorationRole ) + { + switch( rule.action() ) + { + case AccessControlRule::Action::Allow: return QIcon( QStringLiteral(":/configurator/vcs-normal.png") ); + case AccessControlRule::Action::Deny: return QIcon( QStringLiteral(":/configurator/vcs-conflicting.png") ); + case AccessControlRule::Action::AskForPermission: return QIcon( QStringLiteral(":/configurator/access-rule-ask.png") ); + default: return QIcon( QStringLiteral(":/configurator/vcs-removed.png") ); + } + } + else if( role == Qt::DisplayRole ) + { + return rule.name(); + } + else if( role == Qt::ToolTipRole ) + { + return rule.description(); + } + + return QVariant(); +} diff --git a/configurator/src/AccessControlRuleListModel.h b/configurator/src/AccessControlRuleListModel.h new file mode 100644 index 0000000..3e02d23 --- /dev/null +++ b/configurator/src/AccessControlRuleListModel.h @@ -0,0 +1,46 @@ +/* + * AccessControlRuleListModel.h - data model for access control rules + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "AccessControlRule.h" + +class AccessControlRuleListModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit AccessControlRuleListModel(QObject *parent = nullptr); + + void reload(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +private: + QList m_accessControlRules; + +}; diff --git a/configurator/src/AccessControlRulesTestDialog.cpp b/configurator/src/AccessControlRulesTestDialog.cpp new file mode 100644 index 0000000..6c571d7 --- /dev/null +++ b/configurator/src/AccessControlRulesTestDialog.cpp @@ -0,0 +1,90 @@ +/* + * AccessControlRulesTestDialog.cpp - dialog for testing access control rules + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "AccessControlRulesTestDialog.h" +#include "AccessControlProvider.h" +#include "HostAddress.h" +#include "PlatformUserFunctions.h" + +#include "ui_AccessControlRulesTestDialog.h" + + +AccessControlRulesTestDialog::AccessControlRulesTestDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AccessControlRulesTestDialog) +{ + ui->setupUi(this); + + ui->localUserLineEdit->setText( VeyonCore::platform().userFunctions().currentUser() ); + ui->localComputerLineEdit->setText( HostAddress::localFQDN() ); +} + + + +AccessControlRulesTestDialog::~AccessControlRulesTestDialog() +{ + delete ui; +} + + + +int AccessControlRulesTestDialog::exec() +{ + ui->accessingUserLineEdit->setFocus(); + + return QDialog::exec(); +} + + + +void AccessControlRulesTestDialog::accept() +{ + const auto result = + AccessControlProvider().processAccessControlRules( ui->accessingUserLineEdit->text(), + ui->accessingComputerLineEdit->text(), + ui->localUserLineEdit->text(), + ui->localComputerLineEdit->text(), + ui->connectedUsersLineEdit->text().split( QLatin1Char(',') ) ); + QString resultText; + + switch( result ) + { + case AccessControlRule::Action::Allow: + resultText = tr( "The access in the given scenario is allowed." ); + break; + case AccessControlRule::Action::Deny: + resultText = tr( "The access in the given scenario is denied." ); + break; + case AccessControlRule::Action::AskForPermission: + resultText = tr( "The access in the given scenario needs permission of the logged on user." ); + break; + default: + resultText = tr( "ERROR: Unknown action" ); + break; + } + + QMessageBox::information( this, tr( "Test result" ), resultText ); +} diff --git a/configurator/src/AccessControlRulesTestDialog.h b/configurator/src/AccessControlRulesTestDialog.h new file mode 100644 index 0000000..6ea4187 --- /dev/null +++ b/configurator/src/AccessControlRulesTestDialog.h @@ -0,0 +1,46 @@ +/* + * AccessControlRulesTestDialog.h - dialog for testing access control rules + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +namespace Ui { +class AccessControlRulesTestDialog; +} + +class AccessControlRulesTestDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AccessControlRulesTestDialog(QWidget *parent = nullptr); + ~AccessControlRulesTestDialog() override; + + int exec() override; + void accept() override; + +private: + Ui::AccessControlRulesTestDialog *ui; +}; diff --git a/configurator/src/AccessControlRulesTestDialog.ui b/configurator/src/AccessControlRulesTestDialog.ui new file mode 100644 index 0000000..2b72abf --- /dev/null +++ b/configurator/src/AccessControlRulesTestDialog.ui @@ -0,0 +1,128 @@ + + + AccessControlRulesTestDialog + + + Access control rules test + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Ok + + + + + + + + + + + + + Accessing user: + + + + + + + Local computer: + + + + + + + Accessing computer: + + + + + + + + 0 + 0 + + + + Please enter the following user and computer information in order to test the configured ruleset. + + + true + + + + + + + Local user: + + + + + + + + + + + + + Connected users: + + + + + + + + + + accessingUserLineEdit + accessingComputerLineEdit + localUserLineEdit + localComputerLineEdit + + + + + buttonBox + accepted() + AccessControlRulesTestDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AccessControlRulesTestDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/configurator/src/GeneralConfigurationPage.cpp b/configurator/src/GeneralConfigurationPage.cpp new file mode 100644 index 0000000..4adfaa9 --- /dev/null +++ b/configurator/src/GeneralConfigurationPage.cpp @@ -0,0 +1,249 @@ +/* + * GeneralConfigurationPage.cpp - configuration page with general settings + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "Configuration/UiMapping.h" +#include "GeneralConfigurationPage.h" +#include "Filesystem.h" +#include "FileSystemBrowser.h" +#include "NetworkObjectDirectoryManager.h" +#include "PasswordDialog.h" +#include "PlatformFilesystemFunctions.h" +#include "PlatformUserFunctions.h" +#include "PluginManager.h" +#include "VeyonServiceControl.h" +#include "VeyonCore.h" +#include "VeyonConfiguration.h" + +#include "ui_GeneralConfigurationPage.h" + + +GeneralConfigurationPage::GeneralConfigurationPage() : + ConfigurationPage(), + ui(new Ui::GeneralConfigurationPage) +{ + ui->setupUi(this); + + // retrieve list of builtin translations and populate language combobox + QStringList languages; + const auto qmFiles = QDir( VeyonCore::translationsDirectory() ).entryList( { QStringLiteral("veyon*.qm") } ); + + languages.reserve( qmFiles.count() ); + + for( const auto& qmFile : qmFiles ) + { + QLocale loc( qmFile.split( QLatin1Char('_') ).mid( 1 ).join( QLatin1Char('_') ) ); + if( loc.language() == QLocale::C ) + { + loc = QLocale( QLocale::English ); + } + languages += QStringLiteral( "%1 - %2 (%3)" ).arg( QLocale::languageToString( loc.language() ), + loc.nativeLanguageName(), + loc.name() ); + } + + std::sort( languages.begin(), languages.end() ); + + ui->uiLanguage->addItems( languages ); + + connect( ui->testAuthenticationButton, &QPushButton::clicked, this, &GeneralConfigurationPage::testAuthentication ); + connect( ui->openLogFileDirectory, &QPushButton::clicked, this, &GeneralConfigurationPage::openLogFileDirectory ); + connect( ui->clearLogFiles, &QPushButton::clicked, this, &GeneralConfigurationPage::clearLogFiles ); + + populateNetworkObjectDirectories(); +} + + + +GeneralConfigurationPage::~GeneralConfigurationPage() +{ + delete ui; +} + + + +void GeneralConfigurationPage::resetWidgets() +{ + FOREACH_VEYON_UI_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); + FOREACH_VEYON_LOGGING_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); + FOREACH_VEYON_NETWORK_OBJECT_DIRECTORY_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); + FOREACH_VEYON_AUTHENTICATION_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); +} + + + +void GeneralConfigurationPage::connectWidgetsToProperties() +{ + FOREACH_VEYON_UI_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY); + FOREACH_VEYON_LOGGING_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY); + FOREACH_VEYON_NETWORK_OBJECT_DIRECTORY_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY); + FOREACH_VEYON_AUTHENTICATION_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY); +} + + + +void GeneralConfigurationPage::applyConfiguration() +{ +} + + + +void GeneralConfigurationPage::testAuthentication() +{ + switch( VeyonCore::config().authenticationMethod() ) + { + case VeyonCore::AuthenticationMethod::LogonAuthentication: + if( testLogonAuthentication() == false ) + { + return; + } + break; + case VeyonCore::AuthenticationMethod::KeyFileAuthentication: + if( testKeyFileAuthentication() == false ) + { + return; + } + break; + } + + QMessageBox::information( this, authenticationTestTitle(), + tr( "Authentication is set up properly on this computer." ) ); +} + + + +bool GeneralConfigurationPage::testLogonAuthentication() +{ + return VeyonCore::instance()->initAuthentication() && + VeyonCore::platform().userFunctions().authenticate( VeyonCore::authenticationCredentials().logonUsername(), + VeyonCore::authenticationCredentials().logonPassword() ); +} + + + +bool GeneralConfigurationPage::testKeyFileAuthentication() +{ + if( VeyonCore::instance()->initAuthentication() == false ) + { + QMessageBox::critical( this, authenticationTestTitle(), + tr( "Authentication keys are not set up properly on this computer." ) ); + return false; + } + + return true; +} + + + + +void GeneralConfigurationPage::openLogFileDirectory() +{ + FileSystemBrowser( FileSystemBrowser::ExistingDirectory ). + exec( ui->logFileDirectory ); +} + + + + +void GeneralConfigurationPage::clearLogFiles() +{ + bool serviceStopped = false; + + VeyonServiceControl serviceControl( this ); + + if( serviceControl.isServiceRunning() ) + { + if( QMessageBox::question( this, tr( "%1 service" ).arg( VeyonCore::applicationName() ), + tr( "The %1 service needs to be stopped temporarily " + "in order to remove the log files. Continue?" + ).arg( VeyonCore::applicationName() ), QMessageBox::Yes | QMessageBox::No, + QMessageBox::Yes ) == QMessageBox::Yes ) + { + serviceControl.stopService(); + serviceStopped = true; + } + else + { + return; + } + } + + bool success = true; + const QStringList logFilesFilter( { QStringLiteral("Veyon*.log") } ); + + QDir d( VeyonCore::filesystem().expandPath( VeyonCore::config().logFileDirectory() ) ); + const auto localLogFiles = d.entryList( logFilesFilter ); + + for( const auto& f : localLogFiles ) + { + if( f.startsWith( QLatin1String("VeyonConfigurator") ) ) + { + d.remove( f ); + } + else + { + success &= d.remove( f ); + } + } + + d = QDir( VeyonCore::platform().filesystemFunctions().globalTempPath() ); + const auto globalLogFiles = d.entryList( logFilesFilter ); + for( const auto& f : globalLogFiles ) + { + if( f != QLatin1String("VeyonConfigurator.log") ) + { + success &= d.remove( f ); + } + } + + if( serviceStopped ) + { + serviceControl.startService(); + } + + if( success ) + { + QMessageBox::information( this, tr( "Log files cleared" ), + tr( "All log files were cleared successfully." ) ); + } + else + { + QMessageBox::critical( this, tr( "Error" ), + tr( "Could not remove all log files." ) ); + } +} + + + +void GeneralConfigurationPage::populateNetworkObjectDirectories() +{ + const auto directories = VeyonCore::networkObjectDirectoryManager().availableDirectories(); + + for( auto it = directories.constBegin(), end = directories.constEnd(); it != end; ++it ) + { + ui->networkObjectDirectoryPlugin->addItem( it.value(), it.key() ); + } +} diff --git a/configurator/src/GeneralConfigurationPage.h b/configurator/src/GeneralConfigurationPage.h new file mode 100644 index 0000000..b7e6a24 --- /dev/null +++ b/configurator/src/GeneralConfigurationPage.h @@ -0,0 +1,59 @@ +/* + * GeneralConfigurationPage.h - configuration page with general settings + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "ConfigurationPage.h" + +namespace Ui { class GeneralConfigurationPage; } + +class GeneralConfigurationPage : public ConfigurationPage +{ + Q_OBJECT +public: + GeneralConfigurationPage(); + ~GeneralConfigurationPage() override; + + void resetWidgets() override; + void connectWidgetsToProperties() override; + void applyConfiguration() override; + +private: + void testAuthentication(); + bool testLogonAuthentication(); + bool testKeyFileAuthentication(); + + void openLogFileDirectory(); + void clearLogFiles(); + + void populateNetworkObjectDirectories(); + + static QString authenticationTestTitle() + { + return tr( "Authentication test"); + } + + Ui::GeneralConfigurationPage* ui; + +} ; diff --git a/configurator/src/GeneralConfigurationPage.ui b/configurator/src/GeneralConfigurationPage.ui new file mode 100644 index 0000000..0bacbb7 --- /dev/null +++ b/configurator/src/GeneralConfigurationPage.ui @@ -0,0 +1,411 @@ + + + GeneralConfigurationPage + + + + 0 + + + 0 + + + + + User interface + + + + + + Language: + + + + + + + + 0 + 0 + + + + QComboBox::AdjustToContents + + + + Use system language setting + + + + + + + + Veyon + + + + + + + + + + Authentication + + + + + + Method: + + + + + + + + Logon authentication + + + + + Key file authentication + + + + + + + + Test + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + + + + Network object directory + + + + + + Backend: + + + + + + + + + + Update interval: + + + + + + + seconds + + + 10 + + + 3600 + + + 600 + + + + + + + + + + Logging + + + + + + + + Log file directory + + + + + + + + + + + :/core/document-open.png:/core/document-open.png + + + + + + + Log level + + + + + + + QComboBox::AdjustToContents + + + + Nothing + + + + + Only critical messages + + + + + Errors and critical messages + + + + + Warnings and errors + + + + + Information, warnings and errors + + + + + Debug messages and everything else + + + + + + + + + + + + false + + + x + + + 1 + + + 10 + + + 10 + + + + + + + false + + + Rotate log files + + + + + + + false + + + MB + + + 1 + + + 999 + + + 100 + + + + + + + Limit log file size + + + + + + + Qt::Horizontal + + + + 40 + 0 + + + + + + + + + + Log to standard error output + + + + + + + Write to logging system of operating system + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Clear all log files + + + + :/core/edit-delete.png:/core/edit-delete.png + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + applicationName + uiLanguage + authenticationMethod + testAuthenticationButton + networkObjectDirectoryPlugin + networkObjectDirectoryUpdateInterval + logFileDirectory + openLogFileDirectory + logLevel + logFileSizeLimitEnabled + logFileSizeLimit + logFileRotationEnabled + logFileRotationCount + logToStdErr + logToSystem + clearLogFiles + + + + + + + logFileSizeLimitEnabled + toggled(bool) + logFileSizeLimit + setEnabled(bool) + + + 308 + 376 + + + 408 + 377 + + + + + logFileSizeLimitEnabled + toggled(bool) + logFileRotationEnabled + setEnabled(bool) + + + 138 + 381 + + + 129 + 436 + + + + + logFileRotationEnabled + toggled(bool) + logFileRotationCount + setEnabled(bool) + + + 129 + 436 + + + 658 + 437 + + + + + diff --git a/configurator/src/MainWindow.cpp b/configurator/src/MainWindow.cpp new file mode 100644 index 0000000..7cb47be --- /dev/null +++ b/configurator/src/MainWindow.cpp @@ -0,0 +1,393 @@ +/* + * MainWindow.cpp - implementation of MainWindow class + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "Configuration/JsonStore.h" +#include "Configuration/UiMapping.h" + +#include "AboutDialog.h" +#include "ConfigurationPagePluginInterface.h" +#include "ConfigurationManager.h" +#include "VeyonConfiguration.h" +#include "MainWindow.h" +#include "PluginManager.h" + +#include "ui_MainWindow.h" + + +MainWindow::MainWindow( QWidget* parent ) : + QMainWindow( parent ), + ui( new Ui::MainWindow ), + m_configChanged( false ) +{ + ui->setupUi( this ); + + setWindowTitle( tr( "%1 Configurator %2" ).arg( VeyonCore::applicationName(), VeyonCore::version() ) ); + + loadConfigurationPagePlugins(); + + // reset all widget's values to current configuration + reset( true ); + + // if local configuration is incomplete, re-enable the apply button + if( VeyonConfiguration().data().size() < VeyonCore::config().data().size() ) + { + configurationChanged(); + } + + const auto pages = findChildren(); + for( auto page : pages ) + { + page->connectWidgetsToProperties(); + connect( page, &ConfigurationPage::widgetsChanged, this, &MainWindow::updateView ); + } + + connect( ui->buttonBox, &QDialogButtonBox::clicked, this, &MainWindow::resetOrApply ); + + connect( ui->actionLoadSettings, &QAction::triggered, this, &MainWindow::loadSettingsFromFile ); + connect( ui->actionSaveSettings, &QAction::triggered, this, &MainWindow::saveSettingsToFile ); + + auto viewModeGroup = new QActionGroup( this ); + viewModeGroup->addAction( ui->viewModeStandard ); + viewModeGroup->addAction( ui->viewModeAdvanced ); + viewModeGroup->setExclusive( true ); + ui->viewModeStandard->setChecked( true ); + + connect( viewModeGroup, &QActionGroup::triggered, this, &MainWindow::updateView ); + + connect( ui->actionAboutQt, &QAction::triggered, QApplication::instance(), &QApplication::aboutQt ); + + connect( &VeyonCore::config(), &VeyonConfiguration::configurationChanged, this, &MainWindow::configurationChanged ); + + connect( ui->configPages, &QStackedWidget::currentChanged, this, &MainWindow::updateSizes ); + + resize( ui->pageSelector->width() + ui->generalConfigurationPage->minimumSizeHint().width(), + ui->generalConfigurationPage->minimumSizeHint().height() ); + + updateView(); + + VeyonCore::enforceBranding( this ); +} + + + +MainWindow::~MainWindow() +{ + delete ui; +} + + + +void MainWindow::reset( bool onlyUI ) +{ + if( onlyUI == false ) + { + VeyonCore::config().clear(); + VeyonCore::config().reloadFromStore(); + } + + const auto pages = findChildren(); + for( auto page : pages ) + { + page->resetWidgets(); + } + + ui->buttonBox->setEnabled( false ); + m_configChanged = false; +} + + + + +void MainWindow::apply() +{ + if( applyConfiguration() ) + { + const auto pages = findChildren(); + for( auto page : pages ) + { + page->applyConfiguration(); + } + + ui->buttonBox->setEnabled( false ); + m_configChanged = false; + } +} + + + +void MainWindow::resizeEvent( QResizeEvent* event ) +{ + QMainWindow::resizeEvent( event ); + + updateSizes(); +} + + + + +void MainWindow::configurationChanged() +{ + ui->buttonBox->setEnabled( true ); + m_configChanged = true; +} + + + + +void MainWindow::resetOrApply( QAbstractButton *btn ) +{ + if( ui->buttonBox->standardButton( btn ) & QDialogButtonBox::Apply ) + { + apply(); + } + else if( ui->buttonBox->standardButton( btn ) & QDialogButtonBox::Reset ) + { + reset(); + } +} + + + +void MainWindow::loadSettingsFromFile() +{ + QString fileName = QFileDialog::getOpenFileName( this, tr( "Load settings from file" ), + QDir::homePath(), tr( "JSON files (*.json)" ) ); + if( !fileName.isEmpty() ) + { + // write current configuration to output file + Configuration::JsonStore( Configuration::JsonStore::System, fileName ).load( &VeyonCore::config() ); + reset( true ); + configurationChanged(); // give user a chance to apply possible changes + } +} + + + + +void MainWindow::saveSettingsToFile() +{ + QString fileName = QFileDialog::getSaveFileName( this, tr( "Save settings to file" ), + QDir::homePath(), tr( "JSON files (*.json)" ) ); + if( !fileName.isEmpty() ) + { + if( !fileName.endsWith( QStringLiteral(".json"), Qt::CaseInsensitive ) ) + { + fileName += QStringLiteral(".json"); + } + + bool configChangedPrevious = m_configChanged; + + // write current configuration to output file + Configuration::JsonStore( Configuration::JsonStore::System, fileName ).flush( &VeyonCore::config() ); + + m_configChanged = configChangedPrevious; + ui->buttonBox->setEnabled( m_configChanged ); + } +} + + + +void MainWindow::resetConfiguration() +{ + if( QMessageBox::warning( this, tr( "Reset configuration" ), + tr( "Do you really want to reset the local configuration and revert " + "all settings to their defaults?" ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == + QMessageBox::Yes ) + { + ConfigurationManager().clearConfiguration(); + reset( false ); + } +} + + + +void MainWindow::aboutVeyon() +{ + AboutDialog( this ).exec(); +} + + + +void MainWindow::updateSizes() +{ + ui->configPages->setMinimumSize( ui->scrollArea->width() - ui->scrollArea->verticalScrollBar()->width(), + ui->configPages->currentWidget()->minimumSizeHint().height() ); +} + + + +void MainWindow::updateView() +{ + if( ui->viewModeAdvanced->isChecked() ) + { + switchToAdvancedView(); + } + else + { + switchToStandardView(); + } + + QTimer::singleShot( 0, this, &MainWindow::updateSizes ); +} + + + +void MainWindow::switchToStandardView() +{ + const auto widgets = findChildren(); + + // hide widgets with advanced property flag + for( auto widget : widgets ) + { + const auto flags = Configuration::UiMapping::flags( widget ); + if( flags & Configuration::Property::Flag::Hidden ) + { + widget->hide(); + } + else if( flags & Configuration::Property::Flag::Standard ) + { + widget->show(); + } + else if( flags & Configuration::Property::Flag::Advanced ) + { + widget->hide(); + } + } + + // hide page selector items for advanced pages + for( int i = 0; i < ui->pageSelector->count(); ++i ) + { + auto item = ui->pageSelector->item( i ); + auto page = item->data( Qt::UserRole ).value(); + if( page ) + { + const auto flags = Configuration::UiMapping::flags( page ); + + ui->pageSelector->setRowHidden( i, flags & Configuration::Property::Flag::Advanced ); + ui->configPages->widget( i )->setVisible( i == ui->pageSelector->currentRow() ); + if( flags & Configuration::Property::Flag::Advanced && + i == ui->pageSelector->currentRow() ) + { + ui->pageSelector->setCurrentRow( 0 ); + } + } + } +} + + + +void MainWindow::switchToAdvancedView() +{ + const auto widgets = findChildren(); + for( auto widget : widgets ) + { + const auto flags = Configuration::UiMapping::flags( widget ); + if( flags & Configuration::Property::Flag::Advanced ) + { + widget->show(); + } + } + + for( int i = 0; i < ui->pageSelector->count(); ++i ) + { + ui->pageSelector->setRowHidden( i, false ); + ui->configPages->widget( i )->setVisible( i == ui->pageSelector->currentRow() ); + } +} + + + +bool MainWindow::applyConfiguration() +{ + ConfigurationManager configurationManager; + + if( configurationManager.saveConfiguration() == false || + configurationManager.applyConfiguration() == false ) + { + vCritical() << configurationManager.errorString().toUtf8().constData(); + + QMessageBox::critical( nullptr, + tr( "%1 Configurator" ).arg( VeyonCore::applicationName() ), + configurationManager.errorString() ); + return false; + } + + return true; +} + + + +void MainWindow::loadConfigurationPagePlugins() +{ + for( auto pluginObject : qAsConst( VeyonCore::pluginManager().pluginObjects() ) ) + { + auto pluginInterface = qobject_cast( pluginObject ); + auto configurationPagePluginInterface = qobject_cast( pluginObject ); + + if( pluginInterface && configurationPagePluginInterface ) + { + auto page = configurationPagePluginInterface->createConfigurationPage(); + if( page ) + { + ui->configPages->addWidget( page ); + + auto item = new QListWidgetItem( page->windowIcon(), page->windowTitle() ); + item->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); + item->setData( Qt::UserRole, QVariant::fromValue( page ) ); + ui->pageSelector->addItem( item ); + } + } + } + + // adjust minimum size + ui->pageSelector->setMinimumSize( ui->pageSelector->sizeHintForColumn(0) + 3 * ui->pageSelector->spacing(), + ui->pageSelector->minimumHeight() ); +} + + + +void MainWindow::closeEvent( QCloseEvent *closeEvent ) +{ + if( m_configChanged && + QMessageBox::question( this, tr( "Unsaved settings" ), + tr( "There are unsaved settings. " + "Quit anyway?" ), + QMessageBox::Yes | QMessageBox::No ) != + QMessageBox::Yes ) + { + closeEvent->ignore(); + return; + } + + closeEvent->accept(); + QMainWindow::closeEvent( closeEvent ); +} diff --git a/configurator/src/MainWindow.h b/configurator/src/MainWindow.h new file mode 100644 index 0000000..97f20d6 --- /dev/null +++ b/configurator/src/MainWindow.h @@ -0,0 +1,71 @@ +/* + * MainWindow.h - main window of the Veyon Configurator + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +class QAbstractButton; + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MainWindow( QWidget* parent = nullptr ); + ~MainWindow() override; + + void reset( bool onlyUI = false ); + void apply(); + +protected: + void resizeEvent( QResizeEvent* event ) override; + +private Q_SLOTS: + void configurationChanged(); + void resetOrApply( QAbstractButton *btn ); + void loadSettingsFromFile(); + void saveSettingsToFile(); + void resetConfiguration(); + void aboutVeyon(); + +private: + void updateSizes(); + void updateView(); + + void switchToStandardView(); + void switchToAdvancedView(); + + bool applyConfiguration(); + void loadConfigurationPagePlugins(); + + void closeEvent( QCloseEvent *closeEvent ) override; + + Ui::MainWindow *ui; + bool m_configChanged; + +} ; diff --git a/configurator/src/MainWindow.ui b/configurator/src/MainWindow.ui new file mode 100644 index 0000000..792c323 --- /dev/null +++ b/configurator/src/MainWindow.ui @@ -0,0 +1,357 @@ + + + MainWindow + + + Veyon Configurator + + + + :/configurator/veyon-configurator.png:/configurator/veyon-configurator.png + + + + + + + Qt::ScrollBarAsNeeded + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + + 48 + 48 + + + + Qt::ElideNone + + + 4 + + + QListView::ListMode + + + true + + + 0 + + + + General + + + + :/configurator/configure-shortcuts.png:/configurator/configure-shortcuts.png + + + ItemIsSelectable|ItemIsUserCheckable|ItemIsEnabled + + + + + Service + + + + :/configurator/application-x-sharedlib.png:/configurator/application-x-sharedlib.png + + + ItemIsSelectable|ItemIsUserCheckable|ItemIsEnabled + + + + + Master + + + + :/configurator/application-x-ms-dos-executable.png:/configurator/application-x-ms-dos-executable.png + + + ItemIsSelectable|ItemIsUserCheckable|ItemIsEnabled + + + + + Access control + + + + :/configurator/network-vpn.png:/configurator/network-vpn.png + + + ItemIsSelectable|ItemIsUserCheckable|ItemIsEnabled + + + + + + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + + + 10 + + + 0 + + + 10 + + + + + 0 + + + + + + + + + + + + + + + Qt::Horizontal + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Reset + + + true + + + + + + + + + &File + + + + + + + + + + + &Help + + + + + + + &View + + + + + + + + + + + &Quit + + + Ctrl+Q + + + + + + :/core/document-save.png:/core/document-save.png + + + &Save settings to file + + + Save settings to file + + + Ctrl+S + + + + + + :/core/document-open.png:/core/document-open.png + + + L&oad settings from file + + + Ctrl+O + + + + + + :/core/help-about.png:/core/help-about.png + + + About Veyon + + + + + About Qt + + + + + + :/core/edit-delete.png:/core/edit-delete.png + + + Reset configuration + + + + + true + + + &Standard + + + + + true + + + &Advanced + + + + + + AccessControlPage + QWidget +
AccessControlPage.h
+ 1 +
+ + GeneralConfigurationPage + QWidget +
GeneralConfigurationPage.h
+ 1 +
+ + MasterConfigurationPage + QWidget +
MasterConfigurationPage.h
+ 1 +
+ + ServiceConfigurationPage + QWidget +
ServiceConfigurationPage.h
+ 1 +
+
+ + + + + + + pageSelector + currentRowChanged(int) + configPages + setCurrentIndex(int) + + + 108 + 301 + + + 502 + 301 + + + + + actionAboutVeyon + triggered() + MainWindow + aboutVeyon() + + + -1 + -1 + + + 357 + 282 + + + + + actionQuit + triggered() + MainWindow + close() + + + -1 + -1 + + + 357 + 282 + + + + + actionResetConfiguration + triggered() + MainWindow + resetConfiguration() + + + -1 + -1 + + + 680 + 426 + + + + + + aboutVeyon() + resetConfiguration() + +
diff --git a/configurator/src/MasterConfigurationPage.cpp b/configurator/src/MasterConfigurationPage.cpp new file mode 100644 index 0000000..a3886dc --- /dev/null +++ b/configurator/src/MasterConfigurationPage.cpp @@ -0,0 +1,188 @@ +/* + * MasterConfigurationPage.cpp - page for configuring master application + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "BuiltinFeatures.h" +#include "PluginManager.h" +#include "FeatureManager.h" +#include "FileSystemBrowser.h" +#include "MonitoringMode.h" +#include "VeyonCore.h" +#include "VeyonConfiguration.h" +#include "MasterConfigurationPage.h" +#include "Configuration/UiMapping.h" + +#include "ui_MasterConfigurationPage.h" + + +MasterConfigurationPage::MasterConfigurationPage() : + ConfigurationPage(), + ui(new Ui::MasterConfigurationPage), + m_featureManager() +{ + ui->setupUi(this); + + connect( ui->openUserConfigurationDirectory, &QPushButton::clicked, + this, &MasterConfigurationPage::openUserConfigurationDirectory ); + + connect( ui->openScreenshotDirectory, &QPushButton::clicked, + this, &MasterConfigurationPage::openScreenshotDirectory ); + + populateFeatureComboBox(); +} + + + +MasterConfigurationPage::~MasterConfigurationPage() +{ + delete ui; +} + + + +void MasterConfigurationPage::resetWidgets() +{ + FOREACH_VEYON_DIRECTORIES_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); + FOREACH_VEYON_MASTER_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); + + m_disabledFeatures = VeyonCore::config().disabledFeatures(); + + updateFeatureLists(); +} + + + +void MasterConfigurationPage::connectWidgetsToProperties() +{ + FOREACH_VEYON_DIRECTORIES_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY); + FOREACH_VEYON_MASTER_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY); +} + + + +void MasterConfigurationPage::applyConfiguration() +{ +} + + + +void MasterConfigurationPage::enableFeature() +{ + const auto items = ui->disabledFeaturesListWidget->selectedItems(); + + for( auto item : items ) + { + m_disabledFeatures.removeAll( item->data( Qt::UserRole ).toString() ); + } + + VeyonCore::config().setDisabledFeatures( m_disabledFeatures ); + + updateFeatureLists(); +} + + + +void MasterConfigurationPage::disableFeature() +{ + const auto items = ui->allFeaturesListWidget->selectedItems(); + + for( auto item : items ) + { + QString featureUid = item->data( Qt::UserRole ).toString(); + m_disabledFeatures.removeAll( featureUid ); + m_disabledFeatures.append( featureUid ); + } + + VeyonCore::config().setDisabledFeatures( m_disabledFeatures ); + + updateFeatureLists(); +} + + + +void MasterConfigurationPage::openUserConfigurationDirectory() +{ + FileSystemBrowser( FileSystemBrowser::ExistingFile ).exec( ui->userConfigurationDirectory ); +} + + + +void MasterConfigurationPage::openScreenshotDirectory() +{ + FileSystemBrowser( FileSystemBrowser::ExistingDirectory ).exec( ui->screenshotDirectory ); +} + + + +void MasterConfigurationPage::populateFeatureComboBox() +{ + ui->computerDoubleClickFeature->addItem( QIcon(), tr( "" ), QUuid() ); + ui->computerDoubleClickFeature->insertSeparator( ui->computerDoubleClickFeature->count() ); + + for( const auto& feature : m_featureManager.features() ) + { + if( feature.testFlag( Feature::Master ) && + feature.testFlag( Feature::Internal ) == false ) + { + ui->computerDoubleClickFeature->addItem( QIcon( feature.iconUrl() ), + feature.displayName(), + feature.uid() ); + } + } +} + + + +void MasterConfigurationPage::updateFeatureLists() +{ + ui->allFeaturesListWidget->setUpdatesEnabled( false ); + ui->disabledFeaturesListWidget->setUpdatesEnabled( false ); + + ui->allFeaturesListWidget->clear(); + ui->disabledFeaturesListWidget->clear(); + + for( const auto& feature : qAsConst( m_featureManager.features() ) ) + { + if( feature.testFlag( Feature::Master ) == false || + feature.testFlag( Feature::Internal ) || + feature == VeyonCore::builtinFeatures().monitoringMode().feature() ) + { + continue; + } + + QListWidgetItem* item = new QListWidgetItem( QIcon( feature.iconUrl() ), feature.displayName() ); + item->setData( Qt::UserRole, feature.uid().toString() ); + + if( m_disabledFeatures.contains( feature.uid().toString() ) ) + { + ui->disabledFeaturesListWidget->addItem( item ); + } + else + { + ui->allFeaturesListWidget->addItem( item ); + } + } + + ui->allFeaturesListWidget->setUpdatesEnabled( true ); + ui->disabledFeaturesListWidget->setUpdatesEnabled( true ); +} diff --git a/configurator/src/MasterConfigurationPage.h b/configurator/src/MasterConfigurationPage.h new file mode 100644 index 0000000..19fd36e --- /dev/null +++ b/configurator/src/MasterConfigurationPage.h @@ -0,0 +1,62 @@ +/* + * MasterConfigurationPage.h - header for the MasterConfigurationPage class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "ConfigurationPage.h" +#include "FeatureManager.h" + +class BuiltinFeatures; + +namespace Ui { +class MasterConfigurationPage; +} + +class MasterConfigurationPage : public ConfigurationPage +{ + Q_OBJECT +public: + MasterConfigurationPage(); + ~MasterConfigurationPage() override; + + void resetWidgets() override; + void connectWidgetsToProperties() override; + void applyConfiguration() override; + +private Q_SLOTS: + void enableFeature(); + void disableFeature(); + +private: + void openUserConfigurationDirectory(); + void openScreenshotDirectory(); + void populateFeatureComboBox(); + void updateFeatureLists(); + + Ui::MasterConfigurationPage *ui; + + FeatureManager m_featureManager; + QStringList m_disabledFeatures; + +}; diff --git a/configurator/src/MasterConfigurationPage.ui b/configurator/src/MasterConfigurationPage.ui new file mode 100644 index 0000000..2c42608 --- /dev/null +++ b/configurator/src/MasterConfigurationPage.ui @@ -0,0 +1,613 @@ + + + MasterConfigurationPage + + + + 0 + + + 0 + + + + + 0 + + + + Basic settings + + + + + + Directories + + + + 10 + + + 10 + + + + + + :/core/document-open.png:/core/document-open.png + + + + + + + User configuration + + + + + + + + + + Screenshots + + + + + + + + + + + :/core/document-open.png:/core/document-open.png + + + + + + + + + + User interface + + + + + + Text color + + + + + + + + + + px + + + + + + 2 + + + 50 + + + 16 + + + + + + + + + + Thumbnail spacing + + + + + + + + User and computer name + + + + + Only user name + + + + + Only computer name + + + + + + + + + Computer and user name + + + + + Only user name + + + + + Only computer name + + + + + + + + Sort order + + + + + + + + Auto + + + + + 16:9 + + + + + 16:10 + + + + + 3:2 + + + + + 4:3 + + + + + + + + ms + + + 250 + + + 10000 + + + 250 + + + 1000 + + + + + + + Computer thumbnail caption + + + + + + + Thumbnail update interval + + + + + + + Background color + + + + + + + Thumbnail aspect ratio + + + + + + + + + + Qt::Vertical + + + + 20 + 10 + + + + + + + + + Behaviour + + + + + + Program start + + + + + + Perform access control + + + + + + + Automatically select current location + + + + + + + Automatically adjust computer icon size + + + + + + + Automatically open computer select panel + + + + + + + + + + Computer locations + + + + + + Show current location only + + + + + + + false + + + Allow adding hidden locations manually + + + + + + + Hide local computer + + + + + + + Hide local session + + + + + + + Hide empty locations + + + + + + + Hide computer filter field + + + + + + + + + + Modes and features + + + + + + Enforce selected mode for client computers + + + + + + + Actions such as rebooting or powering down computers + + + Show confirmation dialog for potentially unsafe actions + + + + + + + + + + Feature on computer double click: + + + + + + + Open feature windows on the same screen as the main window + + + + + + + + + + Qt::Vertical + + + + 20 + 10 + + + + + + + + + Features + + + + + + + + All features + + + + + + + Disabled features + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + :/core/go-next.png:/core/go-next.png + + + + + + + + :/core/go-previous.png:/core/go-previous.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + + tabWidget + userConfigurationDirectory + screenshotDirectory + openUserConfigurationDirectory + openScreenshotDirectory + computerMonitoringUpdateInterval + computerMonitoringAspectRatio + computerMonitoringBackgroundColor + computerMonitoringTextColor + computerDisplayRoleContent + computerMonitoringSortOrder + computerMonitoringThumbnailSpacing + accessControlForMasterEnabled + autoSelectCurrentLocation + autoAdjustMonitoringIconSize + autoOpenComputerSelectPanel + showCurrentLocationOnly + allowAddingHiddenLocations + hideLocalComputer + hideOwnSession + hideEmptyLocations + hideComputerFilter + enforceSelectedModeForClients + confirmUnsafeActions + showFeatureWindowsOnSameScreen + computerDoubleClickFeature + allFeaturesListWidget + disableFeatureButton + enableFeatureButton + disabledFeaturesListWidget + + + + + + + showCurrentLocationOnly + toggled(bool) + allowAddingHiddenLocations + setEnabled(bool) + + + 380 + 343 + + + 380 + 389 + + + + + allFeaturesListWidget + doubleClicked(QModelIndex) + MasterConfigurationPage + disableFeature() + + + 224 + 682 + + + 444 + 413 + + + + + disabledFeaturesListWidget + doubleClicked(QModelIndex) + MasterConfigurationPage + enableFeature() + + + 664 + 682 + + + 444 + 413 + + + + + disableFeatureButton + clicked() + MasterConfigurationPage + disableFeature() + + + 461 + 723 + + + 444 + 413 + + + + + enableFeatureButton + clicked() + MasterConfigurationPage + enableFeature() + + + 461 + 766 + + + 444 + 413 + + + + + + enableFeature() + disableFeature() + + diff --git a/configurator/src/ServiceConfigurationPage.cpp b/configurator/src/ServiceConfigurationPage.cpp new file mode 100644 index 0000000..8c64dd1 --- /dev/null +++ b/configurator/src/ServiceConfigurationPage.cpp @@ -0,0 +1,168 @@ +/* + * ServiceConfigurationPage.cpp - page for configuring service application + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "FileSystemBrowser.h" +#include "VeyonConfiguration.h" +#include "PluginManager.h" +#include "ServiceConfigurationPage.h" +#include "VeyonServiceControl.h" +#include "VncServerPluginInterface.h" +#include "Configuration/UiMapping.h" + +#include "ui_ServiceConfigurationPage.h" + + +ServiceConfigurationPage::ServiceConfigurationPage() : + ConfigurationPage(), + ui(new Ui::ServiceConfigurationPage), + m_vncServerPluginConfigurationWidget( nullptr ) +{ + ui->setupUi(this); + + Configuration::UiMapping::setFlags( ui->networkPortNumbersGroupBox, Configuration::Property::Flag::Advanced ); + Configuration::UiMapping::setFlags( ui->miscNetworkSettingsGroupBox, Configuration::Property::Flag::Advanced ); + + updateServiceControl(); + populateVncServerPluginComboBox(); + + auto serviceUpdateTimer = new QTimer( this ); + serviceUpdateTimer->start( 2000 ); + + connect( serviceUpdateTimer, &QTimer::timeout, this, &ServiceConfigurationPage::updateServiceControl ); +} + + + +ServiceConfigurationPage::~ServiceConfigurationPage() +{ + delete ui; +} + + + +void ServiceConfigurationPage::resetWidgets() +{ + FOREACH_VEYON_SERVICE_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); + FOREACH_VEYON_NETWORK_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); + FOREACH_VEYON_VNC_SERVER_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); +} + + + +void ServiceConfigurationPage::connectWidgetsToProperties() +{ + FOREACH_VEYON_SERVICE_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY); + FOREACH_VEYON_NETWORK_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY); + FOREACH_VEYON_VNC_SERVER_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY); +} + + +void ServiceConfigurationPage::applyConfiguration() +{ + VeyonServiceControl serviceControl( this ); + + if( serviceControl.isServiceRunning() && + QMessageBox::question( this, tr( "Restart %1 Service" ).arg( VeyonCore::applicationName() ), + tr( "All settings were saved successfully. In order to take " + "effect the %1 service needs to be restarted. " + "Restart it now?" ).arg( VeyonCore::applicationName() ), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes ) == QMessageBox::Yes ) + { + serviceControl.stopService(); + serviceControl.startService(); + } +} + + + +void ServiceConfigurationPage::startService() +{ + VeyonServiceControl( this ).startService(); + + updateServiceControl(); +} + + + +void ServiceConfigurationPage::stopService() +{ + VeyonServiceControl( this ).stopService(); + + updateServiceControl(); +} + + + +void ServiceConfigurationPage::updateServiceControl() +{ + bool running = VeyonServiceControl( this ).isServiceRunning(); + + ui->startService->setEnabled( !running ); + ui->stopService->setEnabled( running ); + ui->serviceState->setText( running ? tr( "Running" ) : tr( "Stopped" ) ); +} + + + +void ServiceConfigurationPage::updateVncServerPluginConfigurationWidget() +{ + delete m_vncServerPluginConfigurationWidget; + m_vncServerPluginConfigurationWidget = nullptr; + + auto pluginUid = ui->vncServerPlugin->currentData().toUuid(); + + auto vncServerPluginInterface = m_vncServerPluginInterfaces.value( pluginUid ); + + if( vncServerPluginInterface ) + { + m_vncServerPluginConfigurationWidget = vncServerPluginInterface->configurationWidget(); + + if( m_vncServerPluginConfigurationWidget ) + { + ui->vncServerGroupBoxLayout->addWidget( m_vncServerPluginConfigurationWidget ); + } + } + + Q_EMIT widgetsChanged(); +} + + + +void ServiceConfigurationPage::populateVncServerPluginComboBox() +{ + for( auto pluginObject : qAsConst( VeyonCore::pluginManager().pluginObjects() ) ) + { + auto pluginInterface = qobject_cast( pluginObject ); + auto vncServerPluginInterface = qobject_cast( pluginObject ); + + if( pluginInterface && vncServerPluginInterface ) + { + m_vncServerPluginInterfaces[pluginInterface->uid()] = vncServerPluginInterface; + ui->vncServerPlugin->addItem( pluginInterface->description(), pluginInterface->uid() ); + } + } +} diff --git a/configurator/src/ServiceConfigurationPage.h b/configurator/src/ServiceConfigurationPage.h new file mode 100644 index 0000000..a4a6b87 --- /dev/null +++ b/configurator/src/ServiceConfigurationPage.h @@ -0,0 +1,66 @@ +/* + * ServiceConfigurationPage.h - header for the ServiceConfigurationPage class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "ConfigurationPage.h" +#include "Plugin.h" + +namespace Ui { +class ServiceConfigurationPage; +} + +class VncServerPluginInterface; + +class ServiceConfigurationPage : public ConfigurationPage +{ + Q_OBJECT +public: + ServiceConfigurationPage(); + ~ServiceConfigurationPage() override; + + void resetWidgets() override; + void connectWidgetsToProperties() override; + void applyConfiguration() override; + +public Q_SLOTS: + void startService(); + void stopService(); + + +private Q_SLOTS: + void updateServiceControl(); + void updateVncServerPluginConfigurationWidget(); + +private: + void populateVncServerPluginComboBox(); + + Ui::ServiceConfigurationPage *ui; + + QMap m_vncServerPluginInterfaces; + QWidget* m_vncServerPluginConfigurationWidget; + +}; diff --git a/configurator/src/ServiceConfigurationPage.ui b/configurator/src/ServiceConfigurationPage.ui new file mode 100644 index 0000000..113a227 --- /dev/null +++ b/configurator/src/ServiceConfigurationPage.ui @@ -0,0 +1,424 @@ + + + ServiceConfigurationPage + + + Form + + + + 0 + + + 0 + + + + + General + + + + + + Show notification when an unauthorized access is blocked + + + + + + + Show notification on remote connection + + + + + + + Hide tray icon + + + + + + + Autostart + + + + + + + + + State: + + + + + + + + 75 + true + + + + Stopped + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Start service + + + + :/configurator/media-playback-start.png:/configurator/media-playback-start.png + + + + + + + Stop service + + + + :/configurator/media-playback-stop.png:/configurator/media-playback-stop.png + + + + + + + + + + + + Sessions + + + + + + Single session mode (create server instance for local/physical session only) + + + true + + + sessionModeButtonGroup + + + + + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + Multi session mode (create server instance for each local and remote desktop session) + + + sessionModeButtonGroup + + + + + + + + + Maximum session count + + + + + + + false + + + 1 + + + 1000 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Network port numbers + + + + + + 1024 + + + 65535 + + + + + + + 1024 + + + 65535 + + + + + + + 1024 + + + 65535 + + + + + + + Veyon server + + + + + + + Internal VNC server + + + + + + + Feature manager + + + + + + + Demo server + + + + + + + 1024 + + + 65535 + + + + + + + + + + Miscellaneous network settings + + + + + + Enable firewall exception + + + + + + + Allow connections from localhost only + + + + + + + + + + VNC server + + + + + + + + Plugin: + + + + + + + + 0 + 0 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + isTrayIconHidden + failedAuthenticationNotificationsEnabled + remoteConnectionNotificationsEnabled + autostartService + startService + stopService + singleSessionModeEnabled + multiSessionModeEnabled + maximumSessionCount + veyonServerPort + vncServerPort + featureWorkerManagerPort + demoServerPort + isFirewallExceptionEnabled + localConnectOnly + vncServerPlugin + + + + + + + vncServerPlugin + currentIndexChanged(int) + ServiceConfigurationPage + updateVncServerPluginConfigurationWidget() + + + 381 + 703 + + + 340 + 484 + + + + + startService + clicked() + ServiceConfigurationPage + startService() + + + 230 + 118 + + + 207 + 234 + + + + + stopService + clicked() + ServiceConfigurationPage + stopService() + + + 342 + 118 + + + 207 + 234 + + + + + multiSessionModeEnabled + toggled(bool) + maximumSessionCount + setEnabled(bool) + + + 320 + 371 + + + 295 + 415 + + + + + + updateVncServerPluginConfigurationWidget() + startService() + stopService() + + + + + diff --git a/configurator/src/main.cpp b/configurator/src/main.cpp new file mode 100644 index 0000000..db2f490 --- /dev/null +++ b/configurator/src/main.cpp @@ -0,0 +1,78 @@ +/* + * main.cpp - main file for Veyon Configurator + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "VeyonConfiguration.h" +#include "VeyonCore.h" +#include "MainWindow.h" +#include "PlatformCoreFunctions.h" +#include "Logger.h" + + + +int main( int argc, char **argv ) +{ + VeyonCore::setupApplicationParameters(); + + QApplication app( argc, argv ); + + VeyonCore core( &app, VeyonCore::Component::Configurator, QStringLiteral("Configurator") ); + + // make sure to run as admin + if( qEnvironmentVariableIntValue( "VEYON_CONFIGURATOR_NO_ELEVATION" ) == 0 && + VeyonCore::platform().coreFunctions().isRunningAsAdmin() == false && + app.arguments().size() <= 1 ) + { + if( VeyonCore::platform().coreFunctions().runProgramAsAdmin( QCoreApplication::applicationFilePath(), { + QStringLiteral("-elevated") } ) ) + { + return 0; + } + + QMessageBox::warning( nullptr, MainWindow::tr( "Insufficient privileges" ), + MainWindow::tr( "Could not start with administrative privileges. " + "Please make sure a sudo-like program is installed " + "for your desktop environment! The program will " + "be run with normal user privileges.") ); + } + + if( VeyonConfiguration().isStoreWritable() == false && + VeyonCore::config().logLevel() != Logger::LogLevel::Debug ) + { + QMessageBox::critical( nullptr, + MainWindow::tr( "Configuration not writable" ), + MainWindow::tr( "The local configuration backend reported that the " + "configuration is not writable! Please run the %1 " + "Configurator with higher privileges." ).arg( VeyonCore::applicationName() ) ); + return -1; + } + + // now create the main window + auto mainWindow = new MainWindow; + mainWindow->show(); + + return core.exec(); +} diff --git a/configurator/veyon-configurator.1 b/configurator/veyon-configurator.1 new file mode 100644 index 0000000..51aa9cf --- /dev/null +++ b/configurator/veyon-configurator.1 @@ -0,0 +1,24 @@ +.TH VEYON-CONFIGURATOR 1 2018-12-07 Veyon +.SH NAME +veyon-configurator \- Veyon Configurator +.SH SYNOPSIS +\fBveyon-configurator\fP +.SH DESCRIPTION + +\fBVEYON-CONFIGURATOR\fR is a configuration tool which allows the site +admin to configure and customize all components of a local Veyon +installation through a graphical user interface. The program is started +by the administrator with elevated privileges whenever necessary. + +.PP +For more information about the veyon-configurator, point your browser to file:///usr/share/doc/packages/veyon-configurator or https://veyon.io/. +.SH SEE ALSO +.IR veyon-service (1), veyon-master (1) + +.PP +.IR https://veyon.io/ + +.SH AUTHOR +Veyon has been written by Tobias Junghans. +.PP +This manual page has been written by Frank Schütte and updated by Tobias Junghans. diff --git a/configurator/veyon-configurator.rc.in b/configurator/veyon-configurator.rc.in new file mode 100644 index 0000000..ae6df6c --- /dev/null +++ b/configurator/veyon-configurator.rc.in @@ -0,0 +1,32 @@ +veyonconfiguratoricon ICON data/veyon-configurator.ico +#include + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST data/veyon-configurator.exe.manifest + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@,@VERSION_BUILD@ + FILEFLAGSMASK 0 + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, charset = Windows, Multilingual + BEGIN + VALUE "Comments", "Virtual Eye On Networks (https://veyon.io)\0" + VALUE "CompanyName", "Veyon Solutions\0" + VALUE "ProductName", "Veyon\0" + VALUE "ProductVersion", "@VERSION_STRING@\0" + VALUE "FileDescription", "Veyon Configurator\0" + VALUE "FileVersion", "@VERSION_STRING@\0" + VALUE "LegalCopyright", "Copyright (c) 2017-2021 Veyon Solutions / Tobias Junghans\0" + VALUE "OriginalFilename", "veyon-configurator.exe\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04E4 + END +END diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt new file mode 100644 index 0000000..d8d4425 --- /dev/null +++ b/core/CMakeLists.txt @@ -0,0 +1,99 @@ +FILE(GLOB veyoncore_INCLUDES + ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/Configuration/*.h +) +FILE(GLOB veyoncore_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/*.ui + ${CMAKE_CURRENT_SOURCE_DIR}/src/Configuration/*.cpp +) + +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/builddata.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/builddata.qrc) +SET(core_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/resources/core.qrc ${CMAKE_CURRENT_BINARY_DIR}/builddata.qrc) + +if(NOT LibVNCClient_FOUND) + set(libvncclient_SOURCES + ${libvncserver_DIR}/libvncclient/cursor.c + ${libvncserver_DIR}/libvncclient/listen.c + ${libvncserver_DIR}/libvncclient/rfbproto.c + ${libvncserver_DIR}/libvncclient/sockets.c + ${libvncserver_DIR}/libvncclient/tls_openssl.c + ${libvncserver_DIR}/libvncclient/vncviewer.c + ${libvncserver_DIR}/common/crypto_openssl.c + ${libvncserver_DIR}/common/turbojpeg.c) + + set_source_files_properties(${libvncclient_SOURCES} PROPERTIES COMPILE_FLAGS "-Wno-unused-function -Wno-unused-variable -fvisibility=default") +endif() + +ADD_LIBRARY(veyon-core SHARED ${veyoncore_SOURCES} ${veyoncore_INCLUDES} ${core_RESOURCES} ${libvncclient_SOURCES}) +target_compile_definitions(veyon-core PRIVATE XK_KOREAN BUILD_VEYON_CORE_LIBRARY) + +# find Qt's translation directory +if(NOT VEYON_BUILD_WIN32) + include(FindQtTranslations) + find_qt_translations() + target_compile_definitions(veyon-core PRIVATE QT_TRANSLATIONS_DIR="${QT_TRANSLATIONS_DIR}") +endif() + +set_default_target_properties(veyon-core) +target_compile_options(veyon-core PRIVATE -Wno-parentheses) + +target_include_directories(veyon-core PUBLIC + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include + ${QCA_INCLUDE_DIR} +) + +TARGET_LINK_LIBRARIES(veyon-core + Qt5::Concurrent + Qt5::Gui + Qt5::Network + Qt5::Widgets + OpenSSL::SSL + ${VEYON_DEBUG_LIBRARIES} + ${QCA_LIBRARY} + ) + +if(LibVNCClient_FOUND) + target_link_libraries(veyon-core LibVNC::LibVNCClient) +else() + target_include_directories(veyon-core PRIVATE + ${ZLIB_INCLUDE_DIR} + ${JPEG_INCLUDE_DIR} + ${LZO_INCLUDE_DIR} + ) + target_include_directories(veyon-core PUBLIC + ${libvncserver_DIR}/common/ + ${libvncserver_DIR} + ) + target_link_libraries(veyon-core + Threads::Threads + PNG::PNG + ${ZLIB_LIBRARIES} + ${JPEG_LIBRARIES} + ${LZO_LIBRARIES} + ) +endif() + +IF(VEYON_BUILD_WIN32) + # add Windows Socket library required by libvncclient + TARGET_LINK_LIBRARIES(veyon-core -lws2_32) + SET_TARGET_PROPERTIES(veyon-core PROPERTIES PREFIX "") + if(NOT WITH_CORE_ONLY) + INSTALL(TARGETS veyon-core RUNTIME DESTINATION ${VEYON_LIB_DIR}) + endif() +ELSE() + if(NOT WITH_CORE_ONLY) + INSTALL(TARGETS veyon-core LIBRARY DESTINATION ${VEYON_LIB_DIR}) + endif() +ENDIF(VEYON_BUILD_WIN32) + +if(WITH_PCH) + target_precompile_headers(veyon-core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/PrecompiledHeader.h") + + add_library(veyon-pch STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/PrecompiledHeader.h") + target_link_libraries(veyon-pch Qt5::Core Qt5::Concurrent Qt5::Network Qt5::Widgets) + target_precompile_headers(veyon-pch PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src/PrecompiledHeader.h") +endif() diff --git a/core/builddata.qrc.in b/core/builddata.qrc.in new file mode 100644 index 0000000..ce4e9d7 --- /dev/null +++ b/core/builddata.qrc.in @@ -0,0 +1,5 @@ + + + @CONTRIBUTORS@ + + diff --git a/core/include/AboutDialog.h b/core/include/AboutDialog.h new file mode 100644 index 0000000..1925561 --- /dev/null +++ b/core/include/AboutDialog.h @@ -0,0 +1,48 @@ +/* + * AboutDialog.h - declaration of AboutDialog class + * + * Copyright (c) 2011-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "VeyonCore.h" + +namespace Ui +{ + class AboutDialog; +} + +class VEYON_CORE_EXPORT AboutDialog : public QDialog +{ + Q_OBJECT +public: + explicit AboutDialog( QWidget *parent ); + ~AboutDialog() override; + +private Q_SLOTS: + void openDonationWebsite(); + +private: + Ui::AboutDialog *ui; +} ; diff --git a/core/include/AccessControlProvider.h b/core/include/AccessControlProvider.h new file mode 100644 index 0000000..3be3fcd --- /dev/null +++ b/core/include/AccessControlProvider.h @@ -0,0 +1,86 @@ +/* + * AccessControlProvider.h - declaration of class AccessControlProvider + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "AccessControlRule.h" +#include "NetworkObject.h" + +class UserGroupsBackendInterface; +class NetworkObjectDirectory; + +class VEYON_CORE_EXPORT AccessControlProvider +{ +public: + enum class Access { + Deny, + Allow, + ToBeConfirmed, + } ; + + AccessControlProvider(); + + QStringList userGroups() const; + QStringList locations() const; + QStringList locationsOfComputer( const QString& computer ) const; + + Access checkAccess( const QString& accessingUser, const QString& accessingComputer, + const QStringList& connectedUsers ); + + bool processAuthorizedGroups( const QString& accessingUser ); + + AccessControlRule::Action processAccessControlRules( const QString& accessingUser, + const QString& accessingComputer, + const QString& localUser, + const QString& localComputer, + const QStringList& connectedUsers ); + + bool isAccessToLocalComputerDenied() const; + +private: + bool isMemberOfUserGroup( const QString& user, const QString& groupName ) const; + bool isLocatedAt( const QString& computer, const QString& locationName ) const; + bool haveGroupsInCommon( const QString& userOne, const QString& userTwo ) const; + bool haveSameLocations( const QString& computerOne, const QString& computerTwo ) const; + bool isLocalHost( const QString& accessingComputer ) const; + bool isLocalUser( const QString& accessingUser, const QString& localUser ) const; + bool isNoUserLoggedOn() const; + + QString lookupSubject( AccessControlRule::Subject subject, + const QString& accessingUser, const QString& accessingComputer, + const QString& localUser, const QString& localComputer ) const; + + bool matchConditions( const AccessControlRule& rule, + const QString& accessingUser, const QString& accessingComputer, + const QString& localUser, const QString& localComputer, + const QStringList& connectedUsers ) const; + + static QStringList objectNames( const NetworkObjectList& objects ); + + QList m_accessControlRules; + UserGroupsBackendInterface* m_userGroupsBackend; + NetworkObjectDirectory* m_networkObjectDirectory; + bool m_queryDomainGroups; + +} ; diff --git a/core/include/AccessControlRule.h b/core/include/AccessControlRule.h new file mode 100644 index 0000000..13e37a5 --- /dev/null +++ b/core/include/AccessControlRule.h @@ -0,0 +1,194 @@ +/* + * AccessControlRule.h - declaration of class AccessControlRule + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include + +#include "VeyonCore.h" + +class VEYON_CORE_EXPORT AccessControlRule +{ + Q_GADGET +public: + enum class Action + { + None, + Allow, + Deny, + AskForPermission, + } ; + + Q_ENUM(Action) + + enum class Condition + { + None, + MemberOfUserGroup, + GroupsInCommon, + LocatedAt, + SameLocation, + AccessFromLocalHost, + AccessFromLocalUser, + AccessFromAlreadyConnectedUser, + NoUserLoggedOn, + } ; + + Q_ENUM(Condition) + + enum class Subject + { + None, + AccessingUser, + AccessingComputer, + LocalUser, + LocalComputer, + } ; + + Q_ENUM(Subject) + + using ConditionArgument = QString; + + struct ConditionParameters + { + bool enabled{false}; + Subject subject{Subject::None}; + ConditionArgument argument; + }; + + using ConditionParameterMap = QMap; + + + AccessControlRule(); + AccessControlRule( const AccessControlRule& other ); + explicit AccessControlRule( const QJsonValue& jsonValue ); + + ~AccessControlRule() = default; + + AccessControlRule& operator=( const AccessControlRule& other ); + + const QString& name() const + { + return m_name; + } + + void setName( const QString& name ) + { + m_name = name; + } + + const QString& description() const + { + return m_description; + } + + void setDescription( const QString& description ) + { + m_description = description; + } + + Action action() const + { + return m_action; + } + + void setAction( Action action ) + { + m_action = action; + } + + const ConditionParameterMap& parameters() const + { + return m_parameters; + } + + Subject subject( Condition condition ) const + { + return m_parameters.value( condition ).subject; + } + + void setSubject( Condition condition, Subject subject ) + { + m_parameters[condition].subject = subject; + } + + bool areConditionsIgnored() const + { + return m_ignoreConditions; + } + + void setConditionsIgnored( bool ignored ) + { + m_ignoreConditions = ignored; + } + + bool areConditionsInverted() const + { + return m_invertConditions; + } + + void setConditionsInverted( bool inverted ) + { + m_invertConditions = inverted; + } + + bool isConditionEnabled( Condition condition ) const + { + return m_parameters.value( condition ).enabled; + } + + void setConditionEnabled( Condition condition, bool enabled ) + { + m_parameters[condition].enabled = enabled; + } + + ConditionArgument argument( Condition condition ) const + { + return m_parameters.value( condition ).argument; + } + + void clearParameters() + { + m_parameters.clear(); + } + + void setArgument( Condition condition, const ConditionArgument& conditionArgument ) + { + m_parameters[condition].argument = conditionArgument; + } + + QJsonObject toJson() const; + + +private: + QString m_name; + QString m_description; + Action m_action; + ConditionParameterMap m_parameters; + bool m_invertConditions; + bool m_ignoreConditions; + +} ; diff --git a/core/include/AuthenticationCredentials.h b/core/include/AuthenticationCredentials.h new file mode 100644 index 0000000..9d0c9a2 --- /dev/null +++ b/core/include/AuthenticationCredentials.h @@ -0,0 +1,123 @@ +/* + * AuthenticationCredentials.h - class holding credentials for authentication + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CryptoCore.h" + +// clazy:excludeall=rule-of-three + +class VEYON_CORE_EXPORT AuthenticationCredentials +{ +public: + using Token = CryptoCore::SecureArray; + using Password = CryptoCore::SecureArray; + + enum class Type + { + None = 0x00, + PrivateKey = 0x01, + UserLogon = 0x02, + Token = 0x04, + AllTypes = PrivateKey | UserLogon | Token + } ; + + Q_DECLARE_FLAGS(TypeFlags, Type) + + AuthenticationCredentials(); + AuthenticationCredentials( const AuthenticationCredentials &other ); + + bool hasCredentials( Type credentialType ) const; + + // private key auth + bool loadPrivateKey( const QString& privateKeyFile ); + bool setPrivateKey( const CryptoCore::PrivateKey& privateKey ); + const CryptoCore::PrivateKey& privateKey() const + { + return m_privateKey; + } + + const QString& authenticationKeyName() const + { + return m_authenticationKeyName; + } + + void setAuthenticationKeyName( const QString& authenticationKeyName ) + { + m_authenticationKeyName = authenticationKeyName; + } + + // user logon auth + void setLogonUsername( const QString& username ) + { + m_logonUsername = username; + } + + void setLogonPassword( const Password& password ) + { + m_logonPassword = password; + } + + const QString& logonUsername() const + { + return m_logonUsername; + } + + const Password& logonPassword() const + { + return m_logonPassword; + } + + void setToken( const Token& token ) + { + m_token = token; + } + + const Token& token() const + { + return m_token; + } + + void setInternalVncServerPassword( const Password& password ) + { + m_internalVncServerPassword = password; + } + + const Password& internalVncServerPassword() const + { + return m_internalVncServerPassword; + } + +private: + CryptoCore::PrivateKey m_privateKey; + QString m_authenticationKeyName; + + QString m_logonUsername; + Password m_logonPassword; + + Token m_token; + + Password m_internalVncServerPassword; + +} ; diff --git a/core/include/AuthenticationProxy.h b/core/include/AuthenticationProxy.h new file mode 100644 index 0000000..fe8b133 --- /dev/null +++ b/core/include/AuthenticationProxy.h @@ -0,0 +1,103 @@ +/* + * AuthenticationProxy.h - declaration of class AuthenticationProxy + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "AuthenticationCredentials.h" +#include "RfbVeyonAuth.h" + +class AuthenticationProxy +{ +public: + using AuthenticationMethod = RfbVeyonAuth::Type; + using AuthenticationMethods = QList; + + virtual ~AuthenticationProxy() = default; + + void setAuthenticationTypes( const AuthenticationMethods& authenticationTypes ) + { + lock(); + m_authenticationTypes = authenticationTypes; + unlock(); + + m_authenticationMethodsAvailable.wakeAll(); + } + + AuthenticationMethods authenticationTypes() + { + QMutexLocker l( &m_mutex ); + return m_authenticationTypes; + } + + bool waitForAuthenticationMethods( int timeout ) + { + QMutex m; + QMutexLocker l( &m ); + if( authenticationTypes().isEmpty() == false ) + { + return true; + } + return m_authenticationMethodsAvailable.wait( &m, QDeadlineTimer( timeout ) ); + } + + AuthenticationCredentials credentials() + { + QMutexLocker l( &m_mutex ); + return m_credentials; + } + + void setCredentials( const AuthenticationCredentials& credentials ) + { + QMutexLocker l( &m_mutex ); + m_credentials = credentials; + } + + virtual RfbVeyonAuth::Type initCredentials() = 0; + +protected: + void lock() + { + m_mutex.lock(); + } + + void unlock() + { + m_mutex.unlock(); + } + + QMutex* mutex() + { + return &m_mutex; + } + +private: + QMutex m_mutex; + AuthenticationCredentials m_credentials; + AuthenticationMethods m_authenticationTypes; + QWaitCondition m_authenticationMethodsAvailable; + +}; diff --git a/core/include/BuiltinFeatures.h b/core/include/BuiltinFeatures.h new file mode 100644 index 0000000..99d6d5d --- /dev/null +++ b/core/include/BuiltinFeatures.h @@ -0,0 +1,67 @@ +/* + * BuiltinFeatures.h - declaration of BuiltinFeatures class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonCore.h" + +class DesktopAccessDialog; +class FeatureControl; +class MonitoringMode; +class SystemTrayIcon; + +// clazy:excludeall=rule-of-three + +class VEYON_CORE_EXPORT BuiltinFeatures +{ +public: + BuiltinFeatures(); + ~BuiltinFeatures(); + + FeatureControl& featureControl() + { + return *m_featureControl; + } + + SystemTrayIcon& systemTrayIcon() + { + return *m_systemTrayIcon; + } + + MonitoringMode& monitoringMode() + { + return *m_monitoringMode; + } + + DesktopAccessDialog& desktopAccessDialog() + { + return *m_desktopAccessDialog; + } + +private: + FeatureControl* m_featureControl; + SystemTrayIcon* m_systemTrayIcon; + MonitoringMode* m_monitoringMode; + DesktopAccessDialog* m_desktopAccessDialog; +}; diff --git a/core/include/CommandLineIO.h b/core/include/CommandLineIO.h new file mode 100644 index 0000000..9586940 --- /dev/null +++ b/core/include/CommandLineIO.h @@ -0,0 +1,60 @@ +/* + * CommandLineIO.h - text input/output for command line plugins + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonCore.h" + +// clazy:excludeall=rule-of-three + +class VEYON_CORE_EXPORT CommandLineIO +{ +public: + using TableCell = QString; + using TableHeader = QList; + using TableRow = QList; + using TableRows = QVector; + using TableColumnWidths = QVector; + using Table = QPair; + using ArgumentValue = QString; + using ArgumentSpecifier = QString; + using Arguments = QMap; + using Examples = QList >; + + static void print( const QString& message ); + static void newline(); + static void info( const QString& message ); + static void warning( const QString& message ); + static void error( const QString& message ); + static void printTable( const Table& table, char horizontal = '-', char vertical = '|', char corner = '+' ); + static void printUsage( const QString& module, const QString& command, + const Arguments& mandatoryArguments, const Arguments& optionalArguments = {} ); + static void printDescription( const QString& description ); + static void printExamples( const QString& module, const QString& command, const Examples& examples ); + +private: + static void printTableRuler( const TableColumnWidths& columnWidths, char horizontal, char corner ); + static void printTableRow( const TableColumnWidths& columnWidths, char vertical, const TableRow& row ); + +} ; diff --git a/core/include/CommandLinePluginInterface.h b/core/include/CommandLinePluginInterface.h new file mode 100644 index 0000000..7e13ec5 --- /dev/null +++ b/core/include/CommandLinePluginInterface.h @@ -0,0 +1,61 @@ +/* + * CommandLinePluginInterface.h - interface class for command line plugins + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PluginInterface.h" + +// clazy:excludeall=copyable-polymorphic + +class VEYON_CORE_EXPORT CommandLinePluginInterface +{ + Q_GADGET +public: + enum RunResult + { + Unknown, + Successful, + Failed, + InvalidArguments, + NotEnoughArguments, + InvalidCommand, + NotLicensed, + NoResult, + RunResultCount + } ; + + Q_ENUM(RunResult) + + virtual QString commandLineModuleName() const = 0; + virtual QString commandLineModuleHelp() const = 0; + virtual QStringList commands() const = 0; + virtual QString commandHelp( const QString& command ) const = 0; + +}; + +using CommandLinePluginInterfaceList = QList; + +#define CommandLinePluginInterface_iid "io.veyon.Veyon.Plugins.CommandLinePluginInterface" + +Q_DECLARE_INTERFACE(CommandLinePluginInterface, CommandLinePluginInterface_iid) diff --git a/core/include/Computer.h b/core/include/Computer.h new file mode 100644 index 0000000..bda6dde --- /dev/null +++ b/core/include/Computer.h @@ -0,0 +1,107 @@ +/* + * Computer.h - represents a computer and provides control methods and data + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "NetworkObject.h" + +// clazy:excludeall=rule-of-three + +class VEYON_CORE_EXPORT Computer +{ +public: + explicit Computer( NetworkObject::Uid networkObjectUid = NetworkObject::Uid(), + const QString& name = {}, + const QString& hostAddress = {}, + const QString& macAddress = {}, + const QString& location = {} ); + + bool operator==( const Computer& other ) const + { + return networkObjectUid() == other.networkObjectUid(); + } + + bool operator!=( const Computer& other ) const + { + return !(*this == other); + } + + NetworkObject::Uid networkObjectUid() const + { + return m_networkObjectUid; + } + + void setName( const QString& name ) + { + m_name = name; + } + + QString name() const + { + return m_name; + } + + void setHostAddress( const QString& hostAddress ) + { + m_hostAddress = hostAddress; + } + + QString hostAddress() const + { + return m_hostAddress; + } + + void setMacAddress( const QString& macAddress ) + { + m_macAddress = macAddress; + } + + QString macAddress() const + { + return m_macAddress; + } + + void setLocation( const QString& location ) + { + m_location = location; + } + + const QString& location() const + { + return m_location; + } + +private: + NetworkObject::Uid m_networkObjectUid; + QString m_name; + QString m_hostAddress; + QString m_macAddress; + QString m_location; + +}; + +using ComputerList = QVector; diff --git a/core/include/ComputerControlInterface.h b/core/include/ComputerControlInterface.h new file mode 100644 index 0000000..d46dc79 --- /dev/null +++ b/core/include/ComputerControlInterface.h @@ -0,0 +1,180 @@ +/* + * ComputerControlInterface.h - interface class for controlling a computer + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include +#include + +#include "Computer.h" +#include "Feature.h" +#include "Lockable.h" +#include "VeyonCore.h" +#include "VncConnection.h" + +class QImage; + +class AuthenticationProxy; +class FeatureMessage; +class VncConnection; +class VeyonConnection; + +class VEYON_CORE_EXPORT ComputerControlInterface : public QObject, public Lockable +{ + Q_OBJECT +public: + enum class UpdateMode { + Disabled, + Monitoring, + Live + }; + + using Pointer = QSharedPointer; + + using State = VncConnection::State; + + explicit ComputerControlInterface( const Computer& computer, QObject* parent = nullptr ); + ~ComputerControlInterface() override; + + void start( QSize scaledScreenSize = {}, UpdateMode updateMode = UpdateMode::Monitoring, AuthenticationProxy* authenticationProxy = nullptr ); + void stop(); + + const Computer& computer() const + { + return m_computer; + } + + State state() const + { + return m_state; + } + + bool hasValidFramebuffer() const; + + QSize screenSize() const; + + const QSize& scaledScreenSize() const + { + return m_scaledScreenSize; + } + + void setScaledScreenSize( QSize size ); + + QImage scaledScreen() const; + + QImage screen() const; + + const QString& userLoginName() const + { + return m_userLoginName; + } + + const QString& userFullName() const + { + return m_userFullName; + } + + int userSessionId() const + { + return m_userSessionId; + } + + void setUserInformation( const QString& userLoginName, const QString& userFullName, int sessionId ); + + const FeatureUidList& activeFeatures() const + { + return m_activeFeatures; + } + + void setActiveFeatures( const FeatureUidList& activeFeatures ); + + Feature::Uid designatedModeFeature() const + { + return m_designatedModeFeature; + } + + void setDesignatedModeFeature( Feature::Uid designatedModeFeature ) + { + m_designatedModeFeature = designatedModeFeature; + } + + void updateActiveFeatures(); + + void sendFeatureMessage( const FeatureMessage& featureMessage, bool wake ); + bool isMessageQueueEmpty(); + + void setUpdateMode( UpdateMode updateMode ); + UpdateMode updateMode() const + { + return m_updateMode; + } + + Pointer weakPointer(); + +private: + void resetWatchdog(); + void restartConnection(); + + void updateState(); + void updateUser(); + + void handleFeatureMessage( const FeatureMessage& message ); + + static constexpr int ConnectionWatchdogTimeout = 10000; + static constexpr int UpdateIntervalDisabled = 5000; + + Computer m_computer; + + UpdateMode m_updateMode{UpdateMode::Disabled}; + + State m_state; + QString m_userLoginName; + QString m_userFullName; + int m_userSessionId{0}; + FeatureUidList m_activeFeatures; + Feature::Uid m_designatedModeFeature; + + QSize m_scaledScreenSize; + + VncConnection* m_vncConnection; + VeyonConnection* m_connection; + QTimer m_connectionWatchdogTimer; + QTimer m_userUpdateTimer; + QTimer m_activeFeaturesUpdateTimer; + +Q_SIGNALS: + void featureMessageReceived( const FeatureMessage&, ComputerControlInterface::Pointer ); + void screenSizeChanged(); + void screenUpdated(); + void userChanged(); + void stateChanged(); + void activeFeaturesChanged(); + +}; + +using ComputerControlInterfaceList = QVector; + +Q_DECLARE_METATYPE(ComputerControlInterface::Pointer) diff --git a/core/include/ComputerListModel.h b/core/include/ComputerListModel.h new file mode 100644 index 0000000..34d1f8a --- /dev/null +++ b/core/include/ComputerListModel.h @@ -0,0 +1,94 @@ +/* + * ComputerListModel.h - data model base class for computer objects + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "VeyonCore.h" + +class VEYON_CORE_EXPORT ComputerListModel : public QAbstractListModel +{ + Q_OBJECT +public: + enum { + UidRole = Qt::UserRole, + StateRole, + ImageIdRole, + GroupsRole, + ScreenRole, + ControlInterfaceRole + }; + + enum class DisplayRoleContent { + UserAndComputerName, + UserName, + ComputerName, + }; + Q_ENUM(DisplayRoleContent) + + enum class SortOrder { + ComputerAndUserName, + UserName, + ComputerName, + }; + Q_ENUM(SortOrder) + + enum class AspectRatio { + Auto, + AR16_9, + AR16_10, + AR3_2, + AR4_3 + }; + Q_ENUM(AspectRatio) + + explicit ComputerListModel( QObject* parent = nullptr ); + + Qt::ItemFlags flags( const QModelIndex& index ) const override; + + Qt::DropActions supportedDragActions() const override; + Qt::DropActions supportedDropActions() const override; + + DisplayRoleContent displayRoleContent() const + { + return m_displayRoleContent; + } + + SortOrder sortOrder() const + { + return m_sortOrder; + } + + AspectRatio aspectRatio() const + { + return m_aspectRatio; + } + +private: + DisplayRoleContent m_displayRoleContent; + SortOrder m_sortOrder; + AspectRatio m_aspectRatio; + +}; diff --git a/core/include/Configuration/JsonStore.h b/core/include/Configuration/JsonStore.h new file mode 100644 index 0000000..afb4532 --- /dev/null +++ b/core/include/Configuration/JsonStore.h @@ -0,0 +1,51 @@ +/* + * Configuration/JsonStore.h - JsonStore class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "Configuration/Store.h" + +namespace Configuration +{ + +// clazy:excludeall=copyable-polymorphic + +class VEYON_CORE_EXPORT JsonStore : public Store +{ +public: + explicit JsonStore( Scope scope, const QString & file = {} ); + + void load( Object *obj ) override; + void flush( const Object *obj ) override; + bool isWritable() const override; + void clear() override; + +private: + QString configurationFilePath() const; + + QString m_file; + +} ; + +} diff --git a/core/include/Configuration/LocalStore.h b/core/include/Configuration/LocalStore.h new file mode 100644 index 0000000..d411e6c --- /dev/null +++ b/core/include/Configuration/LocalStore.h @@ -0,0 +1,50 @@ +/* + * Configuration/LocalStore.h - LocalStore class + * + * Copyright (c) 2009-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "Configuration/Store.h" + +class QSettings; + +namespace Configuration +{ + +// clazy:excludeall=copyable-polymorphic + +class VEYON_CORE_EXPORT LocalStore : public Store +{ +public: + explicit LocalStore( Scope scope ); + + void load( Object *obj ) override; + void flush( const Object *obj ) override; + bool isWritable() const override; + void clear() override; + + QSettings *createSettingsObject() const; + +} ; + +} diff --git a/core/include/Configuration/Object.h b/core/include/Configuration/Object.h new file mode 100644 index 0000000..b7ec23b --- /dev/null +++ b/core/include/Configuration/Object.h @@ -0,0 +1,105 @@ +/* + * Configuration/Object.h - ConfigurationObject class + * + * Copyright (c) 2009-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonCore.h" +#include "Configuration/Store.h" + +namespace Configuration +{ + +// clazy:excludeall=ctor-missing-parent-argument,copyable-polymorphic + +class VEYON_CORE_EXPORT Object : public QObject +{ + Q_OBJECT +public: + using DataMap = QMap; + + Object(); + Object( Store::Backend backend, Store::Scope scope, const QString& storeName = {} ); + explicit Object( Store* store ); + Object( const Object& ); + ~Object() override; + + Object& operator=( const Object& ref ); + Object& operator+=( const Object& ref ); + + bool hasValue( const QString& key, const QString& parentKey ) const; + + QVariant value( const QString& key, const QString& parentKey, const QVariant& defaultValue ) const; + + void setValue( const QString& key, const QVariant& value, const QString& parentKey ); + + void removeValue( const QString& key, const QString& parentKey ); + + void addSubObject( Object* obj, const QString& parentKey ); + + void reloadFromStore() + { + if( m_store ) + { + m_store->load( this ); + } + } + + void flushStore() + { + if( m_store ) + { + m_store->flush( this ); + } + } + + bool isStoreWritable() const + { + return m_store->isWritable(); + } + + void clear() + { + m_data.clear(); + } + + const DataMap & data() const + { + return m_data; + } + + +Q_SIGNALS: + void configurationChanged(); + + +private: + static Store* createStore( Store::Backend backend, Store::Scope scope ); + + Configuration::Store *m_store; + bool m_customStore; + DataMap m_data; + +} ; + +} diff --git a/core/include/Configuration/Password.h b/core/include/Configuration/Password.h new file mode 100644 index 0000000..34a2575 --- /dev/null +++ b/core/include/Configuration/Password.h @@ -0,0 +1,55 @@ +/* + * Configuration/Password.h - Configuration::Password class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CryptoCore.h" + +namespace Configuration +{ + +class VEYON_CORE_EXPORT Password +{ + Q_GADGET +public: + Password() = default; + + CryptoCore::PlaintextPassword plainText() const; + + const QString& encrypted() const + { + return m_encrypted; + } + + static Password fromPlainText( const CryptoCore::PlaintextPassword& plainText ); + static Password fromEncrypted( const QString& encrypted ); + +private: + QString m_encrypted; + +}; + +} + +Q_DECLARE_METATYPE(Configuration::Password) diff --git a/core/include/Configuration/Property.h b/core/include/Configuration/Property.h new file mode 100644 index 0000000..ceb74a2 --- /dev/null +++ b/core/include/Configuration/Property.h @@ -0,0 +1,181 @@ +/* + * Configuration/Property.h - Configuration::Property class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "QtCompat.h" +#include "VeyonCore.h" +#include "Configuration/Password.h" + +namespace Configuration +{ + +class Object; +class Proxy; + +template struct CheapestType { using Type = const T &; }; +template struct CheapestType::value >::type> { using Type = T; }; +template<> struct CheapestType { using Type = bool; }; +template<> struct CheapestType { using Type = quint8; }; +template<> struct CheapestType { using Type = quint16; }; +template<> struct CheapestType { using Type = quint32; }; +template<> struct CheapestType { using Type = quint64; }; +template<> struct CheapestType { using Type = qint8; }; +template<> struct CheapestType { using Type = qint16; }; +template<> struct CheapestType { using Type = qint32; }; +template<> struct CheapestType { using Type = qint64; }; +template<> struct CheapestType { using Type = float; }; +template<> struct CheapestType { using Type = double; }; +template<> struct CheapestType { using Type = QUuid; }; +template struct CheapestType { using Type = T *; }; + + +class VEYON_CORE_EXPORT Property : public QObject +{ + Q_OBJECT +public: + enum class Flag + { + Standard = 0x01, + Advanced = 0x02, + Hidden = 0x04, + Legacy = 0x08 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + // work around QTBUG-47652 where Q_FLAG() is broken for enum classes when using Qt < 5.12 +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + Q_FLAG(Flags) +#endif + + Property( Object* object, const QString& key, const QString& parentKey, + const QVariant& defaultValue, Flags flags ); + + Property( Proxy* proxy, const QString& key, const QString& parentKey, + const QVariant& defaultValue, Flags flags ); + + QObject* lambdaContext() const; + + QVariant variantValue() const; + + void setVariantValue( const QVariant& value ) const; + + static Property* find( QObject* parent, const QString& key, const QString& parentKey ); + + const QString& key() const + { + return m_key; + } + + const QString& parentKey() const + { + return m_parentKey; + } + + QString absoluteKey() const + { + if( m_parentKey.isEmpty() ) + { + return m_key; + } + + return m_parentKey + QLatin1Char('/') + m_key; + } + + const QVariant& defaultValue() const + { + return m_defaultValue; + } + + Flags flags() const + { + return m_flags; + } + +private: + Object* m_object; + Proxy* m_proxy; + const QString m_key; + const QString m_parentKey; + const QVariant m_defaultValue; + const Flags m_flags; + +}; + + +template +class VEYON_CORE_EXPORT TypedProperty : public Property { +public: + using Type = typename CheapestType::Type; + + TypedProperty( Object* object, const QString& key, const QString& parentKey, + const QVariant& defaultValue, Flags flags ) : + Property( object, key, parentKey, defaultValue, flags ) + { + } + + TypedProperty( Proxy* proxy, const QString& key, const QString& parentKey, + const QVariant& defaultValue, Flags flags ) : + Property( proxy, key, parentKey, defaultValue, flags ) + { + } + + T value() const + { + return QVariantHelper::value( variantValue() ); + } + + void setValue( Type value ) const + { + setVariantValue( QVariant::fromValue( value ) ); + } +}; + +template<> +VEYON_CORE_EXPORT Password TypedProperty::value() const; + +template<> +VEYON_CORE_EXPORT void TypedProperty::setValue( const Password& value ) const; + + +#define DECLARE_CONFIG_PROPERTY(className,config,type, name, setter, key, parentKey, defaultValue, flags) \ + private: \ + const Configuration::TypedProperty* m_##name{ new Configuration::TypedProperty( this, QStringLiteral(key), QStringLiteral(parentKey), defaultValue, flags ) }; \ + public: \ + type name() const \ + { \ + return m_##name->value(); \ + } \ + const Configuration::TypedProperty& name##Property() const \ + { \ + return *m_##name; \ + } \ + void setter( Configuration::TypedProperty::Type value ) const \ + { \ + m_##name->setValue( value ); \ + } + +} diff --git a/core/include/Configuration/Proxy.h b/core/include/Configuration/Proxy.h new file mode 100644 index 0000000..8de9431 --- /dev/null +++ b/core/include/Configuration/Proxy.h @@ -0,0 +1,86 @@ +/* + * Configuration/Proxy.h - ConfigurationProxy class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "Configuration/Object.h" +#include "Configuration/Property.h" + +namespace Configuration +{ + +class VEYON_CORE_EXPORT Proxy : public QObject +{ + Q_OBJECT +public: + explicit Proxy( Object* object ); + ~Proxy() override = default; + + bool hasValue( const QString& key, const QString& parentKey ) const; + + QVariant value( const QString& key, const QString& parentKey, const QVariant& defaultValue ) const; + + void setValue( const QString& key, const QVariant& value, const QString& parentKey ); + + void removeValue( const QString &key, const QString &parentKey ); + + void reloadFromStore(); + void flushStore(); + + QObject* object() const + { + return m_object; + } + + const QString& instanceId() const + { + return m_instanceId; + } + + void setInstanceId( const QString& instanceId ) + { + m_instanceId = instanceId; + } + + void removeInstance( const QString& parentKey ); + +private: + QString instanceParentKey( const QString& parentKey ) const; + + Object* m_object; + QString m_instanceId; + +} ; + +#define DECLARE_CONFIG_PROXY(name, ops) \ + class name : public Configuration::Proxy { \ + public: \ + explicit name( Configuration::Object* object ); \ + ops(DECLARE_CONFIG_PROPERTY) \ + }; + +#define IMPLEMENT_CONFIG_PROXY(name) \ + name::name( Configuration::Object* object ) : Configuration::Proxy( object ) { } + +} diff --git a/core/include/Configuration/Store.h b/core/include/Configuration/Store.h new file mode 100644 index 0000000..cb96dbe --- /dev/null +++ b/core/include/Configuration/Store.h @@ -0,0 +1,111 @@ +/* + * Configuration/Store.h - ConfigurationStore class + * + * Copyright (c) 2009-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include + +#include "VeyonCore.h" + +namespace Configuration +{ + +class Object; + +// clazy:excludeall=copyable-polymorphic + +class Store +{ +public: + enum Backends + { + LocalBackend, // registry or similiar + JsonFile, + NoBackend + } ; + using Backend = Backends; + + enum Scopes + { + User, // for current user + System, // system-wide (service settings etc.) + } ; + using Scope = Scopes; + + + Store( Backend backend, Scope scope ) : + m_backend( backend ), + m_scope( scope ) + { + } + + virtual ~Store() = default; + + Backend backend() const + { + return m_backend; + } + + Scope scope() const + { + return m_scope; + } + + QString configurationNameFromScope() const + { + switch( scope() ) + { + case User: return QStringLiteral( "UserConfig" ); + case System: return QStringLiteral( "SystemConfig" ); + } + + return {}; + } + + void setName( const QString& name ) + { + m_name = name; + } + + const QString& name() const + { + return m_name; + } + + virtual void load( Object *obj ) = 0; + virtual void flush( const Object *obj ) = 0; + virtual bool isWritable() const = 0; + virtual void clear() = 0; + + +private: + const Backend m_backend; + const Scope m_scope; + QString m_name; + +} ; + +} diff --git a/core/include/Configuration/UiMapping.h b/core/include/Configuration/UiMapping.h new file mode 100644 index 0000000..3285e1a --- /dev/null +++ b/core/include/Configuration/UiMapping.h @@ -0,0 +1,128 @@ +/* + * Configuration/UiMapping.h - helper macros and functions for connecting config with UI + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "Configuration/Property.h" + +class QCheckBox; +class QGroupBox; +class QLabel; +class QLineEdit; +class QPushButton; +class QRadioButton; +class QSpinBox; + +namespace Configuration +{ + +class VEYON_CORE_EXPORT UiMapping +{ +public: + static void initWidgetFromProperty( const Configuration::TypedProperty& property, QCheckBox* widget ); + static void initWidgetFromProperty( const Configuration::TypedProperty& property, QGroupBox* widget ); + static void initWidgetFromProperty( const Configuration::TypedProperty& property, QRadioButton* widget ); + static void initWidgetFromProperty( const Configuration::TypedProperty& property, QComboBox* widget ); + static void initWidgetFromProperty( const Configuration::TypedProperty& property, QLineEdit* widget ); + static void initWidgetFromProperty( const Configuration::TypedProperty& property, QLineEdit* widget ); + static void initWidgetFromProperty( const Configuration::TypedProperty& property, QPushButton* widget ); + static void initWidgetFromProperty( const Configuration::TypedProperty& property, QComboBox* widget ); + static void initWidgetFromProperty( const Configuration::TypedProperty& property, QSpinBox* widget ); + static void initWidgetFromProperty( const Configuration::TypedProperty& property, QComboBox* widget ); + + // SFINAE overload for enum classes + template + static typename std::enable_if::value, void>::type + initWidgetFromProperty( const Configuration::TypedProperty& property, QComboBox* widget ) + { + widget->setCurrentIndex( static_cast( property.value() ) ); + } + + // overloads for special properties which can't be mapped to widgets + static void initWidgetFromProperty( const Configuration::TypedProperty& property, QLabel* widget ) + { + Q_UNUSED(property) + Q_UNUSED(widget) + } + + static void initWidgetFromProperty( const Configuration::TypedProperty& property, QLabel* widget ) + { + Q_UNUSED(property) + Q_UNUSED(widget) + } + + static void setFlags( QObject* object, Configuration::Property::Flags flags ); + + static Property::Flags flags( QObject* object ); + + // widget initialization +#define INIT_WIDGET_FROM_PROPERTY(className, config, type, get, set, key, parentKey, defaultValue, flags) \ + Configuration::UiMapping::initWidgetFromProperty( config.get##Property(), ui->get ); \ + Configuration::UiMapping::setFlags( ui->get, flags ); + + + // connect widget signals to configuration property write methods + + static void connectWidgetToProperty( const Configuration::TypedProperty& property, QCheckBox* widget ); + static void connectWidgetToProperty( const Configuration::TypedProperty& property, QGroupBox* widget ); + static void connectWidgetToProperty( const Configuration::TypedProperty& property, QRadioButton* widget ); + static void connectWidgetToProperty( const Configuration::TypedProperty& property, QComboBox* widget ); + static void connectWidgetToProperty( const Configuration::TypedProperty& property, QLineEdit* widget ); + static void connectWidgetToProperty( const Configuration::TypedProperty& property, QLineEdit* widget ); + static void connectWidgetToProperty( const Configuration::TypedProperty& property, QPushButton* widget ); + static void connectWidgetToProperty( const Configuration::TypedProperty& property, QComboBox* widget ); + static void connectWidgetToProperty( const Configuration::TypedProperty& property, QSpinBox* widget ); + static void connectWidgetToProperty( const Configuration::TypedProperty& property, QComboBox* widget ); + + // SFINAE overload for enum classes + template + static typename std::enable_if::value, void>::type + connectWidgetToProperty( const Configuration::TypedProperty& property, QComboBox* widget ) + { + QObject::connect( widget, QOverload::of(&QComboBox::currentIndexChanged), property.lambdaContext(), + [&property]( int index ) { property.setValue( static_cast( index ) ); } ); + } + + + // overloads for special properties which can't be connected to widgets + static void connectWidgetToProperty( const Configuration::TypedProperty& property, QLabel* widget ) + { + Q_UNUSED(property) + Q_UNUSED(widget) + } + + static void connectWidgetToProperty( const Configuration::TypedProperty& property, QLabel* widget ) + { + Q_UNUSED(property) + Q_UNUSED(widget) + } + +#define CONNECT_WIDGET_TO_PROPERTY(className, config, type, get, set, key, parentKey, defaultValue, flags) \ + Configuration::UiMapping::connectWidgetToProperty( config.get##Property(), ui->get ); + +}; + +} diff --git a/core/include/ConfigurationManager.h b/core/include/ConfigurationManager.h new file mode 100644 index 0000000..c8d817b --- /dev/null +++ b/core/include/ConfigurationManager.h @@ -0,0 +1,52 @@ +/* + * ConfigurationManager.h - class for managing Veyon's configuration + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonCore.h" + +class VeyonConfiguration; + +// clazy:excludeall=ctor-missing-parent-argument + +class VEYON_CORE_EXPORT ConfigurationManager : public QObject +{ + Q_OBJECT +public: + explicit ConfigurationManager( QObject* parent = nullptr ); + + bool clearConfiguration(); + bool applyConfiguration(); + bool saveConfiguration(); + + const QString& errorString() const + { + return m_errorString; + } + +private: + VeyonConfiguration& m_configuration; + QString m_errorString; + +} ; diff --git a/core/include/ConfigurationPage.h b/core/include/ConfigurationPage.h new file mode 100644 index 0000000..3e4d95a --- /dev/null +++ b/core/include/ConfigurationPage.h @@ -0,0 +1,45 @@ +/* + * ConfigurationPage.h - base class for all configuration pages + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonCore.h" + +#include + +class VEYON_CORE_EXPORT ConfigurationPage : public QWidget +{ + Q_OBJECT +public: + explicit ConfigurationPage( QWidget* parent = nullptr ); + ~ConfigurationPage() override = default; + + virtual void resetWidgets() = 0; + virtual void connectWidgetsToProperties() = 0; + virtual void applyConfiguration() = 0; + +Q_SIGNALS: + void widgetsChanged(); + +}; diff --git a/core/include/ConfigurationPagePluginInterface.h b/core/include/ConfigurationPagePluginInterface.h new file mode 100644 index 0000000..7702898 --- /dev/null +++ b/core/include/ConfigurationPagePluginInterface.h @@ -0,0 +1,44 @@ +/* + * ConfigurationPagePluginInterface.h - interface class for configuration pages + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PluginInterface.h" + +class ConfigurationPage; + +// clazy:excludeall=copyable-polymorphic + +class ConfigurationPagePluginInterface +{ +public: + virtual ConfigurationPage* createConfigurationPage() = 0; + +}; + +using ConfigurationPagePluginInterfaceList = QList; + +#define ConfigurationPagePluginInterface_iid "io.veyon.Veyon.Plugins.ConfigurationPagePluginInterface" + +Q_DECLARE_INTERFACE(ConfigurationPagePluginInterface, ConfigurationPagePluginInterface_iid) diff --git a/core/include/CryptoCore.h b/core/include/CryptoCore.h new file mode 100644 index 0000000..5ce66e9 --- /dev/null +++ b/core/include/CryptoCore.h @@ -0,0 +1,62 @@ +/* + * CryptoCore.h - core functions for crypto features + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonCore.h" + +#include + +// clazy:excludeall=rule-of-three + +class VEYON_CORE_EXPORT CryptoCore +{ +public: + using KeyGenerator = QCA::KeyGenerator; + using PrivateKey = QCA::PrivateKey; + using PublicKey = QCA::PublicKey; + using SecureArray = QCA::SecureArray; + using PlaintextPassword = SecureArray; + + enum { + RsaKeySize = 4096, + ChallengeSize = 128, + }; + + static constexpr QCA::EncryptionAlgorithm DefaultEncryptionAlgorithm = QCA::EME_PKCS1_OAEP; + static constexpr QCA::SignatureAlgorithm DefaultSignatureAlgorithm = QCA::EMSA3_SHA512; + + CryptoCore(); + ~CryptoCore(); + + static QByteArray generateChallenge(); + + QString encryptPassword( const PlaintextPassword& password ) const; + PlaintextPassword decryptPassword( const QString& encryptedPassword ) const; + +private: + QCA::Initializer m_qcaInitializer; + PrivateKey m_defaultPrivateKey; + +}; diff --git a/core/include/DesktopAccessDialog.h b/core/include/DesktopAccessDialog.h new file mode 100644 index 0000000..a59d10a --- /dev/null +++ b/core/include/DesktopAccessDialog.h @@ -0,0 +1,147 @@ +/* + * DesktopAccessDialog.h - declaration of DesktopAccessDialog class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "FeatureProviderInterface.h" + +class FeatureWorkerManager; + +class VEYON_CORE_EXPORT DesktopAccessDialog : public QObject, public FeatureProviderInterface, public PluginInterface +{ + Q_OBJECT + Q_INTERFACES(FeatureProviderInterface PluginInterface) +public: + enum class Argument + { + User, + Host, + Choice + }; + Q_ENUM(Argument) + + enum Choice + { + ChoiceNone, + ChoiceYes, + ChoiceNo, + ChoiceAlways, + ChoiceNever, + ChoiceCount + } ; + Q_ENUM(Choice) + + explicit DesktopAccessDialog( QObject* parent = nullptr ); + ~DesktopAccessDialog() override = default; + + bool isBusy( FeatureWorkerManager* featureWorkerManager ) const; + + void exec( FeatureWorkerManager* featureWorkerManager, const QString& user, const QString& host ); + void abort( FeatureWorkerManager* featureWorkerManager ); + + Choice choice() const + { + return m_choice; + } + + Plugin::Uid uid() const override + { + return QStringLiteral("13fb9ce8-afbc-4397-93e3-e01c4d8288c9"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral( "DesktopAccessDialog" ); + } + + QString description() const override + { + return tr( "Desktop access dialog" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + const FeatureList& featureList() const override + { + return m_features; + } + + bool controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) override + { + Q_UNUSED(featureUid) + Q_UNUSED(operation) + Q_UNUSED(arguments) + Q_UNUSED(computerControlInterfaces) + + return false; + } + + bool handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) override; + + bool handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) override; + +private: + static Choice requestDesktopAccess( const QString& user, const QString& host ); + + enum + { + DialogTimeout = 30000, + SleepTime = 100 + }; + + enum Commands + { + RequestDesktopAccess, + ReportDesktopAccessChoice + }; + + const Feature m_desktopAccessDialogFeature; + const FeatureList m_features; + + Choice m_choice; + + QTimer m_abortTimer; + +Q_SIGNALS: + void finished(); + +}; diff --git a/core/include/EnumHelper.h b/core/include/EnumHelper.h new file mode 100644 index 0000000..fcd599e --- /dev/null +++ b/core/include/EnumHelper.h @@ -0,0 +1,47 @@ +/* + * EnumHelper.h - helper functions for enumerations + * + * Copyright (c) 2019 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +// clazy:excludeall=rule-of-three + +class EnumHelper +{ +public: + template + static QString toString( T item ) + { + return QLatin1String( QMetaEnum::fromType().key( static_cast( item ) ) ); + } + + template + static QString toCamelCaseString( T item ) + { + const auto name = toString( item ); + return name.mid( 0, 1 ).toLower() + name.mid( 1 ); + } + +} ; diff --git a/core/include/Feature.h b/core/include/Feature.h new file mode 100644 index 0000000..7ca479f --- /dev/null +++ b/core/include/Feature.h @@ -0,0 +1,219 @@ +/* + * Feature.h - declaration of the Feature class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include +#include + +#include "VeyonCore.h" + +class VEYON_CORE_EXPORT Feature +{ + Q_GADGET +public: + using Uid = QUuid; + + enum FeatureFlag + { + NoFlags, + Mode = 0x0001, + Action = 0x0002, + Session = 0x0004, + Internal = 0x0008, + Option = 0x0010, + Checked = 0x0020, + Master = 0x0100, + Service = 0x0200, + Worker = 0x0400, + Builtin = 0x1000, + AllComponents = Master | Service | Worker + } ; + + Q_DECLARE_FLAGS(Flags, FeatureFlag) + Q_FLAG(Flags) + + explicit Feature( const QString& name, + Flags flags, + Uid uid, + Uid parentUid, + const QString& displayName, + const QString& displayNameActive, + const QString& description, + const QString& iconUrl = {}, + const QKeySequence& shortcut = {} ) : + m_name( name ), + m_flags( flags ), + m_uid( uid ), + m_parentUid( parentUid ), + m_displayName( displayName ), + m_displayNameActive( displayNameActive ), + m_description( description ), + m_iconUrl( iconUrl ), + m_shortcut( shortcut ) + { + } + + explicit Feature( Uid uid = Uid() ) : + m_name(), + m_flags( NoFlags ), + m_uid( uid ), + m_parentUid( QUuid() ), + m_displayName(), + m_displayNameActive(), + m_description(), + m_iconUrl(), + m_shortcut() + { + } + + Feature( const Feature& other ) : + m_name( other.name() ), + m_flags( other.flags() ), + m_uid( other.uid() ), + m_parentUid( other.parentUid() ), + m_displayName( other.displayName() ), + m_displayNameActive( other.displayNameActive() ), + m_description( other.description() ), + m_iconUrl( other.iconUrl() ), + m_shortcut( other.shortcut() ) + { + } + + ~Feature() = default; + + Feature& operator=( const Feature& other ) + { + m_name = other.name(); + m_flags = other.flags(); + m_uid = other.uid(); + m_parentUid = other.parentUid(); + m_displayName = other.displayName(); + m_displayNameActive = other.displayNameActive(); + m_description = other.description(); + m_iconUrl = other.iconUrl(); + m_shortcut = other.shortcut(); + + return *this; + } + + bool operator==( const Feature& other ) const + { + return other.uid() == uid(); + } + + bool operator!=( const Feature& other ) const + { + return other.uid() != uid(); + } + + bool isValid() const + { + return m_flags != NoFlags; + } + + bool testFlag( FeatureFlag flag ) const + { + return m_flags.testFlag( flag ); + } + + const QString& name() const + { + return m_name; + } + + const Uid& uid() const + { + return m_uid; + } + + const Uid& parentUid() const + { + return m_parentUid; + } + + void setParentUid( Uid uid ) + { + m_parentUid = uid; + } + + const QString& displayName() const + { + return m_displayName; + } + + void setDisplayName( const QString& displayName ) + { + m_displayName = displayName; + } + + const QString& displayNameActive() const + { + return m_displayNameActive; + } + + const QString& description() const + { + return m_description; + } + + const QString& iconUrl() const + { + return m_iconUrl; + } + + void setIconUrl( const QString& iconUrl ) + { + m_iconUrl = iconUrl; + } + + const QKeySequence& shortcut() const + { + return m_shortcut; + } + +private: + Flags flags() const + { + return m_flags; + } + + QString m_name; + Flags m_flags; + Uid m_uid; + Uid m_parentUid; + QString m_displayName; + QString m_displayNameActive; + QString m_description; + QString m_iconUrl; + QKeySequence m_shortcut; + +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(Feature::Flags) + +using FeatureList = QList; +using FeatureUidList = QList; diff --git a/core/include/FeatureControl.h b/core/include/FeatureControl.h new file mode 100644 index 0000000..8bc4bcc --- /dev/null +++ b/core/include/FeatureControl.h @@ -0,0 +1,109 @@ +/* + * FeatureControl.h - declaration of FeatureControl class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "FeatureProviderInterface.h" + +class VEYON_CORE_EXPORT FeatureControl : public QObject, public FeatureProviderInterface, public PluginInterface +{ + Q_OBJECT + Q_INTERFACES(FeatureProviderInterface PluginInterface) +public: + enum class Argument + { + ActiveFeaturesList + }; + Q_ENUM(Argument) + + explicit FeatureControl( QObject* parent = nullptr ); + ~FeatureControl() override = default; + + void queryActiveFeatures( const ComputerControlInterfaceList& computerControlInterfaces ); + + Plugin::Uid uid() const override + { + return QStringLiteral("f5ec79e0-186c-4af0-89a2-7e5687cc32b2"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral( "FeatureControl" ); + } + + QString description() const override + { + return tr( "Feature control" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + const FeatureList& featureList() const override + { + return m_features; + } + + bool controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) override + { + Q_UNUSED(featureUid) + Q_UNUSED(operation) + Q_UNUSED(arguments) + Q_UNUSED(computerControlInterfaces) + + return false; + } + + bool handleFeatureMessage( ComputerControlInterface::Pointer computerControlInterface, + const FeatureMessage& message ) override; + + bool handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) override; + +private: + enum Commands + { + QueryActiveFeatures, + }; + + const Feature m_featureControlFeature; + const FeatureList m_features; + + FeatureUidList m_activeFeatures; + +}; diff --git a/core/include/FeatureManager.h b/core/include/FeatureManager.h new file mode 100644 index 0000000..0602a93 --- /dev/null +++ b/core/include/FeatureManager.h @@ -0,0 +1,83 @@ +/* + * FeatureManager.h - header for the FeatureManager class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "Feature.h" +#include "FeatureProviderInterface.h" +#include "ComputerControlInterface.h" +#include "Plugin.h" + +class QWidget; + +class FeatureWorkerManager; + +class VEYON_CORE_EXPORT FeatureManager : public QObject +{ + Q_OBJECT +public: + explicit FeatureManager( QObject* parent = nullptr ); + + const FeatureList& features() const + { + return m_features; + } + + const FeatureList& features( Plugin::Uid pluginUid ) const; + + const Feature& feature( Feature::Uid featureUid ) const; + + Plugin::Uid pluginUid( const Feature& feature ) const; + + void controlFeature( Feature::Uid featureUid, + FeatureProviderInterface::Operation operation, + const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) const; + + void startFeature( VeyonMasterInterface& master, + const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) const; + void stopFeature( VeyonMasterInterface& master, + const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) const; + + void updateActiveFeatures( const ComputerControlInterfaceList& computerControlInterfaces ) const; + + bool handleFeatureMessage( ComputerControlInterface::Pointer computerControlInterface, + const FeatureMessage& message ) const; + bool handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) const; + bool handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) const; + +private: + FeatureList m_features; + const FeatureList m_emptyFeatureList; + QObjectList m_pluginObjects; + FeatureProviderInterfaceList m_featurePluginInterfaces; + const Feature m_dummyFeature; + +}; diff --git a/core/include/FeatureMessage.h b/core/include/FeatureMessage.h new file mode 100644 index 0000000..3dc1207 --- /dev/null +++ b/core/include/FeatureMessage.h @@ -0,0 +1,128 @@ +/* + * FeatureMessage.h - header for a message encapsulation class for features + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "Feature.h" +#include "MessageContext.h" + +class QIODevice; + +class VEYON_CORE_EXPORT FeatureMessage +{ +public: + using MessageSize = quint32; + using FeatureUid = Feature::Uid; + using Command = qint32; + using Arguments = QVariantMap; + + static constexpr unsigned char RfbMessageType = 41; + + enum SpecialCommands + { + DefaultCommand = 0, + InvalidCommand = -1, + InitCommand = -2, + }; + + explicit FeatureMessage() : + m_featureUid(), + m_command( InvalidCommand ), + m_arguments() + { + } + + explicit FeatureMessage( FeatureUid featureUid, Command command ) : + m_featureUid( featureUid ), + m_command( command ), + m_arguments() + { + } + + explicit FeatureMessage( const FeatureMessage& other ) : + m_featureUid( other.featureUid() ), + m_command( other.command() ), + m_arguments( other.arguments() ) + { + } + + ~FeatureMessage() = default; + + FeatureMessage& operator=( const FeatureMessage& other ) + { + m_featureUid = other.featureUid(); + m_command = other.command(); + m_arguments = other.arguments(); + + return *this; + } + + const FeatureUid& featureUid() const + { + return m_featureUid; + } + + Command command() const + { + return m_command; + } + + const Arguments& arguments() const + { + return m_arguments; + } + + template + FeatureMessage& addArgument( T index, const QVariant& value ) + { + m_arguments[QString::number( static_cast( index ) )] = value; + return *this; + } + + template + QVariant argument( T index ) const + { + return m_arguments[QString::number( static_cast( index ) )]; + } + + template + bool hasArgument( T index ) const + { + return m_arguments.contains( QString::number( static_cast( index ) ) ); + } + + bool send( QIODevice* ioDevice ) const; + + bool isReadyForReceive( QIODevice* ioDevice ); + + bool receive( QIODevice* ioDevice ); + +private: + FeatureUid m_featureUid; + Command m_command; + Arguments m_arguments; + +} ; diff --git a/core/include/FeatureProviderInterface.h b/core/include/FeatureProviderInterface.h new file mode 100644 index 0000000..78c04c5 --- /dev/null +++ b/core/include/FeatureProviderInterface.h @@ -0,0 +1,179 @@ +/* + * FeatureProviderInterface.h - interface class for feature plugins + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "ComputerControlInterface.h" +#include "EnumHelper.h" +#include "FeatureMessage.h" +#include "Feature.h" +#include "MessageContext.h" +#include "PluginInterface.h" + +class VeyonMasterInterface; +class VeyonServerInterface; +class VeyonWorkerInterface; + +// clazy:excludeall=copyable-polymorphic + +class VEYON_CORE_EXPORT FeatureProviderInterface +{ + Q_GADGET +public: + enum class Operation + { + Initialize, + Start, + Stop + }; + Q_ENUM(Operation) + + virtual ~FeatureProviderInterface() = default; + + /*! + * \brief Returns a list of features implemented by the feature class + */ + virtual const FeatureList& featureList() const = 0; + + virtual bool hasFeature( Feature::Uid featureUid ) const + { + for( const auto& feature : featureList() ) + { + if( feature.uid() == featureUid ) + { + return true; + } + } + + return false; + } + + template + static QString argToString( T item ) + { + return EnumHelper::toCamelCaseString( item ); + } + + /*! + * \brief Control feature in a generic way based on passed arguments + * \param featureUid the UID of the feature to control + * \param operation the operation to perform for the feature + * \param arguments the arguments specifying what and how to control + * \param computerControlInterfaces a list of ComputerControlInterfaces to operate on + */ + virtual bool controlFeature( Feature::Uid featureUid, + Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) = 0; + + /*! + * \brief Start a feature on master side for given computer control interfaces + * \param master a reference to a master instance implementing the VeyonMasterInterface + * \param feature the feature to start + * \param computerControlInterfaces a list of ComputerControlInterfaces to operate on + */ + virtual bool startFeature( VeyonMasterInterface& master, + const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) + { + Q_UNUSED(master) + + return controlFeature( feature.uid(), Operation::Start, {}, computerControlInterfaces ); + } + + /*! + * \brief Stops a feature on master side for given computer control interfaces + * \param master a reference to a master instance implementing the VeyonMasterInterface + * \param feature the feature to stop + * \param computerControlInterfaces a list of ComputerControlInterfaces to operate on + */ + virtual bool stopFeature( VeyonMasterInterface& master, + const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) + { + Q_UNUSED(master) + + return controlFeature( feature.uid(), Operation::Stop, {}, computerControlInterfaces ); + } + + /*! + * \brief Handles a received feature message received on a certain ComputerControlInterface + * \param message the message which has been received and needs to be handled + * \param computerControlInterfaces the interface over which the message has been received + */ + virtual bool handleFeatureMessage( ComputerControlInterface::Pointer computerControlInterface , + const FeatureMessage& message ) + { + Q_UNUSED(computerControlInterface) + Q_UNUSED(message) + + return false; + } + + /*! + * \brief Handles a received feature message inside server + * \param server a reference to a server instance implementing the VeyonServerInterface + * \param message the message which has been received and needs to be handled + */ + virtual bool handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) + { + Q_UNUSED(server) + Q_UNUSED(messageContext) + Q_UNUSED(message) + + return false; + } + + /*! + * \brief Handles a received feature message inside worker + * \param worker a reference to a worker instance implementing the VeyonWorkerInterface + * \param message the message which has been received and needs to be handled + */ + virtual bool handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) + { + Q_UNUSED(worker) + Q_UNUSED(message) + + return false; + } + +protected: + void sendFeatureMessage( const FeatureMessage& message, + const ComputerControlInterfaceList& computerControlInterfaces, + bool wake = true ) + { + for( const auto& controlInterface : computerControlInterfaces ) + { + controlInterface->sendFeatureMessage( message, wake ); + } + } + +}; + +using FeatureProviderInterfaceList = QList; + +#define FeatureProviderInterface_iid "io.veyon.Veyon.FeatureProviderInterface" + +Q_DECLARE_INTERFACE(FeatureProviderInterface, FeatureProviderInterface_iid) diff --git a/core/include/FeatureWorkerManager.h b/core/include/FeatureWorkerManager.h new file mode 100644 index 0000000..123c55f --- /dev/null +++ b/core/include/FeatureWorkerManager.h @@ -0,0 +1,83 @@ +/* + * FeatureWorkerManager.h - class for managing feature worker instances + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include "FeatureMessage.h" + +class FeatureManager; +class VeyonServerInterface; + +class VEYON_CORE_EXPORT FeatureWorkerManager : public QObject +{ + Q_OBJECT +public: + FeatureWorkerManager( VeyonServerInterface& server, FeatureManager& featureManager, QObject* parent = nullptr ); + ~FeatureWorkerManager() override; + + bool startManagedSystemWorker( Feature::Uid featureUid ); + bool startUnmanagedSessionWorker( Feature::Uid featureUid ); + + bool stopWorker( Feature::Uid featureUid ); + + void sendMessageToManagedSystemWorker( const FeatureMessage& message ); + void sendMessageToUnmanagedSessionWorker( const FeatureMessage& message ); + + bool isWorkerRunning( Feature::Uid featureUid ); + FeatureUidList runningWorkers(); + +private: + void acceptConnection(); + void processConnection( QTcpSocket* socket ); + void closeConnection( QTcpSocket* socket ); + + void sendMessage( const FeatureMessage& message ); + + void sendPendingMessages(); + + static constexpr auto UnmanagedSessionProcessRetryInterval = 5000; + + VeyonServerInterface& m_server; + FeatureManager& m_featureManager; + QTcpServer m_tcpServer; + + struct Worker + { + QPointer socket; + QPointer process; + QList pendingMessages; + }; + + using WorkerMap = QMap; + WorkerMap m_workers; + + QMutex m_workersMutex; + +} ; diff --git a/core/include/FileSystemBrowser.h b/core/include/FileSystemBrowser.h new file mode 100644 index 0000000..be46258 --- /dev/null +++ b/core/include/FileSystemBrowser.h @@ -0,0 +1,71 @@ +/* + * FileSystemBrowser.h - a wrapper class for easily browsing the file system + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonCore.h" + +class QLineEdit; + +class VEYON_CORE_EXPORT FileSystemBrowser +{ +public: + enum BrowseModes + { + ExistingDirectory, + ExistingFile, + SaveFile, + NumBrowseModes + } ; + using BrowseMode = BrowseModes; + + explicit FileSystemBrowser( BrowseMode m ) : + m_browseMode( m ), + m_expandPath( true ), + m_shrinkPath( true ) + { + } + + void setExpandPath( bool enabled ) + { + m_expandPath = enabled; + } + + void setShrinkPath( bool enabled ) + { + m_shrinkPath = enabled; + } + + QString exec( const QString &path, const QString &title = {}, + const QString &filter = {} ); + void exec( QLineEdit *lineEdit, const QString &title = {}, + const QString &filter = {} ); + + +private: + BrowseMode m_browseMode; + bool m_expandPath; + bool m_shrinkPath; + +} ; diff --git a/core/include/Filesystem.h b/core/include/Filesystem.h new file mode 100644 index 0000000..25da03f --- /dev/null +++ b/core/include/Filesystem.h @@ -0,0 +1,50 @@ +/* + * Filesystem.h - filesystem related query and manipulation functions + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonCore.h" + +// clazy:excludeall=rule-of-three + +class VEYON_CORE_EXPORT Filesystem : public QObject +{ + Q_OBJECT +public: + QString expandPath( QString path ) const; + QString shrinkPath( QString path ) const; + bool ensurePathExists( const QString &path ) const; + + QString privateKeyPath( const QString& name ) const; + QString publicKeyPath( const QString& name ) const; + + QString screenshotDirectoryPath() const; + + QString serverFilePath() const; + QString workerFilePath() const; + +Q_SIGNALS: + void screenshotDirectoryModified(); + +}; diff --git a/core/include/HashList.h b/core/include/HashList.h new file mode 100644 index 0000000..f8af96f --- /dev/null +++ b/core/include/HashList.h @@ -0,0 +1,39 @@ +/* + * HashList.h - extended QHash acting like a list + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +template +class HashList : public QHash +{ +public: + void append( const Key& key ) + { + this->insert( key, true ); + } + +}; + diff --git a/core/include/HostAddress.h b/core/include/HostAddress.h new file mode 100644 index 0000000..1db2aa0 --- /dev/null +++ b/core/include/HostAddress.h @@ -0,0 +1,71 @@ +/* + * HostAddress.h - header for HostAddress class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonCore.h" + +class VEYON_CORE_EXPORT HostAddress +{ + Q_GADGET +public: + enum class Type { + Invalid, + IpAddress, + HostName, + FullyQualifiedDomainName + }; + Q_ENUM(Type) + + explicit HostAddress( const QString& address ); + ~HostAddress() = default; + + Type type() const + { + return m_type; + } + + bool isLocalHost() const; + + QString convert( Type targetType ) const; + QString tryConvert( Type targetType ) const; + + QStringList lookupIpAddresses() const; + + static QString parseHost( const QString& address ); + static int parsePortNumber( const QString& address ); + + static QString localFQDN(); + +private: + static Type determineType( const QString& address ); + static QString toIpAddress( const QString& hostName ); + static QString toHostName( Type type, const QString& address ); + static QString toFQDN( Type type, const QString& address ); + static QString fqdnToHostName( const QString& fqdn ); + + Type m_type; + QString m_address; + +} ; diff --git a/core/include/KeyboardShortcutTrapper.h b/core/include/KeyboardShortcutTrapper.h new file mode 100644 index 0000000..f4d9722 --- /dev/null +++ b/core/include/KeyboardShortcutTrapper.h @@ -0,0 +1,56 @@ +/* + * KeyboardShortcutTrapper.h - class for trapping system-wide keyboard shortcuts + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonCore.h" + +class VEYON_CORE_EXPORT KeyboardShortcutTrapper : public QObject +{ + Q_OBJECT +public: + enum Shortcut + { + NoShortcut, + AltTab, + AltEsc, + AltSpace, + AltF4, + CtrlEsc, + SuperKeyDown, + SuperKeyUp + } ; + Q_ENUM(Shortcut) + + explicit KeyboardShortcutTrapper( QObject* parent = nullptr ) : + QObject( parent ) + { + } + + virtual void setEnabled( bool on ) = 0; + +Q_SIGNALS: + void shortcutTrapped( KeyboardShortcutTrapper::Shortcut ); + +} ; diff --git a/core/include/LockWidget.h b/core/include/LockWidget.h new file mode 100644 index 0000000..2b88b74 --- /dev/null +++ b/core/include/LockWidget.h @@ -0,0 +1,53 @@ +/* + * LockWidget.h - widget for locking a client + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#pragma once + +#include +#include + +#include "VeyonCore.h" + + +class VEYON_CORE_EXPORT LockWidget : public QWidget +{ + Q_OBJECT +public: + enum Mode + { + DesktopVisible, + BackgroundPixmap, + NoBackground + } ; + + explicit LockWidget( Mode mode, const QPixmap& background = QPixmap(), QWidget* parent = nullptr ); + ~LockWidget() override; + + +private: + void paintEvent( QPaintEvent * ) override; + + QPixmap m_background; + Mode m_mode; + +} ; diff --git a/core/include/Lockable.h b/core/include/Lockable.h new file mode 100644 index 0000000..09de5cb --- /dev/null +++ b/core/include/Lockable.h @@ -0,0 +1,44 @@ +/* + * Lockable.h - header file for Lockable + * + * Copyright (c) 2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#pragma once + +#include + +class Lockable +{ +public: + virtual void lock() + { + m_mutex.lock(); + } + + virtual void unlock() + { + m_mutex.unlock(); + } + +private: + QMutex m_mutex{QMutex::Recursive}; + +}; diff --git a/core/include/LockingPointer.h b/core/include/LockingPointer.h new file mode 100644 index 0000000..8def73c --- /dev/null +++ b/core/include/LockingPointer.h @@ -0,0 +1,62 @@ +/* + * LockingPointer.h - smart pointer for lockables + * + * Copyright (c) 2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#pragma once + +template +class LockingPointer +{ +public: + LockingPointer( T lockable ) : m_lockable( lockable ) + { + if( m_lockable ) + { + m_lockable->lock(); + } + } + + ~LockingPointer() + { + if( m_lockable ) + { + m_lockable->unlock(); + } + } + + LockingPointer( LockingPointer&& lockingPointer ) : m_lockable( lockingPointer.m_lockable ) + { + lockingPointer.m_lockable = nullptr; + } + + LockingPointer( LockingPointer const& ) = delete; + LockingPointer& operator=( LockingPointer const& ) = delete; + + T operator->() const + { + return m_lockable; + } + +private: + T m_lockable; +}; + diff --git a/core/include/Logger.h b/core/include/Logger.h new file mode 100644 index 0000000..187bbd2 --- /dev/null +++ b/core/include/Logger.h @@ -0,0 +1,104 @@ +/* + * Logger.h - a global clas for easily logging messages to log files + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "VeyonCore.h" + +class QFile; + +// clazy:excludeall=rule-of-three + +class VEYON_CORE_EXPORT Logger +{ + Q_GADGET +public: + enum class LogLevel + { + Nothing, + Critical, + Error, + Warning, + Info, + Debug, + NumLogLevels, + Min = Nothing+1, + Max = NumLogLevels-1, + Default = Warning + }; + Q_ENUM(LogLevel) + + static constexpr int DefaultFileSizeLimit = 100; + static constexpr int DefaultFileRotationCount = 10; + static constexpr int MaximumMessageSize = 1000; + static constexpr const char* DefaultLogFileDirectory = "$TEMP"; + + explicit Logger( const QString &appName ); + ~Logger(); + + static const char* logLevelEnvironmentVariable() + { + return "VEYON_LOG_LEVEL"; + } + + LogLevel logLevel() const + { + return m_logLevel; + } + + +private: + void initLogFile(); + void openLogFile(); + void closeLogFile(); + void clearLogFile(); + void rotateLogFile(); + + void log( LogLevel logLevel, const QString& message ); + void outputMessage( const QString& message ); + + static QString formatMessage( LogLevel ll, const QString &msg ); + static void qtMsgHandler( QtMsgType msgType, const QMessageLogContext &, const QString& msg ); + + static QAtomicPointer s_instance; + static QMutex s_instanceMutex; + + LogLevel m_logLevel; + QMutex m_logMutex; + + LogLevel m_lastMessageLevel; + QString m_lastMessage; + int m_lastMessageCount; + bool m_logToSystem; + + QString m_appName; + + QFile *m_logFile; + int m_logFileSizeLimit; + int m_logFileRotationCount; + +} ; diff --git a/core/include/MessageContext.h b/core/include/MessageContext.h new file mode 100644 index 0000000..17ac0da --- /dev/null +++ b/core/include/MessageContext.h @@ -0,0 +1,53 @@ +/* + * MessageContext.h - header for transporting context for message I/O + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "VeyonCore.h" + +class QIODevice; + +class VEYON_CORE_EXPORT MessageContext +{ +public: + using IODevice = QPointer; + + explicit MessageContext( QIODevice* ioDevice ) : + m_ioDevice( ioDevice ) + { + } + + ~MessageContext() = default; + + QIODevice* ioDevice() const + { + return m_ioDevice; + } + +private: + IODevice m_ioDevice; + +} ; diff --git a/core/include/MonitoringMode.h b/core/include/MonitoringMode.h new file mode 100644 index 0000000..e7d4c7a --- /dev/null +++ b/core/include/MonitoringMode.h @@ -0,0 +1,117 @@ +/* + * MonitoringMode.h - header for the MonitoringMode class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "FeatureProviderInterface.h" + +class VEYON_CORE_EXPORT MonitoringMode : public QObject, FeatureProviderInterface, PluginInterface +{ + Q_OBJECT + Q_INTERFACES(FeatureProviderInterface PluginInterface) +public: + enum class Argument + { + UserLoginName, + UserFullName, + UserSessionId + }; + Q_ENUM(Argument) + + explicit MonitoringMode( QObject* parent = nullptr ); + + const Feature& feature() const + { + return m_monitoringModeFeature; + } + + Plugin::Uid uid() const override + { + return QStringLiteral("1a6a59b1-c7a1-43cc-bcab-c136a4d91be8"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 2 ); + } + + QString name() const override + { + return QStringLiteral( "MonitoringMode" ); + } + + QString description() const override + { + return tr( "Builtin monitoring mode" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + const FeatureList& featureList() const override + { + return m_features; + } + + void queryLoggedOnUserInfo( const ComputerControlInterfaceList& computerControlInterfaces ); + + bool controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) override + { + Q_UNUSED(featureUid) + Q_UNUSED(operation) + Q_UNUSED(arguments) + Q_UNUSED(computerControlInterfaces) + + return false; + } + + bool handleFeatureMessage( ComputerControlInterface::Pointer computerControlInterface, + const FeatureMessage& message ) override; + + bool handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) override; + + +private: + void queryUserInformation(); + + const Feature m_monitoringModeFeature; + const Feature m_queryLoggedOnUserInfoFeature; + const FeatureList m_features; + + QReadWriteLock m_userDataLock; + QString m_userLoginName; + QString m_userFullName; + int m_userSessionId{0}; + +}; diff --git a/core/include/NetworkObject.h b/core/include/NetworkObject.h new file mode 100644 index 0000000..d38aaf8 --- /dev/null +++ b/core/include/NetworkObject.h @@ -0,0 +1,163 @@ +/* + * NetworkObject.h - data class representing a network object + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "HashList.h" +#include "VeyonCore.h" + +class VEYON_CORE_EXPORT NetworkObject +{ + Q_GADGET +public: + using Uid = QUuid; + using Name = QString; + using ModelId = quintptr; + + enum class Type + { + None, + Root, + Location, + Host, + Label, + DesktopGroup + } ; + Q_ENUM(Type) + + enum class Attribute + { + None, + Type, + Name, + HostAddress, + MacAddress, + DirectoryAddress, + Uid, + ParentUid + }; + Q_ENUM(Attribute) + + NetworkObject( const NetworkObject& other ); + explicit NetworkObject( Type type = Type::None, + const Name& name = {}, + const QString& hostAddress = {}, + const QString& macAddress = {}, + const QString& directoryAddress = {}, + Uid uid = {}, + Uid parentUid = {} ); + explicit NetworkObject( const QJsonObject& jsonObject ); + ~NetworkObject() = default; + + NetworkObject& operator=( const NetworkObject& other ); + + bool operator==( const NetworkObject& other ) const; + bool exactMatch( const NetworkObject& other ) const; + + bool isValid() const + { + return type() != Type::None; + } + + bool isPopulated() const + { + return m_populated; + } + + void setPopulated() + { + m_populated = true; + } + + const Uid& uid() const + { + return m_uid; + } + + const Uid& parentUid() const + { + return m_parentUid; + } + + void setParentUid( Uid parentUid ) + { + m_parentUid = parentUid; + } + + ModelId modelId() const; + + Type type() const + { + return m_type; + } + + const Name& name() const + { + return m_name; + } + + const QString& hostAddress() const + { + return m_hostAddress; + } + + const QString& macAddress() const + { + return m_macAddress; + } + + const QString& directoryAddress() const + { + return m_directoryAddress; + } + + QJsonObject toJson() const; + + QVariant attributeValue( Attribute attribute ) const; + + bool isAttributeValueEqual( Attribute attribute, const QVariant& value, Qt::CaseSensitivity cs ) const; + +private: + Uid calculateUid() const; + + Type m_type; + QString m_name; + QString m_hostAddress; + QString m_macAddress; + QString m_directoryAddress; + Uid m_uid; + Uid m_parentUid; + bool m_populated; + + static const QUuid networkObjectNamespace; + +}; + +Q_DECLARE_METATYPE(NetworkObject::Type) + +using NetworkObjectList = QVector; +using NetworkObjectUidList = HashList; diff --git a/core/include/NetworkObjectDirectory.h b/core/include/NetworkObjectDirectory.h new file mode 100644 index 0000000..d996a94 --- /dev/null +++ b/core/include/NetworkObjectDirectory.h @@ -0,0 +1,88 @@ +/* + * NetworkObjectDirectory.h - base class for network object directory implementations + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "NetworkObject.h" + +class QTimer; + +class VEYON_CORE_EXPORT NetworkObjectDirectory : public QObject +{ + Q_OBJECT +public: + enum { + MinimumUpdateInterval = 10, + DefaultUpdateInterval = 60, + MaximumUpdateInterval = 3600 + }; + + explicit NetworkObjectDirectory( QObject* parent ); + + void setUpdateInterval( int interval ); + + const NetworkObjectList& objects( const NetworkObject& parent ) const; + + const NetworkObject& object( NetworkObject::ModelId parent, NetworkObject::ModelId object ) const; + int index( NetworkObject::ModelId parent, NetworkObject::ModelId child ) const; + int childCount( NetworkObject::ModelId parent ) const; + NetworkObject::ModelId childId( NetworkObject::ModelId parent, int index ) const; + NetworkObject::ModelId parentId( NetworkObject::ModelId child ) const; + + NetworkObject::ModelId rootId() const; + + virtual NetworkObjectList queryObjects( NetworkObject::Type type, + NetworkObject::Attribute attribute, const QVariant& value ); + virtual NetworkObjectList queryParents( const NetworkObject& child ); + + virtual void update() = 0; + virtual void fetchObjects( const NetworkObject& object ); + +protected: + using NetworkObjectFilter = std::function; + + bool hasObjects() const; + void addOrUpdateObject( const NetworkObject& networkObject, const NetworkObject& parent ); + void removeObjects( const NetworkObject& parent, const NetworkObjectFilter& removeObjectFilter ); + void replaceObjects( const NetworkObjectList& objects, const NetworkObject& parent ); + void setObjectPopulated( const NetworkObject& networkObject ); + +private: + QTimer* m_updateTimer; + QHash m_objects; + NetworkObject m_invalidObject; + NetworkObject m_rootObject; + NetworkObjectList m_defaultObjectList; + +Q_SIGNALS: + void objectsAboutToBeInserted( const NetworkObject& parent, int index, int count ); + void objectsInserted(); + void objectsAboutToBeRemoved( const NetworkObject& parent, int index, int count ); + void objectsRemoved(); + void objectChanged( const NetworkObject& parent, int index ); + +}; diff --git a/core/include/NetworkObjectDirectoryManager.h b/core/include/NetworkObjectDirectoryManager.h new file mode 100644 index 0000000..7c4f68c --- /dev/null +++ b/core/include/NetworkObjectDirectoryManager.h @@ -0,0 +1,50 @@ +/* + * NetworkObjectDirectoryManager.h - header file for NetworkObjectDirectoryManager + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonCore.h" +#include "Plugin.h" + +class NetworkObjectDirectory; +class NetworkObjectDirectoryPluginInterface; +class PluginInterface; + +class VEYON_CORE_EXPORT NetworkObjectDirectoryManager : public QObject +{ + Q_OBJECT +public: + explicit NetworkObjectDirectoryManager( QObject* parent = nullptr ); + + QMap availableDirectories(); + + NetworkObjectDirectory* createDirectory( Plugin::Uid uid, QObject* parent ); + + NetworkObjectDirectory* configuredDirectory(); + +private: + QMap m_directoryPluginInterfaces{}; + NetworkObjectDirectory* m_configuredDirectory{nullptr}; + +}; diff --git a/core/include/NetworkObjectDirectoryPluginInterface.h b/core/include/NetworkObjectDirectoryPluginInterface.h new file mode 100644 index 0000000..1532da1 --- /dev/null +++ b/core/include/NetworkObjectDirectoryPluginInterface.h @@ -0,0 +1,46 @@ +/* + * NetworkObjectDirectoryPluginInterface.h - plugin interface for network + * object directory implementations + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PluginInterface.h" + +class NetworkObjectDirectory; + +// clazy:excludeall=copyable-polymorphic + +class NetworkObjectDirectoryPluginInterface +{ +public: + virtual QString directoryName() const = 0; + virtual NetworkObjectDirectory* createNetworkObjectDirectory( QObject* parent ) = 0; + +}; + +using NetworkObjectDirectoryPluginInterfaceList = QList; + +#define NetworkObjectDirectoryPluginInterface_iid "io.veyon.Veyon.Plugins.NetworkObjectPluginInterface" + +Q_DECLARE_INTERFACE(NetworkObjectDirectoryPluginInterface, NetworkObjectDirectoryPluginInterface_iid) diff --git a/core/include/NetworkObjectModel.h b/core/include/NetworkObjectModel.h new file mode 100644 index 0000000..7fb94b5 --- /dev/null +++ b/core/include/NetworkObjectModel.h @@ -0,0 +1,52 @@ +/* + * NetworkObjectModel.h - base class for data models providing grouped network objects + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "NetworkObject.h" + +class VEYON_CORE_EXPORT NetworkObjectModel : public QAbstractItemModel +{ + Q_OBJECT +public: + explicit NetworkObjectModel( QObject* parent = nullptr) : + QAbstractItemModel( parent ) + { + } + + enum Role + { + CheckStateRole = Qt::CheckStateRole, + NameRole = Qt::DisplayRole, + UidRole = Qt::UserRole, + TypeRole, + HostAddressRole, + MacAddressRole, + DirectoryAddressRole, + ParentUidRole + } ; + +}; diff --git a/core/include/ObjectManager.h b/core/include/ObjectManager.h new file mode 100644 index 0000000..56339d6 --- /dev/null +++ b/core/include/ObjectManager.h @@ -0,0 +1,146 @@ +/* + * ObjectManager.h - header file for ObjectManager + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "VeyonCore.h" + +template +class ObjectManager +{ +public: + explicit ObjectManager( const QJsonArray& objects ) : + m_objects( objects ) + { + } + + const QJsonArray& objects() const + { + return m_objects; + } + + const QJsonArray& add( const T& object ) + { + m_objects.append( object.toJson() ); + + return m_objects; + } + + const QJsonArray& update( const T& object, bool addIfNotFound = false ) + { + bool found = false; + + for( auto it = m_objects.begin(); it != m_objects.end(); ++it ) + { + T currentObject( it->toObject() ); + if( currentObject.uid() == object.uid() ) + { + *it = object.toJson(); + found = true; + break; + } + } + + if( !found ) + { + add( object ); + } + + return m_objects; + } + + const QJsonArray& remove( typename T::Uid objectUid, bool recursive = false ) + { + if( recursive ) + { + removeChildren( objectUid ); + } + + for( auto it = m_objects.begin(); it != m_objects.end(); ) + { + T currentObject( it->toObject() ); + if( currentObject.uid() == objectUid ) + { + it = m_objects.erase( it ); + } + else + { + ++it; + } + } + + return m_objects; + } + + void removeChildren( typename T::Uid objectUid ) + { + for( auto it = m_objects.begin(); it != m_objects.end(); ) + { + T currentObject( it->toObject() ); + if( currentObject.parentUid() == objectUid ) + { + removeChildren( currentObject.uid() ); + it = m_objects.erase( it ); + } + else + { + ++it; + } + } + } + + T findByUid( typename T::Uid uid ) const + { + for( auto it = m_objects.constBegin(); it != m_objects.constEnd(); ++it ) + { + T currentObject( it->toObject() ); + if( currentObject.uid() == uid ) + { + return currentObject; + } + } + + return T(); + } + + T findByName( const typename T::Name& name ) const + { + for( auto it = m_objects.constBegin(); it != m_objects.constEnd(); ++it ) + { + T currentObject( it->toObject() ); + if( T( it->toObject() ).name() == name ) + { + return currentObject; + } + } + + return T(); + } + +private: + QJsonArray m_objects; + +}; diff --git a/core/include/PasswordDialog.h b/core/include/PasswordDialog.h new file mode 100644 index 0000000..ed2f105 --- /dev/null +++ b/core/include/PasswordDialog.h @@ -0,0 +1,54 @@ +/* + * PasswordDialog.h - declaration of password dialog + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "AuthenticationCredentials.h" + +#include + +namespace Ui { class PasswordDialog; } + +class VEYON_CORE_EXPORT PasswordDialog : public QDialog +{ + Q_OBJECT +public: + explicit PasswordDialog( QWidget *parent ); + ~PasswordDialog() override; + + QString username() const; + CryptoCore::PlaintextPassword password() const; + + AuthenticationCredentials credentials() const; + + void accept() override; + +private Q_SLOTS: + void updateOkButton(); + + +private: + Ui::PasswordDialog *ui; + +} ; diff --git a/core/include/PlatformCoreFunctions.h b/core/include/PlatformCoreFunctions.h new file mode 100644 index 0000000..688c3ed --- /dev/null +++ b/core/include/PlatformCoreFunctions.h @@ -0,0 +1,66 @@ +/* + * PlatformCoreFunctions.h - interface class for platform plugins + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "Logger.h" +#include "PlatformPluginInterface.h" + +// clazy:excludeall=copyable-polymorphic + +class PlatformCoreFunctions +{ +public: + virtual ~PlatformCoreFunctions() = default; + + virtual bool applyConfiguration() = 0; + + virtual void initNativeLoggingSystem( const QString& appName ) = 0; + virtual void writeToNativeLoggingSystem( const QString& message, Logger::LogLevel loglevel ) = 0; + + virtual void reboot() = 0; + virtual void powerDown( bool installUpdates ) = 0; + + virtual void raiseWindow( QWidget* widget, bool stayOnTop ) = 0; + + virtual void disableScreenSaver() = 0; + virtual void restoreScreenSaverSettings() = 0; + + virtual void setSystemUiState( bool enabled ) = 0; + + virtual QString activeDesktopName() = 0; + + virtual bool isRunningAsAdmin() const = 0; + virtual bool runProgramAsAdmin( const QString& program, const QStringList& parameters ) = 0; + + virtual bool runProgramAsUser( const QString& program, + const QStringList& parameters, + const QString& username, + const QString& desktop ) = 0; + + virtual QString genericUrlHandler() const = 0; + +}; diff --git a/core/include/PlatformFilesystemFunctions.h b/core/include/PlatformFilesystemFunctions.h new file mode 100644 index 0000000..f58fff8 --- /dev/null +++ b/core/include/PlatformFilesystemFunctions.h @@ -0,0 +1,46 @@ +/* + * PlatformFilesystemFunctions.h - interface class for platform filesystem + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "PlatformPluginInterface.h" + +// clazy:excludeall=copyable-polymorphic + +class PlatformFilesystemFunctions +{ +public: + virtual QString personalAppDataPath() const = 0; + virtual QString globalAppDataPath() const = 0; + virtual QString globalTempPath() const = 0; + + virtual QString fileOwnerGroup( const QString& filePath ) = 0; + virtual bool setFileOwnerGroup( const QString& filePath, const QString& ownerGroup ) = 0; + virtual bool setFileOwnerGroupPermissions( const QString& filePath, QFile::Permissions permissions ) = 0; + + virtual bool openFileSafely( QFile* file, QFile::OpenMode openMode, QFile::Permissions permissions ) = 0; + +}; diff --git a/core/include/PlatformInputDeviceFunctions.h b/core/include/PlatformInputDeviceFunctions.h new file mode 100644 index 0000000..20df944 --- /dev/null +++ b/core/include/PlatformInputDeviceFunctions.h @@ -0,0 +1,41 @@ +/* + * PlatformInputDeviceFunctions.h - interface class for platform plugins + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PlatformPluginInterface.h" + +// clazy:excludeall=copyable-polymorphic + +class KeyboardShortcutTrapper; + +class PlatformInputDeviceFunctions +{ +public: + virtual void enableInputDevices() = 0; + virtual void disableInputDevices() = 0; + + virtual KeyboardShortcutTrapper* createKeyboardShortcutTrapper( QObject* parent ) = 0; + +}; diff --git a/core/include/PlatformNetworkFunctions.h b/core/include/PlatformNetworkFunctions.h new file mode 100644 index 0000000..b3c43c2 --- /dev/null +++ b/core/include/PlatformNetworkFunctions.h @@ -0,0 +1,48 @@ +/* + * PlatformNetworkFunctions.h - interface class for platform plugins + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PlatformPluginInterface.h" + +// clazy:excludeall=copyable-polymorphic + +class PlatformNetworkFunctions +{ +public: + using Socket = uintptr_t; + + enum { + PingTimeout = 1000, + PingProcessTimeout = PingTimeout*2 + }; + + virtual ~PlatformNetworkFunctions() = default; + + virtual bool ping( const QString& hostAddress ) = 0; + virtual bool configureFirewallException( const QString& applicationPath, const QString& description, bool enabled ) = 0; + + virtual bool configureSocketKeepalive( Socket socket, bool enabled, int idleTime, int interval, int probes ) = 0; + +}; diff --git a/core/include/PlatformPluginInterface.h b/core/include/PlatformPluginInterface.h new file mode 100644 index 0000000..13afb75 --- /dev/null +++ b/core/include/PlatformPluginInterface.h @@ -0,0 +1,56 @@ +/* + * PlatformPluginInterface.h - interface class for platform plugins + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PluginInterface.h" + +class PlatformCoreFunctions; +class PlatformFilesystemFunctions; +class PlatformInputDeviceFunctions; +class PlatformNetworkFunctions; +class PlatformServiceFunctions; +class PlatformSessionFunctions; +class PlatformUserFunctions; + +// clazy:excludeall=copyable-polymorphic + +class PlatformPluginInterface +{ +public: + virtual PlatformCoreFunctions& coreFunctions() = 0; + virtual PlatformFilesystemFunctions& filesystemFunctions() = 0; + virtual PlatformInputDeviceFunctions& inputDeviceFunctions() = 0; + virtual PlatformNetworkFunctions& networkFunctions() = 0; + virtual PlatformServiceFunctions& serviceFunctions() = 0; + virtual PlatformSessionFunctions& sessionFunctions() = 0; + virtual PlatformUserFunctions& userFunctions() = 0; + +}; + +using PlatformPluginInterfaceList = QList; + +#define PlatformPluginInterface_iid "io.veyon.Veyon.Plugins.PlatformPluginInterface" + +Q_DECLARE_INTERFACE(PlatformPluginInterface, PlatformPluginInterface_iid) diff --git a/core/include/PlatformPluginManager.h b/core/include/PlatformPluginManager.h new file mode 100644 index 0000000..7c8b0ab --- /dev/null +++ b/core/include/PlatformPluginManager.h @@ -0,0 +1,43 @@ +/* + * PlatformPluginManager.h - header file for PlatformPluginManager + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PlatformPluginInterface.h" + +class VEYON_CORE_EXPORT PlatformPluginManager : public QObject +{ + Q_OBJECT +public: + explicit PlatformPluginManager( PluginManager& pluginManager, QObject* parent = nullptr ); + + PlatformPluginInterface* platformPlugin() + { + return m_platformPlugin; + } + +private: + PlatformPluginInterface* m_platformPlugin; + +}; diff --git a/core/include/PlatformServiceFunctions.h b/core/include/PlatformServiceFunctions.h new file mode 100644 index 0000000..ee20158 --- /dev/null +++ b/core/include/PlatformServiceFunctions.h @@ -0,0 +1,57 @@ +/* + * PlatformServiceFunctions.h - interface class for platform plugins + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PlatformPluginInterface.h" + +// clazy:excludeall=copyable-polymorphic + +class PlatformServiceFunctions +{ +public: + using ServiceEntryPoint = std::function; + + enum class StartMode { + Disabled, + Manual, + Auto + } ; + + virtual ~PlatformServiceFunctions() = default; + + virtual QString veyonServiceName() const = 0; + + virtual bool isRegistered( const QString& name ) = 0; + virtual bool isRunning( const QString& name ) = 0; + virtual bool start( const QString& name ) = 0; + virtual bool stop( const QString& name ) = 0; + virtual bool install( const QString& name, const QString& filePath, + StartMode startMode, const QString& displayName ) = 0; + virtual bool uninstall( const QString& name ) = 0; + virtual bool setStartMode( const QString& name, StartMode startMode ) = 0; + virtual bool runAsService( const QString& name, const ServiceEntryPoint& serviceEntryPoint ) = 0; + virtual void manageServerInstances() = 0; + +}; diff --git a/core/include/PlatformSessionFunctions.h b/core/include/PlatformSessionFunctions.h new file mode 100644 index 0000000..4001f93 --- /dev/null +++ b/core/include/PlatformSessionFunctions.h @@ -0,0 +1,45 @@ +/* + * PlatformSessionsFunctions.h - interface class for platform session functions + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PlatformPluginInterface.h" + +// clazy:excludeall=copyable-polymorphic + +class PlatformSessionFunctions +{ +public: + using SessionId = int; + + static constexpr SessionId DefaultSessionId = 0; + static constexpr SessionId InvalidSessionId = -1; + + virtual ~PlatformSessionFunctions() = default; + + virtual SessionId currentSessionId() = 0; + + virtual QString currentSessionType() const = 0; + +}; diff --git a/core/include/PlatformUserFunctions.h b/core/include/PlatformUserFunctions.h new file mode 100644 index 0000000..8813dcf --- /dev/null +++ b/core/include/PlatformUserFunctions.h @@ -0,0 +1,53 @@ +/* + * PlatformUserFunctions.h - interface class for platform plugins + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CryptoCore.h" +#include "PlatformPluginInterface.h" + +// clazy:excludeall=copyable-polymorphic + +class PlatformUserFunctions +{ +public: + using Password = CryptoCore::SecureArray; + + virtual ~PlatformUserFunctions() = default; + + virtual QString fullName( const QString& username ) = 0; + + virtual QStringList userGroups( bool queryDomainGroups ) = 0; + virtual QStringList groupsOfUser( const QString& username, bool queryDomainGroups ) = 0; + + virtual bool isAnyUserLoggedOn() = 0; + virtual QString currentUser() = 0; + + virtual bool prepareLogon( const QString& username, const Password& password ) = 0; + virtual bool performLogon( const QString& username, const Password& password ) = 0; + virtual void logoff() = 0; + + virtual bool authenticate( const QString& username, const Password& password ) = 0; + +}; diff --git a/core/include/Plugin.h b/core/include/Plugin.h new file mode 100644 index 0000000..2aa1303 --- /dev/null +++ b/core/include/Plugin.h @@ -0,0 +1,49 @@ +/* + * Plugin.h - generic abstraction of a plugin + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include + +class Plugin +{ + Q_GADGET +public: + using Uid = QUuid; + + enum PluginFlags + { + NoFlags, + ProvidesDefaultImplementation = 0x0001, + RequiresLicensing = 0x002, + } ; + + Q_DECLARE_FLAGS(Flags, PluginFlags) + Q_FLAG(Flags) + +}; + +using PluginUidList = QVector; diff --git a/core/include/PluginInterface.h b/core/include/PluginInterface.h new file mode 100644 index 0000000..23311d0 --- /dev/null +++ b/core/include/PluginInterface.h @@ -0,0 +1,58 @@ +/* + * PluginInterface.h - interface class for plugins + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "VeyonCore.h" +#include "Plugin.h" + +// clazy:excludeall=copyable-polymorphic + +class VEYON_CORE_EXPORT PluginInterface +{ +public: + virtual Plugin::Uid uid() const = 0; + virtual QVersionNumber version() const = 0; + virtual QString name() const = 0; + virtual QString description() const = 0; + virtual QString vendor() const = 0; + virtual QString copyright() const = 0; + virtual Plugin::Flags flags() const + { + return Plugin::NoFlags; + } + virtual void upgrade( const QVersionNumber& oldVersion ) + { + Q_UNUSED(oldVersion) + } + +}; + +using PluginInterfaceList = QList; + +#define PluginInterface_iid "io.veyon.Veyon.Plugins.PluginInterface" + +Q_DECLARE_INTERFACE(PluginInterface, PluginInterface_iid) diff --git a/core/include/PluginManager.h b/core/include/PluginManager.h new file mode 100644 index 0000000..b8e81d7 --- /dev/null +++ b/core/include/PluginManager.h @@ -0,0 +1,72 @@ +/* + * PluginManager.h - header for the PluginManager class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "Plugin.h" +#include "PluginInterface.h" + +class QPluginLoader; + +class VEYON_CORE_EXPORT PluginManager : public QObject +{ + Q_OBJECT +public: + explicit PluginManager( QObject* parent = nullptr ); + ~PluginManager(); + + void loadPlatformPlugins(); + void loadPlugins(); + void upgradePlugins(); + + const PluginInterfaceList& pluginInterfaces() const + { + return m_pluginInterfaces; + } + + const QObjectList& pluginObjects() const + { + return m_pluginObjects; + } + + void registerExtraPluginInterface( QObject* pluginObject ); + + PluginUidList pluginUids() const; + + PluginInterface* pluginInterface( Plugin::Uid pluginUid ); + + QString pluginName( Plugin::Uid pluginUid ) const; + +private: + void initPluginSearchPath(); + void loadPlugins( const QString& nameFilter ); + + PluginInterfaceList m_pluginInterfaces; + QObjectList m_pluginObjects; + QList m_pluginLoaders; + bool m_noDebugMessages; + +}; diff --git a/core/include/ProcessHelper.h b/core/include/ProcessHelper.h new file mode 100644 index 0000000..7944f6d --- /dev/null +++ b/core/include/ProcessHelper.h @@ -0,0 +1,43 @@ +/* + * ProcessHelper.h - header file for ProcessHelper + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "VeyonCore.h" + +class VEYON_CORE_EXPORT ProcessHelper { +public: + ProcessHelper( const QString& program, const QStringList& arguments ); + + int run(); + QByteArray runAndReadAll(); + + static bool waitForProcess( QProcess* process, int timeout, int sleepInterval ); + +private: + QProcess m_process; + +}; diff --git a/core/include/ProgressWidget.h b/core/include/ProgressWidget.h new file mode 100644 index 0000000..fd06360 --- /dev/null +++ b/core/include/ProgressWidget.h @@ -0,0 +1,49 @@ +/* + * ProgressWidget.h - widget with animated progress indicator + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#pragma once + +#include +#include +#include + +class ProgressWidget : public QWidget +{ + Q_OBJECT +public: + ProgressWidget( const QString& text, const QString& animationPixmapBase, int frames, QWidget* parent = nullptr ); + ~ProgressWidget() override = default; + +protected: + void paintEvent( QPaintEvent* event ) override; + +private: + void nextFrame(); + + QString m_text; + int m_frames; + int m_curFrame; + + QVector m_pixmaps; + +} ; diff --git a/core/include/QtCompat.h b/core/include/QtCompat.h new file mode 100644 index 0000000..67d2b9e --- /dev/null +++ b/core/include/QtCompat.h @@ -0,0 +1,375 @@ +/* + * QtCompat.h - functions and templates for compatibility with older Qt versions + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include +#include + +template +static inline bool intersects( const QSet& a, const QSet& b ) +{ +#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) + return QSet( a ).intersect( b ).isEmpty() == false; +#else + return a.intersects( b ); +#endif +} + +#if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) +#define Q_DISABLE_MOVE(Class) \ + Class(const Class &&) Q_DECL_EQ_DELETE;\ + Class &operator=(const Class &&) Q_DECL_EQ_DELETE; +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) +#include +#else + +// taken from qtbase/src/corelib/tools/qversionnumber.h + +class QVersionNumber +{ + /* + * QVersionNumber stores small values inline, without memory allocation. + * We do that by setting the LSB in the pointer that would otherwise hold + * the longer form of the segments. + * The constants below help us deal with the permutations for 32- and 64-bit, + * little- and big-endian architectures. + */ + enum { + // in little-endian, inline_segments[0] is shared with the pointer's LSB, while + // in big-endian, it's inline_segments[7] + InlineSegmentMarker = Q_BYTE_ORDER == Q_LITTLE_ENDIAN ? 0 : sizeof(void*) - 1, + InlineSegmentStartIdx = !InlineSegmentMarker, // 0 for BE, 1 for LE + InlineSegmentCount = sizeof(void*) - 1 + }; + Q_STATIC_ASSERT(InlineSegmentCount >= 3); // at least major, minor, micro + + struct SegmentStorage { + // Note: we alias the use of dummy and inline_segments in the use of the + // union below. This is undefined behavior in C++98, but most compilers implement + // the C++11 behavior. The one known exception is older versions of Sun Studio. + union { + quintptr dummy; + qint8 inline_segments[sizeof(void*)]; + QVector *pointer_segments; + }; + + // set the InlineSegmentMarker and set length to zero + SegmentStorage() Q_DECL_NOTHROW : dummy(1) {} + + SegmentStorage(const QVector &seg) + { + if (dataFitsInline(seg.begin(), seg.size())) + setInlineData(seg.begin(), seg.size()); + else + pointer_segments = new QVector(seg); + } + + SegmentStorage(const SegmentStorage &other) + { + if (other.isUsingPointer()) + pointer_segments = new QVector(*other.pointer_segments); + else + dummy = other.dummy; + } + + SegmentStorage &operator=(const SegmentStorage &other) + { + if (isUsingPointer() && other.isUsingPointer()) { + *pointer_segments = *other.pointer_segments; + } else if (other.isUsingPointer()) { + pointer_segments = new QVector(*other.pointer_segments); + } else { + if (isUsingPointer()) + delete pointer_segments; + dummy = other.dummy; + } + return *this; + } + +#ifdef Q_COMPILER_RVALUE_REFS + SegmentStorage(SegmentStorage &&other) Q_DECL_NOTHROW + : dummy(other.dummy) + { + other.dummy = 1; + } + + SegmentStorage &operator=(SegmentStorage &&other) Q_DECL_NOTHROW + { + qSwap(dummy, other.dummy); + return *this; + } + + explicit SegmentStorage(QVector &&seg) + { + if (dataFitsInline(seg.begin(), seg.size())) + setInlineData(seg.begin(), seg.size()); + else + pointer_segments = new QVector(std::move(seg)); + } +#endif +#ifdef Q_COMPILER_INITIALIZER_LISTS + SegmentStorage(std::initializer_list args) + { + if (dataFitsInline(args.begin(), int(args.size()))) { + setInlineData(args.begin(), int(args.size())); + } else { + pointer_segments = new QVector(args); + } + } +#endif + + ~SegmentStorage() { if (isUsingPointer()) delete pointer_segments; } + + bool isUsingPointer() const Q_DECL_NOTHROW + { return (inline_segments[InlineSegmentMarker] & 1) == 0; } + + int size() const Q_DECL_NOTHROW + { return isUsingPointer() ? pointer_segments->size() : (inline_segments[InlineSegmentMarker] >> 1); } + + void setInlineSize(int len) + { inline_segments[InlineSegmentMarker] = 1 + 2 * len; } + + void resize(int len) + { + if (isUsingPointer()) + pointer_segments->resize(len); + else + setInlineSize(len); + } + + int at(int index) const + { + return isUsingPointer() ? + pointer_segments->at(index) : + inline_segments[InlineSegmentStartIdx + index]; + } + + void setSegments(int len, int maj, int min = 0, int mic = 0) + { + if (maj == qint8(maj) && min == qint8(min) && mic == qint8(mic)) { + int data[] = { maj, min, mic }; + setInlineData(data, len); + } else { + setVector(len, maj, min, mic); + } + } + + private: + static bool dataFitsInline(const int *data, int len) + { + if (len > InlineSegmentCount) + return false; + for (int i = 0; i < len; ++i) + if (data[i] != qint8(data[i])) + return false; + return true; + } + void setInlineData(const int *data, int len) + { + dummy = 1 + len * 2; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + for (int i = 0; i < len; ++i) + dummy |= quintptr(data[i] & 0xFF) << (8 * (i + 1)); +#elif Q_BYTE_ORDER == Q_BIG_ENDIAN + for (int i = 0; i < len; ++i) + dummy |= quintptr(data[i] & 0xFF) << (8 * (sizeof(void *) - i - 1)); +#else + // the code above is equivalent to: + setInlineSize(len); + for (int i = 0; i < len; ++i) + inline_segments[InlineSegmentStartIdx + i] = data[i] & 0xFF; +#endif + } + + Q_CORE_EXPORT void setVector(int len, int maj, int min, int mic); + } m_segments; + +public: + inline QVersionNumber() Q_DECL_NOTHROW + : m_segments() + {} + inline explicit QVersionNumber(const QVector &seg) + : m_segments(seg) + {} + + // compiler-generated copy/move ctor/assignment operators and the destructor are ok + +#ifdef Q_COMPILER_RVALUE_REFS + explicit QVersionNumber(QVector &&seg) + : m_segments(std::move(seg)) + {} +#endif + +#ifdef Q_COMPILER_INITIALIZER_LISTS + inline QVersionNumber(std::initializer_list args) + : m_segments(args) + {} +#endif + + inline explicit QVersionNumber(int maj) + { m_segments.setSegments(1, maj); } + + inline explicit QVersionNumber(int maj, int min) + { m_segments.setSegments(2, maj, min); } + + inline explicit QVersionNumber(int maj, int min, int mic) + { m_segments.setSegments(3, maj, min, mic); } + + Q_REQUIRED_RESULT inline bool isNull() const Q_DECL_NOTHROW + { return segmentCount() == 0; } + + Q_REQUIRED_RESULT inline bool isNormalized() const Q_DECL_NOTHROW + { return isNull() || segmentAt(segmentCount() - 1) != 0; } + + Q_REQUIRED_RESULT inline int majorVersion() const Q_DECL_NOTHROW + { return segmentAt(0); } + + Q_REQUIRED_RESULT inline int minorVersion() const Q_DECL_NOTHROW + { return segmentAt(1); } + + Q_REQUIRED_RESULT inline int microVersion() const Q_DECL_NOTHROW + { return segmentAt(2); } + + Q_REQUIRED_RESULT Q_CORE_EXPORT QVersionNumber normalized() const; + + Q_REQUIRED_RESULT Q_CORE_EXPORT QVector segments() const; + + Q_REQUIRED_RESULT inline int segmentAt(int index) const Q_DECL_NOTHROW + { return (m_segments.size() > index) ? m_segments.at(index) : 0; } + + Q_REQUIRED_RESULT inline int segmentCount() const Q_DECL_NOTHROW + { return m_segments.size(); } + + Q_REQUIRED_RESULT Q_CORE_EXPORT bool isPrefixOf(const QVersionNumber &other) const Q_DECL_NOTHROW; + + Q_REQUIRED_RESULT Q_CORE_EXPORT static int compare(const QVersionNumber &v1, const QVersionNumber &v2) Q_DECL_NOTHROW; + + Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber commonPrefix(const QVersionNumber &v1, const QVersionNumber &v2); + + Q_REQUIRED_RESULT Q_CORE_EXPORT QString toString() const; + Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(const QString &string, int *suffixIndex = Q_NULLPTR); + +}; + +Q_DECLARE_TYPEINFO(QVersionNumber, Q_MOVABLE_TYPE); + +Q_REQUIRED_RESULT inline bool operator> (const QVersionNumber &lhs, const QVersionNumber &rhs) Q_DECL_NOTHROW +{ return QVersionNumber::compare(lhs, rhs) > 0; } + +Q_REQUIRED_RESULT inline bool operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs) Q_DECL_NOTHROW +{ return QVersionNumber::compare(lhs, rhs) >= 0; } + +Q_REQUIRED_RESULT inline bool operator< (const QVersionNumber &lhs, const QVersionNumber &rhs) Q_DECL_NOTHROW +{ return QVersionNumber::compare(lhs, rhs) < 0; } + +Q_REQUIRED_RESULT inline bool operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs) Q_DECL_NOTHROW +{ return QVersionNumber::compare(lhs, rhs) <= 0; } + +Q_REQUIRED_RESULT inline bool operator==(const QVersionNumber &lhs, const QVersionNumber &rhs) Q_DECL_NOTHROW +{ return QVersionNumber::compare(lhs, rhs) == 0; } + +Q_REQUIRED_RESULT inline bool operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs) Q_DECL_NOTHROW +{ return QVersionNumber::compare(lhs, rhs) != 0; } + +Q_DECLARE_METATYPE(QVersionNumber) +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) +template struct QAddConst { using Type = const T; }; +template constexpr typename QAddConst::Type &qAsConst(T &t) { return t; } +template void qAsConst(const T &&) = delete; + +template +struct QNonConstOverload +{ + template + Q_DECL_CONSTEXPR auto operator()(R (T::*ptr)(Args...)) const Q_DECL_NOTHROW -> decltype(ptr) + { return ptr; } + + template + static Q_DECL_CONSTEXPR auto of(R (T::*ptr)(Args...)) Q_DECL_NOTHROW -> decltype(ptr) + { return ptr; } +}; + +template +struct QConstOverload +{ + template + Q_DECL_CONSTEXPR auto operator()(R (T::*ptr)(Args...) const) const Q_DECL_NOTHROW -> decltype(ptr) + { return ptr; } + + template + static Q_DECL_CONSTEXPR auto of(R (T::*ptr)(Args...) const) Q_DECL_NOTHROW -> decltype(ptr) + { return ptr; } +}; + +template +struct QOverload : QConstOverload, QNonConstOverload +{ + using QConstOverload::of; + using QConstOverload::operator(); + using QNonConstOverload::of; + using QNonConstOverload::operator(); + + template + Q_DECL_CONSTEXPR auto operator()(R (*ptr)(Args...)) const Q_DECL_NOTHROW -> decltype(ptr) + { return ptr; } + + template + static Q_DECL_CONSTEXPR auto of(R (*ptr)(Args...)) Q_DECL_NOTHROW -> decltype(ptr) + { return ptr; } +}; +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 12, 0) +#define QDeadlineTimer(x) static_cast(x) +#else +#include +#endif + + +template +struct QVariantHelper +{ + static T value( const QVariant& variant ) + { + return variant.value(); + } +}; + +#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) +template +struct QVariantHelper::value >::type> +{ + static T value( const QVariant& variant ) + { + return static_cast( variant.toInt() ); + } +}; +#endif diff --git a/core/include/RfbVeyonAuth.h b/core/include/RfbVeyonAuth.h new file mode 100644 index 0000000..bfd6ab9 --- /dev/null +++ b/core/include/RfbVeyonAuth.h @@ -0,0 +1,62 @@ +/* + * RfbVeyonAuth.h - types and names related to veyon-specific RFB + * authentication type + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonCore.h" + +static constexpr char rfbSecTypeVeyon = 40; + +class VEYON_CORE_EXPORT RfbVeyonAuth +{ + Q_GADGET +public: + enum Type + { + // invalid/null authentication type + Invalid, + + // no authentication needed + None, + + // only hosts in an internal whitelist list are allowed + HostWhiteListLegacy, // TODO: drop in VEYON5 + + // client has to sign some data to verify it's authority + KeyFile, + + // authentication is performed using given username and password + Logon, + + // client has to prove its authenticity by knowing common token + Token, + + AuthTypeCount + + } ; + + Q_ENUM(Type) + +}; diff --git a/core/include/Screenshot.h b/core/include/Screenshot.h new file mode 100644 index 0000000..ed745c2 --- /dev/null +++ b/core/include/Screenshot.h @@ -0,0 +1,90 @@ +/* + * Screenshot.h - class representing a screenshot + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#pragma once + +#include "ComputerControlInterface.h" +#include "VeyonCore.h" + +#include +#include + +class ComputerControlInterface; + +class VEYON_CORE_EXPORT Screenshot : public QObject +{ + Q_OBJECT +public: + enum class MetaData + { + User, + Host, + Date, + Time + }; + Q_ENUM(MetaData) + + explicit Screenshot( const QString &fileName = {}, QObject* parent = nullptr ); + + void take( const ComputerControlInterface::Pointer& computerControlInterface ); + + bool isValid() const + { + return !fileName().isEmpty() && !image().isNull(); + } + + const QString& fileName() const + { + return m_fileName; + } + + void setImage( const QImage& image ) + { + m_image = image; + } + + const QImage& image() const + { + return m_image; + } + + static QString constructFileName( const QString& user, const QString& hostAddress, + const QDate& date = QDate::currentDate(), + const QTime& time = QTime::currentTime() ); + + QString user() const; + QString host() const; + QString date() const; + QString time() const; + + static QString metaDataKey( MetaData key ); + +private: + QString property( const QString& key, int section ) const; + QString fileNameSection( int n ) const; + + QString m_fileName; + QImage m_image; + +} ; + diff --git a/core/include/ServiceControl.h b/core/include/ServiceControl.h new file mode 100644 index 0000000..ec5de61 --- /dev/null +++ b/core/include/ServiceControl.h @@ -0,0 +1,66 @@ +/* + * ServiceControl.h - header for the ServiceControl class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "VeyonCore.h" + +class QWidget; + +// clazy:excludeall=ctor-missing-parent-argument + +class VEYON_CORE_EXPORT ServiceControl : public QObject +{ + Q_OBJECT +public: + ServiceControl( const QString& name, + const QString& filePath, + const QString& displayName, + QWidget* parent ); + + bool isServiceRegistered(); + void registerService(); + void unregisterService(); + + bool isServiceRunning(); + + void startService(); + void stopService(); + + +private: + using Operation = QFuture; + void serviceControl( const QString& title, const Operation& operation ); + void graphicalFeedback( const QString &title, const Operation& operation ); + void textFeedback( const QString &title, const Operation& operation ); + + const QString m_name; + const QString m_filePath; + const QString m_displayName; + + QWidget* m_parent; + +}; diff --git a/core/include/SocketDevice.h b/core/include/SocketDevice.h new file mode 100644 index 0000000..ac2e39f --- /dev/null +++ b/core/include/SocketDevice.h @@ -0,0 +1,91 @@ +/* + * SocketDevice.h - SocketDevice abstraction + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +class SocketDevice : public QIODevice +{ + Q_OBJECT +public: + enum SocketOperation + { + SocketOpRead, + SocketOpWrite + } ; + + using Dispatcher = qint64 (*)(char *, const qint64, SocketOperation, void *); + + explicit SocketDevice( Dispatcher dispatcher, void *user = nullptr, QObject* parent = nullptr ) : + QIODevice( parent ), + m_dispatcher( dispatcher ), + m_user( user ) + { + open( ReadWrite | Unbuffered ); // Flawfinder: ignore + } + + Dispatcher dispatcher() + { + return m_dispatcher; + } + + void *user() + { + return m_user; + } + + void setUser( void *user ) + { + m_user = user; + } + + qint64 read( char *buf, qint64 bytes ) // Flawfinder: ignore + { + return readData( buf, bytes ); + } + + qint64 write( const char *buf, qint64 bytes ) + { + return writeData( buf, bytes ); + } + +protected: + qint64 readData( char *buf, qint64 bytes ) override + { + return m_dispatcher( buf, bytes, SocketOpRead, m_user ); + } + + qint64 writeData( const char *buf, qint64 bytes ) override + { + return m_dispatcher( const_cast( buf ), bytes, + SocketOpWrite, m_user ); + } + + +private: + Dispatcher m_dispatcher; + void * m_user; + +} ; diff --git a/core/include/SystemTrayIcon.h b/core/include/SystemTrayIcon.h new file mode 100644 index 0000000..bf5eaab --- /dev/null +++ b/core/include/SystemTrayIcon.h @@ -0,0 +1,119 @@ +/* + * SystemTrayIcon.h - declaration of SystemTrayIcon class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "FeatureProviderInterface.h" + +class QSystemTrayIcon; +class FeatureWorkerManager; + +class VEYON_CORE_EXPORT SystemTrayIcon : public QObject, public FeatureProviderInterface, public PluginInterface +{ + Q_OBJECT + Q_INTERFACES(FeatureProviderInterface PluginInterface) +public: + enum class Argument + { + ToolTipText, + MessageTitle, + MessageText + }; + Q_ENUM(Argument) + + explicit SystemTrayIcon( QObject* parent = nullptr ); + ~SystemTrayIcon() override = default; + + void setToolTip( const QString& toolTipText, + FeatureWorkerManager& featureWorkerManager ); + + void showMessage( const QString& messageTitle, + const QString& messageText, + FeatureWorkerManager& featureWorkerManager ); + + Plugin::Uid uid() const override + { + return QStringLiteral("3cb1adb1-6b4d-4934-a641-db767df83eea"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral( "SystemTrayIcon" ); + } + + QString description() const override + { + return tr( "System tray icon" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + const FeatureList& featureList() const override + { + return m_features; + } + + bool controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) override + { + Q_UNUSED(featureUid) + Q_UNUSED(operation) + Q_UNUSED(arguments) + Q_UNUSED(computerControlInterfaces) + + return false; + } + + bool handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) override; + + bool handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) override; + +private: + enum Commands + { + SetToolTipCommand, + ShowMessageCommand + }; + + const Feature m_systemTrayIconFeature; + const FeatureList m_features; + + QSystemTrayIcon* m_systemTrayIcon; + +}; diff --git a/core/include/ToolButton.h b/core/include/ToolButton.h new file mode 100644 index 0000000..f1bb9e7 --- /dev/null +++ b/core/include/ToolButton.h @@ -0,0 +1,152 @@ +/* + * ToolButton.h - declaration of class ToolButton + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "VeyonCore.h" + +class QToolBar; + +// clazy:excludeall=ctor-missing-parent-argument + +class VEYON_CORE_EXPORT ToolButton : public QToolButton +{ + Q_OBJECT +public: + ToolButton( const QIcon& icon, + const QString& label, + const QString& altLabel = {}, + const QString& description = {}, + const QKeySequence& shortcut = QKeySequence() ); + ~ToolButton() override = default; + + static void setIconOnlyMode( QWidget* mainWindow, bool enabled ); + + static bool iconOnlyMode() + { + return s_iconOnlyMode; + } + + static void setToolTipsDisabled( bool disabled ) + { + s_toolTipsDisabled = disabled; + } + + static bool toolTipsDisabled() + { + return s_toolTipsDisabled; + } + + void addTo( QToolBar * ); + + +protected: + void enterEvent( QEvent * _e ) override; + void leaveEvent( QEvent * _e ) override; + void mousePressEvent( QMouseEvent * _me ) override; + void paintEvent( QPaintEvent * _pe ) override; + + +Q_SIGNALS: + void mouseLeftButton(); + + +private: + bool checkForLeaveEvent(); + + void updateSize(); + + static bool s_toolTipsDisabled; + static bool s_iconOnlyMode; + + int iconSize() const + { + return static_cast( 32 * m_pixelRatio ); + } + + int margin() const + { + return static_cast( 8 * m_pixelRatio ); + } + + int roundness() const + { + return static_cast( 3 * m_pixelRatio ); + } + + int stepSize() const + { + return static_cast( 8 * m_pixelRatio ); + } + + qreal m_pixelRatio; + QIcon m_icon; + QPixmap m_pixmap; + bool m_mouseOver; + + QString m_label; + QString m_altLabel; + QString m_descr; + +} ; + + + +class ToolButtonTip : public QWidget +{ + Q_OBJECT +public: + ToolButtonTip( const QIcon& icon, const QString& title, const QString& description, + QWidget* parent, QWidget* toolButton = nullptr ); + + QSize sizeHint( void ) const override; + + +protected: + void paintEvent( QPaintEvent * _pe ) override; + void resizeEvent( QResizeEvent * _re ) override; + + +private: + void updateMask( void ); + + int margin() const + { + return static_cast( 8 * m_pixelRatio ); + } + + const int ROUNDED = 2000; + + qreal m_pixelRatio; + QPixmap m_pixmap; + QString m_title; + QString m_description; + + QImage m_bg; + + QWidget* m_toolButton; + +} ; diff --git a/core/include/TranslationLoader.h b/core/include/TranslationLoader.h new file mode 100644 index 0000000..183479f --- /dev/null +++ b/core/include/TranslationLoader.h @@ -0,0 +1,38 @@ +/* + * TranslationLoader.h - declaration of TranslationLoader class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "VeyonCore.h" + +class VEYON_CORE_EXPORT TranslationLoader +{ +public: + TranslationLoader( const QString& resourceName ); + + static QLocale load( const QString& resourceName ); + +}; diff --git a/core/include/UserGroupsBackendInterface.h b/core/include/UserGroupsBackendInterface.h new file mode 100644 index 0000000..57e0e06 --- /dev/null +++ b/core/include/UserGroupsBackendInterface.h @@ -0,0 +1,47 @@ +/* + * UserGroupsBackendInterface.h - interface for a UserGroupsBackend + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PluginInterface.h" + +// clazy:excludeall=copyable-polymorphic + +class UserGroupsBackendInterface +{ +public: + virtual QString userGroupsBackendName() const = 0; + + virtual void reloadConfiguration() = 0; + + virtual QStringList userGroups( bool queryDomainGroups ) = 0; + virtual QStringList groupsOfUser( const QString& username, bool queryDomainGroups ) = 0; + +}; + +using UserGroupsBackendInterfaceList = QList; + +#define UserGroupsBackendInterface_iid "io.veyon.Veyon.Plugins.UserGroupsBackendInterface" + +Q_DECLARE_INTERFACE(UserGroupsBackendInterface, UserGroupsBackendInterface_iid) diff --git a/core/include/UserGroupsBackendManager.h b/core/include/UserGroupsBackendManager.h new file mode 100644 index 0000000..0e4c78b --- /dev/null +++ b/core/include/UserGroupsBackendManager.h @@ -0,0 +1,58 @@ +/* + * UserGroupsBackendManager.h - header file for UserGroupsBackendManager + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "UserGroupsBackendInterface.h" + +class VEYON_CORE_EXPORT UserGroupsBackendManager : public QObject +{ + Q_OBJECT +public: + using Backends = QMap; + + explicit UserGroupsBackendManager( QObject* parent = nullptr ); + + const Backends& backends() const + { + return m_backends; + } + + QMap availableBackends(); + + UserGroupsBackendInterface* defaultBackend() const + { + return m_defaultBackend; + } + + UserGroupsBackendInterface* accessControlBackend(); + + void reloadConfiguration(); + +private: + Backends m_backends{}; + UserGroupsBackendInterface* m_defaultBackend{nullptr}; + UserGroupsBackendInterface* m_accessControlBackend{nullptr}; + +}; diff --git a/core/include/VariantArrayMessage.h b/core/include/VariantArrayMessage.h new file mode 100644 index 0000000..172ee2c --- /dev/null +++ b/core/include/VariantArrayMessage.h @@ -0,0 +1,65 @@ +/* + * VariantArrayMessage.h - class for sending/receiving a variant array as message + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "VariantStream.h" + +// clazy:excludeall=rule-of-three + +class VEYON_CORE_EXPORT VariantArrayMessage +{ +public: + using MessageSize = quint32; + + explicit VariantArrayMessage( QIODevice* ioDevice ); + + bool send(); + + bool isReadyForReceive(); + + bool receive(); + + QVariant read(); // Flawfinder: ignore + + VariantArrayMessage& write( const QVariant& v ); + + QIODevice* ioDevice() const + { + return m_ioDevice; + } + +private: + enum { + MaxMessageSize = 1024*1024*32 + }; + + QBuffer m_buffer; + VariantStream m_stream; + QIODevice* m_ioDevice; + +} ; diff --git a/core/include/VariantStream.h b/core/include/VariantStream.h new file mode 100644 index 0000000..e851230 --- /dev/null +++ b/core/include/VariantStream.h @@ -0,0 +1,43 @@ +/* + * VariantStream.h - read/write QVariant objects to/from QIODevice + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "VeyonCore.h" + +class VEYON_CORE_EXPORT VariantStream +{ +public: + explicit VariantStream( QIODevice* ioDevice ); + + QVariant read(); // Flawfinder: ignore + + void write( const QVariant& v ); + +private: + QDataStream m_dataStream; + +} ; diff --git a/core/include/VeyonConfiguration.h b/core/include/VeyonConfiguration.h new file mode 100644 index 0000000..eaf7a58 --- /dev/null +++ b/core/include/VeyonConfiguration.h @@ -0,0 +1,51 @@ +/* + * VeyonConfiguration.h - a Configuration object storing system wide + * configuration values + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonCore.h" +#include "Configuration/Object.h" +#include "Configuration/Property.h" + +#include "VeyonConfigurationProperties.h" + +// clazy:excludeall=ctor-missing-parent-argument,copyable-polymorphic + +class VEYON_CORE_EXPORT VeyonConfiguration : public Configuration::Object +{ + Q_OBJECT +public: + VeyonConfiguration(); + explicit VeyonConfiguration( Configuration::Store* store ); + + void upgrade(); + + static QString expandPath( QString path ); + + FOREACH_VEYON_CONFIG_PROPERTY(DECLARE_CONFIG_PROPERTY) + + +} ; + diff --git a/core/include/VeyonConfigurationProperties.h b/core/include/VeyonConfigurationProperties.h new file mode 100644 index 0000000..0c68167 --- /dev/null +++ b/core/include/VeyonConfigurationProperties.h @@ -0,0 +1,166 @@ +/* + * VeyonConfigurationProperties.h - definition of every configuration property + * stored in global veyon configuration + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING) if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include +#include + +#include "ComputerListModel.h" +#include "Logger.h" +#include "NetworkObjectDirectory.h" +#include "VncConnection.h" + +#define FOREACH_VEYON_CORE_CONFIG_PROPERTIES(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), VeyonCore::ApplicationVersion, applicationVersion, setApplicationVersion, "ApplicationVersion", "Core", QVariant::fromValue(VeyonCore::ApplicationVersion::Version_4_0), Configuration::Property::Flag::Hidden ) \ + OP( VeyonConfiguration, VeyonCore::config(), QJsonObject, pluginVersions, setPluginVersions, "PluginVersions", "Core", QVariant(), Configuration::Property::Flag::Hidden ) \ + OP( VeyonConfiguration, VeyonCore::config(), QString, installationID, setInstallationID, "InstallationID", "Core", QString(), Configuration::Property::Flag::Hidden ) \ + +#define FOREACH_VEYON_VNC_CONNECTION_CONFIG_PROPERTIES(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, useCustomVncConnectionSettings, setUseCustomVncConnectionSettings, "UseCustomSettings", "VncConnection", false, Configuration::Property::Flag::Hidden ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, vncConnectionThreadTerminationTimeout, setVncConnectionThreadTerminationTimeout, "ThreadTerminationTimeout", "VncConnection", VncConnection::DefaultThreadTerminationTimeout, Configuration::Property::Flag::Hidden ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, vncConnectionConnectTimeout, setVncConnectionConnectTimeout, "ConnectTimeout", "VncConnection", VncConnection::DefaultConnectTimeout, Configuration::Property::Flag::Hidden ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, vncConnectionReadTimeout, setVncConnectionReadTimeout, "ReadTimeout", "VncConnection", VncConnection::DefaultReadTimeout, Configuration::Property::Flag::Hidden ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, vncConnectionRetryInterval, setVncConnectionRetryInterval, "ConnectionRetryInterval", "VncConnection", VncConnection::DefaultConnectionRetryInterval, Configuration::Property::Flag::Hidden ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, vncConnectionMessageWaitTimeout, setVncConnectionMessageWaitTimeout, "MessageWaitTimeout", "VncConnection", VncConnection::DefaultMessageWaitTimeout, Configuration::Property::Flag::Hidden ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, vncConnectionFastFramebufferUpdateInterval, setVncConnectionFastFramebufferUpdateInterval, "FastFramebufferUpdateInterval", "VncConnection", VncConnection::DefaultFastFramebufferUpdateInterval, Configuration::Property::Flag::Hidden ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, vncConnectionFramebufferUpdateWatchdogTimeout, setVncConnectionFramebufferUpdateWatchdogTimeout, "FramebufferUpdateWatchdogTimeout", "VncConnection", VncConnection::DefaultFramebufferUpdateWatchdogTimeout, Configuration::Property::Flag::Hidden ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, vncConnectionSocketKeepaliveIdleTime, setVncConnectionSocketKeepaliveIdleTime, "SocketKeepaliveIdleTime", "VncConnection", VncConnection::DefaultSocketKeepaliveIdleTime, Configuration::Property::Flag::Hidden ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, vncConnectionSocketKeepaliveInterval, setVncConnectionSocketKeepaliveInterval, "SocketKeepaliveInterval", "VncConnection", VncConnection::DefaultSocketKeepaliveInterval, Configuration::Property::Flag::Hidden ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, vncConnectionSocketKeepaliveCount, setVncConnectionSocketKeepaliveCount, "SocketKeepaliveCount", "VncConnection", VncConnection::DefaultSocketKeepaliveCount, Configuration::Property::Flag::Hidden ) \ + +#define FOREACH_VEYON_UI_CONFIG_PROPERTY(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), QString, applicationName, setApplicationName, "ApplicationName", "UI", QStringLiteral("Veyon"), Configuration::Property::Flag::Hidden ) \ + OP( VeyonConfiguration, VeyonCore::config(), QString, uiLanguage, setUiLanguage, "Language", "UI", QString(), Configuration::Property::Flag::Standard ) + +#define FOREACH_VEYON_SERVICE_CONFIG_PROPERTY(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, isTrayIconHidden, setTrayIconHidden, "HideTrayIcon", "Service", false, Configuration::Property::Flag::Advanced ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, failedAuthenticationNotificationsEnabled, setFailedAuthenticationNotificationsEnabled, "FailedAuthenticationNotifications", "Service", true, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, remoteConnectionNotificationsEnabled, setRemoteConnectionNotificationsEnabled, "RemoteConnectionNotifications", "Service", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, multiSessionModeEnabled, setMultiSessionModeEnabled, "MultiSession", "Service", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, maximumSessionCount, setMaximumSessionCount, "MaximumSessionCount", "Service", 100, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, autostartService, setServiceAutostart, "Autostart", "Service", true, Configuration::Property::Flag::Advanced ) \ + +#define FOREACH_VEYON_NETWORK_OBJECT_DIRECTORY_CONFIG_PROPERTY(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), QUuid, networkObjectDirectoryPlugin, setNetworkObjectDirectoryPlugin, "Plugin", "NetworkObjectDirectory", QUuid(), Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, networkObjectDirectoryUpdateInterval, setNetworkObjectDirectoryUpdateInterval, "UpdateInterval", "NetworkObjectDirectory", NetworkObjectDirectory::DefaultUpdateInterval, Configuration::Property::Flag::Standard ) \ + +#define FOREACH_VEYON_FEATURES_CONFIG_PROPERTY(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), QStringList, disabledFeatures, setDisabledFeatures, "DisabledFeatures", "Features", QStringList(), Configuration::Property::Flag::Standard ) \ + +#define FOREACH_VEYON_LOGGING_CONFIG_PROPERTY(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), Logger::LogLevel, logLevel, setLogLevel, "LogLevel", "Logging", QVariant::fromValue(Logger::LogLevel::Default), Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, logFileSizeLimitEnabled, setLogFileSizeLimitEnabled, "LogFileSizeLimitEnabled", "Logging", false, Configuration::Property::Flag::Advanced ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, logFileRotationEnabled, setLogFileRotationEnabled, "LogFileRotationEnabled", "Logging", false, Configuration::Property::Flag::Advanced ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, logToStdErr, setLogToStdErr, "LogToStdErr", "Logging", true, Configuration::Property::Flag::Advanced ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, logToSystem, setLogToSystem, "LogToSystem", "Logging", false, Configuration::Property::Flag::Advanced ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, logFileSizeLimit, setLogFileSizeLimit, "LogFileSizeLimit", "Logging", Logger::DefaultFileSizeLimit, Configuration::Property::Flag::Advanced ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, logFileRotationCount, setLogFileRotationCount, "LogFileRotationCount", "Logging", Logger::DefaultFileRotationCount, Configuration::Property::Flag::Advanced ) \ + OP( VeyonConfiguration, VeyonCore::config(), QString, logFileDirectory, setLogFileDirectory, "LogFileDirectory", "Logging", QLatin1String(Logger::DefaultLogFileDirectory), Configuration::Property::Flag::Standard ) \ + +#define FOREACH_VEYON_VNC_SERVER_CONFIG_PROPERTY(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), QUuid, vncServerPlugin, setVncServerPlugin, "Plugin", "VncServer", QUuid(), Configuration::Property::Flag::Standard ) \ + +#define FOREACH_VEYON_NETWORK_CONFIG_PROPERTY(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), int, veyonServerPort, setVeyonServerPort, "VeyonServerPort", "Network", 11100, Configuration::Property::Flag::Advanced ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, vncServerPort, setVncServerPort, "VncServerPort", "Network", 11200, Configuration::Property::Flag::Advanced ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, featureWorkerManagerPort, setFeatureWorkerManagerPort, "FeatureWorkerManagerPort", "Network", 11300, Configuration::Property::Flag::Advanced ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, demoServerPort, setDemoServerPort, "DemoServerPort", "Network", 11400, Configuration::Property::Flag::Advanced ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, isFirewallExceptionEnabled, setFirewallExceptionEnabled, "FirewallExceptionEnabled", "Network", true, Configuration::Property::Flag::Advanced ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, localConnectOnly, setLocalConnectOnly, "LocalConnectOnly", "Network", false, Configuration::Property::Flag::Advanced ) \ + +#define FOREACH_VEYON_DIRECTORIES_CONFIG_PROPERTY(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), QString, userConfigurationDirectory, setUserConfigurationDirectory, "UserConfiguration", "Directories", QDir::toNativeSeparators( QStringLiteral( "%APPDATA%/Config" ) ), Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), QString, screenshotDirectory, setScreenshotDirectory, "Screenshots", "Directories", QDir::toNativeSeparators( QStringLiteral( "%APPDATA%/Screenshots" ) ), Configuration::Property::Flag::Standard ) \ + +#define FOREACH_VEYON_MASTER_CONFIG_PROPERTY(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), int, computerMonitoringUpdateInterval, setComputerMonitoringUpdateInterval, "ComputerMonitoringUpdateInterval", "Master", 1000, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, computerMonitoringThumbnailSpacing, setComputerMonitoringThumbnailSpacing, "ComputerMonitoringThumbnailSpacing", "Master", 5, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), ComputerListModel::DisplayRoleContent, computerDisplayRoleContent, setComputerDisplayRoleContent, "ComputerDisplayRoleContent", "Master", QVariant::fromValue(ComputerListModel::DisplayRoleContent::UserAndComputerName), Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), ComputerListModel::SortOrder, computerMonitoringSortOrder, setComputerMonitoringSortOrder, "ComputerMonitoringSortOrder", "Master", QVariant::fromValue(ComputerListModel::SortOrder::ComputerAndUserName), Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), ComputerListModel::AspectRatio, computerMonitoringAspectRatio, setComputerMonitoringAspectRatio, "ComputerMonitoringAspectRatio", "Master", QVariant::fromValue(ComputerListModel::AspectRatio::Auto), Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), QColor, computerMonitoringBackgroundColor, setComputerMonitoringBackgroundColor, "ComputerMonitoringBackgroundColor", "Master", QColor(Qt::white), Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), QColor, computerMonitoringTextColor, setComputerMonitoringTextColor, "ComputerMonitoringTextColor", "Master", QColor(Qt::black), Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, accessControlForMasterEnabled, setAccessControlForMasterEnabled, "AccessControlForMasterEnabled", "Master", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, autoAdjustMonitoringIconSize, setAutoAdjustMonitoringIconSize, "AutoAdjustMonitoringIconSize", "Master", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, autoSelectCurrentLocation, setAutoSelectCurrentLocation, "AutoSelectCurrentLocation", "Master", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, showCurrentLocationOnly, setShowCurrentLocationOnly, "ShowCurrentLocationOnly", "Master", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, allowAddingHiddenLocations, setAllowAddingHiddenLocations, "AllowAddingHiddenLocations", "Master", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, hideLocalComputer, setHideLocalComputer, "HideLocalComputer", "Master", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, hideOwnSession, setHideOwnSession, "HideOwnSession", "Master", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, hideEmptyLocations, setHideEmptyLocations, "HideEmptyLocations", "Master", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, hideComputerFilter, setHideComputerFilter, "HideComputerFilter", "Master", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), QUuid, computerDoubleClickFeature, setComputerDoubleClickFeature, "ComputerDoubleClickFeature", "Master", QUuid(), Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, enforceSelectedModeForClients, setEnforceSelectedModeForClients, "EnforceSelectedModeForClients", "Master", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, autoOpenComputerSelectPanel, setAutoOpenComputerSelectPanel, "AutoOpenComputerSelectPanel", "Master", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, confirmUnsafeActions, setConfirmUnsafeActions, "ConfirmUnsafeActions", "Master", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, showFeatureWindowsOnSameScreen, setShowFeatureWindowsOnSameScreen, "ShowFeatureWindowsOnSameScreen", "Master", false, Configuration::Property::Flag::Standard ) \ + +#define FOREACH_VEYON_AUTHENTICATION_CONFIG_PROPERTY(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), VeyonCore::AuthenticationMethod, authenticationMethod, setAuthenticationMethod, "Method", "Authentication", QVariant::fromValue(VeyonCore::AuthenticationMethod::LogonAuthentication), Configuration::Property::Flag::Standard ) \ + +#define FOREACH_VEYON_KEY_AUTHENTICATION_CONFIG_PROPERTY(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), QString, privateKeyBaseDir, setPrivateKeyBaseDir, "PrivateKeyBaseDir", "Authentication", QDir::toNativeSeparators( QStringLiteral( "%GLOBALAPPDATA%/keys/private" ) ), Configuration::Property::Flag::Advanced ) \ + OP( VeyonConfiguration, VeyonCore::config(), QString, publicKeyBaseDir, setPublicKeyBaseDir, "PublicKeyBaseDir", "Authentication", QDir::toNativeSeparators( QStringLiteral( "%GLOBALAPPDATA%/keys/public" ) ), Configuration::Property::Flag::Advanced ) \ + +#define FOREACH_VEYON_ACCESS_CONTROL_CONFIG_PROPERTY(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), QUuid, accessControlUserGroupsBackend, setAccessControlUserGroupsBackend, "UserGroupsBackend", "AccessControl", QUuid(), Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, domainGroupsForAccessControlEnabled, setDomainGroupsForAccessControlEnabled, "DomainGroupsEnabled", "AccessControl", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, isAccessRestrictedToUserGroups, setAccessRestrictedToUserGroups, "AccessRestrictedToUserGroups", "AccessControl", false , Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, isAccessControlRulesProcessingEnabled, setAccessControlRulesProcessingEnabled, "AccessControlRulesProcessingEnabled", "AccessControl", false, Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), QStringList, authorizedUserGroups, setAuthorizedUserGroups, "AuthorizedUserGroups", "AccessControl", QStringList(), Configuration::Property::Flag::Standard ) \ + OP( VeyonConfiguration, VeyonCore::config(), QJsonArray, accessControlRules, setAccessControlRules, "AccessControlRules", "AccessControl", QVariant(), Configuration::Property::Flag::Standard ) \ + +#define FOREACH_VEYON_LEGACY_CONFIG_PROPERTY(OP) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, legacyAutoAdjustGridSize, setLegacyAutoAdjustGridSize, "AutoAdjustGridSize", "Master", false, Configuration::Property::Flag::Legacy ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, legacyOpenComputerManagementAtStart, setLegacyOpenComputerManagementAtStart, "OpenComputerManagementAtStart", "Master", false, Configuration::Property::Flag::Legacy ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, legacyAutoSwitchToCurrentRoom, setLegacyAutoSwitchToCurrentRoom, "AutoSwitchToCurrentRoom", "Master", false, Configuration::Property::Flag::Legacy ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, legacyOnlyCurrentRoomVisible, setLegacyOnlyCurrentRoomVisible, "OnlyCurrentRoomVisible", "Master", false, Configuration::Property::Flag::Legacy ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, legacyManualRoomAdditionAllowed, setLegacyManualRoomAdditionAllowed, "ManualRoomAdditionAllowed", "Master", false, Configuration::Property::Flag::Legacy ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, legacyEmptyRoomsHidden, setLegacyEmptyRoomsHidden, "EmptyRoomsHidden", "Master", false, Configuration::Property::Flag::Legacy ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, legacyConfirmDangerousActions, setLegacyConfirmDangerousActions, "ConfirmDangerousActions", "Master", false, Configuration::Property::Flag::Legacy ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, legacyIsSoftwareSASEnabled, setLegacySoftwareSASEnabled, "SoftwareSASEnabled", "Service", true, Configuration::Property::Flag::Legacy ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, legacyLocalComputerHidden, setLegacyLocalComputerHidden, "LocalComputerHidden", "Master", false, Configuration::Property::Flag::Legacy ) \ + OP( VeyonConfiguration, VeyonCore::config(), bool, legacyComputerFilterHidden, setLegacyComputerFilterHidden, "ComputerFilterHidden", "Master", false, Configuration::Property::Flag::Legacy ) \ + OP( VeyonConfiguration, VeyonCore::config(), int, legacyPrimaryServicePort, setLegacyPrimaryServicePort, "PrimaryServicePort", "Network", 11100, Configuration::Property::Flag::Legacy ) \ + +#define FOREACH_VEYON_CONFIG_PROPERTY(OP) \ + FOREACH_VEYON_CORE_CONFIG_PROPERTIES(OP) \ + FOREACH_VEYON_VNC_CONNECTION_CONFIG_PROPERTIES(OP) \ + FOREACH_VEYON_UI_CONFIG_PROPERTY(OP) \ + FOREACH_VEYON_SERVICE_CONFIG_PROPERTY(OP) \ + FOREACH_VEYON_LOGGING_CONFIG_PROPERTY(OP) \ + FOREACH_VEYON_NETWORK_OBJECT_DIRECTORY_CONFIG_PROPERTY(OP)\ + FOREACH_VEYON_FEATURES_CONFIG_PROPERTY(OP)\ + FOREACH_VEYON_VNC_SERVER_CONFIG_PROPERTY(OP) \ + FOREACH_VEYON_NETWORK_CONFIG_PROPERTY(OP) \ + FOREACH_VEYON_DIRECTORIES_CONFIG_PROPERTY(OP) \ + FOREACH_VEYON_MASTER_CONFIG_PROPERTY(OP) \ + FOREACH_VEYON_AUTHENTICATION_CONFIG_PROPERTY(OP) \ + FOREACH_VEYON_KEY_AUTHENTICATION_CONFIG_PROPERTY(OP) \ + FOREACH_VEYON_ACCESS_CONTROL_CONFIG_PROPERTY(OP) \ + FOREACH_VEYON_LEGACY_CONFIG_PROPERTY(OP) diff --git a/core/include/VeyonConnection.h b/core/include/VeyonConnection.h new file mode 100644 index 0000000..17191fd --- /dev/null +++ b/core/include/VeyonConnection.h @@ -0,0 +1,111 @@ +/* + * VeyonConnection.h - declaration of class VeyonConnection + * + * Copyright (c) 2008-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "RfbVeyonAuth.h" +#include "VncConnection.h" + +class AuthenticationProxy; +class FeatureMessage; + +class VEYON_CORE_EXPORT VeyonConnection : public QObject +{ + Q_OBJECT +public: + explicit VeyonConnection( VncConnection* vncConnection ); + ~VeyonConnection() override; + + VncConnection* vncConnection() + { + return m_vncConnection; + } + + VncConnection::State state() const + { + return m_vncConnection->state(); + } + + bool isConnected() const + { + return m_vncConnection && m_vncConnection->isConnected(); + } + + const QString& user() const + { + return m_user; + } + + const QString& userHomeDir() const + { + return m_userHomeDir; + } + + void setVeyonAuthType( RfbVeyonAuth::Type authType ) + { + m_veyonAuthType = authType; + } + + RfbVeyonAuth::Type veyonAuthType() const + { + return m_veyonAuthType; + } + + void setAuthenticationProxy( AuthenticationProxy* authenticationProxy ) + { + m_authenticationProxy = authenticationProxy; + } + + void sendFeatureMessage( const FeatureMessage& featureMessage, bool wake ); + + bool handleServerMessage( rfbClient* client, uint8_t msg ); + + static constexpr auto VeyonConnectionTag = 0xFE14A11; + + +Q_SIGNALS: + void featureMessageReceived( const FeatureMessage& ); + +private: + void registerConnection(); + void unregisterConnection(); + + // authentication + static int8_t handleSecTypeVeyon( rfbClient* client, uint32_t authScheme ); + static void hookPrepareAuthentication( rfbClient* client ); + + AuthenticationCredentials authenticationCredentials() const; + + QPointer m_vncConnection; + + RfbVeyonAuth::Type m_veyonAuthType; + + AuthenticationProxy* m_authenticationProxy{nullptr}; + + QString m_user; + QString m_userHomeDir; + +} ; diff --git a/core/include/VeyonCore.h b/core/include/VeyonCore.h new file mode 100644 index 0000000..686a350 --- /dev/null +++ b/core/include/VeyonCore.h @@ -0,0 +1,224 @@ +/* + * VeyonCore.h - declaration of VeyonCore class + basic headers + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include "QtCompat.h" + +#if defined(BUILD_VEYON_CORE_LIBRARY) +# define VEYON_CORE_EXPORT Q_DECL_EXPORT +#else +# define VEYON_CORE_EXPORT Q_DECL_IMPORT +#endif + +class QCoreApplication; +class QWidget; + +class AuthenticationCredentials; +class BuiltinFeatures; +class CryptoCore; +class Filesystem; +class Logger; +class NetworkObjectDirectoryManager; +class PlatformPluginInterface; +class PlatformPluginManager; +class PluginManager; +class UserGroupsBackendManager; +class VeyonConfiguration; + +// clazy:excludeall=ctor-missing-parent-argument + +class VEYON_CORE_EXPORT VeyonCore : public QObject +{ + Q_OBJECT +public: + enum class ApplicationVersion { + Version_4_0, + Version_4_1, + Version_4_2, + Version_4_3, + Version_4_4, + Version_4_5, + }; + Q_ENUM(ApplicationVersion) + + enum class Component { + Service, + Server, + Worker, + Master, + CLI, + Configurator + }; + Q_ENUM(Component) + + VeyonCore( QCoreApplication* application, Component component, const QString& appComponentName ); + ~VeyonCore() override; + + static VeyonCore* instance(); + + static QString version(); + static QString pluginDir(); + static QString translationsDirectory(); + static QString qtTranslationsDirectory(); + static QString executableSuffix(); + static QString sharedLibrarySuffix(); + + static QString sessionIdEnvironmentVariable(); + + static Component component() + { + return instance()->m_component; + } + + static VeyonConfiguration& config() + { + return *( instance()->m_config ); + } + + static AuthenticationCredentials& authenticationCredentials() + { + return *( instance()->m_authenticationCredentials ); + } + + static CryptoCore& cryptoCore() + { + return *( instance()->m_cryptoCore ); + } + + static PluginManager& pluginManager() + { + return *( instance()->m_pluginManager ); + } + + static PlatformPluginInterface& platform() + { + return *( instance()->m_platformPlugin ); + } + + static BuiltinFeatures& builtinFeatures() + { + return *( instance()->m_builtinFeatures ); + } + + static UserGroupsBackendManager& userGroupsBackendManager() + { + return *( instance()->m_userGroupsBackendManager ); + } + + static NetworkObjectDirectoryManager& networkObjectDirectoryManager() + { + return *( instance()->m_networkObjectDirectoryManager ); + } + + static Filesystem& filesystem() + { + return *( instance()->m_filesystem ); + } + + static void setupApplicationParameters(); + bool initAuthentication(); + + static int sessionId() + { + return instance()->m_sessionId; + } + + static QString applicationName(); + static void enforceBranding( QWidget* topLevelWidget ); + + static bool isDebugging(); + + static QByteArray shortenFuncinfo( QByteArray info ); + static QByteArray cleanupFuncinfo( QByteArray info ); + + static QString stripDomain( const QString& username ); + static QString formattedUuid( QUuid uuid ); + + static bool isAuthenticationKeyNameValid( const QString& authKeyName ); + + enum class AuthenticationMethod + { + LogonAuthentication, + KeyFileAuthentication, + }; + Q_ENUM(AuthenticationMethod) + + int exec(); + +private: + void initPlatformPlugin(); + void initSession(); + void initConfiguration(); + void initLogging( const QString& appComponentName ); + void initLocaleAndTranslation(); + void initCryptoCore(); + void initAuthenticationCredentials(); + void initPlugins(); + void initManagers(); + void initLocalComputerControlInterface(); + bool initLogonAuthentication(); + bool initKeyFileAuthentication(); + void initSystemInfo(); + + static VeyonCore* s_instance; + + Filesystem* m_filesystem; + VeyonConfiguration* m_config; + Logger* m_logger; + AuthenticationCredentials* m_authenticationCredentials; + CryptoCore* m_cryptoCore; + PluginManager* m_pluginManager; + PlatformPluginManager* m_platformPluginManager; + PlatformPluginInterface* m_platformPlugin; + BuiltinFeatures* m_builtinFeatures; + UserGroupsBackendManager* m_userGroupsBackendManager; + NetworkObjectDirectoryManager* m_networkObjectDirectoryManager; + + Component m_component; + QString m_applicationName; + bool m_debugging; + + int m_sessionId{0}; + +Q_SIGNALS: + void initialized(); + void applicationLoaded(); + +}; + +#define V_FUNC_INFO VeyonCore::shortenFuncinfo(Q_FUNC_INFO).constData() + +#define vDebug() if( VeyonCore::isDebugging()==false ); else qDebug() << V_FUNC_INFO +#define vInfo() qInfo() << V_FUNC_INFO +#define vWarning() qWarning() << V_FUNC_INFO +#define vCritical() qCritical() << V_FUNC_INFO diff --git a/core/include/VeyonMasterInterface.h b/core/include/VeyonMasterInterface.h new file mode 100644 index 0000000..4305396 --- /dev/null +++ b/core/include/VeyonMasterInterface.h @@ -0,0 +1,53 @@ +/* + * VeyonMasterInterface.h - interface class for VeyonMaster + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "Configuration/Object.h" +#include "ComputerControlInterface.h" + +class BuiltinFeatures; +class ComputerControlInterface; +class QWidget; + +class VEYON_CORE_EXPORT VeyonMasterInterface : public QObject +{ + Q_OBJECT +public: + explicit VeyonMasterInterface( QObject* parent ) : + QObject( parent ) + { + } + + ~VeyonMasterInterface() override = default; + + virtual QWidget* mainWindow() = 0; + virtual Configuration::Object* userConfigurationObject() = 0; + virtual void reloadSubFeatures() = 0; + + virtual ComputerControlInterface& localSessionControlInterface() = 0; + + virtual ComputerControlInterfaceList selectedComputerControlInterfaces() const = 0; + +}; diff --git a/core/include/VeyonServerInterface.h b/core/include/VeyonServerInterface.h new file mode 100644 index 0000000..6cadb01 --- /dev/null +++ b/core/include/VeyonServerInterface.h @@ -0,0 +1,42 @@ +/* + * VeyonServerInterface.h - interface class for VeyonServer + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +class BuiltinFeatures; +class FeatureMessage; +class FeatureWorkerManager; +class MessageContext; + +class VeyonServerInterface +{ +public: + virtual ~VeyonServerInterface() = default; + + virtual FeatureWorkerManager& featureWorkerManager() = 0; + virtual bool sendFeatureMessageReply( const MessageContext& context, const FeatureMessage& reply ) = 0; + + virtual int vncServerBasePort() const = 0; + +}; diff --git a/core/include/VeyonServiceControl.h b/core/include/VeyonServiceControl.h new file mode 100644 index 0000000..63c605d --- /dev/null +++ b/core/include/VeyonServiceControl.h @@ -0,0 +1,42 @@ +/* + * VeyonServiceControl.h - class for controlling the Veyon service + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "ServiceControl.h" + +// clazy:excludeall=ctor-missing-parent-argument + +class VEYON_CORE_EXPORT VeyonServiceControl : public ServiceControl +{ + Q_OBJECT +public: + explicit VeyonServiceControl( QWidget* parent = nullptr ); + + bool setAutostart( bool enabled ); + + static QString name(); + static QString filePath(); + static QString displayName(); +}; diff --git a/core/include/VeyonWorkerInterface.h b/core/include/VeyonWorkerInterface.h new file mode 100644 index 0000000..5caa27d --- /dev/null +++ b/core/include/VeyonWorkerInterface.h @@ -0,0 +1,37 @@ +/* + * VeyonWorkerInterface.h - interface class for VeyonWorker + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +class BuiltinFeatures; +class FeatureMessage; + +class VeyonWorkerInterface +{ +public: + virtual ~VeyonWorkerInterface() = default; + + virtual bool sendFeatureMessageReply( const FeatureMessage& reply ) = 0; + +}; diff --git a/core/include/VncClientProtocol.h b/core/include/VncClientProtocol.h new file mode 100644 index 0000000..f93c677 --- /dev/null +++ b/core/include/VncClientProtocol.h @@ -0,0 +1,143 @@ +/* + * VncClientProtocol.h - header file for the VncClientProtocol class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "rfb/rfbproto.h" + +#include "CryptoCore.h" + +class QBuffer; +class QTcpSocket; + +class VEYON_CORE_EXPORT VncClientProtocol +{ +public: + using Password = CryptoCore::SecureArray; + + enum State { + Disconnected, + Protocol, + SecurityInit, + SecurityChallenge, + SecurityResult, + FramebufferInit, + Running, + StateCount + } ; + + VncClientProtocol( QTcpSocket* socket, const Password& vncPassword ); + + State state() const + { + return m_state; + } + + void start(); + bool read(); // Flawfinder: ignore + + const QByteArray& serverInitMessage() const + { + return m_serverInitMessage; + } + + int framebufferWidth() const + { + return m_framebufferWidth; + } + + int framebufferHeight() const + { + return m_framebufferHeight; + } + + bool setPixelFormat( rfbPixelFormat pixelFormat ); + bool setEncodings( const QVector& encodings ); + + void requestFramebufferUpdate( bool incremental ); + + bool receiveMessage(); + + const QByteArray& lastMessage() const + { + return m_lastMessage; + } + + uint8_t lastMessageType() const + { + return static_cast( m_lastMessage.constData()[0] ); + } + + const QRect& lastUpdatedRect() const + { + return m_lastUpdatedRect; + } + +private: + bool readProtocol(); + bool receiveSecurityTypes(); + bool receiveSecurityChallenge(); + bool receiveSecurityResult(); + bool receiveServerInitMessage(); + + bool receiveFramebufferUpdateMessage(); + bool receiveColourMapEntriesMessage(); + bool receiveBellMessage(); + bool receiveCutTextMessage(); + bool receiveResizeFramebufferMessage(); + bool receiveXvpMessage(); + + bool readMessage( int size ); + + bool handleRect( QBuffer& buffer, rfbFramebufferUpdateRectHeader rectHeader ); + bool handleRectEncodingRRE( QBuffer& buffer, uint bytesPerPixel ); + bool handleRectEncodingCoRRE( QBuffer& buffer, uint bytesPerPixel ); + bool handleRectEncodingHextile( QBuffer& buffer, + const rfbFramebufferUpdateRectHeader rectHeader, + uint bytesPerPixel ); + bool handleRectEncodingZlib( QBuffer& buffer ); + bool handleRectEncodingZRLE( QBuffer& buffer ); + + static bool isPseudoEncoding( rfbFramebufferUpdateRectHeader header ); + + static constexpr auto MaximumMessageSize = 4096*4096*4; + + QTcpSocket* m_socket; + State m_state; + + Password m_vncPassword; + + QByteArray m_serverInitMessage; + + rfbPixelFormat m_pixelFormat; + + quint16 m_framebufferWidth; + quint16 m_framebufferHeight; + + QByteArray m_lastMessage; + QRect m_lastUpdatedRect; + +} ; diff --git a/core/include/VncConnection.h b/core/include/VncConnection.h new file mode 100644 index 0000000..6da92b5 --- /dev/null +++ b/core/include/VncConnection.h @@ -0,0 +1,248 @@ +/* + * VncConnection.h - declaration of VncConnection class + * + * Copyright (c) 2008-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * code partly taken from KRDC / vncclientthread.h: + * Copyright (C) 2007-2008 Urs Wolfer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "VeyonCore.h" +#include "SocketDevice.h" + +using rfbClient = struct _rfbClient; + +class VncEvent; + +class VEYON_CORE_EXPORT VncConnection : public QThread +{ + Q_OBJECT +public: + // intervals and timeouts + static constexpr int DefaultThreadTerminationTimeout = 30000; + static constexpr int DefaultConnectTimeout = 10000; + static constexpr int DefaultReadTimeout = 30000; + static constexpr int DefaultConnectionRetryInterval = 1000; + static constexpr int DefaultMessageWaitTimeout = 500; + static constexpr int DefaultFastFramebufferUpdateInterval = 100; + static constexpr int DefaultFramebufferUpdateWatchdogTimeout = 10000; + static constexpr int DefaultSocketKeepaliveIdleTime = 1000; + static constexpr int DefaultSocketKeepaliveInterval = 500; + static constexpr int DefaultSocketKeepaliveCount = 5; + + enum class Quality + { + Thumbnail, + Screenshot, + RemoteControl, + Default + } ; + + enum class FramebufferState + { + Invalid, + Initialized, + Valid + } ; + + enum class State + { + None, + Disconnected, + Connecting, + HostOffline, + ServerNotRunning, + AuthenticationFailed, + ConnectionFailed, + Connected + } ; + Q_ENUM(State) + + explicit VncConnection( QObject *parent = nullptr ); + ~VncConnection() override; + + static void initLogging( bool debug ); + + QImage image(); + + void restart(); + void stop(); + void stopAndDeleteLater(); + + void setHost( const QString& host ); + void setPort( int port ); + + State state() const + { + return m_state; + } + + bool isConnected() const + { + return state() == State::Connected && isRunning(); + } + + const QString& host() const + { + return m_host; + } + + void setQuality( Quality quality ) + { + m_quality = quality ; + } + + void setServerReachable(); + + void enqueueEvent( VncEvent* event, bool wake ); + bool isEventQueueEmpty(); + + /** \brief Returns whether framebuffer data is valid, i.e. at least one full FB update received */ + bool hasValidFramebuffer() const + { + return m_framebufferState == FramebufferState::Valid; + } + + void setScaledSize( QSize s ); + + QImage scaledScreen(); + + void setFramebufferUpdateInterval( int interval ); + + void rescaleScreen(); + + static constexpr int VncConnectionTag = 0x590123; + + static void* clientData( rfbClient* client, int tag ); + void setClientData( int tag, void* data ); + + static qint64 libvncClientDispatcher( char * buffer, const qint64 bytes, + SocketDevice::SocketOperation operation, void * user ); + + void mouseEvent( int x, int y, int buttonMask ); + void keyEvent( unsigned int key, bool pressed ); + void clientCut( const QString& text ); + +Q_SIGNALS: + void connectionPrepared(); + void connectionEstablished(); + void imageUpdated( int x, int y, int w, int h ); + void framebufferUpdateComplete(); + void framebufferSizeChanged( int w, int h ); + void cursorPosChanged( int x, int y ); + void cursorShapeUpdated( const QPixmap& cursorShape, int xh, int yh ); + void gotCut( const QString& text ); + void stateChanged(); + +protected: + void run() override; + +private: + // RFB parameters + using RfbPixel = uint32_t; + static constexpr int RfbBitsPerSample = 8; + static constexpr int RfbSamplesPerPixel = 3; + static constexpr int RfbBytesPerPixel = sizeof(RfbPixel); + + enum class ControlFlag { + ScaledScreenNeedsUpdate = 0x01, + ServerReachable = 0x02, + TerminateThread = 0x04, + RestartConnection = 0x08, + }; + + void establishConnection(); + void handleConnection(); + void closeConnection(); + + void setState( State state ); + + void setControlFlag( ControlFlag flag, bool on ); + bool isControlFlagSet( ControlFlag flag ); + + bool initFrameBuffer( rfbClient* client ); + void finishFrameBufferUpdate(); + + void sendEvents(); + + // hooks for LibVNCClient + static int8_t hookInitFrameBuffer( rfbClient* client ); + static void hookUpdateFB( rfbClient* client, int x, int y, int w, int h ); + static void hookFinishFrameBufferUpdate( rfbClient* client ); + static int8_t hookHandleCursorPos( rfbClient* client, int x, int y ); + static void hookCursorShape( rfbClient* client, int xh, int yh, int w, int h, int bpp ); + static void hookCutText( rfbClient* client, const char *text, int textlen ); + static void rfbClientLogDebug( const char* format, ... ); + static void rfbClientLogNone( const char* format, ... ); + static void framebufferCleanup( void* framebuffer ); + + // intervals and timeouts + int m_threadTerminationTimeout{DefaultThreadTerminationTimeout}; + int m_connectTimeout{DefaultConnectTimeout}; + int m_readTimeout{DefaultReadTimeout}; + int m_connectionRetryInterval{DefaultConnectionRetryInterval}; + int m_messageWaitTimeout{DefaultMessageWaitTimeout}; + int m_fastFramebufferUpdateInterval{DefaultFastFramebufferUpdateInterval}; + int m_framebufferUpdateWatchdogTimeout{DefaultFramebufferUpdateWatchdogTimeout}; + int m_socketKeepaliveIdleTime{DefaultSocketKeepaliveIdleTime}; + int m_socketKeepaliveInterval{DefaultSocketKeepaliveInterval}; + int m_socketKeepaliveCount{DefaultSocketKeepaliveCount}; + + // states and flags + std::atomic m_state; + std::atomic m_framebufferState; + QAtomicInt m_controlFlags; + + // connection parameters and data + rfbClient* m_client; + Quality m_quality; + QString m_host; + int m_port; + int m_defaultPort{-1}; + + // thread and timing control + QMutex m_globalMutex; + QMutex m_eventQueueMutex; + QWaitCondition m_updateIntervalSleeper; + QAtomicInt m_framebufferUpdateInterval; + QElapsedTimer m_framebufferUpdateWatchdog; + + // queue for RFB and custom events + QQueue m_eventQueue; + + // framebuffer data and thread synchronization objects + QImage m_image; + QImage m_scaledScreen; + QSize m_scaledSize; + QReadWriteLock m_imgLock; + +} ; diff --git a/core/include/VncEvents.h b/core/include/VncEvents.h new file mode 100644 index 0000000..114ceb0 --- /dev/null +++ b/core/include/VncEvents.h @@ -0,0 +1,78 @@ +/* + * VncEvent.h - declaration of VncEvent and subclasses + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +using rfbClient = struct _rfbClient; + +// clazy:excludeall=copyable-polymorphic + +class VncEvent +{ +public: + virtual ~VncEvent() = default; + virtual void fire( rfbClient* client ) = 0; + +} ; + + +class VncKeyEvent : public VncEvent +{ +public: + VncKeyEvent( unsigned int key, bool pressed ); + + void fire( rfbClient* client ) override; + +private: + unsigned int m_key; + bool m_pressed; +} ; + + +class VncPointerEvent : public VncEvent +{ +public: + VncPointerEvent( int x, int y, int buttonMask ); + + void fire( rfbClient* client ) override; + +private: + int m_x; + int m_y; + int m_buttonMask; +} ; + + +class VncClientCutEvent : public VncEvent +{ +public: + explicit VncClientCutEvent( const QString& text ); + + void fire( rfbClient* client ) override; + +private: + QByteArray m_text; +} ; diff --git a/core/include/VncFeatureMessageEvent.h b/core/include/VncFeatureMessageEvent.h new file mode 100644 index 0000000..dae2485 --- /dev/null +++ b/core/include/VncFeatureMessageEvent.h @@ -0,0 +1,42 @@ +/* + * FeatureMessageEvent.h - declaration of class FeatureMessageEvent + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "FeatureMessage.h" +#include "VncEvents.h" + +// clazy:excludeall=copyable-polymorphic + +class VncFeatureMessageEvent : public VncEvent +{ +public: + explicit VncFeatureMessageEvent( const FeatureMessage& featureMessage ); + + void fire( rfbClient* client ) override; + +private: + FeatureMessage m_featureMessage; + +} ; diff --git a/core/include/VncServerClient.h b/core/include/VncServerClient.h new file mode 100644 index 0000000..4b346c5 --- /dev/null +++ b/core/include/VncServerClient.h @@ -0,0 +1,174 @@ +/* + * VncServerClient.h - header file for the VncServerClient class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "CryptoCore.h" +#include "VncServerProtocol.h" + +class VEYON_CORE_EXPORT VncServerClient : public QObject +{ + Q_OBJECT +public: + enum class AuthState { + Init, + Challenge, + Password, + Token, + Successful, + Failed, + } ; + Q_ENUM(AuthState) + + enum class AccessControlState { + Init, + Successful, + Pending, + Waiting, + Failed + } ; + Q_ENUM(AccessControlState) + + explicit VncServerClient( QObject* parent = nullptr ) : + QObject( parent ), + m_protocolState( VncServerProtocol::Disconnected ), + m_authState( AuthState::Init ), + m_authType( RfbVeyonAuth::Invalid ), + m_accessControlState( AccessControlState::Init ), + m_username(), + m_hostAddress(), + m_challenge() + { + } + + VncServerProtocol::State protocolState() const + { + return m_protocolState; + } + + void setProtocolState( VncServerProtocol::State protocolState ) + { + m_protocolState = protocolState; + } + + AuthState authState() const + { + return m_authState; + } + + void setAuthState( AuthState authState ) + { + m_authState = authState; + } + + RfbVeyonAuth::Type authType() const + { + return m_authType; + } + + void setAuthType( RfbVeyonAuth::Type authType ) + { + m_authType = authType; + } + + AccessControlState accessControlState() const + { + return m_accessControlState; + } + + void setAccessControlState( AccessControlState accessControlState ) + { + m_accessControlState = accessControlState; + } + + QElapsedTimer& accessControlTimer() + { + return m_accessControlTimer; + } + + const QString& username() const + { + return m_username; + } + + void setUsername( const QString& username ) + { + m_username = username; + } + + const QString& hostAddress() const + { + return m_hostAddress; + } + + void setHostAddress( const QString& hostAddress ) + { + m_hostAddress = hostAddress; + } + + const QByteArray& challenge() const + { + return m_challenge; + } + + void setChallenge( const QByteArray& challenge ) + { + m_challenge = challenge; + } + + const CryptoCore::PrivateKey& privateKey() const + { + return m_privateKey; + } + + void setPrivateKey( const CryptoCore::PrivateKey& privateKey ) + { + m_privateKey = privateKey; + } + +public Q_SLOTS: + void finishAccessControl() + { + Q_EMIT accessControlFinished( this ); + } + +Q_SIGNALS: + void accessControlFinished( VncServerClient* ); + +private: + VncServerProtocol::State m_protocolState; + AuthState m_authState; + RfbVeyonAuth::Type m_authType; + AccessControlState m_accessControlState; + QElapsedTimer m_accessControlTimer; + QString m_username; + QString m_hostAddress; + QByteArray m_challenge; + CryptoCore::PrivateKey m_privateKey; + +} ; + +using VncServerClientList = QList; diff --git a/core/include/VncServerPluginInterface.h b/core/include/VncServerPluginInterface.h new file mode 100644 index 0000000..1d17530 --- /dev/null +++ b/core/include/VncServerPluginInterface.h @@ -0,0 +1,65 @@ +/* + * VncServerPluginInterface.h - abstract interface class for VNC server plugins + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CryptoCore.h" +#include "PluginInterface.h" + +// clazy:excludeall=copyable-polymorphic + +class VncServerPluginInterface +{ +public: + using Password = CryptoCore::SecureArray; + + virtual ~VncServerPluginInterface() = default; + + virtual QStringList supportedSessionTypes() const = 0; + + /*! + * \brief Create configuration widget for VNC server plugin - used in Configurator + */ + virtual QWidget* configurationWidget() = 0; + + virtual void prepareServer() = 0; + + /*! + * \brief Run the VNC server and make it listen at given port and use given password - function has to block + * \param serverPort the port the VNC server should listen at + * \param password the password to be used for VNC authentication + */ + virtual bool runServer( int serverPort, const Password& password ) = 0; + + virtual int configuredServerPort() = 0; + + virtual Password configuredPassword() = 0; + +} ; + +using VncServerPluginInterfaceList = QList; + +#define VncServerPluginInterface_iid "io.veyon.Veyon.Plugins.VncServerPluginInterface" + +Q_DECLARE_INTERFACE(VncServerPluginInterface, VncServerPluginInterface_iid) diff --git a/core/include/VncServerProtocol.h b/core/include/VncServerProtocol.h new file mode 100644 index 0000000..05802f0 --- /dev/null +++ b/core/include/VncServerProtocol.h @@ -0,0 +1,102 @@ +/* + * VncServerProtocol.h - header file for the VncServerProtocol class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "RfbVeyonAuth.h" + +class QTcpSocket; + +class VariantArrayMessage; +class VncServerClient; + +// clazy:excludeall=copyable-polymorphic + +class VEYON_CORE_EXPORT VncServerProtocol +{ +public: + enum State { + Disconnected, + Protocol, + SecurityInit, + AuthenticationTypes, + Authenticating, + AccessControl, + FramebufferInit, + Running, + Close, + StateCount + } ; + + VncServerProtocol( QTcpSocket* socket, + VncServerClient* client ); + virtual ~VncServerProtocol() = default; + + State state() const; + + void start(); + bool read(); // Flawfinder: ignore + + void setServerInitMessage( const QByteArray& serverInitMessage ) + { + m_serverInitMessage = serverInitMessage; + } + +protected: + virtual QVector supportedAuthTypes() const = 0; + virtual void processAuthenticationMessage( VariantArrayMessage& message ) = 0; + virtual void performAccessControl() = 0; + + QTcpSocket* socket() + { + return m_socket; + } + + VncServerClient* client() + { + return m_client; + } + +private: + void setState( State state ); + + bool readProtocol(); + bool sendSecurityTypes(); + bool receiveSecurityTypeResponse(); + bool sendAuthenticationTypes(); + bool receiveAuthenticationTypeResponse(); + bool receiveAuthenticationMessage(); + + bool processAuthentication( VariantArrayMessage& message ); + bool processAccessControl(); + + bool processFramebufferInit(); + +private: + QTcpSocket* m_socket; + VncServerClient* m_client; + + QByteArray m_serverInitMessage; + +} ; diff --git a/core/include/VncView.h b/core/include/VncView.h new file mode 100644 index 0000000..ca4354d --- /dev/null +++ b/core/include/VncView.h @@ -0,0 +1,172 @@ +/* + * VncView.h - abstract base for all VNC views + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "KeyboardShortcutTrapper.h" +#include "VncConnection.h" + +class QHoverEvent; +class QKeyEvent; +class QMouseEvent; +class QWheelEvent; +class KeyboardShortcutTrapper; +class VncConnection; + +// clazy:excludeall=copyable-polymorphic + +class VEYON_CORE_EXPORT VncView +{ +public: + enum Modes + { + RemoteControlMode, + DemoMode, + NumModes + } ; + using Mode = Modes; + + enum Shortcut + { + ShortcutCtrlAltDel, + ShortcutCtrlEscape, + ShortcutAltTab, + ShortcutAltF4, + ShortcutWinTab, + ShortcutWin, + ShortcutMenu, + ShortcutAltCtrlF1, + ShortcutCount + } ; + + VncView( VncConnection* connection ); + virtual ~VncView(); + + VncConnection* connection() const + { + return m_connection; + } + + bool viewOnly() const + { + return m_viewOnly; + } + + QSize scaledSize() const; + + QSize effectiveFramebufferSize() const; + + const QRect& viewport() const + { + return m_viewport; + } + + void setViewport( const QRect& viewport ) + { + m_viewport = viewport; + } + + virtual void setViewOnly( bool viewOnly ); + void sendShortcut( VncView::Shortcut shortcut ); + +protected: + template + void connectUpdateFunctions( SubClass* object ) + { + QObject::connect( connection(), &VncConnection::imageUpdated, object, + [this](int x, int y, int w, int h) { updateImage( x, y, w, h ); } ); + QObject::connect( connection(), &VncConnection::framebufferSizeChanged, object, + [this]( int w, int h ) { updateFramebufferSize( w, h ); } ); + + QObject::connect( connection(), &VncConnection::cursorPosChanged, object, + [this]( int x, int y ) { updateCursorPos( x, y ); } ); + QObject::connect( connection(), &VncConnection::cursorShapeUpdated, object, + [this]( const QPixmap& cursorShape, int xh, int yh ) { updateCursorShape( cursorShape, xh, yh ); } ); + } + + virtual void updateView( int x, int y, int w, int h ) = 0; + virtual QSize viewSize() const = 0; + virtual void setViewCursor( const QCursor& cursor ) = 0; + + virtual void updateCursorPos( int x, int y ); + virtual void updateCursorShape( const QPixmap& cursorShape, int xh, int yh ); + virtual void updateFramebufferSize( int w, int h ); + virtual void updateImage( int x, int y, int w, int h ); + + void unpressModifiers(); + + void handleShortcut( KeyboardShortcutTrapper::Shortcut shortcut ); + bool handleEvent( QEvent* handleEvent ); + + virtual void hoverEventHandler( QHoverEvent* event ); + virtual void keyEventHandler( QKeyEvent* event ); + virtual void mouseEventHandler( QMouseEvent* event ); + virtual void wheelEventHandler( QWheelEvent* event ); + + bool isScaledView() const; + + QPixmap cursorShape() const + { + return m_cursorShape; + } + + QPoint cursorPos() const + { + return m_cursorPos; + } + + QPoint cursorHot() const + { + return m_cursorHot; + } + + qreal scaleFactor() const; + QPoint mapToFramebuffer( QPoint pos ); + QRect mapFromFramebuffer( QRect rect ); + + void updateLocalCursor(); + +private: + void updatePaintedCursor(); + void pressKey( unsigned int key ); + void unpressKey( unsigned int key ); + + VncConnection* m_connection{nullptr}; + QPixmap m_cursorShape{}; + QPoint m_cursorPos{0, 0}; + QPoint m_cursorHot{0, 0}; + QSize m_framebufferSize{0, 0}; + bool m_viewOnly{true}; + + QRect m_viewport{}; + + uint m_buttonMask{0}; + QMap m_mods; + + KeyboardShortcutTrapper* m_keyboardShortcutTrapper{nullptr}; + +} ; diff --git a/core/include/VncViewWidget.h b/core/include/VncViewWidget.h new file mode 100644 index 0000000..08e2386 --- /dev/null +++ b/core/include/VncViewWidget.h @@ -0,0 +1,81 @@ +/* + * VncViewWidget.h - VNC viewer widget + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "VncView.h" + +class ProgressWidget; +class VeyonConnection; + +class VEYON_CORE_EXPORT VncViewWidget : public QWidget, public VncView +{ + Q_OBJECT +public: + VncViewWidget( const QString& host, int port, QWidget* parent, Mode mode, const QRect& viewport = {} ); + ~VncViewWidget() override; + + QSize sizeHint() const override; + + void setViewOnly( bool enabled ) override; + +Q_SIGNALS: + void mouseAtBorder(); + void startConnection(); + void connectionEstablished(); + void sizeHintChanged(); + +protected: + void updateView( int x, int y, int w, int h ) override; + QSize viewSize() const override; + void setViewCursor( const QCursor& cursor ) override; + + void updateFramebufferSize( int w, int h ) override; + void updateImage( int x, int y, int w, int h ) override; + + bool event( QEvent* handleEvent ) override; + bool eventFilter( QObject* obj, QEvent* handleEvent ) override; + void focusInEvent( QFocusEvent* handleEvent ) override; + void focusOutEvent( QFocusEvent* handleEvent ) override; + void mouseEventHandler( QMouseEvent* handleEvent ) override; + void paintEvent( QPaintEvent* handleEvent ) override; + void resizeEvent( QResizeEvent* handleEvent ) override; + +private: + void updateConnectionState(); + + VeyonConnection* m_veyonConnection{nullptr}; + + bool m_viewOnlyFocus{true}; + bool m_initDone{false}; + + ProgressWidget* m_establishingConnectionWidget{nullptr}; + + static constexpr int MouseBorderSignalDelay = 500; + QTimer m_mouseBorderSignalTimer{this}; + +} ; diff --git a/core/include/veyonconfig.h.in b/core/include/veyonconfig.h.in new file mode 100644 index 0000000..64ac4a6 --- /dev/null +++ b/core/include/veyonconfig.h.in @@ -0,0 +1,9 @@ +#pragma once + +#define VEYON_VERSION "@VERSION_STRING@" +#define VEYON_VERSION_MAJOR @VERSION_MAJOR@ +#define VEYON_VERSION_MINOR @VERSION_MINOR@ +#define VEYON_PLUGIN_DIR "@VEYON_PLUGIN_DIR@" +#define VEYON_TRANSLATIONS_DIR "@VEYON_TRANSLATIONS_DIR@" +#define VEYON_EXECUTABLE_SUFFIX "@CMAKE_EXECUTABLE_SUFFIX@" +#define VEYON_SHARED_LIBRARY_SUFFIX "@CMAKE_SHARED_LIBRARY_SUFFIX@" diff --git a/core/resources/application-x-pem-key.png b/core/resources/application-x-pem-key.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a7d6b2de52143d97d56370a5b35368f327ec01 GIT binary patch literal 1990 zcmV;%2RZnOP)28kP>2DfwN+IB000?uMObuGZ)S9NVRB^vcXxL#X>MzCV_|S* zE^l&Yo9;Xs000L?Nklm^t(WsK_}_LT1^<-m^-qqo-IU?y}CG~U4-BGRMLqmok8UNC&?eXyGfe2eb2j1dNY zawm8fu29AIYZHD8ItqZFF%Z6@4&S2RS_gBEkEakWbd8F_}4o**fMpCh9_y^*dI%Q+N z$LY!3OPp4}HgTs2&lrcdo^qC`b!vT}%9W1ogk#8N;dg=KM)9+D_`F`B| z3Fbfs`vBNz%mA)3GJL-hq{9l<=lp$)?fkQL1YvG=&yvT>W7KqYaZngkV7+SN65~Ds ze%6@(^Gdad%95O(*GG3xaBtE6yRAVt2nyH+z$Qbx`9D={0$e4h1fP{kt+RBEu+}&0OuGi zfHrC1R)_ix^tIp8elsxA$v z1?xyoiW!{@K&o*AI4goG!RHGGTr;maPF))?Lu!e(#m$axtlFOxPIT2!=K$sE+Q1sn zuhTt*qxNTW41PC_W3Xveq+20(HJSf?xn®V5o0nw+ju5`3lJ@g?z?cd`kAG)n!w zGp-6&${WM{PtSc4cCKqV^Pf0ZrK(+|_LziP{cC)+1nc zKn^G9_V{wvyEJuyvL&z7zBA%+4za5Qx6<$NSdZFwJ8E+_JGR*x;9^u`)UZ1bde78$ zSX;W7-3*Xk)D? zcsYvyO!f)zeU9+AGk@w7&*cdJGK^py{Zmc88>+4Uidg6V;c%8C|9`>wIpfS8aF`?f z6B&J3C%=a{!oLWQFwg$@X>RbZfMi1Pi*vb2ekw9U-~}%5n@qy@8Q=}QW*5G|AokOd zS%NhOQT0;aouXJ@IH_-W7<@3(&7WgAueE!H7SeQzBC4aEPs#?V*a^Gode z=$!YMu;xebD0GnbTVHR;Ut`~C=&9lcj7@ne48Ld`|Nmz9x~#G&9b2E3th_3NllO3_yghcnSwzAJ^VT0{kkh^JN(AI&l^b zK)JLCoR&Kf4?1`gkeGsBGLEm5$bE$Y zhE%xUG6qlhS0_mTC59*ervl+Sx{;SXWZrkLo_@11_;NDT(8X6*$pXC%upN9t+50OC zdkU29qynQge}JyY=_yvee~0Si|4{CZ#1gVgkA(Oi%;<(h6KM%8d}Vp=O#+rY`kh`v z?=KS)-&X=Hr-Hv&PC;Ty_@_V#@rwn*ha`~jTs+^e(hQKp-t{0`_=;zX^Wt0Rh)d~~ zJ<6LiIthb6B?UH9E_z=V2p_sa9cAw?65>U-EvGN!uR135{%dlwINrYn_EW|$66nR} zwp-yR%J{DcgrD9Uj?mrlxnCCQBM8=!l=u4!1^WhsBM*|_hf|P(f~x6f2%qPp|~^v8NM + + ../../COPYING + icon64.png + application-x-pem-key.png + license.png + user-group-new.png + languages.png + presentation-none.png + icon16.png + icon22.png + icon32.png + watch1.png + watch2.png + watch3.png + watch4.png + watch5.png + watch6.png + watch7.png + watch8.png + watch9.png + watch10.png + watch11.png + watch12.png + watch13.png + watch14.png + watch15.png + watch16.png + toolbar-background.png + document-open.png + document-save.png + list-add.png + edit-delete.png + edit-find.png + help-about.png + dialog-ok-apply.png + document-edit.png + default-pkey.pem + go-up.png + go-down.png + go-previous.png + go-next.png + + diff --git a/core/resources/default-pkey.pem b/core/resources/default-pkey.pem new file mode 100644 index 0000000..50c52e4 --- /dev/null +++ b/core/resources/default-pkey.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJRQIBADANBgkqhkiG9w0BAQEFAASCCS8wggkrAgEAAoICAQDDgd2eD5Lb1alS +tUcadW6ABmQ+kpgEZwMfWRo5IGwugx/P5DxFJtg2tCJ+pVP8CQ4Cp678XTPdxsO8 +blN/OHyumExBNoOw5WDNqjhCnOVWEqy3vFACLH1hM3WH2INO/Imo8WRXw5+MHpG+ +mQ2zlcYKn9kajnp/O809J9E6FFbhR9v7YFIH+N4vFPan6iMWzKZj+cZSNQt0sE14 +LO9sNTTRb1yuq5KCt0vp8ANtXaOG+zyA1QKY37Lwvs9IY+2oJ9QD3m8Kg2HydhbG +L8V15sL5Y0YaoAICpr9RK/hKwXgYy93xP6j6Q/pc8X2Yp+2XvlKDot6V/hVuJEVJ +KeNMhAWAgTdPSOk+EHbbUKQgFtWtLjZsZdlotW+FPZQKLhAIfaD8q5PfFLs7p5Q8 +SZz2whKP4fla1DOHwjgoY/bTKPk2l5Q4GDe1RF9o8wKH/0YgFgAjfUfdx/mBOqkC +j3keIRC9srGbpLlu016w4eR9Gm6W8Nx48e1in8e3gLIdYnACotxtn8f9W7dwFVy4 +DqPn6yHfyF+1LZaN1GHPyH3cf4it7jWFISk2I5u2GfvCNcfMiaJnD0yV61z9G8FE +yHhZEDeyudmJ4CXKR7af2BB6nQccTp07dTNDPWw0lxvPLCDfKYUaN7FpUZQMfgus +A2GsRtuxDFdTFxY9YlBaPU30f2cLCQIDAQABAoICAQCM2Ai87sFAx+rkn52qrQC/ +EFu1TExhl4iDU+B1WSs2UtZfITBeLP7EYf8bt6UKQ8epF/4S5l4QHjoyjFRwqVQB +P8xniotSq9ySLVe8fWUmHev5rZsCBeUJWlYIWG04QOw3HjfIljBhdEBQy9g4EOUM +EF4KKKyM7psLvJyZN7/jKa6LqLdP8PS3fGW5UA6ZRTN1gKJj5LztAnm84rpU/Puh +CZX+a7Hvo3b4ACWn6zZ8bpAGy+3EDhvOHdgccYjrhE3Tv7HFJCMy1enQOugI6QZ7 +PQ5q7eyk1lWrd3629Cu9yViJjPAB42kA/iQaDpg2ojC1jmbGLbZok46qh5etLsr6 +p0S5euqvCLCQziNt29BHITkqGSZ/T+xYjEZfVFuhoq/nn8DCqxp3LimPbVOfFeiK +pV13/wxRAy0ricCs21i7hWHMQqQ8o+rwxQ4wlPpY8JRjAvUIi0F2RKDFrYLzvBoI +nG/KuH8w82pX0/gW9SjGvlaNL7r1oDD5fHitSl46unCbNrSdEsjia+sSzMteLUTw +LLCJVK5Qm4CpL38Hk00+AHVbgq+UPiKSMgQ3y642LupnNxdcKgJdzeykR4y1lbMD +PZNnf/pYaPJEE8CNb34R05/UOjBTR5xJ93LZDQvVgQY9DZnl5SX1vfd10T3SmyvV +tRYoiNm1BPht81qYKUxxgQKCAQEA4IsZEJqUIgQZKcIr3uEnqMaq0Qc9ygVsFv4q +L7fY7vYiW/UeM+8mwLoUouX8EEF8hE8WsPnFBWR/9kwCmMmUZ5yI1W4V54FG8caa +NTqyRoWmYPmXYA+3tIvfGraP0V166k9KkFGdDY3wVnFCOHarBrTmuYMYA2g5HbQT +yVq+CwQeGFrRrH6nBeo4b3RdDYxqZbABEtBJE1BIeQC2XJZmRouB25Gqv+D6vzAN +9eKkpcfXHiMEluHQBsXplNMdaiy4N/iJeFmTnAaUU+6ovTmPUrxCq0l1SpoZ72YE +VJpnoHtRAUo+LzoFsai/AHZNmY40uJg22XLoHyPEXYtpF+YqEQKCAQEA3uVvG0f6 +e222if/0iYQEEtB3JgqdtL5wRZcY3QfNGT73AeEkGExofM+YN6rITYo3AbmdUzzi +MdqZlS2MAoFaT0HsIOa+akHqHMjhwD8+iM2kEqjL6R6hJN2l2/wunS9rt9PpZOeP +N4dqQk36yl9QMV/A53+WiwH9LukvU13YEwaGuGJ+1nOxAiTSNAOqdoBG4Ykik3br +AwG/AcEo4Nx28D6EYsjorkDuLfT+1T7CTcS+E8DMQcFbazb01DKpEpDQ30oo9jKB +4YKy3i/FLNQWlIhpSKOHn1ppGv5HjctZGHwfznWB8whYuo/8W6Jyb4TzSpmVx7bE +XaHaQUrKq+mZeQKCAQEA1WCZcwPR3PlUCDCyi9weWe5giL5+4yl/6+GfqTTl+z7+ +q7AlAWkzxFWQIrS2caQpdXnKsGbJ6TP1vdHBbfuxATzpcxhnzFU3W65FAGxE2WtM +Dj9gsDJ/ehyo6LOvu18/3MV4r+g+fJDzYNHEAfAS2g6yanu/KA20qRtk6iVHDyTc +jHxRX8ADbEWFV7SWcLeMrBEzE/PGXYofNI2OvjFWA5ey0Tp9rlVXMrOxsIWtvLRJ +IiRYyiDQFH+iVoDAJRNWBCo0SJ+m+8/syE21IAxAvHiWSjtGMJx8JNknKFaKPyCw +e69XpH1Yx9j5mLDTUSanxvsutDjTiDPu+I3wUnuwYQKCAQEAhLvWTUWwyR3NjSJq +ChYeouGXgoTh40dg7mALQ1yo63bOe0UKTJn7ww+Qkt3VyC8dkJ5rLK1d+dg2i1qD +PN6w2+/i4Ljn/bjg5GFP5j8DS05CfYYdX+aIib6gTX9AdwRNPjwoiZlHdQbj5TYw +uaDGzZA6/ODC0mbN0ci1EQ0aZkIFqfmllKlyaFdrAeSI92ZuT1Jtz6kKTFEW4MGZ +SUagYXKpPXZs8Y/jBmUV+AEWjl8x3et691Fg0lj4tQCyJEcuyzmLaQksxREi2SNT +ZN2r0KnnIIWZdvDNFHEGRU8p8GXCPx1E1GhNBlRGMH9tbDGc+/i39CVHjIacxTL3 +vCYw6QKCAQEA2Ma+DIrMdmf7IDBRdOqE+P8fNF1kfsV9yWx4qZ0YSSnjVbnHOSog +a8niA7//FzvhZCv2Wkx99mkEpRSehs+bSf3/8WfEOrLLW30jEpPQBcd4mgEqZQau +HlgvPCPfVQOyR/6qUqPBBxoM5xExRLIu0jEDUdrZ/EtkME/NVO7wvONIr8evs9H5 +Z9m3p4vSsxRQLqC168WaYPHlXj0Xl/rqPH57wtQ27Esor794D4GFCkPLqMWgVf7n +4A/wRcoXac1/frLCad/H3eawcnLzayjZ5rGyiny6ddFYhqtubzqVFJOfvyJhfLwn +zgYgd9G8jakFPrPEnzsxq8pVmeEUJml4dA== +-----END PRIVATE KEY----- diff --git a/core/resources/dialog-ok-apply.png b/core/resources/dialog-ok-apply.png new file mode 100644 index 0000000000000000000000000000000000000000..d87eb9c18a9ccbd4e768f4ecc71a46198f79ba90 GIT binary patch literal 994 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!n2Vh}LpV4%Za?&Y0OWEOctjR6 zFqp@JFr#FH8<4>uS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjT8d|V^x4p zh$~Pr5HvJ2gocKurKJH$Pft$=2L~Vn2m%5EyuH2M-Q9hBe1PJ}L>w*))CdG{DIgmt z1s4IyfdNDi$b_hbiehjv6<`&CXh$;&ZYD_#F4qYwfI(kd666=mz{teR%Er#Y$s-^r zBrPK+ub`x=rmmr>rLCi9Vrph?VQK5&>*pU37!(y96BnP5mY$hkSX5kER^Hgu z+}hSZVbYYT(`L?@JAc8#MT?g%Teo5JmTlX2?A){ez`;X@Pv5?C@BV|wPo6$|{^I4U zcke%Z{QTwXw;w-${rUTE?_#k-z~m6<>Eaj?aro`!t9geV1XwTl#(2#P0wxKsoxU@H z;Pv0{&-$XLDy7=ibv^l=aC5Pxa-}1Ob=e!1!dZL#y!S;;PYzSt@+!zn|Ni!$JFZ0E z*eCmM@5Hy(5AI9d*f+Vx_ICNi^~t~UK5Z4N<-Ps=!2Yn!d)sS7Z+}0uKV(y_{7+O^Az(tGd6rPb)2m>Kl{-MmH7=hyLjxI`)A7px9h!>$lw;<68Y(-T7T{##T(na zGm0yAn!RrAuxGf_IZu85(H&Nu?psPKVp2Eh&HpIAFDA`keg2(&rvEJ;+W3AD-mR{G ksdeg+oE@#Q)@4mP^5@m+I-IrUy#(bjPgg&ebxsLQ0M(p>=>Px# literal 0 HcmV?d00001 diff --git a/core/resources/document-edit.png b/core/resources/document-edit.png new file mode 100644 index 0000000000000000000000000000000000000000..1af3826f13b3cf05227fb4a11aa5342cb9351b87 GIT binary patch literal 606 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!n2Vh}LpV4%Za?&Y0OWEOctjR6 zFqp@JFr#FH8<4>uS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjTBH(V}MVH zE07Ki4GjngaCdk2^z`)d@&dB`{QQ6nUoi0Z_XpBI5g_pK@d1($E<_ec0tMkDTo981 z(TFLYG;ca1&=FcCL4Lsu`~pIv63QxS8rr%RL80LhQEBOUD|hcbdgA1%(`V0Jx_0}~ zP zsS}szo^QP2dwd$m6yxeiFbIFNJJrk;)G1XJ9bzorQreV(8E|Ihboc5!lIL8@MUQTpt6Hc~)E(g8jp zu0R?HLPJA6Jw1Ioc5!lIL8@MUQTpt6Hc~)E(g8jp zu0Yzy$H&*#*U!%n!T_=%;I`D~89-@{k|4ie1_w6}-~HczR`uP~1B$Kiba4#HxcBz@ zM&3gP5-y3(N3?nz&RR$|NIp2W%tLtLpKl8%Ykb&!%Gb^|GV_j9a!;+#wYcvB7nE6F zu`K2E5}u-_;TteZ!IS;@>3^5Y-l{|#s9Y&M?*( fH~eRjNWT1@SD^0~dtdylb0Bd~S3j3^P6-r-BEPo^`&XMk~PmiSGiYt{JymiBhX@8 vz=-LZ*-BIE2Vo2hl?+#Fc58V|Qu$jif8l!R;avN#Wgu;yu6{1-oD!MMZCPcfNFpYpfaEokO2_~a)Atp2t*2~3nGQA7@`19LKr}ma1n@NAPEDzDyeH|Y3u0f85x_HnOoS{ z+Sxlgxw?6H`}q0=1Oi3Sbd&-g!z1QnO;wb@KBwXEyquH-5kV+24ER z=VyMdy6h?-N=#do_6oqqbJ_wt``iEB>(by~9y;mz-YFax*9AMl^$G^8%|6A{LE-LfQgz$go zo-D2Ry;ol?7r)jsZjbcgm%ry9R-B$>tKa{-X?paQ7@iY9j|+Kix@XbMnQQaKIHmTG zQ}T?XQ*OWcHH%;4Mv;Z4dI@Kt*bVK=!h8NFo%H-zp?+=CEB4$)0bN1Sg6qy5>|vAo z@O^#H_0EdYp1;?vPSq=(>TsJP9DdWQSn-!)s@KfWkoD(S7q9rI;itJP)I&DyV!zAZ z{H;ZKvahBdmgEUp@XLSWblw&A2`f+MC-hu5d6MVxF4>}fp_)y1+uY`$M*gjhDIqtR zR}@LSQkt}aSL859a>*A?5B()(7nqc{T(}sm_*ZjlW1Yl77kB-}FLGJTFEl23x$kFv z8GMB;AhLhSVt2=1=~>KS5(gdJ_xGH8bA>I(psDSX0sl+(|LYmo?`PV=xxOM>*zfaC z9`%*HUp{)e+`Ym!>*tb3YR{5P-mOh)IrTA7A=J_?N$>a$#goetpF4e0t95RQ-sC4$ z#IN$*=u68}jp>0r3A`Hp(~KlK9C9mri}GxdGbSl)8_*ZIm9%yVsi z{_!dhp0uH7y8ma%t4%j-Ws3HnNS1E-KL5*e#Rn}uy9;Zd&y>_$bNV6Idp3RcpJn1= z)}hOvnf;#?yWHC)kMGR4aI@8=d8;=+{5b2iX&RS4dnMoWF!oqW@wrB6dHrYKx@v7t zpYZZaN&h`NZO?AA43oAOTP9uWR6f3c-iikgUWxoX`t%O-YA(Jlxl4ZOZ&3*3Wmm|Z z=2EA~%&=e*v+vtw>OXt4?$>8uEo5LibN~FL`0Od|yR)Mu=9)zcAm?Y%{%G3`fOV4oHuz!vE|kOf-W;kwlF_*un^#bQ{hWa z+I^Rn&076f&dH@H1DqpzIKW0z2I+P*~Z}M>gTe~ HDWM4fPstqz literal 0 HcmV?d00001 diff --git a/core/resources/go-down.png b/core/resources/go-down.png new file mode 100644 index 0000000000000000000000000000000000000000..106738cd70f38836863bd107b8dd296592473cb4 GIT binary patch literal 1576 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&F&8^|hH!9j+7^74P>~vxdBN(KR+Pp?d|RE?(XaB8yFY}6!G%% z3JMAWGJuNx{r!RB0RaI(H9!(5=;Px9Q~+c^q<|zu3y=h=1S$iPKs7)Bk%Dj`BwPfj z45A)F0u@6P0J%T__(3c^hQvXR9hY-CxuVu(?2^$<14vT$?Y zQkaUdiXa;TR{%E+D1r=di6g5agWbqA^(HX-SC$0%1v4-*F|)9;v2$>8^YHQu3WKPas8Jn6}TG`n8(B3=B-qJzX3_DsH`<@jN`(QQ)ZhSq>qVH(k6(;x?G;bllK(+wi^5E#M6+ zcf`iUi#!AsPyW{L?R=wSdEj=fHD~;@@12v++?jp&w$XP+1D?YO)_}(``D{RwL zf7tb&t3P%9|Dxq@viYO;*39;LYQHkFLvFk7I-#xCo^zV~OSWH-wzT$V;F>!7wnO&c zb+zUNM7Av7xKH!^9sB2|uVRBDTORCckkxma^^1Mp;;60BYmXQ6R?kbD`7_HW{ ztm*6dcFU#D`jKwqu++Nl>%PS88{hvcc>eEP>67@l=f$ty@9fk1syxSjx72#)`o&wH zqLN zqyO?k^=~uJini)HY3%adyH-c(mXG@CA3D<&f>zzyqNg+KO#0~?CbzvS_C}qaC3n%u zFjoA(Skc|pAyU)II@|13ekRRS*>PuI&BQOw;mn-bOY2|O%yr+!*r9RM^{!d zG8RWS^qdVmnj5N+d;6`!3B9e&shdg)-m(XMTl8sqC$nhp|Bg^cg=Yd;^->RtChD)( zQgG+fS^Qi3L~XOE`pdruOVd6uDnAD%+~oezMk?>bXtB3ng|jLoILS51Vy7 z3~%7^7?d$97=H>?B1_T6ndwaXPy9Wja0>yy}{QUfa zf`WW}eEj|Wfg)aBUO+WK22dGL3MdXFfl@#&kOXpp3LvsT5(t2@$P73as1gW(Qa}>rK78FU}S7+ zVQpjY=;Z3=>E#;`5)qS-n39^FnUh;sR90T!*xcUH)7L+7@|3AFX3m|zVA0Z*t5&aB zyKeo)En9c&+_P{0p~J^co;rQz?D>nAu3o=&=kC4x4|S+E|6IL}BdsoEeetSy9xMya|6IQPB8$VrK7l>`svKO~S`*$V zIZoKE$TImO%fYpxia+Lf2rSX(Jh(ljNg+3k-6Eb-Xq>P?t>3)TNVU!^u1U9x5k&F zW-G_RVAp{5iY3RL9+)Vx@OCwD^7Q+)Jg^j!SS89RETP5M3KV(D?sTDMJ0?ln_4s#BY-O2aL7t5y3cV? zPDny&Pf5asu&JCp@5B@>R;8?2<h$3Q3m?y|Wj|jDHf^fCu`av8?T6r%)(8Co z|9Knltc!PXDR$i7^|D{^ujMjc&fJqP#H<$_XV0+P3zgc-jGkw%R@%}geFXIBW VSk`+_|7wCtC{I^Emvv4FO#q?;{gMCx literal 0 HcmV?d00001 diff --git a/core/resources/go-previous.png b/core/resources/go-previous.png new file mode 100644 index 0000000000000000000000000000000000000000..ee690e3f839c3185c14e9d7558039e6395586825 GIT binary patch literal 1297 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&F&8^|hH!9j+7^7?d$97=H>?B0?B}Y0B>(^cX#)|z(AlBP>r9T zpO=>xko58K@%Q%!3If#tNuUT202vTLpe~>Sph_SEA_9>GDgcT@*gz7_28ts~0olm1 z5M^)^z-mDXAPk@^L_L}~kPDQ8$f8NX1tEsOxya@~v|trM6NI}2zZSSG@r=TV*Y|)? z)mswe7tFxO#LU9V#?HaT&BMnpASfg(A}TH+B`qtbps1p*p{1>(Z(wY0VQpjY;OOM) z<>wy|91rm}lxZ_&&YC-K(c&dbmn~npX5IRYo3?D- zv2)Mfeftj`JAUf)*>mSFUAuns*4=v#9zK5Z?D@-AZ{EHC@bS~+)!uYdO4zrWe%?Y(CCdQLjOl3f4s#~&+fxTjt-*03w|+giJD!BcUrU|zT)L+1S-z@A>#%{xe|GDdfBcNUq+i)D zoX@-Y*n5UAp9^MPJ+F58{~68(`4{z47uE;t4`x>p=RP3s7{=eVr{!jBg>{glMZ1EX z%b(>l{!4z`pB(2=q562~l=-JVS98`GA2wh9d0TO)3+tD~KF6*2UngAm@EA0RP literal 0 HcmV?d00001 diff --git a/core/resources/go-up.png b/core/resources/go-up.png new file mode 100644 index 0000000000000000000000000000000000000000..be16d90f57df80fac1d9f15716ce3cb61da0c665 GIT binary patch literal 1566 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&F&8^|hH!9j+7^74P>~vxdBN(KR+Pp?d|RE?(XaB8yFY}6!G%% z3JMAWGJuNx{r!RB0RaI(H9!(5=;Px9Q~+c^q<|zu3y=h=1S$iPKs7)Bk%Dj`BwPfj z45A)F0u@6P0J%T_cis zVl*yL5LpC`3o!(46i^l!;1WkxLk64eBC9em`d5|&`2{mDGBLBTvaxe;a`W)=3kr#d ziAzXI$;iqnC@HI`s;O&f>*^U88X23KSz6iIJ2*MJxVpJ}czXNz1_TC$hDXH2CL|^$ zr=(|O=H%uT7L}BiS5{RwHZ`}jwRd!O_f42MY3j7;GiJ@6Gk4zng^QOhU$tiKx{X`5 zZr`zM_x^*2j~qRA;^djL=Pz8meC68p8#iy?d+_kllc&#Lyn6HY-TMz8KY#u9{m0K= zzyJLGx5{_#AqEDfyPhtNAr-gY&iL+p*+Jy^?69b{Vl3Mit=Lh1mszwhzp#P%dzi@T z9qd{U#Kg6_m?eGh)oU(#BD>X7@8sR5)4%`ZytlXf{x;8dd-I(Xn-nTCKxhPL^| zA+P=OTJ37)uM6A%gFEFz?kcaph4K&W?fe6l$KS{~wQcLndNc7=m*hVl6L}kBbiYR8 z;a?5YU#$ARo_l|DXnD^5Z^j$E(Ei6x&5WEs!WzbB4!lG(ptwolGo+@ z{ZSiYeM8!&W#jSYC$IQ-bVrGAy|MQsB1(&XJ96M|(vHx=XygEPKS9kip zM(*s>-k!ASi}IQ8H4^7{n0=pdf7yJQzXhgQCMrhZ!n`}8WT!1$Ki8!-oHg=Xd-{&L zxXB+FR~7tUw(zFVr}%_ph97_ZF8p3Dmj18cjbMOgR6YBLTgk{&-6CCy`AykY%l$%x=}O5 z(jvN|jo2LQPL-THeyx}B=F4RT-1iSYJtASEu;F&>%d$0pLY{=q&(-^VM=ZP{|L~-K zHkQ6!`sr?|`E9L+3iH@_-nRT@c!C)D4~SKEJn{rk^L zxLEcV6;>_^Yx*$VFUNmn!5KS+1>HAGt}36q+0Oc<@bu0{7Uz8{ybh=&Cr0QMn3}$w z(=z?M?dGSd=az}B6f`)SHgnzjLi4Q|TAKIns|Q~ca1>xcLM~!;%sbAVbl>f*Hwjca Nd%F6$taD0e0s!WzhxGse literal 0 HcmV?d00001 diff --git a/core/resources/help-about.png b/core/resources/help-about.png new file mode 100644 index 0000000000000000000000000000000000000000..20093fcfbfb4afe5b5a3bb4348464941c0867404 GIT binary patch literal 2335 zcmb7`cTiL58paO^p&E)vk={YTpb&x-AqW@(Aqh=D>Vkk21p`h(kp$F51r(5u1@*F` zR6!O+gn%nuBSlaGM3yEk;v!3x?Ae={jC1Gi%)Mvkd%x%Vz0dQW^T(Mp=PD78fs4qC z0002TI@q~_JMHf!1O@B2PuZK`28}(2br%v6Vonl1fk)vO2k%$_@Ru5Q#)5CnpRB;4^^w$I9jZkpc}M;ib3{ zE(9x2@HiK8D=)>ffr97c3f}F0^l;<6%Jux1{l8kgI4|{2;(vGk(*z!ufAn+-e3RPa zT=8xI9|Xz|6A%;<-2)dBmyp~mwNF-Vzr2EyvWl9zhNhObj;@}*!9l}AhmDMpCMeS* zX66=_N3E>UHpgu3>@f~lC!8~$=;G?;?&0a>ebUFzKQJUTEIcAI>de__Qf%CLG9^AC z@j@~+CH2zfv@2JyrDtSbzmb)lb2Go7sQ5ueWmQdWeM953=S?q~U$)TO+F$+lx}&qJ zySMMnTgJQofuWI6=GgcI>(lR3(=)Sk^9zeh%j_>Jf2@96+t}RN-ub?(9H5c~0I&qC zosBzXbh-GukGuW8PR7LLcSJO-9eJOK)7~K{tH%x^SaTx$o4nw(X_C=*rd^ z;YahcVH$-kF?1<^c1x;I8Fi*Ot-QZZXh+AW6^t4*CUQ} znq3$x5_D>31XMbk3dm>7H;j`Dyp-dr^NLzGOsSt)j9I-lKS%a+SS#afcK+_w!QJ!g z4JO*TCLQL}cQeoQ=j2a#1oTagPcFw7Bt0`l^>(!muGhTGc2+#RP-5_LboI;bMM0mW z5eKCQ?g{redIQCrH7ce24qW}!-tCP;n9ChIh>R&qgxASj0UeeqQgUgA%BjRARmNZE z2qX^8F?OYb$FOe33C66(-M1bkRH?g;Q&<}nov4)qYUOJ2kWR)KcW-L->dv{gyM&F3 z3A-4KVxzB{XEuK7GZR}X1LRQK=c*$9kPWseafYn{Sw9>1za&^v71`ErF$}5*P$MnQYb79T)epg?t?5Jpn2TBh^7TQWLK4+xb z-40V@USSKX`R3pZrsdu=UstmGqBoh8d3SS2?rlXa88u>_vVYa=Yuf#x6wayOmW!G+ z#B5cbXiVXzxdTv8Wa@{YpO{rp<@@k)UGRY~O}EpQWthA3q*9EK>I)PGF_|~{X$CTN zJ}A0u-?Q5QvHg~F9pA~W-qLGki>IK?of&!jfbXH`9sx@FvuAu0VQ)zv0qIHnrj?1& zLwgiGIW)>QoJR263Lv%)+^h{PE0|Q^2lQi6y#l90I-vYCrEG)Oxt>GPtc*Zy67wrk z%X&{;q0cnr)S&=tVO4cZodyJBG3oJiCQ+YEVNIc(pdL`l?rs{7{i*i7bKvfpQ~Nx#D>phnpKDy2WJ(8}Jkn8k2@u zV)xKk5wcPZP|fk+d^HI-0e_?~Dgs7~5ioBQhM;wR-bL%v(>@>3DpnaD%^s_SmjZ@X zq3%d|?^^jcW#N33WaUVrWI0hGx)19jo|V*T`+Zcxu3y8lX(7FLLxsY!&D8@a33t*6 zVp~U+-Y>gZa-u}ahtel{7w3jDl4mi=@wcIm8`1(;jH1__GAk^`SH083%Z(}bY$O%R zZ+*(dRF9x<>M17Oxsa!8WblZ^Y`=Hd8hx)=JAWZ@LOcIusrSd&7G;y)qk6U{EP_y? znz|t$SY1!8o5$n2!&+N}|E1gXp4D@{3^^Ts!K3Kz@VFwZ?kR&^lOQ_he8Wq!V@EVS znT3;d7%CQ<%kh5+V@@9AY%kvbRr$%m@Uqn-gzmFX$5BhdS;0NSq{^nTpaV5l!$Bn^ zek-LOVAIIO+;P)4lNh^PZAIQ#V*4B0;Sa2t+CJnwH`@&)TfX}K6fj6$!IAq zDHe+1tj1aARF(5RDvYc0OvFo=k$P23&x;5@?$>Z6xPr>3UY|K%s+#lf-xI7o-mdys GVCr9MzCV_|S* zE^l&Yo9;Xs000YzNklWl4%DOQzf-fC*33v z=q7bi3sq7v?Vx0`+n%2JLnU%(>PVWRrPE1jBbX>{bV6OHy)<=X*fRd&ee{BsL}#ea z6yt2^qY^byoBZN^VZ}T>u6|(hi+d~I&^B$d@;%G59y3N;K{ZlYA6l-cDYjo)_q+d^ zwvjVn%-<<4+8;g6ddyFt{Q+lvXgQ_5Y#HXY-_z-!GJmI>)Fy|V`KYn9$7qLRumnli zio+;J6FT5P4*;MC4s@Ui_#1&@Rj;ei+n8eE`&;&7*ysb4bu`lulKRYMgwHg zRpelS7u}$K5VUa+HI~kJ>dfC!=Eqmp9}Q(#<3%$BWj)Ydrmyw7pdkgd2qZNj1Dd`F zI%rB@m%V%GJ#y(WpNT6723__f1ecbYa8JNieRaE@?B75If=N|asy`0$?cWas`}fnA z;s$3U&xDGTw>O{@VzyY`r zN^)TzV#Fe-jT$R!@IOS=q9zuj23DYY#EC^vKjRSz?9X~YJowY#fK|wWRIvz3{dTF* z2r3j+y(z>)pPvd_b(TZFoM{R7XnCS)b6`cU3B%4LVJ$#7V>G6kpLUgahS?L~zFBW?3}* zHz8bTvq%KV=Da;FUhliYMgT6P@B(nE&l=bLRlL=A1K~stye4p*8Jq^{D&C)4gK(od z%-}?zE;I7|ALBi}2M~_5mmdQ+4QK<}&2M;JVJQF?l6Vp9@E+x1{xSz02v=&yY<4+J7IynRfGIpxCqeV}8CA{b4PDKD^0~0ey8yM_ALh65&o&ya)N94*SvLSXpHv z5-6P;1JO^2zyCh=`(KF&0bJ(CK)&ev|AIAt(-0}d`Hr0edq91M9VoJ`KL@IC7~Am? z)?z8*@G9)kpuvs>h{G}@V>~c$OQHy2Jy(m z0W?W7!QSPZ=Bxr7&vv5BZ#%3Wxmc~|wKrJoNW~$v`z?TT{6gpsw9+=Xzn<@h{&%7r z8xSiM540S4xax-h`gwf8dUsT3r~B)zJ~ng}Ig(i)TH>(>-}>0IEo^P=G-7Z8{2klW zWz!9Ohhtb71Z-Fg(oo^U+R*4}S3t?E0c?&sunqHq3XzM)A>1CL`Am$>+l%Fb}t>MxjpuN}qRlUI~oAJIUz)Dbr@G1W5^CXCR^!E3k!s7az zhK7BXpDIpPnT;Gnfm#>0JZsTH7kH}Is%~PNz~dIm@+$TjK&fQ1%V5Us5pdFs2hfEg z%(oQtcghm4VY@?O=LmbWp~*uPO;{nNvx||9ax|j{J!nQbJ~3q~>z6gSZe|r|RTPCD zWW%3&TajMmNg4PHaagd*xKJYU<(n=uJwUd4xzOpfQ3^f74kSI#%9~QYQJM$+-K~f8 zFj4!~Ibqvx~|zb9}*LS!B?7?UuHk(vZ#UY71FKxLu)Q_eU3o z{Jaf+lc=VS88%vvNNjZ_T8GqJ&b~jc&?P-3cm{Tf9a(GwLhqEM6z`bJC+^nVz`m_e zsKxzJ`#?Y;OR|}gZEEMXNpkBS4=DFG_D!=w-?~3?0O-VO>2}(AQvxWH-u`m50dR2W zA%(i#AAN`FfcSJ;ObMVl06q8m5VgW7AXo&uur}fr!ME{^&LVhSqz0Y%OtQ1< z6H@~Cr_{DJd^%tea%%9JLY3~18i(uwWSS$3Oc+A_Qd^!sB+6CKzP+GOk-jN>lXD{Z zQ;r%IK%vA|*P+XkDSXV!W(X+49BGSCcaS$jvrxd){iR?tl}*ics{8HcN>IEy5<0;Tmq?7OtTTo1`ed zImjDG%VE$h-f}xGaKaokx((|?91#?6bh6OtrRN9Pe-#NsV<`qyRXq|c#r_&YkiPfD z0dh7tI@cgPrUGwSg85abGKgB@)|lx!;qf}dzPn>s5>n<@qtq~3q=EnckM95^N_+=M zIB_aOBVnycB=b)02+bfyDA7NNj0@#R3F5*y8ykFp@v=E5)OCX(xyfu3O+MhTT7!ZV zdINiXI>6Q75hOK&JtgWZOs)^5$Ow>E@fEE12_L4j3+NaElT#G+V|hSHpbLkQBoR6_ z4!~? zq+<&Ta28)lKQF+;<<(WZ0DjIMm`FjS5O;piNsoz+%2q#G#tCvkW~navsgV>K%Xx=mb=a|AEAU=x z@kVn9UEtl=U17z(Fp0`V!?}cLBA-SloDyj`MaeUq+ITaWgi`rE+QzUEfH1zeRzfPR zys%(DSH%8_NyE(Mj~-3!d_`Yao_!&>qRr6i&uN1{iqh$wwlw>vM80V!{lV9af@z|J ziiMVC|B%o%uO|ADQj>DSJ*B4lnO~{4V~5irQMDggY?tW7MzMeKsNXjfO&v+4;*8SP z$uHL>()%FLS-M|FTJ0Z+6Du#Wh=E+E2kotsl301*N`+c6C}D5NQ)}8tUJA|rzt{2Ps zvj=QeU|gLZE?C$Zu)FCJ?$J{9uh>ZUDX4Cq8hJWOxWgXLYE3xJu_k5X^Qv=csUc>;6M(mZ70Khl`LiX3lA8-jV!(!gHv zS-R*g%)J!5(CCMy;6fvIBHnvI{es6}N1!NnxVnt@AoFDbGI0=%CRAlF8gUSr#!_{3 zY4N6=9TKnI8ZEwpK~!vrLn5}I5a-c=HaG@w_U9a$|lH@;e9V zIrap4kjn8O-!bP=IgY3OdKN-J$|-tIu^L4`LA!$>1SjRwSvo0;CWOJo%Wk(6yg=`Hzn0T$sZ`gg&xhnzQAv~HQ7u}{pdyna zqLqr&3^nR5pgj_3+NkJp>Rc^}a_Kl#P&0K7m{B*VlbWf5j#I9hsLrLQri}_-ul@&E W#fjqF literal 0 HcmV?d00001 diff --git a/core/resources/icon16.png b/core/resources/icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..dd937a4167d99474c59d80ad3f34f56ca57624f3 GIT binary patch literal 424 zcmV;Z0ayNsP)0b000?uMObuGZ)S9NVRB^vcXxL#X>MzCV_|S* zE^l&Yo9;Xs0003eNklsPt6q_q^MX}v)H^ep$_b4D0 zW>cih3BrsN4UAe@mTz5^U>gZsCC7z?g05PliZx5d!Z z3W_6q<5CaKS6mm-6@J(eXU<24`p9=Y;FM=sIKv}8^{l7-$aaJNd8UF?#(r+pz};!o zxgvk_O*J`oD^pZnel354CB96;Y=R|o!UoBN{Ia}3vo3F(xtW?N&8!|AY@2L2!s)i1R^3LA|oS#Bv2O+0NFqi$b|r) zLLdXKJR>6`6AZGlvg+#U>g(%)vOp~j4GoQrjX-5UHjsqa2+<2MFgiNAprD|nq@=8@ ztfHc#s;a7{rlzf}t)ru(udi>ygb5QTPMkDp(){`J7cE+}c=6&TOO`BMx^&sHWy_Z@ zU$J7v%9Sfuty;Bu_38~9Hf-L!dE2&a4<9~!^yty!$B&;pdGg}LiC@M*U%!3(_T$HopFe;8`t|Gg@85s^{Q3L$@4tWl{{R1fVbSL)z`)Wg3GxeO z;NTP#l9q{2NUEr;s$Rcg)8=#MFWkL%|H1PYpTB(h`t3VdH%L2B|3AMJ{a|2lTY9=U zhDe0R24*@pIS9x)a-D5Dq8xlky{ycS zLH^;1Dy#Yaoq}S^5|S+UUfH}R&U1S5nf<$_Xq2_zIKClz+75@iA@e`5Y@7A5RBhp| z3buFaM3g>dT|8F2VXMYU#p|-0^^gCQi}%DXN_^2`%yk8y{WNy(lyR&54LT!Fps>|wdVcIX{lmg oa!go)H|&`ZGG)mDcI|)6i9BgVnf76Nz~E=_boFyt=akR{08M9v2LJ#7 literal 0 HcmV?d00001 diff --git a/core/resources/icon32.png b/core/resources/icon32.png new file mode 100644 index 0000000000000000000000000000000000000000..b0d8e0b0ddcecdccc29d8b66302fce44153a5c98 GIT binary patch literal 1097 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy%*9TgAsieWw;%dH0CG7CJR*x3 z7}(x`Fr!?lg(*-_vcxr_Bsf2(yEr+qAXP8FD1G)j8z}|`#@PWr zA+A8hp`oFko}LK_2|z|C zC`bta8UnN(=#r?YsOaeEn3$N@*x0zZxcK;Zpd%9#6H`-D)6>&4GBPqVGqbX?a&mHV zb93|Z^78ZZi;Ig(N=iygOUug2fZndCsHm!{s;Q}|t*x!AtE;cCZ)j*}Y;5f8?Ck03 z>Fw?9>+72|Y0{J_Q>IU!K4Zp=dGqEiT)1%2qD6}rFJ8WU`Knc`R(_7C zuwm=gtvhz?*t>V{v17+hpFVy5{P|0lE?vEP_1d*-*RNl{apT6VTet4qxpVjK-3JdI zJbLu#$&)8fpFVx|?Ai0@&tJTF@#@v9*RNl{ef##^yLa#3zyI*z!^e*wKYjZ2`Sa%= zKYslD`SaJWU%!9<{`2S0-@kwV|NqYw^il&DJq9H~e!&cE>>Qlp5|UEVx_bJqZtfnQ z^^Hv}Q>IRvKI7=|6DLnSdi><+%U7>IeEjtJ%hw-2e<@#`x(^t|Pdr^5LnI_i4{}Be zC5o_J;J)Gz>87W{snqGT;76U}me~=Kx?(L`ZZw%?N_R-!FcT2H@b@O~=L1hT<~jeL z`8MzVl{n*p9Aalb6lQHxRHGJ{r2qd>yAF={yu+y_J(CTO=?!XyYGB^Stzrs+uy!8 znrr!qUf#5Szc}2Ke)rD&Tri2>_21$IjIX z4uLtU7nW{V)wS-Y@0lQxXmKFt75lD7vu7;LymD&$1;x#;Os%G!=3+AGQ`&wnrU_!w zvrTuNFxfmgWMv#apF?6!eOU4{8<`aMuG{<8#5TTr)&2g?ww}o;e4Sj`QSamJB0N;q zrN`C&I6cd#Nli(-sHQ__j!Q~!htT(}x`%{Tx=mP9EdSM&Cvf)B=92De6ZxbwK2n)X z+NO33<{ezpYsL1*W7>^`YC+-Sp6Op5)`UfbpX2KB+|r+F!L;O4Yu4hxzccppwzQu~ wQL${kz$)v$a(d>4hrBL}8P_LYaM@L#E3D>M6JYilm{u4(UHx3vIVCg!09<KSwbKPf>Z%9 zNQ+1dp&B-z8!4;1JRnr5iqwQ&LUDf#GjHC!nfLyDXXc#qo$s8vckVa$&Lu)@?|{$9 zo&kYCU`q>AdmtN~xDX!@i<;wBfW#MOVhI%z5*nYmHw~1+XbV^vFg|?ZJSJ+-q5x6` zW9EW!2=T>4dWQOdA|oR;P{9FVUY=+ljgU~kXY5esJd*VNS1)zvjLG*GG3#>Pe(jn>rE)Y8(@+S=OI*4EzM{^rdaI-TCp(ZOIa zIy*a=Oy>Ld@4LIZdwP0$dwV~A{5Uu`I5afGVzGvYhet+6Mn^}-#>U3S$0sHxCMPGS zrlzK+r)Otp=jP@+WPwX#>U3x=H}Mc z*7o-H&d$#6?(W{+9+%5KI5;>wJUlu&IzB%B_U&6?T;p@#3VLDeZ5=>7y!--S5z(_^ z;*#g2q!kn|DJiRJY3u6QJ3t*>Jm3gVFK?gFFidz%EbeD~TzmpCC6$!+I6Z@0`m(IN zg4#%H>U{f-+0)zi`=9*-gF~$0Pm@y%i%ZKZt8C8N`o`Y=v2v09dl2Y5KTA_%=y{p! zPU8J6{_~7=zl)Pj5y-?GxdJgcDfnfN$veoGm&un8I(Om~cww%Y*_ZuMyyavmGSb)I zGU<%3wyvBeWN!aw>=bx+<`d8O?$!QV8JRg+KVDVU*H;a|nY;FC68pM)@@)eP^RvvY zNZj=MHm0f1?`3v?ams|sP`IoT-8xF|8OV9RLjrs)XqQc+=@gDQvlS7e10svRU81hv zh$z~uO5^-)tB<25Swl;my*tT z;(v5($AwCCAz8t91r$in+o6B(h4~0i_m7VYIg}Y^Cyrtr-0-5_{C{F!B%-BtE1#g1 zrQa|@d-xx>5=(wcFi2)N@>M@^pZ4Yv!yq%#g-8a7v}%y+^`MIrW?+A%2~}x2BNWCP zHQ4-2(@_>`na6RZ@porFGD@HnbMwxcXGH{rN{|y>(&EdJ&jI$3-TaIyF=)YmqHk;?BorN%dFEzKzsD+{jRWTR6Re z(hmt&pkLwEd$iNOB!ne*M>Ax={7vpVYM{mEOu05AM+@b48K$`lkD p#0EP>1P4d33Q^aUM4zY~3kr#JM__d&KArrsmS(o5H71DozW{R@!WaMm literal 0 HcmV?d00001 diff --git a/core/resources/languages.png b/core/resources/languages.png new file mode 100644 index 0000000000000000000000000000000000000000..0293e2f5df12a00d61f24229c3bb04f209cc221e GIT binary patch literal 344 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?I3?vN&YJLDI=3*z$5DpHG+YkL80J)q69+AZi z3}rGP%((ne#VeqoWQl7;NpOBzNqJ&XDnogBxn5>oc5!lIL8@MUQTpt6Hc~)E`~f~8 zu0Yz?*Vo6#$Is6X$T)YLQ5q=3QWE4B%urt6u-xch7?8i()5S3)aCc;dekSL3jh_4VD}xGsf8s kzBsttIw_IU=HB32`)9V?>Jzt}fzD>|boFyt=akR{0Ma{jy#N3J literal 0 HcmV?d00001 diff --git a/core/resources/license.png b/core/resources/license.png new file mode 100644 index 0000000000000000000000000000000000000000..a46fb812d82df787b57a11cefcfe64be3352b330 GIT binary patch literal 2863 zcmcJRc{J2tAIGN#G4`4fN*YtPsIg>CA%n4unK2|ZjAf!GWyvy?NDR{y(q>I#CuNUc zvWz`qRAh-?Mz#z}LO=C*zWwGrPEWtnU(b2&IrrYzeShBfeeU_5@ArIf92R3C3Xz3? zKp;`Hr5PTWBYt#2FfcB?OZ*B<;6PI}K~PX|c+Bn{uoOCHc{~sV3f}pnZ^~nZegH(7 zAe2*({aJEQ2r0lD6cQ4$-`DSSpeN~^_x`g1CsXGS$$~(etFbl&6krBgL_~yxgM+cL zF_}yT0F6ci41lezt+}~502mAg5COnqvDVhs78Vvb91f@gA^=uaR(L!f@YvYc0E(TR z9Z&*9zzhK32Y^=vL;!dq&jwTh_-B=80|I$A9&GFd5&(ra^0xCbH+KC3Jm)VWkib*^ z$Li0qKWFk{fsxlXY`~ItcmRGr0MGVI!iITc3(vNp^OhSq8}Lv6?@GVZf0w**fS+L_ z0#v=8d&>ggL89x^&&Y z&MvMeh;HsA4^J;|@<|`xQ>Q6s{LcCZ1fCBL38h{PkGv9fH6}Lh8sqwn_=LopNw;p_ zxtE-hmY$Kx%*x5jFDNW}Sp4X5NoiSmMdg#~nm?Y_)i*SA%l06C90U?r zMw^)sq-EwBzq%#Sm*`hhik~~?Nq?kd=>@?XUp3u+>d|M~OwpaSe z7am{hde5ZL5KnyqI)+AQ!vzzrvCWpla+&m};p1LAv?=z5qYJrKnM09$$_8z9-`bwP zl0>x@j)yOQ!NEDp*-4i|i3iN;>7yUZ;+JP)UzMK_2|U|Ldw?^1D>AEwa(zCpMOoLo zhsU-d|HL$R}W=ePjy{DDM>@6nn zNx{t#t^_^#81HQ-2zoSx&gD#&A=v-kL=H#2`F#NhHY;)WI)RU@Ah2w%`a$AFds#}; z`h4}mvrBXD`uFO`vX)P(?jIx(_ri2FC2{kuf!v~%rphJ>oZ~(i4EbWzwB?W)zNFJO zef>-)YL9S!wT>WJJ&(CdGP|z7CDx!2FWDHb`rIA=90pl&nl=he#-;BPe#Y{nEAHq@ zwrJ6re#Y(y59*2}9)$HYrWrsMl&5d4l(-rFNyF69m{Pf37gUI}?oMrvn^3y0${$*! zR$0hpcm;9VdtfBQ(XPkZwllSK(k@-(^ChtJI2c+)YDLqt*wm6F_=_R|gx7%h5pPQu z*^0y2dSctks49*oP5#Ly^R~hH+23DV_by2UIodCIH4KO>e{;xki!p%_gt zH0i;e+1zt>X3%k(0=-T7+YAzK<3B7=DN3N2a`E$6o2Wz>l$Pm#gR{+0Z8gF}hCz9d zAL$$(!QAQiEyhEO@f!Cq+F7P$q_M;Uj8bQ$WxZn<4se#9Vt7`W9w@%TrHn@e>?n~dA--oRQTU)7MSe9}v&iU#& z)rNeIl+66-L^nJ)W~d$1qUuyi<7`KkrE!qj-FWR`QllhYQ#u*b>lZa7ph`q>@o7+d z(jF~i4gOO#C!iS~W*Q_xW@sAgzSvPwr~^Acms8IltGqthmLxG)1l>-0n8LaA_XA(_ zF~fe;7_8YWDVIGU<|(ZFR))Q*5#?tjl+JMsA1iKTcP?VPqD#Aobcjzj(JNu<7B{VS9#9MZ-3$hbOC)k75z3H3mmvNyMCV92A?~9kj4fu)l z5K_0RYOwKw;mUyc)RPBKuF>_8N)v9-d4KlW2bB4j_$`^bqepvvqR^fBt2-1dzvGk; z0mbErD^Y}6!E)}K0;dSe#gJfJ-uN5+ST9M~u`P`ieS`{cIP>k7b&-#=6(YDGPbTOz zL@o5aV$|V)hAXUKff_QLY!`x@UIskHNhU@|O5uuCs`63uMw_nH&3Id0((y&D>cEd}~9gmNBd+(l$QF7%}nbm>4yG>p%v* z*=ahvKV5wJ;d*}-N?*(Z)0>quyRy?CKihqAk4^CP`x=nibedTT^^Qi3wy+TSakcrY zM|Z?cIXPYR#`YVTLCxS^C^j>;L>i)Gi)>nRok4 x_yXbRwa%%)O6|155m%jqa=>Ns?{?>#yjW#=)$Q_nxgXb6Gzw$($khGHzX5or)e8Us literal 0 HcmV?d00001 diff --git a/core/resources/list-add.png b/core/resources/list-add.png new file mode 100644 index 0000000000000000000000000000000000000000..49bbfdec91b4e30fa05dc904e5f72c1a65ffd4d6 GIT binary patch literal 254 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?I3?vN&YJLDI=3*z$5DpHG+YkL80J)q69+AZi z40*dinDN@Zjp9H-$r9IylHmNblJdl&REF~Ma=pyF?Be9af>gcyqV(DCY@~pS_yc@G zT!C~zK!7g{xTao90Sd8{1o;Is2z>dIofi+{2YR|VhGg7(d)bipfB}!IW3WbRfClU5 z<00keC!FwJeMfr#n%gppo|lpqXE8=H0yTg@9q$vjhVx9{mwd0-_L~(V#^AucW7UKb ZFaDer>!%t>F0}v&d%F6$taD0e0sx_lPBs7l literal 0 HcmV?d00001 diff --git a/core/resources/presentation-none.png b/core/resources/presentation-none.png new file mode 100644 index 0000000000000000000000000000000000000000..18b3dd17f0b328c1e9e8e52d9897ccbdf948fbb3 GIT binary patch literal 618 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSEX7WqAsj$Z!;#Vf2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR4nnR4Clqej z|DM}l{LLAgz5n05y>s^=&@wov2~+>vDqo&{+feVx-a9cidddIppU>Z4Zu{^3{&n+v z|Cj80n0fth{cSPn|3M75FB|GTX6_6c3odK&!|Jn9Oo6HG#xG{5kF2-+Df{>H?c;U*E;cc9_x`G9{2s3l^o@S~|2_Ze z8QXtM-0+9r+5CCeLXk%pF{&l*wfX|Wt~$(697~x@caM( literal 0 HcmV?d00001 diff --git a/core/resources/toolbar-background.png b/core/resources/toolbar-background.png new file mode 100644 index 0000000000000000000000000000000000000000..c6c5bf5180a3788464fbefb258d34e648580485a GIT binary patch literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^HXzK%3?vzhybXaQV{wqX6T`Z5GB1G~wg8_HS2Hs+ zCnu++&mM0CNhf)`yD)UH%6b4foCO|{#S9GG!XV7ZFl&wkP>{XE)7O>#7P|yLqxLiQ zom+uIk|nMYCC>S|xv6<249-QVi6yBi3gww484B*6z5(HleBwYwx}GkMAsXkCSM2*< z`~LTKLqG#ZcvM)cO;lv*O1c^|aNg~V)hXbk)yy=zy2Gq#l>FVdQ I&MBb@0I6h$erfPfTbEi@s4kOF}O2o?kZ7Z9nT%aE8*TofA!ic1ub z)sGdELs%} zL69toh;s!y`^!QhL7pDWmm5Rne}_ZV*BODJiu=8nqI= zN=8mzVa-}aB^A{T8`ab|Y3t~2)-y0P-nz~7$L%|I?lQBmwAyWLgTdO`;qU~Kqcg>g zwrAgd4^OXy-ad!?4~K+CL`KEL9*s|69%CgYC8wmGO+TNRb>ZTb+`Rm&1=oMOQB+(~ zT2@|BS#`7KR_*U~^|$ZbZD{1(Yr5b3p!MOS$4}dzb-sM{y6cag-hsiPw<8}uj*g8_ z@IOsXP0!2<=jJ~zEG{i0B`3(>nUs=nSlZE^@f_iSwcFOU_S)*(Vdn6W06 z>t^D#GKku1PU1|HFWtIcbGvrd^TWGK)aYIBU#`~wRi?i$NVy;WME(Wy-lEF8(JG&; zMfV)EKT+hSc{HUT?Hi}4H$cQ9+Vw0htGuwGboz5nELSH?BkxwDQFI)(GLOYNrJb!Gu&)RnLAPe5=3GQFXastjBe&ban=t{yzPsOX5BTh`X=*=05Jke)$=E0~t^JkE|o%y7%;h{e|3 zItoopja${_l!el+>g1uE6V1YR*9^0_Y&khzOv~cO#GjE9oKJu@aS5B6ghV1kgOhWG z!)ee;43!|mCMHKvLDd!G%`0L@4;jMY{2a!xo4-NHyxCcPwGV-B-mT4>Y*6u_haxg1 z&A4$*j$9SNqQ0|$S@Jxgp6;L2rh2%;C|FfS*~_OuN(MH0g4)n{+AwfK$<%|JM9fe^ zYj@j%Rj$1R8sdSp4uHzgI?G_k2V zek<41>>nRUm)dHc*a4?juL};dIKvO->1_Q>KHE*}oITzyoMI)VCSILA;+`iv$5p+Y zPKK?gtUd8{TyJ#ox#zW%=@ANXD-$kyUF$k)GDdKEBw(<}MoaeoDTO&c6^YXR#tD}% zv^*IHs5W2(IlRSZP`cq-&4?#m=lgpn>S_0tFGxN*I9($d+*q&|0uos@W+SpI0 ziV+_C*5+Vfw{Y;)NBif{^`4QKowo4i+c%(;VcoI3awl9xio$|A7q2Pn6OV2WsyV+f z@JJhzQ7gS8wMBSv1)_8?do8a!UHZ!*8CT5?MqoGt(5rvs7fopI&1K1qKAF_Zdd literal 0 HcmV?d00001 diff --git a/core/resources/watch1.png b/core/resources/watch1.png new file mode 100644 index 0000000000000000000000000000000000000000..a3e5fc112b1579e456a3de83d1f6d7222c324614 GIT binary patch literal 2616 zcmV-83di+{P)@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000SxNklS^cA4E}*=1S4Kn;F@n_xjD>?n$zG_4aQ zj-149`iCo5O8&BHBK40%Dz($5O(d%ir-@WoD2bm+EZ5lByCI0!I2ioE=CQEAdSQ3i z_dM>*?A&|LIsIb>46ak#>XE+ET;0*{`#HaJ?>XNq_yl9$zJ2L>-F`qRvsNk7qqWXx zt;Uto6H1x!Z+zo(|Ncqp0f>+K!%u#4*clp9|E0D1VjSzV>)N5?*wXV{OG+WC)tai; zn1+4hch|Z97r>KG9&@|8>W^xz?@gx@y1&22>FX;124lb&1Ax{B ztu@9N=I6^CJ$lMaP0foaipHwdr9a)d^PbU93@|+W`f4FK0%W&r*&wso6vn_}sluyo z&rq#})ax-}q)GaaPFQSQ-9c|p9w`O&ddR+ghbWf=Gchr~^9Mipn;(8`fZ^f8w@PVV z&SX+%%a%W~jnSMLpJnf%Mc$coNG6jEcG(Q%B}<`VG7wD6$HYrDdYw709~69Q^AN7< zaQygLjvhVD^z_s(9(w43ZvoK<{j4i5UVH70YvWiST)DDGY}&NOYBZYsYWOrie$isn z+B~1zoMH2j%f^*1@10)Y?1@GG;_d-9uTOCO`Yd*)izi-pxHxv2&WvHzU@y5`hS}Nb zbwfiNdX5}9FapFM3UK)F;am`y(OfPqH*Q>GU!1M*^A{&LGTp^JpUKdZv&iNgmM>4y zk@v{tJbL?6bgxKZyB2Q3W?8{w`^`BjZh>cBxIog(vvzeao@di&uo=X}*|R5)0{VXe z+#yJXC7_R1 z8^pcIq;CMm&X*{MDU<>?j2;-J%7#W%ogb@Ta zgX=hy%P~=Dwr zPPTvkOZcfAv1+aVC4ffLaA8()VJ1dnXvhpf(7^LNj3L>636@D?^nmX>fFTSOmNbmL zJ5TXKjisP(q@Pl|L`5~Vc8fMW|B8*EE( ze(YVAXC0c&CU5NDL(tGDZHQusVvqt-uD}A87HW7&8=19>mSoEp){-C0(FiqCLedwc5`t7hl1>PA{MpwzaNq#DcI~28De<4*{}!GVkxmFw zz98vI622tiNp87w9Yfa@@N^B!vZz+;w4NLQ9I2F=sMYG)7)aQc*4W_Mf`r>DlJq4h zUy$}&5Zga{JH{9`-1x_=x%Ng5Joig>{pD@MwK8UPDy4rP1a$+@TS(EB3~_|PcUwi0UR%Cwb-)*7QVsweot=E|-yY-kyYA)m(Zf9W zl}|G_ah!yAwYh5xY*oh?!^}+CG@Fg-Ho%gMWBxUcV_B&L4D@xfDjOg%c+UF;5^ac7 zy8{VNw)SyLlJ7|K{eSr>_y6rK%JZ}A{OWez{oNjXSKzq<&jArklTS&aNU^X`)fX>b z7-<7EX`14|M zj`8s+RuA?tn69xjIe~2n?6#pTTIQYvUqYhY5l4b8TlZ}N`DJ;cTEOT_&ok7&jAnC* zv9Sw!diuR5+vW{m;&L7Uc;SU-rf$A@yIZX`wl7`*CrAks|I_qbU}}8`K>W%pBO~c_x|+#!?-(5&M@oy{-cHe#&(M)gA%R-8PNh`FcP%n0 zpIkPHW7{k)RycO-oSL0o6qA$Ze(}T;Kl*Wd=5^pTAiUD-|N1mb0H6Byx4(CPU*D=b zjWJ#-%)R=Xqs0000@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000S^NkljBt?p%NXe3@OH4UVEjWmBaU44^ z@(`zS1*3M+1jvH}2n~uv1zO~x37QB7io7I9n*<1)II`tdfnmw9Buj~7*{OAdB-ScL zQj{oB;+?zP`<`=VrVqQMWWz{pbbt@%?3r)A|8KszuHYk-mtQ{BVp;s1)@pkc(E~cI zlp0pbjA^aTKKbOM2R}+Y0P%l096b28pH9mcv^JktO2uv44qVrkuIt#gZBZ&!C>G1A zT&@eP6+sZ4uh*l$dght0jQ_s?4jw${W-@)hP)a|UNO-ENYqitUvj!my#(*&f0IdyL zYm6}zi)Buqe%DM-FN!b>ChGOledzPI=L9~$8J@pHSR z)TdUhN-;RN$x=#ls+{S#oWF<`TYL<-~L-5dcU4^_m4N}>I4 zn>KClK7IO)WABGp0XTZ}XaeHb+S*diz(Ai^%#}EBY=VpPS@wLoh2rdWM*im<+jjKQ zwYHU1n@2}ig7)qNwrk<~HmkB8pZs`=ve(XwM{bevimdO+5RZEli_y+_Je9k0<>DDY zFYDUNFP@h@Af<>64sMc_YQUkBH^^BXJanIfHj2Gpe+;P8v;HMoGoL2Y8K)GK2+i;>26MAb)c2y_==y zIUGJY#_8EM?%U)M^KH6&TWIgdaQLMIL`t!3=lvv7X_UDm9vO%7fIa|u^yu3UYHjS*t6Q0#E%5u%IBPN% zmTQ3}sRxE?T@yrxfloZh7r*)>@4Ru6xBvJD7MBXtHPp4C78+_n17H~`Ap>l%RJ>pAj)kJ8{KU21`$8Z-h|>J5-@{NoSse4iKo z{l|=sj8F{>)w-cpZ$4KbAPAvcF~l63Le-#@qO&tAeLuMcNCCbCv)S_;X$%y~5xy_b z2$Yf3n&Q>^9ZP1qdHm~p86SR^y^nu^pY8u==I8S(pet=^nwxVGL)QW{hMIAyRBCvh z3*a{wU)DCptn+;rFa)8(wP5tp0$LlodRtH$LJ5&-q|_L=H95;8k37P|4?oPleg8oJ zjtALt|86Rwpj0IdllxHZ1S z;#{3dU6U&)W^$UPvL;dnA;69K7-PugayVX$QcaUDE2=evQU=?CxF?8vf`#!#RHY8m zAqWDL(rCSM1bDXA=0>Gb>jfiXj>eLLkALYN5=oaz-4JsGz6-7+aBP8N!I!_bm+wCP zd4`6s@;Cc_$mZ?aNF*G>$Pk2JhXN@LLKt@M8K7F%i0jvIU59+WgfVJ)Cv;^L>8s^( zH8KV~$6#3o9T{8)d{^Rol6Xv#hzXK0LBf|L;}-K%H|gCpz+deCEM05YkcbHqv4(Fv zCh%Q>?+IdV!|bSxl#)WBh*COe0z@{L*O!(`PXTnbr&xWn4#FemzBh>H3OuKgftueq zLM&-W_!h~SAXI`NG*}i$dFQav29$y@ETWXl!a~8+>(!ZNbs&Ra=EX1!WT{kTU3Z4Q z))Eqp=iH(9o8y^iW+3i16^zDrC5dK+;<5Mkx&aO3CrNk^hKfR=tZ(1Geyj;llTSSH zgi^}fxO{mW&vWTbYaAk6yV3Y=fS4`tBzRV14oPE(S&eToyW!(6i#rW-Z9#49EbXh3 zlu8w}*7CL2UVOC)Fx%XU#lDG&X%hq?+qZPn=S~rn0~|---WAA|;7Mpr#`)4$_poDd zkeDU$B)Ae>c}Ki4X(5=Oq{UVUAs8FGsY<2%1*IwqC<5h`8_{#m9iHy!$gbYCYr7mB zyTzqjn(WpMq>~o$m?X9$0*-)Y641>AXlO+fO#>OVi%*^C(o8kd*edRs?aOB8;Uf;FrGx1!m{9s2% zR;=l0BUoBwac+Uu_AIUxD;aPlyz}NXe>yeI$L?K)S)T1Q?CPaqhK|2M-`WhuXoiNa z>Pn?@0xY}-0O0l451)A8fzJ(2PcLq6X-SIit}IRzaPibxgcPJRZ8%ck zNT`)UY)i1Nucdi0F$`ZC;pBh(4qYtJk!hh;3m6)@s)L|j`Qk{n47!#^7GF>`!8V_Xdnds z3#i;J_UHbZxxoG3{N_JBvv%!ICpo?KMcTfeIKxC^X7ZI9)0wQzfPsn+l-MZt(B{_mP(l@ zic}DWwcNF9qrW|JFgSS~iO8Cb^$58L}o1l**t*43YL{ICFb tmsTJP#ApnYM@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000SSNkl;b;o~upZj;4`^|H+ov|G|w!_u+gq9v~u3S<=$kI+y+bowb(sUR&@1?C8;>xq99Hdt=P5I9B6A(3K>~v{L3m zk|b|`_Ol;*;e*TvAl~nV0|(CLIy$N^8e{e)iORUH6MCL)d7f(vA*fdCRI5$hY_?EJ znJ7x$bR7TD&wcLU#Xkt(z<~p9clXBcYOVh&lkubB;XZF{YzQzI1I8Etv^Hq1F~(4< z)j4tEoVsyi*$Tt(e7#=#vnQVT!t@6QIDGizm}RM7fhB)xy67}7bDKSdx_;+OAHTm zGBGhKY}*pGTJq4YUH9Md)?0^v1|+{1;Egxl*`l>RF*-V6?b>yRtT$Tx_{c@R_oifg zw2wczr@+LZ&2YEHp_h;I`r)(u!x!&m=S0Arn{$xw;f0ePm(QQ4yTfARXg>o3y<%~3 zW%uUIcMhIBdF*u{dQX7k$B*Ze#GdKyF4#MEY?2F0RsQ|ZRnC@r`15^v20JbC1&99r zfZjfze|`F^9DMovJp742W@OVCZeY{f=dt(RJS*-1Fa7)qzA3YLtcOg-74^EA^t?>z z!i6)Z0JW+sR|iQF{HN<${@r(Pv8uHeFTHV{s@u;;CVebNGBlQ@yFWmB7NLP48v%2) zl7z%SVn72%2qw2@`0z(3dHJ;D)ak2i*wD|=P_NmuXaBzd_pPrz3Gn8dzucjed2sjc zEz%gn;kRZvvCzr3Q5)N{=pV^qY)Pb9b79#jxQ1A#07?5AE0`Fu+5X|X`0wc|3pY!A z=tC1y2;o2W*jN4z82=4`BvH>43K^ZxXE;5zz<<1x;enkVMoPK|GiV`*+d^U6vC`}* zB$0u*9fdeCsVmqq?lRwXFUL+@C5|=QwvEfd!Ld&RTUOIA0gTai-*Lx?rM2e7`4vWn zd_d6A8>GSpqE&AL2$=#ThBdfI8N%2Q#bym;_e8*%`WTn5EHOOXk1@h}@WD_15wH+*EYrsd!7pzS}lbwVdmW;1{fU4qBKMzEvh5|WehKV`)Rgr8s#6q_&85J z^&~qU_!!wjH_gycZyBl$O}U{7TZX0SC7gms;$&zvT4XXVN(rY8kg=80j+6ppKnR1; zaCN#!M{hvBFN11mj0VRRI5yappi!;hIu7-EomR8K{Ea30tWM%2je?Yrbp;ksU8vCI zc7kscMNM4S!5Fo=4*0fG`dY2l7zZN)S0F98|5Mw@$C@Jc;j$6v6@57TC66WNe7?t(#2l-Ooop`3U_3JtT=CPDEO$mO+Sg zxif$51Q}0|^#$2L5;$oJ1%|*8WPM527i9d~z_+2nl&;d5wTWXzsZ`ZBZ(cjv z258Cs`}Zftm@89Lvt+XY!}$nLB)C@k@p=H?7I+fe_8uyX#*^Sl@a^=SU@c{-yS5;@ ze45_w0xK(Zk|ef{9C_i#ZGgpgu=GF8&d!@Cin(XkMn;@@!g_?`q{X*7k=qvQ%4K=@ z6Q5$+_8s`L4JE;qx5d+??w-(A=IC6;b!2;!SmFej# z=HS5}e12|j>K1SoxV%;_^?LZojT=kM&d#%C(-3{KNaNZSY)fFb1#OXvdlGyJK|3Oj z1Y4%hZ2`6oNvpy1p`S1|P#_FLW@hGevAB4sE#3gGz`Bm;;K2iPd-pyV6pOXJ0|Px` z)5akh%S%*B6*~HQaHUv_fGgn_ug`Pr=sc76^kCYNP^5n0SLgVjpS{ZXa1Tl;&YZcZ zTdl^8XP062X3Cr9?|*?-^se0fWukQJjN{WvP-^joJ;O3~fhg<}aE z2`1K9QZT+Lk7}o3cIFDNK7Wv;RAjKPiztdXbLOIsqNw$wAARkw<2cs9Jn-vP&G%}{ z-6x;?w;v1*jecf)e8}8&*Ji1d=KA$TZY&g$j*aI!G09Dm6-_s##tx zYo(MZmrEyKeDOP9tJTV@EqEC?@!R6>2LNfywi%e%xpQ*ELk~Ur`CKl)Lu)d|7)L21 zv`&&F(P0#Yw=P|pdg+x{o_$RzwR#Ftz$IXYb*=aZ0a!H~*b0ob$M0tvxC%`19;c!I v55RgW0DV9P2mqfnv?0&}7J=gbb+Z2jpX;6}?9S2h00000NkvXXu0mjflz{Bc literal 0 HcmV?d00001 diff --git a/core/resources/watch12.png b/core/resources/watch12.png new file mode 100644 index 0000000000000000000000000000000000000000..d3b37cc42401ec18ad1ad6ce3020064e9828cf45 GIT binary patch literal 2630 zcmV-M3c2-(P)@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000S43q6~}+~t@CEa_IR0iNgO+?NkX7C2~`Lol!~$l5EKd} z2wD&UArKNMaO`xG^0!draIJJ{Fb{pH+8P9n3nYY|~ z`7mRr2^F+(q@%01%=`b(Isdu$9KnmQg9rbSh@#?WrPM=8=|Qb^Dh#6urSvSQ$=hyw z!?#|{_W;Cyd*QzOPTFhMME6Un-=dUkaU3gjUB`A^8`HEXmn%A-uSAtf&6H9KDdj_k z5xw{J+i%JKUj($)Vr1m+-v{-Hc-)Qp`qJ*eKp&vd8no5`D5X(Kp|z$`sd3`O88tVT z6TV*?_5JcYKKrcHw)U#Rf2KV9O9Ob74U)xwHagO*mCo}M@zoi2%1hn1_7 ztXP@AbWF0@Ii7y4|Ecb$EelRws<_bFg||z)N?bjOkmHR zj~=jXbKUmso6Sq=VeklXTAN?yw{;;2IyKdyVoj0O{O%Un^iAb|` zoy&N61=F)x`ue&6Vcm4oyLJGB0LGz1CvKGzqr1DE%b7e!&s+3$nOLrc)B-=y)O8m4euJ?TLPE5<Hd;MZz^v7$}S@1SD8y!wmx# z=BD||-dmYCdm6`$ar1jVOroQYdZ3pH)C0|QM)K_C2&tedTBuZNB$FOm<1{tcWhkZ6 zu4@a>RBH;$)QmrwVS1`WJcvHN>xSnT!vt=LS%WA%QfQh7)t4-zrSZ_jcZvJT7%RQB{j2CMRE%P z6_VSsJoDzn`YbYo7w%Qx6sqKf+*62k!T3j)D5B1 zI0Cg6gta_|;ZQ1-ky3@t68IJvj}!`(cK}9DXM%J*z!WYqN8mXE&utQL1g_o4K;3JU z(6&ullP<}aAe4e2)R;}7E}7Dxq(+4WAWbGy)Im^RXg;nP&pcE9k?#j)wOV8K%1+j{ z`J$ei zoBeZ>K-Ku}6b$63+tQ7o2_GBS=H{q_E4b+XN5sk<*+nATw!G5pegRylJ7l@Qx*h;QjbP7|!X zHOVb|KEV2oLwKfvYk*^bV_X4mh!(0d7io1hT5BdIW>l?KyO7JxSAZfAEVpDo{po?( zo}P5~4L58to<2Xz(;4X8Fi0wC5|0_gmO;Q4jp&zZ99?rp)B;Vpu9%&J!O^q!8!l7^o>@g$=UKf;>+ zG)igCoSBf-YI$PczI(pigc}F4R}lah8QFh$*RI#!Ff)@~ACJex;6M+y3ORl36bM18 zvmM(I*arBO2*);9y`}}(EW^auMMn1h4yq-3yW8>ofHP+%r0@I1`|tnS$C|Bt9(bxL z!sl(v?Yr;3=f}Oh{d>B*yTtJD5K<~;XERLCW|3OsIyS9s2|O>x<;z*}`3l8CiG=6U zo=Or$l3cFH`1rHBQYoH&;DK-6)!g|iz@yC)@NXGd+B=ROJ#?_8rK8r;(z$DVe990) zh~C~Vx;k6wNF{M>lb{|@E|&3Jo7QBERBHm)u_+cSjE+vq`T4Avo15JC^Phk3-e%-= z;CORgw*NGm*}(ALy`SH^X3fz1v?k_xuIlVe8J_3R7|l$w*#h}|nL?qY7Z*!PN+}A3 z#S_2y#SibP)$&V`E&!*1@LG`nIvWE0z~$q;2aIbKksDpLI^;kR2zW-zOh9ZF86}$Ut&$Sjl}%J)Bpeg07*qoM6N<$f(8ij*8l(j literal 0 HcmV?d00001 diff --git a/core/resources/watch13.png b/core/resources/watch13.png new file mode 100644 index 0000000000000000000000000000000000000000..4010c1bb0bafe47577ca5943b12f57b6cc3ee669 GIT binary patch literal 2630 zcmV-M3c2-(P)@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000SXTLtPKk4)BR#7_Ld} zuJ-o6`<`?7uxnXKO(DZG^Phcp-gnRc|NNh4-{%~`JBX*B{&ll4wJ&OI2I5$EXsuI9 zsW+9<=Z)dnJ$r8d#XDIKK>WT3jvhVg$8qrQN|}!+r4qJnRXxvdkSij-}HHv1`|e)LJt=v&eHVFY@YafafRZZ?);qS{P%PT-LmCDJBX_^mvO5bqj7C z9mH{LCMMqG`0>}x+}!LV`}Y0a{Xq0~K5OHbv9TAnD#eR^eeH7l_CZ;xggk!uEZ;r? zy*;gbWLFb6UgL6Iug7aAmN+xM!Y4o8%S|HzH*9O7WVi6h|2WJ}ouxf((cjxfI-L?r zOU0c-LnE7CdTHzs5MNcm(W9?sl;TV_+hmQ5Y_XSDOFaDZa~xmFa@VbCI@1>Ej6+vf zl61x;m3HXvOVQDj#C9#*z^1#)<4HF<<@M zrzsZlOr0L*nhwEob{GHs@;TyIF*0(E?C9vb6BxSez68)(-*oM@Jyxw2Gd`80y)8kk z4C&UOPHe6aMH|UCxbW0}9N-^6yO&gRnooaaKZ83*(Z*tAi_cW0gX!subal0%wUKw+ zvF8IoAAme~@Wq>rF;+I4=F(!G=g%eR*koaQHVQ!)87iSBip^Uy-*ElRfBlq){`wOH z$rPXZ@_x4NdOw-=KBCx=Nl6OLLrk8}5yzTD!c&8T*WCeh1K3jPzw$g!0rKq3DofRX zehqfQr5YKu0i_{UU|R-D3WNY5z!-S$>Bo8K{(Eq}fCv6zAH(myg-l03VGW`h6f5Ak z4*8-WjukB}8GCs-zZJ*;fvq*$g21zlfwi*2_bikVXv7r(3T&$mDWP1<^YAym#>>YJ zG49CN?a~;t#q%5i zhH9vAgkfrYh56Ys)yPl@4aJIPDX+M&sF=PO^Mh}Fla z0haTcxn;#0^EC=3&BgJHM5`qt;ZrV$1c8q+*bM=`ZH#to+rpT-&052SiB&S~NmAJW zr8L@rYYQA3Y)cS@RpK}%3_}3g26hm%4pOcfYOz5INO}TGK=I-lUcy0SETSmFbzPKN z9|4}NHH+nPwHpk9Z(-Sz(Z3ufnY5`!8qW~~j=*&UjxBI3xc#pC_@})Ob8KvkKil&+ zboF$R3M94I5Y@o038XX#VYub)5h|fZOrJvtL7`B@m{>IgICdPzv*mL5E@KdZqmdF+ zP2<=C-<9~Tz;^|nQ+L6(1*iHksj<>i8@R;zQ3+Z7qdwQq!B%_^5G^lr{D*j&KE;5lMF z`@j<LgyOOn1I;a?GaMKp-=5}Oi|T1}D9uj#qD=_3sRW%;p> z{b}A9y)Zt0jzq$zt0}^_Vmzn*STm?=7Z?3mX7)E!`&Mu0XnTre!bkZA?iIy&5TLQa5Xp1_zC&8BxG#qgx*s^}# z7GT>DtreI!_yql(8N#s2&l{(=H$sKT`HB% zJ^JX^_c!QH0fn~+0628$i9@@0fABr?^SNuAo0FoqyA4a#I5GAT#u&0~n{cGSkx;9| zSW?h8(1dD~Ve<48hYmc2F6QZIX{K6@IC*l))M{bz```cP&lsZ&un4?OPo6OhFVz2iP_mV9yxH}U;nL&W>#0%v{G6Y3b|K* z_OoyA%jXv>jkOkmS2kw;hX{cBmTe0#v}4D6dp`W(JMT*-o40{TYHb{)wA5OKQ52WL zu)HumedeiWp83gG{m%%EtWN{8zl-4a9!?u!10z5WS1;S!V-1)GCfRTX_+tcYj3%H3 o2mrri_3w(`0%z`+wvrNPh5!Hn07*qoM6N<$f;lq-_5c6? literal 0 HcmV?d00001 diff --git a/core/resources/watch14.png b/core/resources/watch14.png new file mode 100644 index 0000000000000000000000000000000000000000..1b17692ed26fe129194af2bddc7cde4f3ae301e1 GIT binary patch literal 2609 zcmV-13eNS3P)@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000SqNklU$;ml6mwj2w zhjWs)fLwIVd=GoCwfFzGzV&@;eM|5j+LKQ{8HLQZwN`7wP&2koEiJ>J@?%HUzf&s}8MXY682?!?PUv#}qexc{!WcMY!zZ1VJRjgMT-fI(dxttU-5Ij!Y&grlv|8SFO6F>%f5* z_5j{n0t^fcq-!+>GntgJc5Sbmnkn<>b0>LaD$AF)rD%ysk|~Rhe3VSqA)2u1S{|o; zSq#HAup=fN?G9I8k>c1wmZx`}B55tKa(OGRYqPKrZgQM>VR-n}1AuxHzzsLt`WM5H zeOtDyH>y>SKlYzsBxvKq?~7nrlCEVjQfV7$2ow-c+GN`z1R4SjzHaz8(9qLqqfh{vW^q0Q>eGS{(%8eVaC|k&a{W%-@dj!dRBA8(iFo zNq$)j(~@XFH}ON=u;6@C4G=GYbX-tzS~&6AQMy|#y1H5!9v(G%d)Fog23~v#C;>=- zFbsYdk4JSn9p_)elRWl{%jN4_6oPC=6eR?{0$)M>4d&^F`HP+iG=8A*e8t*s$#{A* zFB}*_DOkC(TXuD=xE5GLGXo_+5QLk1d-Kxw0}c%pNVh~$p(fc9X$b3cLf-k}Os-2CCy?0n#E*7rp?QflM%Q&aTxbfC4cuDRy=ZNM@B+24QQTBU@@<WC6$2T6agI zj-fS_Dn70)3AI2W&H;pA8V!>Y(29rdyPM&|18m&-5q@|752%#qS#j|uKJ~Tlu(`N$eHOfT3}?Opcbxb*D(=VyIA|G}xxVGQl(i^=bvnvZ&Q+n3lt> zKYWP3O&=sw27aJ1q$XwyM5rmeK1a?rAnoA$1uV;4I>-Rm3`0FtsnnK%7LEm`CE5Jh z)x={KwYtK!1rbYNTLQ}zSO#qS5OE}~EpTmtV>P^Bnu1iq zVWBj~Wgq$|ANlm>S=Q4<5NP~BGy>JojX-T~Tv`i)Y5~JYQYw`R!?4x_@J+45{l#Ks zJ3vP+MlR+du^ThD1retSU<(|p;enFXaA7nSA>Y|X+!gqt!1Fc6VxTtLVi0RpM=8b3 z%)G8vs}s%cy7WBve&6@Za=AixKEsM+38@v1C6;C%aRgCE5OW2wh{Q1(0EQH}mLTRz zVy+Pq1XDDY&=ielM}liM#zdButYNk(sGk_1CFN4B)(8S+?A`m=GfjZAi_ZGy=;)O8 zJde#AmeVCq;Vt-B&EQ-7k=+z)NvGKH^_$tSaTBg=K-dy&c}~0$v{0WuP15qwT66U1 zsc>Oo{z#=#3V=DFx>S-q_}~+#^7;0zE3Q~C|9y0f!LyR=>R!@ugQzQUmqfr4V2E>T z9K7L-sCt@mO)+z($l$Y2(c77&P$+Wr=%{|;iC^9{F)>mEjsPc@!ew^0aNYR$Sur{~ z$?CpN+RXyt>@=n!Fq?v=Xox!!TnUk;M=S}ZZ1kG~OcR3Y0)x-|nVx)xTCK*hV`F-9 z^3uI}FTU006Li_g|jh zy7lTU32kxj==R6u! z(2GE@DE6kZ90>RA>wl)TwJosiyia#l`Ej6Aw?=e<7;f?5dFfR};#`C@N(HM4=sZn@=-Z(MZI`kS=Y zk!aM_t*vS4I1bUMjbRv^Jv&FSSf*Gk>FMct6$HL07Uy1m{PADjS1!*kicJ9nfd593 zce)!2pa)pJcI_pd+qZxDYpGOvy;35fl(x0jG6+IXsjyb7RnHC&AO1ss|0Da)``)LK zZewpJ@NSv}E&^5n*1H%V&`7hRyv1$ke*rkZC4hFoZJJGEUF$UR`z*~AdpGSrVlau$ Tyq~MT00000NkvXXu0mjf(&O}N literal 0 HcmV?d00001 diff --git a/core/resources/watch15.png b/core/resources/watch15.png new file mode 100644 index 0000000000000000000000000000000000000000..af32f9877a8ffbfd9cf40d2ab4feece48778b348 GIT binary patch literal 2534 zcmV@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000R!NklN~%imIhmL0ulGpsEU~5T%4Q1Op0bpoAvGh{UmR7RPqH-s`)4_giNA z@LfA$DT_MNd79<_J7>DF3hgCH7>qHrRL)c75DT>H?M zSr0&b-hi!J-?wwQ@?VwG*G5s4vMkHDZNs)K3&W6<%T>x{Pt|I5skPQY5WHm=jhpVe z>z3pH7r@ZakQqhkN1{mGlTJHfS692;+uMl{8m&QV4S-S_r4(9gW@n4+-#@CRrsiZ2 zG=^)n^7Z%Kch8Y84KOscuScoIULj=e{PWiu*=!1>U}15I-TP-KRzfOOjUV`=98J=J z^#d9Dd)qM#NwwNwXz1U%R4VHuN5+2e;Dh)7;eP=bpA_G_S*c)eE|+mHz4UCuGz|`p zO|$it<2?O#z)U4g(oNHqPm@fvFzsb{Z!*cw0|CRshnbsQU`=0^GtTG}An-hO-NucZ zI(O}QoSovFK8tldHV5|4 zF+Q@$cW>|Gf^{wztZN~3T6yAan~{U#^cE!jeeEKj&xnPE+J*i7r{~{&ch?(0@Tma1 zckj-49;5ku#@w)BwVau+@We|;*fU$;_N!Vbqz$q;n^m0&a(M?gWzo}@roAhPX&cxH zlg>_;t1fQgSS`=f+b78yMNS`R$8{~?dEz1|<>c7d2m65N698MbeE(U?G6pZYXroas zdpy14Fvp_Pxbhqq%Mx_=rfA7nNGVW2Ht*2do*>i^Y6x_^YJ^~*+a}po;Duca6pTd% zSFd7esZJ)-amCQkv#$XYfL;L@8rn0cwSMT_bJt4SHhJOo5nel1VDs4yZo;IqCxvNA zbaR#hA{|?Js;L;n51}O`P^lba!y|O%4Ep;!#K_1|b70_1YwzA&dx0W=1Q3G9(&?nm zW>X9woa7(-6I{H}MGHymsw7GX0tJDB#>dRpu?5SK2z6``oY`YDncc*${~RR@73F7wKlqTDjh=uh@A^%kIrY*zyk){!7{6MdO zY*>@vy-E+qCT8gBYDa6#tFONCBA_2YzWVC!3$;cT3K?c*OY9s^(vdeX9g_$_!`IXs z3P02z_534|(0GBy^J9Qz2tU-cW+Yx_fRQ7MgrOppa`pQ4n{EU;0n9MuJE@cdz}T@x zW*RBF6_`m2&(|mok%BM+)6f`FAOr{jy4e>gO&Ee7#-e_pX$0U0P_Akm+n`vKgkeBi zTi%$TFRlY}fGve!&~a=-YbezejwMhC6cW#my{b1(TCJg(n_uF$KfRytU2_@x_w0%L znq!x(R#zN85i)$(2Q+mfO{L=DI2KxKhxisQm{An=g`N{w;hvMXgrE zb#0WwY@Yt4sWoXKe+e+1mPg=s&N5><|P&h!7S^X}}N=CCn&drdIR%K?~QGSeE3g-&{)~ZBh3$t}RH~ zU|Ryqj6trv{$?Jy^N;M_{T8?W=x2QGhAn7e5}uL+en>MZK_&{AM~@?fU}>p_);fxB z5gaoNqsdC8s$dnRCf+N8- z<1vYqEsM=I1>Wd8bhIX^RO&=gD0l35>cu9&eDf{P_Z~fZf=0vVg0p(*mPct+eJo30 zpR$nM6w9}yx$(9woVRHc2_uHECD`($c$~CQJ$8t!69bQq9#z$9P zdu-c8M@OOal1t8(qvI#oH{sK^?sRf#gQP2QS46-P@gZ8?`Q&?B~6g zUZuaQl_*jS509y8b?M-fPyYH>O}PWW{6_!)wr~IEE1Nf8ec9yX{F;`Qr0D5x$BF{p ze`_xYL9UR)k^)PDh!g_oTip^DC&01s=N&a~N%dHpKuzvlfo@=i8_Fd_8cD>fJMQdd# zrDPbYK&i;{y!z~iAAa!6i!c6ds2OgWp*{qR^C_Q_FA4zhR-H*)$C;lq7Gh72G{vLO w8uPHh?BG{6O%rg=bvc%Ns0$`@Vizc~u4qIekMm;e9(07*qoM6N<$g7uZcb^rhX literal 0 HcmV?d00001 diff --git a/core/resources/watch16.png b/core/resources/watch16.png new file mode 100644 index 0000000000000000000000000000000000000000..e8ef8c33096febf8d6d85ab452b2b9a5b1a376ca GIT binary patch literal 2655 zcmV-l3ZV6gP)@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000TDNklh5r}8$&)9Ye4c+(O8t2(=Bb{Zl+)jTBSILA0b>jRS{t<1 z7-J}x%N#rQj=6H>stCj2T`9Bo?A^O}?n48-_S&fd-w#hoDcf(ob;L>}qG)ZnnkjJn z^fIMVjeOOjQrEy!huEp`*@vfErB0N($m#WG9G8Gk>KpI&ylk=PM^6% zCX->XuZ^Lhe&INlD3!v`jg9RYIC}KJK0y6mfFnnaZ&%73-MY2M8X4Il%as~0zdpqe ze=Zms?&1%3#M$2OFqpQvG@0eyGgtY>*Eh5CW}lrS2}Hb;ADyt7erJ+R9TuDWQl!(J zVr8Xx%S|_J?>%wi@B|S2R)F#GsWvHt$y6#ShldB`;&Ptnes-C2xi0SeWE)*^NuteR zQ#wL-%BQWvWm8X#R8JJ!wQwUg>9oh)x3^RF)BM+Km+^Ftp@DAVabJ{5;VrRPTjBiq zU!Azti4A~nfBT!SxUPNk?%mt$T)x6f2Nx)~Del^Q0zBuhk7t5AtAR2e*OhpI{ zga#U>IcjL2yVJ()PO<-Jh7K#k(3VXUixm-#cHZ^I8{@BQtrxBXICSXLfHC^}yLOFO zj$^a$@CE+&Qj(AF@bP?$p8hztBQbyhG@wkg!)seNL4ptxQGt#ndFRX=Te>9u{V66V z=j7nvw&bZ(ZyX1501`k-{Y*6K>qH{TTjv&e<*f*Jj`4Gr~Q>%cU- zvF=1@nyug?{VutVog6-SnJ_dA4-d#2ZrJ=KU>I;V0JPR0-?F90QcCgeOqNtC20(k4 z-xN0Q2?f`Rw|X5eP==Q%tRp*L;e_%;ebJup=3njkdS|MTmdFw`1`TF?YohX_JLM?#`H2AG=75{8;+ z)MI#f=jVYQ02}n5d7c*vfqP*tOCgM)G&pgOT42xyl!j1&Z5b>n5CVh%)4Hd$Aq*i1 zo1#I(P;WrcfLuvqOG&Y+2*U=cR8n5Oni~b8K*9z+;QNka3=}E~*O6!hq2fIN1-8{} zDFF;mJ^EcN%jQcDJxp8YCPLMm|8;TP z2{6=Z3QHKyzq!i%r6RS!P^}v(HAAMLSy)qCTx#%xe|&`O^xNznO!0T$`YOeWrd(BI zi;C5(=JIOD#pRGvMYDW*i6B#=Zbe%F9$Fi_1>j4qP2939ntO`|A-Fu1C$n0oQa5A@ ziYr-7zN~44%_*3jd>bhxxm=F(Z~lU3|M?%tmlU<2IUSBAhT)i^K55D^i9=ZPy zxNvem5mylL1issBS<4}%q);fLl&ZA=>e3kXi+sKUKwmmRI$py<tYy|Oa*^AqFxV}n!0ROR_0%BiPwO+ z>jKI6_$y1hcYmx=DmDHno$eHydsEaht7NZcXisp4&f`YG#C1 zAW^{l)J5L>=>fL%r%+0B`t$`|sgy50|NIk=x8%;Xrr`QaI5F|Fqq}zPxp{T9^wC5j zBKrH%*kPSt9XWy4hR*H|982IxFb#w6Nd~sIZ3L6)cV{^AlY^8NuW&;uNv#%e=FGII z*Mssi&-~L~W0av8M9!>h{|E}nao-STT0p!KFRhNuH%r+7C3u$ zTCc6;Sz4NT@r4)un0Lz-QaTcecqW-_6Oo9E z=Q&uGB$FvnD3r)%OD3N$8l{ZL=d-_j_0^}om(Qz6av*g$`00;XI28;6n)0@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000SmNklyzAZXoxPo{)6y17DWzbAMqJnq(m`*!)W_s_PyT8jhez${)R- zdH~|xCT!ih)lQ}Q?$ugf9fo1TFl1nwhHzcSkWx^s)+v{(VX0IVK@bYx_jhRR-f-)! zSDpI50Jd%0<~UC4AA*3-$6}7(-QDi=_I4ozXboCx0F=@wrO;YasnpoFZz!CcoR;-^ zeYjGof94zC_`<>W4X|g=%d0)F{;Uu(wqe6+Gm(g)lxAk8!2Uy1%#}P!bxGCJ#9d9) zR-8SMrn|cpAq3T`$IhM4=|Z8%$&-`c`u_L7^5eGyF#h|4UAy*O`2X=YfBFOcv8vPLj@alFqgSQ`H0qPQ>{4VV_rC8KYLpv9vS6%9VXW2q+f4 zOV_X8v|`Vm?T-QK9RUUh_pR4j4EFVP>a}ZEn3YP6M|Y0!=YK(8Pdk^bPjYU*#aX=$ zBSSO1^ujbZUf<8Tt6bKtj-z6&+_PIUJ$8h)l%T(_Q)DtJk#FvV+gEj;qnBuTTv8T~CpA~vN`us#}1=MEo!>ABN8u>f$(Ew?>l81jq{ zesHZ-ELM4B=NLo94ldge!LkHBy>XH$3n>K(NTh69IwAxb0u8=~kjCqQ9vdUw%Hz9F z(P8IUv8yvOkUfZ_33Iyxd~4aruwVXWUY@=iBzY97uDHFaO( z`Fa6l%?g)S%S$+VEK7HHJ0QfRmtOM$U^##s9Nc%6QW$M*DYDaf{xuSzyUoCIEtHVd zJx#T)@B+Pf<%>F@srj0k*8rG@@B&RsQc_C|Fmh~$AW$R{u3owF{Eq`m0Zh+RH^pKu z0Hd$xn5sqT(O|`GYMw@E2o(e&n1;rX0wGxVM+_B&N)rU&1r1Zr*VKLRd?;2lmTgli z1o(bHXJ?y{&F0nsaUdpzV8FIbsWlYJ0j_1BkOU#ryvC}k^*2?iYs%G-Uw-=zzWm9{ zdGXn28s9bNo_@_6(*Xy^JU~-3Q-9LENTH8(-Ksg&w+T?c4T8&dP?3sVF^J3Rvl zLIrivxT(SbLao{MryntQ*%EHtMx-o!-^aF1!cfnjLAI%sI$17Pdq86vAPv}f z?P}r)i>jw^EkVQ**p|RD1(pFjx-Nt)>?qvf+qA|AL|3~suM~S+kVeTw8cT9~LRtZDJ z%uGSoYPFM1fU4~4OZ+|v0$C^&8R%(Yd9r|k#~0&6Jt*<09h^w6O(A`yq4w8qBAHo&o1 z1mKziM}jRIX-J~^?2R|kv1BPvKDdSBBL|4gn_CUprl3ChJnb!Uip3IP7|NYH?|ZU& ztV~M)rR0|SZ6RdGpN|vL!eYT2o0rcheVHcisk)kqBS=?hmPy zOWgO{pF>z}%(oi5rk*`U%5E%obo32XE*D;|*Xs&61$YaQ?B07HJ=)RH*1u`fIypQ# z$tth@S6v>an41TP^u}i6ElqLe3Jg9 zt<28m86BNa4?Xmo8>Xix%fJEPn4AZwRDw@TO`Q^BW0R~{)`OVhjxys;>d zJn_T>*^L`Fh53B_W1XF?qQAF;^2{{(*&ON4Hf$*tI$%rK`_B{X-803y^(|GNU{NM~>ZxseF1+xg8>Xgm ztK#vf=bIdEV! ztkr6jd+zzcmzq&N3%oY(`Bt&qz4OkW{;8*D+4a4>ot$&d3K52iiHVb(n93tf6U(wl zCZjlx#q4Z>T&_g+$5 zr=R}&Q>j$Oi$*gSA3i)TgpgwC(so)~lBAO{EJIK!mC4QKF@zut|u;E5HMu0qUolz0;$a3v9Uk_HW*{Y}u;KN)e4l9hJ$XWF+Dc zjXD^H!OTpae7;CNU(~r=QHNnD=H?3fAAb0EKPeQZtIb-ozza=?MdMwc4S_!3tTWGC z*LCHUn{Q4e(r1H=E2Zr)R8pzX4+7Q{kdZ&&=i06^oX zS_$;S!dd=KBLK#MBQ(@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000SBNklmB)X3pYQwK-Raw%{t6vBAtA{C#!LjQij-JVisC3Z zLe&)dz)aOVP>i3iQ#B9jFr}rImXr^ar76*(0F6K-vDFa97&{<@3`yv8I!UKVci-H; zAAQd~pL_doZ+8MRAmyrBFX!xi{=c>MI(zLcco(*B-@Xj=JfM`?rnMT;TCYnIHKUZ8 z*IJ$X>}Nmn^>_0<0P#)(9)5Vr8Xr%8sI|T`Ns^M3M&vlAbX~_ZO@l^bg=W)Fd_OeO zG$oFsL!Oua=X>wHZQ<_%c;u0@?&#>!50p~>lFMb{p`mro$jAUffYzY320$r|QVOj# zmoGPX`Q_7Uett=YVR*XLYJT#oU%mg;cMY(A|Nl-{mN_Ma$i3%1TZ}>>i&C0uwZV%g zE3B-vX|^GV60$aUj$q4pk>R0r2q9>UEG$Bqdj2tt1Gn=}0M51P#r z1MI#o&(>j!Eh9Fwryu$tWUBi2~xa{0kAaZ;7$@3-`XU@=*(`>wEkWwixmX=yO zuf29_d1~spgFyP008>*_nJ`qROQnLjZQDlUQl-hyo}J}Ht&cn2SD@%giY1Hn>$8-4 zY_fTq;jtX$;Vhq?3B+F$c#s~2{8{bzu^Z8QkR_3Y;gJl5qK%XS1>_1gy=9L?L!u$pozM~u z{k;}m|2iiButZ)jGqG_!z8~T^J$nuxe&*Losrfem96I#E^=YcVdELNgfq}740h?EYms)F)B{0*7V1hFQFIsmH>QKHFtf~a>hr)H}pi6Wcz^!3-j z_fB9Kz)TW#S1#vjfYaxytfU?qu(LKnq){4D1xX5~p)sUD2-bGQR6(jVNdi&Q5shL^ z7(*08qovW(q!|k0IH6qbm6b|uD^LV762u1Ab*0u&Ux~3SL83qjNf33SYKK?c4>k2> zz)!yS9lm()Cz&}t-Fep?du5K|{9?k1vk^*be4~i(w{aW`A++253HnW~(;>$(1!#gG z#X$4gOBE1;p|KpPf0r-KY=_?w( zA_z5?Ub#f3oF_K3wA(81*Ox z1A=C)ijs&Z@kw&YNp(NE924oz8=YW5C1=S7_hCv+1q^Uw(*^q5hDY4-D z;Reunwm=$^ogbedld}mzg=-5OOJG|9%M@4!eB!=;=PRGy&4B|4xZ}PD7`NxA@*nI!SN@B69N7_O<164DfG6Ff)a+M){q zwka@8L0@m4B#se6aLaACQ!e+CB$_x8okBHqr%<~am)3$LtRsX=v)Lj|RnP^9O{Mhn z^?K`LfYIOU(U%D^uyJjHXA3;13((DkWq|Ko>EgMpP0kg>sUV6w>6h|Kvr)PekP7O6 zOQq7#Vd%f!eI3cUx%PL1ATrwRkPX9qY{=Fz&^VS@i{5ht8Ap(H1zAtxS{(o(I#5|x zl63_c_X_Y8Xwb`Z6tgCAoYH8l=()MG2fF})eBgnPhDzzPCr_T$*{sJ%F~-sfj@6lb zHGpdh90|5uYi8I290{)38RM-jS+{mfL3n10a$lZWtwEZmVsi4aXS>svB!HCS!RhHa zog^tccWhwTxJb|pvAV^#`XO6(#Cq~M`UeIPLgJc67Yb}C-c+;@EX`4{JHThp&Z}0d zc_NDZwA+kY65#H;Z~Ijo#}|(tJ)trgmn|b6a(N!16}CyI`UO~Bq2a*+wr<~!pkE|mI0;Z?u^}@pJ zqYDdj4ZsJ^t(^w|o_gxBxtnjkRaYvFJ)@&#F*Z`huUw+Kc$s3kAKMV!GUo7Ut=*R$;ih1eq6iJfM*WZg}NGu6M1LN%4brb(^)9Bj4Wai`y&+dH^ z?Y9^j>>~&wPMnxlaUA)NJo1fy(>m6`CE(Pm=35=h?FS$HukT!Y?dE?S8yjN#_HmJ> ziq~IXVs8F2(ll{hn|v;VZChMgs8U;Qu)JKSkoPDRawJL0;$oFEXXbUgy)v_R@3+1b zMa`s}`saXnRqU^hFX^}Dr68E+P+V{*1$Fe zg`7)IDUWSgEH5`XabhN|RO(`B>D=Rc_x{h<(=<{*1RMc^tHs{#YUTpFKL7bI+_QP} zj?Zb0>v=XkJq3}?X2|C~KvJ#NSYB>Wtu}SFT32bRWW8QH_T-Zf|98E<)b4)kGVlTr zzFFj*?uG_14ovLWv1|0hAO7^`vf09QN|DoATS{q>BuSE{Dh$K0a^b?O`<{M!?}0Q; zS9huec$IF&zm>h?<+N)O*aTbyYrXvKECJ5bDYo?O2L7!8S6cxX06aR&b(- literal 0 HcmV?d00001 diff --git a/core/resources/watch4.png b/core/resources/watch4.png new file mode 100644 index 0000000000000000000000000000000000000000..630b1a9fd9c8ee8845832109e35fa8054f29f49c GIT binary patch literal 2603 zcmV+`3e@$9P)@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000SkNklDYbUWoY=a5Lp)w>;g1~Kv zXhITNZc;4RqWj~~uvGS26e(l;o@ura1j zDS1{(eZd%W{N8&%_`SFCJ^=B@MIZO_`LX}dTJKg$Wo_GvT-ULKz;lGK2*W1LW=l4k zk*$<6O35RZW!?SBPwu|-{{r~-w~x8wA%Y5Qo2+cbjsybfWa6r#sHwTL2HdM zhJ}S1Cr?hBnVDBblEmkXN$z~$f%|^_mH~eH(_gGr%KDWt^lsg{(JB;jC}pTtYaE}N zr`AXao0@j4NxO=`g}06887cQ8grL=GbLh}9YPHbJ&dz=L!3Y25Yi|Z%zk&FzQd-A) zdo#h#oi|xQ;Bofc90w;Z@aXfBEA1SqAV)EqBbCWd37%v zHjH9fmI%Z4ZCkhQ7@L@Q<_SRmL4b*gqt{6(em*+7+P?9|^`hO5Iq=+B{_~d>Bf|sS zx+Tlzvdj38$JwcQ&i?iapSf?8cWq9yeRGc3FYw6o7W3y%Gmy6#9T{X`pde;v>)Y0? zyPhIGYk%Auss_uu!!e zxHHGQZ{NcHV-7D)RVbAPDV6%gjvep)JK&vH>)HU%K6`W>@U5*|H`>1Ma^Sf${OnSJ z9h-doz@{{k!}Tl-V7em8bX;ET8WIDsgx;*cWP6!BF~j;ni;?mGr%%6Pm&@y|-~8t1 zCx9y}8E_mP$z*(!&u2L?^*i>zkmlW+e2it&H$NV`Cfd1;znq1wbkD&hhaPJ4qzRr)mtXN}&z(_W2!Q^O{h6wRqQ; z;bLWo5<{Gr6_B^B_c>7;WTsN3R2l$;d&?~!x(OHqu%3SU7q@C{>|(Kp*~>L1UQRLG zZ{r3o3X3Qsu=Q~zavb_|ty$%Q$I(hyl$!mx?&JAer2?81Ex=(6W|78u%zCU68( zFI>hjz|csxvsiQ@lp3^wy$}B_{b`3?yLR!Fzy3Jicyf}#k+n1USL>B@)M^Kxu zF!97b0Ltse*>dx{D3wIdVT2* zfZ4a z0pI0y06*IEOPf@RjD5b^W!+Q^Q zt24ixtgoCqS21xc*|w?7kadxGDZ%YFpYICdW}Us?`Dcbl*YN(^Kg5Ucyo(JRuOqMo zz6G8Io+YknT8QUo$l4NPV0!wJ3d8z|X0s`~hf!<++E^a0ED3Cu-LL)eL#{_|I51 z(97atohXXMGtYegp>EI>mQN&rdOiH8)*Ly1zQTreC0gzCygFOKxo#9kz(?-7hf^nx zv-{4EF<30%bh}&{5ay~QT7ct#Y&V&D>HuS2S zuQMZ<&X}WsGr#^VC!Ttau~Gr84U>~Et7fw~vv=>;m!p4ac^-J(OnB(fe;wYr^8?!} zm4ywNOiGl82XM9I#Qz+}0ENCnXGgR^MHw7CWVxeYPH6xQ!}R3YMk4*@9UrMHeM5$0G2nR-yh2X zH8JtyALnv~D3d9^=hUh5mNAIoQa}C09*Vs=Jl7_PV!~P-&#~yq`s91ExQD(2>9e)!m9|L{Zf#Eu_>^DHX+WyL$2Bxq}A}K6)^Yo6BRj0$v8DxoQ-Ds{qg` z)%9JNH!9n;CV`8@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000S?g|0l2+Do{+w2!Y6zn7(f@3Gxj%C=f6OY@D=RKb3 znd$fA-dk0CxIM-WAta)tqn5g>>;F5aPMta>cnf3y{xeQ8x%?ke>Ycy_rF1R`!k49t zE+{1r-gnv=AwX;Q7$D3$86TCEEy zl?ub~Mca0F-+%v|bN>{;{{3g1T(15zt>x!@KOy`3dz|&_`w#-O2CX#!N@Ks2lrswCEMG&;cOQrh9A9>`#SKczfp+hfjG)?ok5TtkP*l1?69cT^3Vwn?XmRK&g zsMHkARz${Eq&(PiU6!>2T?ip)Gy+CPPq4C5)-yAc4}bT&Uw-uO0hq7-Vs!MSJB1K0 zq|?50+ig3{M8aWWYLO=nzsj$UhE$>yULs8{mBMjTOx4o7G@azg*^qOuOcKTZe?AGQzXABn zXFh+xFvQMXyKXe9)ds&gJVC)+!@D-Q*p^^$U5bv3gJB31;HPYIJsy#UNJFUO_(mGm z_dB?`Zhm{TNSC?H$lLm;)f)JI_V&@yC!PXkuK_r8=*5j7ezIf7CgHjc2an`=Zl;TO zZgxoc76a>2n3g~Tx{WV&Y|+)GVvrz$bW&iZI~n``Db{CUaIi<5KR;!TjBM#Rb?WE~ zKn1`6ux;y?$)ra%o968J9KSxD@Q7LemU2%^(I?g$N=|XUah4t|LFSf|Ls1clDMnH{Ama0$5R` z?@gyY1(@e2i&T|QBsG@r&FP>8<*NU+S<3tU1T7zWLMJb(VAA%lBDZR$BYyp}^Bg7Oi ze!75Z8}#=3NCi!ykx?841?+qDVbsC|d+xfEuYUG!zPIlMlAT@Dn~Hi1Ds@Gru4o3D zrSS#q4xhkCP_H-fJP)O=9DmQyT05pG&>EE1NU52*SYmOmLA9wVlq3s9MWvyMlt#;t zv&Wx9YfThI)TmMCGzr77eR=8CJ1AjEspf07)(~hs zS76zKn?JD$KjqMDDH4vrwFQnXXjYbZbniXPTsXtRbe>~>Ji+cSe2u|%{dk@cZ$SuF zC@>6-5SsVyxt>N#VO$)?G)*d%8d|H>N@yEW>Q@_$rq&ufM`IcWQi5ZF=NfpffuAs_ z6(;!BXK&~HpN_MA*X@L@78`EZ&bvSGVfqGoNhSozL_F6|#NK0 z%*@4uZGf7wd-v`@DLr@Q%tb8AVmK$UiEwSfZ2mOge&{aHn!Vrr0r!3Wi;Rqn;Iwnt z(3*rPa1BUU;!5JJ=8bC$97|At8@cU+cXP)*_j3CO@1(c88{2?vI>m?YxtE?beYgT#0}KrUfp-2{ zxI(F^UV53d1BM~U=Ok+U7gjZU!U;N^U`GJA%-dk?DQM^1p&Dlkho=rn! zQYIDw9ex7G0LK7ZfGOg>P-&2eYf5t?&{Ug>%hQXDKXZ`b!5k|qCGzP5H{=HC` zssMimCXH2qMx%c3?CcVkF3quNWPqV$6Wi6 zmH^9wpjzhqv%h0_u#0B1MLs{P=jJZ$Yl}C4>1!I2C!hTF)UI9caf-#pzx4Fv#JaWJ zlxHU?t(3_0cHEOxlg?ZQ+lW1` zl>*W!i=p9;xH$o)UY+6TU;Y+VEz{SNq1g-=8@r&I&F0EukA3Hhao({4jICUtoG>356!%I%KmQ_(_lX%S$XTl_@MP(~n4^3lRVQA|#bKX%~2|NN}&c>w$os9!Di zMo%*rc*mYS4}EOs&fE4XMbh&enay^Zo@e8_4whw7ELJI%suYVAxx8G`Q6$Cka^cAS z{XhQiAgHgt0}8;YHpDgKAHEHN^}yDxTXzh6=tFxR@cqp7AktcETPbBkkqV`Zn$2c& z@xq0(|99xnua30C4e=Y`EHL%=7JoAU5O39%Hq7g7+iOPzOb}>V3Bsq{bt5r03P<;=TmPG7XSbN07*qoM6N<$g5*{J2><{9 literal 0 HcmV?d00001 diff --git a/core/resources/watch6.png b/core/resources/watch6.png new file mode 100644 index 0000000000000000000000000000000000000000..f16493053e30db409ae29d90569760d8f043982c GIT binary patch literal 2623 zcmV-F3c&S=P)@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000S&Nkl9aCR!1rMq*V& z0Y$+F5u>IiJ`!UjF`AHwsF0vB@j(L78f}!s)=25HQo006DN&ZfzUa2y-FBbzp5M&x zaj$>OY$*>B^du+wCBI+p_jAuZ=lsqUyaO8?9CRxc>uXUIZ35P5O)3oi;ULgsQ4~J* zna^DR^gCG(K)fAM-;eLtTD?DtqJ&|{&~a?jb!|-3q);eRDpjINrDiCl!yxdFo2GHo z?YHlp{=WeR2A=(C6h-?I2{+1SJMF%{ZiLWi4O(jel+q}r&|0&!RN$Zg98=TN3)1&% zFXeKDy?5Pp=fpb(IC}KN$}rR?q!b-jU%km{X^EkfCZ8{H{L})uf=|^83H$)ZF-XMV ziq$E4R<6J>B(++=z`${x&lmOS)1zO%_ulV4@SgyTH;SKpUl^*B>2ynC*RHLWX&Q{b zGRMIuCi(RdpIj|TYfF;ObdpFUnembgUq~=C5-|M2BufkP^!K!~etn++M7_?3wrtsv z9U40FD4^aF;ONnlS42_tR9|10v3c`4S*g@GG&ss{{{h`AJNWR<6kAqVtY2v}GQ7yd zON-p`+0|ULF~(IJlgMO-11D@IPK}dYVbR~$K{}lj3k#KN)~wmE;<@LJ4Fcht0vtQ` zaxw_~(R4axZr;2`&Mp)=aO5n{&9(FKcc;jv?Xw>iXg*ants z($VR#XGbe#r-MKIb&{A~V#C@lY}>~3^mf;cFN}`9@Z6=5xCn6DZJ&R{FvR6McWyRH zWsgT5pJGOJ@UANzEKAVSmmrz4kW!$4WUE7_Ge)Q()DUQhXgqghOxzV692zQ;F&5dd zuA6+mOiN4qp25MtJ`PO12H?=4p?=ep4{Y1EK{}4j-;Yl4}q3^!Im)v9T$0-MY=GlP8Zo4HN++KqBEh9FMy? zolf$vm(TO3=UujMbkIVQ>5ij>AW#q}sK3sA-SA*J5}|H*1ef(#6f;+F^w~3np=SO1 zUfJEf>PFzQWo-!%hSBc+{;X6=adM-QY+ksuzpp@zf`>G%W!_AsRtTA&=*0j zTE!g;dHVXg(OSssuDfA3&FqQz9TO!abzkGv z6@I8+yYtsXLQ@MgHNOF{4B>~Gwxq<*tYvI+ktkBc8iaz4}htZzBL|qB0!Fv zSt1w2SS!Gc+thrG(hw;KBQOn(Aq7H!5TKjyBBcpK@WY0vA86_U_yH8k8fiL|i<%$^ zX>U)NIa@i)Fm-Sha$5AG+s zW&>f=IR9k;udbPzk2p6U0-BnUqFVKc$6d4*aT;B?-2^%v#}c5a`3lPvjGUYY0W14j zh!oUCV~3#vrQyJRKP0X^_Fn%!zJ1FFxaTh?Xi2r<)xoPPik>E4QPk_2xff@NcP8=8 z7Cf&^Jno_tX0r<8QYmdoDbbq7Peo~FCW|c1*QwMri-m~UyrSf37H7}%@ZDeK@cy48 zgrHO^QS+*tpSnP?s;SmBeh5-P!WP6GLFqz~s8R#NA_xMa2$WLG{~*_lB6Xos@m7Jx zwj@#tc6{VA5-A6-u5fLE@Jl@U(9d}E;rprAs&w_Of5zO#w>*)8;NCGV9Su9KmUqD zzqy-IevwR9mK*Q5;gKXZun`=LO1Krt9!cY^0qLHYEZX{~6acM0G>Uo54 zDVNJcQRFuP0#j>stWYT30Lacvoc0zUA#Bc%9_Lrz`6Q*>JT0wleDn+Z*n8_8^sigT z;)O;hb!>}tDn^Sd2qJ+WXpCl}F57YvE5C?R4s&xkRj=0`38F{Km4T#KMy<+>X`$477bGM8Vunby_>mMJLDH--@@1+FDXxRM~!7pnvsCE6%q zO?hUT*0@CwMC9`&H8(f)SQEfAZ@TFw|BrthIP>C*ldHFFTTf3aWG?TsX3I4^aQJ09 zIx@7TTw=DsHUy>wlZfRu>KXz^f*}NkY=&KfQqBGm3hEOl={UdWeg1b{$LYe zp~)M5`}FB)?fU`OZ0#q@S^P>sM|U@ggo|SdY*S!Ma3o|>N%nr?7Peh=H8In`kzh-( zrNFgC<9kbBnNT@5N+#Z@vhneAQMFn+RW6r(pafJdj${u#bm)9nSEg&%t}Wu_u_=Zx z2s$>dA>CpScO~vc5wHXp;?*+_U-ysjd`-EgxNv5ci6@S*I-4PvD>6EIRv$e0o7<B*wnwy*cOH-YNd-r>HPERkcO{bG$Rc|Mj3VHtMvuLf!w6|dy0!xBd3W&!H z`qv~I!wE1sdWNU|Hb`-5n(mG?wVKcH@PzXHdiB8v@A;C}fd*!P(Phmy8_VtQe)lI2 zX0yGY>gnlX>(=!miWJi`bIhDyLYgLyW6{=@!gU>HXP3z3isW;75;2!lGENYL%+Kc- zAD`0odgYb<`|tf$wOS3EyB=x=iZ?`{Y0;BU9{zhK(_W9o($|iROlm2mSlQjqicE@h zDuF2_l~S49VjfdMBIb}v#<5J3e7?e|Q?Eqx^Eoj)J9*%Dzx&CLf*?>p4R{*xFBN;M zn~@1zy>H+5zm?6d`iN54v6!pd+tboqY+C-B1Aiz~wu3yl3s6J=fnBk0&-LB@@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000SBNklLcj~C3eZ$mkx*NpPEy=U$|WQv3sRC<@O;k$&*8#~wcZz5({{KeAqk@DPZ^wr%UJY_#!qCpP)c)TD#;tis~kIao`r=4`c^ivW=)T9 z97`07(MPv#y<_0bH~0S;Q2!TTc=+&It@XjazD{e)mesOUuJYnv&+zm8HrqC2dEk~5 zH+9>r?eZ8O%X8}JEIS|R)nq68}1jGhn1CgowH#RWP<3e*g z`~EgdySK1K79kP@fRy7hAWkmA>!tm)FNQa52kWH+cj} zhpwI^wj(i(Owxcdb&IbxT?Ywc$fN|JpXKP$3#@LF^mMn2k&y|zzkjWBtuCA(It}wQCk)ldPz~zer z&x--~{A7+rl|mbEQ!drepbaPuu>#vNSW+Ma2mz+ir?erCA&l#yVPvR95Js?2G#KeH zUo^zAqP;z9<#L4$Knh6NTAQ`L?>ff7LP_B}5{)F*;wpdw+p3$CpyH6<{`C7?nZC%^ zzV~B-REAj9wk?bStrc1;rZ1E@e{uvN#;-dqgs8tnwFwL{SwQ!2*U^|C0Z|!>e@<~nNq3N4F=znNGW;n z@h@QFkD=odo+EHAa2$bc2^Whkw8j=5Qe)S z+DN6Qk>lr)Qc^5dFh(uygpRFLJYK0(ZZ!sxa6noD6>D1CR}gp--xc_-z;nR01-30{ z3O>s-yPjanop-Z&+cx?J`p`xY#iE|5mZ>LdW8*SLplW$6E5YJo38mE1c@Wvgm^bqI z;%5N6qt&N96(NL!?+OA}5O@s$SKv8y4-~z+2UATcdRO(5PTE8Y!pLAX5_PH7lUNYO zMIgc4T){+9IM?V7<@~()Wvv!kl}e57&Md3bi&%i?h~?P_o*>}~lD;4rNIa{4vFJ5R4SrMKq>K^ z(a{MLh7q@K?W4!OM6DF!I0E;Yfm{imgltoaPd)fK)^FTMU|V<+TnVncD(*T0$A;46 zC@Du_42+IWs#0n3Xr;0k0rNn4d6Dhj{ijPSR%APN?6_H+8lB?!G_-G6MJ8pD@Fo7T z2si>PaW%&AJI;toXed@SmoHvnWZ%oI?rmXVp~R_E=hfig(~snG(`DcoFut5FVHiI& zH8m&3$1kyNppzcIKy7v!+Y;CfL0iLK%N}qg zyt?-yuMACc$6f998sVCP+598mNh~t z&E(`|F3seyJqO?SNjD|%e3z+&J}(@{7M-1~w6>&4rvjugR7xfCxdmKX zl1?SaHYf31mqMY$$&+L8%uHU)%#1zv?6W`lQ5;tlPyzl9gx8AwzpI&V+xA^McYf>9 zHET9Ksuh&LSg>U^Uwe4sbVo#ZHzS! z9ONA(`MUu?y>43z4BUI~r#9Yp+kIb5rP3R<#@E`|S{tc#9L2G!)@s$c@$u6yz5Md; z_bH{8Zb1q-3ygAuOUZiyxE5p`(BF7|w-!@hW~aF(-X8?u+SUwo06q`^9`$uy0m{HE ejgtGlwEqHjU#61jc5T@J0000@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000S)NklF(FaV-~})L7!Vr~V!aWp)sk&jg=(=-#05&b-Lmeb+u7^P?#z5M z-}jw!{9|UfEi@wfCU3svyFJf&-uImI9Kjoi{{H@^a@o1v7`-VBO%6;Zio!EWnNf(2 z@812MpS+R(0f^Uo;h~3KiLF>M^{_GO-AbvX>$)|^k+$o)mXwlmxlF0#t5T^dlu{xL z)zL)4`|$4F?;8ET2<+SUqMOZ@9#D!s$z(k2?r!tCyE_1bF<^`VKx>268ezWzpB;f17G;U?&sbxfrAJCp0jQ758IZhE3Vk!G&RN18s-Y~9Q|jWO39~G zv#5oJgd5@7f;X*eVMS*aDFwbCaQN^sip6;|Iy!dOJ@wviwmi6iz{ryL;5kmem zlS#*}zIwCmI2NaeC)t1a43GS|imzL66D=%jPSci2F;z`-Y9`6C!GIGl4dXA&(!MOk zx^+EBDMYy(UcY6__TD2$`VIieeddp?|F^V&EY*EAv1S$_JYL;jT^+R}!eTsxUeN=!|axApdJ zSoZ9*PaX!MOAa4kUO72c77ynRQS6AP^z>_1CV%&}%wJMp-S@9V2P&zl?^eDOIzzlOl4 zKmEB!q!e4Pxn{F9zfj?Ke;#34c5u~t562dC=bA`2yGSX}Ky#}{W_g^*Kx7~^b$ufP zy{G{pB7g)?il3)aank7|&!0HUV*_!vu65A}G93xD5QG{+4YgNgVCoqzDiN7_D%iBr zrMT?P963Hh6lvD2>yaHDD?bRVTf~+CTI-#wSFey^7;$oFmP{tj*zgcqFVum}d7$7z z@J23@3zVT28iLR)5!tXh&dKs}CeF>$)wLXBjJ)y25A6hc0Ayd^({I(S--RAMV-{t13yZPje*YNnGKc`%&T|DxOlrU67Lp7)qSR@fdhSrp%l3vY;Gt(%g zNhafF{rWfU0#*P>W5g}4>u7+X@d8Di#2DE1@!Qyb!+SY%Vt~7EyM z{y|h9yeJqrX-cIko@ZkWaq6pZn>0rE#A2=hgCA%tX*fMF!}v&v$T%$P zxt!gf{}y-u;us(O@{d@(Wd{fM?Zpe``OK$2%00J#jJbkOrK%~GG_ys;__Sj9TtwN| z%$%AcER+dk0^hIVx(-S$P5*?{+IW^_VGJm3P)cxic%JEem2%ZEGq1=OG{(uW^ZlRX z&Ib;0`PJ{BQmK&7=ds-w=kjwD%bH5f5JX@}NO*#TCn$^*P?Z{37PVT4)?kcT+=F7a z*32v{_&G2nU4yg)SM6FyGHK)2G%;7;xdPV_IJUsCBpDS zusvBXK?rszkkTN8;cYi>#II?@$SAgLQ7Tn1MlbFOo*hLhUoQLF7-Zb3f21Rg;|SuO zB<6}b2?tzTVB3QB_E!Az0^6>=j>|S}BAZPUMTRgE*p|Vv3_{e4Yye{fDx613mvXs^ z);eeq2yCs@v4w^5I{~`dQ?!rP0FRg}h`WNgCze$39D!p&Emkj~>p5hzX_{h!Pzi$2 zU|AsL`OQWfP)ZQ0GFm&#%oI(v>gOAeYqCaSX-&*(+_UTN#Ft98sFP#()^`%p%Mokncwr$FhlVj9M0gh8|KF<-jw!oF(NoYwWx#=UfvSsTwVwS{{ z;7V}idGLDELTTh>(w;$U!{FeEDwh{t@clvn6oBfICEL4q|3p{U^7if9H;I#{#~B#c zEZew-mXt*zCW$S9fFoc@gvKUx;nM5}hEi2CH8ID_f85WSo-DJoMNXd{)4%-X5B5w? zpDh6|0K-e|GCMnS)7aP~V`F*N_I9!=UBw?C!L|f;1JD+Aa8E)^LcF1fBf*yS=e7Xb zhRCmQ>WTgIc4zVZ8bd?ldTeZHZv)&1Mwa{lz`=u$jqTX6Gd4F@+1c5-OmuZ-@n$Ys&BQy+&0!|%&fq^Ib>FI367{kE8kSdoKPVd|I zz55$*F97ow5db)J=s^FD9oKD}n3!FkPB)2M_i|(uF!0yo2q|f8Yr&BMM?z4Ea2?6Y z-c;S303&C{>D%{v)O>;VY%{f5z`($e4uV?c!3XcXt!`mB2b^5Qyp%0>@3`aM2XndJ zJ-J*5n>Vi!N@*r0rWv1@#d2&s&n2Bs;CeP^$EKMp%u`w@lT3Itr; ztL4)VKYZU8gP@{-67Uok7}M9-azGt9(s!_>r7eudTi?Hi}v;m*-VP&R02yF zs+B6GVu_e*lS;;DP9<<1heDymi!To8>FFZ*{K#(}dE~xtDis=_3LFJ$7lQq#qnQWn z`0Quzx~;pr_a?1z6A90>wYADv%*FE@gpkb67AY1Nn4MkFg+j?_9f?Ar@XTY6{rJ9O zamsJ}YYI3Hgs%p9y|bZB-M{s2-Fj{Ah8x~@Yg1Etz1A|VwRVirLMas~rE5V@n;9NH z`Mdr5AAO=e8KEKdOEfzEQt`Tn(?&SJWk8Ngm+jS|8lW$8iPO;kLg2#40Ly_G^)k8C j=XJGlouV@2HQ*UHxaBpO3pY$&$0000EbVXQnL3MO! zZ*l-tZfkCDcW#U!4DtW~03Lc&Sad{Xb7OL8aCB*JZU6vyoJ&+F$V@INElLFd5MKj+ z>hujP000S~NklB6*2APz(Xt+HT{lKwK$@Zk zY}Zlj#7@w{Xc3@DTPHw27O2ypM*AU6o1zJ7J1GRgN&Jvy*`_Gjk!;9PoY)pEStKRW z6e;n!+~?hU_uiR)*d?W;PHLkA91L)0=KO#6oEe-M!6#^k4!z-dp8g8NJEJJ>0oFxP zbUu!`q?9^);J_a|`$_x(i2qx3)XrooPin0`6UR|n2vN6fL%ObONGYjQs+7yMs9N<6 zrIZSS`Wuep{^>&x{o&RB7r>DtV~*<$S0Bv3Aebzy9#(Ck8ln?9@#{*k^(Cu3g)WOeTp^u$(XQ*10)~ z<(RS$wJ;*>K}$lgb5oiP8`=;;;QJxRkDt-`e37ZC%U}EEH^24-pgu~=_~j3;z4p_) zl`_t>wx+E8`)@UE+hk&DfkVSH{MRv`s>+b`QmoIWkut&6TAH8Fc$~Tra(;A*pjx1F zeG6N*Y!s#`sZ`?MyXBT$-EY1*@-h(rT7coOnwX@sRnXJjAzE8AVri*5 z)YrE)`}W(XUIXeM2{1M`=9EgoL^j)E@7U2Vug(`a_{xWzTWaU?pH9)4HOORbIy)0& zvNp+-MR#9{Tu&0yHn2UDjt+-AZ*Rf3+cqdMd z4Q;I^Ui&(Zo?N2MDzLe4J=JO`(&_fQjvs$v1h}%QD*;}8^<-}psrv>8w@9ruue>qE znfWZ+H%h+s4}V9kHjA(&p<>N~W?yewq#@ofGdCm|HBdbLS?cl!-5X z@vDCZ^!);WWx3x^CS9`GG@}>ic;;s=`*ym#e)I*7KJ#rJ{>)vxa`<_IND(x}S1r^H ztFJ~P(hZAYN1x4N+ikplW|AmUY}?i^`}%G^0Bl{2z5(#ygJ1rS?c4he&$AgmJxgAt z$+a5nzUwyDwP*OrOV4ra@UtY+8MfUzgq8-ehPWxZmWEgp)HPvU6Gn~Rwk78;mFY^9 z>D2O&CE~Zvd=91a*yV3dWWbSj%H{=USMbUs8_Kk~(|# z_*=k-GLGw?_dF*CxHz@Ua^L`9Cv9q>#t$`vcl<8@_R?v(Hr>PzzWvWU_N4>NEi6)t z8smPTsRhkGT!nzJ4*8Nsnl{CnCXQorxppI&Oy2@zfP@r6_aqXwsWq&WB3xS%Yk?Ay zTG(*t2by(VTlm)D)9k+M_j&!uvpleWfO~guW#sU4RBH;qId^SMO))hWabYS1pe7Pj zs{yucp*6|o9_*072FtPqX#7xNNX__ri(Hv3t%+A_ntVx72$TH96VK7rzm1_=2l%VM ze3Zw(@&(SncaCbHD3ldTg_xPen9J89%D!T0e1WJ~B{0&I%M~2QL@CTBfNLs+W10f3 zK`DU-u3j$DnoE)0kR+})ijQdsEE7ya(zA7tpn8pBu}E94he9zRT~^d1s7GK(NH_v1 zpgdP1V;P97Nv#&*x(-U|)f(i;IF4q^jCMc7Jv&ZJS!D1{xv2a|B6Gkn|)e zPjKI(k1;ki!@qv#Ke^}6|AyX88%Z_ClCB`(NIX~KIgDJ6wM2~evw z!7`&L9;;UU+qFizHW*T%VvTKqXG>gL;MxMm0^1arrl6xE%iQ#32KU~{(7rp^(48ZS zH1$Ye8XCiBfD1qaS_{I;GKP_%T&|#0Tx$Z4N76?K8`ll(uVDX$kBr9ml(<0_D0dAY1>HGlQ863Q@~e<}>? zvRd`&$+ggzC?K`QvBcWndyXLCG{T$oByMvHnWn@sG)Y%BLhoJ&z77qdJV!QZQm@CX ztd#ZDtJ5c%0KUBc{`+I4xI8*KMJknGLpsLM0glyJd?SEs3LFWhXcRM38rVYPNN~-@ z9B(aU4YN%_?c!T>tZSiIETdE`Pn>w>XcJ(*87%d;lap6e6h-XWwTVtWMNkQ`EP;K4 zBU^$ap{*s&Jr6v{r-p`bMB|$+!Isy>ZL4u=f~8rKMvM@GiHVuGTCH3xmkR+<0Q|Kp z*^^Hmp6%*dpWCx%r#ydghI2D9xq&TYQU(cE;;xB+CBP8Za~$dG6*5rZhnjLtu{2-c z{7Wy=-<>6&FL3eV75(zdPkw1~^1>o83S3^>F7VSlO^Ey|7mmKbrmieO5Hc|_tLNuuo^6U(fvL4>2=M&# zho<-Jxzow#E5F&*l@oovIZE@_D6N#ptZ&B>*CSv{I5{%QiQyS;+ue?CMk1zR7tX%R z`H|PzxS^FeR-8LGt}2!C<%0+R>G7uAIiUCp001K+FCO2w@9v@5*~P7yOiJ|j+u# zqkwY__+VA@qps!FV~>5~JH5S|AMWYNF*taWh-1a{^c=HSmk@@;wk=ZW1diiSD3mD_ z$}BA`lS+DIGfC?8h=qkbVOeNj_iV{r4}ag@qOJ`D?E{ z{q#5fUh7x`K5!ZcZWQ}KC*4wz{7E@9nW)BM@OqnBpeb64XeO&{7e&e5i9Dvo<3+x;m+_&-WyFdG2DwWwG zgppQC*>S8y9LJGTakX0Y=f}s-{b+dj;K>`-hBS`b%&&6(aR2~JU^}ppk51b!w+Q$U zxWKP?l6_Ku8(S7=2Ry)OTCHh5umF@kuCxCEzQUI`mRG?c00000NkvXXu0mjf$m$F( literal 0 HcmV?d00001 diff --git a/core/src/AboutDialog.cpp b/core/src/AboutDialog.cpp new file mode 100644 index 0000000..451b5fb --- /dev/null +++ b/core/src/AboutDialog.cpp @@ -0,0 +1,67 @@ +/* + * AboutDialog.cpp - implementation of AboutDialog + * + * Copyright (c) 2011-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "AboutDialog.h" +#include "VeyonCore.h" + +#include "ui_AboutDialog.h" + + +AboutDialog::AboutDialog( QWidget *parent ) : + QDialog( parent ), + ui( new Ui::AboutDialog ) +{ + ui->setupUi( this ); + + setWindowTitle( tr( "About %1 %2" ).arg( VeyonCore::applicationName(), VeyonCore::version() ) ); + + ui->versionLabel->setText( VeyonCore::version() ); + + QFile authors( QStringLiteral( ":/CONTRIBUTORS" ) ); + authors.open( QFile::ReadOnly ); // Flawfinder: ignore + ui->authors->setPlainText( QString::fromUtf8( authors.readAll() ) ); + + QFile license( QStringLiteral( ":/core/COPYING" ) ); + license.open( QFile::ReadOnly ); // Flawfinder: ignore + ui->license->setPlainText( QString::fromUtf8( license.readAll() ) ); + + VeyonCore::enforceBranding( this ); +} + + + +AboutDialog::~AboutDialog() +{ + delete ui; +} + + + +void AboutDialog::openDonationWebsite() +{ + QDesktopServices::openUrl( QUrl( QStringLiteral( "https://www.paypal.com/cgi-bin/webscr?item_name=Donation+to+Veyon+-+OpenSource+classroom+management&cmd=_donations&business=donate%40veyon.io" ) ) ); +} diff --git a/core/src/AboutDialog.ui b/core/src/AboutDialog.ui new file mode 100644 index 0000000..9209d80 --- /dev/null +++ b/core/src/AboutDialog.ui @@ -0,0 +1,340 @@ + + + Tobias Junghans + AboutDialog + + + + 0 + 0 + 738 + 500 + + + + About Veyon + + + + :/core/help-about.png:/core/help-about.png + + + true + + + + 10 + + + 15 + + + 15 + + + 15 + + + 15 + + + + + 20 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + :/core/icon64.png + + + + + + + + 14 + 75 + true + + + + About Veyon + + + + + + + + + 0 + + + + + :/core/help-about.png:/core/help-about.png + + + About + + + + 24 + + + 24 + + + 9 + + + 9 + + + 16 + + + + + Veyon - Virtual Eye On Networks + + + + + + + Version: + + + + + + + Website: + + + + + + + <a href="https://veyon.io">https://veyon.io</a> + + + true + + + + + + + Copyright © 2004-2021 Tobias Junghans / Veyon Solutions + + + + + + + 0.0 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Support Veyon project with a donation + + + + + + + + + :/core/user-group-new.png:/core/user-group-new.png + + + Contributors + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + QFrame::NoFrame + + + true + + + + + + + + + :/core/languages.png:/core/languages.png + + + Translation + + + + 6 + + + 24 + + + 24 + + + 24 + + + 24 + + + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + :/core/license.png:/core/license.png + + + License + + + + 9 + + + 6 + + + + + QFrame::NoFrame + + + true + + + + + + + + + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + AboutDialog + accept() + + + 285 + 361 + + + 285 + 194 + + + + + donateButton + clicked() + AboutDialog + openDonationWebsite() + + + 376 + 368 + + + 368 + 249 + + + + + + openDonationWebsite() + + diff --git a/core/src/AccessControlProvider.cpp b/core/src/AccessControlProvider.cpp new file mode 100644 index 0000000..6aab0b6 --- /dev/null +++ b/core/src/AccessControlProvider.cpp @@ -0,0 +1,467 @@ +/* + * AccessControlProvider.cpp - implementation of the AccessControlProvider class + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "UserGroupsBackendManager.h" +#include "AccessControlProvider.h" +#include "HostAddress.h" +#include "NetworkObjectDirectory.h" +#include "NetworkObjectDirectoryManager.h" +#include "VeyonConfiguration.h" +#include "VeyonCore.h" +#include "PlatformPluginInterface.h" +#include "PlatformUserFunctions.h" + + +AccessControlProvider::AccessControlProvider() : + m_accessControlRules(), + m_userGroupsBackend( VeyonCore::userGroupsBackendManager().accessControlBackend() ), + m_networkObjectDirectory( VeyonCore::networkObjectDirectoryManager().configuredDirectory() ), + m_queryDomainGroups( VeyonCore::config().domainGroupsForAccessControlEnabled() ) +{ + const QJsonArray accessControlRules = VeyonCore::config().accessControlRules(); + + m_accessControlRules.reserve( accessControlRules.size() ); + + for( const auto& accessControlRule : accessControlRules ) + { + m_accessControlRules.append( AccessControlRule( accessControlRule ) ); + } +} + + + +QStringList AccessControlProvider::userGroups() const +{ + auto userGroupList = m_userGroupsBackend->userGroups( m_queryDomainGroups ); + + std::sort( userGroupList.begin(), userGroupList.end() ); + + return userGroupList; +} + + + +QStringList AccessControlProvider::locations() const +{ + auto locationList = objectNames( m_networkObjectDirectory->queryObjects( NetworkObject::Type::Location, + NetworkObject::Attribute::None, {} ) ); + + std::sort( locationList.begin(), locationList.end() ); + + return locationList; +} + + + +QStringList AccessControlProvider::locationsOfComputer( const QString& computer ) const +{ + const auto fqdn = HostAddress( computer ).convert( HostAddress::Type::FullyQualifiedDomainName ); + + vDebug() << "Searching for locations of computer" << computer << "via FQDN" << fqdn; + + if( fqdn.isEmpty() ) + { + vWarning() << "Empty FQDN - returning empty location list"; + return {}; + } + + const auto computers = m_networkObjectDirectory->queryObjects( NetworkObject::Type::Host, + NetworkObject::Attribute::HostAddress, fqdn ); + if( computers.isEmpty() ) + { + vWarning() << "Could not query any network objects for host" << fqdn; + return {}; + } + + QStringList locationList; + locationList.reserve( computers.size()*3 ); + + for( const auto& computer : computers ) + { + const auto parents = m_networkObjectDirectory->queryParents( computer ); + for( const auto& parent : parents ) + { + locationList.append( parent.name() ); + } + } + + std::sort( locationList.begin(), locationList.end() ); + + vDebug() << "Found locations:" << locationList; + + return locationList; +} + + + +AccessControlProvider::Access AccessControlProvider::checkAccess( const QString& accessingUser, + const QString& accessingComputer, + const QStringList& connectedUsers ) +{ + if( VeyonCore::config().isAccessRestrictedToUserGroups() ) + { + if( processAuthorizedGroups( accessingUser ) ) + { + return Access::Allow; + } + } + else if( VeyonCore::config().isAccessControlRulesProcessingEnabled() ) + { + auto action = processAccessControlRules( accessingUser, + accessingComputer, + VeyonCore::platform().userFunctions().currentUser(), + HostAddress::localFQDN(), + connectedUsers ); + switch( action ) + { + case AccessControlRule::Action::Allow: + return Access::Allow; + case AccessControlRule::Action::AskForPermission: + return Access::ToBeConfirmed; + default: break; + } + } + else + { + vDebug() << "no access control method configured, allowing access."; + + // no access control method configured, therefore grant access + return Access::Allow; + } + + vDebug() << "configured access control method did not succeed, denying access."; + + // configured access control method did not succeed, therefore deny access + return Access::Deny; +} + + + +bool AccessControlProvider::processAuthorizedGroups( const QString& accessingUser ) +{ + vDebug() << "processing for user" << accessingUser; + + const auto groupsOfAccessingUser = m_userGroupsBackend->groupsOfUser( accessingUser, m_queryDomainGroups ); + const auto authorizedUserGroups = VeyonCore::config().authorizedUserGroups(); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + const auto groupsOfAccessingUserSet = QSet{ groupsOfAccessingUser.begin(), groupsOfAccessingUser.end() }; + const auto authorizedUserGroupSet = QSet{ authorizedUserGroups.begin(), authorizedUserGroups.end() }; +#else + const auto groupsOfAccessingUserSet = groupsOfAccessingUser.toSet(); + const auto authorizedUserGroupSet = authorizedUserGroups.toSet(); +#endif + + return intersects( groupsOfAccessingUserSet, authorizedUserGroupSet ); +} + + + +AccessControlRule::Action AccessControlProvider::processAccessControlRules( const QString& accessingUser, + const QString& accessingComputer, + const QString& localUser, + const QString& localComputer, + const QStringList& connectedUsers ) +{ + vDebug() << "processing rules for" << accessingUser << accessingComputer << localUser << localComputer << connectedUsers; + + for( const auto& rule : qAsConst( m_accessControlRules ) ) + { + // rule disabled? + if( rule.action() == AccessControlRule::Action::None ) + { + // then continue with next rule + continue; + } + + if( rule.areConditionsIgnored() || + matchConditions( rule, accessingUser, accessingComputer, localUser, localComputer, connectedUsers ) ) + { + vDebug() << "rule" << rule.name() << "matched with action" << rule.action(); + return rule.action(); + } + } + + vDebug() << "no matching rule, denying access"; + + return AccessControlRule::Action::Deny; +} + + +/*! + * \brief Returns whether any incoming access requests would be denied due to a deny rule matching the local state (e.g. teacher logged on) + */ +bool AccessControlProvider::isAccessToLocalComputerDenied() const +{ + if( VeyonCore::config().isAccessControlRulesProcessingEnabled() == false ) + { + return false; + } + + for( const auto& rule : qAsConst( m_accessControlRules ) ) + { + if( matchConditions( rule, {}, {}, + VeyonCore::platform().userFunctions().currentUser(), HostAddress::localFQDN(), {} ) ) + { + switch( rule.action() ) + { + case AccessControlRule::Action::Deny: + return true; + case AccessControlRule::Action::Allow: + case AccessControlRule::Action::AskForPermission: + return false; + default: + break; + } + } + } + + return false; +} + + + +bool AccessControlProvider::isMemberOfUserGroup( const QString &user, + const QString &groupName ) const +{ + const QRegularExpression groupNameRX( groupName ); + + if( groupNameRX.isValid() ) + { + return m_userGroupsBackend->groupsOfUser( user, m_queryDomainGroups ).indexOf( groupNameRX ) >= 0; + } + + return m_userGroupsBackend->groupsOfUser( user, m_queryDomainGroups ).contains( groupName ); +} + + + +bool AccessControlProvider::isLocatedAt( const QString &computer, const QString &locationName ) const +{ + return locationsOfComputer( computer ).contains( locationName ); +} + + + +bool AccessControlProvider::haveGroupsInCommon( const QString &userOne, const QString &userTwo ) const +{ + const auto userOneGroups = m_userGroupsBackend->groupsOfUser( userOne, m_queryDomainGroups ); + const auto userTwoGroups = m_userGroupsBackend->groupsOfUser( userTwo, m_queryDomainGroups ); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + const auto userOneGroupSet = QSet{ userOneGroups.begin(), userOneGroups.end() }; + const auto userTwoGroupSet = QSet{ userTwoGroups.begin(), userTwoGroups.end() }; +#else + const auto userOneGroupSet = userOneGroups.toSet(); + const auto userTwoGroupSet = userTwoGroups.toSet(); +#endif + + return intersects( userOneGroupSet, userTwoGroupSet ); +} + + + +bool AccessControlProvider::haveSameLocations( const QString &computerOne, const QString &computerTwo ) const +{ + const auto computerOneLocations = locationsOfComputer( computerOne ); + const auto computerTwoLocations = locationsOfComputer( computerTwo ); + + return computerOneLocations.isEmpty() == false && + computerOneLocations == computerTwoLocations; +} + + + +bool AccessControlProvider::isLocalHost( const QString &accessingComputer ) const +{ + return HostAddress( accessingComputer ).isLocalHost(); +} + + + +bool AccessControlProvider::isLocalUser( const QString &accessingUser, const QString &localUser ) const +{ + return accessingUser.isEmpty() == false && + accessingUser == localUser; +} + + + +bool AccessControlProvider::isNoUserLoggedOn() const +{ + return VeyonCore::platform().userFunctions().isAnyUserLoggedOn() == false; +} + + + +QString AccessControlProvider::lookupSubject( AccessControlRule::Subject subject, + const QString &accessingUser, const QString &accessingComputer, + const QString &localUser, const QString &localComputer ) const +{ + switch( subject ) + { + case AccessControlRule::Subject::AccessingUser: return accessingUser; + case AccessControlRule::Subject::AccessingComputer: return accessingComputer; + case AccessControlRule::Subject::LocalUser: return localUser; + case AccessControlRule::Subject::LocalComputer: return localComputer; + default: break; + } + + return {}; +} + + + +bool AccessControlProvider::matchConditions( const AccessControlRule &rule, + const QString& accessingUser, const QString& accessingComputer, + const QString& localUser, const QString& localComputer, + const QStringList& connectedUsers ) const +{ + bool hasConditions = false; + + // normally all selected conditions have to match in order to make the whole rule match + // if conditions should be inverted (i.e. "is member of" is to be interpreted as "is NOT member of") + // we have to check against the opposite boolean value + bool matchResult = rule.areConditionsInverted() == false; + + vDebug() << rule.toJson() << matchResult; + + if( rule.isConditionEnabled( AccessControlRule::Condition::MemberOfUserGroup ) ) + { + hasConditions = true; + + const auto condition = AccessControlRule::Condition::MemberOfUserGroup; + const auto user = lookupSubject( rule.subject( condition ), accessingUser, {}, localUser, {} ); + const auto group = rule.argument( condition ); + + if( user.isEmpty() || group.isEmpty() || + isMemberOfUserGroup( user, group ) != matchResult ) + { + return false; + } + } + + if( rule.isConditionEnabled( AccessControlRule::Condition::GroupsInCommon ) ) + { + hasConditions = true; + + if( accessingUser.isEmpty() || localUser.isEmpty() || + haveGroupsInCommon( accessingUser, localUser ) != matchResult ) + { + return false; + } + } + + if( rule.isConditionEnabled( AccessControlRule::Condition::LocatedAt ) ) + { + hasConditions = true; + + const auto condition = AccessControlRule::Condition::LocatedAt; + const auto computer = lookupSubject( rule.subject( condition ), {}, accessingComputer, {}, localComputer ); + const auto location = rule.argument( condition ); + + if( computer.isEmpty() || location.isEmpty() || + isLocatedAt( computer, location ) != matchResult ) + { + return false; + } + } + + if( rule.isConditionEnabled( AccessControlRule::Condition::SameLocation ) ) + { + hasConditions = true; + + if( accessingComputer.isEmpty() || localComputer.isEmpty() || + haveSameLocations( accessingComputer, localComputer ) != matchResult ) + { + return false; + } + } + + if( rule.isConditionEnabled( AccessControlRule::Condition::AccessFromLocalHost ) ) + { + hasConditions = true; + + if( isLocalHost( accessingComputer ) != matchResult ) + { + return false; + } + } + + if( rule.isConditionEnabled( AccessControlRule::Condition::AccessFromLocalUser ) ) + { + hasConditions = true; + + if( isLocalUser( accessingUser, localUser ) != matchResult ) + { + return false; + } + } + + if( rule.isConditionEnabled( AccessControlRule::Condition::AccessFromAlreadyConnectedUser ) ) + { + hasConditions = true; + + if( connectedUsers.contains( accessingUser ) != matchResult ) + { + return false; + } + } + + if( rule.isConditionEnabled( AccessControlRule::Condition::NoUserLoggedOn ) ) + { + hasConditions = true; + + if( isNoUserLoggedOn() != matchResult ) + { + return false; + } + } + + // do not match the rule if no conditions are set at all + if( hasConditions == false ) + { + return false; + } + + return true; +} + + + +QStringList AccessControlProvider::objectNames( const NetworkObjectList& objects ) +{ + QStringList nameList; + nameList.reserve( objects.size() ); + + for( const auto& object : objects ) + { + nameList.append( object.name() ); + } + + return nameList; +} diff --git a/core/src/AccessControlRule.cpp b/core/src/AccessControlRule.cpp new file mode 100644 index 0000000..fab3dce --- /dev/null +++ b/core/src/AccessControlRule.cpp @@ -0,0 +1,129 @@ +/* + * AccessControlRule.cpp - implementation of the AccessControlRule class + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "AccessControlRule.h" + +AccessControlRule::AccessControlRule() : + m_name(), + m_description(), + m_action( Action::None ), + m_parameters(), + m_invertConditions( false ), + m_ignoreConditions( false ) +{ +} + + + +AccessControlRule::AccessControlRule(const AccessControlRule &other) : + m_name( other.name() ), + m_description( other.description() ), + m_action( other.action() ), + m_parameters( other.parameters() ), + m_invertConditions( other.areConditionsInverted() ), + m_ignoreConditions( other.areConditionsIgnored() ) +{ +} + + + +AccessControlRule::AccessControlRule(const QJsonValue &jsonValue) : + m_name(), + m_description(), + m_action( Action::None ), + m_parameters(), + m_invertConditions( false ), + m_ignoreConditions( false ) +{ + if( jsonValue.isObject() ) + { + QJsonObject json = jsonValue.toObject(); + + m_name = json[QStringLiteral("Name")].toString(); + m_description = json[QStringLiteral("Description")].toString(); + m_action = static_cast( json[QStringLiteral("Action")].toInt() ); + m_invertConditions = json[QStringLiteral("InvertConditions")].toBool(); + m_ignoreConditions = json[QStringLiteral("IgnoreConditions")].toBool(); + + auto parameters = json[QStringLiteral("Parameters")].toArray(); + + for( auto parametersValue : parameters ) + { + QJsonObject parametersObj = parametersValue.toObject(); + auto condition = static_cast( parametersObj[QStringLiteral("Condition")].toInt() ); + + m_parameters[condition].enabled = parametersObj[QStringLiteral("Enabled")].toBool(); + m_parameters[condition].subject = static_cast( parametersObj[QStringLiteral("Subject")].toInt() ); + m_parameters[condition].argument = parametersObj[QStringLiteral("Argument")].toString(); + } + } +} + + + +AccessControlRule& AccessControlRule::operator=( const AccessControlRule& other ) +{ + m_name = other.name(); + m_description = other.description(); + m_action = other.action(); + m_parameters = other.parameters(); + m_invertConditions = other.areConditionsInverted(); + m_ignoreConditions = other.areConditionsIgnored(); + + return *this; +} + + + +QJsonObject AccessControlRule::toJson() const +{ + QJsonObject json; + + json[QStringLiteral("Name")] = m_name; + json[QStringLiteral("Description")] = m_description; + json[QStringLiteral("Action")] = static_cast( m_action ); + json[QStringLiteral("InvertConditions")] = m_invertConditions; + json[QStringLiteral("IgnoreConditions")] = m_ignoreConditions; + + QJsonArray parameters; + + for( auto it = m_parameters.constBegin(), end = m_parameters.constEnd(); it != end; ++it ) + { + if( isConditionEnabled( it.key() ) ) + { + QJsonObject parametersObject; + parametersObject[QStringLiteral("Condition")] = static_cast( it.key() ); + parametersObject[QStringLiteral("Enabled")] = true; + parametersObject[QStringLiteral("Subject")] = static_cast( subject( it.key() ) ); + parametersObject[QStringLiteral("Argument")] = argument( it.key() ); + parameters.append( parametersObject ); + } + } + + json[QStringLiteral("Parameters")] = parameters; + + return json; +} diff --git a/core/src/AuthenticationCredentials.cpp b/core/src/AuthenticationCredentials.cpp new file mode 100644 index 0000000..727c2fa --- /dev/null +++ b/core/src/AuthenticationCredentials.cpp @@ -0,0 +1,98 @@ +/* + * AuthenticationCredentials.cpp - class holding credentials for authentication + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "AuthenticationCredentials.h" + + +AuthenticationCredentials::AuthenticationCredentials() : + m_privateKey(), + m_logonUsername(), + m_logonPassword(), + m_token(), + m_internalVncServerPassword() +{ +} + + + +AuthenticationCredentials::AuthenticationCredentials( const AuthenticationCredentials &other ) : + m_privateKey( other.privateKey() ), + m_authenticationKeyName( other.authenticationKeyName() ), + m_logonUsername( other.logonUsername() ), + m_logonPassword( other.logonPassword() ), + m_token( other.token() ), + m_internalVncServerPassword( other.internalVncServerPassword() ) +{ +} + + + +bool AuthenticationCredentials::hasCredentials( Type type ) const +{ + switch( type ) + { + case Type::PrivateKey: + return m_privateKey.isNull() == false; + + case Type::UserLogon: + return m_logonUsername.isEmpty() == false && m_logonPassword.isEmpty() == false; + + case Type::Token: + return m_token.isEmpty() == false && m_token.size() == CryptoCore::ChallengeSize; + + default: + break; + } + + vCritical() << "no valid credential type given:" << TypeFlags( type ); + + return false; +} + + + +bool AuthenticationCredentials::loadPrivateKey( const QString& privateKeyFile ) +{ + vDebug() << privateKeyFile; + + if( privateKeyFile.isEmpty() ) + { + return false; + } + + return setPrivateKey( CryptoCore::PrivateKey( privateKeyFile ) ); +} + + + +bool AuthenticationCredentials::setPrivateKey( const CryptoCore::PrivateKey& privateKey ) +{ + if( privateKey.isNull() == false && privateKey.isPrivate() ) + { + m_privateKey = privateKey; + + return true; + } + + return false; +} diff --git a/core/src/BuiltinFeatures.cpp b/core/src/BuiltinFeatures.cpp new file mode 100644 index 0000000..3171dfd --- /dev/null +++ b/core/src/BuiltinFeatures.cpp @@ -0,0 +1,53 @@ +/* + * BuiltinFeatures.cpp - implementation of BuiltinFeatures class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "BuiltinFeatures.h" +#include "FeatureControl.h" +#include "MonitoringMode.h" +#include "PluginManager.h" +#include "SystemTrayIcon.h" +#include "DesktopAccessDialog.h" + + +BuiltinFeatures::BuiltinFeatures() : + m_featureControl( new FeatureControl ), + m_systemTrayIcon( new SystemTrayIcon ), + m_monitoringMode( new MonitoringMode ), + m_desktopAccessDialog( new DesktopAccessDialog ) +{ + VeyonCore::pluginManager().registerExtraPluginInterface( m_featureControl ); + VeyonCore::pluginManager().registerExtraPluginInterface( m_systemTrayIcon ); + VeyonCore::pluginManager().registerExtraPluginInterface( m_monitoringMode ); + VeyonCore::pluginManager().registerExtraPluginInterface( m_desktopAccessDialog ); +} + + + +BuiltinFeatures::~BuiltinFeatures() +{ + delete m_systemTrayIcon; + delete m_monitoringMode; + delete m_desktopAccessDialog; + delete m_featureControl; +} diff --git a/core/src/CommandLineIO.cpp b/core/src/CommandLineIO.cpp new file mode 100644 index 0000000..409bba2 --- /dev/null +++ b/core/src/CommandLineIO.cpp @@ -0,0 +1,190 @@ +/* + * CommandLineIO.cpp - text input/output for command line plugins + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "CommandLineIO.h" + + +void CommandLineIO::print( const QString& message ) +{ + fprintf( stdout, "%s\n", qUtf8Printable( message ) ); + fflush( stdout ); +} + + + +void CommandLineIO::newline() +{ + putc( '\n', stdout ); +} + + + +void CommandLineIO::info( const QString &message ) +{ + fprintf( stderr, "[%s] %s\n", qUtf8Printable( VeyonCore::tr( "INFO" ) ), qUtf8Printable( message ) ); + fflush( stderr ); +} + + + +void CommandLineIO::warning( const QString &message ) +{ + fprintf( stderr, "[%s] %s\n", qUtf8Printable( VeyonCore::tr( "WARNING" ) ), qUtf8Printable( message ) ); + fflush( stderr ); +} + + + +void CommandLineIO::error( const QString& message ) +{ + fprintf( stderr, "[%s] %s\n", qUtf8Printable( VeyonCore::tr( "ERROR" ) ), qUtf8Printable( message ) ); + fflush( stderr ); +} + + + +void CommandLineIO::printTable( const CommandLineIO::Table& table, char horizontal, char vertical, char corner ) +{ + // determine column count + int columnCount = table.first.size(); + for( const auto& row : table.second ) + { + columnCount = qMax( columnCount, row.size() ); + } + + // determine maximum width for each column + QVector columnWidths( columnCount ); + for( int col = 0; col < table.first.size(); ++col ) + { + columnWidths[col] = qMax( columnWidths[col], table.first[col].size()+2 ); + } + + for( const auto& row : table.second ) + { + for( int col = 0; col < row.size(); ++col ) + { + columnWidths[col] = qMax( columnWidths[col], row[col].size()+2 ); + } + } + + printTableRuler( columnWidths, horizontal, corner ); + printTableRow( columnWidths, vertical, table.first ); + printTableRuler( columnWidths, horizontal, corner ); + + for( const auto& row : table.second ) + { + printTableRow( columnWidths, vertical, row ); + } + + printTableRuler( columnWidths, horizontal, corner ); +} + + + +void CommandLineIO::printUsage( const QString& module, const QString& command, + const Arguments& mandatoryArguments, const Arguments& optionalArguments ) +{ + QStringList arguments; + for( auto it = mandatoryArguments.begin(), end = mandatoryArguments.end(); it != end; ++it ) + { + if( it.value().isEmpty() ) + { + arguments.append( QStringLiteral("<%1>").arg( it.key() ) ); + } + else + { + arguments.append( QStringLiteral("%1 <%2>").arg( it.value(), it.key() ) ); + } + } + + for( auto it = optionalArguments.begin(), end = optionalArguments.end(); it != end; ++it ) + { + if( it.value().isEmpty() ) + { + arguments.append( QStringLiteral("[<%1>]").arg( it.key() ) ); + } + else + { + arguments.append( QStringLiteral("[%1 <%2>]").arg( it.value(), it.key() ) ); + } + } + + newline(); + print( VeyonCore::tr( "USAGE") ); + newline(); + print( QStringLiteral(" %1 %2 %3\n").arg( module, command, arguments.join(QLatin1Char(' ')) ) ); +} + + + +void CommandLineIO::printDescription( const QString& description ) +{ + print( VeyonCore::tr( "DESCRIPTION") ); + newline(); + print( QStringLiteral(" %2\n").arg( description ) ); +} + + + +void CommandLineIO::printExamples( const QString& module, const QString& command, const CommandLineIO::Examples& examples ) +{ + print( VeyonCore::tr( "EXAMPLES") ); + newline(); + + for( const auto& example : examples ) + { + print( QStringLiteral(" * %1:\n\n %2 %3 %4\n").arg( example.first, module, command, + example.second.join(QLatin1Char(' ')) ) ); + } +} + + + +void CommandLineIO::printTableRuler( const CommandLineIO::TableColumnWidths& columnWidths, char horizontal, char corner ) +{ + putc( corner, stdout ); + for( const auto& width : columnWidths ) + { + for( int i = 0; i < width; ++i ) + { + putc( horizontal, stdout ); + } + putc( corner, stdout ); + } + newline(); +} + + + +void CommandLineIO::printTableRow( const TableColumnWidths& columnWidths, char vertical, const TableRow& row ) +{ + putc( vertical, stdout ); + for( int col = 0; col < columnWidths.size(); ++col ) + { + const auto cell = row.value( col ); + fprintf( stdout, " %s%c", qUtf8Printable( cell + QString( columnWidths[col] - cell.size() - 1, QLatin1Char(' ') ) ), vertical ); + fflush( stdout ); + } + newline(); +} diff --git a/core/src/Computer.cpp b/core/src/Computer.cpp new file mode 100644 index 0000000..9079d12 --- /dev/null +++ b/core/src/Computer.cpp @@ -0,0 +1,38 @@ +/* + * Computer.cpp - represents a computer and provides control methods and data + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "Computer.h" + +Computer::Computer( NetworkObject::Uid networkObjectUid, + const QString& name, + const QString& hostAddress, + const QString& macAddress, + const QString& location ) : + m_networkObjectUid( networkObjectUid ), + m_name( name ), + m_hostAddress( hostAddress ), + m_macAddress( macAddress ), + m_location( location ) +{ +} diff --git a/core/src/ComputerControlInterface.cpp b/core/src/ComputerControlInterface.cpp new file mode 100644 index 0000000..394c23a --- /dev/null +++ b/core/src/ComputerControlInterface.cpp @@ -0,0 +1,362 @@ +/* + * ComputerControlInterface.cpp - interface class for controlling a computer + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "BuiltinFeatures.h" +#include "ComputerControlInterface.h" +#include "Computer.h" +#include "FeatureControl.h" +#include "MonitoringMode.h" +#include "VeyonConfiguration.h" +#include "VeyonConnection.h" +#include "VncConnection.h" + + +ComputerControlInterface::ComputerControlInterface( const Computer& computer, + QObject* parent ) : + QObject( parent ), + m_computer( computer ), + m_state( State::Disconnected ), + m_userLoginName(), + m_userFullName(), + m_scaledScreenSize(), + m_vncConnection( nullptr ), + m_connection( nullptr ), + m_connectionWatchdogTimer( this ), + m_userUpdateTimer( this ), + m_activeFeaturesUpdateTimer( this ) +{ + m_connectionWatchdogTimer.setInterval( ConnectionWatchdogTimeout ); + m_connectionWatchdogTimer.setSingleShot( true ); + connect( &m_connectionWatchdogTimer, &QTimer::timeout, this, &ComputerControlInterface::restartConnection ); + + connect( &m_userUpdateTimer, &QTimer::timeout, this, &ComputerControlInterface::updateUser ); + connect( &m_activeFeaturesUpdateTimer, &QTimer::timeout, this, &ComputerControlInterface::updateActiveFeatures ); +} + + + +ComputerControlInterface::~ComputerControlInterface() +{ + stop(); +} + + + +void ComputerControlInterface::start( QSize scaledScreenSize, UpdateMode updateMode, AuthenticationProxy* authenticationProxy ) +{ + // make sure we do not leak + stop(); + + m_scaledScreenSize = scaledScreenSize; + + if( m_computer.hostAddress().isEmpty() == false ) + { + m_vncConnection = new VncConnection(); + m_vncConnection->setHost( m_computer.hostAddress() ); + m_vncConnection->setQuality( VncConnection::Quality::Thumbnail ); + m_vncConnection->setScaledSize( m_scaledScreenSize ); + + setUpdateMode( updateMode ); + + m_connection = new VeyonConnection( m_vncConnection ); + m_connection->setAuthenticationProxy( authenticationProxy ); + + m_vncConnection->start(); + + connect( m_vncConnection, &VncConnection::framebufferUpdateComplete, this, &ComputerControlInterface::resetWatchdog ); + connect( m_vncConnection, &VncConnection::framebufferUpdateComplete, this, &ComputerControlInterface::screenUpdated ); + + connect( m_vncConnection, &VncConnection::framebufferSizeChanged, this, &ComputerControlInterface::screenSizeChanged ); + + connect( m_vncConnection, &VncConnection::stateChanged, this, &ComputerControlInterface::updateState ); + connect( m_vncConnection, &VncConnection::stateChanged, this, &ComputerControlInterface::updateUser ); + connect( m_vncConnection, &VncConnection::stateChanged, this, &ComputerControlInterface::updateActiveFeatures ); + connect( m_vncConnection, &VncConnection::stateChanged, this, &ComputerControlInterface::stateChanged ); + + connect( m_connection, &VeyonConnection::featureMessageReceived, this, &ComputerControlInterface::handleFeatureMessage ); + connect( m_connection, &VeyonConnection::featureMessageReceived, this, &ComputerControlInterface::resetWatchdog ); + } + else + { + vWarning() << "computer host address is empty!"; + } +} + + + +void ComputerControlInterface::stop() +{ + // VeyonConnection destroys itself when VncConnection is destroyed + m_connection = nullptr; + + if( m_vncConnection ) + { + // do not delete VNC connection but let it delete itself after stopping automatically + m_vncConnection->stopAndDeleteLater(); + m_vncConnection = nullptr; + } + + m_activeFeaturesUpdateTimer.stop(); + m_userUpdateTimer.stop(); + m_connectionWatchdogTimer.stop(); + + m_state = State::Disconnected; +} + + + +bool ComputerControlInterface::hasValidFramebuffer() const +{ + return m_vncConnection->hasValidFramebuffer(); +} + + + +QSize ComputerControlInterface::screenSize() const +{ + return m_vncConnection->image().size(); +} + + + +void ComputerControlInterface::setScaledScreenSize( QSize scaledScreenSize ) +{ + m_scaledScreenSize = scaledScreenSize; + + if( m_vncConnection ) + { + m_vncConnection->setScaledSize( m_scaledScreenSize ); + } +} + + + +QImage ComputerControlInterface::scaledScreen() const +{ + if( m_vncConnection && m_vncConnection->isConnected() ) + { + return m_vncConnection->scaledScreen(); + } + + return {}; +} + + + +QImage ComputerControlInterface::screen() const +{ + if( m_vncConnection && m_vncConnection->isConnected() ) + { + return m_vncConnection->image(); + } + + return {}; +} + + + +void ComputerControlInterface::setUserInformation( const QString& userLoginName, const QString& userFullName, int sessionId ) +{ + if( userLoginName != m_userLoginName || + userFullName != m_userFullName || + sessionId != m_userSessionId ) + { + m_userLoginName = userLoginName; + m_userFullName = userFullName; + m_userSessionId = sessionId; + + Q_EMIT userChanged(); + } +} + + + +void ComputerControlInterface::setActiveFeatures( const FeatureUidList& activeFeatures ) +{ + if( activeFeatures != m_activeFeatures ) + { + m_activeFeatures = activeFeatures; + + Q_EMIT activeFeaturesChanged(); + } +} + + + +void ComputerControlInterface::updateActiveFeatures() +{ + lock(); + + if( m_vncConnection && m_connection && state() == State::Connected ) + { + VeyonCore::builtinFeatures().featureControl().queryActiveFeatures( { weakPointer() } ); + } + else + { + setActiveFeatures( {} ); + } + + unlock(); +} + + + +void ComputerControlInterface::sendFeatureMessage( const FeatureMessage& featureMessage, bool wake ) +{ + if( m_connection && m_connection->isConnected() ) + { + m_connection->sendFeatureMessage( featureMessage, wake ); + } +} + + + +bool ComputerControlInterface::isMessageQueueEmpty() +{ + if( m_vncConnection && m_vncConnection->isConnected() ) + { + return m_vncConnection->isEventQueueEmpty(); + } + + return true; +} + + + +void ComputerControlInterface::setUpdateMode( UpdateMode updateMode ) +{ + m_updateMode = updateMode; + + const auto computerMonitoringUpdateInterval = VeyonCore::config().computerMonitoringUpdateInterval(); + + switch( updateMode ) + { + case UpdateMode::Disabled: + if( m_vncConnection ) + { + m_vncConnection->setFramebufferUpdateInterval( UpdateIntervalDisabled ); + } + + m_userUpdateTimer.stop(); + m_activeFeaturesUpdateTimer.start( UpdateIntervalDisabled ); + break; + + case UpdateMode::Monitoring: + case UpdateMode::Live: + if( m_vncConnection ) + { + m_vncConnection->setFramebufferUpdateInterval( updateMode == UpdateMode::Monitoring ? + computerMonitoringUpdateInterval : -1 ); + } + + m_userUpdateTimer.start( computerMonitoringUpdateInterval ); + m_activeFeaturesUpdateTimer.start( computerMonitoringUpdateInterval ); + break; + } +} + + + +ComputerControlInterface::Pointer ComputerControlInterface::weakPointer() +{ + return Pointer( this, []( ComputerControlInterface* ) { } ); +} + + + +void ComputerControlInterface::resetWatchdog() +{ + if( state() == State::Connected ) + { + m_connectionWatchdogTimer.start(); + } +} + + + +void ComputerControlInterface::restartConnection() +{ + if( m_vncConnection ) + { + vDebug(); + m_vncConnection->restart(); + + m_connectionWatchdogTimer.stop(); + } +} + + + +void ComputerControlInterface::updateState() +{ + lock(); + + if( m_vncConnection ) + { + switch( m_vncConnection->state() ) + { + case VncConnection::State::Disconnected: m_state = State::Disconnected; break; + case VncConnection::State::Connecting: m_state = State::Connecting; break; + case VncConnection::State::Connected: m_state = State::Connected; break; + case VncConnection::State::HostOffline: m_state = State::HostOffline; break; + case VncConnection::State::ServerNotRunning: m_state = State::ServerNotRunning; break; + case VncConnection::State::AuthenticationFailed: m_state = State::AuthenticationFailed; break; + default: m_state = VncConnection::State::Disconnected; break; + } + } + else + { + m_state = State::Disconnected; + } + + unlock(); +} + + + +void ComputerControlInterface::updateUser() +{ + lock(); + + if( m_vncConnection && m_connection && state() == State::Connected ) + { + if( userLoginName().isEmpty() ) + { + VeyonCore::builtinFeatures().monitoringMode().queryLoggedOnUserInfo( { weakPointer() } ); + } + } + else + { + setUserInformation( {}, {}, -1 ); + } + + unlock(); +} + + + +void ComputerControlInterface::handleFeatureMessage( const FeatureMessage& message ) +{ + Q_EMIT featureMessageReceived( message, weakPointer() ); +} diff --git a/core/src/ComputerListModel.cpp b/core/src/ComputerListModel.cpp new file mode 100644 index 0000000..fe8db51 --- /dev/null +++ b/core/src/ComputerListModel.cpp @@ -0,0 +1,63 @@ +/* + * ComputerListModel.cpp - data model base class for computer objects + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "ComputerListModel.h" +#include "VeyonConfiguration.h" + + +ComputerListModel::ComputerListModel( QObject* parent ) : + QAbstractListModel( parent ), + m_displayRoleContent( VeyonCore::config().computerDisplayRoleContent() ), + m_sortOrder( VeyonCore::config().computerMonitoringSortOrder() ), + m_aspectRatio( VeyonCore::config().computerMonitoringAspectRatio() ) +{ +} + + + +Qt::ItemFlags ComputerListModel::flags( const QModelIndex& index ) const +{ + auto defaultFlags = QAbstractListModel::flags( index ); + + if( index.isValid() ) + { + return Qt::ItemIsDragEnabled | defaultFlags; + } + + return Qt::ItemIsDropEnabled | defaultFlags; +} + + + +Qt::DropActions ComputerListModel::supportedDragActions() const +{ + return Qt::MoveAction; +} + + + +Qt::DropActions ComputerListModel::supportedDropActions() const +{ + return Qt::MoveAction; +} diff --git a/core/src/Configuration/JsonStore.cpp b/core/src/Configuration/JsonStore.cpp new file mode 100644 index 0000000..a06778f --- /dev/null +++ b/core/src/Configuration/JsonStore.cpp @@ -0,0 +1,198 @@ +/* + * Configuration/JsonStore.cpp - implementation of JsonStore + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "Configuration/JsonStore.h" +#include "Configuration/Object.h" +#include "Filesystem.h" +#include "VeyonConfiguration.h" +#include "PlatformFilesystemFunctions.h" + + +namespace Configuration +{ + +JsonStore::JsonStore( Scope scope, const QString &file ) : + Store( Store::JsonFile, scope ), + m_file( file ) +{ +} + + + +static void loadJsonTree( Object* obj, const QJsonObject& jsonParent, const QString& parentKey ) +{ + for( auto it = jsonParent.begin(); it != jsonParent.end(); ++it ) + { + if( it.value().isObject() ) + { + QJsonObject jsonObject = it.value().toObject(); + + if( jsonObject.contains( QStringLiteral( "JsonStoreArray" ) ) ) + { + obj->setValue( it.key(), jsonObject[QStringLiteral("JsonStoreArray")].toArray(), parentKey ); + } + else if( jsonObject.contains( QStringLiteral( "JsonStoreObject" ) ) ) + { + obj->setValue( it.key(), jsonObject[QStringLiteral("JsonStoreObject")].toObject(), parentKey ); + } + else + { + const QString subParentKey = parentKey + ( parentKey.isEmpty() ? QString() : QStringLiteral("/") ) + it.key(); + loadJsonTree( obj, it.value().toObject(), subParentKey ); + } + } + else + { + obj->setValue( it.key(), it.value().toVariant(), parentKey ); + } + } +} + + + +void JsonStore::load( Object* obj ) +{ + QFile jsonFile( configurationFilePath() ); + if( !jsonFile.open( QFile::ReadOnly ) ) + { + vWarning() << "could not open" << jsonFile.fileName(); + return; + } + + QJsonDocument jsonDoc = QJsonDocument::fromJson( jsonFile.readAll() ); + + loadJsonTree( obj, jsonDoc.object(), {} ); +} + + + +static QJsonObject saveJsonTree( const Object::DataMap& dataMap ) +{ + QJsonObject jsonData; + + for( auto it = dataMap.begin(); it != dataMap.end(); ++it ) + { + if( it.value().type() == QVariant::Map ) + { + jsonData[it.key()] = saveJsonTree( it.value().toMap() ); + } + else if( static_cast( it.value().type() ) == QMetaType::QJsonArray ) + { + QJsonObject jsonObj; + jsonObj[QStringLiteral("JsonStoreArray")] = it.value().toJsonArray(); + jsonData[it.key()] = jsonObj; + } + else if( static_cast( it.value().type() ) == QMetaType::QJsonObject ) + { + QJsonObject jsonObj; + jsonObj[QStringLiteral("JsonStoreObject")] = it.value().toJsonObject(); + jsonData[it.key()] = jsonObj; + } + else if( QMetaType( it.value().userType() ).flags().testFlag( QMetaType::IsEnumeration ) ) + { + jsonData[it.key()] = QJsonValue( it.value().toInt() ); + } + else + { + jsonData[it.key()] = QJsonValue::fromVariant( it.value() ); + } + } + + return jsonData; +} + + + +void JsonStore::flush( const Object* obj ) +{ + QFile outfile( configurationFilePath() ); + if( !outfile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) + { + vCritical() << "could not write to configuration file" << configurationFilePath(); + return; + } + + outfile.write( QJsonDocument( saveJsonTree( obj->data() ) ).toJson() ); +} + + + +bool JsonStore::isWritable() const +{ + QFile outfile( configurationFilePath() ); + outfile.open( QFile::WriteOnly | QFile::Append ); + outfile.close(); + + return QFileInfo( configurationFilePath() ).isWritable(); + +} + + + +void JsonStore::clear() +{ + // truncate configuration file + QFile outfile( configurationFilePath() ); + outfile.open( QIODevice::WriteOnly | QIODevice::Truncate ); +} + + + +QString JsonStore::configurationFilePath() const +{ + if( m_file.isEmpty() == false ) + { + return m_file; + } + + QString base; + switch( scope() ) + { + case User: + base = VeyonCore::config().userConfigurationDirectory(); + break; + case System: + base = VeyonCore::platform().filesystemFunctions().globalAppDataPath(); + break; + } + + base = VeyonCore::filesystem().expandPath( base ); + + VeyonCore::filesystem().ensurePathExists( base ); + + auto fileNameBase = name(); + if( fileNameBase.isEmpty() ) + { + fileNameBase = configurationNameFromScope(); + } + + return QDir::toNativeSeparators( base + QDir::separator() + fileNameBase + QLatin1String(".json") ); // clazy:exclude=qstring-allocations +} + +} diff --git a/core/src/Configuration/LocalStore.cpp b/core/src/Configuration/LocalStore.cpp new file mode 100644 index 0000000..17181c0 --- /dev/null +++ b/core/src/Configuration/LocalStore.cpp @@ -0,0 +1,197 @@ +/* + * ConfigurationLocalStore.cpp - implementation of LocalStore + * + * Copyright (c) 2009-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "Configuration/LocalStore.h" +#include "Configuration/Object.h" + + +namespace Configuration +{ + +LocalStore::LocalStore( Scope scope ) : + Store( Store::LocalBackend, scope ) +{ +} + + + + +static void loadSettingsTree( Object *obj, QSettings *s, + const QString &parentKey ) +{ + const auto childGroups = s->childGroups(); + + for( const auto& g : childGroups ) + { + const QString subParentKey = parentKey + + ( parentKey.isEmpty() ? QString() : QStringLiteral("/") ) + g; + s->beginGroup( g ); + loadSettingsTree( obj, s, subParentKey ); + s->endGroup(); + } + + const auto childKeys = s->childKeys(); + + for( const auto& k : childKeys ) + { + QString stringValue = s->value( k ).toString(); + QRegExp jsonValueRX( QStringLiteral("@JsonValue(\\(.*\\))") ); + + if( jsonValueRX.indexIn( stringValue ) == 0 ) + { + auto jsonValue = QJsonDocument::fromJson( QByteArray::fromBase64( jsonValueRX.cap( 1 ).toUtf8() ) ).object(); + if( jsonValue.contains( QStringLiteral( "a" ) ) ) + { + obj->setValue( k, jsonValue[QStringLiteral("a")].toArray(), parentKey ); + } + else if( jsonValue.contains( QStringLiteral("o") ) ) + { + obj->setValue( k, jsonValue[QStringLiteral("o")].toObject(), parentKey ); + } + else + { + vCritical() << "trying to load unknown JSON value type!"; + } + } + else + { + obj->setValue( k, s->value( k ), parentKey ); + } + } +} + + + +void LocalStore::load( Object *obj ) +{ + auto s = createSettingsObject(); + loadSettingsTree( obj, s, {} ); + delete s; +} + + + +static QString serializeJsonValue( const QJsonValue& jsonValue ) +{ + QJsonObject jsonObject; + + if( jsonValue.isArray() ) + { + jsonObject[QStringLiteral("a")] = jsonValue; + } + else if( jsonValue.isObject() ) + { + jsonObject[QStringLiteral("o")] = jsonValue; + } + else + { + vCritical() << "trying to save unknown JSON value type" << jsonValue.type(); + } + + return QStringLiteral("@JsonValue(%1)").arg( + QString::fromLatin1( QJsonDocument( jsonObject ).toJson( QJsonDocument::Compact ).toBase64() ) ); +} + + + +static void saveSettingsTree( const Object::DataMap &dataMap, QSettings *s ) +{ + for( auto it = dataMap.begin(); it != dataMap.end(); ++it ) + { + if( it.value().type() == QVariant::Map ) + { + s->beginGroup( it.key() ); + saveSettingsTree( it.value().toMap(), s ); + s->endGroup(); + } + else if( static_cast( it.value().type() ) == QMetaType::QJsonArray ) + { + s->setValue( it.key(), serializeJsonValue( it.value().toJsonArray() ) ); + } + else if( static_cast( it.value().type() ) == QMetaType::QJsonObject ) + { + s->setValue( it.key(), serializeJsonValue( it.value().toJsonObject() ) ); + } + else if( QMetaType( it.value().userType() ).flags().testFlag( QMetaType::IsEnumeration ) ) + { + s->setValue( it.key(), it.value().toInt() ); + } + else + { + s->setValue( it.key(), it.value() ); + } + } +} + + + +void LocalStore::flush( const Object *obj ) +{ + auto s = createSettingsObject(); + // clear previously saved items + s->setFallbacksEnabled( false ); + s->clear(); + saveSettingsTree( obj->data(), s ); + delete s; +} + + + +bool LocalStore::isWritable() const +{ + auto s = createSettingsObject(); + bool ret = s->isWritable(); + delete s; + + return ret; +} + + + +void LocalStore::clear() +{ + auto s = createSettingsObject(); + s->setFallbacksEnabled( false ); + s->clear(); + delete s; +} + + + +QSettings *LocalStore::createSettingsObject() const +{ + return new QSettings( scope() == System ? + QSettings::SystemScope : QSettings::UserScope, + QSettings().organizationName(), + QSettings().applicationName() ); +} + + +} + diff --git a/core/src/Configuration/Object.cpp b/core/src/Configuration/Object.cpp new file mode 100644 index 0000000..05b4a28 --- /dev/null +++ b/core/src/Configuration/Object.cpp @@ -0,0 +1,362 @@ +/* + * ConfigurationObject.cpp - implementation of ConfigurationObject + * + * Copyright (c) 2009-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "Configuration/Object.h" +#include "Configuration/LocalStore.h" +#include "Configuration/JsonStore.h" + + +namespace Configuration +{ + + +Object::Object() : + m_store( nullptr ), + m_customStore( false ), + m_data() +{ +} + + + +Object::Object( Store::Backend backend, Store::Scope scope, const QString& storeName ) : + m_store( createStore( backend, scope ) ), + m_customStore( false ), + m_data() +{ + m_store->setName( storeName ); + + reloadFromStore(); +} + + + +Object::Object( Store* store ) : + m_store( store ), + m_customStore( true ), + m_data() +{ + reloadFromStore(); +} + + + +Object::Object( const Object& obj ) : + m_store( nullptr ), + m_customStore( false ) +{ + *this = obj; +} + + + +Object::~Object() +{ + if( m_customStore == false ) + { + delete m_store; + } +} + + + +Object& Object::operator=( const Object& ref ) +{ + if( &ref == this ) + { + return *this; + } + + if( m_customStore == false && + ref.m_customStore == false && + ref.m_store ) + { + const auto backend = ref.m_store->backend(); + const auto scope = ref.m_store->scope(); + + delete m_store; + + m_store = createStore( backend, scope ); + } + + m_data = ref.data(); + + return *this; +} + + + +// allow easy merging of two data maps - source is dominant over destination +static Object::DataMap operator+( Object::DataMap dst, Object::DataMap src ) +{ + for( auto it = src.begin(), end = src.end(); it != end; ++it ) + { + if( it.value().type() == QVariant::Map && dst.contains( it.key() ) ) + { + dst[it.key()] = dst[it.key()].toMap() + it.value().toMap(); + } + else + { + dst[it.key()] = it.value(); + } + } + return dst; +} + + + +Object& Object::operator+=( const Object& ref ) +{ + m_data = m_data + ref.data(); + + return *this; +} + + + +bool Object::hasValue( const QString& key, const QString& parentKey ) const +{ + // empty parentKey? + if( parentKey.isEmpty() ) + { + // search for key in toplevel data map + return m_data.contains( key ); + } + + // recursively search through data maps and sub data-maps until + // all levels of the parentKey are processed + const QStringList subLevels = parentKey.split( QLatin1Char('/') ); + DataMap currentMap = m_data; + + for( const auto& level : subLevels ) + { + if( currentMap.contains( level ) && + currentMap[level].type() == QVariant::Map ) + { + currentMap = currentMap[level].toMap(); + } + else + { + return false; + } + } + + // ok, we're there - does the current submap then contain our key? + return currentMap.contains( key ); +} + + + + +QVariant Object::value( const QString& key, const QString& parentKey, const QVariant& defaultValue ) const +{ + // empty parentKey? + if( parentKey.isEmpty() ) + { + // search for key in toplevel data map + if( m_data.contains( key ) ) + { + return m_data[key]; + } + return defaultValue; + } + + // recursively search through data maps and sub data-maps until + // all levels of the parentKey are processed + const QStringList subLevels = parentKey.split( QLatin1Char('/') ); + DataMap currentMap = m_data; + for( const auto& level : subLevels ) + { + if( currentMap.contains( level ) && + currentMap[level].type() == QVariant::Map ) + { + currentMap = currentMap[level].toMap(); + } + else + { + return defaultValue; + } + } + + // ok, we're there - does the current submap then contain our key? + if( currentMap.contains( key ) ) + { + return currentMap[key]; + } + return defaultValue; +} + + + + +static Object::DataMap setValueRecursive( Object::DataMap data, + QStringList subLevels, + const QString& key, + const QVariant& value ) +{ + if( subLevels.isEmpty() ) + { + // search for key in toplevel data map + if( !data.contains( key ) || data[key].type() != QVariant::Map ) + { + data[key] = value; + } + else + { + vWarning() << "cannot replace sub data map with a value!"; + } + + return data; + } + + const QString level = subLevels.takeFirst(); + if( data.contains( level ) ) + { + if( data[level].type() != QVariant::Map ) + { + vWarning() << "parent key points doesn't point to a data map!"; + return data; + } + } + else + { + data[level] = Object::DataMap(); + } + + data[level] = setValueRecursive( data[level].toMap(), subLevels, key, value ); + + return data; +} + + + + +void Object::setValue( const QString& key, const QVariant& value, const QString& parentKey ) +{ + // recursively search through data maps and sub data-maps until + // all levels of the parentKey are processed + QStringList subLevels = parentKey.split( QLatin1Char('/') ); + DataMap data = setValueRecursive( m_data, subLevels, key, value ); + + if( data != m_data ) + { + m_data = data; + Q_EMIT configurationChanged(); + } +} + + + + +static Object::DataMap removeValueRecursive( Object::DataMap data, + QStringList subLevels, + const QString& key ) +{ + if( subLevels.isEmpty() ) + { + // search for key in toplevel data map + if( data.contains( key ) ) + { + data.remove( key ); + } + + return data; + } + + const QString level = subLevels.takeFirst(); + if( data.contains( level ) && data[level].type() == QVariant::Map ) + { + data[level] = removeValueRecursive( data[level].toMap(), subLevels, key ); + } + + return data; +} + + + + + +void Object::removeValue( const QString& key, const QString& parentKey ) +{ + QStringList subLevels = parentKey.split( QLatin1Char('/') ); + DataMap data = removeValueRecursive( m_data, subLevels, key ); + if( data != m_data ) + { + m_data = data; + Q_EMIT configurationChanged(); + } +} + + + + +static void addSubObjectRecursive( const Object::DataMap& dataMap, + Object* _this, + const QString& parentKey ) +{ + for( auto it = dataMap.begin(), end = dataMap.end(); it != end; ++it ) + { + if( it.value().type() == QVariant::Map ) + { + auto newParentKey = it.key(); + if( parentKey.isEmpty() == false ) + { + newParentKey = parentKey + QLatin1Char('/') + newParentKey; + } + addSubObjectRecursive( it.value().toMap(), _this, newParentKey ); + } + else + { + _this->setValue( it.key(), it.value(), parentKey ); + } + } +} + + + +void Object::addSubObject( Object* obj, const QString& parentKey ) +{ + addSubObjectRecursive( obj->data(), this, parentKey ); +} + + + +Store* Object::createStore( Store::Backend backend, Store::Scope scope ) +{ + switch( backend ) + { + case Store::LocalBackend: return new LocalStore( scope ); + case Store::JsonFile: return new JsonStore( scope ); + case Store::NoBackend: + break; + default: + vCritical() << "invalid store" << backend << "selected"; + break; + } + + return nullptr; +} + + +} diff --git a/core/src/Configuration/Password.cpp b/core/src/Configuration/Password.cpp new file mode 100644 index 0000000..599ed34 --- /dev/null +++ b/core/src/Configuration/Password.cpp @@ -0,0 +1,55 @@ +/* + * Password.cpp - implementation of Configuration::Password + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "Configuration/Password.h" +#include "CryptoCore.h" + + +namespace Configuration +{ + +CryptoCore::PlaintextPassword Password::plainText() const +{ + return VeyonCore::cryptoCore().decryptPassword( m_encrypted ); +} + + + +Password Password::fromPlainText( const CryptoCore::PlaintextPassword& plaintext ) +{ + Password password; + password.m_encrypted = VeyonCore::cryptoCore().encryptPassword( plaintext ); + return password; +} + + + +Password Password::fromEncrypted( const QString& encrypted ) +{ + Password password; + password.m_encrypted = encrypted; + return password; +} + +} diff --git a/core/src/Configuration/Property.cpp b/core/src/Configuration/Property.cpp new file mode 100644 index 0000000..2b8b1aa --- /dev/null +++ b/core/src/Configuration/Property.cpp @@ -0,0 +1,138 @@ +/* + * ConfigurationObject.cpp - implementation of ConfigurationObject + * + * Copyright (c) 2009-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "Configuration/Object.h" +#include "Configuration/Property.h" +#include "Configuration/Proxy.h" + + +namespace Configuration +{ + +Configuration::Property::Property( Object* object, const QString& key, const QString& parentKey, + const QVariant& defaultValue, Flags flags ) : + QObject( object ), + m_object( object ), + m_proxy( nullptr ), + m_key( key ), + m_parentKey( parentKey ), + m_defaultValue( defaultValue ), + m_flags( flags ) +{ +} + + + +Configuration::Property::Property( Proxy* proxy, const QString& key, const QString& parentKey, + const QVariant& defaultValue, Flags flags ) : + QObject( proxy->object() ), + m_object( nullptr ), + m_proxy( proxy ), + m_key( key ), + m_parentKey( parentKey ), + m_defaultValue( defaultValue ), + m_flags( flags ) +{ +} + + + +QObject* Property::lambdaContext() const +{ + if( m_object ) + { + return m_object; + } + + return m_proxy; +} + + + +QVariant Property::variantValue() const +{ + if( m_object ) + { + return m_object->value( m_key, m_parentKey, m_defaultValue ); + } + else if( m_proxy ) + { + return m_proxy->value( m_key, m_parentKey, m_defaultValue ); + } + + return m_defaultValue; +} + + + +void Property::setVariantValue( const QVariant& value ) const +{ + if( m_object ) + { + m_object->setValue( m_key, value, m_parentKey ); + } + else if( m_proxy ) + { + m_proxy->setValue( m_key, value, m_parentKey ); + } + else + { + qFatal("%s", Q_FUNC_INFO); + } +} + + + +Property* Property::find( QObject* parent, const QString& key, const QString& parentKey ) +{ + const auto properties = parent->findChildren(); + for( auto property : properties ) + { + if( property->m_key == key && property->m_parentKey == parentKey ) + { + return property; + } + } + + return nullptr; +} + + + +template<> +Password Configuration::TypedProperty::value() const +{ + return Password::fromEncrypted( variantValue().toString() ); +} + + + +template<> +void Configuration::TypedProperty::setValue( const Password& value ) const +{ + setVariantValue( value.encrypted() ); +} + + +} diff --git a/core/src/Configuration/Proxy.cpp b/core/src/Configuration/Proxy.cpp new file mode 100644 index 0000000..e106396 --- /dev/null +++ b/core/src/Configuration/Proxy.cpp @@ -0,0 +1,99 @@ +/* + * Proxy.cpp - implementation of Configuration::Proxy + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "Configuration/Proxy.h" + + +namespace Configuration +{ + +Proxy::Proxy( Object* object ) : + QObject(), + m_object( object ), + m_instanceId() +{ +} + + + +bool Proxy::hasValue( const QString& key, const QString& parentKey ) const +{ + return m_object->hasValue( key, instanceParentKey( parentKey ) ); +} + + + +QVariant Proxy::value( const QString& key, const QString& parentKey, const QVariant& defaultValue ) const +{ + return m_object->value( key, instanceParentKey( parentKey ), defaultValue ); +} + + + +void Proxy::setValue( const QString& key, const QVariant& value, const QString& parentKey ) +{ + m_object->setValue( key, value, instanceParentKey( parentKey ) ); +} + + + +void Proxy::removeValue( const QString& key, const QString& parentKey ) +{ + m_object->removeValue( key, instanceParentKey( parentKey ) ); +} + + + +void Proxy::reloadFromStore() +{ + m_object->reloadFromStore(); +} + + + +void Proxy::flushStore() +{ + m_object->flushStore(); +} + + + +void Proxy::removeInstance( const QString& parentKey ) +{ + m_object->removeValue( instanceId(), parentKey ); +} + + + +QString Proxy::instanceParentKey( const QString& parentKey ) const +{ + if( m_instanceId.isEmpty() ) + { + return parentKey; + } + + return parentKey + QLatin1Char('/') + m_instanceId; +} + +} diff --git a/core/src/Configuration/UiMapping.cpp b/core/src/Configuration/UiMapping.cpp new file mode 100644 index 0000000..4c3e307 --- /dev/null +++ b/core/src/Configuration/UiMapping.cpp @@ -0,0 +1,224 @@ +/* + * ConfigurationObject.cpp - implementation of ConfigurationObject + * + * Copyright (c) 2009-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "Configuration/UiMapping.h" + +namespace Configuration +{ + +static constexpr auto WidgetConfigPropertyFlags = "ConfigPropertyFlags"; + + +void UiMapping::initWidgetFromProperty( const Configuration::TypedProperty& property, QCheckBox* widget ) +{ + widget->setChecked( property.value() ); +} + + + +void UiMapping::initWidgetFromProperty( const Configuration::TypedProperty& property, QGroupBox* widget ) +{ + widget->setChecked( property.value() ); +} + + + +void UiMapping::initWidgetFromProperty( const Configuration::TypedProperty& property, QRadioButton* widget ) +{ + widget->setChecked( property.value() ); +} + + + +void UiMapping::initWidgetFromProperty( const Configuration::TypedProperty& property, QComboBox* widget ) +{ + widget->setCurrentText( property.value() ); +} + + + +void UiMapping::initWidgetFromProperty( const Configuration::TypedProperty& property, QLineEdit* widget ) +{ + widget->setText( property.value() ); +} + + + +void UiMapping::initWidgetFromProperty( const Configuration::TypedProperty& property, QLineEdit* widget ) +{ + widget->setText( QString::fromUtf8( property.value().plainText().toByteArray() ) ); +} + + + +void UiMapping::initWidgetFromProperty( const Configuration::TypedProperty& property, QPushButton* widget ) +{ + auto palette = widget->palette(); + palette.setColor( QPalette::Button, property.value() ); + widget->setPalette( palette ); +} + + + +void UiMapping::initWidgetFromProperty( const Configuration::TypedProperty& property, QComboBox* widget ) +{ + widget->setCurrentIndex( property.value() ); +} + + + +void UiMapping::initWidgetFromProperty( const Configuration::TypedProperty& property, QSpinBox* widget ) +{ + widget->setValue( property.value() ); +} + + + +void UiMapping::initWidgetFromProperty( const Configuration::TypedProperty& property, QComboBox* widget ) +{ + widget->setCurrentIndex( widget->findData( property.value() ) ); +} + + + +void UiMapping::setFlags( QObject* object, Property::Flags flags ) +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + object->setProperty( WidgetConfigPropertyFlags, QVariant::fromValue( flags ) ); +#else + object->setProperty( WidgetConfigPropertyFlags, static_cast( flags ) ); +#endif +} + + + +Property::Flags UiMapping::flags( QObject* object ) +{ + const auto flagsData = object->property( WidgetConfigPropertyFlags ); +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + return flagsData.value(); +#else + return static_cast( flagsData.toUInt() ); +#endif +} + + + +void UiMapping::connectWidgetToProperty( const Configuration::TypedProperty& property, QCheckBox* widget ) +{ + QObject::connect( widget, &QCheckBox::toggled, property.lambdaContext(), + [&property]( bool value ) { property.setValue( value ); } ); +} + + + +void UiMapping::connectWidgetToProperty( const Configuration::TypedProperty& property, QGroupBox* widget ) +{ + QObject::connect( widget, &QGroupBox::toggled, property.lambdaContext(), + [&property]( bool value ) { property.setValue( value ); } ); +} + + + +void UiMapping::connectWidgetToProperty( const Configuration::TypedProperty& property, QRadioButton* widget ) +{ + QObject::connect( widget, &QCheckBox::toggled, property.lambdaContext(), + [&property]( bool value ) { property.setValue( value ); } ); +} + + + +void UiMapping::connectWidgetToProperty( const Configuration::TypedProperty& property, QComboBox* widget ) +{ + QObject::connect( widget, &QComboBox::currentTextChanged, property.lambdaContext(), + [&property]( const QString& value ) { property.setValue( value ); } ); +} + + + +void UiMapping::connectWidgetToProperty( const Configuration::TypedProperty& property, QLineEdit* widget ) +{ + QObject::connect( widget, &QLineEdit::textChanged, property.lambdaContext(), + [&property]( const QString& value ) { property.setValue( value ); } ); +} + + + +void UiMapping::connectWidgetToProperty( const Configuration::TypedProperty& property, QLineEdit* widget ) +{ + QObject::connect( widget, &QLineEdit::textChanged, property.lambdaContext(), + [&property]( const QString& plainText ) { property.setValue( Password::fromPlainText( plainText.toUtf8() ) ); } ); +} + + + +void UiMapping::connectWidgetToProperty( const Configuration::TypedProperty& property, QPushButton* widget ) +{ + QObject::connect( widget, &QAbstractButton::clicked, property.lambdaContext(), [&property, widget]() { + auto palette = widget->palette(); + QColorDialog d( widget->palette().color( QPalette::Button ), widget ); + if( d.exec() ) + { + property.setValue( d.selectedColor() ); + palette.setColor( QPalette::Button, d.selectedColor() ); + widget->setPalette( palette ); + } + } ); +} + + + +void UiMapping::connectWidgetToProperty( const Configuration::TypedProperty& property, QComboBox* widget ) +{ + QObject::connect( widget, QOverload::of(&QComboBox::currentIndexChanged), property.lambdaContext(), + [&property]( int index ) { property.setValue( index ); } ); +} + + + +void UiMapping::connectWidgetToProperty( const Configuration::TypedProperty& property, QSpinBox* widget ) +{ + QObject::connect( widget, QOverload::of(&QSpinBox::valueChanged), property.lambdaContext(), + [&property]( int index ) { property.setValue( index ); } ); +} + + + +void UiMapping::connectWidgetToProperty( const Configuration::TypedProperty& property, QComboBox* widget ) +{ + QObject::connect( widget, QOverload::of(&QComboBox::currentIndexChanged), property.lambdaContext(), + [widget, &property]( int index ) { property.setValue( widget->itemData( index ).toUuid() ); } ); + +} + + +} diff --git a/core/src/ConfigurationManager.cpp b/core/src/ConfigurationManager.cpp new file mode 100644 index 0000000..a7994e8 --- /dev/null +++ b/core/src/ConfigurationManager.cpp @@ -0,0 +1,103 @@ +/* + * ConfigurationManager.cpp - class for managing Veyon's configuration + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "Configuration/LocalStore.h" +#include "ConfigurationManager.h" +#include "Filesystem.h" +#include "PlatformCoreFunctions.h" +#include "PlatformInputDeviceFunctions.h" +#include "PlatformNetworkFunctions.h" +#include "VeyonConfiguration.h" +#include "VeyonServiceControl.h" + + + +ConfigurationManager::ConfigurationManager( QObject* parent ) : + QObject( parent ), + m_configuration( VeyonCore::config() ) +{ +} + + + +bool ConfigurationManager::clearConfiguration() +{ + Configuration::LocalStore( Configuration::LocalStore::System ).clear(); + + return true; +} + + + +bool ConfigurationManager::applyConfiguration() +{ + // update Veyon Service configuration + if( VeyonServiceControl().setAutostart( m_configuration.autostartService() ) == false ) + { + m_errorString = tr( "Could not modify the autostart property for the %1 Service." ).arg( VeyonCore::applicationName() ); + return false; + } + + auto& network = VeyonCore::platform().networkFunctions(); + + if( network.configureFirewallException( VeyonCore::filesystem().serverFilePath(), + QStringLiteral("Veyon Server"), + m_configuration.isFirewallExceptionEnabled() ) == false ) + { + m_errorString = tr( "Could not configure the firewall configuration for the %1 Server." ).arg( VeyonCore::applicationName() ); + return false; + } + + if( network.configureFirewallException( VeyonCore::filesystem().workerFilePath(), + QStringLiteral("Veyon Worker"), + m_configuration.isFirewallExceptionEnabled() ) == false ) + { + m_errorString = tr( "Could not configure the firewall configuration for the %1 Worker." ).arg( VeyonCore::applicationName() ); + return false; + } + + if( VeyonCore::platform().coreFunctions().applyConfiguration() == false ) + { + m_errorString = tr( "Could not apply platform-specific configuration settings." ); + return false; + } + + return true; +} + + + +bool ConfigurationManager::saveConfiguration() +{ + // write global configuration + Configuration::LocalStore localStore( Configuration::LocalStore::System ); + if( localStore.isWritable() == false ) + { + m_errorString = tr( "Configuration is not writable. Please check your permissions!" ); + return false; + } + + localStore.flush( &m_configuration ); + return true; +} diff --git a/core/src/ConfigurationPage.cpp b/core/src/ConfigurationPage.cpp new file mode 100644 index 0000000..d5dfba4 --- /dev/null +++ b/core/src/ConfigurationPage.cpp @@ -0,0 +1,30 @@ +/* + * ConfigurationPage.cpp - implementation of configuration page base class + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "ConfigurationPage.h" + +ConfigurationPage::ConfigurationPage( QWidget* parent ) : + QWidget( parent ) +{ +} diff --git a/core/src/CryptoCore.cpp b/core/src/CryptoCore.cpp new file mode 100644 index 0000000..54315b0 --- /dev/null +++ b/core/src/CryptoCore.cpp @@ -0,0 +1,97 @@ +/* + * CryptoCore.cpp - core functions for crypto features + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "CryptoCore.h" + +CryptoCore::CryptoCore() : + m_qcaInitializer(), + m_defaultPrivateKey() +{ + const auto features = QCA::supportedFeatures(); + + vDebug() << "CryptoCore instance created - features supported by QCA" << qcaVersionStr() << features; + + if( features.contains( QStringLiteral( "rsa" ) ) == false ) + { + qFatal( "CryptoCore: RSA not supported! Please install a QCA plugin which provides RSA support " + "(e.g. packages such as libqca-qt5-2-plugins or qca-qt5-ossl)." ); + } + + m_defaultPrivateKey = PrivateKey::fromPEMFile( QStringLiteral(":/core/default-pkey.pem") ); +} + + + +CryptoCore::~CryptoCore() +{ + vDebug(); +} + + + +QByteArray CryptoCore::generateChallenge() +{ + const auto challengeBigNum = BN_new(); + + if( challengeBigNum == nullptr ) + { + vCritical() << "BN_new() failed"; + return QByteArray(); + } + + // generate a random challenge + BN_rand( challengeBigNum, ChallengeSize * 8, 0, 0 ); + QByteArray chall( BN_num_bytes( challengeBigNum ), 0 ); + BN_bn2bin( challengeBigNum, reinterpret_cast( chall.data() ) ); + BN_free( challengeBigNum ); + + return chall; +} + + + +QString CryptoCore::encryptPassword( const PlaintextPassword& password ) const +{ + return QString::fromLatin1( m_defaultPrivateKey.toPublicKey(). + encrypt( password, DefaultEncryptionAlgorithm ).toByteArray().toHex() ); +} + + + +CryptoCore::PlaintextPassword CryptoCore::decryptPassword( const QString& encryptedPassword ) const +{ + PlaintextPassword decryptedPassword; + + if( PrivateKey( m_defaultPrivateKey ).decrypt( QByteArray::fromHex( encryptedPassword.toUtf8() ), + &decryptedPassword, DefaultEncryptionAlgorithm ) ) + { + return decryptedPassword; + } + + vCritical() << "failed to decrypt password!"; + + return {}; +} diff --git a/core/src/DesktopAccessDialog.cpp b/core/src/DesktopAccessDialog.cpp new file mode 100644 index 0000000..79a6e4b --- /dev/null +++ b/core/src/DesktopAccessDialog.cpp @@ -0,0 +1,170 @@ +/* + * DesktopAccessDialog.cpp - implementation of DesktopAccessDialog class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "DesktopAccessDialog.h" +#include "FeatureWorkerManager.h" +#include "HostAddress.h" +#include "PlatformCoreFunctions.h" +#include "VeyonServerInterface.h" +#include "VeyonWorkerInterface.h" + + +DesktopAccessDialog::DesktopAccessDialog( QObject* parent ) : + QObject( parent ), + m_desktopAccessDialogFeature( Feature( QLatin1String( staticMetaObject.className() ), + Feature::Service | Feature::Worker | Feature::Builtin, + Feature::Uid( "3dd8ec3e-7004-4936-8f2a-70699b9819be" ), + Feature::Uid(), + tr( "Desktop access dialog" ), {}, {} ) ), + m_features( { m_desktopAccessDialogFeature } ), + m_choice( ChoiceNone ), + m_abortTimer( this ) +{ + m_abortTimer.setSingleShot( true ); +} + + + +bool DesktopAccessDialog::isBusy( FeatureWorkerManager* featureWorkerManager ) const +{ + return featureWorkerManager->isWorkerRunning( m_desktopAccessDialogFeature.uid() ); +} + + + +void DesktopAccessDialog::exec( FeatureWorkerManager* featureWorkerManager, const QString& user, const QString& host ) +{ + m_choice = ChoiceNone; + + featureWorkerManager->sendMessageToManagedSystemWorker( + FeatureMessage( m_desktopAccessDialogFeature.uid(), RequestDesktopAccess ) + .addArgument( Argument::User, user ) + .addArgument( Argument::Host, host ) ); + + connect( &m_abortTimer, &QTimer::timeout, this, [=]() { abort( featureWorkerManager ); } ); + m_abortTimer.start( DialogTimeout ); +} + + + +void DesktopAccessDialog::abort( FeatureWorkerManager* featureWorkerManager ) +{ + featureWorkerManager->stopWorker( m_desktopAccessDialogFeature.uid() ); + + m_choice = ChoiceNone; + + Q_EMIT finished(); +} + + + +bool DesktopAccessDialog::handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) +{ + Q_UNUSED(messageContext) + + if( m_desktopAccessDialogFeature.uid() == message.featureUid() && + message.command() == ReportDesktopAccessChoice ) + { + m_choice = QVariantHelper::value( message.argument( Argument::Choice ) ); + + server.featureWorkerManager().stopWorker( m_desktopAccessDialogFeature.uid() ); + + m_abortTimer.stop(); + + Q_EMIT finished(); + + return true; + } + + return false; +} + + + +bool DesktopAccessDialog::handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) +{ + if( message.featureUid() != m_desktopAccessDialogFeature.uid() || + message.command() != RequestDesktopAccess ) + { + return false; + } + + const auto result = requestDesktopAccess( message.argument( Argument::User ).toString(), + message.argument( Argument::Host ).toString() ); + + FeatureMessage reply( m_desktopAccessDialogFeature.uid(), ReportDesktopAccessChoice ); + reply.addArgument( Argument::Choice, result ); + + return worker.sendFeatureMessageReply( reply ); +} + + + +DesktopAccessDialog::Choice DesktopAccessDialog::requestDesktopAccess( const QString& user, const QString& host ) +{ + auto hostName = HostAddress( host ).convert( HostAddress::Type::FullyQualifiedDomainName ); + if( hostName.isEmpty() ) + { + hostName = host; + } + + qApp->setQuitOnLastWindowClosed( false ); + + QMessageBox m( QMessageBox::Question, + tr( "Confirm desktop access" ), + tr( "The user %1 at computer %2 wants to access your desktop. Do you want to grant access?" ). + arg( user, hostName ), QMessageBox::Yes | QMessageBox::No ); + + auto neverBtn = m.addButton( tr( "Never for this session" ), QMessageBox::NoRole ); + auto alwaysBtn = m.addButton( tr( "Always for this session" ), QMessageBox::YesRole ); + + m.setEscapeButton( m.button( QMessageBox::No ) ); + m.setDefaultButton( neverBtn ); + + VeyonCore::platform().coreFunctions().raiseWindow( &m, true ); + + const auto result = m.exec(); + + if( m.clickedButton() == neverBtn ) + { + return ChoiceNever; + } + else if( m.clickedButton() == alwaysBtn ) + { + return ChoiceAlways; + } + else if( result == QMessageBox::Yes ) + { + return ChoiceYes; + } + + return ChoiceNo; +} diff --git a/core/src/FeatureControl.cpp b/core/src/FeatureControl.cpp new file mode 100644 index 0000000..eae67b5 --- /dev/null +++ b/core/src/FeatureControl.cpp @@ -0,0 +1,100 @@ +/* + * FeatureControl.cpp - implementation of FeatureControl class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "FeatureControl.h" +#include "FeatureWorkerManager.h" +#include "VeyonCore.h" +#include "VeyonServerInterface.h" + + +FeatureControl::FeatureControl( QObject* parent ) : + QObject( parent ), + m_featureControlFeature( Feature( QLatin1String( staticMetaObject.className() ), + Feature::Service | Feature::Worker | Feature::Builtin, + Feature::Uid( "a0a96fba-425d-414a-aaf4-352b76d7c4f3" ), + Feature::Uid(), + tr( "Feature control" ), {}, {}, {}, {} ) ), + m_features( { m_featureControlFeature } ) +{ +} + + + +void FeatureControl::queryActiveFeatures( const ComputerControlInterfaceList& computerControlInterfaces ) +{ + sendFeatureMessage( FeatureMessage{ m_featureControlFeature.uid(), QueryActiveFeatures }, + computerControlInterfaces, false ); +} + + + +bool FeatureControl::handleFeatureMessage( ComputerControlInterface::Pointer computerControlInterface, + const FeatureMessage& message ) +{ + if( message.featureUid() == m_featureControlFeature.uid() ) + { + const auto featureUidStrings = message.argument( Argument::ActiveFeaturesList ).toStringList(); + + FeatureUidList activeFeatures{}; + activeFeatures.reserve( featureUidStrings.size() ); + + for( const auto& featureUidString : featureUidStrings ) + { + activeFeatures.append( featureUidString ); + } + + computerControlInterface->setActiveFeatures( activeFeatures ); + + return true; + } + + return false; +} + + + +bool FeatureControl::handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) +{ + if( m_featureControlFeature.uid() == message.featureUid() ) + { + const auto featureUids = server.featureWorkerManager().runningWorkers(); + + QStringList featureUidStrings; + featureUidStrings.reserve( featureUids.size() ); + + for( const auto& featureUid : featureUids ) + { + featureUidStrings.append( featureUid.toString() ); + } + + FeatureMessage reply( message.featureUid(), message.command() ); + reply.addArgument( Argument::ActiveFeaturesList, featureUidStrings ); + + return server.sendFeatureMessageReply( messageContext, reply ); + } + + return false; +} diff --git a/core/src/FeatureManager.cpp b/core/src/FeatureManager.cpp new file mode 100644 index 0000000..38456d1 --- /dev/null +++ b/core/src/FeatureManager.cpp @@ -0,0 +1,259 @@ +/* + * FeatureManager.cpp - implementation of the FeatureManager class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "FeatureManager.h" +#include "FeatureMessage.h" +#include "PluginInterface.h" +#include "PluginManager.h" +#include "VeyonConfiguration.h" + +Q_DECLARE_METATYPE(Feature) +Q_DECLARE_METATYPE(FeatureMessage) + +// clazy:excludeall=reserve-candidates + +FeatureManager::FeatureManager( QObject* parent ) : + QObject( parent ), + m_features(), + m_emptyFeatureList(), + m_pluginObjects(), + m_dummyFeature() +{ + qRegisterMetaType(); + qRegisterMetaType(); + + for( const auto& pluginObject : qAsConst( VeyonCore::pluginManager().pluginObjects() ) ) + { + auto featurePluginInterface = qobject_cast( pluginObject ); + + if( featurePluginInterface ) + { + m_pluginObjects += pluginObject; + m_featurePluginInterfaces += featurePluginInterface; + + m_features += featurePluginInterface->featureList(); + } + } + +} + + + +const FeatureList& FeatureManager::features( Plugin::Uid pluginUid ) const +{ + for( auto pluginObject : m_pluginObjects ) + { + auto pluginInterface = qobject_cast( pluginObject ); + auto featurePluginInterface = qobject_cast( pluginObject ); + + if( pluginInterface && featurePluginInterface && pluginInterface->uid() == pluginUid ) + { + return featurePluginInterface->featureList(); + } + } + + return m_emptyFeatureList; +} + + + +const Feature& FeatureManager::feature( Feature::Uid featureUid ) const +{ + for( const auto& featureInterface : m_featurePluginInterfaces ) + { + for( const auto& feature : featureInterface->featureList() ) + { + if( feature.uid() == featureUid ) + { + return feature; + } + } + } + + return m_dummyFeature; +} + + + +Plugin::Uid FeatureManager::pluginUid( const Feature& feature ) const +{ + for( auto pluginObject : m_pluginObjects ) + { + auto pluginInterface = qobject_cast( pluginObject ); + auto featurePluginInterface = qobject_cast( pluginObject ); + + if( pluginInterface && featurePluginInterface && + featurePluginInterface->featureList().contains( feature ) ) + { + return pluginInterface->uid(); + } + } + + return {}; +} + + + +void FeatureManager::controlFeature( Feature::Uid featureUid, + FeatureProviderInterface::Operation operation, + const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) const +{ + for( auto featureInterface : qAsConst( m_featurePluginInterfaces ) ) + { + featureInterface->controlFeature( featureUid, operation, arguments, computerControlInterfaces ); + } + + updateActiveFeatures( computerControlInterfaces ); +} + + + +void FeatureManager::startFeature( VeyonMasterInterface& master, + const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) const +{ + vDebug() << "feature" << feature.name() << feature.uid() << computerControlInterfaces; + + for( auto featureInterface : qAsConst( m_featurePluginInterfaces ) ) + { + featureInterface->startFeature( master, feature, computerControlInterfaces ); + } + + if( feature.testFlag( Feature::Mode ) ) + { + for( const auto& controlInterface : computerControlInterfaces ) + { + controlInterface->setDesignatedModeFeature( feature.uid() ); + } + } + + updateActiveFeatures( computerControlInterfaces ); +} + + + +void FeatureManager::stopFeature( VeyonMasterInterface& master, + const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) const +{ + vDebug() << "feature" << feature.name() << feature.uid() << computerControlInterfaces; + + for( const auto& featureInterface : qAsConst( m_featurePluginInterfaces ) ) + { + featureInterface->stopFeature( master, feature, computerControlInterfaces ); + } + + for( const auto& controlInterface : computerControlInterfaces ) + { + if( controlInterface->designatedModeFeature() == feature.uid() ) + { + controlInterface->setDesignatedModeFeature( Feature::Uid() ); + } + } + + updateActiveFeatures( computerControlInterfaces ); +} + + + +void FeatureManager::updateActiveFeatures( const ComputerControlInterfaceList& computerControlInterfaces ) const +{ + for( const auto& controlInterface : computerControlInterfaces ) + { + controlInterface->updateActiveFeatures(); + } +} + + + +bool FeatureManager::handleFeatureMessage( ComputerControlInterface::Pointer computerControlInterface, + const FeatureMessage& message ) const +{ + vDebug() << "feature" << feature(message.featureUid()).name() + << "command" << message.command() + << "arguments" << message.arguments(); + + bool handled = false; + + for( const auto& featureInterface : qAsConst( m_featurePluginInterfaces ) ) + { + if( featureInterface->handleFeatureMessage( computerControlInterface, message ) ) + { + handled = true; + } + } + + return handled; +} + + + +bool FeatureManager::handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) const +{ + vDebug() << "feature" << feature(message.featureUid()).name() + << "command" << message.command() + << "arguments" << message.arguments(); + + if( VeyonCore::config().disabledFeatures().contains( message.featureUid().toString() ) ) + { + vWarning() << "ignoring message as feature" << message.featureUid() << "is disabled by configuration!"; + return false; + } + + bool handled = false; + + for( const auto& featureInterface : qAsConst( m_featurePluginInterfaces ) ) + { + if( featureInterface->handleFeatureMessage( server, messageContext, message ) ) + { + handled = true; + } + } + + return handled; +} + + + +bool FeatureManager::handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) const +{ + vDebug() << "feature" << feature(message.featureUid()).name() + << "command" << message.command() + << "arguments" << message.arguments(); + + bool handled = false; + + for( const auto& featureInterface : qAsConst( m_featurePluginInterfaces ) ) + { + if( featureInterface->handleFeatureMessage( worker, message ) ) + { + handled = true; + } + } + + return handled; +} diff --git a/core/src/FeatureMessage.cpp b/core/src/FeatureMessage.cpp new file mode 100644 index 0000000..ec1ef4d --- /dev/null +++ b/core/src/FeatureMessage.cpp @@ -0,0 +1,79 @@ +/* + * FeatureMessage.cpp - implementation of a message encapsulation class for features + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "FeatureMessage.h" +#include "VariantArrayMessage.h" + + +bool FeatureMessage::send( QIODevice* ioDevice ) const +{ + if( ioDevice ) + { + VariantArrayMessage message( ioDevice ); + + message.write( m_featureUid ); + message.write( m_command ); + message.write( m_arguments ); + + return message.send(); + } + + vCritical() << "no IO device!"; + + return false; +} + + + +bool FeatureMessage::isReadyForReceive( QIODevice* ioDevice ) +{ + return ioDevice != nullptr && + VariantArrayMessage( ioDevice ).isReadyForReceive(); +} + + + +bool FeatureMessage::receive( QIODevice* ioDevice ) +{ + if( ioDevice != nullptr ) + { + VariantArrayMessage message( ioDevice ); + + if( message.receive() ) + { + m_featureUid = message.read().toUuid(); // Flawfinder: ignore + m_command = QVariantHelper::value( message.read() ); // Flawfinder: ignore + m_arguments = message.read().toMap(); // Flawfinder: ignore + return true; + } + + vWarning() << "could not receive message!"; + } + else + { + vCritical() << "no IO device!"; + } + + return false; +} diff --git a/core/src/FeatureWorkerManager.cpp b/core/src/FeatureWorkerManager.cpp new file mode 100644 index 0000000..6552e09 --- /dev/null +++ b/core/src/FeatureWorkerManager.cpp @@ -0,0 +1,343 @@ +/* + * FeatureWorkerManager.cpp - class for managing feature worker instances + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "FeatureManager.h" +#include "FeatureWorkerManager.h" +#include "Filesystem.h" +#include "VeyonConfiguration.h" +#include "VeyonCore.h" +#include "PlatformCoreFunctions.h" +#include "PlatformUserFunctions.h" + +// clazy:excludeall=detaching-member + +FeatureWorkerManager::FeatureWorkerManager( VeyonServerInterface& server, FeatureManager& featureManager, QObject* parent ) : + QObject( parent ), + m_server( server ), + m_featureManager( featureManager ), + m_tcpServer( this ), + m_workersMutex( QMutex::Recursive ) +{ + connect( &m_tcpServer, &QTcpServer::newConnection, + this, &FeatureWorkerManager::acceptConnection ); + + if( !m_tcpServer.listen( QHostAddress::LocalHost, + static_cast( VeyonCore::config().featureWorkerManagerPort() + VeyonCore::sessionId() ) ) ) + { + vCritical() << "can't listen on localhost!"; + } + + auto pendingMessagesTimer = new QTimer( this ); + connect( pendingMessagesTimer, &QTimer::timeout, this, &FeatureWorkerManager::sendPendingMessages ); + + pendingMessagesTimer->start( 100 ); +} + + + +FeatureWorkerManager::~FeatureWorkerManager() +{ + m_tcpServer.close(); + + // properly shutdown all worker processes + while( m_workers.isEmpty() == false ) + { + stopWorker( m_workers.firstKey() ); + } +} + + + +bool FeatureWorkerManager::startManagedSystemWorker( Feature::Uid featureUid ) +{ + if( thread() != QThread::currentThread() ) + { + vCritical() << "thread mismatch for feature" << featureUid; + return false; + } + + stopWorker( featureUid ); + + Worker worker; + + worker.process = new QProcess; + worker.process->setProcessChannelMode( QProcess::ForwardedChannels ); + + connect( worker.process, static_cast(&QProcess::finished), + worker.process, &QProcess::deleteLater ); + + vDebug() << "Starting managed system worker for feature" << featureUid; + worker.process->start( VeyonCore::filesystem().workerFilePath(), { featureUid.toString() } ); + + m_workersMutex.lock(); + m_workers[featureUid] = worker; + m_workersMutex.unlock(); + + return true; +} + + + +bool FeatureWorkerManager::startUnmanagedSessionWorker( Feature::Uid featureUid ) +{ + if( thread() != QThread::currentThread() ) + { + vCritical() << "thread mismatch for feature" << featureUid; + return false; + } + + stopWorker( featureUid ); + + Worker worker; + + vDebug() << "Starting worker (unmanaged session process) for feature" << featureUid; + + const auto currentUser = VeyonCore::platform().userFunctions().currentUser(); + if( currentUser.isEmpty() ) + { + vDebug() << "could not determine current user - probably a console session with logon screen"; + return false; + } + + const auto ret = VeyonCore::platform().coreFunctions(). + runProgramAsUser( VeyonCore::filesystem().workerFilePath(), { featureUid.toString() }, + currentUser, + VeyonCore::platform().coreFunctions().activeDesktopName() ); + if( ret == false ) + { + return false; + } + + m_workersMutex.lock(); + m_workers[featureUid] = worker; + m_workersMutex.unlock(); + + return true; +} + + + +bool FeatureWorkerManager::stopWorker( Feature::Uid featureUid ) +{ + if( thread() != QThread::currentThread() ) + { + vCritical() << "thread mismatch for feature" << featureUid; + return false; + } + + QMutexLocker locker( &m_workersMutex ); + + if( m_workers.contains( featureUid ) ) + { + vDebug() << "Stopping worker for feature" << featureUid; + + auto& worker = m_workers[featureUid]; + + if( worker.socket ) + { + worker.socket->disconnect( this ); + disconnect( worker.socket ); + + worker.socket->close(); + worker.socket->deleteLater(); + } + + if( worker.process ) + { + auto killTimer = new QTimer; + connect( killTimer, &QTimer::timeout, worker.process, &QProcess::terminate ); + connect( killTimer, &QTimer::timeout, worker.process, &QProcess::kill ); + connect( killTimer, &QTimer::timeout, killTimer, &QTimer::deleteLater ); + killTimer->start( 5000 ); + } + + m_workers.remove( featureUid ); + } + + return false; +} + + + +void FeatureWorkerManager::sendMessageToManagedSystemWorker( const FeatureMessage& message ) +{ + if( isWorkerRunning( message.featureUid() ) == false && + startManagedSystemWorker( message.featureUid() ) == false ) + { + vCritical() << "could not start managed system worker"; + return; + } + + sendMessage( message ); + +} + + + +void FeatureWorkerManager::sendMessageToUnmanagedSessionWorker( const FeatureMessage& message ) +{ + if( isWorkerRunning( message.featureUid() ) == false && + startUnmanagedSessionWorker( message.featureUid() ) == false ) + { + vDebug() << "User session likely not yet available - retrying worker start"; + QTimer::singleShot( UnmanagedSessionProcessRetryInterval, this, + [=]() { sendMessageToUnmanagedSessionWorker( message ); } ); + return; + } + + sendMessage( message ); +} + + + +bool FeatureWorkerManager::isWorkerRunning( Feature::Uid featureUid ) +{ + QMutexLocker locker( &m_workersMutex ); + return m_workers.contains( featureUid ); +} + + + +FeatureUidList FeatureWorkerManager::runningWorkers() +{ + QMutexLocker locker( &m_workersMutex ); + + return m_workers.keys(); +} + + + +void FeatureWorkerManager::acceptConnection() +{ + vDebug() << "accepting connection"; + + QTcpSocket* socket = m_tcpServer.nextPendingConnection(); + + // connect to readyRead() signal of new connection + connect( socket, &QTcpSocket::readyRead, + this, [=] () { processConnection( socket ); } ); + + connect( socket, &QTcpSocket::disconnected, + this, [=] () { closeConnection( socket ); } ); +} + + + +void FeatureWorkerManager::processConnection( QTcpSocket* socket ) +{ + FeatureMessage message; + message.receive( socket ); + + m_workersMutex.lock(); + + // set socket information + if( m_workers.contains( message.featureUid() ) ) + { + if( m_workers[message.featureUid()].socket.isNull() ) + { + m_workers[message.featureUid()].socket = socket; + sendPendingMessages(); + } + + m_workersMutex.unlock(); + + if( message.command() >= 0 ) + { + m_featureManager.handleFeatureMessage( m_server, MessageContext( socket ), message ); + } + } + else + { + m_workersMutex.unlock(); + + vCritical() << "got data from non-existing worker!" << message.featureUid(); + } +} + + + +void FeatureWorkerManager::closeConnection( QTcpSocket* socket ) +{ + m_workersMutex.lock(); + + for( auto it = m_workers.begin(); it != m_workers.end(); ) + { + if( it.value().socket == socket ) + { + vDebug() << "removing worker after socket has been closed"; + it = m_workers.erase( it ); + } + else + { + ++it; + } + } + + m_workersMutex.unlock(); + + socket->deleteLater(); +} + + + +void FeatureWorkerManager::sendMessage( const FeatureMessage& message ) +{ + m_workersMutex.lock(); + + if( m_workers.contains( message.featureUid() ) ) + { + m_workers[message.featureUid()].pendingMessages.append( message ); + } + else + { + vWarning() << "worker does not exist for feature" << message.featureUid(); + } + + m_workersMutex.unlock(); +} + + + +void FeatureWorkerManager::sendPendingMessages() +{ + m_workersMutex.lock(); + + for( auto it = m_workers.begin(); it != m_workers.end(); ++it ) + { + auto& worker = it.value(); + + while( worker.socket && worker.pendingMessages.isEmpty() == false ) + { + worker.pendingMessages.first().send( worker.socket ); + worker.pendingMessages.removeFirst(); + } + } + + m_workersMutex.unlock(); +} diff --git a/core/src/FileSystemBrowser.cpp b/core/src/FileSystemBrowser.cpp new file mode 100644 index 0000000..c6cdda1 --- /dev/null +++ b/core/src/FileSystemBrowser.cpp @@ -0,0 +1,105 @@ +/* + * FileSystemBrowser.cpp - a wrapper class for easily browsing the file system + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "Filesystem.h" +#include "FileSystemBrowser.h" + + +QString FileSystemBrowser::exec( const QString &path, + const QString &title, + const QString &filter ) +{ + QString browsePath = path; + if( m_expandPath ) + { + browsePath = VeyonCore::filesystem().expandPath( browsePath ); + } + + switch( m_browseMode ) + { + case ExistingDirectory: + if( !QFileInfo( browsePath ).isDir() ) + { + browsePath = QDir::homePath(); + } + break; + case ExistingFile: + case SaveFile: + if( QFileInfo( browsePath ).isFile() ) + { + browsePath = QFileInfo( browsePath ).absolutePath(); + } + else + { + browsePath = QDir::homePath(); + } + break; + + default: break; + } + + QString chosenPath; + switch( m_browseMode ) + { + case ExistingDirectory: + chosenPath = QFileDialog::getExistingDirectory( nullptr, title, + browsePath, + QFileDialog::ShowDirsOnly | + QFileDialog::DontResolveSymlinks ); + break; + case ExistingFile: + chosenPath = QFileDialog::getOpenFileName( nullptr, title, + browsePath, filter ); + break; + case SaveFile: + chosenPath = QFileDialog::getSaveFileName( nullptr, title, + browsePath, filter ); + break; + + default: break; + } + + if( !chosenPath.isEmpty() ) + { + if( m_shrinkPath ) + { + return VeyonCore::filesystem().shrinkPath( chosenPath ); + } + return chosenPath; + } + + return path; +} + + + +void FileSystemBrowser::exec( QLineEdit *lineEdit, const QString &title, + const QString & filter ) +{ + lineEdit->setText( exec( lineEdit->text(), title, filter ) ); +} + diff --git a/core/src/Filesystem.cpp b/core/src/Filesystem.cpp new file mode 100644 index 0000000..7155ab3 --- /dev/null +++ b/core/src/Filesystem.cpp @@ -0,0 +1,170 @@ +/* + * Filesystem.cpp - filesystem related query and manipulation functions + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "VeyonConfiguration.h" +#include "Filesystem.h" +#include "PlatformFilesystemFunctions.h" + + +QString Filesystem::expandPath( QString path ) const +{ + const auto p = QDir::toNativeSeparators( path.replace( QStringLiteral( "%HOME%" ), QDir::homePath() ). + replace( QStringLiteral( "$HOME" ), QDir::homePath() ). + replace( QStringLiteral( "%PROFILE%" ), QDir::homePath() ). + replace( QStringLiteral( "$PROFILE" ), QDir::homePath() ). + replace( QStringLiteral( "%APPDATA%" ), VeyonCore::platform().filesystemFunctions().personalAppDataPath() ). + replace( QStringLiteral( "$APPDATA" ), VeyonCore::platform().filesystemFunctions().personalAppDataPath() ). + replace( QStringLiteral( "%GLOBALAPPDATA%" ), VeyonCore::platform().filesystemFunctions().globalAppDataPath() ). + replace( QStringLiteral( "$GLOBALAPPDATA" ), VeyonCore::platform().filesystemFunctions().globalAppDataPath() ). + replace( QStringLiteral( "%TMP%" ), QDir::tempPath() ). + replace( QStringLiteral( "$TMP" ), QDir::tempPath() ). + replace( QStringLiteral( "%TEMP%" ), QDir::tempPath() ). + replace( QStringLiteral( "$TEMP" ), QDir::tempPath() ) ); + + // remove duplicate directory separators - however skip the first two chars + // as they might specify an UNC path on Windows + if( p.length() > 3 ) + { + return p.left( 2 ) + p.mid( 2 ).replace( + QString( QStringLiteral( "%1%1" ) ).arg( QDir::separator() ), + QDir::separator() ); + } + + return p; +} + + + +QString Filesystem::shrinkPath( QString path ) const +{ + path = QDir::toNativeSeparators( path ); + + const QString envVar( QStringLiteral( "%%1%" ) ); + const auto personalAppDataPath = VeyonCore::platform().filesystemFunctions().personalAppDataPath(); + const auto globalAppDataPath = VeyonCore::platform().filesystemFunctions().globalAppDataPath(); + + if( path.startsWith( QDir::toNativeSeparators( QDir::tempPath() ) ) ) + { + path.replace( QDir::toNativeSeparators( QDir::tempPath() ), envVar.arg( QStringLiteral( "TEMP" ) ) ); + } + else if( path.startsWith( personalAppDataPath ) ) + { + path.replace( personalAppDataPath, envVar.arg( QStringLiteral( "APPDATA" ) ) ); + } + else if( path.startsWith( globalAppDataPath ) ) + { + path.replace( globalAppDataPath, envVar.arg( QStringLiteral( "GLOBALAPPDATA" ) ) ); + } + else if( path.startsWith( QDir::toNativeSeparators( QDir::homePath() ) ) ) + { + path.replace( QDir::toNativeSeparators( QDir::homePath() ), envVar.arg( QStringLiteral( "HOME" ) ) ); + } + + // remove duplicate directory separators - however skip the first two chars + // as they might specify an UNC path on Windows + if( path.length() > 3 ) + { + return QDir::toNativeSeparators( path.left( 2 ) + path.mid( 2 ).replace( + QString( QStringLiteral( "%1%1" ) ).arg( QDir::separator() ), QDir::separator() ) ); + } + + return QDir::toNativeSeparators( path ); +} + + + + +bool Filesystem::ensurePathExists( const QString &path ) const +{ + const QString expandedPath = VeyonCore::filesystem().expandPath( path ); + + if( path.isEmpty() || QDir( expandedPath ).exists() ) + { + return true; + } + + vDebug() << "creating " << path << "=>" << expandedPath; + + QString p = expandedPath; + + QStringList dirs; + while( !QDir( p ).exists() && !p.isEmpty() ) + { + dirs.push_front( QDir( p ).dirName() ); + p.chop( dirs.front().size() + 1 ); + } + + if( !p.isEmpty() ) + { + return QDir( p ).mkpath( dirs.join( QDir::separator() ) ); + } + + return false; +} + + + +QString Filesystem::privateKeyPath( const QString& name ) const +{ + const auto d = VeyonCore::filesystem().expandPath( VeyonCore::config().privateKeyBaseDir() ) + + QDir::separator() + name + QDir::separator() + QStringLiteral( "key" ); + + return QDir::toNativeSeparators( d ); +} + + + +QString Filesystem::publicKeyPath( const QString& name ) const +{ + const auto d = VeyonCore::filesystem().expandPath( VeyonCore::config().publicKeyBaseDir() ) + + QDir::separator() + name + QDir::separator() + QStringLiteral( "key" ); + + return QDir::toNativeSeparators( d ); +} + + + +QString Filesystem::screenshotDirectoryPath() const +{ + return expandPath( VeyonCore::config().screenshotDirectory() ); +} + + + +QString Filesystem::serverFilePath() const +{ + return QDir::toNativeSeparators( QCoreApplication::applicationDirPath() + QDir::separator() + + QStringLiteral("veyon-server" ) + VeyonCore::executableSuffix() ); +} + + + +QString Filesystem::workerFilePath() const +{ + return QDir::toNativeSeparators( QCoreApplication::applicationDirPath() + QDir::separator() + + QStringLiteral("veyon-worker" ) + VeyonCore::executableSuffix() ); +} diff --git a/core/src/HostAddress.cpp b/core/src/HostAddress.cpp new file mode 100644 index 0000000..932d8b0 --- /dev/null +++ b/core/src/HostAddress.cpp @@ -0,0 +1,315 @@ +/* + * HostAddress.cpp - implementation of HostAddress class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include "HostAddress.h" + + +HostAddress::HostAddress( const QString& address ) : + m_type( determineType( address ) ), + m_address( address ) +{ +} + + + +bool HostAddress::isLocalHost() const +{ + if( type() == Type::Invalid || m_address.isEmpty() ) + { + return false; + } + + const auto allLocalAddresses = QNetworkInterface::allAddresses(); + + if( type() == Type::IpAddress ) + { + QHostAddress hostAddress( m_address ); + return hostAddress.isLoopback() || allLocalAddresses.contains( hostAddress ); + } + + const auto addresses = QHostInfo::fromName( m_address ).addresses(); + for( const auto& address : addresses ) + { + if( address.isLoopback() || allLocalAddresses.contains( address ) ) + { + return true; + } + } + + return false; +} + + + +QString HostAddress::convert( HostAddress::Type targetType ) const +{ + if( m_type == targetType ) + { + return m_address; + } + + switch( targetType ) + { + case Type::Invalid: return {}; + case Type::IpAddress: return toIpAddress( m_address ); + case Type::HostName: return toHostName( m_type, m_address ); + case Type::FullyQualifiedDomainName: return toFQDN( m_type, m_address ); + } + + vWarning() << "invalid address type" << targetType; + return {}; +} + + + +QString HostAddress::tryConvert( HostAddress::Type targetType ) const +{ + const auto address = convert( targetType ); + if( address.isEmpty() ) + { + return m_address; + } + + return address; +} + + + +QStringList HostAddress::lookupIpAddresses() const +{ + const auto hostName = convert( Type::FullyQualifiedDomainName ); + const auto hostInfo = QHostInfo::fromName( hostName ); + if( hostInfo.error() != QHostInfo::NoError || hostInfo.addresses().isEmpty() ) + { + vWarning() << "could not lookup IP addresses of host" << hostName << "error:" << hostInfo.errorString(); + return {}; + } + + QStringList ipAddresses; + + const auto addresses = hostInfo.addresses(); + ipAddresses.reserve( addresses.size() ); + + for( const auto& address : addresses ) + { + ipAddresses.append( address.toString() ); + } + + return ipAddresses; +} + + +static QUrl parseAddressToUrl( const QString& address ) +{ + const auto colonCount = address.count( QLatin1Char(':') ); + if( colonCount > 1 ) + { + const auto parts = address.split( QLatin1Char(':') ); + return QUrl( QStringLiteral("scheme://[%1]:%2").arg( parts.mid( 0, colonCount ).join( QLatin1Char(':') ), + parts.last() ) ); + } + + return QUrl( QStringLiteral("scheme://%1").arg( address ) ); +} + + + +QString HostAddress::parseHost( const QString& address ) +{ + return parseAddressToUrl( address ).host(); +} + + + +int HostAddress::parsePortNumber( const QString& address ) +{ + return parseAddressToUrl( address ).port(); +} + + + +QString HostAddress::localFQDN() +{ + const auto localHostName = QHostInfo::localHostName(); + const auto type = determineType( localHostName ); + + switch( type ) + { + case Type::HostName: + if( QHostInfo::localDomainName().isEmpty() == false ) + { + return localHostName + QStringLiteral( "." ) + QHostInfo::localDomainName(); + } + + case Type::FullyQualifiedDomainName: + return localHostName; + + default: + vWarning() << "Could not determine local host name:" << localHostName; + break; + } + + return HostAddress( localHostName ).tryConvert( Type::FullyQualifiedDomainName ); +} + + + +HostAddress::Type HostAddress::determineType( const QString& address ) +{ + if( address.isEmpty() ) + { + return Type::Invalid; + } + + QHostAddress hostAddress( address ); + + if( hostAddress.isNull() || + hostAddress.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol ) + { + if( address.contains( QLatin1Char( '.' ) ) ) + { + return Type::FullyQualifiedDomainName; + } + + return Type::HostName; + } + + return Type::IpAddress; +} + + + +QString HostAddress::toIpAddress( const QString& hostName ) +{ + if( hostName.isEmpty() ) + { + vWarning() << "empty hostname"; + return {}; + } + + // then try to resolve ist first + const auto hostInfo = QHostInfo::fromName( hostName ); + if( hostInfo.error() != QHostInfo::NoError || hostInfo.addresses().isEmpty() ) + { + vWarning() << "could not lookup IP address of host" << hostName << "error:" << hostInfo.errorString(); + return {}; + } + +#if QT_VERSION < 0x050600 + const auto ipAddress = hostInfo.addresses().value( 0 ).toString(); +#else + const auto ipAddress = hostInfo.addresses().constFirst().toString(); +#endif + vDebug() << "Resolved IP address of host" << hostName << "to" << ipAddress; + + return ipAddress; +} + + + +QString HostAddress::toHostName( HostAddress::Type type, const QString& address ) +{ + if( address.isEmpty() ) + { + vWarning() << "empty address"; + return {}; + } + + switch( type ) + { + case Type::FullyQualifiedDomainName: + return fqdnToHostName( address ); + + case Type::IpAddress: + { + const auto hostInfo = QHostInfo::fromName( address ); + if( hostInfo.error() != QHostInfo::NoError ) + { + vWarning() << "could not lookup hostname for IP address" << address << "error:" << hostInfo.errorString(); + return {}; + } + + return fqdnToHostName( hostInfo.hostName() ); + } + + case Type::HostName: + return address; + + case Type::Invalid: + break; + } + + return {}; +} + + +QString HostAddress::toFQDN( HostAddress::Type type, const QString& address ) +{ + if( address.isEmpty() ) + { + vWarning() << "empty address"; + return {}; + } + + switch( type ) + { + case Type::HostName: + return toFQDN( Type::IpAddress, toIpAddress( address ) ); + + case Type::IpAddress: + { + const auto hostInfo = QHostInfo::fromName( address ); + if( hostInfo.error() != QHostInfo::NoError ) + { + vWarning() << "could not lookup hostname for IP address" << address << "error:" << hostInfo.errorString(); + return {}; + } + + return hostInfo.hostName(); + } + + case Type::FullyQualifiedDomainName: + return address; + + case Type::Invalid: + break; + } + + return {}; +} + + + +QString HostAddress::fqdnToHostName( const QString& fqdn ) +{ +#if QT_VERSION < 0x050600 + return fqdn.split( QLatin1Char( '.' ) ).value( 0 ); +#else + return fqdn.split( QLatin1Char( '.' ) ).constFirst(); +#endif +} diff --git a/core/src/LockWidget.cpp b/core/src/LockWidget.cpp new file mode 100644 index 0000000..a2682bc --- /dev/null +++ b/core/src/LockWidget.cpp @@ -0,0 +1,107 @@ +/* + * LockWidget.cpp - widget for locking a client + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "LockWidget.h" +#include "PlatformCoreFunctions.h" +#include "PlatformInputDeviceFunctions.h" + +#include +#include +#include +#include +#include + + +LockWidget::LockWidget( Mode mode, const QPixmap& background, QWidget* parent ) : + QWidget( parent, Qt::X11BypassWindowManagerHint ), + m_background( background ), + m_mode( mode ) +{ + if( mode == DesktopVisible ) + { + auto screen = QGuiApplication::primaryScreen(); + if( windowHandle() ) + { + screen = windowHandle()->screen(); + } + + if( screen ) + { + m_background = screen->grabWindow( 0 ); + } + } + + + VeyonCore::platform().coreFunctions().setSystemUiState( false ); + VeyonCore::platform().inputDeviceFunctions().disableInputDevices(); + + setWindowTitle( {} ); + show(); + move( 0, 0 ); + setFixedSize( qApp->desktop()->size() ); + VeyonCore::platform().coreFunctions().raiseWindow( this, true ); + showFullScreen(); + setFocusPolicy( Qt::StrongFocus ); + setFocus(); + grabMouse(); + grabKeyboard(); + setCursor( Qt::BlankCursor ); + QGuiApplication::setOverrideCursor( Qt::BlankCursor ); + + QCursor::setPos( mapToGlobal( QPoint( 0, 0 ) ) ); +} + + + +LockWidget::~LockWidget() +{ + VeyonCore::platform().inputDeviceFunctions().enableInputDevices(); + VeyonCore::platform().coreFunctions().setSystemUiState( true ); + + QGuiApplication::restoreOverrideCursor(); +} + + + +void LockWidget::paintEvent( QPaintEvent* event ) +{ + Q_UNUSED(event); + + QPainter p( this ); + switch( m_mode ) + { + case DesktopVisible: + p.drawPixmap( 0, 0, m_background ); + break; + + case BackgroundPixmap: + p.fillRect( rect(), QColor( 64, 64, 64 ) ); + p.drawPixmap( ( width() - m_background.width() ) / 2, + ( height() - m_background.height() ) / 2, + m_background ); + break; + + default: + break; + } +} diff --git a/core/src/Logger.cpp b/core/src/Logger.cpp new file mode 100644 index 0000000..d359467 --- /dev/null +++ b/core/src/Logger.cpp @@ -0,0 +1,350 @@ +/* + * Logger.cpp - a global clas for easily logging messages to log files + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "VeyonConfiguration.h" +#include "Filesystem.h" +#include "Logger.h" +#include "PlatformCoreFunctions.h" +#include "PlatformFilesystemFunctions.h" + +QAtomicPointer Logger::s_instance = nullptr; +QMutex Logger::s_instanceMutex; + + +Logger::Logger( const QString &appName ) : + m_logLevel( LogLevel::Default ), + m_logMutex(), + m_lastMessageLevel( LogLevel::Nothing ), + m_lastMessage(), + m_lastMessageCount( 0 ), + m_logToSystem( false ), + m_appName( QStringLiteral( "Veyon" ) + appName ), + m_logFile( nullptr ), + m_logFileSizeLimit( -1 ), + m_logFileRotationCount( -1 ) +{ + s_instanceMutex.lock(); + + Q_ASSERT(s_instance == nullptr); + + s_instance = this; + s_instanceMutex.unlock(); + + auto configuredLogLevel = VeyonCore::config().logLevel(); + if( qEnvironmentVariableIsSet( logLevelEnvironmentVariable() ) ) + { + configuredLogLevel = static_cast( qEnvironmentVariableIntValue( logLevelEnvironmentVariable() ) ); + } + + m_logLevel = qBound( LogLevel::Min, configuredLogLevel, LogLevel::Max ); + m_logToSystem = VeyonCore::config().logToSystem(); + + if( m_logLevel > LogLevel::Nothing ) + { + initLogFile(); + } + + qInstallMessageHandler( qtMsgHandler ); + + VeyonCore::platform().coreFunctions().initNativeLoggingSystem( appName ); + + if( QCoreApplication::instance() ) + { + // log current application start up + vDebug() << "Startup with arguments" << QCoreApplication::arguments(); + } + else + { + vDebug() << "Startup without QCoreApplication instance"; + } +} + + + + +Logger::~Logger() +{ + vDebug() << "Shutdown"; + + QMutexLocker l( &m_logMutex ); + + qInstallMessageHandler(nullptr); + + s_instanceMutex.lock(); + s_instance = nullptr; + s_instanceMutex.unlock(); + + delete m_logFile; +} + + + + +void Logger::initLogFile() +{ + QString logPath = VeyonCore::filesystem().expandPath( VeyonCore::config().logFileDirectory() ); + + if( !QDir( logPath ).exists() ) + { + if( QDir( QDir::rootPath() ).mkdir( logPath ) ) + { + QFile::setPermissions( logPath, + QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | + QFile::ReadUser | QFile::WriteUser | QFile::ExeUser | + QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup | + QFile::ReadOther | QFile::WriteOther | QFile::ExeOther ); + } + } + + logPath = logPath + QDir::separator(); + m_logFile = new QFile( logPath + QString( QStringLiteral( "%1.log" ) ).arg( m_appName ) ); + + openLogFile(); + + if( VeyonCore::config().logFileSizeLimitEnabled() ) + { + m_logFileSizeLimit = VeyonCore::config().logFileSizeLimit() * 1024 * 1024; + } + + if( VeyonCore::config().logFileRotationEnabled() ) + { + m_logFileRotationCount = VeyonCore::config().logFileRotationCount(); + } +} + + + +void Logger::openLogFile() +{ + if( VeyonCore::platform().filesystemFunctions().openFileSafely( + m_logFile, + QFile::WriteOnly | QFile::Append | QFile::Unbuffered | QFile::Text, + QFile::ReadOwner | QFile::WriteOwner ) == false ) + { + vCritical() << m_logFile->fileName() << "is a symlink and will not be written to for security reasons"; + m_logFile->close(); + delete m_logFile; + m_logFile = nullptr; + } +} + + + +void Logger::closeLogFile() +{ + if( m_logFile ) + { + m_logFile->close(); + } +} + + + +void Logger::clearLogFile() +{ + closeLogFile(); + if( m_logFile ) + { + m_logFile->remove(); + } + openLogFile(); +} + + + +void Logger::rotateLogFile() +{ + if( m_logFileRotationCount < 1 || m_logFile == nullptr ) + { + return; + } + + closeLogFile(); + + const QFileInfo logFileInfo( *m_logFile ); + const QStringList logFileFilter( { logFileInfo.fileName() + QStringLiteral( ".*" ) } ); + + auto rotatedLogFiles = logFileInfo.dir().entryList( logFileFilter, QDir::NoFilter, QDir::Name ); + + while( rotatedLogFiles.isEmpty() == false && + rotatedLogFiles.count() >= m_logFileRotationCount ) + { + logFileInfo.dir().remove( rotatedLogFiles.takeLast() ); + } + +#if QT_VERSION < 0x050600 +#warning Building compat code for unsupported version of Qt + using QStringListReverseIterator = std::reverse_iterator; + for( auto it = QStringListReverseIterator(rotatedLogFiles.cend()), + end = QStringListReverseIterator(rotatedLogFiles.cbegin()); it != end; ++it ) +#else + for( auto it = rotatedLogFiles.crbegin(), end = rotatedLogFiles.crend(); it != end; ++it ) +#endif + { + bool numberOk = false; + int logFileIndex = it->section( QLatin1Char('.'), -1 ).toInt( &numberOk ); + if( numberOk ) + { + const auto oldFileName = QString( QStringLiteral( "%1.%2" ) ).arg( m_logFile->fileName() ).arg( logFileIndex ); + const auto newFileName = QString( QStringLiteral( "%1.%2" ) ).arg( m_logFile->fileName() ).arg( logFileIndex + 1 ); + QFile::rename( oldFileName, newFileName ); + } + else + { + // remove stale log file + logFileInfo.dir().remove( *it ); + } + } + + QFile::rename( m_logFile->fileName(), m_logFile->fileName() + QStringLiteral( ".0" ) ); + + openLogFile(); +} + + + + +QString Logger::formatMessage( LogLevel ll, const QString& message ) +{ + QString messageType; + switch( ll ) + { + case LogLevel::Debug: messageType = QStringLiteral( "DEBUG" ); break; + case LogLevel::Info: messageType = QStringLiteral( "INFO" ); break; + case LogLevel::Warning: messageType = QStringLiteral( "WARN" ); break; + case LogLevel::Error: messageType = QStringLiteral( "ERR" ); break; + case LogLevel::Critical: messageType = QStringLiteral( "CRIT" ); break; + default: break; + } + + return QStringLiteral( "%1.%2: [%3] %4\n" ).arg( + QDateTime::currentDateTime().toString( Qt::ISODate ), + QDateTime::currentDateTime().toString( QStringLiteral( "zzz" ) ), + messageType, + message.trimmed() ); +} + + + + +void Logger::qtMsgHandler( QtMsgType messageType, const QMessageLogContext& context, const QString& message ) +{ + QMutexLocker instanceLocker( &s_instanceMutex ); + + const auto instance = s_instance.loadAcquire(); + + if( instance == nullptr || message.size() > MaximumMessageSize ) + { + return; + } + + LogLevel logLevel = LogLevel::Default; + + switch( messageType ) + { + case QtDebugMsg: logLevel = LogLevel::Debug; break; + case QtInfoMsg: logLevel = LogLevel::Info; break; + case QtWarningMsg: logLevel = LogLevel::Warning; break; + case QtCriticalMsg: logLevel = LogLevel::Error; break; + case QtFatalMsg: logLevel = LogLevel::Critical; break; + } + + if( context.category && strcmp(context.category, "default") != 0 ) + { + instance->log( logLevel, QStringLiteral( "[%1] " ).arg(QLatin1String(context.category)) + message ); + } + else + { + instance->log( logLevel, message ); + } +} + + + + +void Logger::log( LogLevel logLevel, const QString& message ) +{ + if( m_logLevel >= logLevel ) + { + QMutexLocker l( &m_logMutex ); + if( message == m_lastMessage && logLevel == m_lastMessageLevel ) + { + ++m_lastMessageCount; + } + else + { + if( m_lastMessageCount ) + { + outputMessage( formatMessage( m_lastMessageLevel, QStringLiteral( "---" ) ) ); + outputMessage( formatMessage( m_lastMessageLevel, QStringLiteral( "Last message repeated %1 times" ).arg( m_lastMessageCount ) ) ); + outputMessage( formatMessage( m_lastMessageLevel, QStringLiteral( "---" ) ) ); + m_lastMessageCount = 0; + } + outputMessage( formatMessage( logLevel, message ) ); + + if( m_logToSystem ) + { + VeyonCore::platform().coreFunctions().writeToNativeLoggingSystem( message, logLevel ); + } + + m_lastMessage = message; + m_lastMessageLevel = logLevel; + } + } +} + + + +void Logger::outputMessage( const QString& message ) +{ + if( m_logFile ) + { + m_logFile->write( message.toUtf8() ); + m_logFile->flush(); + + if( m_logFileSizeLimit > 0 && + m_logFile->size() > m_logFileSizeLimit ) + { + if( m_logFileRotationCount > 0 ) + { + rotateLogFile(); + } + else + { + clearLogFile(); + } + } + } + + if( VeyonCore::config().logToStdErr() ) + { + fprintf( stderr, "%s", message.toUtf8().constData() ); + fflush( stderr ); + } +} diff --git a/core/src/MonitoringMode.cpp b/core/src/MonitoringMode.cpp new file mode 100644 index 0000000..e5fba32 --- /dev/null +++ b/core/src/MonitoringMode.cpp @@ -0,0 +1,123 @@ +/* + * MonitoringMode.cpp - implementation of MonitoringMode class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "MonitoringMode.h" +#include "PlatformSessionFunctions.h" +#include "PlatformUserFunctions.h" +#include "VeyonServerInterface.h" + + +MonitoringMode::MonitoringMode( QObject* parent ) : + QObject( parent ), + m_monitoringModeFeature( QLatin1String( staticMetaObject.className() ), + Feature::Mode | Feature::Master | Feature::Builtin, + Feature::Uid( "edad8259-b4ef-4ca5-90e6-f238d0fda694" ), + Feature::Uid(), + tr( "Monitoring" ), tr( "Monitoring" ), + tr( "This mode allows you to monitor all computers at one or more locations." ), + QStringLiteral( ":/core/presentation-none.png" ) ), + m_queryLoggedOnUserInfoFeature( QStringLiteral("UserSessionInfo"), + Feature::Session | Feature::Service | Feature::Worker | Feature::Builtin, + Feature::Uid( "79a5e74d-50bd-4aab-8012-0e70dc08cc72" ), + Feature::Uid(), {}, {}, {} ), + m_features( { m_monitoringModeFeature, m_queryLoggedOnUserInfoFeature } ) +{ +} + + + +void MonitoringMode::queryLoggedOnUserInfo( const ComputerControlInterfaceList& computerControlInterfaces ) +{ + sendFeatureMessage( FeatureMessage{ m_queryLoggedOnUserInfoFeature.uid(), FeatureMessage::DefaultCommand }, + computerControlInterfaces, false ); +} + + + +bool MonitoringMode::handleFeatureMessage( ComputerControlInterface::Pointer computerControlInterface, + const FeatureMessage& message ) +{ + if( message.featureUid() == m_queryLoggedOnUserInfoFeature.uid() ) + { + computerControlInterface->setUserInformation( message.argument( Argument::UserLoginName ).toString(), + message.argument( Argument::UserFullName ).toString(), + message.argument( Argument::UserSessionId ).toInt() ); + + return true; + } + + return false; +} + + + +bool MonitoringMode::handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) +{ + if( m_queryLoggedOnUserInfoFeature.uid() == message.featureUid() ) + { + FeatureMessage reply( message.featureUid(), message.command() ); + + m_userDataLock.lockForRead(); + if( m_userLoginName.isEmpty() ) + { + queryUserInformation(); + reply.addArgument( Argument::UserLoginName, QString() ); + reply.addArgument( Argument::UserFullName, QString() ); + reply.addArgument( Argument::UserSessionId, -1 ); + } + else + { + reply.addArgument( Argument::UserLoginName, m_userLoginName ); + reply.addArgument( Argument::UserFullName, m_userFullName ); + reply.addArgument( Argument::UserSessionId, m_userSessionId ); + } + m_userDataLock.unlock(); + + return server.sendFeatureMessageReply( messageContext, reply ); + } + + return false; +} + + + +void MonitoringMode::queryUserInformation() +{ + // asynchronously query information about logged on user (which might block + // due to domain controller queries and timeouts etc.) + QtConcurrent::run( [=]() { + const auto userLoginName = VeyonCore::platform().userFunctions().currentUser(); + const auto userFullName = VeyonCore::platform().userFunctions().fullName( userLoginName ); + const auto userSessionId = VeyonCore::sessionId(); + m_userDataLock.lockForWrite(); + m_userLoginName = userLoginName; + m_userFullName = userFullName; + m_userSessionId = userSessionId; + m_userDataLock.unlock(); + } ); +} diff --git a/core/src/NetworkObject.cpp b/core/src/NetworkObject.cpp new file mode 100644 index 0000000..12ed8fb --- /dev/null +++ b/core/src/NetworkObject.cpp @@ -0,0 +1,237 @@ +/* + * NetworkObject.cpp - data class representing a network object + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "NetworkObject.h" +#include "HostAddress.h" + + +const QUuid NetworkObject::networkObjectNamespace( QStringLiteral( "8a6c479e-243e-4ccb-8e5a-0ddf5cf3c7d0" ) ); + + +NetworkObject::NetworkObject( const NetworkObject& other ) : + m_type( other.type() ), + m_name( other.name() ), + m_hostAddress( other.hostAddress() ), + m_macAddress( other.macAddress() ), + m_directoryAddress( other.directoryAddress() ), + m_uid( other.uid() ), + m_parentUid( other.parentUid() ), + m_populated( other.isPopulated() ) +{ +} + + + +NetworkObject::NetworkObject( NetworkObject::Type type, + const Name& name, + const QString& hostAddress, + const QString& macAddress, + const QString& directoryAddress, + Uid uid, + Uid parentUid ) : + m_type( type ), + m_name( name ), + m_hostAddress( hostAddress ), + m_macAddress( macAddress ), + m_directoryAddress( directoryAddress ), + m_uid( uid ), + m_parentUid( parentUid ), + m_populated( false ) +{ + if( m_uid.isNull() ) + { + m_uid = calculateUid(); + } +} + + + +NetworkObject::NetworkObject( const QJsonObject& jsonObject ) : + m_type( static_cast( jsonObject.value( QStringLiteral( "Type" ) ).toInt() ) ), + m_name( jsonObject.value( QStringLiteral( "Name" ) ).toString() ), + m_hostAddress( jsonObject.value( QStringLiteral( "HostAddress" ) ).toString() ), + m_macAddress( jsonObject.value( QStringLiteral( "MacAddress" ) ).toString() ), + m_directoryAddress( jsonObject.value( QStringLiteral( "DirectoryAddress" ) ).toString() ), + m_uid( jsonObject.value( QStringLiteral( "Uid" ) ).toString() ), + m_parentUid( jsonObject.value( QStringLiteral( "ParentUid" ) ).toString() ), + m_populated( false ) +{ +} + + + +NetworkObject& NetworkObject::operator=( const NetworkObject& other ) +{ + m_type = other.type(); + m_name = other.name(); + m_hostAddress = other.hostAddress(); + m_macAddress = other.macAddress(); + m_directoryAddress = other.directoryAddress(); + m_uid = other.uid(); + m_parentUid = other.parentUid(); + + return *this; +} + + + +bool NetworkObject::operator==( const NetworkObject& other ) const +{ + return uid() == other.uid(); +} + + + +bool NetworkObject::exactMatch( const NetworkObject& other ) const +{ + return uid() == other.uid() && + type() == other.type() && + name() == other.name() && + hostAddress() == other.hostAddress() && + macAddress() == other.macAddress() && + directoryAddress() == other.directoryAddress() && + parentUid() == other.parentUid(); +} + + + +NetworkObject::ModelId NetworkObject::modelId() const +{ + if( type() == Type::Root ) + { + return 0; + } + + auto id = + ( static_cast( uid().data1 ) << 0u ) + + ( static_cast( uid().data2 ) << 32u ) + + ( static_cast( uid().data3 ) << 48u ) + + ( static_cast( uid().data4[0] ) << 0u ) + + ( static_cast( uid().data4[1] ) << 8u ) + + ( static_cast( uid().data4[2] ) << 16u ) + + ( static_cast( uid().data4[3] ) << 24u ) + + ( static_cast( uid().data4[4] ) << 32u ) + + ( static_cast( uid().data4[5] ) << 40u ) + + ( static_cast( uid().data4[6] ) << 48u ) + + ( static_cast( uid().data4[7] ) << 56u ); + + return static_cast( id ); +} + + + +QJsonObject NetworkObject::toJson() const +{ + QJsonObject json; + json[QStringLiteral("Type")] = static_cast( type() ); + json[QStringLiteral("Uid")] = uid().toString(); + json[QStringLiteral("Name")] = name(); + + if( hostAddress().isEmpty() == false ) + { + json[QStringLiteral("HostAddress")] = hostAddress(); + } + + if( macAddress().isEmpty() == false ) + { + json[QStringLiteral("MacAddress")] = macAddress(); + } + + if( directoryAddress().isEmpty() == false ) + { + json[QStringLiteral("DirectoryAddress")] = directoryAddress(); + } + + if( parentUid().isNull() == false ) + { + json[QStringLiteral("ParentUid")] = parentUid().toString(); + } + + return json; +} + + + +QVariant NetworkObject::attributeValue( NetworkObject::Attribute attribute ) const +{ + switch( attribute ) + { + case Attribute::None: return {}; + case Attribute::Type: return QVariant::fromValue( type() ); + case Attribute::Name: return name(); + case Attribute::HostAddress: return hostAddress(); + case Attribute::MacAddress: return macAddress(); + case Attribute::DirectoryAddress: return directoryAddress(); + case Attribute::Uid: return uid(); + case Attribute::ParentUid: return parentUid(); + } + + return {}; +} + + + +bool NetworkObject::isAttributeValueEqual( NetworkObject::Attribute attribute, + const QVariant& value, Qt::CaseSensitivity cs ) const +{ + const auto myValue = attributeValue( attribute ); + const auto myValueType = myValue.userType(); + + if( myValueType == value.userType() && + myValueType == QMetaType::QString ) + { + // make sure to compare host addresses in the same format + if( attribute == NetworkObject::Attribute::HostAddress ) + { + const HostAddress myHostAddress( myValue.toString() ); + const auto otherHost = HostAddress( value.toString() ).convert( myHostAddress.type() ); + + return myValue.toString().compare( otherHost, cs ) == 0; + } + + return myValue.toString().compare( value.toString(), cs ) == 0; + } + + return myValue == value; +} + + + +NetworkObject::Uid NetworkObject::calculateUid() const +{ + // if a directory address is set (e.g. full DN in LDAP) it should be unique and can be + // used for hashing + if( directoryAddress().isEmpty() == false ) + { + return QUuid::createUuidV5( networkObjectNamespace, directoryAddress() ); + } + else if( type() == Type::Root ) + { + return QUuid::createUuidV5( networkObjectNamespace, QByteArrayLiteral("Root") ); + } + + return QUuid::createUuidV5( networkObjectNamespace, name() + hostAddress() + macAddress() + parentUid().toString() ); +} diff --git a/core/src/NetworkObjectDirectory.cpp b/core/src/NetworkObjectDirectory.cpp new file mode 100644 index 0000000..7a6762e --- /dev/null +++ b/core/src/NetworkObjectDirectory.cpp @@ -0,0 +1,376 @@ +/* + * NetworkObjectDirectory.cpp - base class for network object directory implementations + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "VeyonConfiguration.h" +#include "VeyonCore.h" +#include "NetworkObjectDirectory.h" + + +NetworkObjectDirectory::NetworkObjectDirectory( QObject* parent ) : + QObject( parent ), + m_updateTimer( new QTimer( this ) ), + m_objects(), + m_invalidObject( NetworkObject::Type::None ), + m_rootObject( NetworkObject::Type::Root ), + m_defaultObjectList() +{ + connect( m_updateTimer, &QTimer::timeout, this, &NetworkObjectDirectory::update ); + + // insert root item + m_objects[rootId()] = {}; +} + + + +void NetworkObjectDirectory::setUpdateInterval( int interval ) +{ + if( interval >= MinimumUpdateInterval ) + { + m_updateTimer->start( interval*1000 ); + } + else + { + m_updateTimer->stop(); + } +} + + + + +const NetworkObjectList& NetworkObjectDirectory::objects( const NetworkObject& parent ) const +{ + if( parent.type() == NetworkObject::Type::Root || + parent.type() == NetworkObject::Type::Location || + parent.type() == NetworkObject::Type::DesktopGroup ) + { + const auto it = m_objects.constFind( parent.modelId() ); + if( it != m_objects.end() ) + { + return it.value(); + } + } + + return m_defaultObjectList; +} + + + +const NetworkObject& NetworkObjectDirectory::object( NetworkObject::ModelId parent, NetworkObject::ModelId object ) const +{ + if( object == rootId() ) + { + return m_rootObject; + } + + const auto it = m_objects.constFind( parent ); + if( it != m_objects.end() ) + { + int index = 0; + for( const auto& entry : *it ) + { + if( entry.modelId() == object ) + { + return entry; + } + ++index; + } + } + + return m_invalidObject; +} + + + +int NetworkObjectDirectory::index( NetworkObject::ModelId parent, NetworkObject::ModelId child ) const +{ + const auto it = m_objects.constFind( parent ); + if( it != m_objects.end() ) + { + int index = 0; + for( const auto& entry : *it ) + { + if( entry.modelId() == child ) + { + return index; + } + ++index; + } + } + + return -1; +} + + + +int NetworkObjectDirectory::childCount( NetworkObject::ModelId parent ) const +{ + const auto it = m_objects.constFind( parent ); + if( it != m_objects.end() ) + { + return it->count(); + } + + return 0; +} + + + +NetworkObject::ModelId NetworkObjectDirectory::childId( NetworkObject::ModelId parent, int index ) const +{ + const auto it = m_objects.constFind( parent ); + if( it != m_objects.end() ) + { + if( index < it->count() ) + { + return it->at( index ).modelId(); + } + } + + return 0; +} + + + +NetworkObject::ModelId NetworkObjectDirectory::parentId( NetworkObject::ModelId child ) const +{ + if( child == rootId() ) + { + return 0; + } + + for( auto it = m_objects.constBegin(); it != m_objects.constEnd(); ++it ) + { + const auto& objectList = it.value(); + + for( const auto& object : objectList ) + { + if( object.modelId() == child ) + { + return it.key(); + } + } + } + + return 0; +} + + + +NetworkObject::ModelId NetworkObjectDirectory::rootId() const +{ + return m_rootObject.modelId(); +} + + + +NetworkObjectList NetworkObjectDirectory::queryObjects( NetworkObject::Type type, + NetworkObject::Attribute attribute, + const QVariant& value ) +{ + if( hasObjects() == false ) + { + update(); + } + + NetworkObjectList objects; + + for( auto it = m_objects.constBegin(); it != m_objects.constEnd(); ++it ) + { + const auto& objectList = it.value(); + + for( const auto& object : objectList ) + { + if( ( type == NetworkObject::Type::None || object.type() == type ) && + ( attribute == NetworkObject::Attribute::None || + object.isAttributeValueEqual( attribute, value, Qt::CaseInsensitive ) ) ) + { + objects.append( object ); + } + } + } + + return objects; +} + + + +NetworkObjectList NetworkObjectDirectory::queryParents( const NetworkObject& child ) +{ + if( hasObjects() == false ) + { + update(); + } + + if( child.type() == NetworkObject::Type::Root ) + { + return {}; + } + + for( auto it = m_objects.constBegin(); it != m_objects.constEnd(); ++it ) + { + const auto& objectList = it.value(); + + for( const auto& object : objectList ) + { + if( object.uid() == child.parentUid() ) + { + return queryParents( object ) + NetworkObjectList( { object } ); + } + } + } + + return {}; +} + + + +void NetworkObjectDirectory::fetchObjects( const NetworkObject& object ) +{ + if( object.type() == NetworkObject::Type::Root ) + { + update(); + } + + setObjectPopulated( object ); +} + + + +bool NetworkObjectDirectory::hasObjects() const +{ + return m_objects.size() > 1; +} + + + +void NetworkObjectDirectory::addOrUpdateObject( const NetworkObject& networkObject, const NetworkObject& parent ) +{ + if( m_objects.contains( parent.modelId() ) == false ) + { + vCritical() << "parent" << parent.toJson() << "does not exist for object" << networkObject.toJson(); + return; + } + + auto completeNetworkObject = networkObject; + if( completeNetworkObject.parentUid().isNull() ) + { + completeNetworkObject.setParentUid( parent.uid() ); + } + + auto& objectList = m_objects[parent.modelId()]; // clazy:exclude=detaching-member + const auto index = objectList.indexOf( completeNetworkObject ); + + if( index < 0 ) + { + Q_EMIT objectsAboutToBeInserted( parent, objectList.count(), 1 ); + + objectList.append( completeNetworkObject ); + if( completeNetworkObject.type() == NetworkObject::Type::Location || + completeNetworkObject.type() == NetworkObject::Type::DesktopGroup ) + { + m_objects[completeNetworkObject.modelId()] = {}; + } + + Q_EMIT objectsInserted(); + } + else if( objectList[index].exactMatch( completeNetworkObject ) == false ) + { + objectList.replace( index, completeNetworkObject ); + Q_EMIT objectChanged( parent, index ); + } +} + + + +void NetworkObjectDirectory::removeObjects( const NetworkObject& parent, const NetworkObjectFilter& removeObjectFilter ) +{ + if( m_objects.contains( parent.modelId() ) == false ) + { + return; + } + + auto& objectList = m_objects[parent.modelId()]; // clazy:exclude=detaching-member + int index = 0; + QList groupsToRemove; + + for( auto it = objectList.begin(); it != objectList.end(); ) + { + if( removeObjectFilter( *it ) ) + { + if( it->type() == NetworkObject::Type::Location || + it->type() == NetworkObject::Type::DesktopGroup ) + { + groupsToRemove.append( it->modelId() ); + } + + Q_EMIT objectsAboutToBeRemoved( parent, index, 1 ); + it = objectList.erase( it ); + Q_EMIT objectsRemoved(); + } + else + { + ++it; + ++index; + } + } + + for( const auto& groupId : groupsToRemove ) + { + m_objects.remove( groupId ); + } +} + + + +void NetworkObjectDirectory::replaceObjects( const NetworkObjectList& objects, const NetworkObject& parent ) +{ + for( const auto& object : objects ) + { + addOrUpdateObject( object, parent ); + } + + removeObjects( parent, [&objects]( const NetworkObject& object ) { return objects.contains( object ) == false; } ); +} + + + +void NetworkObjectDirectory::setObjectPopulated( const NetworkObject& networkObject ) +{ + const auto objectModelId = networkObject.modelId(); + + auto it = m_objects.find( parentId( objectModelId ) ); // clazy:exclude=detaching-member + if( it != m_objects.end() ) + { + for( auto& entry : *it ) + { + if( entry.modelId() == objectModelId ) + { + entry.setPopulated(); + break; + } + } + } +} diff --git a/core/src/NetworkObjectDirectoryManager.cpp b/core/src/NetworkObjectDirectoryManager.cpp new file mode 100644 index 0000000..b233a89 --- /dev/null +++ b/core/src/NetworkObjectDirectoryManager.cpp @@ -0,0 +1,104 @@ +/* + * NetworkObjectDirectoryManager.cpp - implementation of NetworkObjectDirectoryManager + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "VeyonConfiguration.h" +#include "NetworkObjectDirectoryManager.h" +#include "NetworkObjectDirectoryPluginInterface.h" +#include "PluginManager.h" + + +NetworkObjectDirectoryManager::NetworkObjectDirectoryManager( QObject* parent ) : + QObject( parent ), + m_directoryPluginInterfaces(), + m_configuredDirectory( nullptr ) +{ + for( auto pluginObject : qAsConst( VeyonCore::pluginManager().pluginObjects() ) ) + { + auto pluginInterface = qobject_cast( pluginObject ); + auto directoryPluginInterface = qobject_cast( pluginObject ); + + if( pluginInterface && directoryPluginInterface ) + { + m_directoryPluginInterfaces[pluginInterface] = directoryPluginInterface; + } + } +} + + + +QMap NetworkObjectDirectoryManager::availableDirectories() +{ + QMap items; + + for( auto it = m_directoryPluginInterfaces.constBegin(), end = m_directoryPluginInterfaces.constEnd(); it != end; ++it ) + { + items[it.key()->uid()] = it.value()->directoryName(); + } + + return items; +} + + + +NetworkObjectDirectory* NetworkObjectDirectoryManager::configuredDirectory() +{ + if( m_configuredDirectory == nullptr ) + { + m_configuredDirectory = createDirectory( VeyonCore::config().networkObjectDirectoryPlugin(), this ); + } + + return m_configuredDirectory; +} + + + +NetworkObjectDirectory* NetworkObjectDirectoryManager::createDirectory( Plugin::Uid uid, QObject* parent ) +{ + for( auto it = m_directoryPluginInterfaces.constBegin(), end = m_directoryPluginInterfaces.constEnd(); it != end; ++it ) + { + if( it.key()->uid() == uid ) + { + auto directory = it.value()->createNetworkObjectDirectory( parent ); + if( directory ) + { + return directory; + } + } + } + + for( auto it = m_directoryPluginInterfaces.constBegin(), end = m_directoryPluginInterfaces.constEnd(); it != end; ++it ) + { + if( it.key()->flags().testFlag( Plugin::ProvidesDefaultImplementation ) ) + { + auto defaultDirectory = it.value()->createNetworkObjectDirectory( parent ); + if( defaultDirectory ) + { + return defaultDirectory; + } + } + } + + vCritical() << "no default plugin available! requested plugin:" << uid; + return nullptr; +} diff --git a/core/src/PasswordDialog.cpp b/core/src/PasswordDialog.cpp new file mode 100644 index 0000000..d9cb572 --- /dev/null +++ b/core/src/PasswordDialog.cpp @@ -0,0 +1,108 @@ +/* + * PasswordDialog.cpp - dialog for querying logon credentials + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "PasswordDialog.h" +#include "PlatformUserFunctions.h" + +#include "ui_PasswordDialog.h" + + +PasswordDialog::PasswordDialog( QWidget *parent ) : + QDialog( parent ), + ui( new Ui::PasswordDialog ) +{ + ui->setupUi( this ); + + ui->username->setText( VeyonCore::platform().userFunctions().currentUser() ); + + if( ui->username->text().isEmpty() == false ) + { + ui->password->setFocus(); + } + + updateOkButton(); + + VeyonCore::enforceBranding( this ); +} + + + +PasswordDialog::~PasswordDialog() +{ + delete ui; +} + + + +QString PasswordDialog::username() const +{ + return ui->username->text(); +} + + + +CryptoCore::PlaintextPassword PasswordDialog::password() const +{ + return ui->password->text().toUtf8(); +} + + + + +AuthenticationCredentials PasswordDialog::credentials() const +{ + AuthenticationCredentials cred; + cred.setLogonUsername( username() ); + cred.setLogonPassword( password() ); + + return cred; +} + + + +void PasswordDialog::accept() +{ + if( VeyonCore::platform().userFunctions().authenticate( username(), password() ) == false ) + { + QMessageBox::critical( window(), + tr( "Authentication error" ), + tr( "Logon failed with given username and password. Please try again!" ) ); + } + else + { + QDialog::accept(); + } +} + + + +void PasswordDialog::updateOkButton() +{ + ui->buttonBox->button( QDialogButtonBox::Ok )-> + setEnabled( !username().isEmpty() && !password().isEmpty() ); +} + diff --git a/core/src/PasswordDialog.ui b/core/src/PasswordDialog.ui new file mode 100644 index 0000000..f068824 --- /dev/null +++ b/core/src/PasswordDialog.ui @@ -0,0 +1,142 @@ + + + PasswordDialog + + + Veyon Logon + + + + :/core/application-x-pem-key.png:/core/application-x-pem-key.png + + + + + + Please enter your username and password in order to access computers. + + + true + + + + + + + 16 + + + + + Username + + + + + + + + 350 + 0 + + + + + + + + Password + + + + + + + QLineEdit::Password + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + PasswordDialog + accept() + + + 206 + 137 + + + 169 + 79 + + + + + buttonBox + rejected() + PasswordDialog + reject() + + + 206 + 137 + + + 169 + 79 + + + + + password + textChanged(QString) + PasswordDialog + updateOkButton() + + + 204 + 107 + + + 169 + 78 + + + + + username + textChanged(QString) + PasswordDialog + updateOkButton() + + + 204 + 76 + + + 169 + 78 + + + + + + updateOkButton() + + diff --git a/core/src/PlatformPluginManager.cpp b/core/src/PlatformPluginManager.cpp new file mode 100644 index 0000000..b120c13 --- /dev/null +++ b/core/src/PlatformPluginManager.cpp @@ -0,0 +1,48 @@ +/* + * PlatformPluginManager.cpp - implementation of PlatformPluginManager + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "PluginManager.h" +#include "PlatformPluginManager.h" + + +PlatformPluginManager::PlatformPluginManager( PluginManager& pluginManager, QObject* parent ) : + QObject( parent ), + m_platformPlugin( nullptr ) +{ + for( auto pluginObject : qAsConst( pluginManager.pluginObjects() ) ) + { + auto pluginInterface = qobject_cast( pluginObject ); + auto platformPluginInterface = qobject_cast( pluginObject ); + + if( pluginInterface && platformPluginInterface ) + { + m_platformPlugin = platformPluginInterface; + } + } + + if( m_platformPlugin == nullptr ) + { + qFatal( "PlatformPluginManager: no platform plugin available!" ); + } +} diff --git a/core/src/PluginManager.cpp b/core/src/PluginManager.cpp new file mode 100644 index 0000000..aadf518 --- /dev/null +++ b/core/src/PluginManager.cpp @@ -0,0 +1,215 @@ +/* + * PluginManager.cpp - implementation of the PluginManager class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include "Logger.h" +#include "PluginManager.h" +#include "VeyonConfiguration.h" + + +PluginManager::PluginManager( QObject* parent ) : + QObject( parent ), + m_pluginInterfaces(), + m_pluginObjects(), + m_pluginLoaders(), + m_noDebugMessages( qEnvironmentVariableIsSet( Logger::logLevelEnvironmentVariable() ) ) +{ + initPluginSearchPath(); +} + + + +PluginManager::~PluginManager() +{ + vDebug(); + + for( auto pluginLoader : qAsConst(m_pluginLoaders) ) + { + pluginLoader->unload(); + } + + m_pluginInterfaces.clear(); + m_pluginObjects.clear(); +} + + + +void PluginManager::loadPlatformPlugins() +{ + loadPlugins( QStringLiteral("*-platform") + VeyonCore::sharedLibrarySuffix() ); +} + + + +void PluginManager::loadPlugins() +{ + loadPlugins( QStringLiteral("*") + VeyonCore::sharedLibrarySuffix() ); +} + + + +void PluginManager::upgradePlugins() +{ + auto versions = VeyonCore::config().pluginVersions(); + + for( auto pluginInterface : qAsConst( m_pluginInterfaces ) ) + { + const auto pluginUid = pluginInterface->uid().toString(); + auto previousPluginVersion = QVersionNumber::fromString( versions.value( pluginUid ).toString() ); + if( previousPluginVersion.isNull() ) + { + previousPluginVersion = QVersionNumber( 1, 1 ); + } + const auto currentPluginVersion = pluginInterface->version(); + if( currentPluginVersion > previousPluginVersion ) + { + vDebug() << "Upgrading plugin" << pluginInterface->name() + << "from" << previousPluginVersion.toString() + << "to" << currentPluginVersion.toString(); + pluginInterface->upgrade( previousPluginVersion ); + } + + versions[pluginUid] = currentPluginVersion.toString(); + } + + VeyonCore::config().setPluginVersions( versions ); +} + + + +void PluginManager::registerExtraPluginInterface( QObject* pluginObject ) +{ + auto pluginInterface = qobject_cast( pluginObject ); + if( pluginInterface ) + { + m_pluginInterfaces += pluginInterface; + m_pluginObjects += pluginObject; + } +} + + + +PluginUidList PluginManager::pluginUids() const +{ + PluginUidList pluginUidList; + + pluginUidList.reserve( m_pluginInterfaces.size() ); + + for( auto pluginInterface : qAsConst( m_pluginInterfaces ) ) + { + pluginUidList += pluginInterface->uid(); + } + + std::sort( pluginUidList.begin(), pluginUidList.end() ); + + return pluginUidList; +} + + + +PluginInterface* PluginManager::pluginInterface( Plugin::Uid pluginUid ) +{ + for( auto pluginInterface : qAsConst( m_pluginInterfaces ) ) + { + if( pluginInterface->uid() == pluginUid ) + { + return pluginInterface; + } + } + + return nullptr; +} + + + +QString PluginManager::pluginName( Plugin::Uid pluginUid ) const +{ + for( auto pluginInterface : m_pluginInterfaces ) + { + if( pluginInterface->uid() == pluginUid ) + { + return pluginInterface->name(); + } + } + + return {}; +} + + + +void PluginManager::initPluginSearchPath() +{ + QDir dir( QCoreApplication::applicationDirPath() ); + if( dir.cd( VeyonCore::pluginDir() ) ) + { + const auto pluginSearchPath = dir.absolutePath(); + if( m_noDebugMessages == false ) + { + vDebug() << "Adding plugin search path" << pluginSearchPath; + } + QDir::addSearchPath( QStringLiteral( "plugins" ), pluginSearchPath ); + QCoreApplication::addLibraryPath( pluginSearchPath ); + } +} + + + +void PluginManager::loadPlugins( const QString& nameFilter ) +{ + const auto plugins = QDir( QStringLiteral( "plugins:" ) ).entryInfoList( { nameFilter } ); + for( const auto& fileInfo : plugins ) + { + const auto fileName = fileInfo.fileName(); + + // skip simple shared libraries + if( fileName.startsWith( QLatin1String("lib") ) && + fileName.startsWith( QLatin1String("libveyon") ) == false ) + { + continue; + } + + auto pluginLoader = new QPluginLoader( fileInfo.filePath(), this ); + auto pluginObject = pluginLoader->instance(); + auto pluginInterface = qobject_cast( pluginObject ); + + if( pluginObject && pluginInterface && + m_pluginInterfaces.contains( pluginInterface ) == false ) + { + if( m_noDebugMessages == false ) + { + vDebug() << "discovered plugin" << pluginInterface->name() << "at" << fileInfo.filePath(); + } + m_pluginInterfaces += pluginInterface; // clazy:exclude=reserve-candidates + m_pluginObjects += pluginObject; // clazy:exclude=reserve-candidates + m_pluginLoaders += pluginLoader; // clazy:exclude=reserve-candidates + } + else + { + delete pluginLoader; + } + } +} diff --git a/core/src/PrecompiledHeader.h b/core/src/PrecompiledHeader.h new file mode 100644 index 0000000..3eec61c --- /dev/null +++ b/core/src/PrecompiledHeader.h @@ -0,0 +1,29 @@ +#pragma once + +#ifdef WIN32 +#include +#include +#endif + +#ifdef __cplusplus +#ifdef QT_WIDGETS_LIB +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef QT_NETWORK_LIB +#include +#endif +#endif diff --git a/core/src/ProcessHelper.cpp b/core/src/ProcessHelper.cpp new file mode 100644 index 0000000..1b439c4 --- /dev/null +++ b/core/src/ProcessHelper.cpp @@ -0,0 +1,81 @@ +/* + * ProcessHelper.cpp - implementation of ProcessHelper + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "ProcessHelper.h" + + +ProcessHelper::ProcessHelper( const QString& program, const QStringList& arguments ) : + m_process() +{ + m_process.start( program, arguments ); +} + + + +int ProcessHelper::run() +{ + if( m_process.waitForStarted() ) + { + m_process.waitForFinished(); + return m_process.exitCode(); + } + + return -1; +} + + + +QByteArray ProcessHelper::runAndReadAll() +{ + if( m_process.waitForStarted() ) + { + m_process.waitForFinished(); + return m_process.readAll(); + } + + return QByteArray(); +} + + + +bool ProcessHelper::waitForProcess( QProcess* process, int timeout, int sleepInterval ) +{ + QElapsedTimer timeoutTimer; + timeoutTimer.start(); + + while( process->state() != QProcess::NotRunning ) + { + if( timeoutTimer.elapsed() >= timeout ) + { + return false; + } + + QThread::msleep( static_cast( sleepInterval ) ); + } + + return true; +} diff --git a/core/src/ProgressWidget.cpp b/core/src/ProgressWidget.cpp new file mode 100644 index 0000000..c505b9c --- /dev/null +++ b/core/src/ProgressWidget.cpp @@ -0,0 +1,80 @@ +/* + * ProgressWidget.cpp - widget with animated progress indicator + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "ProgressWidget.h" + +#include +#include + +// clazy:excludeall=detaching-member + +ProgressWidget::ProgressWidget( const QString& text, const QString& animationPixmapBase, int frames, QWidget* parent ) : + QWidget( parent ), + m_text( text ), + m_frames( frames ), + m_curFrame( 0 ) +{ + m_pixmaps.reserve( m_frames ); + + for( int i = 0; i < m_frames; ++i ) + { + m_pixmaps.push_back( QPixmap( animationPixmapBase.arg( QString::number( i+1 ) ) ) ); + } + + QFont f = font(); + f.setPointSize( 12 ); + setFont( f ); + + setFixedSize( 30 + m_pixmaps[0].width() + fontMetrics().boundingRect( m_text ).width(), + m_pixmaps[0].height() * 5 / 4 ); + + auto t = new QTimer( this ); + connect( t, &QTimer::timeout, this, &ProgressWidget::nextFrame ); + t->start( 150 ); +} + + + +void ProgressWidget::nextFrame() +{ + m_curFrame = ( m_curFrame+1 ) % m_frames; + + update(); +} + + + +void ProgressWidget::paintEvent( QPaintEvent* event ) +{ + Q_UNUSED(event); + + QPainter p( this ); + p.setRenderHint( QPainter::Antialiasing ); + p.setPen( QColor( 0x55, 0x55, 0x55 ) ); + + p.setBrush( Qt::white ); + p.drawRect( 0, 0, width() - 1, height() - 1 ); + p.drawPixmap( 6, ( height() - m_pixmaps[m_curFrame].height() ) / 2 - 1, m_pixmaps[m_curFrame] ); + + p.drawText( 14 + m_pixmaps[m_curFrame].width(), 25, m_text ); +} diff --git a/core/src/QtCompat.cpp b/core/src/QtCompat.cpp new file mode 100644 index 0000000..175b39b --- /dev/null +++ b/core/src/QtCompat.cpp @@ -0,0 +1,166 @@ +/* + * QtCompat.cpp - functions and templates for compatibility with older Qt versions + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "QtCompat.h" + +#if QT_VERSION < 0x050600 + +// taken from qtbase/src/corelib/tools/qversionnumber.cpp + +QVector QVersionNumber::segments() const +{ + if (m_segments.isUsingPointer()) + return *m_segments.pointer_segments; + + QVector result; + result.resize(segmentCount()); + for (int i = 0; i < segmentCount(); ++i) + result[i] = segmentAt(i); + return result; +} + +QVersionNumber QVersionNumber::normalized() const +{ + int i; + for (i = m_segments.size(); i; --i) + if (m_segments.at(i - 1) != 0) + break; + + QVersionNumber result(*this); + result.m_segments.resize(i); + return result; +} + +bool QVersionNumber::isPrefixOf(const QVersionNumber &other) const Q_DECL_NOTHROW +{ + if (segmentCount() > other.segmentCount()) + return false; + for (int i = 0; i < segmentCount(); ++i) { + if (segmentAt(i) != other.segmentAt(i)) + return false; + } + return true; +} + +int QVersionNumber::compare(const QVersionNumber &v1, const QVersionNumber &v2) Q_DECL_NOTHROW +{ + int commonlen; + + if (Q_LIKELY(!v1.m_segments.isUsingPointer() && !v2.m_segments.isUsingPointer())) { + // we can't use memcmp because it interprets the data as unsigned bytes + const qint8 *ptr1 = v1.m_segments.inline_segments + InlineSegmentStartIdx; + const qint8 *ptr2 = v2.m_segments.inline_segments + InlineSegmentStartIdx; + commonlen = qMin(v1.m_segments.size(), + v2.m_segments.size()); + for (int i = 0; i < commonlen; ++i) + if (int x = ptr1[i] - ptr2[i]) + return x; + } else { + commonlen = qMin(v1.segmentCount(), v2.segmentCount()); + for (int i = 0; i < commonlen; ++i) { + if (v1.segmentAt(i) != v2.segmentAt(i)) + return v1.segmentAt(i) - v2.segmentAt(i); + } + } + + // ran out of segments in v1 and/or v2 and need to check the first trailing + // segment to finish the compare + if (v1.segmentCount() > commonlen) { + // v1 is longer + if (v1.segmentAt(commonlen) != 0) + return v1.segmentAt(commonlen); + else + return 1; + } else if (v2.segmentCount() > commonlen) { + // v2 is longer + if (v2.segmentAt(commonlen) != 0) + return -v2.segmentAt(commonlen); + else + return -1; + } + + // the two version numbers are the same + return 0; +} + +QVersionNumber QVersionNumber::commonPrefix(const QVersionNumber &v1, + const QVersionNumber &v2) +{ + int commonlen = qMin(v1.segmentCount(), v2.segmentCount()); + int i; + for (i = 0; i < commonlen; ++i) { + if (v1.segmentAt(i) != v2.segmentAt(i)) + break; + } + + if (i == 0) + return QVersionNumber(); + + // try to use the one with inline segments, if there's one + QVersionNumber result(!v1.m_segments.isUsingPointer() ? v1 : v2); + result.m_segments.resize(i); + return result; +} + +QString QVersionNumber::toString() const +{ + QString version; + version.reserve(qMax(segmentCount() * 2 - 1, 0)); + bool first = true; + for (int i = 0; i < segmentCount(); ++i) { + if (!first) + version += QLatin1Char('.'); + version += QString::number(segmentAt(i)); + first = false; + } + return version; +} + +QVersionNumber QVersionNumber::fromString(const QString &string, int *suffixIndex) +{ + QVector seg; + + const QByteArray cString(string.toLatin1()); + + const char *start = cString.constData(); + const char *end = start; + const char *lastGoodEnd = start; + const char *endOfString = cString.constData() + cString.size(); + + do { + const qulonglong value = strtoull(start, (char **) &end, 10); + if (value > qulonglong(std::numeric_limits::max())) + break; + seg.append(int(value)); + start = end + 1; + lastGoodEnd = end; + } while (start < endOfString && (end < endOfString && *end == '.')); + + if (suffixIndex) + *suffixIndex = int(lastGoodEnd - cString.constData()); + + return QVersionNumber(qMove(seg)); +} + +#endif diff --git a/core/src/Screenshot.cpp b/core/src/Screenshot.cpp new file mode 100644 index 0000000..d426d9c --- /dev/null +++ b/core/src/Screenshot.cpp @@ -0,0 +1,223 @@ +/* + * Screenshot.cpp - class representing a screenshot + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Screenshot.h" +#include "VeyonConfiguration.h" +#include "Computer.h" +#include "ComputerControlInterface.h" +#include "Filesystem.h" +#include "PlatformFilesystemFunctions.h" + +Screenshot::Screenshot( const QString &fileName, QObject* parent ) : + QObject( parent ), + m_fileName( fileName ), + m_image() +{ + if( !m_fileName.isEmpty() && QFileInfo( m_fileName ).isFile() ) + { + m_image.load( m_fileName ); + } +} + + + +void Screenshot::take( const ComputerControlInterface::Pointer& computerControlInterface ) +{ + auto userLogin = computerControlInterface->userLoginName(); + if( userLogin.isEmpty() ) + { + userLogin = tr( "unknown" ); + } + + const auto dir = VeyonCore::filesystem().screenshotDirectoryPath(); + + if( VeyonCore::filesystem().ensurePathExists( dir ) == false ) + { + const auto msg = tr( "Could not take a screenshot as directory %1 doesn't exist and couldn't be created." ).arg( dir ); + vCritical() << msg.toUtf8().constData(); + if( qobject_cast( QCoreApplication::instance() ) ) + { + QMessageBox::critical( nullptr, tr( "Screenshot" ), msg ); + } + + return; + } + + // construct filename + m_fileName = dir + QDir::separator() + constructFileName( userLogin, computerControlInterface->computer().hostAddress() ); + + QFile outputFile( m_fileName ); + if( VeyonCore::platform().filesystemFunctions().openFileSafely( + &outputFile, + QFile::WriteOnly | QFile::Truncate, + QFile::ReadOwner | QFile::WriteOwner ) == false ) + { + const auto msg = tr( "Could not open screenshot file %1 for writing." ).arg( m_fileName ); + vCritical() << msg.toUtf8().constData(); + if( qobject_cast( QCoreApplication::instance() ) ) + { + QMessageBox::critical( nullptr, tr( "Screenshot" ), msg ); + } + + return; + } + + // construct caption + auto user = userLogin; + if( computerControlInterface->userFullName().isEmpty() == false ) + { + user = QStringLiteral( "%1 (%2)" ).arg( userLogin, computerControlInterface->userFullName() ); + } + + const auto host = computerControlInterface->computer().hostAddress(); + const auto date = QDate::currentDate().toString( Qt::ISODate ); + const auto time = QTime::currentTime().toString( Qt::ISODate ); + + const auto caption = QStringLiteral( "%1@%2 %3 %4" ).arg( user, host, date, time ); + + m_image = computerControlInterface->screen(); + + QPixmap icon( QStringLiteral( ":/core/icon16.png" ) ); + + QPainter painter( &m_image ); + + auto font = painter.font(); + font.setPointSize( 14 ); + font.setBold( true ); + painter.setFont( font ); + + const QFontMetrics fontMetrics( painter.font() ); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + const auto captionWidth = fontMetrics.horizontalAdvance( caption ); + const auto captionHeight = fontMetrics.boundingRect( caption ).height(); +#else + const auto boundingRect = fontMetrics.boundingRect( caption ); + const auto captionWidth = boundingRect.width(); + const auto captionHeight = boundingRect.height(); +#endif + + const auto MARGIN = 14; + const auto PADDING = 7; + const QRect rect{ MARGIN, + m_image.height() - MARGIN - 2 * PADDING - captionHeight, + 4 * PADDING + captionWidth + icon.width(), + 2 * PADDING + captionHeight }; + const auto iconX = rect.x() + PADDING + 1; + const auto iconY = rect.y() + ( rect.height() - icon.height() ) / 2; + const auto textX = iconX + icon.width() + PADDING; + const auto textY = rect.y() + PADDING + fontMetrics.ascent(); + + painter.fillRect( rect, QColor( 255, 255, 255, 160 ) ); + painter.drawPixmap( iconX, iconY, icon ); + painter.drawText( textX, textY, caption ); + + m_image.setText( metaDataKey( MetaData::User ), user ); + m_image.setText( metaDataKey( MetaData::Host ), host ); + m_image.setText( metaDataKey( MetaData::Date ), date ); + m_image.setText( metaDataKey( MetaData::Time ), time ); + + m_image.save( &outputFile, "PNG", 50 ); + + Q_EMIT VeyonCore::filesystem().screenshotDirectoryModified(); +} + + + +QString Screenshot::constructFileName( const QString& user, const QString& hostAddress, + const QDate& date, const QTime& time ) +{ + const auto userSimplified = VeyonCore::stripDomain( user ).toLower().remove( + QRegularExpression( QStringLiteral("[^a-z0-9.]") ) ); + + return QStringLiteral( "%1_%2_%3_%4.png" ).arg( userSimplified, + hostAddress, + date.toString( Qt::ISODate ), + time.toString( Qt::ISODate ) ). + replace( QLatin1Char(':'), QLatin1Char('-') ); +} + + + +QString Screenshot::user() const +{ + return property( metaDataKey( MetaData::User ), 0 ); +} + + + +QString Screenshot::host() const +{ + return property( metaDataKey( MetaData::Host ), 1 ); +} + + + +QString Screenshot::date() const +{ + return QDate::fromString( property( metaDataKey( MetaData::Date ), 2 ), + Qt::ISODate ).toString( Qt::LocalDate ); +} + + + +QString Screenshot::time() const +{ + return property( metaDataKey( MetaData::Time ), 3 ).section( QLatin1Char('.'), 0, 0 ).replace( QLatin1Char('-'), QLatin1Char(':') ); +} + + + +QString Screenshot::metaDataKey( MetaData key ) +{ + return QLatin1String( QMetaEnum::fromType().valueToKey( static_cast( key ) ) ); +} + + + +QString Screenshot::property( const QString& key, int section ) const +{ + const auto embeddedProperty = m_image.text( key ); + if( embeddedProperty.isEmpty() ) + { + return fileNameSection( section ); + } + + return embeddedProperty; +} + + + +QString Screenshot::fileNameSection( int n ) const +{ + return QFileInfo( fileName() ).fileName().section( QLatin1Char('_'), n, n ); +} diff --git a/core/src/ServiceControl.cpp b/core/src/ServiceControl.cpp new file mode 100644 index 0000000..00aeb83 --- /dev/null +++ b/core/src/ServiceControl.cpp @@ -0,0 +1,147 @@ +/* + * ServiceControl.cpp - class for controlling veyon service + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "PlatformServiceFunctions.h" +#include "ServiceControl.h" + + +ServiceControl::ServiceControl( const QString& name, + const QString& filePath, + const QString& displayName, + QWidget* parent ) : + QObject( parent ), + m_name( name ), + m_filePath( filePath ), + m_displayName( displayName ), + m_parent( parent ) +{ +} + + + +bool ServiceControl::isServiceRegistered() +{ + return VeyonCore::platform().serviceFunctions().isRegistered( m_name ); +} + + + +bool ServiceControl::isServiceRunning() +{ + return VeyonCore::platform().serviceFunctions().isRunning( m_name ); +} + + + +void ServiceControl::startService() +{ + serviceControl( tr( "Starting service %1" ).arg( m_name ), + QtConcurrent::run( [=]() { VeyonCore::platform().serviceFunctions().start( m_name ); } ) ); +} + + + + +void ServiceControl::stopService() +{ + serviceControl( tr( "Stopping service %1" ).arg( m_name ), + QtConcurrent::run( [=]() { VeyonCore::platform().serviceFunctions().stop( m_name ); } ) ); +} + + + +void ServiceControl::registerService() +{ + serviceControl( tr( "Registering service %1" ).arg( m_name ), + QtConcurrent::run( [=]() { VeyonCore::platform().serviceFunctions().install( m_name, + m_filePath, + PlatformServiceFunctions::StartMode::Auto, + m_displayName ); } ) ); +} + + + +void ServiceControl::unregisterService() +{ + serviceControl( tr( "Unregistering service %1" ).arg( m_name ), + QtConcurrent::run( [=]() { VeyonCore::platform().serviceFunctions().uninstall( m_name ); } ) ); + +} + + + +void ServiceControl::serviceControl( const QString& title, const Operation& operation ) +{ + if( m_parent ) + { + graphicalFeedback( title, operation ); + } + else + { + textFeedback( title, operation ); + } +} + + + +void ServiceControl::graphicalFeedback( const QString& title, const Operation& operation ) +{ + QProgressDialog pd( title, {}, 0, 0, m_parent ); + pd.setWindowTitle( tr( "Service control" ) ); + + auto b = new QProgressBar( &pd ); + b->setMaximum( 100 ); + b->setTextVisible( false ); + pd.setBar( b ); + b->show(); + pd.setWindowModality( Qt::WindowModal ); + pd.show(); + + int j = 0; + while( operation.isFinished() == false ) + { + QCoreApplication::processEvents(); + b->setValue( ++j % 100 ); + QThread::msleep( 10 ); + } +} + + + +void ServiceControl::textFeedback( const QString& title, const Operation& operation ) +{ + printf( "%s", qUtf8Printable( title ) ); + + while( operation.isFinished() == false ) + { + QCoreApplication::processEvents(); + QThread::msleep( 200 ); + printf( "." ); + } +} diff --git a/core/src/SystemTrayIcon.cpp b/core/src/SystemTrayIcon.cpp new file mode 100644 index 0000000..729041d --- /dev/null +++ b/core/src/SystemTrayIcon.cpp @@ -0,0 +1,141 @@ +/* + * SystemTrayIcon.cpp - implementation of SystemTrayIcon class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "SystemTrayIcon.h" +#include "FeatureWorkerManager.h" +#include "VeyonCore.h" +#include "VeyonConfiguration.h" +#include "VeyonServerInterface.h" + + +SystemTrayIcon::SystemTrayIcon( QObject* parent ) : + QObject( parent ), + m_systemTrayIconFeature( Feature( QLatin1String( staticMetaObject.className() ), + Feature::Session | Feature::Service | Feature::Worker | Feature::Builtin, + Feature::Uid( "8e997d84-ebb9-430f-8f72-d45d9821963d" ), + Feature::Uid(), + tr( "System tray icon"), {}, {} ) ), + m_features( { m_systemTrayIconFeature } ), + m_systemTrayIcon( nullptr ) +{ +} + + + +void SystemTrayIcon::setToolTip( const QString& toolTipText, + FeatureWorkerManager& featureWorkerManager ) +{ + FeatureMessage featureMessage( m_systemTrayIconFeature.uid(), SetToolTipCommand ); + featureMessage.addArgument( Argument::ToolTipText, toolTipText ); + + featureWorkerManager.sendMessageToUnmanagedSessionWorker( featureMessage ); +} + + + +void SystemTrayIcon::showMessage( const QString& messageTitle, + const QString& messageText, + FeatureWorkerManager& featureWorkerManager ) +{ + FeatureMessage featureMessage( m_systemTrayIconFeature.uid(), ShowMessageCommand ); + featureMessage.addArgument( Argument::MessageTitle, messageTitle ); + featureMessage.addArgument( Argument::MessageText, messageText ); + + featureWorkerManager.sendMessageToUnmanagedSessionWorker( featureMessage ); +} + + + +bool SystemTrayIcon::handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) +{ + Q_UNUSED(messageContext) + + if( m_systemTrayIconFeature.uid() == message.featureUid() ) + { + // forward message to worker + server.featureWorkerManager().sendMessageToUnmanagedSessionWorker( message ); + return true; + } + + return false; +} + + + +bool SystemTrayIcon::handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) +{ + Q_UNUSED(worker) + + if( message.featureUid() != m_systemTrayIconFeature.uid() ) + { + return false; + } + + if( m_systemTrayIcon == nullptr && VeyonCore::config().isTrayIconHidden() == false ) + { + m_systemTrayIcon = new QSystemTrayIcon( this ); + + QIcon icon( QStringLiteral( ":/core/icon16.png" ) ); + icon.addFile( QStringLiteral( ":/core/icon22.png" ) ); + icon.addFile( QStringLiteral( ":/core/icon32.png" ) ); + icon.addFile( QStringLiteral( ":/core/icon64.png" ) ); + + m_systemTrayIcon->setIcon( icon ); + m_systemTrayIcon->show(); + } + + switch( message.command() ) + { + case SetToolTipCommand: + if( m_systemTrayIcon ) + { + m_systemTrayIcon->setToolTip( message.argument( Argument::ToolTipText ).toString() ); + } + return true; + + case ShowMessageCommand: + if( m_systemTrayIcon ) + { + m_systemTrayIcon->showMessage( message.argument( Argument::MessageTitle ).toString(), + message.argument( Argument::MessageText ).toString() ); + } + else + { + QMessageBox::information( nullptr, + message.argument( Argument::MessageTitle ).toString(), + message.argument( Argument::MessageText ).toString() ); + } + return true; + + default: + break; + } + + return false; +} diff --git a/core/src/ToolButton.cpp b/core/src/ToolButton.cpp new file mode 100644 index 0000000..bb15b97 --- /dev/null +++ b/core/src/ToolButton.cpp @@ -0,0 +1,409 @@ +/* + * ToolButton.cpp - implementation of Veyon-tool-button + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ToolButton.h" + +bool ToolButton::s_toolTipsDisabled = false; +bool ToolButton::s_iconOnlyMode = false; + + +ToolButton::ToolButton( const QIcon& icon, + const QString& label, + const QString& altLabel, + const QString& description, + const QKeySequence& shortcut ) : + m_pixelRatio( 1 ), + m_icon( icon ), + m_pixmap(), + m_mouseOver( false ), + m_label( label ), + m_altLabel( altLabel ), + m_descr( description ) +{ + setShortcut( shortcut ); + + setAttribute( Qt::WA_NoSystemBackground, true ); + + updateSize(); +} + + + +void ToolButton::setIconOnlyMode( QWidget* mainWindow, bool enabled ) +{ + s_iconOnlyMode = enabled; + const auto toolButtons = mainWindow->findChildren(); + for( auto toolButton : toolButtons ) + { + toolButton->updateSize(); + } +} + + + +void ToolButton::addTo( QToolBar* toolBar ) +{ + auto action = toolBar->addWidget( this ); + action->setText( m_label ); +} + + + + +void ToolButton::enterEvent( QEvent* event ) +{ + m_mouseOver = true; + if( !s_toolTipsDisabled && !m_label.isEmpty() && !m_descr.isEmpty() ) + { + auto toolTipPos = mapToGlobal( QPoint( 0, 0 ) ); +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + const auto screenRect = QGuiApplication::screenAt( toolTipPos )->availableGeometry(); +#else + int screenNumber = QApplication::desktop()->isVirtualDesktop() ? + QApplication::desktop()->screenNumber( toolTipPos ) : + QApplication::desktop()->screenNumber( this ); + const auto screenRect = QApplication::desktop()->screenGeometry( screenNumber ); +#endif + + auto toolTip = new ToolButtonTip( m_icon.pixmap( 128, 128 ), m_label, m_descr, nullptr, this ); + connect( this, &ToolButton::mouseLeftButton, toolTip, &QWidget::close ); + + if( toolTipPos.x() + toolTip->width() > screenRect.x() + screenRect.width() ) + { + toolTipPos.rx() -= 4; + } + if( toolTipPos.y() + toolTip->height() > screenRect.y() + screenRect.height() ) + { + toolTipPos.ry() -= 30 + toolTip->height(); + } + if( toolTipPos.y() < screenRect.y() ) + { + toolTipPos.setY( screenRect.y() ); + } + if( toolTipPos.x() + toolTip->width() > screenRect.x() + screenRect.width() ) + { + toolTipPos.setX( screenRect.x() + screenRect.width() - toolTip->width() ); + } + if( toolTipPos.x() < screenRect.x() ) + { + toolTipPos.setX( screenRect.x() ); + } + if( toolTipPos.y() + toolTip->height() > screenRect.y() + screenRect.height() ) + { + toolTipPos.setY( screenRect.y() + screenRect.height() - toolTip->height() ); + } + + toolTip->move( toolTipPos += QPoint( -4, height() ) ); + toolTip->show(); + } + + QToolButton::enterEvent( event ); +} + + + + +void ToolButton::leaveEvent( QEvent* event ) +{ + if( checkForLeaveEvent() ) + { + QToolButton::leaveEvent( event ); + } +} + + + + +void ToolButton::mousePressEvent( QMouseEvent* event ) +{ + Q_EMIT mouseLeftButton(); + QToolButton::mousePressEvent( event ); +} + + + + +void ToolButton::paintEvent( QPaintEvent* ) +{ + const bool active = isDown() || isChecked(); + + QPainter painter(this); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(Qt::NoPen); + + QLinearGradient outlinebrush(0, 0, 0, height()); + QLinearGradient brush(0, 0, 0, height()); + + brush.setSpread(QLinearGradient::PadSpread); + QColor highlight(255, 255, 255, 70); + QColor shadow(0, 0, 0, 70); + QColor sunken(220, 220, 220, 30); + QColor normal1(255, 255, 245, 60); + QColor normal2(255, 255, 235, 10); + + if( active ) + { + outlinebrush.setColorAt( 0.0, shadow ); + outlinebrush.setColorAt( 1.0, highlight ); + brush.setColorAt( 0.0, sunken ); + painter.setPen(Qt::NoPen); + } + else + { + outlinebrush.setColorAt( 1.0, shadow ); + outlinebrush.setColorAt( 0.0, highlight ); + brush.setColorAt( 0.0, normal1 ); + if( m_mouseOver == false ) + { + brush.setColorAt( 1.0, normal2 ); + } + painter.setPen(QPen(outlinebrush, 1)); + } + + painter.setBrush(brush); + + painter.drawRoundedRect( rect(), roundness(), roundness() ); + + const int delta = active ? 1 : 0; + QPoint pixmapPos( ( width() - m_pixmap.width() ) / 2 + delta, margin() / 2 + delta ); + if( s_iconOnlyMode ) + { + pixmapPos.setY( ( height() - m_pixmap.height() ) / 2 - 1 + delta ); + } + painter.drawPixmap( pixmapPos, m_pixmap ); + + if( s_iconOnlyMode == false ) + { + const auto label = ( isChecked() && m_altLabel.isEmpty() == false ) ? m_altLabel : m_label; + const int labelX = 1 + ( width() - painter.fontMetrics().boundingRect( label ).width() ) / 2; + const int deltaNormal = delta - 1; + const int deltaShadow = deltaNormal + 1; + + painter.setPen( Qt::black ); + painter.drawText( labelX + deltaShadow, height() - margin() / 2 + deltaShadow, label ); + + painter.setPen( Qt::white ); + painter.drawText( labelX + deltaNormal, height() - margin() / 2 + deltaNormal, label ); + } +} + + + + +bool ToolButton::checkForLeaveEvent() +{ + if( QRect( mapToGlobal( QPoint( 0, 0 ) ), size() ). + contains( QCursor::pos() ) ) + { + QTimer::singleShot( 20, this, &ToolButton::checkForLeaveEvent ); + } + else + { + Q_EMIT mouseLeftButton(); + m_mouseOver = false; + + return true; + } + return false; +} + + + + +void ToolButton::updateSize() +{ + auto f = QApplication::font(); + f.setPointSizeF( qMax( 7.5, f.pointSizeF() * 0.9 ) ); + setFont( f ); + + m_pixelRatio = fontInfo().pixelSize() / fontInfo().pointSizeF(); + + const auto metrics = fontMetrics(); + + m_pixmap = m_icon.pixmap( static_cast( iconSize() ) ); + + if( s_iconOnlyMode ) + { + setFixedSize( margin() + iconSize(), margin() + iconSize() ); + } + else + { + const int textWidth = ( qMax( metrics.boundingRect( m_label ).width(), metrics.boundingRect( m_altLabel ).width() ) / stepSize() + 1 ) * stepSize(); + const int width = qMax( textWidth, iconSize() * 3 / 2 ); + const int height = iconSize() + fontInfo().pixelSize(); + setFixedSize( width + margin(), height + margin() ); + } +} + + + + + + + +ToolButtonTip::ToolButtonTip( const QIcon& icon, const QString &title, + const QString & _description, + QWidget * _parent, QWidget * _tool_btn ) : + QWidget( _parent, Qt::ToolTip ), + m_pixelRatio( fontInfo().pixelSize() / fontInfo().pointSizeF() ), + m_pixmap( icon.pixmap( static_cast( 64 * m_pixelRatio ) ) ), + m_title( title ), + m_description( _description ), + m_toolButton( _tool_btn ) +{ + setAttribute( Qt::WA_DeleteOnClose, true ); + setAttribute( Qt::WA_NoSystemBackground, true ); + + QTimer::singleShot( 0, this, [this]() { resize( sizeHint() ); } ); + + updateMask(); +} + + + + +QSize ToolButtonTip::sizeHint() const +{ + auto f = font(); + f.setBold( true ); + + const auto titleWidth = QFontMetrics( f ).boundingRect( m_title ).width(); + const auto descriptionRect = fontMetrics().boundingRect( QRect( 0, 0, 250, 100 ), Qt::TextWordWrap, m_description ); + + return { margin() + m_pixmap.width() + margin() + qMax( titleWidth, descriptionRect.width() ) + margin(), + margin() + qMax( m_pixmap.height(), fontMetrics().height() + margin() + descriptionRect.height() ) + margin() }; +} + + + + +void ToolButtonTip::paintEvent( QPaintEvent* ) +{ + QPainter p( this ); + p.drawImage( 0, 0, m_bg ); +} + + + + +void ToolButtonTip::resizeEvent( QResizeEvent * _re ) +{ + const QColor color_frame = QColor( 48, 48, 48 ); + m_bg = QImage( size(), QImage::Format_ARGB32 ); + m_bg.fill( color_frame.rgba() ); + QPainter p( &m_bg ); + p.setRenderHint( QPainter::Antialiasing ); + QPen pen( color_frame ); + pen.setWidthF( 1.5 ); + p.setPen( pen ); + QLinearGradient grad( 0, 0, 0, height() ); + const QColor color_top = palette().color( QPalette::Active, + QPalette::Window ).lighter( 120 ); + grad.setColorAt( 0, color_top ); + grad.setColorAt( 1, palette().color( QPalette::Active, + QPalette::Window ). + lighter( 80 ) ); + p.setBrush( grad ); + p.drawRoundedRect( 0, 0, width() - 1, height() - 1, ROUNDED / width(), ROUNDED / height() ); + + if( m_toolButton ) + { + QPoint pt = m_toolButton->mapToGlobal( QPoint( 0, 0 ) ); + p.setPen( color_top ); + p.setBrush( color_top ); + p.setRenderHint( QPainter::Antialiasing, false ); + p.drawLine( pt.x() - x(), 0, + pt.x() + m_toolButton->width() - x() - 2, 0 ); + const int dx = pt.x() - x(); + p.setRenderHint( QPainter::Antialiasing, true ); + if( dx < 10 && dx >= 0 ) + { + p.setPen( pen ); + p.drawImage( dx+1, 0, m_bg.copy( 20, 0, 10-dx, 10 ) ); + p.drawImage( dx, 0, m_bg.copy( 0, 10, 1, 10-dx*2 ) ); + } + } + p.setPen( Qt::black ); + + p.drawPixmap( margin(), margin(), m_pixmap ); + QFont f = p.font(); + f.setBold( true ); + p.setFont( f ); + const auto titleX = margin() + m_pixmap.width() + margin(); + const auto titleY = margin() + fontMetrics().height() - 2; + p.drawText( titleX, titleY, m_title ); + + f.setBold( false ); + p.setFont( f ); + p.drawText( QRect( titleX, + titleY + margin(), + width() - margin() - titleX, + height() - margin() - titleY ), + Qt::TextWordWrap, m_description ); + + updateMask(); + QWidget::resizeEvent( _re ); +} + + + + +void ToolButtonTip::updateMask() +{ + // as this widget has not a rectangular shape AND is a top + // level widget (which doesn't allow painting only particular + // regions), we have to set a mask for it + QBitmap b( size() ); + b.clear(); + + QPainter p( &b ); + p.setBrush( Qt::color1 ); + p.setPen( Qt::color1 ); + p.drawRoundedRect( 0, 0, width() - 1, height() - 1, ROUNDED / width(), ROUNDED / height() ); + + if( m_toolButton ) + { + QPoint pt = m_toolButton->mapToGlobal( QPoint( 0, 0 ) ); + const int dx = pt.x()-x(); + if( dx < 10 && dx >= 0 ) + { + p.fillRect( dx, 0, 10, 10, Qt::color1 ); + } + } + + setMask( b ); +} diff --git a/core/src/TranslationLoader.cpp b/core/src/TranslationLoader.cpp new file mode 100644 index 0000000..2ae7829 --- /dev/null +++ b/core/src/TranslationLoader.cpp @@ -0,0 +1,76 @@ +/* + * TranslationLoader.cpp - implementation of TranslationLoader class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "TranslationLoader.h" +#include "VeyonConfiguration.h" +#include "VeyonCore.h" + + +TranslationLoader::TranslationLoader( const QString& resourceName ) +{ + load( resourceName ); +} + + + +QLocale TranslationLoader::load( const QString& resourceName ) +{ + QLocale configuredLocale( QLocale::C ); + + QRegExp localeRegEx( QStringLiteral( "[^(]*\\(([^)]*)\\)") ); + if( localeRegEx.indexIn( VeyonCore::config().uiLanguage() ) == 0 ) + { + configuredLocale = QLocale( localeRegEx.cap( 1 ) ); + } + + if( configuredLocale.language() != QLocale::English && + VeyonCore::instance()->findChild( resourceName ) == nullptr ) + { + auto translator = new QTranslator( VeyonCore::instance() ); + translator->setObjectName( resourceName ); + + if( configuredLocale == QLocale::C || + translator->load( QStringLiteral( "%1_%2.qm" ).arg( resourceName, configuredLocale.name() ), + VeyonCore::translationsDirectory() ) == false ) + { + configuredLocale = QLocale::system(); // Flawfinder: ignore + + if( translator->load( QStringLiteral( "%1_%2.qm" ).arg( resourceName, configuredLocale.name() ), + VeyonCore::translationsDirectory() ) == false ) + { + translator->load( QStringLiteral( "%1_%2.qm" ).arg( resourceName, configuredLocale.language() ), + VeyonCore::translationsDirectory() ); + } + } + + QLocale::setDefault( configuredLocale ); + + QCoreApplication::installTranslator( translator ); + } + + return configuredLocale; +} diff --git a/core/src/UserGroupsBackendManager.cpp b/core/src/UserGroupsBackendManager.cpp new file mode 100644 index 0000000..06d5999 --- /dev/null +++ b/core/src/UserGroupsBackendManager.cpp @@ -0,0 +1,96 @@ +/* + * UserGroupsBackendManager.cpp - implementation of UserGroupsBackendManager + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "VeyonConfiguration.h" +#include "PluginManager.h" +#include "UserGroupsBackendManager.h" + + +UserGroupsBackendManager::UserGroupsBackendManager( QObject* parent ) : + QObject( parent ), + m_backends(), + m_defaultBackend( nullptr ), + m_accessControlBackend( nullptr ) +{ + for( auto pluginObject : qAsConst( VeyonCore::pluginManager().pluginObjects() ) ) + { + auto pluginInterface = qobject_cast( pluginObject ); + auto userGroupsBackendInterface = qobject_cast( pluginObject ); + + if( pluginInterface && userGroupsBackendInterface ) + { + m_backends[pluginInterface->uid()] = userGroupsBackendInterface; + + if( pluginInterface->flags().testFlag( Plugin::ProvidesDefaultImplementation ) ) + { + m_defaultBackend = userGroupsBackendInterface; + } + } + } + + if( m_defaultBackend == nullptr ) + { + vCritical() << "no default plugin available!"; + } + + reloadConfiguration(); +} + + + +QMap UserGroupsBackendManager::availableBackends() +{ + QMap items; + + for( auto it = m_backends.constBegin(), end = m_backends.constEnd(); it != end; ++it ) + { + items[it.key()] = it.value()->userGroupsBackendName(); + } + + return items; +} + + + +UserGroupsBackendInterface* UserGroupsBackendManager::accessControlBackend() +{ + if( m_accessControlBackend == nullptr ) + { + reloadConfiguration(); + } + + return m_accessControlBackend; +} + + + +void UserGroupsBackendManager::reloadConfiguration() +{ + m_accessControlBackend = m_backends.value( VeyonCore::config().accessControlUserGroupsBackend() ); + + if( m_accessControlBackend == nullptr ) + { + m_accessControlBackend = m_defaultBackend; + } +} diff --git a/core/src/VariantArrayMessage.cpp b/core/src/VariantArrayMessage.cpp new file mode 100644 index 0000000..9f1ecb1 --- /dev/null +++ b/core/src/VariantArrayMessage.cpp @@ -0,0 +1,115 @@ +/* + * VariantArrayMessage.cpp - class for sending/receiving a variant array as message + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "VeyonCore.h" + +#include "VariantArrayMessage.h" +#include "VariantStream.h" + + +VariantArrayMessage::VariantArrayMessage( QIODevice* ioDevice ) : + m_buffer(), + m_stream( &m_buffer ), + m_ioDevice( ioDevice ) +{ + Q_ASSERT( m_ioDevice != nullptr ); + + m_buffer.open( QBuffer::ReadWrite ); // Flawfinder: ignore +} + + + +bool VariantArrayMessage::send() +{ + const auto messageSize = qToBigEndian( static_cast( m_buffer.size() ) ); + m_ioDevice->write( reinterpret_cast( &messageSize ), sizeof(messageSize) ); + m_ioDevice->write( m_buffer.data() ); + + return true; +} + + + +bool VariantArrayMessage::isReadyForReceive() +{ + MessageSize messageSize; + + if( m_ioDevice->peek( reinterpret_cast( &messageSize ), sizeof(messageSize) ) == sizeof(messageSize) ) + { + messageSize = qFromBigEndian(messageSize); + + return m_ioDevice->bytesAvailable() >= static_cast( sizeof(messageSize) + messageSize ); + } + + return false; +} + + + +bool VariantArrayMessage::receive() +{ + MessageSize messageSize; + + if( m_ioDevice->read( reinterpret_cast( &messageSize ), sizeof(messageSize) ) != sizeof(messageSize) ) // Flawfinder: ignore + { + vWarning() << "could not read message size!"; + return false; + } + + messageSize = qFromBigEndian(messageSize); + if( messageSize > MaxMessageSize ) + { + vCritical() << "invalid message size" << messageSize; + return false; + } + + const auto data = m_ioDevice->read( messageSize ); // Flawfinder: ignore + if( data.size() != static_cast( messageSize ) ) + { + vWarning() << "could not read message data!"; + return false; + } + + m_buffer.close(); + m_buffer.setData( data ); + m_buffer.open( QBuffer::ReadOnly ); // Flawfinder: ignore + + return true; +} + + + +QVariant VariantArrayMessage::read() // Flawfinder: ignore +{ + return m_stream.read(); // Flawfinder: ignore +} + + + +VariantArrayMessage& VariantArrayMessage::write( const QVariant& v ) +{ + m_stream.write( v ); + + return *this; +} diff --git a/core/src/VariantStream.cpp b/core/src/VariantStream.cpp new file mode 100644 index 0000000..4436b63 --- /dev/null +++ b/core/src/VariantStream.cpp @@ -0,0 +1,53 @@ +/* + * VariantStream.cpp - read/write QVariant objects to/from QIODevice + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "VariantStream.h" + +VariantStream::VariantStream( QIODevice* ioDevice ) : + m_dataStream( ioDevice ) +{ + m_dataStream.setVersion( QDataStream::Qt_5_5 ); +} + + + +QVariant VariantStream::read() // Flawfinder: ignore +{ + QVariant v; + m_dataStream >> v; + + if( v.isValid() == false || v.isNull() ) + { + vWarning() << "none or invalid data read"; + } + + return v; +} + + + +void VariantStream::write( const QVariant& v ) +{ + m_dataStream << v; +} diff --git a/core/src/VeyonConfiguration.cpp b/core/src/VeyonConfiguration.cpp new file mode 100644 index 0000000..5eb470e --- /dev/null +++ b/core/src/VeyonConfiguration.cpp @@ -0,0 +1,71 @@ +/* + * VeyonConfiguration.cpp - a Configuration object storing system wide + * configuration values + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "VeyonConfiguration.h" +#include "VeyonCore.h" +#include "Logger.h" +#include "NetworkObjectDirectory.h" + + +VeyonConfiguration::VeyonConfiguration() : + Configuration::Object( Configuration::Store::LocalBackend, + Configuration::Store::System ) +{ +} + + + +VeyonConfiguration::VeyonConfiguration( Configuration::Store* store ) : + Configuration::Object( store ) +{ +} + + + +void VeyonConfiguration::upgrade() +{ + if( applicationVersion() < VeyonCore::ApplicationVersion::Version_4_2 ) + { + setAutoSelectCurrentLocation( legacyAutoSwitchToCurrentRoom() ); + setShowCurrentLocationOnly( legacyOnlyCurrentRoomVisible() ); + setAllowAddingHiddenLocations( legacyManualRoomAdditionAllowed() ); + setHideEmptyLocations( legacyEmptyRoomsHidden() ); + setAutoOpenComputerSelectPanel( legacyOpenComputerManagementAtStart() ); + setConfirmUnsafeActions( legacyConfirmDangerousActions() ); + + setApplicationVersion( VeyonCore::ApplicationVersion::Version_4_2 ); + } + else if( applicationVersion() < VeyonCore::ApplicationVersion::Version_4_5 ) + { + setVeyonServerPort( legacyPrimaryServicePort() ); + setHideLocalComputer( legacyLocalComputerHidden() ); + setHideComputerFilter( legacyComputerFilterHidden() ); + setAutoAdjustMonitoringIconSize( legacyAutoAdjustGridSize() ); + + setApplicationVersion( VeyonCore::ApplicationVersion::Version_4_5 ); + } +} diff --git a/core/src/VeyonConnection.cpp b/core/src/VeyonConnection.cpp new file mode 100644 index 0000000..0e0652f --- /dev/null +++ b/core/src/VeyonConnection.cpp @@ -0,0 +1,341 @@ +/* + * VeyonConnection.cpp - implementation of VeyonConnection + * + * Copyright (c) 2008-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "rfb/rfbclient.h" + +#include "AuthenticationProxy.h" +#include "CryptoCore.h" +#include "PlatformUserFunctions.h" +#include "SocketDevice.h" +#include "VariantArrayMessage.h" +#include "VeyonConfiguration.h" +#include "VeyonConnection.h" +#include "VncFeatureMessageEvent.h" + + +static rfbClientProtocolExtension* __veyonProtocolExt = nullptr; +static const uint32_t __veyonSecurityTypes[2] = { rfbSecTypeVeyon, 0 }; + + +rfbBool handleVeyonMessage( rfbClient* client, rfbServerToClientMsg* msg ) +{ + auto connection = reinterpret_cast( VncConnection::clientData( client, VeyonConnection::VeyonConnectionTag ) ); + if( connection ) + { + return connection->handleServerMessage( client, msg->type ); + } + + return false; +} + + + +VeyonConnection::VeyonConnection( VncConnection* vncConnection ): + m_vncConnection( vncConnection ), + m_veyonAuthType( RfbVeyonAuth::Logon ), + m_user(), + m_userHomeDir() +{ + if( __veyonProtocolExt == nullptr ) + { + __veyonProtocolExt = new rfbClientProtocolExtension; + __veyonProtocolExt->encodings = nullptr; + __veyonProtocolExt->handleEncoding = nullptr; + __veyonProtocolExt->handleMessage = handleVeyonMessage; + __veyonProtocolExt->securityTypes = __veyonSecurityTypes; + __veyonProtocolExt->handleAuthentication = handleSecTypeVeyon; + + rfbClientRegisterExtension( __veyonProtocolExt ); + } + + if( VeyonCore::config().authenticationMethod() == VeyonCore::AuthenticationMethod::KeyFileAuthentication ) + { + m_veyonAuthType = RfbVeyonAuth::KeyFile; + } + + connect( m_vncConnection, &VncConnection::connectionPrepared, this, &VeyonConnection::registerConnection, Qt::DirectConnection ); + connect( m_vncConnection, &VncConnection::destroyed, this, &VeyonConnection::deleteLater ); +} + + + +VeyonConnection::~VeyonConnection() +{ + unregisterConnection(); + + delete m_authenticationProxy; +} + + + +void VeyonConnection::sendFeatureMessage( const FeatureMessage& featureMessage, bool wake ) +{ + if( m_vncConnection.isNull() ) + { + vCritical() << "cannot enqueue event as VNC connection is invalid"; + return; + } + + m_vncConnection->enqueueEvent( new VncFeatureMessageEvent( featureMessage ), wake ); +} + + + +bool VeyonConnection::handleServerMessage( rfbClient* client, uint8_t msg ) +{ + if( msg == FeatureMessage::RfbMessageType ) + { + SocketDevice socketDev( VncConnection::libvncClientDispatcher, client ); + FeatureMessage featureMessage; + if( featureMessage.receive( &socketDev ) == false ) + { + vDebug() << "could not receive feature message"; + + return false; + } + + vDebug() << "received feature message" << featureMessage.command() + << "with arguments" << featureMessage.arguments(); + + Q_EMIT featureMessageReceived( featureMessage ); + + return true; + } + else + { + vCritical() << "unknown message type" << static_cast( msg ) + << "from server. Closing connection. Will re-open it later."; + } + + return false; +} + + + +void VeyonConnection::registerConnection() +{ + if( m_vncConnection.isNull() == false ) + { + m_vncConnection->setClientData( VeyonConnectionTag, this ); + } +} + + + +void VeyonConnection::unregisterConnection() +{ + if( m_vncConnection.isNull() == false ) + { + m_vncConnection->setClientData( VeyonConnectionTag, nullptr ); + } +} + + + +int8_t VeyonConnection::handleSecTypeVeyon( rfbClient* client, uint32_t authScheme ) +{ + if( authScheme != rfbSecTypeVeyon ) + { + return false; + } + + hookPrepareAuthentication( client ); + + auto connection = static_cast( VncConnection::clientData( client, VeyonConnectionTag ) ); + if( connection == nullptr ) + { + return false; + } + + SocketDevice socketDevice( VncConnection::libvncClientDispatcher, client ); + VariantArrayMessage message( &socketDevice ); + message.receive(); + + int authTypeCount = message.read().toInt(); + + QList authTypes; + authTypes.reserve( authTypeCount ); + + for( int i = 0; i < authTypeCount; ++i ) + { + authTypes.append( QVariantHelper::value( message.read() ) ); + } + + auto proxy = connection->m_authenticationProxy; + + if( proxy ) + { + proxy->setAuthenticationTypes( authTypes ); + } + + vDebug() << QThread::currentThreadId() << "received authentication types:" << authTypes; + + RfbVeyonAuth::Type chosenAuthType = RfbVeyonAuth::Token; + if( authTypes.count() > 0 ) + { + chosenAuthType = authTypes.first(); + + // look whether the VncConnection recommends a specific + // authentication type (e.g. VeyonAuthHostBased when running as + // demo client) + + for( auto authType : authTypes ) + { + if( connection->veyonAuthType() == authType ) + { + chosenAuthType = authType; + } + } + } + + if( proxy ) + { + chosenAuthType = proxy->initCredentials(); + } + + if( chosenAuthType == RfbVeyonAuth::None ) + { + return false; + } + + vDebug() << QThread::currentThreadId() << "chose authentication type:" << authTypes; + + VariantArrayMessage authReplyMessage( &socketDevice ); + + authReplyMessage.write( chosenAuthType ); + + // send username which is used when displaying an access confirm dialog + if( connection->authenticationCredentials().hasCredentials( AuthenticationCredentials::Type::UserLogon ) ) + { + authReplyMessage.write( connection->authenticationCredentials().logonUsername() ); + } + else + { + authReplyMessage.write( VeyonCore::platform().userFunctions().currentUser() ); + } + + authReplyMessage.send(); + + VariantArrayMessage authAckMessage( &socketDevice ); + authAckMessage.receive(); + + switch( chosenAuthType ) + { + case RfbVeyonAuth::KeyFile: + if( connection->authenticationCredentials().hasCredentials( AuthenticationCredentials::Type::PrivateKey ) ) + { + VariantArrayMessage challengeReceiveMessage( &socketDevice ); + challengeReceiveMessage.receive(); + const auto challenge = challengeReceiveMessage.read().toByteArray(); + + if( challenge.size() != CryptoCore::ChallengeSize ) + { + vCritical() << QThread::currentThreadId() << "challenge size mismatch!"; + return false; + } + + // create local copy of private key so we can modify it within our own thread + auto key = connection->authenticationCredentials().privateKey(); + if( key.isNull() || key.canSign() == false ) + { + vCritical() << QThread::currentThreadId() << "invalid private key!"; + return false; + } + + const auto signature = key.signMessage( challenge, CryptoCore::DefaultSignatureAlgorithm ); + + VariantArrayMessage challengeResponseMessage( &socketDevice ); + challengeResponseMessage.write( connection->authenticationCredentials().authenticationKeyName() ); + challengeResponseMessage.write( signature ); + challengeResponseMessage.send(); + } + break; + + case RfbVeyonAuth::Logon: + { + VariantArrayMessage publicKeyMessage( &socketDevice ); + publicKeyMessage.receive(); + + CryptoCore::PublicKey publicKey = CryptoCore::PublicKey::fromPEM( publicKeyMessage.read().toString() ); + + if( publicKey.canEncrypt() == false ) + { + vCritical() << QThread::currentThreadId() << "can't encrypt with given public key!"; + return false; + } + + CryptoCore::SecureArray plainTextPassword( connection->authenticationCredentials().logonPassword() ); + CryptoCore::SecureArray encryptedPassword = publicKey.encrypt( plainTextPassword, CryptoCore::DefaultEncryptionAlgorithm ); + if( encryptedPassword.isEmpty() ) + { + vCritical() << QThread::currentThreadId() << "password encryption failed!"; + return false; + } + + VariantArrayMessage passwordResponse( &socketDevice ); + passwordResponse.write( encryptedPassword.toByteArray() ); + passwordResponse.send(); + break; + } + + case RfbVeyonAuth::Token: + { + VariantArrayMessage tokenAuthMessage( &socketDevice ); + tokenAuthMessage.write( connection->authenticationCredentials().token().toByteArray() ); + tokenAuthMessage.send(); + break; + } + + default: + // nothing to do - we just get accepted + break; + } + + return true; +} + + + +void VeyonConnection::hookPrepareAuthentication( rfbClient* client ) +{ + auto connection = static_cast( VncConnection::clientData( client, VncConnection::VncConnectionTag ) ); + if( connection ) + { + // set our internal flag which indicates that we basically have communication with the client + // which means that the host is reachable + connection->setServerReachable(); + } +} + + + +AuthenticationCredentials VeyonConnection::authenticationCredentials() const +{ + if( m_authenticationProxy ) + { + return m_authenticationProxy->credentials(); + } + + return VeyonCore::authenticationCredentials(); +} diff --git a/core/src/VeyonCore.cpp b/core/src/VeyonCore.cpp new file mode 100644 index 0000000..cf3ee90 --- /dev/null +++ b/core/src/VeyonCore.cpp @@ -0,0 +1,687 @@ +/* + * VeyonCore.cpp - implementation of Veyon Core + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "BuiltinFeatures.h" +#include "ComputerControlInterface.h" +#include "Filesystem.h" +#include "HostAddress.h" +#include "Logger.h" +#include "NetworkObjectDirectoryManager.h" +#include "PasswordDialog.h" +#include "PlatformPluginManager.h" +#include "PlatformCoreFunctions.h" +#include "PlatformSessionFunctions.h" +#include "PluginManager.h" +#include "TranslationLoader.h" +#include "UserGroupsBackendManager.h" +#include "VeyonConfiguration.h" +#include "VncConnection.h" + + +VeyonCore* VeyonCore::s_instance = nullptr; + + +VeyonCore::VeyonCore( QCoreApplication* application, Component component, const QString& appComponentName ) : + QObject( application ), + m_filesystem( new Filesystem ), + m_config( nullptr ), + m_logger( nullptr ), + m_authenticationCredentials( nullptr ), + m_cryptoCore( nullptr ), + m_pluginManager( nullptr ), + m_platformPluginManager( nullptr ), + m_platformPlugin( nullptr ), + m_builtinFeatures( nullptr ), + m_userGroupsBackendManager( nullptr ), + m_networkObjectDirectoryManager( nullptr ), + m_component( component ), + m_applicationName( QStringLiteral( "Veyon" ) ), + m_debugging( false ) +{ + Q_ASSERT( application != nullptr ); + + s_instance = this; + + setupApplicationParameters(); + + initPlatformPlugin(); + + initConfiguration(); + + initSession(); + + initLogging( appComponentName ); + + initLocaleAndTranslation(); + + initCryptoCore(); + + initAuthenticationCredentials(); + + initPlugins(); + + initManagers(); + + initSystemInfo(); + + Q_EMIT initialized(); +} + + + +VeyonCore::~VeyonCore() +{ + delete m_userGroupsBackendManager; + m_userGroupsBackendManager = nullptr; + + delete m_authenticationCredentials; + m_authenticationCredentials = nullptr; + + delete m_builtinFeatures; + m_builtinFeatures = nullptr; + + delete m_platformPluginManager; + m_platformPluginManager = nullptr; + + delete m_pluginManager; + m_pluginManager = nullptr; + + delete m_logger; + m_logger = nullptr; + + delete m_config; + m_config = nullptr; + + delete m_filesystem; + m_filesystem = nullptr; + + delete m_cryptoCore; + m_cryptoCore = nullptr; + + s_instance = nullptr; +} + + + +VeyonCore* VeyonCore::instance() +{ + Q_ASSERT(s_instance != nullptr); + + return s_instance; +} + + + +QString VeyonCore::version() +{ + return QStringLiteral( VEYON_VERSION ); +} + + + +QString VeyonCore::pluginDir() +{ + return QStringLiteral( VEYON_PLUGIN_DIR ); +} + + + +QString VeyonCore::translationsDirectory() +{ + return QCoreApplication::applicationDirPath() + QDir::separator() + QStringLiteral(VEYON_TRANSLATIONS_DIR); +} + + + +QString VeyonCore::qtTranslationsDirectory() +{ +#ifdef QT_TRANSLATIONS_DIR + return QStringLiteral( QT_TRANSLATIONS_DIR ); +#else + return translationsDirectory(); +#endif +} + + + +QString VeyonCore::executableSuffix() +{ + return QStringLiteral( VEYON_EXECUTABLE_SUFFIX ); // clazy:exclude=empty-qstringliteral +} + + + +QString VeyonCore::sharedLibrarySuffix() +{ + return QStringLiteral( VEYON_SHARED_LIBRARY_SUFFIX ); +} + + + +QString VeyonCore::sessionIdEnvironmentVariable() +{ + return QStringLiteral("VEYON_SESSION_ID"); +} + + + +void VeyonCore::setupApplicationParameters() +{ + QCoreApplication::setOrganizationName( QStringLiteral( "Veyon Solutions" ) ); + QCoreApplication::setOrganizationDomain( QStringLiteral( "veyon.io" ) ); + QCoreApplication::setApplicationName( QStringLiteral( "Veyon" ) ); + + QApplication::setAttribute( Qt::AA_UseHighDpiPixmaps ); +} + + + +bool VeyonCore::initAuthentication() +{ + switch( config().authenticationMethod() ) + { + case AuthenticationMethod::LogonAuthentication: return initLogonAuthentication(); + case AuthenticationMethod::KeyFileAuthentication: return initKeyFileAuthentication(); + } + + return false; +} + + + +QString VeyonCore::applicationName() +{ + return instance()->m_applicationName; +} + + + +void VeyonCore::enforceBranding( QWidget *topLevelWidget ) +{ + const auto appName = QStringLiteral( "Veyon" ); + + auto labels = topLevelWidget->findChildren(); + for( auto label : labels ) + { + label->setText( label->text().replace( appName, VeyonCore::applicationName() ) ); + } + + auto buttons = topLevelWidget->findChildren(); + for( auto button : buttons ) + { + button->setText( button->text().replace( appName, VeyonCore::applicationName() ) ); + } + + auto groupBoxes = topLevelWidget->findChildren(); + for( auto groupBox : groupBoxes ) + { + groupBox->setTitle( groupBox->title().replace( appName, VeyonCore::applicationName() ) ); + } + + auto actions = topLevelWidget->findChildren(); + for( auto action : actions ) + { + action->setText( action->text().replace( appName, VeyonCore::applicationName() ) ); + } + + auto widgets = topLevelWidget->findChildren(); + for( auto widget : widgets ) + { + widget->setWindowTitle( widget->windowTitle().replace( appName, VeyonCore::applicationName() ) ); + } + + topLevelWidget->setWindowTitle( topLevelWidget->windowTitle().replace( appName, VeyonCore::applicationName() ) ); +} + + + +bool VeyonCore::isDebugging() +{ + return instance()->m_debugging; +} + + + +QByteArray VeyonCore::shortenFuncinfo( QByteArray info ) +{ + const auto funcinfo = cleanupFuncinfo( info ); + + if( isDebugging() ) + { + return funcinfo + QByteArrayLiteral( "():" ); + } + + return funcinfo.split( ':' ).first() + QByteArrayLiteral(":"); +} + + + +// taken from qtbase/src/corelib/global/qlogging.cpp + +QByteArray VeyonCore::cleanupFuncinfo( QByteArray info ) +{ + // Strip the function info down to the base function name + // note that this throws away the template definitions, + // the parameter types (overloads) and any const/volatile qualifiers. + + if (info.isEmpty()) + return info; + + int pos; + + // Skip trailing [with XXX] for templates (gcc), but make + // sure to not affect Objective-C message names. + pos = info.size() - 1; + if (info.endsWith(']') && !(info.startsWith('+') || info.startsWith('-'))) { + while (--pos) { + if (info.at(pos) == '[') + info.truncate(pos); + } + } + + // operator names with '(', ')', '<', '>' in it + static const char operator_call[] = "operator()"; + static const char operator_lessThan[] = "operator<"; + static const char operator_greaterThan[] = "operator>"; + static const char operator_lessThanEqual[] = "operator<="; + static const char operator_greaterThanEqual[] = "operator>="; + + // canonize operator names + info.replace("operator ", "operator"); + + // remove argument list + Q_FOREVER { + int parencount = 0; + pos = info.lastIndexOf(')'); + if (pos == -1) { + // Don't know how to parse this function name + return info; + } + + // find the beginning of the argument list + --pos; + ++parencount; + while (pos && parencount) { + if (info.at(pos) == ')') + ++parencount; + else if (info.at(pos) == '(') + --parencount; + --pos; + } + if (parencount != 0) + return info; + + info.truncate(++pos); + + if (info.at(pos - 1) == ')') { + if (info.indexOf(operator_call) == pos - (int)strlen(operator_call)) + break; + + // this function returns a pointer to a function + // and we matched the arguments of the return type's parameter list + // try again + info.remove(0, info.indexOf('(')); + info.chop(1); + continue; + } else { + break; + } + } + + // find the beginning of the function name + int parencount = 0; + int templatecount = 0; + --pos; + + // make sure special characters in operator names are kept + if (pos > -1) { + switch (info.at(pos)) { + case ')': + if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1) + pos -= 2; + break; + case '<': + if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1) + --pos; + break; + case '>': + if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1) + --pos; + break; + case '=': { + int operatorLength = (int)strlen(operator_lessThanEqual); + if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1) + pos -= 2; + else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1) + pos -= 2; + break; + } + default: + break; + } + } + + while (pos > -1) { + if (parencount < 0 || templatecount < 0) + return info; + + char c = info.at(pos); + if (c == ')') + ++parencount; + else if (c == '(') + --parencount; + else if (c == '>') + ++templatecount; + else if (c == '<') + --templatecount; + else if (c == ' ' && templatecount == 0 && parencount == 0) + break; + + --pos; + } + info = info.mid(pos + 1); + + // remove trailing '*', '&' that are part of the return argument + while ((info.at(0) == '*') + || (info.at(0) == '&')) + info = info.mid(1); + + // we have the full function name now. + // clean up the templates + while ((pos = info.lastIndexOf('>')) != -1) { + if (!info.contains('<')) + break; + + // find the matching close + int end = pos; + templatecount = 1; + --pos; + while (pos && templatecount) { + char c = info.at(pos); + if (c == '>') + ++templatecount; + else if (c == '<') + --templatecount; + --pos; + } + ++pos; + info.remove(pos, end - pos + 1); + } + + return info; +} + + + +QString VeyonCore::stripDomain( const QString& username ) +{ + // remove the domain part of username (e.g. "EXAMPLE.COM\Teacher" -> "Teacher") + int domainSeparator = username.indexOf( QLatin1Char('\\') ); + if( domainSeparator >= 0 ) + { + return username.mid( domainSeparator + 1 ); + } + + return username; +} + + + +QString VeyonCore::formattedUuid( QUuid uuid ) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + return uuid.toString( QUuid::WithoutBraces ); +#else + return uuid.toString().remove( QLatin1Char('{') ).remove( QLatin1Char('}') ); +#endif +} + + + +bool VeyonCore::isAuthenticationKeyNameValid( const QString& authKeyName ) +{ + return QRegExp( QStringLiteral("\\w+") ).exactMatch( authKeyName ); +} + + + +int VeyonCore::exec() +{ + Q_EMIT applicationLoaded(); + + vDebug() << "Running"; + + const auto result = QCoreApplication::instance()->exec(); + + vDebug() << "Exit"; + + return result; +} + + + +void VeyonCore::initPlatformPlugin() +{ + // initialize plugin manager and load platform plugins first + m_pluginManager = new PluginManager( this ); + m_pluginManager->loadPlatformPlugins(); + + // initialize platform plugin manager and initialize used platform plugin + m_platformPluginManager = new PlatformPluginManager( *m_pluginManager ); + m_platformPlugin = m_platformPluginManager->platformPlugin(); +} + + + +void VeyonCore::initSession() +{ + if( config().multiSessionModeEnabled() ) + { + const auto systemEnv = QProcessEnvironment::systemEnvironment(); + if( systemEnv.contains( sessionIdEnvironmentVariable() ) ) + { + m_sessionId = systemEnv.value( sessionIdEnvironmentVariable() ).toInt(); + } + else + { + const auto sessionId = platform().sessionFunctions().currentSessionId(); + if( sessionId != PlatformSessionFunctions::InvalidSessionId ) + { + m_sessionId = sessionId; + } + } + } + else + { + m_sessionId = PlatformSessionFunctions::DefaultSessionId; + } +} + + +void VeyonCore::initConfiguration() +{ + m_config = new VeyonConfiguration(); + m_config->upgrade(); + + if( QUuid( config().installationID() ).isNull() ) + { + config().setInstallationID( formattedUuid( QUuid::createUuid() ) ); + } + + if( config().applicationName().isEmpty() == false ) + { + m_applicationName = config().applicationName(); + } +} + + + +void VeyonCore::initLogging( const QString& appComponentName ) +{ + const auto currentSessionId = sessionId(); + + if( currentSessionId != PlatformSessionFunctions::DefaultSessionId ) + { + m_logger = new Logger( QStringLiteral("%1-%2").arg( appComponentName ).arg( currentSessionId ) ); + } + else + { + m_logger = new Logger( appComponentName ); + } + + m_debugging = ( m_logger->logLevel() >= Logger::LogLevel::Debug ); + + VncConnection::initLogging( isDebugging() ); +} + + + +void VeyonCore::initLocaleAndTranslation() +{ + TranslationLoader::load( QStringLiteral("qt") ); + + const auto configuredLocale = TranslationLoader::load( QStringLiteral("veyon") ); + + if( configuredLocale.language() == QLocale::Hebrew || + configuredLocale.language() == QLocale::Arabic ) + { + auto app = qobject_cast( QCoreApplication::instance() ); + if( app ) + { + QApplication::setLayoutDirection( Qt::RightToLeft ); + } + } +} + + + +void VeyonCore::initCryptoCore() +{ + m_cryptoCore = new CryptoCore; +} + + + +void VeyonCore::initAuthenticationCredentials() +{ + if( m_authenticationCredentials ) + { + delete m_authenticationCredentials; + m_authenticationCredentials = nullptr; + } + + m_authenticationCredentials = new AuthenticationCredentials; +} + + + +void VeyonCore::initPlugins() +{ + // load all other (non-platform) plugins + m_pluginManager->loadPlugins(); + m_pluginManager->upgradePlugins(); + + m_builtinFeatures = new BuiltinFeatures(); +} + + + +void VeyonCore::initManagers() +{ + m_userGroupsBackendManager = new UserGroupsBackendManager( this ); + m_networkObjectDirectoryManager = new NetworkObjectDirectoryManager( this ); +} + + + +bool VeyonCore::initLogonAuthentication() +{ + if( qobject_cast( QCoreApplication::instance() ) ) + { + PasswordDialog dlg( QApplication::activeWindow() ); + if( dlg.exec() && + dlg.credentials().hasCredentials( AuthenticationCredentials::Type::UserLogon ) ) + { + m_authenticationCredentials->setLogonUsername( dlg.username() ); + m_authenticationCredentials->setLogonPassword( dlg.password() ); + + return true; + } + } + + return false; +} + + + +bool VeyonCore::initKeyFileAuthentication() +{ + auto authKeyName = QProcessEnvironment::systemEnvironment().value( QStringLiteral("VEYON_AUTH_KEY_NAME") ); + + if( authKeyName.isEmpty() == false ) + { + if( isAuthenticationKeyNameValid( authKeyName ) && + m_authenticationCredentials->loadPrivateKey( VeyonCore::filesystem().privateKeyPath( authKeyName ) ) ) + { + m_authenticationCredentials->setAuthenticationKeyName( authKeyName ); + } + } + else + { + // try to auto-detect private key file by searching for readable file + const auto privateKeyBaseDir = VeyonCore::filesystem().expandPath( VeyonCore::config().privateKeyBaseDir() ); + const auto privateKeyDirs = QDir( privateKeyBaseDir ).entryList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name ); + + for( const auto& privateKeyDir : privateKeyDirs ) + { + if( m_authenticationCredentials->loadPrivateKey( VeyonCore::filesystem().privateKeyPath( privateKeyDir ) ) ) + { + m_authenticationCredentials->setAuthenticationKeyName( privateKeyDir ); + return true; + } + } + } + + return false; +} + + + +void VeyonCore::initSystemInfo() +{ + vDebug() << version() << HostAddress::localFQDN() + << QSysInfo::kernelType() << QSysInfo::kernelVersion() + << QSysInfo::prettyProductName() << QSysInfo::productType() << QSysInfo::productVersion(); +} diff --git a/core/src/VeyonServiceControl.cpp b/core/src/VeyonServiceControl.cpp new file mode 100644 index 0000000..b36212d --- /dev/null +++ b/core/src/VeyonServiceControl.cpp @@ -0,0 +1,68 @@ +/* + * VeyonServiceControl.cpp - class for controlling Veyon service + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "PlatformServiceFunctions.h" +#include "VeyonServiceControl.h" + + +VeyonServiceControl::VeyonServiceControl( QWidget* parent ) : + ServiceControl( name(), filePath(), displayName(), parent ) +{ +} + + + +bool VeyonServiceControl::setAutostart( bool enabled ) +{ + return VeyonCore::platform().serviceFunctions(). + setStartMode( name(), enabled ? PlatformServiceFunctions::StartMode::Auto : + PlatformServiceFunctions::StartMode::Manual ); +} + + + +QString VeyonServiceControl::name() +{ + return VeyonCore::platform().serviceFunctions().veyonServiceName(); +} + + + +QString VeyonServiceControl::filePath() +{ + return QDir::toNativeSeparators( + QCoreApplication::applicationDirPath() + + QDir::separator() + + QStringLiteral("veyon-service") + VeyonCore::executableSuffix() ); +} + + + +QString VeyonServiceControl::displayName() +{ + return tr( "Veyon Service" ); +} diff --git a/core/src/VncClientProtocol.cpp b/core/src/VncClientProtocol.cpp new file mode 100644 index 0000000..433b9b1 --- /dev/null +++ b/core/src/VncClientProtocol.cpp @@ -0,0 +1,795 @@ +/* + * VncClientProtocol.cpp - implementation of the VncClientProtocol class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +extern "C" +{ +#include "rfb/rfbproto.h" +#include "d3des.h" +} + +#include +#include +#include + +#include "VncClientProtocol.h" + + +/* + * Encrypt CHALLENGESIZE bytes in memory using a password. + */ + +static void +vncEncryptBytes(unsigned char *bytes, const char *passwd, size_t passwd_length) +{ + constexpr int KeyLength = 8; + unsigned char key[KeyLength]; // Flawfinder: ignore + unsigned int i; + + /* key is simply password padded with nulls */ + + for (i = 0; i < KeyLength; i++) { + if (i < passwd_length) { + key[i] = static_cast( passwd[i] ); + } else { + key[i] = 0; + } + } + + rfbDesKey(key, EN0); + + for (i = 0; i < CHALLENGESIZE; i += KeyLength) { + rfbDes(bytes+i, bytes+i); + } +} + + + + +VncClientProtocol::VncClientProtocol( QTcpSocket* socket, const Password& vncPassword ) : + m_socket( socket ), + m_state( Disconnected ), + m_vncPassword( vncPassword ), + m_serverInitMessage(), + m_pixelFormat( { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } ), + m_framebufferWidth( 0 ), + m_framebufferHeight( 0 ) +{ +} + + + +void VncClientProtocol::start() +{ + m_state = Protocol; +} + + + +bool VncClientProtocol::read() // Flawfinder: ignore +{ + switch( m_state ) + { + case Protocol: + return readProtocol(); + + case SecurityInit: + return receiveSecurityTypes(); + + case SecurityChallenge: + return receiveSecurityChallenge(); + + case SecurityResult: + return receiveSecurityResult(); + + case FramebufferInit: + return receiveServerInitMessage(); + + default: + break; + } + + return false; +} + + + +bool VncClientProtocol::setPixelFormat( rfbPixelFormat pixelFormat ) +{ + rfbSetPixelFormatMsg spf; + + spf.type = rfbSetPixelFormat; + spf.pad1 = 0; + spf.pad2 = 0; + spf.format = pixelFormat; + spf.format.redMax = qFromBigEndian(pixelFormat.redMax); + spf.format.greenMax = qFromBigEndian(pixelFormat.greenMax); + spf.format.blueMax = qFromBigEndian(pixelFormat.blueMax); + + return m_socket->write( reinterpret_cast( &spf ), sz_rfbSetPixelFormatMsg ) == sz_rfbSetPixelFormatMsg; +} + + + +bool VncClientProtocol::setEncodings( const QVector& encodings ) +{ + if( encodings.size() > MAX_ENCODINGS ) + { + return false; + } + + alignas(rfbSetEncodingsMsg) char buf[sz_rfbSetEncodingsMsg + MAX_ENCODINGS * 4]; // Flawfinder: ignore + + auto setEncodingsMsg = reinterpret_cast( buf ); + auto encs = reinterpret_cast( &buf[sz_rfbSetEncodingsMsg] ); + + setEncodingsMsg->type = rfbSetEncodings; + setEncodingsMsg->pad = 0; + setEncodingsMsg->nEncodings = 0; + + for( auto encoding : encodings ) + { + encs[setEncodingsMsg->nEncodings++] = qFromBigEndian( encoding ); + } + + const auto len = sz_rfbSetEncodingsMsg + setEncodingsMsg->nEncodings * 4; + + setEncodingsMsg->nEncodings = qFromBigEndian(setEncodingsMsg->nEncodings); + + return m_socket->write( buf, len ) == len; +} + + + +void VncClientProtocol::requestFramebufferUpdate( bool incremental ) +{ + rfbFramebufferUpdateRequestMsg updateRequest; + + updateRequest.type = rfbFramebufferUpdateRequest; + updateRequest.incremental = incremental ? 1 : 0; + updateRequest.x = 0; + updateRequest.y = 0; + updateRequest.w = qFromBigEndian( m_framebufferWidth ); + updateRequest.h = qFromBigEndian( m_framebufferHeight ); + + if( m_socket->write( reinterpret_cast( &updateRequest ), sz_rfbFramebufferUpdateRequestMsg ) != sz_rfbFramebufferUpdateRequestMsg ) + { + vDebug() << "could not write to socket - closing connection"; + m_socket->close(); + } +} + + + +bool VncClientProtocol::receiveMessage() +{ + if( m_socket->bytesAvailable() > MaximumMessageSize ) + { + vCritical() << "Message too big or invalid"; + m_socket->close(); + return false; + } + + uint8_t messageType = 0; + if( m_socket->peek( reinterpret_cast( &messageType ), sizeof(messageType) ) != sizeof(messageType) ) + { + return false; + } + + switch( messageType ) + { + case rfbFramebufferUpdate: + return receiveFramebufferUpdateMessage(); + + case rfbSetColourMapEntries: + return receiveColourMapEntriesMessage(); + + case rfbBell: + return receiveBellMessage(); + + case rfbServerCutText: + return receiveCutTextMessage(); + + case rfbResizeFrameBuffer: + return receiveResizeFramebufferMessage(); + + case rfbXvp: + return receiveXvpMessage(); + + default: + vCritical() << "received unknown message type" << static_cast( messageType ); + m_socket->close(); + } + + return false; +} + + + +bool VncClientProtocol::readProtocol() +{ + if( m_socket->bytesAvailable() == sz_rfbProtocolVersionMsg ) + { + const auto protocol = m_socket->read( sz_rfbProtocolVersionMsg ); + + if( protocol.size() != sz_rfbProtocolVersionMsg ) + { + vCritical() << "protocol initialization failed"; + m_socket->close(); + + return false; + } + + QRegExp protocolRX( QStringLiteral("RFB (\\d\\d\\d)\\.(\\d\\d\\d)\n") ); + + if( protocolRX.indexIn( QString::fromUtf8( protocol ) ) != 0 || + protocolRX.cap( 1 ).toInt() != 3 || + protocolRX.cap( 2 ).toInt() < 7 ) + { + vCritical() << "invalid protocol version"; + m_socket->close(); + + return false; + } + + m_socket->write( protocol ); + + m_state = SecurityInit; + + return true; + } + + return false; +} + + + +bool VncClientProtocol::receiveSecurityTypes() +{ + if( m_socket->bytesAvailable() >= 2 ) + { + uint8_t securityTypeCount = 0; + + m_socket->read( reinterpret_cast( &securityTypeCount ), sizeof(securityTypeCount) ); + + if( securityTypeCount == 0 ) + { + vCritical() << "invalid number of security types received!"; + m_socket->close(); + + return false; + } + + const auto securityTypeList = m_socket->read( securityTypeCount ); + if( securityTypeList.count() != securityTypeCount ) + { + vCritical() << "could not read security types!"; + m_socket->close(); + + return false; + } + + char securityType = rfbSecTypeInvalid; + + if( securityTypeList.contains( rfbSecTypeVncAuth ) ) + { + securityType = rfbSecTypeVncAuth; + m_state = State::SecurityChallenge; + } + else if( securityTypeList.contains( rfbSecTypeNone ) ) + { + securityType = rfbSecTypeNone; + m_state = State::SecurityResult; + } + else + { + vCritical() << "unsupported security types!" << securityTypeList; + m_socket->close(); + return false; + } + + m_socket->write( &securityType, sizeof(securityType) ); + + return true; + } + + return false; +} + + + +bool VncClientProtocol::receiveSecurityChallenge() +{ + if( m_socket->bytesAvailable() >= CHALLENGESIZE ) + { + uint8_t challenge[CHALLENGESIZE]; + m_socket->read( reinterpret_cast( challenge ), CHALLENGESIZE ); + + vncEncryptBytes( challenge, m_vncPassword.constData(), static_cast( m_vncPassword.size() ) ); + + m_socket->write( reinterpret_cast( challenge ), CHALLENGESIZE ); + + m_state = SecurityResult; + + return true; + } + + return false; +} + + + +bool VncClientProtocol::receiveSecurityResult() +{ + if( m_socket->bytesAvailable() >= 4 ) + { + uint32_t authResult = 0; + + m_socket->read( reinterpret_cast( &authResult ), sizeof(authResult) ); + + if( qFromBigEndian( authResult ) != rfbVncAuthOK ) + { + vCritical() << "authentication failed!"; + m_socket->close(); + return false; + } + + vDebug() << "authentication successful"; + + // finally send client init message + rfbClientInitMsg clientInitMessage; + clientInitMessage.shared = 1; + m_socket->write( reinterpret_cast( &clientInitMessage ), sz_rfbClientInitMsg ); + + // wait for server init message + m_state = FramebufferInit; + + return true; + } + + return false; +} + + + +bool VncClientProtocol::receiveServerInitMessage() +{ + rfbServerInitMsg message; + + if( m_socket->bytesAvailable() >= sz_rfbServerInitMsg && + m_socket->peek( reinterpret_cast( &message ), sz_rfbServerInitMsg ) == sz_rfbServerInitMsg ) + { + const auto nameLength = qFromBigEndian( message.nameLength ); + + if( nameLength > 255 ) + { + vCritical() << "size of desktop name > 255!"; + m_socket->close(); + return false; + } + + static_assert( sizeof(m_pixelFormat) >= sz_rfbPixelFormat, "m_pixelFormat has wrong size" ); + static_assert( sizeof(m_pixelFormat) >= sizeof(message.format), "m_pixelFormat too small" ); + + memcpy( &m_pixelFormat, &message.format, sz_rfbPixelFormat ); // Flawfinder: ignore + + if( static_cast( m_socket->peek( nameLength ).size() ) == nameLength ) + { + m_serverInitMessage = m_socket->read( sz_rfbServerInitMsg + nameLength ); + + const auto serverInitMessage = reinterpret_cast( m_serverInitMessage.constData() ); + m_framebufferWidth = qFromBigEndian( serverInitMessage->framebufferWidth ); + m_framebufferHeight = qFromBigEndian( serverInitMessage->framebufferHeight ); + + m_state = Running; + + return true; + } + } + + return false; +} + + + +bool VncClientProtocol::receiveFramebufferUpdateMessage() +{ + // peek all available data and work on a local buffer so we can continously read from it + auto data = m_socket->peek( m_socket->bytesAvailable() ); + + QBuffer buffer( &data ); + buffer.open( QBuffer::ReadOnly ); // Flawfinder: ignore + + rfbFramebufferUpdateMsg message; + if( buffer.read( reinterpret_cast( &message ), sz_rfbFramebufferUpdateMsg ) != sz_rfbFramebufferUpdateMsg ) + { + return false; + } + + QRegion updatedRegion; + + const auto nRects = qFromBigEndian( message.nRects ); + + for( int i = 0; i < nRects; ++i ) + { + rfbFramebufferUpdateRectHeader rectHeader; + if( buffer.read( reinterpret_cast( &rectHeader ), sz_rfbFramebufferUpdateRectHeader ) != sz_rfbFramebufferUpdateRectHeader ) + { + return false; + } + + rectHeader.encoding = qFromBigEndian( rectHeader.encoding ); + rectHeader.r.w = qFromBigEndian( rectHeader.r.w ); + rectHeader.r.h = qFromBigEndian( rectHeader.r.h ); + rectHeader.r.x = qFromBigEndian( rectHeader.r.x ); + rectHeader.r.y = qFromBigEndian( rectHeader.r.y ); + + if( rectHeader.encoding == rfbEncodingLastRect ) + { + break; + } + + if( handleRect( buffer, rectHeader ) == false ) + { + return false; + } + + if( isPseudoEncoding( rectHeader ) == false && + rectHeader.r.x+rectHeader.r.w <= m_framebufferWidth && + rectHeader.r.y+rectHeader.r.h <= m_framebufferHeight ) + { + updatedRegion += QRect( rectHeader.r.x, rectHeader.r.y, rectHeader.r.w, rectHeader.r.h ); + } + } + + m_lastUpdatedRect = updatedRegion.boundingRect(); + + // save as much data as we read by processing rects + return readMessage( static_cast( buffer.pos() ) ); +} + + + +bool VncClientProtocol::receiveColourMapEntriesMessage() +{ + rfbSetColourMapEntriesMsg message; + if( m_socket->peek( reinterpret_cast( &message ), sz_rfbSetColourMapEntriesMsg ) != sz_rfbSetColourMapEntriesMsg ) + { + return false; + } + + return readMessage( sz_rfbSetColourMapEntriesMsg + qFromBigEndian( message.nColours ) * 6 ); +} + + + + +bool VncClientProtocol::receiveBellMessage() +{ + return readMessage( sz_rfbBellMsg ); +} + + + +bool VncClientProtocol::receiveCutTextMessage() +{ + rfbServerCutTextMsg message; + if( m_socket->peek( reinterpret_cast( &message ), sz_rfbServerCutTextMsg ) != sz_rfbServerCutTextMsg ) + { + return false; + } + + return readMessage( sz_rfbServerCutTextMsg + static_cast( qFromBigEndian( message.length ) ) ); +} + + + +bool VncClientProtocol::receiveResizeFramebufferMessage() +{ + if( readMessage( sz_rfbResizeFrameBufferMsg ) ) + { + const auto msg = reinterpret_cast( m_lastMessage.constData() ); + m_framebufferWidth = qFromBigEndian( msg->framebufferWidth ); + m_framebufferHeight = qFromBigEndian( msg->framebufferHeigth ); + + return true; + } + + return false; +} + + + +bool VncClientProtocol::receiveXvpMessage() +{ + return readMessage( sz_rfbXvpMsg ); +} + + + +bool VncClientProtocol::readMessage( int size ) +{ + if( m_socket->bytesAvailable() < size ) + { + return false; + } + + auto message = m_socket->read( size ); + if( message.size() == size ) + { + m_lastMessage = message; + return true; + } + + vWarning() << "only received" << message.size() << "of" << size << "bytes"; + + return false; +} + + + +bool VncClientProtocol::handleRect( QBuffer& buffer, rfbFramebufferUpdateRectHeader rectHeader ) +{ + const uint width = rectHeader.r.w; + const uint height = rectHeader.r.h; + + const uint bytesPerPixel = m_pixelFormat.bitsPerPixel / 8; + const uint bytesPerRow = ( width + 7 ) / 8; + + switch( rectHeader.encoding ) + { + case rfbEncodingLastRect: + return true; + + case rfbEncodingXCursor: + return width * height == 0 || + ( buffer.read( sz_rfbXCursorColors ).size() == sz_rfbXCursorColors && + buffer.read( 2 * bytesPerRow * height ).size() == static_cast( 2 * bytesPerRow * height ) ); + + case rfbEncodingRichCursor: + return width * height == 0 || + ( buffer.read( width * height * bytesPerPixel ).size() == static_cast( width * height * bytesPerPixel ) && + buffer.read( bytesPerRow * height ).size() == static_cast( bytesPerRow * height ) ); + + case rfbEncodingSupportedMessages: + return buffer.read( sz_rfbSupportedMessages ).size() == sz_rfbSupportedMessages; + + case rfbEncodingSupportedEncodings: + case rfbEncodingServerIdentity: + // width = byte count + return buffer.read( width ).size() == static_cast( width ); + + case rfbEncodingRaw: + return buffer.read( width * height * bytesPerPixel ).size() == static_cast( width * height * bytesPerPixel ); + + case rfbEncodingCopyRect: + return buffer.read( sz_rfbCopyRect ).size() == sz_rfbCopyRect; + + case rfbEncodingRRE: + return handleRectEncodingRRE( buffer, bytesPerPixel ); + + case rfbEncodingCoRRE: + return handleRectEncodingCoRRE( buffer, bytesPerPixel ); + + case rfbEncodingHextile: + return handleRectEncodingHextile( buffer, rectHeader, bytesPerPixel ); + + case rfbEncodingUltra: + case rfbEncodingUltraZip: + case rfbEncodingZlib: + return handleRectEncodingZlib( buffer ); + + case rfbEncodingZRLE: + case rfbEncodingZYWRLE: + return handleRectEncodingZRLE( buffer ); + + case rfbEncodingPointerPos: + case rfbEncodingKeyboardLedState: + case rfbEncodingNewFBSize: + // no further data to read for this rect + return true; + + default: + vCritical() << "Unsupported rect encoding" << rectHeader.encoding; + m_socket->close(); + break; + } + + return false; +} + + + +bool VncClientProtocol::handleRectEncodingRRE( QBuffer& buffer, uint bytesPerPixel ) +{ + rfbRREHeader hdr; + + if( buffer.read( reinterpret_cast( &hdr ), sz_rfbRREHeader ) != sz_rfbRREHeader ) + { + return false; + } + + const auto rectDataSize = qFromBigEndian( hdr.nSubrects ) * ( bytesPerPixel + sz_rfbRectangle ); + const auto totalDataSize = static_cast( bytesPerPixel + rectDataSize ); + + return buffer.read( totalDataSize ).size() == totalDataSize; +} + + + +bool VncClientProtocol::handleRectEncodingCoRRE( QBuffer& buffer, uint bytesPerPixel ) +{ + rfbRREHeader hdr; + + if( buffer.read( reinterpret_cast( &hdr ), sz_rfbRREHeader ) != sz_rfbRREHeader ) + { + return false; + } + + const auto rectDataSize = qFromBigEndian( hdr.nSubrects ) * ( bytesPerPixel + 4 ); + const auto totalDataSize = static_cast( bytesPerPixel + rectDataSize ); + + return buffer.read( totalDataSize ).size() == totalDataSize; + +} + + + +bool VncClientProtocol::handleRectEncodingHextile( QBuffer& buffer, + const rfbFramebufferUpdateRectHeader rectHeader, + uint bytesPerPixel ) +{ + const uint rx = rectHeader.r.x; + const uint ry = rectHeader.r.y; + const uint rw = rectHeader.r.w; + const uint rh = rectHeader.r.h; + + for( uint y = ry; y < ry+rh; y += 16 ) + { + for( uint x = rx; x < rx+rw; x += 16 ) + { + uint w = 16, h = 16; + if( rx+rw - x < 16 ) + { + w = rx+rw - x; + } + if( ry+rh - y < 16 ) + { + h = ry+rh - y; + } + + uint8_t subEncoding = 0; + if( buffer.read( reinterpret_cast( &subEncoding ), 1 ) != 1 ) + { + return false; + } + + if( subEncoding & rfbHextileRaw ) + { + const auto dataSize = static_cast( w * h * bytesPerPixel ); + if( buffer.read( dataSize ).size() != dataSize ) + { + return false; + } + continue; + } + + if( subEncoding & rfbHextileBackgroundSpecified ) + { + if( buffer.read( bytesPerPixel ).size() != static_cast( bytesPerPixel ) ) + { + return false; + } + } + + if( subEncoding & rfbHextileForegroundSpecified ) + { + if( buffer.read( bytesPerPixel ).size() != static_cast( bytesPerPixel ) ) + { + return false; + } + } + + if( !( subEncoding & rfbHextileAnySubrects ) ) + { + continue; + } + + uint8_t nSubrects = 0; + if( buffer.read( reinterpret_cast( &nSubrects ), 1 ) != 1 ) + { + return false; + } + + int subRectDataSize = 0; + + if( subEncoding & rfbHextileSubrectsColoured ) + { + subRectDataSize = static_cast( nSubrects * ( 2 + bytesPerPixel ) ); + } + else + { + subRectDataSize = nSubrects * 2; + } + + if( buffer.read( subRectDataSize ).size() != subRectDataSize ) + { + return false; + } + } + } + + return true; +} + + + +bool VncClientProtocol::handleRectEncodingZlib( QBuffer& buffer ) +{ + rfbZlibHeader hdr; + + if( buffer.read( reinterpret_cast( &hdr ), sz_rfbZlibHeader ) != sz_rfbZlibHeader ) + { + return false; + } + + const auto n = qFromBigEndian( hdr.nBytes ); + + return buffer.read( n ).size() == static_cast( n ); +} + + + +bool VncClientProtocol::handleRectEncodingZRLE(QBuffer &buffer) +{ + rfbZRLEHeader hdr; + + if( buffer.read( reinterpret_cast( &hdr ), sz_rfbZRLEHeader ) != sz_rfbZRLEHeader ) + { + return false; + } + + const auto n = qFromBigEndian( hdr.length ); + + return buffer.read( n ).size() == static_cast( n ); +} + + + +bool VncClientProtocol::isPseudoEncoding( rfbFramebufferUpdateRectHeader header ) +{ + switch( header.encoding ) + { + case rfbEncodingSupportedEncodings: + case rfbEncodingSupportedMessages: + case rfbEncodingServerIdentity: + case rfbEncodingPointerPos: + case rfbEncodingKeyboardLedState: + case rfbEncodingNewFBSize: + return true; + default: + break; + } + + return false; +} diff --git a/core/src/VncConnection.cpp b/core/src/VncConnection.cpp new file mode 100644 index 0000000..5689f07 --- /dev/null +++ b/core/src/VncConnection.cpp @@ -0,0 +1,802 @@ +/* + * VncConnection.cpp - implementation of VncConnection class + * + * Copyright (c) 2008-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * code partly taken from KRDC / vncclientthread.cpp: + * Copyright (C) 2007-2008 Urs Wolfer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "PlatformNetworkFunctions.h" +#include "VeyonConfiguration.h" +#include "VncConnection.h" +#include "SocketDevice.h" +#include "VncEvents.h" + + +rfbBool VncConnection::hookInitFrameBuffer( rfbClient* client ) +{ + auto connection = static_cast( clientData( client, VncConnectionTag ) ); + if( connection ) + { + return connection->initFrameBuffer( client ); + } + + return false; +} + + + + +void VncConnection::hookUpdateFB( rfbClient* client, int x, int y, int w, int h ) +{ + auto connection = static_cast( clientData( client, VncConnectionTag ) ); + if( connection ) + { + Q_EMIT connection->imageUpdated( x, y, w, h ); + } +} + + + + +void VncConnection::hookFinishFrameBufferUpdate( rfbClient* client ) +{ + auto connection = static_cast( clientData( client, VncConnectionTag ) ); + if( connection ) + { + connection->finishFrameBufferUpdate(); + } +} + + + + +rfbBool VncConnection::hookHandleCursorPos( rfbClient* client, int x, int y ) +{ + auto connection = static_cast( clientData( client, VncConnectionTag ) ); + if( connection ) + { + Q_EMIT connection->cursorPosChanged( x, y ); + } + + return true; +} + + + + +void VncConnection::hookCursorShape( rfbClient* client, int xh, int yh, int w, int h, int bpp ) +{ + if( bpp != 4 ) + { + vWarning() << QThread::currentThreadId() << "bytes per pixel != 4"; + return; + } + + QImage alpha( client->rcMask, w, h, QImage::Format_Indexed8 ); + alpha.setColorTable( { qRgb(255,255,255), qRgb(0,0,0) } ); + + QPixmap cursorShape( QPixmap::fromImage( QImage( client->rcSource, w, h, QImage::Format_RGB32 ) ) ); + cursorShape.setMask( QBitmap::fromImage( alpha ) ); + + auto connection = static_cast( clientData( client, VncConnectionTag ) ); + if( connection ) + { + Q_EMIT connection->cursorShapeUpdated( cursorShape, xh, yh ); + } +} + + + +void VncConnection::hookCutText( rfbClient* client, const char* text, int textlen ) +{ + auto connection = static_cast( clientData( client, VncConnectionTag ) ); + const auto cutText = QString::fromUtf8( text, textlen ); + + if( connection && cutText.isEmpty() == false ) + { + Q_EMIT connection->gotCut( cutText ); + } +} + + + +void VncConnection::rfbClientLogDebug( const char* format, ... ) +{ + va_list args; + va_start( args, format ); + + static constexpr int MaxMessageLength = 256; + char message[MaxMessageLength]; + + vsnprintf( message, sizeof(message), format, args ); + message[MaxMessageLength-1] = 0; + + va_end(args); + + vDebug() << QThread::currentThreadId() << message; +} + + + + +void VncConnection::rfbClientLogNone( const char* format, ... ) +{ + Q_UNUSED(format); +} + + + +void VncConnection::framebufferCleanup( void* framebuffer ) +{ + delete[] static_cast( framebuffer ); +} + + + + +VncConnection::VncConnection( QObject* parent ) : + QThread( parent ), + m_state( State::Disconnected ), + m_framebufferState( FramebufferState::Invalid ), + m_controlFlags(), + m_client( nullptr ), + m_quality( Quality::Default ), + m_host(), + m_port( -1 ), + m_defaultPort( VeyonCore::config().veyonServerPort() ), + m_globalMutex(), + m_eventQueueMutex(), + m_updateIntervalSleeper(), + m_framebufferUpdateInterval( 0 ), + m_image(), + m_scaledScreen(), + m_scaledSize(), + m_imgLock() +{ + if( VeyonCore::config().useCustomVncConnectionSettings() ) + { + m_threadTerminationTimeout = VeyonCore::config().vncConnectionThreadTerminationTimeout(); + m_connectTimeout = VeyonCore::config().vncConnectionConnectTimeout(); + m_readTimeout = VeyonCore::config().vncConnectionReadTimeout(); + m_connectionRetryInterval = VeyonCore::config().vncConnectionRetryInterval(); + m_messageWaitTimeout = VeyonCore::config().vncConnectionMessageWaitTimeout(); + m_fastFramebufferUpdateInterval = VeyonCore::config().vncConnectionFastFramebufferUpdateInterval(); + m_framebufferUpdateWatchdogTimeout = VeyonCore::config().vncConnectionFramebufferUpdateWatchdogTimeout(); + m_socketKeepaliveIdleTime = VeyonCore::config().vncConnectionSocketKeepaliveIdleTime(); + m_socketKeepaliveInterval = VeyonCore::config().vncConnectionSocketKeepaliveInterval(); + m_socketKeepaliveCount = VeyonCore::config().vncConnectionSocketKeepaliveCount(); + } +} + + + +VncConnection::~VncConnection() +{ + stop(); + + if( isRunning() ) + { + vWarning() << "Waiting for VNC connection thread to finish."; + wait( m_threadTerminationTimeout ); + } + + if( isRunning() ) + { + vWarning() << "Terminating hanging VNC connection thread!"; + + terminate(); + wait(); + } +} + + + +void VncConnection::initLogging( bool debug ) +{ + if( debug ) + { + rfbClientLog = rfbClientLogDebug; + rfbClientErr = rfbClientLogDebug; + } + else + { + rfbClientLog = rfbClientLogNone; + rfbClientErr = rfbClientLogNone; + } +} + + + +QImage VncConnection::image() +{ + QReadLocker locker( &m_imgLock ); + return m_image; +} + + + +void VncConnection::restart() +{ + setControlFlag( ControlFlag::RestartConnection, true ); +} + + + +void VncConnection::stop() +{ + setClientData( VncConnectionTag, nullptr ); + + m_scaledScreen = {}; + + setControlFlag( ControlFlag::TerminateThread, true ); + + m_updateIntervalSleeper.wakeAll(); +} + + + +void VncConnection::stopAndDeleteLater() +{ + if( isRunning() ) + { + connect( this, &VncConnection::finished, this, &VncConnection::deleteLater ); + stop(); + } + else + { + deleteLater(); + } +} + + + +void VncConnection::setHost( const QString& host ) +{ + QMutexLocker locker( &m_globalMutex ); + m_host = host; + + QRegularExpressionMatch match; + if( + // if IPv6-mapped IPv4 address use plain IPv4 address as libvncclient cannot handle IPv6-mapped IPv4 addresses on Windows properly + ( match = QRegularExpression( QStringLiteral("^::[fF]{4}:(\\d+.\\d+.\\d+.\\d+)$") ).match( m_host ) ).hasMatch() || + ( match = QRegularExpression( QStringLiteral("^::[fF]{4}:(\\d+.\\d+.\\d+.\\d+):(\\d+)$") ).match( m_host ) ).hasMatch() || + ( match = QRegularExpression( QStringLiteral("^\\[::[fF]{4}:(\\d+.\\d+.\\d+.\\d+)\\]:(\\d+)$") ).match( m_host ) ).hasMatch() || + // any other IPv6 address with port number + ( match = QRegularExpression( QStringLiteral("^\\[([0-9a-fA-F:]+)\\]:(\\d+)$") ).match( m_host ) ).hasMatch() || + // irregular IPv6 address + port number specification where port number can be identified if > 9999 + ( match = QRegularExpression( QStringLiteral("^([0-9a-fA-F:]+):(\\d{5})$"), QRegularExpression::InvertedGreedinessOption ).match( m_host ) ).hasMatch() || + // any other notation with trailing port number + ( match = QRegularExpression( QStringLiteral("^([^:]+):(\\d+)$") ).match( m_host ) ).hasMatch() + ) + { + const auto matchedHost = match.captured( 1 ); + if( matchedHost.isEmpty() == false ) + { + m_host = matchedHost; + } + + const auto port = match.captured( 2 ).toInt(); + if( port > 0 ) + { + m_port = port; + } + } +} + + + +void VncConnection::setPort( int port ) +{ + if( port >= 0 ) + { + QMutexLocker locker( &m_globalMutex ); + m_port = port; + } +} + + + +void VncConnection::setServerReachable() +{ + setControlFlag( ControlFlag::ServerReachable, true ); +} + + + +void VncConnection::setScaledSize( QSize s ) +{ + QMutexLocker globalLock( &m_globalMutex ); + + if( m_scaledSize != s ) + { + m_scaledSize = s; + setControlFlag( ControlFlag::ScaledScreenNeedsUpdate, true ); + } +} + + + +QImage VncConnection::scaledScreen() +{ + rescaleScreen(); + return m_scaledScreen; +} + + + +void VncConnection::setFramebufferUpdateInterval( int interval ) +{ + m_framebufferUpdateInterval = interval; +} + + + +void VncConnection::rescaleScreen() +{ + if( hasValidFramebuffer() == false || m_scaledSize.isNull() ) + { + m_scaledScreen = {}; + return; + } + + if( isControlFlagSet( ControlFlag::ScaledScreenNeedsUpdate ) == false ) + { + return; + } + + QReadLocker locker( &m_imgLock ); + + if( m_image.size().isValid() == false ) + { + return; + } + + m_scaledScreen = m_image.scaled( m_scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); + + setControlFlag( ControlFlag::ScaledScreenNeedsUpdate, false ); +} + + + +void* VncConnection::clientData( rfbClient* client, int tag ) +{ + if( client ) + { + return rfbClientGetClientData( client, reinterpret_cast( tag ) ); + } + + return nullptr; +} + + + +void VncConnection::setClientData( int tag, void* data ) +{ + QMutexLocker globalLock( &m_globalMutex ); + + if( m_client ) + { + rfbClientSetClientData( m_client, reinterpret_cast( tag ), data ); + } +} + + + +void VncConnection::run() +{ + while( isControlFlagSet( ControlFlag::TerminateThread ) == false ) + { + establishConnection(); + handleConnection(); + closeConnection(); + } +} + + + +void VncConnection::establishConnection() +{ + QMutex sleeperMutex; + + setState( State::Connecting ); + setControlFlag( ControlFlag::RestartConnection, false ); + + m_framebufferState = FramebufferState::Invalid; + + while( isControlFlagSet( ControlFlag::TerminateThread ) == false && + state() != State::Connected ) // try to connect as long as the server allows + { + m_client = rfbGetClient( RfbBitsPerSample, RfbSamplesPerPixel, RfbBytesPerPixel ); + m_client->MallocFrameBuffer = hookInitFrameBuffer; + m_client->canHandleNewFBSize = true; + m_client->GotFrameBufferUpdate = hookUpdateFB; + m_client->FinishedFrameBufferUpdate = hookFinishFrameBufferUpdate; + m_client->HandleCursorPos = hookHandleCursorPos; + m_client->GotCursorShape = hookCursorShape; + m_client->GotXCutText = hookCutText; + m_client->connectTimeout = m_connectTimeout / 1000; + m_client->readTimeout = m_readTimeout / 1000; + setClientData( VncConnectionTag, this ); + + Q_EMIT connectionPrepared(); + + m_globalMutex.lock(); + + if( m_port < 0 ) // use default port? + { + m_client->serverPort = m_defaultPort; + } + else + { + m_client->serverPort = m_port; + } + + free( m_client->serverHost ); + m_client->serverHost = strdup( m_host.toUtf8().constData() ); + + m_globalMutex.unlock(); + + setControlFlag( ControlFlag::ServerReachable, false ); + + if( rfbInitClient( m_client, nullptr, nullptr ) && + isControlFlagSet( ControlFlag::TerminateThread ) == false ) + { + m_framebufferUpdateWatchdog.restart(); + + Q_EMIT connectionEstablished(); + + VeyonCore::platform().networkFunctions(). + configureSocketKeepalive( static_cast( m_client->sock ), true, + m_socketKeepaliveIdleTime, m_socketKeepaliveInterval, m_socketKeepaliveCount ); + + setState( State::Connected ); + } + else + { + // rfbInitClient() calls rfbClientCleanup() when failed + m_client = nullptr; + + // do not sleep when already requested to stop + if( isControlFlagSet( ControlFlag::TerminateThread ) ) + { + break; + } + + // guess reason why connection failed + if( isControlFlagSet( ControlFlag::ServerReachable ) == false ) + { + if( VeyonCore::platform().networkFunctions().ping( m_host ) == false ) + { + setState( State::HostOffline ); + } + else + { + setState( State::ServerNotRunning ); + } + } + else if( m_framebufferState == FramebufferState::Invalid ) + { + setState( State::AuthenticationFailed ); + } + else + { + // failed for an unknown reason + setState( State::ConnectionFailed ); + } + + // wait a bit until next connect + sleeperMutex.lock(); + if( m_framebufferUpdateInterval > 0 ) + { + m_updateIntervalSleeper.wait( &sleeperMutex, QDeadlineTimer( m_framebufferUpdateInterval ) ); + } + else + { + // default: retry every second + m_updateIntervalSleeper.wait( &sleeperMutex, QDeadlineTimer( m_connectionRetryInterval ) ); + } + sleeperMutex.unlock(); + } + } +} + + + +void VncConnection::handleConnection() +{ + QMutex sleeperMutex; + QElapsedTimer loopTimer; + + while( state() == State::Connected && + isControlFlagSet( ControlFlag::TerminateThread ) == false && + isControlFlagSet( ControlFlag::RestartConnection ) == false ) + { + loopTimer.start(); + + const int i = WaitForMessage( m_client, m_messageWaitTimeout ); + if( isControlFlagSet( ControlFlag::TerminateThread ) || i < 0 ) + { + break; + } + else if( i ) + { + // handle all available messages + bool handledOkay = true; + do { + handledOkay &= HandleRFBServerMessage( m_client ); + } while( handledOkay && WaitForMessage( m_client, 0 ) ); + + if( handledOkay == false ) + { + break; + } + } + + sendEvents(); + + const auto remainingUpdateInterval = m_framebufferUpdateInterval - loopTimer.elapsed(); + + if( m_framebufferState == FramebufferState::Initialized || + m_framebufferUpdateWatchdog.elapsed() >= qMax( 2*m_framebufferUpdateInterval, m_framebufferUpdateWatchdogTimeout ) ) + { + SendFramebufferUpdateRequest( m_client, 0, 0, m_client->width, m_client->height, false ); + + const auto remainingFastUpdateInterval = m_fastFramebufferUpdateInterval - loopTimer.elapsed(); + + sleeperMutex.lock(); + m_updateIntervalSleeper.wait( &sleeperMutex, QDeadlineTimer( remainingFastUpdateInterval ) ); + sleeperMutex.unlock(); + } + else if( m_framebufferState == FramebufferState::Valid && + remainingUpdateInterval > 0 && + isControlFlagSet( ControlFlag::TerminateThread ) == false ) + { + sleeperMutex.lock(); + m_updateIntervalSleeper.wait( &sleeperMutex, QDeadlineTimer( remainingUpdateInterval ) ); + sleeperMutex.unlock(); + } + + sendEvents(); + } +} + + + +void VncConnection::closeConnection() +{ + if( m_client ) + { + rfbClientCleanup( m_client ); + m_client = nullptr; + } + + setState( State::Disconnected ); +} + + + +void VncConnection::setState( State state ) +{ + if( m_state.exchange( state ) != state ) + { + Q_EMIT stateChanged(); + } +} + + + +void VncConnection::setControlFlag( VncConnection::ControlFlag flag, bool on ) +{ + if( on ) + { + m_controlFlags |= static_cast( flag ); + } + else + { + m_controlFlags &= ~static_cast( flag ); + } +} + + + +bool VncConnection::isControlFlagSet( VncConnection::ControlFlag flag ) +{ + return m_controlFlags & static_cast( flag ); +} + + + + +bool VncConnection::initFrameBuffer( rfbClient* client ) +{ + if( client->format.bitsPerPixel != RfbBitsPerSample * RfbBytesPerPixel ) + { + vCritical() << "Bits per pixel does not match" << client->format.bitsPerPixel; + return false; + } + + const auto pixelCount = static_cast( client->width ) * client->height; + + client->frameBuffer = reinterpret_cast( new RfbPixel[pixelCount] ); + + memset( client->frameBuffer, '\0', pixelCount*RfbBytesPerPixel ); + + // initialize framebuffer image which just wraps the allocated memory and ensures cleanup after last + // image copy using the framebuffer gets destroyed + m_imgLock.lockForWrite(); + m_image = QImage( client->frameBuffer, client->width, client->height, QImage::Format_RGB32, framebufferCleanup, client->frameBuffer ); + m_imgLock.unlock(); + + // set up pixel format according to QImage + client->format.redShift = 16; + client->format.greenShift = 8; + client->format.blueShift = 0; + client->format.redMax = 0xff; + client->format.greenMax = 0xff; + client->format.blueMax = 0xff; + + client->appData.encodingsString = "zrle ultra copyrect hextile zlib corre rre raw"; + client->appData.useRemoteCursor = false; + client->appData.compressLevel = 0; + client->appData.useBGR233 = false; + client->appData.qualityLevel = 9; + client->appData.enableJPEG = false; + + switch( m_quality ) + { + case Quality::Screenshot: + // make sure to use lossless raw encoding + client->appData.encodingsString = "raw"; + break; + case Quality::RemoteControl: + client->appData.useRemoteCursor = true; + break; + case Quality::Thumbnail: + client->appData.compressLevel = 9; + client->appData.qualityLevel = 5; + client->appData.enableJPEG = true; + break; + default: + break; + } + + m_framebufferState = FramebufferState::Initialized; + + Q_EMIT framebufferSizeChanged( client->width, client->height ); + + return true; +} + + + +void VncConnection::finishFrameBufferUpdate() +{ + m_framebufferUpdateWatchdog.restart(); + + m_framebufferState = FramebufferState::Valid; + setControlFlag( ControlFlag::ScaledScreenNeedsUpdate, true ); + + Q_EMIT framebufferUpdateComplete(); +} + + + +void VncConnection::sendEvents() +{ + m_eventQueueMutex.lock(); + + while( m_eventQueue.isEmpty() == false ) + { + auto event = m_eventQueue.dequeue(); + + // unlock the queue mutex during the runtime of ClientEvent::fire() + m_eventQueueMutex.unlock(); + + if( isControlFlagSet( ControlFlag::TerminateThread ) == false ) + { + event->fire( m_client ); + } + + delete event; + + // and lock it again + m_eventQueueMutex.lock(); + } + + m_eventQueueMutex.unlock(); +} + + + +void VncConnection::enqueueEvent( VncEvent* event, bool wake ) +{ + if( state() != State::Connected ) + { + return; + } + + m_eventQueueMutex.lock(); + m_eventQueue.enqueue( event ); + m_eventQueueMutex.unlock(); + + if( wake ) + { + m_updateIntervalSleeper.wakeAll(); + } +} + + + +bool VncConnection::isEventQueueEmpty() +{ + QMutexLocker lock( &m_eventQueueMutex ); + return m_eventQueue.isEmpty(); +} + + + +void VncConnection::mouseEvent( int x, int y, int buttonMask ) +{ + enqueueEvent( new VncPointerEvent( x, y, buttonMask ), true ); +} + + + +void VncConnection::keyEvent( unsigned int key, bool pressed ) +{ + enqueueEvent( new VncKeyEvent( key, pressed ), true ); +} + + + +void VncConnection::clientCut( const QString& text ) +{ + enqueueEvent( new VncClientCutEvent( text ), true ); +} + + + +qint64 VncConnection::libvncClientDispatcher( char* buffer, const qint64 bytes, + SocketDevice::SocketOperation operation, void* user ) +{ + auto client = static_cast( user ); + switch( operation ) + { + case SocketDevice::SocketOpRead: + return ReadFromRFBServer( client, buffer, static_cast( bytes ) ) ? bytes : 0; + + case SocketDevice::SocketOpWrite: + return WriteToRFBServer( client, buffer, static_cast( bytes ) ) ? bytes : 0; + } + + return 0; +} diff --git a/core/src/VncEvents.cpp b/core/src/VncEvents.cpp new file mode 100644 index 0000000..e67142b --- /dev/null +++ b/core/src/VncEvents.cpp @@ -0,0 +1,70 @@ +/* + * VncEvents.cpp - implementation of VNC event classes + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "VncEvents.h" + + +VncKeyEvent::VncKeyEvent( unsigned int key, bool pressed ) : + m_key( key ), + m_pressed( pressed ) +{ +} + + +void VncKeyEvent::fire( rfbClient* client ) +{ + SendKeyEvent( client, m_key, m_pressed ); +} + + + +VncPointerEvent::VncPointerEvent(int x, int y, int buttonMask) : + m_x( x ), + m_y( y ), + m_buttonMask( buttonMask ) +{ +} + + + +void VncPointerEvent::fire(rfbClient *client) +{ + SendPointerEvent( client, m_x, m_y, m_buttonMask ); +} + + + +VncClientCutEvent::VncClientCutEvent( const QString& text ) : + m_text( text.toUtf8() ) +{ +} + + + +void VncClientCutEvent::fire( rfbClient* client ) +{ + SendClientCutText( client, m_text.data(), m_text.size() ); // clazy:exclude=detaching-member +} diff --git a/core/src/VncFeatureMessageEvent.cpp b/core/src/VncFeatureMessageEvent.cpp new file mode 100644 index 0000000..ea298d8 --- /dev/null +++ b/core/src/VncFeatureMessageEvent.cpp @@ -0,0 +1,48 @@ +/* + * FeatureMessageEvent.cpp - implementation of FeatureMessageEvent + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "SocketDevice.h" +#include "VncConnection.h" +#include "VncFeatureMessageEvent.h" + + +VncFeatureMessageEvent::VncFeatureMessageEvent( const FeatureMessage& featureMessage ) : + m_featureMessage( featureMessage ) +{ +} + + + +void VncFeatureMessageEvent::fire( rfbClient* client ) +{ + vDebug() << "sending message" << m_featureMessage.featureUid() + << "command" << m_featureMessage.command() + << "arguments" << m_featureMessage.arguments(); + + SocketDevice socketDevice( VncConnection::libvncClientDispatcher, client ); + const char messageType = FeatureMessage::RfbMessageType; + socketDevice.write( &messageType, sizeof(messageType) ); + + m_featureMessage.send( &socketDevice ); +} diff --git a/core/src/VncServerProtocol.cpp b/core/src/VncServerProtocol.cpp new file mode 100644 index 0000000..831518f --- /dev/null +++ b/core/src/VncServerProtocol.cpp @@ -0,0 +1,328 @@ +/* + * VncServerProtocol.cpp - implementation of the VncServerProtocol class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "rfb/rfbproto.h" + +#include +#include + +#include "AuthenticationCredentials.h" +#include "VariantArrayMessage.h" +#include "VncServerClient.h" +#include "VncServerProtocol.h" + + + +VncServerProtocol::VncServerProtocol( QTcpSocket* socket, + VncServerClient* client ) : + m_socket( socket ), + m_client( client ), + m_serverInitMessage() +{ + m_client->setHostAddress( m_socket->peerAddress().toString() ); + m_client->setAccessControlState( VncServerClient::AccessControlState::Init ); +} + + + +VncServerProtocol::State VncServerProtocol::state() const +{ + return m_client->protocolState(); +} + + + +void VncServerProtocol::start() +{ + if( state() == Disconnected ) + { + char protocol[sz_rfbProtocolVersionMsg+1]; // Flawfinder: ignore + + sprintf( protocol, rfbProtocolVersionFormat, 3, 8 ); // Flawfinder: ignore + + m_socket->write( protocol, sz_rfbProtocolVersionMsg ); + + setState( Protocol ); + } +} + + + +bool VncServerProtocol::read() +{ + switch( state() ) + { + case Protocol: + return readProtocol(); + + case SecurityInit: + return receiveSecurityTypeResponse(); + + case AuthenticationTypes: + return receiveAuthenticationTypeResponse(); + + case Authenticating: + return receiveAuthenticationMessage(); + + case AccessControl: + return processAccessControl(); + + case FramebufferInit: + return processFramebufferInit(); + + case Close: + vDebug() << "closing connection per protocol state"; + m_socket->close(); + break; + + default: + break; + } + + return false; +} + + + +void VncServerProtocol::setState( VncServerProtocol::State state ) +{ + m_client->setProtocolState( state ); +} + + + +bool VncServerProtocol::readProtocol() +{ + if( m_socket->bytesAvailable() == sz_rfbProtocolVersionMsg ) + { + const auto protocol = m_socket->read( sz_rfbProtocolVersionMsg ); + + if( protocol.size() != sz_rfbProtocolVersionMsg ) + { + vCritical() << "protocol initialization failed"; + m_socket->close(); + return false; + } + + QRegExp protocolRX( QStringLiteral("RFB (\\d\\d\\d)\\.(\\d\\d\\d)\n") ); + + if( protocolRX.indexIn( QString::fromUtf8( protocol ) ) != 0 ) + { + vCritical() << "invalid protocol version"; + m_socket->close(); + return false; + } + + setState( SecurityInit ); + + return sendSecurityTypes(); + } + + return false; +} + + + +bool VncServerProtocol::sendSecurityTypes() +{ + // send list of supported security types + const char securityTypeList[2] = { 1, rfbSecTypeVeyon }; // Flawfinder: ignore + m_socket->write( securityTypeList, sizeof( securityTypeList ) ); + + return true; +} + + + +bool VncServerProtocol::receiveSecurityTypeResponse() +{ + if( m_socket->bytesAvailable() >= 1 ) + { + char chosenSecurityType = 0; + + if( m_socket->read( &chosenSecurityType, sizeof(chosenSecurityType) ) != sizeof(chosenSecurityType) || + chosenSecurityType != rfbSecTypeVeyon ) + { + vCritical() << "protocol initialization failed"; + m_socket->close(); + + return false; + } + + setState( AuthenticationTypes ); + + return sendAuthenticationTypes(); + } + + return false; +} + + + +bool VncServerProtocol::sendAuthenticationTypes() +{ + const auto authTypes = supportedAuthTypes(); + + VariantArrayMessage message( m_socket ); + message.write( authTypes.count() ); + + for( auto authType : authTypes ) + { + message.write( authType ); + } + + return message.send(); +} + + + +bool VncServerProtocol::receiveAuthenticationTypeResponse() +{ + VariantArrayMessage message( m_socket ); + + if( message.isReadyForReceive() && message.receive() ) + { + const auto chosenAuthType = QVariantHelper::value( message.read() ); + + if( supportedAuthTypes().contains( chosenAuthType ) == false ) + { + vCritical() << "unsupported authentication type chosen by client!"; + m_socket->close(); + + return false; + } + + if( chosenAuthType == RfbVeyonAuth::None ) + { + vWarning() << "skipping authentication."; + setState( AccessControl ); + return true; + } + + const auto username = message.read().toString(); + + m_client->setAuthType( chosenAuthType ); + m_client->setUsername( username ); + + setState( Authenticating ); + + // send auth ack message + VariantArrayMessage( m_socket ).send(); + + // init authentication + VariantArrayMessage dummyMessage( m_socket ); + processAuthentication( dummyMessage ); + } + + return false; +} + + + +bool VncServerProtocol::receiveAuthenticationMessage() +{ + VariantArrayMessage message( m_socket ); + + if( message.isReadyForReceive() && message.receive() ) + { + return processAuthentication( message ); + } + + return false; +} + + + +bool VncServerProtocol::processAuthentication( VariantArrayMessage& message ) +{ + processAuthenticationMessage( message ); + + switch( m_client->authState() ) + { + case VncServerClient::AuthState::Successful: + { + const auto authResult = qToBigEndian(rfbVncAuthOK); + m_socket->write( reinterpret_cast( &authResult ), sizeof(authResult) ); + + setState( AccessControl ); + return true; + } + + case VncServerClient::AuthState::Failed: + vCritical() << "authentication failed - closing connection"; + m_socket->close(); + + return false; + + default: + break; + } + + return false; +} + + + +bool VncServerProtocol::processAccessControl() +{ + performAccessControl(); + + switch( m_client->accessControlState() ) + { + case VncServerClient::AccessControlState::Successful: + setState( FramebufferInit ); + return true; + + case VncServerClient::AccessControlState::Pending: + case VncServerClient::AccessControlState::Waiting: + break; + + default: + vCritical() << "access control failed - closing connection"; + m_socket->close(); + break; + } + + return false; +} + + + +bool VncServerProtocol::processFramebufferInit() +{ + if( m_socket->bytesAvailable() >= sz_rfbClientInitMsg && + m_serverInitMessage.isEmpty() == false ) + { + // just read client init message without further evaluation + m_socket->read( sz_rfbClientInitMsg ); + + m_socket->write( m_serverInitMessage ); + + setState( Running ); + + return true; + } + + return false; +} diff --git a/core/src/VncView.cpp b/core/src/VncView.cpp new file mode 100644 index 0000000..81ae9ad --- /dev/null +++ b/core/src/VncView.cpp @@ -0,0 +1,649 @@ +/* + * VncView.cpp - abstract base for all VNC views + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include +#include +#include +#include + +#include "PlatformInputDeviceFunctions.h" +#include "KeyboardShortcutTrapper.h" +#include "VncConnection.h" +#include "VncView.h" + + +VncView::VncView( VncConnection* connection ) : + m_connection( connection ), + m_framebufferSize( connection->image().size() ), + m_keyboardShortcutTrapper( VeyonCore::platform().inputDeviceFunctions().createKeyboardShortcutTrapper( nullptr ) ) +{ + // handle/forward trapped keyboard shortcuts + QObject::connect( m_keyboardShortcutTrapper, &KeyboardShortcutTrapper::shortcutTrapped, + m_keyboardShortcutTrapper, [this]( KeyboardShortcutTrapper::Shortcut shortcut ) { + handleShortcut( shortcut ); + } ); +} + + + +VncView::~VncView() +{ + delete m_keyboardShortcutTrapper; +} + + + +QSize VncView::scaledSize() const +{ + if( isScaledView() == false ) + { + return effectiveFramebufferSize(); + } + + return effectiveFramebufferSize().scaled( viewSize(), Qt::KeepAspectRatio ); +} + + + +QSize VncView::effectiveFramebufferSize() const +{ + const auto viewportSize = m_viewport.size(); + + if( viewportSize.isEmpty() == false ) + { + return viewportSize; + } + + return m_framebufferSize; +} + + + +void VncView::setViewOnly( bool viewOnly ) +{ + if( viewOnly == m_viewOnly ) + { + return; + } + m_viewOnly = viewOnly; + + if( m_viewOnly ) + { + m_keyboardShortcutTrapper->setEnabled( false ); + updateLocalCursor(); + } + else + { + updateLocalCursor(); + m_keyboardShortcutTrapper->setEnabled( true ); + } +} + + + +void VncView::sendShortcut( VncView::Shortcut shortcut ) +{ + if( viewOnly() ) + { + return; + } + + unpressModifiers(); + + + switch( shortcut ) + { + case ShortcutCtrlAltDel: + pressKey( XK_Control_L ); + pressKey( XK_Alt_L ); + pressKey( XK_Delete ); + unpressKey( XK_Delete ); + unpressKey( XK_Alt_L ); + unpressKey( XK_Control_L ); + break; + case ShortcutCtrlEscape: + pressKey( XK_Control_L ); + pressKey( XK_Escape ); + unpressKey( XK_Escape ); + unpressKey( XK_Control_L ); + break; + case ShortcutAltTab: + pressKey( XK_Alt_L ); + pressKey( XK_Tab ); + unpressKey( XK_Tab ); + unpressKey( XK_Alt_L ); + break; + case ShortcutAltF4: + pressKey( XK_Alt_L ); + pressKey( XK_F4 ); + unpressKey( XK_F4 ); + unpressKey( XK_Alt_L ); + break; + case ShortcutWinTab: + pressKey( XK_Meta_L ); + pressKey( XK_Tab ); + unpressKey( XK_Tab ); + unpressKey( XK_Meta_L ); + break; + case ShortcutWin: + pressKey( XK_Super_L ); + unpressKey( XK_Super_L ); + break; + case ShortcutMenu: + pressKey( XK_Menu ); + unpressKey( XK_Menu ); + break; + case ShortcutAltCtrlF1: + pressKey( XK_Control_L ); + pressKey( XK_Alt_L ); + pressKey( XK_F1 ); + unpressKey( XK_F1 ); + unpressKey( XK_Alt_L ); + unpressKey( XK_Control_L ); + break; + default: + vWarning() << "unknown shortcut" << static_cast( shortcut ); + break; + } +} + + + +bool VncView::isScaledView() const +{ + return viewSize().width() < effectiveFramebufferSize().width() || + viewSize().height() < effectiveFramebufferSize().height(); +} + + + +qreal VncView::scaleFactor() const +{ + if( isScaledView() ) + { + return qreal( scaledSize().width() ) / effectiveFramebufferSize().width(); + } + + return 1; +} + + + +QPoint VncView::mapToFramebuffer( QPoint pos ) +{ + if( effectiveFramebufferSize().isEmpty() ) + { + return { 0, 0 }; + } + + return { pos.x() * effectiveFramebufferSize().width() / scaledSize().width() + viewport().x(), + pos.y() * effectiveFramebufferSize().height() / scaledSize().height() + viewport().y() }; +} + + + +QRect VncView::mapFromFramebuffer( QRect r ) +{ + if( effectiveFramebufferSize().isEmpty() ) + { + return {}; + } + + r.translate( -viewport().x(), -viewport().y() ); + + const auto dx = scaledSize().width() / qreal( effectiveFramebufferSize().width() ); + const auto dy = scaledSize().height() / qreal( effectiveFramebufferSize().height() ); + + return { int(r.x()*dx), int(r.y()*dy), + int(r.width()*dx), int(r.height()*dy) }; +} + + + +void VncView::updateCursorPos( int x, int y ) +{ + if( viewOnly() ) + { + if( m_cursorShape.isNull() == false ) + { + updatePaintedCursor(); + } + m_cursorPos = { x, y }; + if( m_cursorShape.isNull() == false ) + { + updatePaintedCursor(); + } + } +} + + + +void VncView::updateCursorShape( const QPixmap& cursorShape, int xh, int yh ) +{ + const auto scale = scaleFactor(); + + m_cursorHot = { int( xh*scale ), int( yh*scale ) }; + m_cursorShape = cursorShape.scaled( int( cursorShape.width()*scale ), + int( cursorShape.height()*scale ), + Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); + + if( viewOnly() ) + { + updateView( m_cursorPos.x(), m_cursorPos.y(), m_cursorShape.width(), m_cursorShape.height() ); + } + + updateLocalCursor(); +} + + + +void VncView::updateFramebufferSize( int w, int h ) +{ + m_framebufferSize = QSize( w, h ); +} + + + +void VncView::updateImage( int x, int y, int w, int h ) +{ + x -= viewport().x(); + y -= viewport().y(); + + const auto scale = scaleFactor(); + updateView( qMax( 0, qFloor( x*scale - 1 ) ), qMax( 0, qFloor( y*scale - 1 ) ), + qCeil( w*scale + 2 ), qCeil( h*scale + 2 ) ); +} + + + +void VncView::unpressModifiers() +{ + const auto keys = m_mods.keys(); + for( auto key : keys ) + { + m_connection->keyEvent( key, false ); + } + m_mods.clear(); +} + + + +void VncView::handleShortcut( KeyboardShortcutTrapper::Shortcut shortcut ) +{ + unsigned int key = 0; + + switch( shortcut ) + { + case KeyboardShortcutTrapper::SuperKeyDown: + m_mods[XK_Super_L] = true; + break; + case KeyboardShortcutTrapper::SuperKeyUp: + m_mods.remove( XK_Super_L ); + break; + case KeyboardShortcutTrapper::AltTab: key = XK_Tab; break; + case KeyboardShortcutTrapper::AltEsc: key = XK_Escape; break; + case KeyboardShortcutTrapper::AltSpace: key = XK_KP_Space; break; + case KeyboardShortcutTrapper::AltF4: key = XK_F4; break; + case KeyboardShortcutTrapper::CtrlEsc: key = XK_Escape; break; + default: + break; + } + + if( key ) + { + m_connection->keyEvent( key, true ); + m_connection->keyEvent( key, false ); + } +} + + + +bool VncView::handleEvent( QEvent* event ) +{ + switch( event->type() ) + { + case QEvent::KeyPress: + case QEvent::KeyRelease: + keyEventHandler( dynamic_cast( event ) ); + return true; + case QEvent::HoverMove: + hoverEventHandler( dynamic_cast( event ) ); + return true; + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + mouseEventHandler( dynamic_cast( event ) ); + return true; + case QEvent::Wheel: + wheelEventHandler( dynamic_cast( event ) ); + return true; + default: + break; + } + + return false; +} + + + +void VncView::hoverEventHandler( QHoverEvent* event ) +{ + if( event && m_viewOnly == false ) + { + const auto pos = mapToFramebuffer( event->pos() ); + m_connection->mouseEvent( pos.x(), pos.y(), m_buttonMask ); + } +} + + + +void VncView::keyEventHandler( QKeyEvent* event ) +{ + if( event == nullptr ) + { + return; + } + + const auto pressed = event->type() == QEvent::KeyPress; + +#ifdef Q_OS_LINUX + // on Linux/X11 native key codes are equal to the ones used by RFB protocol + unsigned int key = event->nativeVirtualKey(); + + // we do not handle Key_Backtab separately as the Shift-modifier + // is already enabled + if( event->key() == Qt::Key_Backtab ) + { + key = XK_Tab; + } + +#else + // hmm, either Win32-platform or too old Qt so we have to handle and + // translate Qt-key-codes to X-keycodes + unsigned int key = 0; + switch( event->key() ) + { + // modifiers are handled separately + case Qt::Key_Shift: key = XK_Shift_L; break; + case Qt::Key_Control: key = XK_Control_L; break; + case Qt::Key_Meta: key = XK_Meta_L; break; + case Qt::Key_Alt: key = XK_Alt_L; break; + case Qt::Key_Escape: key = XK_Escape; break; + case Qt::Key_Tab: key = XK_Tab; break; + case Qt::Key_Backtab: key = XK_Tab; break; + case Qt::Key_Backspace: key = XK_BackSpace; break; + case Qt::Key_Return: key = XK_Return; break; + case Qt::Key_Insert: key = XK_Insert; break; + case Qt::Key_Delete: key = XK_Delete; break; + case Qt::Key_Pause: key = XK_Pause; break; + case Qt::Key_Print: key = XK_Print; break; + case Qt::Key_Home: key = XK_Home; break; + case Qt::Key_End: key = XK_End; break; + case Qt::Key_Left: key = XK_Left; break; + case Qt::Key_Up: key = XK_Up; break; + case Qt::Key_Right: key = XK_Right; break; + case Qt::Key_Down: key = XK_Down; break; + case Qt::Key_PageUp: key = XK_Prior; break; + case Qt::Key_PageDown: key = XK_Next; break; + case Qt::Key_CapsLock: key = XK_Caps_Lock; break; + case Qt::Key_NumLock: key = XK_Num_Lock; break; + case Qt::Key_ScrollLock: key = XK_Scroll_Lock; break; + case Qt::Key_Super_L: key = XK_Super_L; break; + case Qt::Key_Super_R: key = XK_Super_R; break; + case Qt::Key_Menu: key = XK_Menu; break; + case Qt::Key_Hyper_L: key = XK_Hyper_L; break; + case Qt::Key_Hyper_R: key = XK_Hyper_R; break; + case Qt::Key_Help: key = XK_Help; break; + case Qt::Key_AltGr: key = XK_ISO_Level3_Shift; break; + case Qt::Key_Multi_key: key = XK_Multi_key; break; + case Qt::Key_SingleCandidate: key = XK_SingleCandidate; break; + case Qt::Key_MultipleCandidate: key = XK_MultipleCandidate; break; + case Qt::Key_PreviousCandidate: key = XK_PreviousCandidate; break; + case Qt::Key_Mode_switch: key = XK_Mode_switch; break; + case Qt::Key_Kanji: key = XK_Kanji; break; + case Qt::Key_Muhenkan: key = XK_Muhenkan; break; + case Qt::Key_Henkan: key = XK_Henkan; break; + case Qt::Key_Romaji: key = XK_Romaji; break; + case Qt::Key_Hiragana: key = XK_Hiragana; break; + case Qt::Key_Katakana: key = XK_Katakana; break; + case Qt::Key_Hiragana_Katakana: key = XK_Hiragana_Katakana; break; + case Qt::Key_Zenkaku: key = XK_Zenkaku; break; + case Qt::Key_Hankaku: key = XK_Hankaku; break; + case Qt::Key_Zenkaku_Hankaku: key = XK_Zenkaku_Hankaku; break; + case Qt::Key_Touroku: key = XK_Touroku; break; + case Qt::Key_Massyo: key = XK_Massyo; break; + case Qt::Key_Kana_Lock: key = XK_Kana_Lock; break; + case Qt::Key_Kana_Shift: key = XK_Kana_Shift; break; + case Qt::Key_Eisu_Shift: key = XK_Eisu_Shift; break; + case Qt::Key_Eisu_toggle: key = XK_Eisu_toggle; break; + case Qt::Key_Hangul: key = XK_Hangul; break; + case Qt::Key_Hangul_Start: key = XK_Hangul_Start; break; + case Qt::Key_Hangul_End: key = XK_Hangul_End; break; + case Qt::Key_Hangul_Hanja: key = XK_Hangul_Hanja; break; + case Qt::Key_Hangul_Jamo: key = XK_Hangul_Jamo; break; + case Qt::Key_Hangul_Romaja: key = XK_Hangul_Romaja; break; + case Qt::Key_Hangul_Jeonja: key = XK_Hangul_Jeonja; break; + case Qt::Key_Hangul_Banja: key = XK_Hangul_Banja; break; + case Qt::Key_Hangul_PreHanja: key = XK_Hangul_PreHanja; break; + case Qt::Key_Hangul_PostHanja: key = XK_Hangul_PostHanja; break; + case Qt::Key_Hangul_Special: key = XK_Hangul_Special; break; + case Qt::Key_Dead_Grave: key = XK_dead_grave; break; + case Qt::Key_Dead_Acute: key = XK_dead_acute; break; + case Qt::Key_Dead_Circumflex: key = XK_dead_circumflex; break; + case Qt::Key_Dead_Tilde: key = XK_dead_tilde; break; + case Qt::Key_Dead_Macron: key = XK_dead_macron; break; + case Qt::Key_Dead_Breve: key = XK_dead_breve; break; + case Qt::Key_Dead_Abovedot: key = XK_dead_abovedot; break; + case Qt::Key_Dead_Diaeresis: key = XK_dead_diaeresis; break; + case Qt::Key_Dead_Abovering: key = XK_dead_abovering; break; + case Qt::Key_Dead_Doubleacute: key = XK_dead_doubleacute; break; + case Qt::Key_Dead_Caron: key = XK_dead_caron; break; + case Qt::Key_Dead_Cedilla: key = XK_dead_cedilla; break; + case Qt::Key_Dead_Ogonek: key = XK_dead_ogonek; break; + case Qt::Key_Dead_Iota: key = XK_dead_iota; break; + case Qt::Key_Dead_Voiced_Sound: key = XK_dead_voiced_sound; break; + case Qt::Key_Dead_Semivoiced_Sound: key = XK_dead_semivoiced_sound; break; + case Qt::Key_Dead_Belowdot: key = XK_dead_belowdot; break; + } + + if( event->key() >= Qt::Key_F1 && event->key() <= Qt::Key_F35 ) + { + key = XK_F1 + event->key() - Qt::Key_F1; + } + else if( key == 0 ) + { + if( m_mods.contains( XK_Control_L ) && + QKeySequence( event->key() ).toString().length() == 1 ) + { + QString s = QKeySequence( event->key() ).toString(); + if( !m_mods.contains( XK_Shift_L ) ) + { + s = s.toLower(); + } + key = s.utf16()[0]; + } + else + { + key = event->text().utf16()[0]; + } + } + // correct translation of AltGr+ (non-US-keyboard layout + // such as German keyboard layout) + if( m_mods.contains( XK_Alt_L ) && m_mods.contains( XK_Control_L ) && + key >= 64 && key < 0xF000 ) + { + unpressModifiers(); + m_connection->keyEvent( XK_ISO_Level3_Shift, true ); + } +#endif + + // handle Ctrl+Alt+Del replacement (Meta/Super key+Del) + if( ( m_mods.contains( XK_Super_L ) || + m_mods.contains( XK_Super_R ) || + m_mods.contains( XK_Meta_L ) ) && + event->key() == Qt::Key_Delete ) + { + if( pressed ) + { + unpressModifiers(); + m_connection->keyEvent( XK_Control_L, true ); + m_connection->keyEvent( XK_Alt_L, true ); + m_connection->keyEvent( XK_Delete, true ); + m_connection->keyEvent( XK_Delete, false ); + m_connection->keyEvent( XK_Alt_L, false ); + m_connection->keyEvent( XK_Control_L, false ); + key = 0; + } + } + + // handle modifiers + if( key == XK_Shift_L || key == XK_Control_L || key == XK_Meta_L || + key == XK_Alt_L || key == XK_Super_L || key == XK_Super_R ) + { + if( pressed ) + { + m_mods[key] = true; + } + else if( m_mods.contains( key ) ) + { + m_mods.remove( key ); + } + else + { + unpressModifiers(); + } + } + + if( key ) + { + // forward key event to the VNC connection + m_connection->keyEvent( key, pressed ); + + // inform Qt that we handled the key event + event->accept(); + } +} + + + +void VncView::mouseEventHandler( QMouseEvent* event ) +{ + if( event == nullptr || m_viewOnly ) + { + return; + } + + struct ButtonTranslation + { + Qt::MouseButton qt; + int rfb; + }; + + static constexpr std::array buttonTranslationMap{ { + { Qt::LeftButton, rfbButton1Mask }, + { Qt::MidButton, rfbButton2Mask }, + { Qt::RightButton, rfbButton3Mask } + } }; + + if( event->type() != QEvent::MouseMove ) + { + for( const auto& i : buttonTranslationMap ) + { + if( event->button() == i.qt ) + { + if( event->type() == QEvent::MouseButtonPress || + event->type() == QEvent::MouseButtonDblClick ) + { + m_buttonMask |= uint(i.rfb); + } + else + { + m_buttonMask &= ~uint(i.rfb); + } + } + } + } + + const auto pos = mapToFramebuffer( event->pos() ); + m_connection->mouseEvent( pos.x(), pos.y(), m_buttonMask ); +} + + + +void VncView::wheelEventHandler( QWheelEvent* event ) +{ + if( event == nullptr ) + { + return; + } + +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + const auto p = mapToFramebuffer( event->position().toPoint() ); + const uint scrollButtonMask = ( event->angleDelta().y() < 0 ) ? rfbButton5Mask : rfbButton4Mask; +#else + const auto p = mapToFramebuffer( event->pos() ); + const uint scrollButtonMask = ( event->delta() < 0 ) ? rfbButton5Mask : rfbButton4Mask; +#endif + m_connection->mouseEvent( p.x(), p.y(), m_buttonMask | scrollButtonMask ); + m_connection->mouseEvent( p.x(), p.y(), m_buttonMask ); +} + + + +void VncView::updateLocalCursor() +{ + if( m_cursorShape.isNull() == false && viewOnly() == false ) + { + setViewCursor( QCursor( m_cursorShape, m_cursorHot.x(), m_cursorHot.y() ) ); + } + else + { + setViewCursor( Qt::ArrowCursor ); + } +} + + + +void VncView::updatePaintedCursor() +{ + updateView( m_cursorPos.x(), m_cursorPos.y(), m_cursorShape.width(), m_cursorShape.height() ); +} + + + +void VncView::pressKey( unsigned int key ) +{ + m_connection->keyEvent( key, true ); +} + + + +void VncView::unpressKey( unsigned int key ) +{ + m_connection->keyEvent( key, false ); +} diff --git a/core/src/VncViewWidget.cpp b/core/src/VncViewWidget.cpp new file mode 100644 index 0000000..3d2cd81 --- /dev/null +++ b/core/src/VncViewWidget.cpp @@ -0,0 +1,344 @@ +/* + * VncViewWidget.cpp - VNC viewer widget + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include + +#include "ProgressWidget.h" +#include "VeyonConnection.h" +#include "VncConnection.h" +#include "VncViewWidget.h" + + +VncViewWidget::VncViewWidget( const QString& host, int port, QWidget* parent, Mode mode, const QRect& viewport ) : + QWidget( parent ), + VncView( new VncConnection( QCoreApplication::instance() ) ), + m_veyonConnection( new VeyonConnection( connection() ) ) +{ + setViewport( viewport ); + + connectUpdateFunctions( this ); + + connection()->setHost( host ); + connection()->setPort( port ); + + if( mode == DemoMode ) + { + connection()->setQuality( VncConnection::Quality::Default ); + m_establishingConnectionWidget = new ProgressWidget( + tr( "Establishing connection to %1 ..." ).arg( connection()->host() ), + QStringLiteral( ":/core/watch%1.png" ), 16, this ); + connect( connection(), &VncConnection::stateChanged, + this, &VncViewWidget::updateConnectionState ); + } + else if( mode == RemoteControlMode ) + { + connection()->setQuality( VncConnection::Quality::RemoteControl ); + } + + // set up mouse border signal timer + m_mouseBorderSignalTimer.setSingleShot( true ); + m_mouseBorderSignalTimer.setInterval( MouseBorderSignalDelay ); + connect( &m_mouseBorderSignalTimer, &QTimer::timeout, this, &VncViewWidget::mouseAtBorder ); + + // set up background color + if( parent == nullptr ) + { + parent = this; + } + QPalette pal = parent->palette(); + pal.setColor( parent->backgroundRole(), Qt::black ); + parent->setPalette( pal ); + + show(); + + resize( QApplication::desktop()->availableGeometry( this ).size() - QSize( 10, 30 ) ); + + setFocusPolicy( Qt::StrongFocus ); + setFocus(); + + connection()->start(); +} + + + +VncViewWidget::~VncViewWidget() +{ + // do not receive any signals during connection shutdown + connection()->disconnect( this ); + + unpressModifiers(); + + delete m_veyonConnection; + m_veyonConnection = nullptr; + + connection()->stopAndDeleteLater(); +} + + + +QSize VncViewWidget::sizeHint() const +{ + return effectiveFramebufferSize(); +} + + + +void VncViewWidget::setViewOnly( bool enabled ) +{ + if( enabled == viewOnly() ) + { + return; + } + + if( enabled ) + { + releaseKeyboard(); + } + else + { + grabKeyboard(); + } + + VncView::setViewOnly( enabled ); +} + + + +void VncViewWidget::updateView( int x, int y, int w, int h ) +{ + update( x, y, w, h ); +} + + + +QSize VncViewWidget::viewSize() const +{ + return size(); +} + + + +void VncViewWidget::setViewCursor(const QCursor& cursor) +{ + setCursor( cursor ); +} + + + +void VncViewWidget::updateFramebufferSize( int w, int h ) +{ + VncView::updateFramebufferSize( w, h ); + + resize( w, h ); + + Q_EMIT sizeHintChanged(); +} + + + +void VncViewWidget::updateImage( int x, int y, int w, int h ) +{ + if( m_initDone == false ) + { + setAttribute( Qt::WA_OpaquePaintEvent ); + installEventFilter( this ); + + setMouseTracking( true ); // get mouse events even when there is no mousebutton pressed + setFocusPolicy( Qt::WheelFocus ); + + resize( sizeHint() ); + + Q_EMIT connectionEstablished(); + m_initDone = true; + + } + + VncView::updateImage( x, y, w, h ); +} + + + +bool VncViewWidget::event( QEvent* event ) +{ + return VncView::handleEvent( event ) || QWidget::event( event ); +} + + + +bool VncViewWidget::eventFilter( QObject* obj, QEvent* event ) +{ + if( viewOnly() ) + { + if( event->type() == QEvent::KeyPress || + event->type() == QEvent::KeyRelease || + event->type() == QEvent::MouseButtonDblClick || + event->type() == QEvent::MouseButtonPress || + event->type() == QEvent::MouseButtonRelease || + event->type() == QEvent::Wheel ) + { + return true; + } + } + + return QWidget::eventFilter( obj, event ); +} + + + +void VncViewWidget::focusInEvent( QFocusEvent* event ) +{ + if( m_viewOnlyFocus == false ) + { + setViewOnly( false ); + } + + QWidget::focusInEvent( event ); +} + + + +void VncViewWidget::focusOutEvent( QFocusEvent* event ) +{ + m_viewOnlyFocus = viewOnly(); + + if( viewOnly() == false ) + { + setViewOnly( true ); + } + + QWidget::focusOutEvent( event ); +} + + + +void VncViewWidget::mouseEventHandler( QMouseEvent* event ) +{ + if( event == nullptr ) + { + return; + } + + VncView::mouseEventHandler( event ); + + if( event->type() == QEvent::MouseMove ) + { + if( event->pos().y() == 0 ) + { + if( m_mouseBorderSignalTimer.isActive() == false ) + { + m_mouseBorderSignalTimer.start(); + } + } + else + { + m_mouseBorderSignalTimer.stop(); + } + } +} + + + +void VncViewWidget::paintEvent( QPaintEvent* paintEvent ) +{ + QPainter p( this ); + p.setRenderHint( QPainter::SmoothPixmapTransform ); + + const auto& image = connection()->image(); + + if( image.isNull() || image.format() == QImage::Format_Invalid ) + { + p.fillRect( paintEvent->rect(), Qt::black ); + return; + } + + auto source = viewport(); + if( source.isNull() || source.isEmpty() ) + { + source = { QPoint{ 0, 0 }, image.size() }; + } + + if( isScaledView() ) + { + // repaint everything in scaled mode to avoid artifacts at rectangle boundaries + p.drawImage( QRect( QPoint( 0, 0 ), scaledSize() ), image, source ); + } + else + { + p.drawImage( { 0, 0 }, image, source ); + } + + if( viewOnly() && cursorShape().isNull() == false ) + { + const auto cursorRect = mapFromFramebuffer( QRect( cursorPos() - cursorHot(), cursorShape().size() ) ); + // parts of cursor within updated region? + if( paintEvent->region().intersects( cursorRect ) ) + { + // then repaint it + p.drawPixmap( cursorRect.topLeft(), cursorShape() ); + } + } + + // draw black borders if neccessary + const int screenWidth = scaledSize().width(); + if( screenWidth < width() ) + { + p.fillRect( screenWidth, 0, width() - screenWidth, height(), Qt::black ); + } + + const int screenHeight = scaledSize().height(); + if( screenHeight < height() ) + { + p.fillRect( 0, screenHeight, width(), height() - screenHeight, Qt::black ); + } +} + + + +void VncViewWidget::resizeEvent( QResizeEvent* event ) +{ + update(); + + if( m_establishingConnectionWidget ) + { + m_establishingConnectionWidget->move( 10, 10 ); + } + + updateLocalCursor(); + + QWidget::resizeEvent( event ); +} + + + +void VncViewWidget::updateConnectionState() +{ + if( m_establishingConnectionWidget ) + { + m_establishingConnectionWidget->setVisible( connection()->state() != VncConnection::State::Connected ); + } +} diff --git a/core/src/d3des.c b/core/src/d3des.c new file mode 100644 index 0000000..e76ece7 --- /dev/null +++ b/core/src/d3des.c @@ -0,0 +1,437 @@ +/* + * This is D3DES (V5.09) by Richard Outerbridge with the double and + * triple-length support removed for use in VNC. Also the bytebit[] array + * has been reversed so that the most significant bit in each byte of the + * key is ignored, not the least significant. + * + * These changes are: + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This software 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. + */ + +/* D3DES (V5.09) - + * + * A portable, public domain, version of the Data Encryption Standard. + * + * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge. + * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation + * code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis + * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau, + * for humouring me on. + * + * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. + * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. + */ + +#include "d3des.h" + +#if defined(__GNUC__) +#define TLS __thread +#elif defined(_MSC_VER) +#define TLS __declspec(thread) +#else +#define TLS +#endif + +static void scrunch(unsigned char *, unsigned long *); +static void unscrun(unsigned long *, unsigned char *); +static void desfunc(unsigned long *, unsigned long *); +static void cookey(unsigned long *); + +static TLS unsigned long KnL[32] = { 0L }; +/* +static unsigned long KnR[32] = { 0L }; +static unsigned long Kn3[32] = { 0L }; +static unsigned char Df_Key[24] = { + 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10, + 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67 }; +*/ + +static const unsigned short bytebit[8] = { + 01, 02, 04, 010, 020, 040, 0100, 0200 }; + +static const unsigned long bigbyte[24] = { + 0x800000L, 0x400000L, 0x200000L, 0x100000L, + 0x80000L, 0x40000L, 0x20000L, 0x10000L, + 0x8000L, 0x4000L, 0x2000L, 0x1000L, + 0x800L, 0x400L, 0x200L, 0x100L, + 0x80L, 0x40L, 0x20L, 0x10L, + 0x8L, 0x4L, 0x2L, 0x1L }; + +/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */ + +static const unsigned char pc1[56] = { + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; + +static const unsigned char totrot[16] = { + 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 }; + +static const unsigned char pc2[48] = { + 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, + 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, + 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, + 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 }; + +void rfbDesKey(unsigned char *key, + int edf) +{ + register int i, j, l, m, n; + unsigned char pc1m[56], pcr[56]; + unsigned long kn[32]; + + for ( j = 0; j < 56; j++ ) { + l = pc1[j]; + m = l & 07; + pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0; + } + for( i = 0; i < 16; i++ ) { + if( edf == DE1 ) m = (15 - i) << 1; + else m = i << 1; + n = m + 1; + kn[m] = kn[n] = 0L; + for( j = 0; j < 28; j++ ) { + l = j + totrot[i]; + if( l < 28 ) pcr[j] = pc1m[l]; + else pcr[j] = pc1m[l - 28]; + } + for( j = 28; j < 56; j++ ) { + l = j + totrot[i]; + if( l < 56 ) pcr[j] = pc1m[l]; + else pcr[j] = pc1m[l - 28]; + } + for( j = 0; j < 24; j++ ) { + if( pcr[pc2[j]] ) kn[m] |= bigbyte[j]; + if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j]; + } + } + cookey(kn); + return; + } + +static void rfbUseKey(register unsigned long *from) +{ + register unsigned long *to, *endp; + + to = KnL, endp = &KnL[32]; + while( to < endp ) *to++ = *from++; + return; + } + + +static void cookey(register unsigned long *raw1) +{ + register unsigned long *cook, *raw0; + unsigned long dough[32]; + register int i; + + cook = dough; + for( i = 0; i < 16; i++, raw1++ ) { + raw0 = raw1++; + *cook = (*raw0 & 0x00fc0000L) << 6; + *cook |= (*raw0 & 0x00000fc0L) << 10; + *cook |= (*raw1 & 0x00fc0000L) >> 10; + *cook++ |= (*raw1 & 0x00000fc0L) >> 6; + *cook = (*raw0 & 0x0003f000L) << 12; + *cook |= (*raw0 & 0x0000003fL) << 16; + *cook |= (*raw1 & 0x0003f000L) >> 4; + *cook++ |= (*raw1 & 0x0000003fL); + } + rfbUseKey(dough); + return; + } + + +void rfbDes(unsigned char *inblock, + unsigned char *outblock) +{ + unsigned long work[2]; + + scrunch(inblock, work); + desfunc(work, KnL); + unscrun(work, outblock); + return; + } + +static void scrunch(register unsigned char *outof, + register unsigned long *into) +{ + *into = (*outof++ & 0xffL) << 24; + *into |= (*outof++ & 0xffL) << 16; + *into |= (*outof++ & 0xffL) << 8; + *into++ |= (*outof++ & 0xffL); + *into = (*outof++ & 0xffL) << 24; + *into |= (*outof++ & 0xffL) << 16; + *into |= (*outof++ & 0xffL) << 8; + *into |= (*outof & 0xffL); + return; + } + +static void unscrun(register unsigned long *outof, + register unsigned char *into) +{ + *into++ = (unsigned char)((*outof >> 24) & 0xffL); + *into++ = (unsigned char)((*outof >> 16) & 0xffL); + *into++ = (unsigned char)((*outof >> 8) & 0xffL); + *into++ = (unsigned char)( *outof++ & 0xffL); + *into++ = (unsigned char)((*outof >> 24) & 0xffL); + *into++ = (unsigned char)((*outof >> 16) & 0xffL); + *into++ = (unsigned char)((*outof >> 8) & 0xffL); + *into = (unsigned char)( *outof & 0xffL); + return; + } + +static const unsigned long SP1[64] = { + 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L, + 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L, + 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L, + 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L, + 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L, + 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L, + 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L, + 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L, + 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L, + 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L, + 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L, + 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L, + 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L, + 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L, + 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L, + 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L }; + +static const unsigned long SP2[64] = { + 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L, + 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L, + 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L, + 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L, + 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L, + 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L, + 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L, + 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L, + 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L, + 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L, + 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L, + 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L, + 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L, + 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L, + 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L, + 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L }; + +static const unsigned long SP3[64] = { + 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L, + 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L, + 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L, + 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L, + 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L, + 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L, + 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L, + 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L, + 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L, + 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L, + 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L, + 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L, + 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L, + 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L, + 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L, + 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L }; + +static const unsigned long SP4[64] = { + 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, + 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L, + 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L, + 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L, + 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L, + 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L, + 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L, + 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L, + 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L, + 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L, + 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L, + 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, + 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L, + 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L, + 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L, + 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L }; + +static const unsigned long SP5[64] = { + 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L, + 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L, + 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L, + 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L, + 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L, + 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L, + 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L, + 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L, + 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L, + 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L, + 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L, + 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L, + 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L, + 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L, + 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L, + 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L }; + +static const unsigned long SP6[64] = { + 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L, + 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L, + 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L, + 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L, + 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L, + 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L, + 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L, + 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L, + 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L, + 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L, + 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L, + 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L, + 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L, + 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L, + 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L, + 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L }; + +static const unsigned long SP7[64] = { + 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L, + 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L, + 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L, + 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L, + 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L, + 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L, + 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L, + 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L, + 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L, + 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L, + 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L, + 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L, + 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L, + 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L, + 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L, + 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L }; + +static const unsigned long SP8[64] = { + 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L, + 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L, + 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L, + 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L, + 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L, + 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L, + 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L, + 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L, + 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L, + 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L, + 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L, + 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L, + 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L, + 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L, + 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L, + 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L }; + +static void desfunc(register unsigned long *block, + register unsigned long *keys) +{ + register unsigned long fval, work, right, leftt; + register int round; + + leftt = block[0]; + right = block[1]; + work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL; + right ^= work; + leftt ^= (work << 4); + work = ((leftt >> 16) ^ right) & 0x0000ffffL; + right ^= work; + leftt ^= (work << 16); + work = ((right >> 2) ^ leftt) & 0x33333333L; + leftt ^= work; + right ^= (work << 2); + work = ((right >> 8) ^ leftt) & 0x00ff00ffL; + leftt ^= work; + right ^= (work << 8); + right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL; + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL; + + for( round = 0; round < 8; round++ ) { + work = (right << 28) | (right >> 4); + work ^= *keys++; + fval = SP7[ work & 0x3fL]; + fval |= SP5[(work >> 8) & 0x3fL]; + fval |= SP3[(work >> 16) & 0x3fL]; + fval |= SP1[(work >> 24) & 0x3fL]; + work = right ^ *keys++; + fval |= SP8[ work & 0x3fL]; + fval |= SP6[(work >> 8) & 0x3fL]; + fval |= SP4[(work >> 16) & 0x3fL]; + fval |= SP2[(work >> 24) & 0x3fL]; + leftt ^= fval; + work = (leftt << 28) | (leftt >> 4); + work ^= *keys++; + fval = SP7[ work & 0x3fL]; + fval |= SP5[(work >> 8) & 0x3fL]; + fval |= SP3[(work >> 16) & 0x3fL]; + fval |= SP1[(work >> 24) & 0x3fL]; + work = leftt ^ *keys++; + fval |= SP8[ work & 0x3fL]; + fval |= SP6[(work >> 8) & 0x3fL]; + fval |= SP4[(work >> 16) & 0x3fL]; + fval |= SP2[(work >> 24) & 0x3fL]; + right ^= fval; + } + + right = (right << 31) | (right >> 1); + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = (leftt << 31) | (leftt >> 1); + work = ((leftt >> 8) ^ right) & 0x00ff00ffL; + right ^= work; + leftt ^= (work << 8); + work = ((leftt >> 2) ^ right) & 0x33333333L; + right ^= work; + leftt ^= (work << 2); + work = ((right >> 16) ^ leftt) & 0x0000ffffL; + leftt ^= work; + right ^= (work << 16); + work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL; + leftt ^= work; + right ^= (work << 4); + *block++ = right; + *block = leftt; + return; + } + +/* Validation sets: + * + * Single-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef + * Plain : 0123 4567 89ab cde7 + * Cipher : c957 4425 6a5e d31d + * + * Double-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 + * Plain : 0123 4567 89ab cde7 + * Cipher : 7f1d 0a77 826b 8aff + * + * Double-length key, double-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 + * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff + * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7 + * + * Triple-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 + * Plain : 0123 4567 89ab cde7 + * Cipher : de0b 7c06 ae5e 0ed5 + * + * Triple-length key, double-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 + * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff + * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5 + * + * d3des V5.0a rwo 9208.07 18:44 Graven Imagery + **********************************************************************/ diff --git a/core/src/d3des.h b/core/src/d3des.h new file mode 100644 index 0000000..4bb6bdb --- /dev/null +++ b/core/src/d3des.h @@ -0,0 +1,46 @@ +#ifndef D3DES_H +#define D3DES_H + +/* + * This is D3DES (V5.09) by Richard Outerbridge with the double and + * triple-length support removed for use in VNC. + * + * These changes are: + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This software 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. + */ + +/* d3des.h - + * + * Headers and defines for d3des.c + * Graven Imagery, 1992. + * + * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge + * (GEnie : OUTER; CIS : [71755,204]) + */ + +#define EN0 0 /* MODE == encrypt */ +#define DE1 1 /* MODE == decrypt */ + +extern void rfbDesKey(unsigned char *, int); +/* hexkey[8] MODE + * Sets the internal key register according to the hexadecimal + * key contained in the 8 bytes of hexkey, according to the DES, + * for encryption or decryption according to MODE. + */ + + +extern void rfbDes(unsigned char *, unsigned char *); +/* from[8] to[8] + * Encrypts/Decrypts (according to the key currently loaded in the + * internal key register) one block of eight bytes at address 'from' + * into the block at address 'to'. They can be the same. + */ + +/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery + ********************************************************************/ + +#endif diff --git a/master/CMakeLists.txt b/master/CMakeLists.txt new file mode 100644 index 0000000..0fccd8b --- /dev/null +++ b/master/CMakeLists.txt @@ -0,0 +1,35 @@ +INCLUDE(BuildVeyonApplication) +INCLUDE(WindowsBuildHelpers) + +SET(kitemmodels_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/kitemmodels/src/core) +SET(kitemmodels_SOURCES + ${kitemmodels_SOURCE_DIR}/kextracolumnsproxymodel.cpp + ${kitemmodels_SOURCE_DIR}/krecursivefilterproxymodel.cpp + ${kitemmodels_SOURCE_DIR}/kextracolumnsproxymodel.h + ${kitemmodels_SOURCE_DIR}/krecursivefilterproxymodel.h +) + +FILE(GLOB master_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h) +FILE(GLOB master_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/*.ui) + +SET(master_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/resources/master.qrc) +IF(VEYON_DEBUG) +SET(master_RESOURCES ${master_RESOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/examples.qrc) +ENDIF() + +# drop once kitemmodels compile with QT_NO_KEYWORDS and the remove_definitions/set_source_files_properties above can be removed +set(WITH_PCH OFF) +remove_definitions(-DQT_NO_KEYWORDS) +set_source_files_properties(${master_SOURCES} PROPERTIES COMPILE_DEFINITIONS QT_NO_KEYWORDS) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src ${kitemmodels_SOURCE_DIR}) +build_veyon_application(veyon-master ${master_SOURCES} ${master_INCLUDES} ${master_RESOURCES} ${kitemmodels_SOURCES}) +TARGET_LINK_LIBRARIES(veyon-master veyon-core) + +ADD_WINDOWS_RESOURCE(veyon-master) +MAKE_GRAPHICAL_APP(veyon-master) + +IF(VEYON_BUILD_LINUX) + XDG_INSTALL(${CMAKE_CURRENT_BINARY_DIR}/data/veyon-master.desktop ${CMAKE_CURRENT_SOURCE_DIR}/data/veyon-master.xpm ${CMAKE_CURRENT_SOURCE_DIR}/data/veyon-master.png ${CMAKE_CURRENT_SOURCE_DIR}/data/veyon-master.svg) +ENDIF() + diff --git a/master/data/veyon-master.desktop.in b/master/data/veyon-master.desktop.in new file mode 100644 index 0000000..34f1c03 --- /dev/null +++ b/master/data/veyon-master.desktop.in @@ -0,0 +1,11 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Exec=@CMAKE_INSTALL_PREFIX@/bin/veyon-master +Icon=veyon-master +Terminal=false +Name=Veyon Master +Comment=Monitor and control remote computers +Comment[de]=Entfernte Computer beobachten und steuern +Categories=Qt;Education;Network;RemoteAccess; +Keywords=classroom,control,computer,room,lab,monitoring,teacher,student diff --git a/master/data/veyon-master.exe.manifest.in b/master/data/veyon-master.exe.manifest.in new file mode 100644 index 0000000..03bfb1b --- /dev/null +++ b/master/data/veyon-master.exe.manifest.in @@ -0,0 +1,14 @@ + + + + Veyon Master + + + + + + + + + + diff --git a/master/data/veyon-master.png b/master/data/veyon-master.png new file mode 100644 index 0000000000000000000000000000000000000000..cee60f2bcd0088607297de54964ac0c1d4b6d268 GIT binary patch literal 1514 zcma)5c`(~~6#rRTN1H0PTGcUITBR-9cGXqI5=*A7k^`XxEkcMRiA0b@#{?e|gDT5d|qRt1MGQkOkR8vzEPu!M(p?ZuPng9R?F7KL>Kx(qvq(^k| zAtItfiR7TT5P(c3n}_3Zgu6j8A?DF>VRlwLy+vp{UMk~B9VIt=0Uz!-A_Oco;?jn_GJ6XZh4}0Rb2cCMYN?TrQ8tD=jT8D=RB6FR!SmsI08y^Z8X(RRV#a zrlzL0wzjUWuBD}=wY9aatxYHtwzs!;baaSBqR!6FuCA`3p&_wYJUTi$Ha0dsJ}!|+ zCMG5(Cnu+-rlzN-7Z(?$QmITPTUuILUS3{VSy^3OU0Yk**x1U_o5$=uvR1O?e)z;BHenLsIzNOJTkfkj2EI}a81z@9+k1A- z0!GLC=>1}>5%a^L^H?0Jo*w}%3@GV& zEl#RLScNQ$%^CDS_X?AxF5uIE7wuY1@*Tn)N#7%4DC77$#8X6XBSV1A!X)a_+21Tu=n@W2 zl(?m68c7ASjbEkf>xy-|ohsv`&GKWlvxf$pkBm}t4Q)9-ZG{*VX<6i=(=#R(uF`l(Qy$QQpqUR;3!5Z%0_>ghm&me*q5_Z(Y(CsXa+fPCi4RK0JHm^aXxDV z5aBB`x_{;Zc>8oeONSFG)@7FBLV4tEGe<<&_-FCpVr1kt4C9K3cvIREd)&l&x;2hu zUEK$1;xAk)$X!RtwnzSTs^AX#)3`4 + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/master/data/veyon-master.xpm b/master/data/veyon-master.xpm new file mode 100644 index 0000000..92bb9b7 --- /dev/null +++ b/master/data/veyon-master.xpm @@ -0,0 +1,112 @@ +/* XPM */ +static char *veyon_master_32[] = { +/* columns rows colors chars-per-pixel */ +"32 32 74 1 ", +" c #535353", +". c gray33", +"X c #555555", +"o c #565656", +"O c gray34", +"+ c #585858", +"@ c #5B5B5B", +"# c gray38", +"$ c #626262", +"% c DimGray", +"& c #6A6A6A", +"* c gray42", +"= c #6C6C6C", +"- c gray43", +"; c gray44", +": c #717171", +"> c #747474", +", c gray46", +"< c #767676", +"1 c #777777", +"2 c #7C7C7C", +"3 c gray49", +"4 c #7E7E7E", +"5 c #808080", +"6 c #818181", +"7 c gray51", +"8 c gray54", +"9 c #8B8B8B", +"0 c gray55", +"q c #8D8D8D", +"w c #8E8E8E", +"e c #959595", +"r c gray59", +"t c #979797", +"y c #989898", +"u c #A2A2A2", +"i c gray64", +"p c #A4A4A4", +"a c gray66", +"s c #A9A9A9", +"d c #AAAAAA", +"f c gray67", +"g c #ACACAC", +"h c #AFAFAF", +"j c gray69", +"k c #B2B2B2", +"l c #CECECE", +"z c gray81", +"x c LightGray", +"c c gray83", +"v c gray84", +"b c #D7D7D7", +"n c gray85", +"m c #DADADA", +"M c gray86", +"N c gainsboro", +"B c #DDDDDD", +"V c #E1E1E1", +"C c #E4E4E4", +"Z c gray90", +"A c #E6E6E6", +"S c gray93", +"D c #EEEEEE", +"F c #EFEFEF", +"G c gray94", +"H c #F1F1F1", +"J c gray95", +"K c gray97", +"L c #F8F8F8", +"P c #F9F9F9", +"I c #FBFBFB", +"U c gray99", +"Y c white", +"T c None", +/* pixels */ +"TTTTTTTTTTTT........TTTTTTTTTTTT", +"TTTTTTTTT..............TTTTTTTTT", +"TTTTTTT....1gvAIIAvf1....TTTTTTT", +"TTTTTT...eAUUUUUUUUUUAq...TTTTTT", +"TTTTT..=BUUUUUUUUUUUUUUB*..TTTTT", +"TTTT..2GUUUUUUUUUUUUUUUUG7..TTTT", +"TTT..7UUUUUUUUUUUUUUUUUUUU2..TTT", +"TT..*JUUUUUUUUUUUUUUUUUUUUG=..TT", +"TT..BUUUUUUUUUUUUUUUUUUUUUUB..TT", +"T. eUUUUUUUUUUUUUUUUUUUUUUUUq..T", +"T. AUUUUUUIzu7;$$;7uxIUUUUUUA..T", +"T.1UUUUUBt+..........+tBUUUUU,.T", +"..gUUUAq................7VUUUf. ", +"..vUUp.......+kGGk+.......yIUv..", +"..AM=........gUUUUg........=vA..", +"..k+.........DUUUUG..........k..", +"..k+.........DUUUUG.........+k..", +"..AB=........kUUUUk........=vA..", +"..vUIp. .....+kDDg+.......yIUx..", +"..gUUUVe................7BUUUf..", +"T.,UUUUUBy@..........@yBUUUUU,.T", +"T..AUUUUUUJxp7;$$;7pxIUUUUUUA..T", +"T..qUUUUUUUUUUUUUUUUUUUUUUUU9..T", +"TT..BUUUUUUUUUUUUUUUUUUUUUUM..TT", +"TT..*GUUUUUUUUUUUUUUUUUUUUG=..TT", +"TTT..2UUUUUUUUUUUUUUUUUUUU2..TTT", +"TTTT..2GUUUUUUUUUUUUUUUUJ7..TTTT", +"TTTTT..=BUUUUUUUUUUUUUUv=. TTTTT", +"TTTTTT...qAUUUUUUUUUUA9...TTTTTT", +"TTTTTTT....,gvAJIAvg,....TTTTTTT", +"TTTTTTTTT..............TTTTTTTTT", +"TTTTTTTTTTTT........TTTTTTTTTTTT" +}; diff --git a/master/resources/align-grid.png b/master/resources/align-grid.png new file mode 100644 index 0000000000000000000000000000000000000000..ec8c5648a611fad02cc9f141a0ecc9ea172df784 GIT binary patch literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?I3?vN&YJLDI=3*z$5DpHG+YkL80J)q69+AZi z3}rGP%((ne#VeqoWQl7;NpOBzNqJ&XDnogBxn5>oc5!lIL8@MUQTpt6Hc~)E`~f~8 zu0Yyu-Ah|A3Jnc)3yq%z6k;w3@(X6z_KL&oSR0UAE3OiV^|It#rO&eoRvRNv;MgR<3*ql zLlx^R`A~adh6_AXtQ*?wn4g?tsAViWIGclEhdfZa;dkSDUWOGrFVdQ&MBb@06St{;{X5v literal 0 HcmV?d00001 diff --git a/master/resources/camera-photo.png b/master/resources/camera-photo.png new file mode 100644 index 0000000000000000000000000000000000000000..964a472cf67e542f92e2353737fee7b49c8bc671 GIT binary patch literal 2986 zcmai$c{J1w7sr3oFh&gHF_uADLXveT_1O2dh>|5{$d-M}6caNO8I5%qvWp~pq7-3z z>=arQS!RrsWEo2t(Re+7y??#uz31NhJ@=mbIp_ZQO>sDH2|Fxt7ytm6wUwFUf#Uxy zDCD4b8+Z*L2v?Me^+o7G;GjOFgPAwn$~6iAjwJtEpp1oe--9JG+T11DDalNt> zV6j-WYoS3=SG~f0)xskE3s?0d0D$kfwVCn78=qH-cbqO>7VX~*du_kz?sM&Rv3`?d;TvI$ND~t22_Yuhm>HKFU6d;)lFH?tkhFvNX0~SQ zVA0J*FTR~!Df`F0Ahb=o$bP)~iOX;O+3np`-ES}B8XG$-;9SQ4J2iQB#t9}Yzsy5E z0FmD)69h#8#t@kSfD4H^&AE^8JT2g%jRHK*pa5fy;Az)ymf&G*Ct0-e?ME_>&GS&xbQO^_9)7&(pMqMyjDu~RZ-1S41_XuAs-4VxStwKQxXtOpn_vd~cx$*TUcxZ$%;zuHxO3+ zJr?4C$|~hkH{Kgd9EP|3Ih?r6<~G54+=&+y4E%hul&Xolc6Mr7FV9Ot{IY zP+Fst+Thyt@W7C+MRFITy+7Dx|GW8|2g1;&{}OcaCX9%s8coc)vn-rz7dNfySMRJx ztQFGq0+M^H!iKO0G>?wrldXxiD1Ec4oG#^;l0Aa6*N+f|!%SGKj-)fr>TPB!PTMzT z0JO(Vc{;Cbcd&S`*eRlzo;}ZnT^l{M_;5!gITC7L@74c-ujJE`oUr(7=SB&PXJ8f% zhEF@HiH-lz8Kk8cKUOjYZ`l^mGFH{?e9Zfdhe;2x^r zzsEY`aQ2&H4kdx2dh}9htZ`J7mUx;43J>R{>c13=nww0wF4U83zSJm=>3$)aaaa0A zndqUe`s903oC`9m&ya3zZe>rOip0jn4FXPtO_f|e`GmKYt9At|mVFDa(D@AA)d|-# zn5&^DRs0RxcP!M&1}?E|wZza70>kh)ktvvvTvi$M!r|pEk}G6_?^P4G8VSt3K$SqkDeyg>FL7ByIC8CMZP& zL8kNcmQ%C)nSy7Iaoq>Wlcd8RgNW&mxTL`g%Bsf=EG#W~&PWNMXv1H$r`Yr*-&3|V zoAa&$EInD3COcJ5BJ_8-u+p!KBvC_|1zs+xz(k#qnDvr4zqAx#biH%;`zrhUwY7YG zeSHIf@Z|pEj0CoQ`|;|*X6A(H#0aH3@tuLoFY_l#;P56{R|*@HTp%OYoV`80J=?4& zCT)oy89A?ZV#~)@Mi-28?c4vTuDnywB0zr_40ZeG&ad9u9`dNZz(j@ITus3GA3+_{ zffJk(T3%(YSWo{GhuU!yELI0e8?-dv>)0n1Cs97P8wa)83def~Pak)~?H{X$=Zus> zOCN`fVjyt0neOrJ zzYgB;mGsb|-R(*drDvq9l{N?kL)KF=yiOOsIDfxg89a$(m=fe2@?GSsTMdilH8QD0 z@7`_Q8UihQZv|r)p48LoqB5<7X`cN05EP!pO*c=SMl091cAXVNgf&n?fnU0yDO)r0 zT3$m^CVw#osbqmu;fsrnou(j$Mu5Rf9ecO%6_+UJWhH}@yioj4Yf8_y?I*$p4`fB$~Yp+!?nCXr>cOn4C>k?W%Jhq}7zK5B8OvmdnU$!Ifc*veM!+_#?l9z+dn zK45lt&w?3+rkAr1DBzRROD9@_X2rXjD+#4t3et!lF+^r@m!1~5?+Fb4JYI*Z2Tj#m zu3&GbOUi1Ky9h}M*&{L@I6KH&py4a{NfG?7xV!|D0b=tJJ2vyX{Z}?R&)%k5C;tE_ zlu^v1w@fM5sgW}@wY9%E8pzm!r^lf7ykxx%8cgfv(nD3w54*iFmAR*Umka9O|0Qh+ z6uGLLlFqCIEia#y`2csjS3$^p;MhoB!@lF!{Fc;8eOvhYy{Po@Oooz-f^_Ce3rqOt zKR-YfS{FS<_J0cdkp@IP8%j8jrb3wWA0kgw;Dce9BS&x3zw-Hi9Qq*9m?ACby@(#- z+&SIR8^Y9u$;2QsfBL<({9#sic;vn5#M}~63?1=VE-))VqJ6hRp>r9AkK0I@6!P(E zNY7`yeVtP3MM(OV)Z5Q3E0H}O#60%IQ|fL`v_Q(0?8ao)79@i#U34Nok20*P?$R1uaXRmze36wQhV6Itw+pvAk8z^2%}X_bcki_1o(Bil*m!Y} zFeRJV8wYGU2I333GS|7`U3o`|-;4{ajW<-`}~V0}&H>^`OjW0#fDWdJR_;6{<4IGMA=3JpVV`IB3h$w^rI z^l73^C9(gUYNk`|?F4H6+zRf~8+_O9S|lIhQC-=of|=hopYnS0IzaH369I?(8ISN8 zJeO=G5r*H1Tgi%~`p2{?g;7rX^Ay`L7M*=+&vWm{$)ZP1Cf5p)EwS@0RS|>6d#+vp z7q`(|F?0To-H858#nsnySIX;GKhT`goIga?PpWY0Gy!5cNvJf8x_U)8!5Ik{zp~mp z8+?GtuiqK~5F7j`0Ey=}`ma?y7-gWu|C1zFS^}ti<3YFkNWsE^uLi8m&zm)vV2J+# DBGZD1 literal 0 HcmV?d00001 diff --git a/master/resources/computer-slideshow.png b/master/resources/computer-slideshow.png new file mode 100644 index 0000000000000000000000000000000000000000..04d32f179101f248ec909b03daa05e5f1e53febf GIT binary patch literal 492 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&F&8^|hH!9j+Bu`33ld zxPlb-Y?kfYBGCDLZWD?YEt`-)qU?kd?zf@cy;&PmwWHN+<*V&{r6uVzW@65=kM>o|Nj5~U-7ro3}_-_NswPKgTu2MX+So! zr;B4q#jUqDb_z8G2)G0W6ddFgpYrp+ys2^8u{u7n@5fk{cdg@$3l#)`+Ew)uKokDS zZ^)j^c>K7TWWsqp$%Mz2k_lyz>dbF$sWI!=xQZ&+xJn*47Hp(o;i{@&;;N}&B4ZoN9vAR58&eZZ;ovG%h&xv4NpnDn4h&Z@-=f8RxP|ptHfYTq V^AkVr{ooBq*wfX|Wt~$(696Eu&Mg1{ literal 0 HcmV?d00001 diff --git a/master/resources/computers.png b/master/resources/computers.png new file mode 100644 index 0000000000000000000000000000000000000000..e11fbd7ce9bb8b7d7bec6e00190cf28a0fa842ef GIT binary patch literal 279 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?I3?vN&YJLDI=3*z$5DpHG+YkL80J)q69+AZi z46^MY%-H|*V=7Qkvcxr_Bsf2(yEr+qAXP8FD1G)j8!4b7(Ey(i zS0G(cQBhf0DcQ3b$XKvo!TN1w^to` z8w_|HE_yn!@NH;d)p-zqXkF@r4RO1_pMPs@nwc$SHHq;R)Atmh7KRm^fA;sMH|%nF zRsrHNba)#c*ySJ%@{WV0g75;h5AV2qdzfD_{{DHDErKUvBj1`sj#@9w&z=LR_H^}g JS?83{1OVSzUhn__ literal 0 HcmV?d00001 diff --git a/master/resources/example-screenshot.png b/master/resources/example-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..5b5a43b3499a309da977706a0c12ea7275cfa220 GIT binary patch literal 9305 zcmV-fB&OSmP)J$)K1 zi557D7&Lz!C4(F`e;quH8Zna?J)0IrkQ^<9As~$(CYTyKoftxu9w=NaSiBHciX|Iu zCt;i(ICU&5l_egZAvT~QFQOwSH9ldZBqNt97=JJ(Wi>;kCLTaRK8q|ifh|x|KRBo- zF-1f@EJk=+KQ)9kBWpb;t12qz7DA#h72g?8m^BoyEhV=tBe5_b@Ebf$OGIWyIa^3Z zv@;y4I1#fo6}~bdkv}C;P)UP9Rkt}1bw^>tHy$5X%)LDdVNyxRI~tQjV_I2Jszg3r zSz2sYL#RhG&OaCZFe#W!Mxsq8U|mt!K^4Yj6DZBR)n~)mnkS5_t$phJRu$?ykx9{i2`SJOj z^Z9(vfO?4A_Z@8Gu*UZE_4#o=JKG$^cC{V+SG?U^RaI3xa8-kXC+bC`;4gRLs@}<{ zU22g(z9Xn3E2(2fb*N`ro#;3rtWOdu2N&+$x8Hsj`Rzl9i3nDk1*@5ICc&Lz!JVsf zv;Z1{9m^IxcH$U3>+k=X0^C1Yy#HkX@R{9 z+YU~#Ts2#8hb4pf2T=xhCXm6w(BzqqZk(!~nHrm%oavvLT5$#eSmJ`8un4E-Ci@}d z*ZuHpWJCol8=E9xBnNaw4@yWTd+u$M0oGBih6KAk`2cs1e zf;(3zxbybdjoXv`Z%@t2ll_A;Q@8)fR&19+li*w^Gz9@CLlAIsQcK{~lY?V}H-?Yj z7#=(`I6Qdr1_B(Dq|3#}&m12d96K`%L#fME8-$4nF1Hz*1kaqA1POfS#;HB0&fGY4 zR3X8$ELo9Z5ITvpKQWqWyNKTK5#QAV<5q2HW}QpYC)Tk5yJA$DXNgh)jVi4}RZmxgQ3uS`*a*K5o2tpY;qXzaT3g{4JZ}MoPEfT1 zmrho7TdId$rmnqmn2r>w} zmnFz8Ly&+xZvdWOt{?&&IW;=kf9Y*3SXZ}_3{rxPjjG^bJHexz3|a}haR@sT5#-@% zgnfOC6n?X^3ebYE*jDgpq8VhjVzDgx29>3)evgZD~K}KNLAC}M%xUw=zx@9XZy-CXazjWE*9akG6?&?vAgr* z?QH8>@f4KU>{2I$_sI;K{wa5N;&fzVp$nG$dgSFo&WGGN-qvz&+G1x`<2G`}VwVCStpmw!O9gCh zJ7`e0b*+(V<85lAe*6A0ErK>lu0Q~t zgFE7OVJh(D_4cY8y*@WS`E+8UUjyv3-!le)WhM1*ef;l#Kl=SwltC3wI2MBdZbm(u z+l_m;E^n9G)p^^xnwtauF4@%_82Z%T<@5KtJqMec+uC})ZBMZRF0XoD+k)OeueLRS z?^KuD>-B<1<92tQ8u@zk)2{cAbZCO&48jCDKRka20{(svjwMbaf*iprK42HPL(K=> zfk5D3vn>0);6gz1!w9yy+)oqGEy)C+%LEAS9C#=$m%F#^)ach&-fMgR13iM`3_`&F z{_4A*e*E_jzyJRG83Is{LD+&ftACg}!_|DS3*51T&ETKY@AA0aLm2R>mmhq?`{C{~ zSl!w^2V)7GDs;8|?bzA=W(rW4K>%3vAppGi@yEg6fBz8-#!jbg1`*&vZZ)H^qZ$2Y zbb<%hLN$|@ngze70Rcke+6yWX)fznq5?KIH}e3S&2@0i75NWr zy?f>aas?sa|9tiB_=QUl@E+JRP8{$c%ZQ7Ge$o7tO8d*|9cjj`ME{-Te(8X=&y>JE z@V`F%>dMzAKfd(quOsD*1X>9p;1;5FCRV=SGgKEoI069ssAr6sgb)z4;cweNzw_%) zKkZ8lumUE~m}K=PR?5<|Q}Iayuur&3jV96{g5Q4k-A_NAfIm2$wuGw#jO(=&U$cUf zs0sJ=*=7*$iM;vYS6_YGSA{ND+6!ucZYk(mHeg)QE4H+T0gvE5P#}1o+?8|I*Q=K* z?FBVJ$?Xkdz+lkhcl*75pFijk1I8mUy&}Xtjztz17uD_LBOx}Ec~!t>#d_@+*c`E5 zn{(rF&fnA$cv^qPjxVS$Cg@dZFQ@~$x+E1)3i<-Qk{|4M*d=gg4AfOn!^1PggN(&v z@XPXG6rEc!ZPuz23b+&M=3xg6^=xvMlFC3rh2=&`se9e0X081Ha$K~0>T@A@?;@4 zf0wxNB?Vm@+^^We$YR_K*g*mE1UY~fKPrQ;1#emDD17rA$R5D^{(CbZc$6{t4C~aA z`7u5K{o2$O5)gVke0Ztt%P%c}3p2qn-^}F1lG*hE035$!x$$UJiH7IdfGj~4;QoRC z4Akx?0kNQ^B@=)yGI+}XJon2l0PuSY;Dd(`A3lhIp*c25_FfOb_6I*k6lH3Z7<1zA z+7Its>L2g_Xnu*hl?Sksy?pudANGpeTb7aC7f+ivqMJ@a~;EcYcHkj1mt$9*3Ln%tfP-$OzfdPs7c3`unc{ zK(Y@68>cQqKN2v$h^~EiJ~DM>lw?AG6ZnOr^NKZrCrp5StOGwF;K_^Eu3bEI@#3{Z zBw!_XCUMwhY1gsei0$*}R)F9azBw|1Xfp_RBr-wX3fdf<4M)RsaqBeZ1mkE{O9maV z1C)p4r6qX)H)0E5rAe{06ebV?PV`@xPysLXUjW}ar~okfi(XOk!OX#mpdug19Ig=f zCLd!2Zj>Z=8Yjn9bY--gL-v^X#zSFH5%O>T1~-Ri!=XsXIuEoq;XcN()Bvz{UC>9I1N$$t@~j-2m1RlOSeOk-Q8_d_2ewJ!nWz+1k!EM*pbTmj{89!y`a_hZAlMX@ zWpZeN*Nnnjo&~=pWWhh16b**q?HK#W3t=DF48I~ibgyDoz&@5Bl|cYlko{Wi*j#Ag z@@ucbsbG;)i;lB~8f32h8XvmuZN@HHC>tGNN!3`N7>p@XlG z6p@WgMM4oNEQdm*Q)mhN&)}9p(!#`b!7t*9B+o`eVcD1qDd10mPEnFWvJ?WBNpS?& zV*`jXh~|Oo*FFFIyTETf{Gq6*NSr|gxMj;9u%H@Q1M7kD2yZ!ypFnajb`+r5^Tv(q z4!xF9&IGh(Py-BlX+Y02xFQ{25(C-^ItmIfp?Zb@*%>S;F$6InXaE6iJ<#j-`UBpe z$LI0+lVeFA4I*dl1R)@xD=-!lii%XiB1*8bq(l=m0KwRU0RuzwEq@>w2)J)?mjs_h zKzl(IkX(%^Vq~zg5~yLpQlkk+bU+_kkM?@KAb~+~0xiqM#OB!AOCaaPb-JKq|7Ub* z2`sV#VnG5BE}$BHptdUG7WaX0)je)yjK#rrp6*jjpdf^7z^z3r!IH`nN^ljU@Z!S4 z0tvdRP^z_FXZ+xp z{M-NH`oaWQMEKDeRQtf7Z`pwG`H3eFrY_&SdGoyqGvLFAckhf}zp{n^EiW=Q3qnBO z1Mm+I>yjA|5tfvYg6L9N8J_n*aOU$$UJnm&{_exOcQ0MOdG6eM z)YBGs!S*|Mpx+t*T$*H4Fd?1%7BcgA=$B z?aFCKCct^1$L|ez-5wM~p8)X6_~`i8C(oG^cx8Ni{L1La#NS?78-QdJ7G%&-{y7AM ziye|AA;3Z&AVNeDQ~`U@LGDB!=t`guoF4(zj{wQ1Eg~bRCO!Vi`8An9jv#8nvK*Ge z2B0W|C6y&c0#`B$+^Yyd0+tK*dqlz!9ehq2r#ZaaY;V&s2=hgUmIhH2AF`LqhLQ>I-pgs$dW)w z>Z0a>ZVx(6(Btz90H28hgNOi-5Oi$YR#31FRsk*pP>{j0@{KPg^-=mFKstkHMVd4u z0QrI`1IT?NxU&>s-f?)$@}LEfEBOC_x}alQ!M42p__1BJ0P+a6J}{{Pk97#&u}38Y zec-m-;jj`3D@q79ViO=!uxP6a==QoPKzFJn&=^VpU~cX3tnbpLUOAkXx2<5)>-i!= zL+;m^TBTR}Y=sMgociH9)sp2XrSJ z;5!7MAcF{ys{8~5RY16ot*r<8-Tok$3c}Y6dy-`$-C_6c=x4=%d_fg391Mh2KsW^q z40*tWI%xpO1oo^bf$Zr_A(6mfP}1jtZody6m*7oKK(3(%mo7ftm6Gc+Y$jREYuR{ zmco+uv}VBLyXAvV344+k(EK40I-qj@{_OqxQuGHnisai1YJhS$rp^O>w}4+0DOsE37d_naHRkz=b4@vMOKcF>%yI}%{hK4X;P6C3AKG2nv zgNXg7ULn9J$R8*bFag078HL*u__!ei7)b;$KNSJ(KR(d_oK+MEn3t1do58Iq0oclK z!o(^y0z_|T0ZIq75!3*KLCKr6&kL)7b`gYtwY#;-76ay60Dao~2i}6u$#Gw(`3yH< zyU$&NfO>7q(*SKUr~_&*a~SgbeUdkMo)@M7g@P0ystxvnTlMuoErD*2$BSMtoRol~ zX6(r20SW{G;LB)N&g*q0^(fq7{~4$ZzzyOI<{N;iFpaUxpd&XI5^npvz03m0Axud? zZU!M>L3!EdJ>8>aWo0Vh2AgKQb!$PA1EM1nTHEbEkGuyABDtqVwJO&D#e&J7Kr0}R z5GL^9U09>zS}wo0B@rv+-d)j z?f@_kMDK3=@nbU}UrzO6C<+@cVE$m@I(ZtOSlqn-FS% zZf+mwde-|u9kA0z(2T0f#BbHCj>5Scn1ZVS zd?x3CIv^v1-Er* z9Cj-PfG?y3pdrYe5IS;Ag4P6DGni_CqJGekYZlDfz$e6hIXa+zkU5wIdhqihZjVbn z_nMw;f2NkR&>+&G$AImfwi$$g=dN7?dP^XKDj++9hG5D9GBOAOVFw5@2u^A>Kye0B0Pr>o zkVOapGcX`Lsm+6fNCJYX3fRd4%-ubJ0S5-)bS5h+fefZuo(1PG>r_$&kOiP&c_`I~Sx0&KPOQxlj=R92P>n8^`LDL{^3rwNcGH1IeX1b*96 z0dUveR&FEiwg6_+fFi+^0_<$J5##}qg7jFDvmq-hCoSm*5eRq`Ca|4NsI5}NY0U6d zt@abs82(tCm6Mk$fGsU8?YsTiV-2%@_j4mX^X_ zwE32nVppkoAg>=xAwW%#KOKa9AV{FwC%qDm#fI@TCNtA4NbT390AO4jL|R&KE9Mb) zLO_tfh|dK9ui`$?$Y7?`By5DY2EjFCYaFe7D}FsfFrAOuum z$}4X7D*;78fSFm$3}zC5c_{;ED+mE2kCmW17z{soG7RT~D1Mpb9xuaMiuyR;|8EEXzCGD7?3Tf?+feu#Odkg3}%{E2vY(OHDN5M4M+O5hUF`?6 z7&|3EFe@`HB>xw&ZwP_@gX&WuViK%yO+3E{plEtrRnBHaT7k;+QQEuNT4Z$>2`uC1_(2Fl%GLIuDLR`%F?IQ+Rhyo%!EzY z@m!KO;q9Oa<5A>j3xiP4pcRmDEJ9``nZeAg6az$pya}O$-VM*-p^>gSP&Sh1$Ks03lQS5S}_ttfPfGyLcw%HP=n0U95ulW znQ4w>Z^8)huujO!pb7~1)~mA_2xt~mpQ$q%cZvWSesC(o%%BFS3udRKQyEONOlQ!p zaE2qvr!fGqkpx5&LPiF4KwS{wSu;pZ2-A{&9*Fut07we5GiU&6f@=Ku8ANLdsQ}nS zCy<>%0#Fsy#7rU7C({TpJK0C!8(JU8%U~-55DEgo3>7bpH5em75YIoQ4~#R5e{Ih8 z1a8yrcH=jycc4DdoWU0J3LyoE1abO=g6Xgm{D)S}|46Ll&~Ivm^h@|Bt;ox(sl;vv zT+8(L^ByR=jQScTxsd^M=l zvxbHPYSm~3%~U$I4j&c>>PriZCX5KP$qc4hTXANZcGyfNv72W(f+2@0?R|TMANEpI z#Q0cua{(`>2Iq8^R?zul(P?-MID@*NT8-Aic056i&;Ly$n!vqthA zHVI-kde0c1@S3pNiur=p<5%VD!6sZ+SE}2Qg!T1Y!Un1b zWDquqGpG(F`~<3k8KPF4N%nqO;tYxf^YfP>h_Y8%q6y**meyHPSj87SU@i+G2%&uj zTZMqx+KzF$?J70%SnB1pVsSI3{6N80Tv(iwEH_|jR0*I}5o!M?0uufGnz zi4sj`DjDS@3yC?M)(jG5q>t5#Nhghv)?*FEt(Jq#v&o}55@LEak$OFmdP7)mnu-E+ z;!Slz!2{entP38X0MQfUe}oCcPbm6`T~)l)d5X7AGJ zJ9P~;oI;B*mTD3T!bQ<_Cug3+Ura!Qk+X2cDeGRb3b@G-q!ty>$6c{X9Y(YT1we-{ zaaI%Rm^;Na`fgnfw;FcsVj(tJ4JklCw{8`Hi3#Rd$^rH-N##q$iWU$nfKx+nTs%nJA z$zUT_(6Nq6pa^i)f?1|+(6TnTiAf0jzzLoOng!W_rGllcda+-_u4M{3VDymQI$OY1 zH)Eu?fiI|$QJtZ75fc{a3#+UQ;)N}zU|qVt$|iy}HI0c05`gQTTW5^L>l}ZwVAclV zNMdTpMTNvjY-+}|V1=GR0bo73!n!&^5>Ok3*VzF&GL}b}m1sxEBcz&hBFz{w(tzaR ze!bQiiraCGS+L0}xR(jI&JOU8%pkiTB-(DFd0tagL;n+Rt{28H5 z2KQ2c+7OJ+9X5i?9!s+L8QdhwAY)!wgq~8e8A>Y5&(CQYtT)dHS1!2M0;mhxB=Ap| z&TPuz4JB5>#9A?SGe6Hn`Bkm1A7lXPf)2-@O=zCYursK2f`!Y^U@0qub(|eL!F*6K zr)B_Zf~x||Sk0805Zt8CUd)8cnGlkWbydp(#I1OjeFpyzg!zXwzt8MF00000NkvXX Hu0mjfg^|!S literal 0 HcmV?d00001 diff --git a/master/resources/examples.qrc b/master/resources/examples.qrc new file mode 100644 index 0000000..1208289 --- /dev/null +++ b/master/resources/examples.qrc @@ -0,0 +1,5 @@ + + + example-screenshot.png + + diff --git a/master/resources/exchange-positions-zorder.png b/master/resources/exchange-positions-zorder.png new file mode 100644 index 0000000000000000000000000000000000000000..0f95dd07aad265b7ed583f5c5014d0cc7fe22f56 GIT binary patch literal 1984 zcmZ8i2~<;88opVcEMXlG2uq=ARh9sPD zRSS5Dj`rU6002iWCte6o%(87sFh0}OzJLd*IF6fcYioOZjIV>Kos_eo7y!}FmQ7gm zo!lFeR9V7$+1l;-veL~(d7!kkG_>%qTZ?lyOY=gv7Zv<{f(}i@OhHO|0yM*gVVFoH zLQ#~<<-&+I-iK*wYATz}h9LTck;mf+1cKz`e8paGhp2Nu#hjkpu4jE*Oefhj|2q<21s zW=O^;pI9#M{7s@O4bhzjXoD)`=}Ns7Ltw?fPGV`n?6ge9?9elnPw)~LaVNd=PYllE z9^8Q6#_X^R05MbfM_-20G7nt1WUa+X7_D7!r`2yYyagDYmM+i+fwlHc&s!wk*LtlL zyjcRV<-*@b#U;EEd>U32H@q2f$r68kBY&j7S3%}2(we4oSdH-8`d%jFtpx-kiELwQ zYiCb!aCCHWb(`<$MfLXf@%4Lm(R=SLUc4kQC^#g9MhgoMU$!hFGICW^G@TKHvf>jG z*z80Omz$IfUlxxqNE4=uL>cQgW@cq?&V4^$Qn*!GB->F^T2{Uj!*=iata@Kf&He)i z<+X>uJW_YG{_CdW|7dA#YwtRF>YM*`t5j!tdd{jf=PzEm+;`<_|G>4u!Rt3}{CIOn zduw=P^v>OTy0HfjA3ide#wRAHrha+)^x5?DnVH$y7cXAU&AobMjvgs%27pZF#>b|Y zjgF5NWZy(xY;@H{pB*0-oOriHku$mU`8|rqr7Cre8dR!h?jhx#NUXac%)h_iloi8S z*}eMQ@c$^YwcF|*FZSU)cR~~J^{6-5@X@h8n{C>?$AP^d6PwRUU8_gpyNBKbJ-=C1lEhs zlS_SE2?5hSZVx5#z$@zlIk*jkAG2R&o=8Nny$a;>1#O_K#%`sY;Jfa_V$qTyAj|n% zoD&sfbR7uI2i!XmkKn z6K|83lzDr(KaH@_ypsKTzbn#LPLHX%*m8q#LkZ7mJC}>_MJ};a{hc@bFM!dwTaSXk)O4Y zwk4uLegS@jLK!Ft>rT_xeH1}3HR=m%S)=MYnybrR=UbD=5zkR|k@--ds*0u$5#A3J zX+l5Ai<|zZeSUy$|N4rrD(f0mnk^qR(98#ZY`FTw+|%Mi%}DXryA0=?=?X#>>H#uNdVJx~=A<#s5yZqoFmCJi1z$OD&_50|6`l_dT4nz<%AC_cJou-iX&rLY^0J*%tZ*Yd>sk_CG z8RqjhqGj9xbFM2;-$U*PF+V^y|EoY-uoZJ5Ql~ey#mq>m=c95|Sh29S`E)~A1VXl5 zq%GJ*9eufr*E$)vu;x{F#gx{$+&r?c+va3;Z!6Y*E)!kXXt&hp6Iq*Ye(?-bo!j@@ a;+lyUuB|W)r_(zu|4(iLFJ2j!Q~7^^jWQ(w literal 0 HcmV?d00001 diff --git a/master/resources/master.qrc b/master/resources/master.qrc new file mode 100644 index 0000000..407e128 --- /dev/null +++ b/master/resources/master.qrc @@ -0,0 +1,22 @@ + + + zoom-fit-best.png + camera-photo.png + preferences-desktop-display.png + preferences-desktop-display-blue.png + preferences-desktop-display-orange.png + preferences-desktop-display-gray.png + preferences-desktop-display-red.png + splash.png + computers.png + align-grid.png + exchange-positions-zorder.png + powered-on.png + computer-slideshow.png + spotlight.png + media-playback-start.png + media-playback-pause.png + update-realtime-enabled.png + update-realtime-disabled.png + + diff --git a/master/resources/media-playback-pause.png b/master/resources/media-playback-pause.png new file mode 100644 index 0000000000000000000000000000000000000000..82dfc9bee8d7663076e279825adbf97be3b7d56d GIT binary patch literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?O3?zSk_}l@cn2Vh}LpV4%Za?&Y0OWEOctjR6 zFqFxFFyrz=6|aDTk|nMYCBgY=CFO}lsSM@i<$9TU*~Q6;1*v-ZMd`EO*+>Buu?6^q zxB_WqHBC8&C_5mFu_VYZn8D%MjWm$3r;B5Vhj(&<1Z&0vdjW;Zioc5!lIL8@MUQTpt6Hc~)ELIFM@ zu0UEvMMFtVQ(0Y8Sxrk>O*8)cTA*1BtR+Ey!3_2;2ab3-OYH>;T=#Tw49U3n_U1;x z76%>{#~>xvrv^Wlf3nL9-z$^LkQ-2-u6w?CXN=zb9;THOxTt_Wq$f<1f26bDWAh)w zv!5D`PPf0(iPza&v-8}i2E)_N*L3)2S-pC-VeR96dNR8{y|}bAd%oVhUB6y^TKjsx zm_Srx2Ipea31Wd!t(lU`OBaYuit5bLT)lOJ*yX6+Y|H=m(qboFyt=akR{0QkCym43T^>}{t2F zw<&G|06@~-4&@AMD3ZDc1HlI2=ASa+(XKVxe|tC;&j+^?gB@oXBZV zL`2xQMPNfn5j0}BFF>QwbOOkM6dz)!uTDs~U+$c#A^->lV@~00Kr00D!+xVyW9;N;|V=FFL`;Oy+|=H><}{|Q(u*45P&Ed7rF z+CVc7hr{FXKj3G&Rs31~8~iQZ+5*l2tN+FDk0nq9hk)^;ee0Eeh~WN@jX$rw6+q{Y z*g6eV!1Vt<5b2M>XFT28kp;eOT@lWXSO6jf6&8VsZj+GQE+s9qLso95{4WX!MWtQ4 zmG>Z3RMqyXYwXk1+OK`!pw1y(J)>jBCZ=W`ywJLOWudoUu3;yer|X zw=c=hKj2(oP%t?pG%TDFL5reaV#LJ8CnTq)rT=~h8Zsz=vn|Jpfv$&*` z_26My#lIdseo|Rg{q$MQ^B3&ey7~rAV^ed>%htB`j?S)E-95eiZ{GfUU~uTeNAAez z*!Z6llT)9jXJ$Xo&GWwU7nhb-R@c@yHU;pwYZw3!8L~%N;UWh=dw3kjxyYFtKwHr% z1=g1J&N}DtC%loTJUeydNr`43`0B&+e$w@M5Js=>^qSH);dK~y znTcCu#nlsm2?92Yb{OC(0RGMc{Oyo@h(FC;uqhhT572+>jq%yTQ)K7_!rUVk8$(zK z#tH2;kHztCgEv`PihwwUxOqE=GknWCPu4tCS5w)n&I)hg=ibQ+Nc42BdE{y;$cQ+$ znL`}mwS})fSZVf3kh}TkQi8?^w^0n=_hp={(ncteLhik={ziUAD9M(u9>1+?QI@<7 z_L1Pn*43(2L)Iv>o(Oq%JC&q|Uv`cXi;6`Y%`3v+@^yqyjP93(GDYev(3M9z{3K zfpt_v0Sw745q>V2Iz9|9x%ANT!;PW&Oq@(#_hNw-M8b^IdrxJE^M2E~)u>IPm8+XM z_VPugaqGMuFFH@d#bjdl&ap3gb7cLDn8I-xIe6Hj!rO*7MKhev5HaFYX~}Y~ex}li z*Ir`#W=*Z{j0fTfmaiL7kSURk+f_V7q!=aSMk|V*LWjwZ?JTkZ))a|X!j@9kJ1vCX z{Tg2~au}1QeZZYn)>0lI!h~@09FVzTs!fAsv`Dt&d-04U%iGAdRKHrOIRQ zsBaJ}zFm=bDy&py(ho59xGz1n5KQX3^)t1I# zhf|){6+Ve?vhUyT2o2OTF*`|FRd;YJjD${Z=%gZSat-22OSn_4f7Skn36x;88cw8$_jLcp2lCC8Sq@|2#Ye;jAx4%v9i6nF7nj2O8*V1 zg{h7ze>>9lE>qZ`?!3$H0#WEULd)I!v1`Hog_&=uhms(k6f0d9DqDXp$N^>++Y=HU zRM2&9sQS^3p5qm&(TCakeZdr0D&0ymNrJnRA4z1IL6zIxNaM70GPHIj`jM`b-#6k3`3#ZJS?~b>5xomQ%KyAmQr3BC8f~BflQ}nL~f+>>17fr2-0$q{V4%H|COO~ z^@iH}G8&?7INK-dvNC+?_vq$!+tt=aoIIrnD`)eU!V_P4Aw>afKBV96?sVXkxB3Ej-$ z`&GHYQVXr!A=nSy+Ansk^^?)Z`b1}EyxRJL;<@LlE8*1xn;l6P+ph?IDuRD)Gi16Z#g z@qUditvY+PZsy(y1-|{rM1c6Ng}DjhNE6>Hx_dYCLvE<=&DxIxMn{i~ND9A@^?^pO l2Rb~y?GAo$Q|yYH>S#g^8eMYc_rCuw+1ogx%B;`E{sj#2K2!hz literal 0 HcmV?d00001 diff --git a/master/resources/preferences-desktop-display-blue.png b/master/resources/preferences-desktop-display-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..5668baf1ac5fc9ebb0d3f322ca7e5fae96bb7b44 GIT binary patch literal 1242 zcmWmCdr%Wc9Ki8Qh!qeDY0;V7C6_}{Anzb=LS7I^ibRUhY8Ay+bktT9n4xuQ45)}; z1SzFJ5VftPfT&cVV6Eb7w2X)d8U>8u(P&$-kmQm}AVb%g&+ogtGyC7}u?%&pzc1_y zL6E;nnVbn$-MjJj0=1h@y1?R2y}d`qR-3`mXRmTYJ_N;Ny&DoumjX5rtw6E9 zU`_7!g2JtNIZ$C?A#dlN-T9wy-J8S9&D&vkD}o`2L}aYeDBfNEW`=fPhNhjN9h{-* ztZ9;O*pW&*a?*}e*^zJU z^lCf(JDgsF)4#{*wK%;Fr`O|j15R(i84V7`DF>s`!8q+;oN+MDIv77V7(Y4~KRMBJ zPV{Fd+T=ukaiYymw8e?G66kpXZ6nYN1oI-nyhJeD3FfaZ=4BW2ii_FdVs^Tizqzn3 z7uM~@uDY?`-PkqviZIrSF!qXYfD;zyak)aCt&5s9+@z5JvKWh$sQDGFk{k#zX*7u`7Y- zxJV!-J_?9Uh>jCQ$0x=B3F25l1W4jU;yBPmKpHQS#Dj`u35n7KPzfLxNn|2WX;LB} zOBMriKp_E=Qlvm~stiyl@8>sLo&* zd76H`(udMX)_=K~1QBZT{B%nIiS1Y6u1}LPbBsDA^txwx;G>U|u=mC+(4DT}z&u+0 z$&Kw6wK9ZypF)OnEKGX>|TUB~`gx>*yy`B|{osTu@NvcFmPK%A00DT1O)pwFhl|LT1_%dp$oet1QGZ-#BH;e>S60xhM|cka!WV&R?K zT^{`E*JgEv*B|BCXPgT@d(}x@e6tMAoajfQ>c`#a!IoDCWdCKfg{)!USYJ|ibId|0 z-iLa`o`*#9t&8J~59KVQDrxQ%Z>es{y|in>tX#b;XwVgd92{yMzP*iIy#q(Z^K@Kaf{)0!fq_7k%L6Zsofzn%{8g{DRZ#E~6*eR=Ta+59UUTFCYE(fKaB zr>9C~xpDo>Ub4E!CzcdyrbMS_J4DlX?-2?Abm^Ym>Yl#7zA1j|p+&`cJ`eYU{{^WO L>f{UZ?Bf3cB3=>D literal 0 HcmV?d00001 diff --git a/master/resources/preferences-desktop-display-gray.png b/master/resources/preferences-desktop-display-gray.png new file mode 100644 index 0000000000000000000000000000000000000000..ac58363e515a57abd78d7dde352888cb4b94e9b8 GIT binary patch literal 1055 zcmWmCdrZ?u0KoBI3v3m}pc`bNh7?fS{@P!EErmYl^VdS56{yO?41q+gs2dwJP%W8? z6a>mkVZ)>w9SXw41er70*aiZb8x$gvLXk&#m6w}Lgt_qscV0gCy}RW8x?IUoO_ZlQ z$sK|qPqiu{4y<`^C&2~u-HeQ(0+{!C*i?$w<%Ep3nJ@!DVFX~&87LhASac@9W*{sE6JRq?AQ(Z}2q=rgM1z^2 z><|rK|OfCk+!XIiOT2BGrY3g+)b0#l^)Ylc}Vn zq_niOtgNiOyu6~K;_JG*Jx>R%14Df_`;)QX!9-DHG z&&*CZ=cnctrso%4F?p+cz1~(<1F9&x;rwy}W0#YeEy3qhfDp0}$l1CCd64e2KrY zk59%yP3oB&-&^6Fha)NYwS#=$R(x%*(!7!q1|28;9ymeD6Y$*>8Q}&FF4^cqfu`FLd59`t79ka-YHiyn3oJkMB z$cOJK=2aT<;Sbx~S|ybmB*HbH*Nx^&p>uaJtE;s*-UEIg?mn>>yxL@p9l;s`DD{c< zyr}(=u0eRBkxA~af9FqPr5BWY#kSZqntT2){oWYE@{GmHNqXRgzv&~> zi|AF`BlU%1ZFclrjy7n|T_y9|-B_~l{;vIUc$07*UGc@xmE<>;a%nENypuU(6p7Ym zQxCdSu)JSYCwumK_>pG^zB2!l_LHQ;Yk8fuswud6=F^2=p6Bm6Uu+am8{2m7|1sdQ zBk$lISjhTzqNnqRGQ#cdRZC{fdh&wzWBqP_HyKX6Vy;$fYTLKh-*}ySyroOLY~8js z-ja21ciB7GXk@BD+rBcl)$hByU00KSi_#)X;Sf`Wy*sT{2*K4D;O&}^d5?;XW3Rh< m3O%lNTn_rR#m)EtjOm8(C;NPQ>hFX91gVvph!(}k{Qm*0`>YQD literal 0 HcmV?d00001 diff --git a/master/resources/preferences-desktop-display-orange.png b/master/resources/preferences-desktop-display-orange.png new file mode 100644 index 0000000000000000000000000000000000000000..2d1f1ea0741b984ba85e88d44aca3f36288136a0 GIT binary patch literal 1206 zcmWmCc~DbF9Ki9%AqFuLf`Ecn@F4dEBq1b_5W*2kks@@&fdPu(H3d0pw6+1%D(Wbx z2!=!MtBw}5gBLAUDRo*QpdgpVaN5BshcM3DXGr^V=d<74o!S3(&qhUtlGnJd!7z-h z4pT+Ln)hjvtf0%rO9&RLY^6GeL?Vri?=-^E_HbBSHin54K23{_c_FaDHs%EH&e@fb zmZM3|O2sr94fjC$q3o38!>QbitbOHkGFJ?3AYgO(Y$(r*!xeC#d~Yrg z`0#+2kPo~?0^lR|0zzMJAd>h1v7Zq5N<|Wx$j@I4qyfG_2ILZ1fCTCf6n-+fA2c9P z>aUPOCfKhPxUYU>K}Oa{AJI; zkbZEae`xgi@YulU>%lR@(CarNhVfD3#H;Zs!{qdvsTsW31}|QXUnSvJZSj&dc!?ce zvKB8T;x4^$1dqUvp^dONBCJ5Y@$ss%JuOGkAKRL4a1ETW!G)N_aiF44dv8u>({ zfN1g}n!Jf7AEH@EG>eE9G11~{nUvZFZ)}kh9k~lr%nYYtn59&$3XC~A`tP3pgJTLx z#jmktd&q+PKHfiXrvxjfw6XGZK*II_M_@zDPCm7bpcnj>?) zREmQ`+lQi1ul%lF#qfBeHYI! zm8{h#hF;t^{OZL*`Mg-eHnrOs4B8A2r>8r#(z%5@%PqQ1KE-RJnc+XAyS@Ang!vA% z=5{vrH;$UWlm1**Y*pY=Fxxaycl^q%&BK|JZq%(ZI~8dK1R9cjGwk1fV`*Jqypfd` zZ@fDhBp~oX{(ncx91X*_O6O}g?C-W*>=g|Zbu1Klm}OTuiD?i2cwy8QPD#mWms@m& z?a6;99nUoVUN`oId1T_Pg}K^Lb}37;b8a$mB+fc2Mbj*9{CG2~AbC&joyE||*bLgu zD6?+fyrNGm3i&E7gLlsUnEb)=yJH>eB5vBH2xiajyU=O6r_}_Sw6inaO=sU#=~r@% z6_w9&S59svS?9Y`tWPK?yM_59AM*=ok)~qUp_8{ki2>flJ# JEoIV){{h@m1jGOU literal 0 HcmV?d00001 diff --git a/master/resources/preferences-desktop-display-red.png b/master/resources/preferences-desktop-display-red.png new file mode 100644 index 0000000000000000000000000000000000000000..c4db932c6f62e192c0fda0255a9de4188ae3d2e3 GIT binary patch literal 1239 zcmWmCdrVVT9Ki8gP+kL-O_0`7M4-L5x4ms?fzn>u+ftCnL;`G%IR!Qm)G0{Wz!w%p zgZRRzF$@ioIGs~bw`>y$j|Jrog#xw~>_ZVJMu&q6Y>NAle9rfGPICS`=V)@$8ZQrq z2ZA78@o}0In7W1K<_w*-ot}f?tX0RSxw*NG*w@+M=$;>!u0@cDjSGuL*TupHp%=z{ zQJ9*yrLZWgAR8$vDk8S-+^+p5D?gjaE6AySO)?OK)|dQgS`1ufqg*zMvoXNqpg0Eu zJPsG|QH+Oj0UyNyfpPg54hW0~1YDfp@_>NL2SS`D!1+Lk6F|fxggmGqluwBGP@$Lr zB8dQmh6#Z z;mhvO4|cBLborrOepnYB>lzs9reoc7Za0J5!@zr1@_Gypdsp&$nf%@%V;_^~^C$HF z0zFHpAAZ!&685u1{Q;tZ1)&40fOx=YHmnjG0)fO}8W{|f46Y6vS{*(V6h35rJRB4; z94s9ku{;cxJ`9O8hRBR;AU8g?n%Hs^2au1@s7DsN8B>@srP=mmgsU9kQlmIfjoQa7 zPoG)G#`Ee1%HTz5g(*p?2sLinf*?*i<290;Kd}Y1jH1 z8d9usIxzDSM!W9m+VIRJ)3yHEUH^R2p_@YUZ!mRt()t5OSvu)@*UPnm*B8CsjQzei zZkNrljCRiZ{NI1pr+6N!OkA~@QK6l2qU~ngmUxREl-HHL^YlBt*ah7!=udUYFojf>Q* zgv0wJAC0wcZAqEGEo6ILOMUb4+1t~|e)*8dMFyz zlBV9h#P)LUj*}HHqP15Y`c31(%IaUU+eZD7OzpbXye2D|ZRhxsmXf$ikNZsJ9KYo3 z&Rq6$mWNBHJYhfAYx{pF!rUfp%ov=$Vt%pb-mM?TmfRO>9QCug*30)=o6-$Jt6yic zlYt)_@j-U@$W2elRi|>1hu$zX^mOj~qVL{WYCf-?#q8~@l3Nyr_tzygrU%V;jyX{$ z-q&0Fo^N=zthM;V+O`1nv&NO@)N|QQ4KF_&S|f41{&}`C=TKsXV(Cojo&8y+n4TwMq|RlH^JXJGYa0eyPi5GhHr?($0TX$)R_nW2P=N_p#T5? literal 0 HcmV?d00001 diff --git a/master/resources/preferences-desktop-display.png b/master/resources/preferences-desktop-display.png new file mode 100644 index 0000000000000000000000000000000000000000..af00f34349df5aaf2af2c8f7ef3c9bd673b49e23 GIT binary patch literal 1193 zcmWmCX;4#F6bJAZA`;P1s#Z-p7O@qwhY&*`gybb8tQL@E3RPJKNwrRaVQp*6R#2+8 zjUVdBQY+(7+75LbwJwMq%Az1bgvJB`L!ihGB7u^Gyh{&ve&>Jgow;+r-1BWrR4CPz z?uua;H9Sle3%z8cC}hZG^FNq;w1;{Sk90UJaMM7%VRa+lvV}-VC-k z6L7p)fXiTV7%aeLumO+B;xgHQ$K(J$i_K$k0H4JLK5P!3%>_Pe9uRPNJ{(BCFPA6a zLiz}KKyZ)`d__J$=qCUN#lAq~F9d!82Z2~30{(%10a8CnkQfBY{DBn61EjJ5$RL1A zq;d(QOc5A_2SUn$QYu$SA@SfKpis$x5~$@MI0Of(Pz6wjDMP}m>KY#R_IK+3dD5qU z+HdI58+FfL^ce^H2Zr>6FP{&Yj4wxCm_`T9W5c84rhi|Jj89D2iK#VW%7Lcs#Izkv zJJ5`cn6aZ-8!=}k<{)io-b&2d(1MkC4Qv1{T8Tv)dSfG&Y=mV4ve?nGjaas$6+5wF zNB@B}w7Q0@z=3QIWOtx70y&6v0zt_#E83(Ffxq(8S;?-6g^la}RfBg^*hfSP(Ts=Q;3bR4>A#+n%~9 zQib?#&stJe$b$uEnfAsl+(fbx34H?3o?6ZU@k&!lSBW2YcUt?2#B8a2xNos$#3T9E z;t^_dGhtA&v=NSim!saarc3q4!=2jLgij;YiLYKewOpmGtcrTpZ;On&p|3@e_e&B> ze=Xn@{d5M){wA(4;$6#kH1v)(&F74RAF#A?_jrPYRPFkev(^>fb4joCcodv(90&Hnvn%zCY+KiJ+k z*6=oBn5x}l??=?OOHu|%@WWNAjW!B>*9M98kPl(L5GQ?_Ia zWnU)yK4drIr|%!%=l9R=pU?MqpXY9`=iGD8z2`jVo^zvbnCLOmank_+z^H%yni&8< z2mk2#s&7s+PiOb?FNVEt;|l;xEq^QcLY#@`6y);LvG%)#arMJF z`rH9XvvU6mUE*hclQX_@0k*CtN1Z_Pp4Hz|6%>gmo;qZL_@ zf@dzCKXX3q71zrtxz~Bu9wpvjNr=Br!vfNFHcl2l?Dn{)8`7$_7saT!;0e9yY?(cT z*!wav!ZkLTskSgOa>Q|-Yg<~4>J^{^J`vTtNjOh;gDvMVgbgP8Paqm%)6?tJVyApg zSF`1aYO=sY>26#DpFr6DG4#(Q`M-|v&!lT z^|u{9bJ}_XpJ-vCFIja?NpA06Jf)ao&jo~ip~QQCX&ePIN&sOwT~!jL@j7UrJ5CEC zpFxtk7=R+e2VKy>l@~%s7Srr_>WJ76kw4vRdLBZOdkqK+wGA1WoQM&d9_CGDn z9!xpRDqs*F`Mn-jF-|;O_Ba~u!++fR<~xN|yYc%5=-ToEt2myt+rK3y{l^h?j<(gf zNyNThh1Y@Au&!h8%danW`i9w0S80ts6Z0sI`V~dCTui3BEGiWejSoM~_4%q%ElAMZ zmZOuAv($1JMPm7^*VVduS4y||unRxvpS~HJR?{#?M*d`Ja}U@{G#{FT(Ia<17ZnUj z9WN=JBk*XE=wAbJvkX{~?mJeJg+7(PbBBJ?)im^2oLkVcuT59+3ammtWqKWyG(~Y-yJ{DSSQ=Yxz)Y`Z1IqX zQ-Zl6g^lFK!#ijq=fFbb_nMY3Ds#kgw>Xwkw0kznA6dC-sUeivcSuLw^kUKm7!=#@3Zb-h$$rNvC z4;L&|zmjCAlwHm{KY}wdeMi{n)sOlH3OJ#Db8Q5FYaV8nz%c-Wkg!R%A|`3z-EB&x zhh{Ywz4nWSZaXa+wN9yxn_?SbHwj#qA-wu!N8%(&O1Q3opHM4x5ZeBuB%Ya|CRX52 zU>|anq&GtV#q7Y&TgW(VXC|dB|IO#+fe~m@oH&s|{86izt6CKVHHInV^&tqc*EA_wH6%XZl>N{f28)e0f~C@WX`=f3 z?HoiarH5%UjGB;T`)f2mvCP`7#Ev9yU!BkABg+$ns*`K!9s)3B7^XVT$(p8Acg~qmW zbVtgab$@a=_rIQMBT%}P=iH1S%_vG22T92vi>?|C(O??<`KHnUo$J-IgB4^mn!L6; zgxAz#jNG$e@jcExywZ(9&pkI_JH*Eu9) z1|}Yq_U}^2>I;2&vTdnkdzJd)x52CXDR#c7mog=94fD9aLQF+>g0TMP)Q8&P+ zxfpwsARV#8jP0(mAnc&V$k1Xdmz0OZ5W*4rYzb8=TpAqx4Qf(8H762;2`*V#?Lf{y+{5aAjg=JR?M^pm~?lIvbs!r?rqC zf(R}h;@l!7W^`SDyM$C0^T?-R<#{Oi%* z9GB$n@JVw)%TA7k_lx`2l;3<}$dGe&S#k+U$D~pu8>6RG6ZGcwNN_8{>$lz-8gt^l z561|MvatQ~yVR?Dtb{w8crcbJdtK>W2#GTtX z2h=OkBfVt=XY~c{c#c32tO8+8Jf~bZw^ZB~W5HNy!{f}Cb0Z}iMI20H9OEBvW8VL0 zK3Zw53PS}MNGbTs|27z0$isf+B8|--K#xAdx6Crhd4Sv<4tze-d8bH{Z-xMNMhwy- zTTsSAx2jUqT%7rS1lC$I5~o_+o>EdJ`TPC-rw7I0vtE7@zauI2R4obFyaPNDM3tTh zU9{IvhI6AOeG3_BF_HFKX&*{|4_>tM+N4df#@^F6IZ2OAv@5A$^3ReaT1&nasW^+) zwJ_?6zOd_1>`PSwGk4ZQRb@c=U^{X**(d#n^OIY|7>uT8Vy=*3r4^N~cIf(cxythd5Q% zrh`~j*?IrrlW=Igs`RFqA8h{;7s9w9g+*UIg%oyHgCl^w*rv+2Wc?twKOjH|-~LQC z&UDsTWHFQb&swefQ&~gag0#lRK&W)8tARb}+SAUOT`d#m;}}Ls^ntmXR+xH7vY8%@ zXB~|cNC|{IScyo!I;0{v5;&8c$h)POFND%0lsz-rdMO4=RjOVvR+Y^pMy zTJFt!f@FB0ShJOnneak$s@)RsFjsz;scc@0khMmd+3 zDDD}|WPcfFVi1YTOuo=iqw<l9E|FroQcyl;UKJ00`YZLkDbaDabz zUO&u{Nq)GIwEHh}Uvc~XJJGqkV#i!Rd^V00yZN^@G&~igWr*~0#(AdoR42lyu#?}Q zdRL?YtAD7E@M(VC(fH+um142R8CMqT&B5aaX#KCSS0AAj@Pj2+ zESWu@;@3A8Px<=s4P|(JEz-SL>M>b;i+qt7dV?_kY@bH8yE@aplz7kB;dtsQ`k>j| ziVN10cIInvhEHO%Lyr0!SIABAgNy*!{uKn4hH!aF${SEz?aA{{FBRQcIxM?ty~h`O z#g9)}GDqYfU2DAoX1(6aE>3I;Ae5c&S8r`gMfw9JD(asDYPivTz3#?x;U37If zbT-T9oPS&6SGcN|E_hr4kw+3DHiFATFDnuFbv5#-#R348XE-?T=^%Lnx?1CqAA7^<$X(o8i@4)%0%1ius68#?6`d8 zOE(Fj6rjY~&eeLRW68-*G;5h_QXr(9Ce=ONIa}iXY<0QXj*KRFyatuG5RxnLJ@~#- zLm>aG9UL<)GYw9w;B-pScuN~d2+*l)FUhxC@xE|IUrp}^TQoyGA#jQ}dRvtE^iXy; z3Et(5$ntJ=qETB5xV_i5&U4@f-+vVh=l#)~bLy_7u{wwdNLRd`mgvx5ATMwiHIAg) zXnjk4qNc_`C!_8CFl%XLMF(u;j0!8+mJuZ~L`(Vycjt%_YviA-7(#Two;)1!F#8-e zELCt1T59r_UsCLnNeMe_-_0&@eoBH3rpQrjvl&WrjSa2KYjpSub(7$WOeP^2Er|8}Cj?8}_|Np-P{Qt=m{=00%p$%Z^5cr#a{O^RuMU+TX z`H;z|Q!x>{_rctdV@>0}XJBlwzT?2=Js>F1*pGfg`Wp$$8nxp>Q>8ZYLwNogdn=7-1!Y_QUFxcPBJ$9Jggi zJd+7cO`VWYbz58YkC2@8<*>IaOo7vxv8sQ9qvg+GHSQ29 zvnfE~QpWxCMifL05;Ub!^WGl`WF@Py8mbD}Xi|Lk_a)IM-i6ALE`FeJT>h-U4$rg- z@sDXJ5+0U^DWKobCM`PT#dF0ph13F2p~mP}t~h8SxNGD|c;{MfONkP)4vtQ-ev%Hp zTi$di2d0GYN>ML($%(-Y+=>$Kn2uqC6lNg++QOw|JdawtH9U^{yk;I=h%t8S! zGHuuf>FcS7(B}v(GR5~m(C$e5`G^Q(8|!xPHyOwvD@U3&C zRS307R4)HKqVWm&+Sb^9pfY{5XEhmwZ)^*Pb7C3bhsmV}d;pV$$nMk4KLtX>-y#CG zV5SP%xhZNUl1SmPk*Q^WqW2`nqZg#}9GjH+&P?^bcu&Gd8Aq&AF0TVIs363r66)P- zCem9>)KTnuP;5OIxPq@R)9>zI5X{ktOY_!HLKO>-)6MQ`+BHNXeHgyQ`X9K9rw4(y zs%iYVU!FbLS4cMi$$S3$?H2_LCeD$R=5cTE z2X@~pxE^>x(7n3AMK}kf^qAm${R)CilT;1x4(sZ(aqfz&_fI=4s>1w6PG-N+UoL&i zLab#rI*Yzbggmxn;TDe!cO7;p$#BJZ39)5`B}fR!&@_bLF|2oxprXaqku_E8VDzQ1 zo;e^_x`4iPq$+}8+jzBOUzrCzz!W}lqlrZdSvA6_>CAVW4IHk5Sy6%rs(P0(rUVbT zAwiPdR$@)lU|sZ(ZgjwmQ<)aVN2zn~#363#QI!`qUa6 zE{n|C?{Z$-H3!$@$bMTUtoPvanXs=O6iuz}!F8|WnD{9!rl{s$r5q@Jaa#fI>_Gbi zmOG4M&{aBZ0id=|LcaN}t1hfA!Srpcfm=%3iG6INjpLDXa>s?cO^t%18PAaCI>^wCa( zKiAmu^vrh{Um}F2%ZZCd)WF>8lX8b5w0kaGVhsj0A_F(-P>c7l5b|DIX^7Cpd07etiXOHH{JM0Ypv5=ITXjU)U&L1!bj z7~3Dk5Wd8$F*bDDYy$Mu2wauzEs(nQA-74keTxghb1jbHy_IQ2l{_6fShZhycoz1l z9w9L7Uz|ry&3+3+Rk!dT`@iP%YQAj5FMy9R5OK)+Q)H;N`9n1?x;e2{4WLJ!AnQC? z>J%L3z*()Vxl~EphJN)~hSqla1K@KxKzb%k3kS=6GD16AK04^T2c>!6x0;r|Z8IOy z;x%Vt{0lZ@wCX`G&JMV^K_~lDc#T^G2vsL)$L9 zZhxumsi}z=EX{K}5PnelkO|^P{?}j}@7nrCZ#UoVu|nj*on*zZrCu7Dz7evN(TZ27z!!pz{C^?iKLYsXPkrFA!&+pO1n~gCC=eEaeNThM`C>Bw4M0A;@f`x z-`bL;)!~&a+tTiPGjD86Lfdl$7YEw*Tf`hXf#7iw0iy}4aZ*`2Ka-j z=c{pPpivDGVQc(?fB^5T-Qfn|MH=0pU+?+PM*w_>CBZz8Mp>QU50Zf6-=NV}C-`;} zFdO4d8uj2Y`2IbL;1dZ5dm!|@A4(_j<2*w1=7$}Idwt-;*Me(z=X+lR_5f5-RNVkDE66aJyTT|WcAK4h(S zj_1jNAHMsS7}BmAkzklxzx_d+q3*w%2}%D6U{ZzwCm0D$zq|lpg<^K{IP)Sx)Mpz& z_*h|XTr{ET%MxHT->f)oFe!34g4NLSB8v>PxuKNu;NeyIMQf=NKWuN3tY7*<>!R3 z42j_Q+t`3L2Q%OkW34cIdp3M}_!O-9z8*H8Y=-qEjW93ugcsM){3k+J?%|MLc!&*J zd$0k2{Sqs@l2iz5OB+Pc-RYfQ@Aa?@2%izMzK}r2Kf(sCEm**Ra%&cRU0ml8c$?1H ze8|&T!D2$yR}vUZ505nou3NGj-cK(Z5_p^5xBh^q6T=0Bs9*a7Vz6lISb%@aiWFFq zUo9K_Z_1lQ`@nsmC!eIMIKL4f!Kmg7Ok0x%D^AqP1|Rjytg}P8gX?kElMwZX1UljO zqeX(dR;R(5k_Ml^M@2h9#`ROkB@AK`{Car`EI(fBGx+$Ps91|fJ3=xO{7;q1AAvCp z`ROBN7~=XJn}5M)@KI-P%l3HP57Nuw!EX*py@Cqd^B_e1IXBSr?~Mcj-q}~;7x=o| zN{H~V6F4P4g&BvKS%;aC;i6rU`8Z{jtJO+O`+M~MIrZ#nEU`1ALdibuent~qsC3at6QVMqd? zE@ey4?XEV&(Q9K(;|XaW2@tL~cZ+#~(+$i`e9YBNti#PO zLfi)e^luiK=gB3(@}i-w|1Yx7iU&hOUF(&kBEP^N!cFYZa{M!dx(@`flfW=E2@J<7 zJpvzBZ~-3Oa>VPDZ__#av0;JlsIR9Sml~LT>^r*%Ou(7$z~T#D zH-cg<;-<}%Wy!LhcE@1Qe=(0B(BRv2VNNAXUYaB&!H!5{!jFjnG6{eZp|jksF$e(- zD!Fwe95L{18dp#wewJj`IUF8m9Y+L^NdS}yUzzV_Pft%k;sdv3!CG~slzjq+U6I}2G_&ZmniT?d2HwQbTsd%jXx1G7t@7Jo;^G^@~WD-F14vdLcV=qDW z|EIhGf6u!8q9Hbxpu5x8T;0STRkghFNC1xS@by}eM$;sAy%)3wVV;H)p2L_u+x2jd zep8TvZ&Qpinh;}Ar<*vQ8Y* zBV`l7X!+1Xk>$QXT%SEjIfENTM}w9CsK&hW@T+AhQWEUN^1Di20zC8&G($iLU=Zdy zZuYD^Q5V!CSbn???q(i}WIPJAi})xafFClDrrs2&L!Qx4+?SB%C9;{I5_dusM*ywC z{FaCOyPm*(h7j+VD=^kBpjFDDjB z&osItR-aho&5@qJjN-_0|D9sOtcrAf=7#LLy82SWu6g>T1X02 ztPA-BRqbNaO&$o4AIQ&{6lc8xTg=<8|DrWNqzHh@F*gyjlcWi%SI1d@7P<@oxP-ZC z$!-YnTA)A5v2xkX@yW2UtU2UJfHN0nJWCT)BNkgDLzV-WV$IgHWdr;9$Iy2li2@AQ z>Z~_7hL(NoCD|jW-Ncecc;FkC@-^ouK~}zl}lR3)^7f2^=PJggOau zbIVOpt5uYs>dy6C#7AM^g>9hTJ5Uz#1VCMsTLCw^L>>#fw9)*Pgu2W5058}EY5InP ziXecPpjuGluE<5^c>RuR33Ue<&zT>0&MQ2x7!s^1Zh(7T6I9D^(~Xd~8-vl8Y7D|j z+W1#P3SWjr?Z4Rb~ZtG zN=XFp5_#N*)!B#v{jP}D%B)!t?mu{H7Y(J66V`W%(7{6jlzsU zw+X82_De}nf{8V%BtXQ%9lvyJYxBmQVnq`GbzMml%-EbE^$@C(aH8YjHJ8ku7`E+nD;%!r5TDA6-J=J# zTxnyo$JvYBkXhT|G)st)$F2#gdsNdJM`JLr984oi^{J)+4&+fbpg0}(1s~=f1-~lJ zN;!2M;_QG4s?%H(RPz;^pz7oj_P4vW-*|N9nIVC%Cm5Z0&G# z54#i{cCOKzciV0j&(U*h+f+}0{JJ(LqcF5HlZSE%b(Y&5L&m19-V2o%yHpE)eP=Js z-s)rrl&N83=pCmv^voW}scumn_~p%=@MMB>ePiY=y{VQ%qOV+9)20?cZM*VaK|GL( z%RK`(ER}|m@m5vE6C&Hi0!>bx#I3L-`;;P=h1!lD*icyMwv{esRjD%FOe{UC=Zjnh zgcD${H4m~bv_MA)`-ynmoL6%Z=BF0I1oyoZI~n*Nh9xBYAWW@-wT1>do;SFwZ8V8Z zm2V~GL*mIgs1AW8qN%e74xVd(Pxc*$$*$A9Q+t~T&T?ZINxwvWC0vEUq}c7lTNA2T zTQXqL;WCjBWlbGo5XV>v&Q2wbZ6K6Y!{_P6Fk|Dvp_*u+2ee760`8H(~`!OqEV@O7Z4Ze0Vk?O=43*50X74BWTj}ayv zp4yrT&zo{!&WOsG7PQOYw=7#ncq{H%yuS#Nk~XYNJvOXNJvOXNJvOXNJdQl55vXK}sP)MzCV_|S* zE^l&Yo9;Xs000XoNklC!n2KaLF6<@knGMk zOA=mkPSbPR#EK%&n7w;JJ>r8ZNE8swDb-q1p%6tlrk+-+fXG|4_@HoX03ooEgkT;d z>Fi?Cow@h!oqO-xn@!Ko{KF&j&3wQ6%{Sj`CUJ4G7HhE*R|sg8k6O%{bfJU^kXMBedwlK&XyVX>kw0KKRl!<7_{SOQzkR0>DOSNB2h? z@9}6N+2gIknD6MVuo>M=v42nK-}gij?{FC^fJ4Gr5AXZP43`DxawT@mgnt$#Umo(| zu&vB@^g}R(fL+x2_diF&w;u~1aV-5Tyo+AK^YB!n9@+oPL~eKpErTlfmV?p@q5*QWE zI0pdVH#xyO2A+h^IUx8ZJEd#BeJT-ItwlBA3OpggSX}@*e=-wR!FM_k%;*X;X9)aD zV>zdBNp4>q@VEg8w!D7Ct_VZ`V3*_Uqseeibt&~k=&$0C9zy_xM)bl9v=yXhw6n&J zUsdh3z|!0waBd&N0E9+eOZ2}g0QfZr%^kc_brJiut#(lWmr!Icgf+aGGi501h6 zu-1FC-I{;3(nmw{)yy1Qq%Z$}8xB5&EqEm^!P6mY+6|Z7Bl{PyP>DRA?%=`uFnSPj ziCJ{5&N^p9ksZ^!6$jr1oD+4Cn0PFcb++JfqSepM3KQf!+!cLc*UnSoH2hc~;p`OH z&9HwFPmMPAp%=Up9y-bCDNg)Xu)#_^hx^$46@J4Z`(!_38!i_`I#*$K`Z$4eU0Reg z(n8!N=7hpO7eIE(6;RC(uH-rR+&_Y~ooe%lBf;x<^e1Me|At=HzyI?L(PMCp-k6-8 zL>JQ@{6Z&WV8-RFgN}Z0)#uNEixkO5$TVfL*17vT5~Ijq@=vYa;TFir39yqv^g&L! zS)(wT${h;P&12?$*NXTfhS+XiqcgX!!ft4TQBD9rKH7F>=6lRL9|!2O-;lrmh(ZQ- zIO8>VhZ5%~eBM1K=fxg|ffo#(%Zhj|DsaeaVC@#Ki0>bmdbL3Sa}+XAJ>zmG0LbgZ zY`~Aq?Eibs89x+_EnStuz$Ns&-Z91(zmXzQYxMhvc4Im;GZ1_??I%tEP;ey^M1S2N z0e62|k(l}Zq1~uRoZt$F^uS^|WIFAKR)QZF@_U@}58LmV)sDPl*oQY#h55Zkb?&v17j^$U@3>X-% z%pDq6sW1w5z+Vj>O04MY9#;U8O@YgUB$Nnrc=XCim>3}Nren-=Cme{GRNp| z3^_*B8H6YC0kE9fIj;x`SRPkch1t4Mg>5QXpa5_FIW+I<)QnH?4WbfII8RVyakhe$ zFST=dd3}fpbx|V`ZXnnRh1A$>e8*M;b$Hw&%unB?kO9^3N_GmGubBi7WhD4z00z^B zIEMydhI}i|hxd+AIP?a~`5bSl-=z;nGUNu|BQ>56VOaJ9kEb)tzJ%;?}MTh`U5H{#5`gFfK>KeC;d1&p|k7nu;7dpP_*9C zU*dkTR-j@*V5l(qD+Ctjb20L!3&7Wrmn1+6{=v!oc`O@Gsp-)CM4!MZ+W{Dc^{-R3 zkzIeCDe95weyvZS)@}gwL52^FWFQOyP^V9z!2tlyN0^}+&9q{C=>X8APoTvC0O~cU zUqDNyTw#&6j$Vll2~fXX5`Y^8(2fj1K%c-#`vE}3s=o=|*te73B-ejqBzOB#NIheZ;5|6ZBGd2LS(pl7NTwHS~SdfWLKn3D`^V^9%jJRl31r zBdVJ#k^nkS5mtpPfe3ULakETr^50kcPvL4ob zTYN*!1CZ907n8Ny+*Ij#=^_vNq2A(uPBwc_fv)R8e~gN6KS(;j&3s2cAe*yem<&#n zxd^Su<{K%UHq3sopOt@V&R=g9TPI|Z{n1jrDfWZC-e+bDGk3(lai+{!y_Kd(XH2sn z>@}svs~pgs){CV@o%qr|OY8^x!MUl%W#D7_X^gynT>}8o{Otm|@#|AeURB&hL}R;u zu+$<1fEL4cft9c7{y$HZ1mjj9Q(y7#IX?xz!}+i_#IwgE48?tO6&W7oKW_IF4CQb z7m?Fb@igWpIl&JjOEVR0#dVZ0GdoM2F61Rv^nb*OHIN7XtWY3 z{4JUp`6qJwdPX2Oo{Jt91i)G00MrQmLixQR69Ha;mf;GbCC-VyWDzw=kZzt^U@dHe zW1973ABc(~OB;pT0qX@U3z8!iQniY!FP8kmB|uDA2Np#DU=1e?j^ZoXB^m%ZO9%k& z{+d@CxL=Fm0w7ti8mtj^ge#sb>a6nR4WRRt-$^S)0^lzbrvG8A<`!r`%Xp%Pd;s`{ zN^JrIA|fMNOvsek2>3C*xAcMHYCpMy) zouXUyA1bdv&mfl?4E!ay{-r_ycv`rn4v%x+{HaCj@Gm_X;W}6wfWjo&*)C3z_F88D zfGqw!J1T#8R`sGY+Z&1IScOXQEHqJhl^SLsKUxQ$kRz4}UdjVaCPq8@sZx6$+zoqV zxrhLg#eE%0_ZOtwr%dr$G` zok1^jA6fz1BR-12zAI3LA48M1UN4o0_{$=cOw{PJZ5i7W84_yL&WP3K9x6eh`8lFzqZI!K^sB>fEe)0>h{&T=||8*8x^Yq1t< eu@-CHwfz^ns~7`Y4O^K20000k8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H14VOtoK~#9!?Oh9$l~onKM3I<s|&5g)5z=H9t? zF2lT{(6yS@Dk@Vm^2I>5Dit3n3QDmuwIsNAW)xvbtQlrh;Qe6SGGKcib5-FX;42d*OqdC*#h*`g+iwWgnTC7LiAOVh8gMnR z%L9hXklI`hVQ55OF0k8w{B{Uw9HUumKxJj+7eVk_K)BZfV7Lr7pl>(U)YN?5q5w3} z@1V7=GMp7d9}wwEUc);zWPDGgJ}yF^FB35TioEw%7#0^MQeP0U9?x|lV0tlJ#bfB( zJ+6|Hk`MMA6Ew%y49D|kJcv?9DmBE6?}^0l1Pk`$0{{Of!}epu84Y|{5z+SmpTP`# z5Hsw0V3G|-+3!Z-H`WguLYdkM1#&AUfa2zx? zHhus?_7%MUE8s>&LhyEJY3YZ1)2#2$D%LKcIv_OjftNdM*R=o?_~t0!9N@byg0iCHf3_Xgy&%liTgyN;ga{lC*9Eeiky=g~+8buh5m zHVCoWNqk*50iY$prnZZoONhbr1OD0K;17_`PSG+C`{SnunCH+XoEoKY?h{sBI^eLV z2tM4z^Ihy+Y@>-2Y68$KCU?!^fEU)+*MBTx;p`V2|L#LZtcX3N3HI^}xks50Og?{1*N`hPIINH;ke{fGJiao(K?8SNA&LH5g-xgG8se zq~|2sL<-B`D=oNs2f_S!%z$($p^PJ@Cry}3bvJwlTL^ya2Y$owRt%KbTEL6g3H*YV z9j1*8&ZreJ|1fRK8urQ~iJSpdFv%nykDsWc>c3x>L`hq<9=Ms`!*~A}vAD@Lj`Z(m6_tLI+-wNa!_+war`$0j7CT@(5^i zOfueQf>QnCB^_I?I=o5QKo;T*U7RBr3E+wLPsy0C@ZCs3qiaO|zK8qyKeeC*UW`R( ze*tR#Jl-rSHr^G!j+ZleMGUylV?TMLg+2B9C@lpR;w;s=%>G<5nLOEJ0gPMF0$a$E zj*0-(*4BPfvi!a5*V*86QKPM7` zcWY{DPK$~FP|}Pf<@_;EJ3v0R$Qbc!_UEmO_gV-+&yScbFkO^T3W5Kum$P4#=y9;F zuI}T05`)@9ySpsd1CiN&=1k*`AflmtM3p&$%4f%n_`a2M`cX=`qTNr4OX$h$A0aWg zN`fE;7hG6tK?oZB4BmKY3z0^XoG$v37g8tMqI@IBzzfN-&!3LQYSfn^2xmn_d z{=h3cL*xOW-Ek@fV5VTb`K>P2F(*@QwGbgJbs2=B+=S;Wp7_bBKH;1<-p2%E{H4l< zUJyuHtxhfyAEJ;k<#i`#?&!SR6)em zI!6F45U9$-BG7Np_X8eZiA1}(7J#?h5J?kF@OQSN#C^!F_l9oQRo0Z<{x6b<$2tb>OF(5j*X2O=T>`yGy93wZ}Y1-x7e zK!=JB9EyklwAt+YL5c^^@%MoH?aEd$8sPnL*tv;>Qm;?fq~C4?Ag7`O+oK==n9V=5 z(eKF`39hS4C{%P{Llgvni&(9)m_ghxA_DN5r<)Cd6(?}FKd?%|{6Qfb(^#(qK}!U& z=JHs`fKL}sM&CEFebdJ0?xrB@loGDhM`5uov)?n1P@L`c?sdW~q>{>jfI% zLBoQ;HNfu$8sJ(L)^mlRfxj3Q1g-&A3i$I)*A4J7Z13C*1CD|7HH)5wpxf3}8BHAm zPv8v0ieP{WN@EH5O_4eYZ825hYss;&s|_oH1)79>$FVp0nxb=Pl#^yypat~d3ToYF z7H(7aWr-oUITo=iU&0KO2~1n~N-~1Gfh$yX2@Z!`U`TJ#aGN*jgm@-3Hl9sRFV7}h zfE$51U?(H}c|&<7)m+w8F|DjQacSw(_AZs?l=cBiJ1%lJ$Ph8C6+?Vs<@x1J6%T>A zdx1x6Vvc^BXikg_(%-yERJz+O?g1WF#r!-xgeBx|T2A~Hg0+IM@E;7@dDdrAXJD4D@qqEiBif+0d>feLq)msP z*|Hk1^%sIUJ3VLq`?v?j;#d9B0E0x;Id!NN6zI%vs<;>oY4ef!-PlHL`y~XTo50_W zQh{bW01JI67_mD5=6B<+@&!KBYQS25i4Mi0b@T1PfK>r9|Coo6O|B*5(+@51ia-e3 zsGMnU5&*XLkLZA9nbci=Xo31rU2N#>x<8EEGF=4VXkWaJ6L4vgH4=|tSTC9VOEhAB zcd+OwKU~6bvh-@&ua3CkH&Qp|Gj{nBEu=kX$x?7*q<#Kua*n0~KnCFheh9!tlFJ<1 z)9Lg%QDi>t(DBaQ5lwM7Jc53H2*G`_YFWy+CN%)srq!7F$2@|jic7p#gYo!@qKhOL zo5^sDnDN)By8y&9sat&^2G^2{gc~ zArOFdehNT;zAw0788y1{EMiMQPJ01>A?Pbn=paIYLW}dF7!QD(^{ zPK!-->R6K)1fHsWBru!QCpiaDuml_+4?$I)p-KUu zq>H1;7aAsnP^y}i?w=%~RJa_#75s(UI)17PTY}h&G!%dqZT){qT|29H^DL$Kfs=dj zR0}}7C4P=(Vc|pC&VB+e*}6l~%^jN|2zStSv{hm7qO>iDK0_M;XsWnf`#3t4FY)01 z9;%A*1T@Td)9LicVjECS1c1IJ+REniu$3A_{?_<@V%VVo6gy+M$Ht}1GHhpiV-5u4 zW?Fjiywb|W(s)j!1Qrq~x_5`&S7DN6qB$|d=R=`AaYt!3IXE=CX-wRLW!mu^cI}Q#|XYR_irvPdUkvr z8}Mw4;o@__v?mos4Cxy!6-^aq$D#r-tj<)hl$=DxEa|djzWiaI2TZ4LE~RKBU6g@8 z546)qEDOUaKJT!I#4h@+)&U+3?TgdFtT>qUb37l$-!s5xf$MnHC^HFo zCtbgezbB|-x~hXMv_nVRw6s@G2^&<@frX--aX0_lf}`)?UF~-D3>YwAz<>b*1`HT5 gV8DO@1EGZf0hB@B{O}7Q%m4rY07*qoM6N<$g3NuXTL1t6 literal 0 HcmV?d00001 diff --git a/master/resources/zoom-fit-best.png b/master/resources/zoom-fit-best.png new file mode 100644 index 0000000000000000000000000000000000000000..c8e956402da73ba19d20cb11fecdc9c0eb50ea9c GIT binary patch literal 566 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?I3?vN&YJLDI=3*z$5DpHG+YkL80J)q69+AZi z40*dinDN@Zjp9H-$r9IylHmNblJdl&REF~Ma=pyF?Be9af>gcyqV(DCY@~pSgaUj* zT!FN&udk1fkDs3(kO2h8Err9Vb4%^ zc*%IQU!m6FFGKBtUImT?c?=8%N*rt}#2FaRa5OQ!VrgJdZhFvgi_u_#q@x>yDRYB^ za#IiEeZdNz6Y&haj2|~BB{<}rVd`brxK%)cy{K4Ki0{a^{>DHB>HTg_jTyNdJ&ZT+ zOypo*v{%!JDJ4Bzjiuw>!2`7lyVZriHolPWyTknH)yID~{!cX9@I`22{H}jO52|>d zCN^X#^wqPPF*h*GWW3;*qRqfHL6l*hn;Fvq21!P~A5N3<8QbzrUWz6B37Ym=P=bAh zTw`Z<=DKAGObfEO()6BN>fd|U&d&7)7@Y54bI!RO?3|KTCJBro22WQ%mvv4FO#o}( B+z0>w literal 0 HcmV?d00001 diff --git a/master/src/CheckableItemProxyModel.cpp b/master/src/CheckableItemProxyModel.cpp new file mode 100644 index 0000000..9ba4dec --- /dev/null +++ b/master/src/CheckableItemProxyModel.cpp @@ -0,0 +1,257 @@ +/* + * CheckableItemProxyModel.cpp - proxy model for overlaying checked property + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "CheckableItemProxyModel.h" +#include "QtCompat.h" + +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) +#include +#endif + + +CheckableItemProxyModel::CheckableItemProxyModel( int uidRole, QObject *parent ) : + QIdentityProxyModel(parent), + m_uidRole( uidRole ), + m_exceptionRole( -1 ), + m_exceptionData() +{ + connect( this, &QIdentityProxyModel::rowsInserted, + this, &CheckableItemProxyModel::updateNewRows ); + connect( this, &QIdentityProxyModel::rowsAboutToBeRemoved, + this, &CheckableItemProxyModel::removeRowStates ); + +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + new QAbstractItemModelTester( this, QAbstractItemModelTester::FailureReportingMode::Warning, this ); +#endif +} + + + +void CheckableItemProxyModel::setException( int exceptionRole, const QVariant& exceptionData ) +{ + beginResetModel(); + + m_exceptionRole = exceptionRole; + m_exceptionData = exceptionData; + + endResetModel(); +} + + + +Qt::ItemFlags CheckableItemProxyModel::flags( const QModelIndex& index ) const +{ + if( index.isValid() == false || index.column() > 0 || + ( m_exceptionRole >= 0 && QIdentityProxyModel::data( index, m_exceptionRole ) == m_exceptionData ) ) + { + return QIdentityProxyModel::flags( index ); + } + + return QIdentityProxyModel::flags( index ) | Qt::ItemIsUserCheckable; +} + + + +QVariant CheckableItemProxyModel::data( const QModelIndex& index, int role ) const +{ + if( !index.isValid() ) + { + return QVariant(); + } + + if( role == Qt::CheckStateRole && index.column() == 0 && + flags( index ).testFlag( Qt::ItemIsUserCheckable ) ) + { + return m_checkStates.value( indexToUuid( index ) ); + } + + return QIdentityProxyModel::data(index, role); +} + + + +bool CheckableItemProxyModel::setData( const QModelIndex& index, const QVariant& value, int role ) +{ + if( role != Qt::CheckStateRole || index.column() > 0 ) + { + return QIdentityProxyModel::setData( index, value, role ); + } + + const auto uuid = indexToUuid( index ); + const auto checkState = checkStateFromVariant( value ); + + if( qAsConst(m_checkStates)[uuid] != checkState ) + { + m_checkStates[uuid] = checkState; + Q_EMIT dataChanged( index, index, { Qt::CheckStateRole } ); + + setChildData( index, checkState ); + setParentData( index.parent(), checkState ); + } + + return true; +} + + + +void CheckableItemProxyModel::updateNewRows(const QModelIndex &parent, int first, int last) +{ + // also set newly inserted items checked if parent is checked + if( parent.isValid() && checkStateFromVariant( data( parent, Qt::CheckStateRole ) ) == Qt::Checked ) + { + for( int i = first; i <= last; ++i ) + { + setData( index( i, 0, parent ), Qt::Checked, Qt::CheckStateRole ); + } + } +} + + + +void CheckableItemProxyModel::removeRowStates(const QModelIndex &parent, int first, int last) +{ + for( int i = first; i <= last; ++i ) + { + m_checkStates.remove( QIdentityProxyModel::data( index( i, 0, parent ), m_uidRole ).toUuid() ); + } +} + + + +QJsonArray CheckableItemProxyModel::saveStates() +{ + QJsonArray data; + + for( auto it = m_checkStates.constBegin(), end = m_checkStates.constEnd(); it != end; ++it ) + { + if( it.value() == Qt::Checked ) + { + data += it.key().toString(); + } + } + + return data; +} + + + +void CheckableItemProxyModel::loadStates( const QJsonArray& data ) +{ + beginResetModel(); + + m_checkStates.clear(); + + for( const auto& item : data ) + { + const QUuid uid = QUuid( item.toString() ); + const auto indexList = match( index( 0, 0 ), m_uidRole, uid, 1, + Qt::MatchExactly | Qt::MatchRecursive ); + if( indexList.isEmpty() == false && + hasChildren( indexList.first() ) == false ) + { + setData( indexList.first(), Qt::Checked, Qt::CheckStateRole ); + } + } + + endResetModel(); +} + + + +QUuid CheckableItemProxyModel::indexToUuid( const QModelIndex& index ) const +{ + return QIdentityProxyModel::data( index, m_uidRole ).toUuid(); +} + + + +bool CheckableItemProxyModel::setChildData( const QModelIndex& index, Qt::CheckState checkState ) +{ + bool modified = false; + + if( canFetchMore( index ) ) + { + fetchMore( index ); + } + + const auto childCount = rowCount( index ); + + if( childCount > 0 ) + { + for( int i = 0; i < childCount; ++i ) + { + const auto childIndex = this->index( i, 0, index ); + const auto childUuid = indexToUuid( childIndex ); + + if( qAsConst(m_checkStates)[childUuid] != checkState ) + { + m_checkStates[childUuid] = checkState; + modified = true; + } + + if( setChildData( childIndex, checkState ) ) + { + modified = true; + } + } + + if( modified ) + { + Q_EMIT dataChanged( this->index( 0, 0, index ), this->index( childCount-1, 0, index ), { Qt::CheckStateRole } ); + } + } + + return modified; +} + + + +void CheckableItemProxyModel::setParentData( const QModelIndex& index, Qt::CheckState checkState ) +{ + if( index.isValid() == false ) + { + return; + } + + const auto childCount = rowCount( index ); + + for( int i = 0; i < childCount; ++i ) + { + if( checkStateFromVariant( data( this->index( i, 0, index ), Qt::CheckStateRole ) ) != checkState ) + { + checkState = Qt::PartiallyChecked; + break; + } + } + + const auto uuid = indexToUuid( index ); + + if( qAsConst(m_checkStates)[uuid] != checkState ) + { + m_checkStates[uuid] = checkState; + Q_EMIT dataChanged( index, index, { Qt::CheckStateRole } ); + + setParentData( index.parent(), checkState ); + } +} diff --git a/master/src/CheckableItemProxyModel.h b/master/src/CheckableItemProxyModel.h new file mode 100644 index 0000000..1358a88 --- /dev/null +++ b/master/src/CheckableItemProxyModel.h @@ -0,0 +1,68 @@ +/* + * CheckableItemProxyModel.h - proxy model for overlaying checked property + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include + +#include "QtCompat.h" + +class CheckableItemProxyModel : public QIdentityProxyModel +{ + Q_OBJECT +public: + explicit CheckableItemProxyModel( int uidRole, QObject *parent = nullptr ); + + void setException( int exceptionRole, const QVariant& exceptionFilterData ); + + Qt::ItemFlags flags(const QModelIndex &index) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + + void updateNewRows(const QModelIndex &parent, int first, int last); + void removeRowStates(const QModelIndex &parent, int first, int last); + + QJsonArray saveStates(); + void loadStates( const QJsonArray& data ); + +private: + QUuid indexToUuid( const QModelIndex& index ) const; + bool setChildData( const QModelIndex &index, Qt::CheckState checkState ); + void setParentData( const QModelIndex &index, Qt::CheckState checkState ); + + Qt::CheckState checkStateFromVariant( const QVariant& data ) const + { + return QVariantHelper::value( data ); + } + + int m_uidRole; + int m_exceptionRole; + QVariant m_exceptionData; + QHash m_checkStates; + +}; diff --git a/master/src/ComputerControlListModel.cpp b/master/src/ComputerControlListModel.cpp new file mode 100644 index 0000000..5ca6abf --- /dev/null +++ b/master/src/ComputerControlListModel.cpp @@ -0,0 +1,537 @@ +/* + * ComputerControlListModel.cpp - data model for computer control objects + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "ComputerControlListModel.h" +#include "ComputerManager.h" +#include "FeatureManager.h" +#include "VeyonMaster.h" +#include "UserConfig.h" +#include "VeyonConfiguration.h" + +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) +#include +#endif + + +ComputerControlListModel::ComputerControlListModel( VeyonMaster* masterCore, QObject* parent ) : + ComputerListModel( parent ), + m_master( masterCore ), + m_iconDefault( QStringLiteral(":/master/preferences-desktop-display-gray.png") ), + m_iconConnectionProblem( QStringLiteral(":/master/preferences-desktop-display-red.png") ), + m_iconServerNotRunning( QStringLiteral(":/master/preferences-desktop-display-orange.png") ) +{ +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + new QAbstractItemModelTester( this, QAbstractItemModelTester::FailureReportingMode::Warning, this ); +#endif + + connect( &m_master->computerManager(), &ComputerManager::computerSelectionReset, + this, &ComputerControlListModel::reload ); + connect( &m_master->computerManager(), &ComputerManager::computerSelectionChanged, + this, &ComputerControlListModel::update ); + + updateComputerScreenSize(); + + reload(); +} + + + +int ComputerControlListModel::rowCount( const QModelIndex& parent ) const +{ + if( parent.isValid() ) + { + return 0; + } + + return m_computerControlInterfaces.count(); +} + + + +QVariant ComputerControlListModel::data( const QModelIndex& index, int role ) const +{ + if( index.isValid() == false ) + { + return QVariant(); + } + + if( index.row() >= m_computerControlInterfaces.count() ) + { + vCritical() << "index out of range!"; + } + + const auto computerControl = m_computerControlInterfaces[index.row()]; + + switch( role ) + { + case Qt::DecorationRole: + return computerDecorationRole( computerControl ); + + case Qt::ToolTipRole: + return computerToolTipRole( computerControl ); + + case Qt::DisplayRole: + return computerDisplayRole( computerControl ); + + case Qt::InitialSortOrderRole: + return computerSortRole( computerControl ); + + case UidRole: + return computerControl->computer().networkObjectUid(); + + case StateRole: + return QVariant::fromValue( computerControl->state() ); + + case ScreenRole: + return computerControl->screen(); + + case ControlInterfaceRole: + return QVariant::fromValue( computerControl ); + + default: + break; + } + + return {}; +} + + + +void ComputerControlListModel::updateComputerScreenSize() +{ + auto ratio = 16.0 / 9.0; + + switch( aspectRatio() ) + { + case AspectRatio::Auto: ratio = averageAspectRatio(); break; + case AspectRatio::AR16_9: ratio = 16.0 / 9.0; break; + case AspectRatio::AR16_10: ratio = 16.0 / 10.0; break; + case AspectRatio::AR3_2: ratio = 3.0 / 2.0; break; + case AspectRatio::AR4_3: ratio = 4.0 / 3.0; break; + + } + + const QSize newSize{ m_master->userConfig().monitoringScreenSize(), + int(m_master->userConfig().monitoringScreenSize() / ratio) }; + + for( auto& controlInterface : m_computerControlInterfaces ) + { + controlInterface->setScaledScreenSize( newSize ); + } + + if( m_computerScreenSize != newSize ) + { + m_computerScreenSize = newSize; + + for( int i = 0; i < rowCount(); ++i ) + { + updateScreen( index( i ) ); + } + + Q_EMIT computerScreenSizeChanged(); + } +} + + + +ComputerControlInterface::Pointer ComputerControlListModel::computerControlInterface( const QModelIndex& index ) const +{ + if( index.isValid() == false || index.row() >= m_computerControlInterfaces.count() ) + { + vCritical() << "invalid ComputerControlInterface requested!"; + return ComputerControlInterface::Pointer(); + } + + return m_computerControlInterfaces[index.row()]; +} + + + +void ComputerControlListModel::reload() +{ + beginResetModel(); + + const auto computerList = m_master->computerManager().selectedComputers( QModelIndex() ); + + m_computerControlInterfaces.clear(); + m_computerControlInterfaces.reserve( computerList.size() ); + + int row = 0; + + for( const auto& computer : computerList ) + { + const auto controlInterface = ComputerControlInterface::Pointer::create( computer ); + m_computerControlInterfaces.append( controlInterface ); + startComputerControlInterface( controlInterface.data() ); + ++row; + } + + endResetModel(); +} + + + +void ComputerControlListModel::update() +{ + const auto newComputerList = m_master->computerManager().selectedComputers( QModelIndex() ); + + int row = 0; + + for( auto it = m_computerControlInterfaces.begin(); it != m_computerControlInterfaces.end(); ) // clazy:exclude=detaching-member + { + if( newComputerList.contains( (*it)->computer() ) == false ) + { + stopComputerControlInterface( *it ); + + beginRemoveRows( QModelIndex(), row, row ); + it = m_computerControlInterfaces.erase( it ); + endRemoveRows(); + } + else + { + ++it; + ++row; + } + } + + row = 0; + + for( const auto& computer : newComputerList ) + { + if( row < m_computerControlInterfaces.count() && m_computerControlInterfaces[row]->computer() != computer ) + { + beginInsertRows( QModelIndex(), row, row ); + const auto controlInterface = ComputerControlInterface::Pointer::create( computer ); + m_computerControlInterfaces.insert( row, controlInterface ); + startComputerControlInterface( controlInterface.data() ); + endInsertRows(); + } + else if( row >= m_computerControlInterfaces.count() ) + { + beginInsertRows( QModelIndex(), row, row ); + const auto controlInterface = ComputerControlInterface::Pointer::create( computer ); + m_computerControlInterfaces.append( controlInterface ); + startComputerControlInterface( controlInterface.data() ); + endInsertRows(); + } + + ++row; + } + + updateComputerScreenSize(); +} + + + +QModelIndex ComputerControlListModel::interfaceIndex( ComputerControlInterface* controlInterface ) const +{ + return ComputerListModel::index( m_computerControlInterfaces.indexOf( controlInterface->weakPointer() ), 0 ); +} + + + +void ComputerControlListModel::updateState( const QModelIndex& index ) +{ + Q_EMIT dataChanged( index, index, { Qt::DisplayRole, Qt::DecorationRole, Qt::ToolTipRole, ScreenRole } ); +} + + + +void ComputerControlListModel::updateScreen( const QModelIndex& index ) +{ + Q_EMIT dataChanged( index, index, { Qt::DecorationRole, ScreenRole } ); +} + + + +void ComputerControlListModel::updateActiveFeatures( const QModelIndex& index ) +{ + Q_EMIT dataChanged( index, index, { Qt::ToolTipRole } ); + Q_EMIT activeFeaturesChanged( index ); +} + + + +void ComputerControlListModel::updateUser( const QModelIndex& index ) +{ + Q_EMIT dataChanged( index, index, { Qt::DisplayRole, Qt::ToolTipRole } ); + + auto controlInterface = computerControlInterface( index ); + if( controlInterface.isNull() == false ) + { + m_master->computerManager().updateUser( controlInterface ); + } +} + + + +void ComputerControlListModel::startComputerControlInterface( ComputerControlInterface* controlInterface ) +{ + controlInterface->start( computerScreenSize(), ComputerControlInterface::UpdateMode::Monitoring ); + + connect( controlInterface, &ComputerControlInterface::featureMessageReceived, this, + [=]( const FeatureMessage& featureMessage, const ComputerControlInterface::Pointer& computerControlInterface ) { + m_master->featureManager().handleFeatureMessage( computerControlInterface, featureMessage ); + } ); + + connect( controlInterface, &ComputerControlInterface::screenSizeChanged, + this, &ComputerControlListModel::updateComputerScreenSize ); + + connect( controlInterface, &ComputerControlInterface::screenUpdated, + this, [=] () { updateScreen( interfaceIndex( controlInterface ) ); } ); + + connect( controlInterface, &ComputerControlInterface::activeFeaturesChanged, + this, [=] () { updateActiveFeatures( interfaceIndex( controlInterface ) ); } ); + + connect( controlInterface, &ComputerControlInterface::stateChanged, + this, [=] () { updateState( interfaceIndex( controlInterface ) ); } ); + + connect( controlInterface, &ComputerControlInterface::userChanged, + this, [=]() { updateUser( interfaceIndex( controlInterface ) ); } ); +} + + + +void ComputerControlListModel::stopComputerControlInterface( const ComputerControlInterface::Pointer& controlInterface ) +{ + m_master->stopAllModeFeatures( { controlInterface } ); + + controlInterface->disconnect( &m_master->computerManager() ); + + controlInterface->setUserInformation( {}, {}, -1 ); + m_master->computerManager().updateUser( controlInterface ); +} + + + +double ComputerControlListModel::averageAspectRatio() const +{ + QSize size{ 16, 9 }; + + for( const auto& controlInterface : m_computerControlInterfaces ) + { + const auto currentSize = controlInterface->screenSize(); + if( currentSize.isValid() ) + { + size += currentSize; + } + } + + return double(size.width()) / double(size.height()); +} + + + +QImage ComputerControlListModel::scaleAndAlignIcon( const QImage& icon, QSize size ) const +{ + const auto scaledIcon = icon.scaled( size.width(), size.height(), Qt::KeepAspectRatio ); + + QImage scaledAndAlignedIcon( size, QImage::Format_ARGB32 ); + scaledAndAlignedIcon.fill( Qt::transparent ); + + QPainter painter( &scaledAndAlignedIcon ); + painter.drawImage( ( scaledAndAlignedIcon.width() - scaledIcon.width() ) / 2, + ( scaledAndAlignedIcon.height() - scaledIcon.height() ) / 2, + scaledIcon ); + + return scaledAndAlignedIcon; +} + + + +QImage ComputerControlListModel::computerDecorationRole( const ComputerControlInterface::Pointer& controlInterface ) const +{ + switch( controlInterface->state() ) + { + case ComputerControlInterface::State::Connected: + { + const auto image = controlInterface->scaledScreen(); + if( image.isNull() == false ) + { + return image; + } + + return scaleAndAlignIcon( m_iconDefault, controlInterface->scaledScreenSize() ); + } + + case ComputerControlInterface::State::ServerNotRunning: + return scaleAndAlignIcon( m_iconServerNotRunning, controlInterface->scaledScreenSize() ); + + case ComputerControlInterface::State::AuthenticationFailed: + return scaleAndAlignIcon( m_iconConnectionProblem, controlInterface->scaledScreenSize() ); + + default: + break; + } + + return scaleAndAlignIcon( m_iconDefault, controlInterface->scaledScreenSize() ); +} + + + +QString ComputerControlListModel::computerToolTipRole( const ComputerControlInterface::Pointer& controlInterface ) const +{ + const QString state( computerStateDescription( controlInterface ) ); + const QString location( tr( "Location: %1" ).arg( controlInterface->computer().location() ) ); + const QString host( tr( "Host/IP address: %1" ).arg( controlInterface->computer().hostAddress() ) ); + const QString user( loggedOnUserInformation( controlInterface ) ); + const QString features( tr( "Active features: %1" ).arg( activeFeatures( controlInterface ) ) ); + + if( user.isEmpty() ) + { + return QStringLiteral( "%1
%2
%3
%4" ).arg( state, location, host, features ); + } + + return QStringLiteral( "%1
%2
%3
%4
%5" ).arg( state, location, host, features, user); +} + + + +QString ComputerControlListModel::computerDisplayRole( const ComputerControlInterface::Pointer& controlInterface ) const +{ + if( displayRoleContent() != DisplayRoleContent::ComputerName && + controlInterface->state() == ComputerControlInterface::State::Connected && + controlInterface->userLoginName().isEmpty() == false ) + { + auto user = controlInterface->userFullName(); + if( user.isEmpty() ) + { + user = controlInterface->userLoginName(); + } + + if( displayRoleContent() == DisplayRoleContent::UserName ) + { + return user; + } + else + { + return QStringLiteral("%1 - %2").arg( user, controlInterface->computer().name() ); + } + } + + if( displayRoleContent() != DisplayRoleContent::UserName ) + { + return controlInterface->computer().name(); + } + + return tr("[no user]"); +} + + + +QString ComputerControlListModel::computerSortRole( const ComputerControlInterface::Pointer& controlInterface ) const +{ + switch( sortOrder() ) + { + case SortOrder::ComputerAndUserName: + return controlInterface->computer().location() + controlInterface->computer().name() + + controlInterface->computer().hostAddress() + controlInterface->userLoginName(); + + case SortOrder::ComputerName: + return controlInterface->computer().location() + controlInterface->computer().name() + + controlInterface->computer().hostAddress(); + + case SortOrder::UserName: + if( controlInterface->userFullName().isEmpty() == false ) + { + return controlInterface->userFullName(); + } + + return controlInterface->userLoginName(); + } + + return {}; +} + + + +QString ComputerControlListModel::computerStateDescription( const ComputerControlInterface::Pointer& controlInterface ) +{ + switch( controlInterface->state() ) + { + case ComputerControlInterface::State::Connected: + return tr( "Online and connected" ); + + case ComputerControlInterface::State::Connecting: + return tr( "Establishing connection" ); + + case ComputerControlInterface::State::HostOffline: + return tr( "Computer offline or switched off" ); + + case ComputerControlInterface::State::ServerNotRunning: + return tr( "Veyon Server unreachable or not running" ); + + case ComputerControlInterface::State::AuthenticationFailed: + return tr( "Authentication failed or access denied" ); + + default: + break; + } + + return tr( "Disconnected" ); +} + + + +QString ComputerControlListModel::loggedOnUserInformation( const ComputerControlInterface::Pointer& controlInterface ) +{ + if( controlInterface->state() == ComputerControlInterface::State::Connected ) + { + if( controlInterface->userLoginName().isEmpty() ) + { + return tr( "No user logged on" ); + } + + auto user = controlInterface->userLoginName(); + if( controlInterface->userFullName().isEmpty() == false ) + { + user = QStringLiteral( "%1 (%2)" ).arg( user, controlInterface->userFullName() ); + } + + return tr( "Logged on user: %1" ).arg( user ); + } + + return {}; +} + + + +QString ComputerControlListModel::activeFeatures( const ComputerControlInterface::Pointer& controlInterface ) const +{ + QStringList featureNames; + + for( const auto& feature : m_master->features() ) + { + if( controlInterface->activeFeatures().contains( feature.uid() ) ) + { + featureNames.append( feature.displayName() ); + } + } + + featureNames.removeAll( {} ); + + return featureNames.join( QStringLiteral(", ") ); +} diff --git a/master/src/ComputerControlListModel.h b/master/src/ComputerControlListModel.h new file mode 100644 index 0000000..4fad1da --- /dev/null +++ b/master/src/ComputerControlListModel.h @@ -0,0 +1,99 @@ +/* + * ComputerControlListModel.h - data model for computer control objects + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "ComputerListModel.h" +#include "ComputerControlInterface.h" + +class VeyonMaster; + +class ComputerControlListModel : public ComputerListModel +{ + Q_OBJECT +public: + explicit ComputerControlListModel( VeyonMaster* masterCore, QObject* parent = nullptr ); + + int rowCount( const QModelIndex& parent = QModelIndex() ) const override; + + QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const override; + + void updateComputerScreenSize(); + + QSize computerScreenSize() const + { + return m_computerScreenSize; + } + + const ComputerControlInterfaceList& computerControlInterfaces() const + { + return m_computerControlInterfaces; + } + + ComputerControlInterface::Pointer computerControlInterface( const QModelIndex& index ) const; + + void reload(); + +Q_SIGNALS: + void activeFeaturesChanged( QModelIndex ); + void computerScreenSizeChanged(); + +private: + void update(); + + QModelIndex interfaceIndex( ComputerControlInterface* controlInterface ) const; + + void updateState( const QModelIndex& index ); + void updateScreen( const QModelIndex& index ); + void updateActiveFeatures( const QModelIndex& index ); + void updateUser( const QModelIndex& index ); + + void startComputerControlInterface( ComputerControlInterface* controlInterface ); + void stopComputerControlInterface( const ComputerControlInterface::Pointer& controlInterface ); + + double averageAspectRatio() const; + + QImage scaleAndAlignIcon( const QImage& icon, QSize size ) const; + QImage computerDecorationRole( const ComputerControlInterface::Pointer& controlInterface ) const; + QString computerToolTipRole( const ComputerControlInterface::Pointer& controlInterface ) const; + QString computerDisplayRole( const ComputerControlInterface::Pointer& controlInterface ) const; + QString computerSortRole( const ComputerControlInterface::Pointer& controlInterface ) const; + static QString computerStateDescription( const ComputerControlInterface::Pointer& controlInterface ); + static QString loggedOnUserInformation( const ComputerControlInterface::Pointer& controlInterface ); + QString activeFeatures( const ComputerControlInterface::Pointer& controlInterface ) const; + + VeyonMaster* m_master; + + QImage m_iconDefault; + QImage m_iconConnectionProblem; + QImage m_iconServerNotRunning; + + QSize m_computerScreenSize{}; + + ComputerControlInterfaceList m_computerControlInterfaces{}; + +}; diff --git a/master/src/ComputerManager.cpp b/master/src/ComputerManager.cpp new file mode 100644 index 0000000..b0e3ebf --- /dev/null +++ b/master/src/ComputerManager.cpp @@ -0,0 +1,476 @@ +/* + * ComputerManager.cpp - maintains and provides a computer object list + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include + +#include "ComputerManager.h" +#include "VeyonConfiguration.h" +#include "NetworkObject.h" +#include "NetworkObjectDirectory.h" +#include "NetworkObjectDirectoryManager.h" +#include "NetworkObjectFilterProxyModel.h" +#include "NetworkObjectOverlayDataModel.h" +#include "NetworkObjectTreeModel.h" +#include "PlatformFilesystemFunctions.h" +#include "UserConfig.h" + + +ComputerManager::ComputerManager( UserConfig& config, QObject* parent ) : + QObject( parent ), + m_config( config ), + m_networkObjectDirectory( VeyonCore::networkObjectDirectoryManager().configuredDirectory() ), + m_networkObjectModel( new NetworkObjectTreeModel( m_networkObjectDirectory, this ) ), + m_networkObjectOverlayDataModel( new NetworkObjectOverlayDataModel( tr( "User" ), this ) ), + m_computerTreeModel( new CheckableItemProxyModel( NetworkObjectModel::UidRole, this ) ), + m_networkObjectFilterProxyModel( new NetworkObjectFilterProxyModel( this ) ), + m_localHostNames( QHostInfo::localHostName().toLower() ), + m_localHostAddresses( QHostInfo::fromName( QHostInfo::localHostName() ).addresses() ) +{ + if( m_networkObjectDirectory == nullptr ) + { + QMessageBox::critical( nullptr, + tr( "Missing network object directory plugin" ), + tr( "No default network object directory plugin was found. " + "Please check your installation or configure a different " + "network object directory backend via %1 Configurator." ). + arg( VeyonCore::applicationName() ) ); + qFatal( "ComputerManager: missing network object directory plugin!" ); + } + + if( QHostInfo::localDomainName().isEmpty() == false ) + { + m_localHostNames.append( QHostInfo::localHostName().toLower() + + QStringLiteral( "." ) + + QHostInfo::localDomainName().toLower() ); + } + + initNetworkObjectLayer(); + initLocations(); + initComputerTreeModel(); +} + + + +ComputerManager::~ComputerManager() +{ + m_config.setCheckedNetworkObjects( m_computerTreeModel->saveStates() ); +} + + + +void ComputerManager::addLocation( const QString& location ) +{ + m_locationFilterList.append( location ); + + updateLocationFilterList(); +} + + + +void ComputerManager::removeLocation( const QString& location ) +{ + if( m_currentLocations.contains( location ) == false ) + { + m_locationFilterList.removeAll( location ); + + updateLocationFilterList(); + } +} + + + +bool ComputerManager::saveComputerAndUsersList( const QString& fileName ) +{ + QStringList lines( tr( "Computer name;Hostname;User" ) ); + + const auto computers = selectedComputers( QModelIndex() ); + + for( const auto& computer : computers ) + { + const auto networkObjectIndex = findNetworkObject( computer.networkObjectUid() ); + if( networkObjectIndex.isValid() ) + { + // fetch user + const auto user = m_networkObjectOverlayDataModel->data( mapToUserNameModelIndex( networkObjectIndex ) ).toString(); + // create new line with computer and user + lines += computer.name() + QLatin1Char(';') + computer.hostAddress() + QLatin1Char(';') + user; // clazy:exclude=reserve-candidates + } + } + + // append empty string to generate final newline at end of file + lines += QString(); + + QFile outputFile( fileName ); + if( VeyonCore::platform().filesystemFunctions().openFileSafely( + &outputFile, + QFile::WriteOnly | QFile::Truncate, + QFile::ReadOwner | QFile::WriteOwner ) == false ) + { + return false; + } + + outputFile.write( lines.join( QStringLiteral("\r\n") ).toUtf8() ); + + return true; +} + + + +void ComputerManager::updateUser( const ComputerControlInterface::Pointer& controlInterface ) +{ + const auto networkObjectIndex = findNetworkObject( controlInterface->computer().networkObjectUid() ); + + if( networkObjectIndex.isValid() ) + { + auto user = controlInterface->userFullName(); + if( user.isEmpty() ) + { + user = controlInterface->userLoginName(); + } + m_networkObjectOverlayDataModel->setData( mapToUserNameModelIndex( networkObjectIndex ), + user, + Qt::DisplayRole ); + } +} + + + +void ComputerManager::checkChangedData( const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles ) +{ + Q_UNUSED(topLeft); + Q_UNUSED(bottomRight); + + if( roles.contains( Qt::CheckStateRole ) ) + { + Q_EMIT computerSelectionChanged(); + } +} + + + +void ComputerManager::initLocations() +{ + for( const auto& hostName : qAsConst( m_localHostNames ) ) + { + vDebug() << "initializing locations for hostname" << hostName; + } + + for( const auto& address : qAsConst( m_localHostAddresses ) ) + { + vDebug() << "initializing locations for host address" << address.toString(); + } + + m_currentLocations.append( findLocationOfComputer( m_localHostNames, m_localHostAddresses, QModelIndex() ) ); + + vDebug() << "found locations" << m_currentLocations; + + if( VeyonCore::config().showCurrentLocationOnly() ) + { + if( m_currentLocations.isEmpty() ) + { + QMessageBox::warning( nullptr, + tr( "Location detection failed" ), + tr( "Could not determine the location of this computer. " + "This indicates a problem with the system configuration. " + "All locations will be shown in the computer select panel instead." ) ); + vWarning() << "location detection failed"; + } + + m_locationFilterList = m_currentLocations; + updateLocationFilterList(); + } +} + + + +void ComputerManager::initNetworkObjectLayer() +{ + m_networkObjectDirectory->update(); + m_networkObjectDirectory->setUpdateInterval( VeyonCore::config().networkObjectDirectoryUpdateInterval() ); + m_networkObjectOverlayDataModel->setSourceModel( m_networkObjectModel ); + m_networkObjectFilterProxyModel->setSourceModel( m_networkObjectOverlayDataModel ); + m_computerTreeModel->setException( NetworkObjectModel::TypeRole, QVariant::fromValue( NetworkObject::Type::Label ) ); + m_computerTreeModel->setSourceModel( m_networkObjectFilterProxyModel ); + + const auto hideLocalComputer = VeyonCore::config().hideLocalComputer(); + const auto hideOwnSession = VeyonCore::config().hideOwnSession(); + + if( hideLocalComputer || hideOwnSession ) + { + QStringList localHostNames( { + QStringLiteral("localhost"), + QHostAddress( QHostAddress::LocalHost ).toString(), + QHostAddress( QHostAddress::LocalHostIPv6 ).toString() + } ); + + localHostNames.append( m_localHostNames ); + + for( const auto& address : qAsConst( m_localHostAddresses ) ) + { + localHostNames.append( address.toString() ); // clazy:exclude=reserve-candidates + } + + QStringList ownSessionNames; + ownSessionNames.reserve( localHostNames.size() ); + + if( hideOwnSession ) + { + const auto sessionServerPort = QString::number( VeyonCore::config().veyonServerPort() + + VeyonCore::sessionId() ); + + for( const auto& localHostName : qAsConst(localHostNames) ) + { + ownSessionNames.append( QStringLiteral("%1:%2").arg( localHostName, sessionServerPort ) ); + } + + vDebug() << "excluding own session via" << ownSessionNames; + } + + if( hideLocalComputer ) + { + vDebug() << "excluding local computer via" << localHostNames; + } + else + { + localHostNames.clear(); + } + + m_networkObjectFilterProxyModel->setComputerExcludeFilter( localHostNames + ownSessionNames ); + } + + m_networkObjectFilterProxyModel->setEmptyGroupsExcluded( VeyonCore::config().hideEmptyLocations() ); +} + + + +void ComputerManager::initComputerTreeModel() +{ + QJsonArray checkedNetworkObjects; + if( VeyonCore::config().autoSelectCurrentLocation() ) + { + for( const auto& location : qAsConst( m_currentLocations ) ) + { + const auto computersAtLocation = getComputersAtLocation( location ); + for( const auto& computer : computersAtLocation ) + { + checkedNetworkObjects += computer.networkObjectUid().toString(); + } + } + } + else + { + checkedNetworkObjects = m_config.checkedNetworkObjects(); + } + + m_computerTreeModel->loadStates( checkedNetworkObjects ); + + connect( computerTreeModel(), &QAbstractItemModel::modelReset, + this, &ComputerManager::computerSelectionReset ); + connect( computerTreeModel(), &QAbstractItemModel::layoutChanged, + this, &ComputerManager::computerSelectionReset ); + + connect( computerTreeModel(), &QAbstractItemModel::dataChanged, + this, &ComputerManager::checkChangedData ); + connect( computerTreeModel(), &QAbstractItemModel::rowsInserted, + this, &ComputerManager::computerSelectionChanged ); + connect( computerTreeModel(), &QAbstractItemModel::rowsRemoved, + this, &ComputerManager::computerSelectionChanged ); +} + + + +void ComputerManager::updateLocationFilterList() +{ + if( VeyonCore::config().showCurrentLocationOnly() ) + { + m_networkObjectFilterProxyModel->setGroupFilter( m_locationFilterList ); + } +} + + + +QString ComputerManager::findLocationOfComputer( const QStringList& hostNames, const QList& hostAddresses, const QModelIndex& parent ) +{ + QAbstractItemModel* model = networkObjectModel(); + + int rows = model->rowCount( parent ); + + for( int i = 0; i < rows; ++i ) + { + QModelIndex entryIndex = model->index( i, 0, parent ); + + auto objectType = static_cast( model->data( entryIndex, NetworkObjectModel::TypeRole ).toInt() ); + + if( objectType == NetworkObject::Type::Location || + objectType == NetworkObject::Type::DesktopGroup ) + { + const auto location = findLocationOfComputer( hostNames, hostAddresses, entryIndex ); + if( location.isEmpty() == false ) + { + return location; + } + } + else if( objectType == NetworkObject::Type::Host ) + { + QString currentHost = model->data( entryIndex, NetworkObjectModel::HostAddressRole ).toString().toLower(); + QHostAddress currentHostAddress; + + if( hostNames.contains( currentHost ) || + ( currentHostAddress.setAddress( currentHost ) && hostAddresses.contains( currentHostAddress ) ) ) + { + return model->data( parent, NetworkObjectModel::NameRole ).toString(); + } + } + } + + return {}; +} + + + +ComputerList ComputerManager::getComputersAtLocation( const QString& locationName, const QModelIndex& parent ) +{ + QAbstractItemModel* model = computerTreeModel(); + + int rows = model->rowCount( parent ); + + ComputerList computers; + + for( int i = 0; i < rows; ++i ) + { + QModelIndex entryIndex = model->index( i, 0, parent ); + + auto objectType = static_cast( model->data( entryIndex, NetworkObjectModel::TypeRole ).toInt() ); + + switch( objectType ) + { + case NetworkObject::Type::Location: + case NetworkObject::Type::DesktopGroup: + if( model->data( entryIndex, NetworkObjectModel::NameRole ).toString() == locationName ) + { + computers += getComputersAtLocation( locationName, entryIndex ); + } + break; + case NetworkObject::Type::Host: + computers += Computer( model->data( entryIndex, NetworkObjectModel::UidRole ).toUuid(), + model->data( entryIndex, NetworkObjectModel::NameRole ).toString(), + model->data( entryIndex, NetworkObjectModel::HostAddressRole ).toString(), + model->data( entryIndex, NetworkObjectModel::MacAddressRole ).toString() ); + break; + default: break; + } + } + + return computers; +} + + + +ComputerList ComputerManager::selectedComputers( const QModelIndex& parent ) +{ + QAbstractItemModel* model = computerTreeModel(); + + int rows = model->rowCount( parent ); + + ComputerList computers; + + for( int i = 0; i < rows; ++i ) + { + QModelIndex entryIndex = model->index( i, 0, parent ); + + if( QVariantHelper::value( model->data( entryIndex, NetworkObjectModel::CheckStateRole ) ) == Qt::Unchecked ) + { + continue; + } + + auto objectType = static_cast( model->data( entryIndex, NetworkObjectModel::TypeRole ).toInt() ); + + switch( objectType ) + { + case NetworkObject::Type::Location: + case NetworkObject::Type::DesktopGroup: + computers += selectedComputers( entryIndex ); + break; + case NetworkObject::Type::Host: + computers += Computer( model->data( entryIndex, NetworkObjectModel::UidRole ).toUuid(), + model->data( entryIndex, NetworkObjectModel::NameRole ).toString(), + model->data( entryIndex, NetworkObjectModel::HostAddressRole ).toString(), + model->data( entryIndex, NetworkObjectModel::MacAddressRole ).toString(), + model->data( parent, NetworkObjectModel::NameRole ).toString() ); + break; + default: break; + } + } + + return computers; +} + + + +QModelIndex ComputerManager::findNetworkObject( NetworkObject::Uid networkObjectUid, const QModelIndex& parent ) +{ + QAbstractItemModel* model = networkObjectModel(); + + int rows = model->rowCount( parent ); + + for( int i = 0; i < rows; ++i ) + { + QModelIndex entryIndex = model->index( i, 0, parent ); + + auto objectType = static_cast( model->data( entryIndex, NetworkObjectModel::TypeRole ).toInt() ); + + if( objectType == NetworkObject::Type::Location || + objectType == NetworkObject::Type::DesktopGroup ) + { + QModelIndex index = findNetworkObject( networkObjectUid, entryIndex ); + if( index.isValid() ) + { + return index; + } + } + else if( objectType == NetworkObject::Type::Host ) + { + if( model->data( entryIndex, NetworkObjectModel::UidRole ).toUuid() == networkObjectUid ) + { + return entryIndex; + } + } + } + + return {}; +} + + + +QModelIndex ComputerManager::mapToUserNameModelIndex( const QModelIndex& networkObjectIndex ) +{ + // map arbitrary index from m_networkObjectModel to username column in m_networkObjectOverlayDataModel + const auto parent = m_networkObjectOverlayDataModel->mapFromSource( networkObjectIndex.parent() ); + + return m_networkObjectOverlayDataModel->index( networkObjectIndex.row(), OverlayDataUsernameColumn, parent ); +} diff --git a/master/src/ComputerManager.h b/master/src/ComputerManager.h new file mode 100644 index 0000000..e688808 --- /dev/null +++ b/master/src/ComputerManager.h @@ -0,0 +1,98 @@ +/* + * ComputerManager.h - maintains and provides a computer object list + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CheckableItemProxyModel.h" +#include "ComputerControlInterface.h" + +class QHostAddress; +class NetworkObjectDirectory; +class NetworkObjectFilterProxyModel; +class NetworkObjectOverlayDataModel; +class UserConfig; + +class ComputerManager : public QObject +{ + Q_OBJECT +public: + ComputerManager( UserConfig& config, QObject* parent ); + ~ComputerManager() override; + + QAbstractItemModel* networkObjectModel() + { + return m_networkObjectModel; + } + + QAbstractItemModel* computerTreeModel() + { + return m_computerTreeModel; + } + + ComputerList selectedComputers( const QModelIndex& parent ); + + void addLocation( const QString& location ); + void removeLocation( const QString& location ); + + bool saveComputerAndUsersList( const QString& fileName ); + + void updateUser( const ComputerControlInterface::Pointer& controlInterface ); + +Q_SIGNALS: + void computerSelectionReset(); + void computerSelectionChanged(); + +private: + void checkChangedData( const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles ); + + void initLocations(); + void initNetworkObjectLayer(); + void initComputerTreeModel(); + void updateLocationFilterList(); + + QString findLocationOfComputer( const QStringList& hostNames, const QList& hostAddresses, const QModelIndex& parent ); + + ComputerList getComputersAtLocation( const QString& locationName, const QModelIndex& parent = QModelIndex() ); + + QModelIndex findNetworkObject( NetworkObject::Uid networkObjectUid, const QModelIndex& parent = QModelIndex() ); + + QModelIndex mapToUserNameModelIndex( const QModelIndex& networkObjectIndex ); + + static constexpr int OverlayDataUsernameColumn = 1; + + UserConfig& m_config; + + NetworkObjectDirectory* m_networkObjectDirectory; + QAbstractItemModel* m_networkObjectModel; + NetworkObjectOverlayDataModel* m_networkObjectOverlayDataModel; + CheckableItemProxyModel* m_computerTreeModel; + NetworkObjectFilterProxyModel* m_networkObjectFilterProxyModel; + + QStringList m_currentLocations; + QStringList m_locationFilterList; + + QStringList m_localHostNames; + QList m_localHostAddresses; + +}; diff --git a/master/src/ComputerMonitoringModel.cpp b/master/src/ComputerMonitoringModel.cpp new file mode 100644 index 0000000..6025d4e --- /dev/null +++ b/master/src/ComputerMonitoringModel.cpp @@ -0,0 +1,75 @@ +/* + * ComputerMonitoringModel.cpp - implementation of ComputerMonitoringModel + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "ComputerMonitoringModel.h" + +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) +#include +#endif + + +ComputerMonitoringModel::ComputerMonitoringModel( QObject* parent ) : + QSortFilterProxyModel( parent ), + m_stateRole( -1 ), + m_stateFilter( ComputerControlInterface::State::None ) +{ +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + new QAbstractItemModelTester( this, QAbstractItemModelTester::FailureReportingMode::Warning, this ); +#endif + + setFilterCaseSensitivity( Qt::CaseInsensitive ); +} + + + +void ComputerMonitoringModel::setStateRole( int role ) +{ + beginResetModel(); + m_stateRole = role; + endResetModel(); +} + + + +void ComputerMonitoringModel::setStateFilter( ComputerControlInterface::State state ) +{ + beginResetModel(); + m_stateFilter = state; + endResetModel(); +} + + + +bool ComputerMonitoringModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const +{ + if( m_stateFilter != ComputerControlInterface::State::None && + m_stateRole >= 0 && + QVariantHelper::value( + sourceModel()->data( sourceModel()->index( sourceRow, 0, sourceParent ), m_stateRole ) ) != m_stateFilter ) + { + return false; + } + + return QSortFilterProxyModel::filterAcceptsRow( sourceRow, sourceParent ); +} diff --git a/master/src/ComputerMonitoringModel.h b/master/src/ComputerMonitoringModel.h new file mode 100644 index 0000000..7d4c7c4 --- /dev/null +++ b/master/src/ComputerMonitoringModel.h @@ -0,0 +1,58 @@ +/* + * ComputerMonitoringModel.h - header file for ComputerMonitoringModel + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "ComputerControlInterface.h" + +class ComputerMonitoringModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + explicit ComputerMonitoringModel( QObject* parent ); + + int stateRole() const + { + return m_stateRole; + } + + void setStateRole( int role ); + + ComputerControlInterface::State stateFilter() const + { + return m_stateFilter; + } + + void setStateFilter( ComputerControlInterface::State state ); + +protected: + bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const override; + +private: + int m_stateRole; + ComputerControlInterface::State m_stateFilter; + +}; diff --git a/master/src/ComputerMonitoringView.cpp b/master/src/ComputerMonitoringView.cpp new file mode 100644 index 0000000..4d62d3e --- /dev/null +++ b/master/src/ComputerMonitoringView.cpp @@ -0,0 +1,212 @@ +/* + * ComputerMonitoringView.cpp - provides a view with computer monitor thumbnails + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "ComputerControlListModel.h" +#include "ComputerManager.h" +#include "ComputerMonitoringView.h" +#include "ComputerMonitoringModel.h" +#include "VeyonMaster.h" +#include "FeatureManager.h" +#include "VeyonConfiguration.h" +#include "UserConfig.h" + + +ComputerMonitoringView::ComputerMonitoringView() : + m_master( VeyonCore::instance()->findChild() ) +{ + m_autoAdjustIconSize = VeyonCore::config().autoAdjustMonitoringIconSize() || + master()->userConfig().autoAdjustMonitoringIconSize(); + + m_iconSizeAutoAdjustTimer.setInterval( IconSizeAdjustDelay ); + m_iconSizeAutoAdjustTimer.setSingleShot( true ); +} + + + +void ComputerMonitoringView::initializeView( QObject* self ) +{ + const auto autoAdjust = [this]() { initiateIconSizeAutoAdjust(); }; + + QObject::connect( &m_iconSizeAutoAdjustTimer, &QTimer::timeout, self, [this]() { performIconSizeAutoAdjust(); } ); + QObject::connect( dataModel(), &ComputerMonitoringModel::rowsInserted, self, autoAdjust ); + QObject::connect( dataModel(), &ComputerMonitoringModel::rowsRemoved, self, autoAdjust ); + QObject::connect( &m_master->computerControlListModel(), &ComputerControlListModel::computerScreenSizeChanged, self, + [this]() { setIconSize( m_master->computerControlListModel().computerScreenSize() ); } ); + + setColors( VeyonCore::config().computerMonitoringBackgroundColor(), + VeyonCore::config().computerMonitoringTextColor() ); + + setComputerScreenSize( m_master->userConfig().monitoringScreenSize() ); + + loadComputerPositions( m_master->userConfig().computerPositions() ); + setUseCustomComputerPositions( m_master->userConfig().useCustomComputerPositions() ); +} + + + +void ComputerMonitoringView::saveConfiguration() +{ + m_master->userConfig().setFilterPoweredOnComputers( dataModel()->stateFilter() != ComputerControlInterface::State::None ); + m_master->userConfig().setComputerPositions( saveComputerPositions() ); + m_master->userConfig().setUseCustomComputerPositions( useCustomComputerPositions() ); +} + + + +ComputerMonitoringModel* ComputerMonitoringView::dataModel() const +{ + return m_master->computerMonitoringModel(); +} + + + +QString ComputerMonitoringView::searchFilter() const +{ + return dataModel()->filterRegExp().pattern(); +} + + + +void ComputerMonitoringView::setSearchFilter( const QString& searchFilter ) +{ + dataModel()->setFilterRegExp( searchFilter ); +} + + + +void ComputerMonitoringView::setFilterPoweredOnComputers( bool enabled ) +{ + dataModel()->setStateFilter( enabled ? ComputerControlInterface::State::Connected : + ComputerControlInterface::State::None ); +} + + + +void ComputerMonitoringView::setComputerScreenSize( int size ) +{ + if( m_computerScreenSize != size ) + { + const auto minSize = MinimumComputerScreenSize; + const auto maxSize = MaximumComputerScreenSize; + size = qBound( minSize, size, maxSize ); + + m_computerScreenSize = size; + + m_master->userConfig().setMonitoringScreenSize( size ); + + m_master->computerControlListModel().updateComputerScreenSize(); + } +} + + + +int ComputerMonitoringView::computerScreenSize() const +{ + return m_computerScreenSize; +} + + + +void ComputerMonitoringView::setAutoAdjustIconSize( bool enabled ) +{ + m_autoAdjustIconSize = enabled; + + if( m_autoAdjustIconSize ) + { + performIconSizeAutoAdjust(); + } +} + + + +bool ComputerMonitoringView::performIconSizeAutoAdjust() +{ + m_iconSizeAutoAdjustTimer.stop(); + + return m_autoAdjustIconSize && dataModel()->rowCount() > 0; +} + + + + +void ComputerMonitoringView::initiateIconSizeAutoAdjust() +{ + m_iconSizeAutoAdjustTimer.start(); +} + + + +void ComputerMonitoringView::runFeature( const Feature& feature ) +{ + auto computerControlInterfaces = selectedComputerControlInterfaces(); + if( computerControlInterfaces.isEmpty() ) + { + computerControlInterfaces = m_master->filteredComputerControlInterfaces(); + } + + // mode feature already active? + if( feature.testFlag( Feature::Mode ) && + isFeatureOrSubFeatureActive( computerControlInterfaces, feature.uid() ) ) + { + // then stop it + m_master->featureManager().stopFeature( *m_master, feature, computerControlInterfaces ); + } + else + { + // stop all other active mode feature + if( feature.testFlag( Feature::Mode ) ) + { + for( const auto& currentFeature : m_master->features() ) + { + if( currentFeature.testFlag( Feature::Mode ) && currentFeature != feature ) + { + m_master->featureManager().stopFeature( *m_master, currentFeature, computerControlInterfaces ); + } + } + } + + m_master->featureManager().startFeature( *m_master, feature, computerControlInterfaces ); + } +} + + + +bool ComputerMonitoringView::isFeatureOrSubFeatureActive( const ComputerControlInterfaceList& computerControlInterfaces, + Feature::Uid featureUid ) const +{ + const auto featureList = FeatureUidList{ featureUid } + m_master->subFeaturesUids( featureUid ); + + for( const auto& controlInterface : computerControlInterfaces ) + { + for( const auto& activeFeature : controlInterface->activeFeatures() ) + { + if( featureList.contains( activeFeature ) ) + { + return true; + } + } + } + + return false; +} diff --git a/master/src/ComputerMonitoringView.h b/master/src/ComputerMonitoringView.h new file mode 100644 index 0000000..297ce1c --- /dev/null +++ b/master/src/ComputerMonitoringView.h @@ -0,0 +1,103 @@ +/* + * ComputerMonitoringView.h - provides a view with computer monitor thumbnails + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "ComputerControlInterface.h" + +class ComputerMonitoringModel; +class VeyonMaster; + +// clazy:excludeall=copyable-polymorphic + +class ComputerMonitoringView +{ +public: + static constexpr int MinimumComputerScreenSize = 50; + static constexpr int MaximumComputerScreenSize = 1000; + static constexpr int DefaultComputerScreenSize = 150; + + static constexpr auto IconSizeAdjustStepSize = 10; + static constexpr auto IconSizeAdjustDelay = 250; + + ComputerMonitoringView(); + virtual ~ComputerMonitoringView() = default; + + void initializeView( QObject* self ); + + void saveConfiguration(); + + ComputerMonitoringModel* dataModel() const; + + virtual ComputerControlInterfaceList selectedComputerControlInterfaces() const = 0; + ComputerControlInterfaceList filteredComputerControlInterfaces() const; + + QString searchFilter() const; + void setSearchFilter( const QString& searchFilter ); + + void setFilterPoweredOnComputers( bool enabled ); + + void setComputerScreenSize( int size ); + int computerScreenSize() const; + + virtual void alignComputers() = 0; + + bool autoAdjustIconSize() const + { + return m_autoAdjustIconSize; + } + void setAutoAdjustIconSize( bool enabled ); + +protected: + virtual void setColors( const QColor& backgroundColor, const QColor& textColor ) = 0; + virtual QJsonArray saveComputerPositions() = 0; + virtual bool useCustomComputerPositions() = 0; + virtual void loadComputerPositions( const QJsonArray& positions ) = 0; + virtual void setUseCustomComputerPositions( bool enabled ) = 0; + virtual void setIconSize( const QSize& size ) = 0; + + virtual bool performIconSizeAutoAdjust(); + + void initiateIconSizeAutoAdjust(); + + VeyonMaster* master() const + { + return m_master; + } + + void runFeature( const Feature& feature ); + + bool isFeatureOrSubFeatureActive( const ComputerControlInterfaceList& computerControlInterfaces, + Feature::Uid featureUid ) const; + +private: + VeyonMaster* m_master{nullptr}; + int m_computerScreenSize{DefaultComputerScreenSize}; + + bool m_autoAdjustIconSize{false}; + QTimer m_iconSizeAutoAdjustTimer{}; + +}; diff --git a/master/src/ComputerMonitoringWidget.cpp b/master/src/ComputerMonitoringWidget.cpp new file mode 100644 index 0000000..a9a39e9 --- /dev/null +++ b/master/src/ComputerMonitoringWidget.cpp @@ -0,0 +1,327 @@ +/* + * ComputerMonitoringWidget.cpp - provides a view with computer monitor thumbnails + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "ComputerControlListModel.h" +#include "ComputerMonitoringWidget.h" +#include "ComputerMonitoringModel.h" +#include "VeyonMaster.h" +#include "FeatureManager.h" +#include "VeyonConfiguration.h" + + +ComputerMonitoringWidget::ComputerMonitoringWidget( QWidget *parent ) : + FlexibleListView( parent ), + m_featureMenu( new QMenu( this ) ) +{ + const auto computerMonitoringThumbnailSpacing = VeyonCore::config().computerMonitoringThumbnailSpacing(); + + setContextMenuPolicy( Qt::CustomContextMenu ); + setAcceptDrops( true ); + setDragEnabled( true ); + setDragDropMode( QAbstractItemView::DropOnly ); + setDefaultDropAction( Qt::MoveAction ); + setSelectionMode( QAbstractItemView::ExtendedSelection ); + setFlow( QListView::LeftToRight ); + setWrapping( true ); + setResizeMode( QListView::Adjust ); + setSpacing( computerMonitoringThumbnailSpacing ); + setViewMode( QListView::IconMode ); + setUniformItemSizes( true ); + setSelectionRectVisible( true ); + + setUidRole( ComputerControlListModel::UidRole ); + + connect( this, &QListView::doubleClicked, this, &ComputerMonitoringWidget::runDoubleClickFeature ); + connect( this, &QListView::customContextMenuRequested, + this, [this]( QPoint pos ) { showContextMenu( mapToGlobal( pos ) ); } ); + + initializeView( this ); + + setModel( dataModel() ); +} + + + +ComputerControlInterfaceList ComputerMonitoringWidget::selectedComputerControlInterfaces() const +{ + ComputerControlInterfaceList computerControlInterfaces; + + const auto selectedIndices = selectionModel()->selectedIndexes(); // clazy:exclude=inefficient-qlist + computerControlInterfaces.reserve( selectedIndices.size() ); + + for( const auto& index : selectedIndices ) + { + computerControlInterfaces.append( model()->data( index, ComputerControlListModel::ControlInterfaceRole ) + .value() ); + } + + return computerControlInterfaces; +} + + + +bool ComputerMonitoringWidget::performIconSizeAutoAdjust() +{ + if( ComputerMonitoringView::performIconSizeAutoAdjust() == false) + { + return false; + } + + m_ignoreResizeEvent = true; + + auto size = iconSize().width(); + + setComputerScreenSize( size ); + QApplication::processEvents(); + + while( verticalScrollBar()->isVisible() == false && + horizontalScrollBar()->isVisible() == false && + size < MaximumComputerScreenSize ) + { + size += IconSizeAdjustStepSize; + setComputerScreenSize( size ); + QApplication::processEvents(); + } + + while( ( verticalScrollBar()->isVisible() || + horizontalScrollBar()->isVisible() ) && + size > MinimumComputerScreenSize ) + { + size -= IconSizeAdjustStepSize; + setComputerScreenSize( size ); + QApplication::processEvents(); + } + + Q_EMIT computerScreenSizeAdjusted( size ); + + m_ignoreResizeEvent = false; + + return true; +} + + + + +void ComputerMonitoringWidget::setUseCustomComputerPositions( bool enabled ) +{ + setFlexible( enabled ); +} + + + +void ComputerMonitoringWidget::alignComputers() +{ + alignToGrid(); +} + + + +void ComputerMonitoringWidget::showContextMenu( QPoint globalPos ) +{ + populateFeatureMenu( selectedComputerControlInterfaces() ); + + m_featureMenu->exec( globalPos ); +} + + + +void ComputerMonitoringWidget::setIconSize( const QSize& size ) +{ + QAbstractItemView::setIconSize( size ); +} + + + +void ComputerMonitoringWidget::setColors( const QColor& backgroundColor, const QColor& textColor ) +{ + auto pal = palette(); + pal.setColor( QPalette::Base, backgroundColor ); + pal.setColor( QPalette::Text, textColor ); + setPalette( pal ); +} + + + +QJsonArray ComputerMonitoringWidget::saveComputerPositions() +{ + return savePositions(); +} + + + +bool ComputerMonitoringWidget::useCustomComputerPositions() +{ + return flexible(); +} + + + +void ComputerMonitoringWidget::loadComputerPositions( const QJsonArray& positions ) +{ + loadPositions( positions ); +} + + + +void ComputerMonitoringWidget::populateFeatureMenu( const ComputerControlInterfaceList& computerControlInterfaces ) +{ + Plugin::Uid previousPluginUid; + + m_featureMenu->clear(); + + for( const auto& feature : master()->features() ) + { + if( feature.testFlag( Feature::Internal ) ) + { + continue; + } + + Plugin::Uid pluginUid = master()->featureManager().pluginUid( feature ); + + if( previousPluginUid.isNull() == false && + pluginUid != previousPluginUid && + feature.testFlag( Feature::Mode ) == false ) + { + m_featureMenu->addSeparator(); + } + + previousPluginUid = pluginUid; + + auto active = false; + + auto label = feature.displayName(); + if( feature.displayNameActive().isEmpty() == false && + isFeatureOrSubFeatureActive( computerControlInterfaces, feature.uid() ) ) + { + label = feature.displayNameActive(); + active = true; + } + + const auto subFeatures = master()->subFeatures( feature.uid() ); + if( subFeatures.isEmpty() || active ) + { + addFeatureToMenu( feature, label ); + } + else + { + addSubFeaturesToMenu( feature, subFeatures, label ); + } + } +} + + + +void ComputerMonitoringWidget::addFeatureToMenu( const Feature& feature, const QString& label ) +{ +#if QT_VERSION < 0x050600 +#warning Building legacy compat code for unsupported version of Qt + auto action = m_featureMenu->addAction( QIcon( feature.iconUrl() ), label ); + connect( action, &QAction::triggered, this, [=] () { runFeature( feature ); } ); +#else + m_featureMenu->addAction( QIcon( feature.iconUrl() ), + label, + this, [=] () { runFeature( feature ); } ); +#endif +} + + + +void ComputerMonitoringWidget::addSubFeaturesToMenu( const Feature& parentFeature, const FeatureList& subFeatures, const QString& label ) +{ + auto menu = m_featureMenu->addMenu( QIcon( parentFeature.iconUrl() ), label ); + + for( const auto& subFeature : subFeatures ) + { +#if QT_VERSION < 0x050600 +#warning Building legacy compat code for unsupported version of Qt + auto action = menu->addAction( QIcon( subFeature.iconUrl() ), subFeature.displayName() ); + action->setShortcut( subFeature.shortcut() ); + connect( action, &QAction::triggered, this, [=] () { runFeature( subFeature ); } ); +#else + menu->addAction( QIcon( subFeature.iconUrl() ), subFeature.displayName(), this, + [=]() { runFeature( subFeature ); }, subFeature.shortcut() ); +#endif + } +} + + + +void ComputerMonitoringWidget::runDoubleClickFeature( const QModelIndex& index ) +{ + const Feature& feature = master()->featureManager().feature( VeyonCore::config().computerDoubleClickFeature() ); + + if( index.isValid() && feature.isValid() ) + { + selectionModel()->select( index, QItemSelectionModel::SelectCurrent ); + runFeature( feature ); + } +} + + + +void ComputerMonitoringWidget::resizeEvent( QResizeEvent* event ) +{ + FlexibleListView::resizeEvent( event ); + + if( m_ignoreResizeEvent == false ) + { + initiateIconSizeAutoAdjust(); + } +} + + + +void ComputerMonitoringWidget::showEvent( QShowEvent* event ) +{ + if( event->spontaneous() == false ) + { + initiateIconSizeAutoAdjust(); + } + + FlexibleListView::showEvent( event ); +} + + + +void ComputerMonitoringWidget::wheelEvent( QWheelEvent* event ) +{ + if( m_ignoreWheelEvent == false && + event->modifiers().testFlag( Qt::ControlModifier ) ) + { + setComputerScreenSize( iconSize().width() + event->angleDelta().y() / 8 ); + + Q_EMIT computerScreenSizeAdjusted( computerScreenSize() ); + + event->accept(); + } + else + { + QListView::wheelEvent( event ); + } +} diff --git a/master/src/ComputerMonitoringWidget.h b/master/src/ComputerMonitoringWidget.h new file mode 100644 index 0000000..8e13257 --- /dev/null +++ b/master/src/ComputerMonitoringWidget.h @@ -0,0 +1,80 @@ +/* + * ComputerMonitoringWidget.h - provides a view with computer monitor thumbnails + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "ComputerMonitoringView.h" +#include "FlexibleListView.h" + +#include + +class FlexibleListView; + +class ComputerMonitoringWidget : public FlexibleListView, public ComputerMonitoringView +{ + Q_OBJECT +public: + explicit ComputerMonitoringWidget( QWidget *parent = nullptr ); + ~ComputerMonitoringWidget() override = default; + + ComputerControlInterfaceList selectedComputerControlInterfaces() const override; + + void setUseCustomComputerPositions( bool enabled ) override; + void alignComputers() override; + + void showContextMenu( QPoint globalPos ); + + void setIconSize( const QSize& size ) override; + + void setIgnoreWheelEvent( bool enabled ) + { + m_ignoreWheelEvent = enabled; + } + +private: + void setColors( const QColor& backgroundColor, const QColor& textColor ) override; + QJsonArray saveComputerPositions() override; + bool useCustomComputerPositions() override; + void loadComputerPositions( const QJsonArray& positions ) override; + + bool performIconSizeAutoAdjust() override; + + void populateFeatureMenu( const ComputerControlInterfaceList& computerControlInterfaces ); + void addFeatureToMenu( const Feature& feature, const QString& label ); + void addSubFeaturesToMenu( const Feature& parentFeature, const FeatureList& subFeatures, const QString& label ); + + void runDoubleClickFeature( const QModelIndex& index ); + + void resizeEvent( QResizeEvent* event ) override; + void showEvent( QShowEvent* event ) override; + void wheelEvent( QWheelEvent* event ) override; + + QMenu* m_featureMenu{}; + bool m_ignoreWheelEvent{false}; + bool m_ignoreResizeEvent{false}; + +Q_SIGNALS: + void computerScreenSizeAdjusted( int size ); + +}; diff --git a/master/src/ComputerSelectPanel.cpp b/master/src/ComputerSelectPanel.cpp new file mode 100644 index 0000000..7670330 --- /dev/null +++ b/master/src/ComputerSelectPanel.cpp @@ -0,0 +1,174 @@ +/* + * ComputerSelectPanel.cpp - provides a view for a network object tree + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include "ComputerSelectPanel.h" +#include "ComputerManager.h" +#include "NetworkObjectModel.h" +#include "RecursiveFilterProxyModel.h" +#include "LocationDialog.h" +#include "VeyonConfiguration.h" + +#include "ui_ComputerSelectPanel.h" + + +ComputerSelectPanel::ComputerSelectPanel( ComputerManager& computerManager, QWidget *parent ) : + QWidget(parent), + ui(new Ui::ComputerSelectPanel), + m_computerManager( computerManager ), + m_filterProxyModel( new RecursiveFilterProxyModel( this ) ) +{ + m_filterProxyModel->setSourceModel( computerManager.computerTreeModel() ); + m_filterProxyModel->setFilterCaseSensitivity( Qt::CaseInsensitive ); + m_filterProxyModel->setFilterKeyColumn( -1 ); // filter all columns instead of first one only + + ui->setupUi(this); + + // capture keyboard events for tree view + ui->treeView->installEventFilter( this ); + + // set computer tree model as data model + ui->treeView->setModel( m_filterProxyModel ); + + // set default sort order + ui->treeView->sortByColumn( 0, Qt::AscendingOrder ); + + ui->addLocationButton->setVisible( VeyonCore::config().showCurrentLocationOnly() && + VeyonCore::config().allowAddingHiddenLocations() ); + + ui->filterLineEdit->setHidden( VeyonCore::config().hideComputerFilter() ); + + connect( ui->filterLineEdit, &QLineEdit::textChanged, + this, &ComputerSelectPanel::updateFilter ); +} + + + +ComputerSelectPanel::~ComputerSelectPanel() +{ + delete ui; +} + + + +bool ComputerSelectPanel::eventFilter( QObject *watched, QEvent *event ) +{ + if( watched == ui->treeView && + event->type() == QEvent::KeyPress && + dynamic_cast(event) != nullptr && + dynamic_cast(event)->key() == Qt::Key_Delete ) + { + removeLocation(); + return true; + } + + return QWidget::eventFilter( watched, event ); +} + + + +void ComputerSelectPanel::addLocation() +{ + LocationDialog dialog( m_computerManager.networkObjectModel(), this ); + if( dialog.exec() && dialog.selectedLocation().isEmpty() == false ) + { + m_computerManager.addLocation( dialog.selectedLocation() ); + } +} + + + +void ComputerSelectPanel::removeLocation() +{ + auto model = m_computerManager.computerTreeModel(); + const auto index = ui->treeView->selectionModel()->currentIndex(); + + if( index.isValid() && + QVariantHelper::value( model->data( index, NetworkObjectModel::TypeRole ) ) == NetworkObject::Type::Location ) + { + m_computerManager.removeLocation( model->data( index, NetworkObjectModel::NameRole ).toString() ); + } +} + + + +void ComputerSelectPanel::saveList() +{ + QString fileName = QFileDialog::getSaveFileName( this, tr( "Select output filename" ), + QDir::homePath(), tr( "CSV files (*.csv)" ) ); + if( fileName.isEmpty() == false ) + { + if( m_computerManager.saveComputerAndUsersList( fileName ) == false ) + { + QMessageBox::critical( this, tr( "File error"), + tr( "Could not write the computer and users list to %1! " + "Please check the file access permissions." ).arg( fileName ) ); + } + } +} + + + +void ComputerSelectPanel::updateFilter() +{ + const auto filter = ui->filterLineEdit->text(); + auto model = ui->treeView->model(); + + if( filter.isEmpty() ) + { + m_filterProxyModel->setFilterWildcard( filter ); + + for( int i = 0; i < model->rowCount(); ++i ) + { + const auto index = model->index( i, 0 ); + ui->treeView->setExpanded( index, m_expandedGroups.contains( index ) ); + } + + m_previousFilter.clear(); + } + else + { + if( m_previousFilter.isEmpty() ) + { + m_expandedGroups.clear(); + + for( int i = 0; i < model->rowCount(); ++i ) + { + const auto index = model->index( i, 0 ); + if( ui->treeView->isExpanded( index ) ) + { + m_expandedGroups.append( index ); + } + } + } + + m_previousFilter = filter; + + m_filterProxyModel->setFilterWildcard( filter ); + ui->treeView->expandAll(); + } +} diff --git a/master/src/ComputerSelectPanel.h b/master/src/ComputerSelectPanel.h new file mode 100644 index 0000000..d3c4220 --- /dev/null +++ b/master/src/ComputerSelectPanel.h @@ -0,0 +1,59 @@ +/* + * ComputerSelectPanel.h - provides a view for a network object tree + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +namespace Ui { +class ComputerSelectPanel; +} + +class ComputerManager; +class RecursiveFilterProxyModel; + +class ComputerSelectPanel : public QWidget +{ + Q_OBJECT +public: + explicit ComputerSelectPanel( ComputerManager& computerManager, QWidget *parent = nullptr ); + ~ComputerSelectPanel() override; + + bool eventFilter(QObject *watched, QEvent *event) override; + +private Q_SLOTS: + void addLocation(); + void removeLocation(); + void saveList(); + void updateFilter(); + +private: + Ui::ComputerSelectPanel *ui; + ComputerManager& m_computerManager; + RecursiveFilterProxyModel* m_filterProxyModel; + QString m_previousFilter; + QModelIndexList m_expandedGroups; + +}; diff --git a/master/src/ComputerSelectPanel.ui b/master/src/ComputerSelectPanel.ui new file mode 100644 index 0000000..ad2cf0a --- /dev/null +++ b/master/src/ComputerSelectPanel.ui @@ -0,0 +1,90 @@ + + + ComputerSelectPanel + + + + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::NoEditTriggers + + + true + + + true + + + 180 + + + false + + + + + + + Computer search + + + + + + + Add location + + + + + + + Save computer/user list + + + + + + + + + addLocationButton + clicked() + ComputerSelectPanel + addLocation() + + + 262 + 663 + + + 262 + 375 + + + + + saveListButton + clicked() + ComputerSelectPanel + saveList() + + + 142 + 219 + + + 142 + 127 + + + + + + addLocation() + saveList() + + diff --git a/master/src/DocumentationFigureCreator.cpp b/master/src/DocumentationFigureCreator.cpp new file mode 100644 index 0000000..e43c964 --- /dev/null +++ b/master/src/DocumentationFigureCreator.cpp @@ -0,0 +1,605 @@ +/* + * DocumentationFigureCreator.cpp - helper for creating documentation figures + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ComputerMonitoringWidget.h" +#include "DocumentationFigureCreator.h" +#include "FeatureManager.h" +#include "LocationDialog.h" +#include "MainToolBar.h" +#include "MainWindow.h" +#include "PasswordDialog.h" +#include "Plugin.h" +#include "PluginManager.h" +#include "Screenshot.h" +#include "ScreenshotManagementPanel.h" +#include "ToolButton.h" +#include "VeyonConfiguration.h" +#include "VncViewWidget.h" + + +#ifdef VEYON_DEBUG + +DocumentationFigureCreator::DocumentationFigureCreator() : + QObject(), + m_master( nullptr ), + m_eventLoop( this ) +{ + m_master = new VeyonMaster; +} + + + +void DocumentationFigureCreator::run() +{ + auto mainWindow = m_master->mainWindow(); + + mainWindow->move( 0, 0 ); + mainWindow->resize( 3000, 1000 ); + mainWindow->show(); + + hideComputers(); + + createFeatureFigures(); + createContextMenuFigure(); + createLogonDialogFigure(); + createLocationDialogFigure(); + createScreenshotManagementPanelFigure(); + createUserLoginDialogFigure(); + createTextMessageDialogFigure(); + createOpenWebsiteDialogFigure(); + createWebsiteMenuFigure(); + createRunProgramDialogFigure(); + createProgramMenuFigure(); + createRemoteAccessHostDialogFigure(); + createRemoteAccessWindowFigure(); + createPowerDownOptionsFigure(); + createPowerDownTimeInputDialogFigure(); + createFileTransferDialogFigure(); +} + + + +void DocumentationFigureCreator::createFeatureFigures() +{ + Plugin::Uid previousPluginUid; + Feature const* previousFeature = nullptr; + + int x = -1; + int w = 0; + + auto toolbar = m_master->mainWindow()->findChild(); + + const QStringList separatedPluginFeatures( { QStringLiteral("a54ee018-42bf-4569-90c7-0d8470125ccf"), + QStringLiteral("80580500-2e59-4297-9e35-e53959b028cd") + } ); + + const auto& features = m_master->features(); + + for( const auto& feature : features ) + { + auto btn = toolbar->findChild( feature.name() ); + const auto pluginUid = m_master->featureManager().pluginUid( feature ); + + if( previousPluginUid.isNull() ) + { + previousPluginUid = pluginUid; + previousFeature = &feature; + x = btn->x(); + } + + const auto separatedFeature = separatedPluginFeatures.contains( VeyonCore::formattedUuid( previousPluginUid ) ); + + if( pluginUid != previousPluginUid || separatedFeature ) + { + auto fileName = VeyonCore::pluginManager().pluginName( previousPluginUid ); + if( separatedFeature && previousFeature ) + { + fileName = previousFeature->name(); + } + grabWidget( toolbar, QPoint( x-2, btn->y()-1 ), QSize( w, btn->height() + 2 ), + QStringLiteral("Feature%1.png").arg( fileName ) ); + previousPluginUid = pluginUid; + x = btn->x(); + w = btn->width() + 4; + } + else + { + w += btn->width() + 2; + } + + if( feature == features.last() ) + { + auto fileName = VeyonCore::pluginManager().pluginName( pluginUid ); + if( separatedFeature ) + { + fileName = feature.name(); + } + grabWidget( toolbar, QPoint( x-2, btn->y()-1 ), QSize( w, btn->height() + 2 ), + QStringLiteral("Feature%1.png").arg( fileName ) ); + } + + previousFeature = &feature; + } +} + + + +void DocumentationFigureCreator::createContextMenuFigure() +{ + auto view = m_master->mainWindow()->findChild(); + auto menu = view->findChild(); + + connect( menu, &QMenu::aboutToShow, this, [menu]() { + grabWidget( menu, QPoint(), menu->size(), QStringLiteral("ContextMenu.png") ); + menu->close(); + }, Qt::QueuedConnection ); + + view->showContextMenu( QPoint( 200, 200 ) ); +} + + + +void DocumentationFigureCreator::createLogonDialogFigure() +{ + PasswordDialog dialog( m_master->mainWindow() ); + dialog.show(); + dialog.findChild( QStringLiteral("password") )->setText( QStringLiteral( "TeacherPassword") ); + dialog.findChild( QStringLiteral("password") )->cursorForward( false ); + dialog.findChild( QStringLiteral("username") )->setText( tr( "Teacher") ); + + grabDialog( &dialog, {}, QStringLiteral("LogonDialog.png") ); + + dialog.exec(); +} + + + +void DocumentationFigureCreator::createLocationDialogFigure() +{ + QStringList locations; + locations.reserve( 3 ); + + for( int i = 0; i < 3; ++i ) + { + locations.append( tr( "Room %1").arg( 101 + i ) ); + } + + QStringListModel locationsModel( locations, this ); + LocationDialog dialog( &locationsModel, m_master->mainWindow() ); + + grabDialog( &dialog, QSize( 300, 200 ), QStringLiteral("LocationDialog.png") ); + + dialog.exec(); +} + + + +void DocumentationFigureCreator::createScreenshotManagementPanelFigure() +{ + auto window = m_master->mainWindow(); + auto panel = window->findChild(); + auto panelButton = window->findChild( QStringLiteral("screenshotManagementPanelButton") ); + auto list = panel->findChild(); + + const QStringList exampleUsers({ + QStringLiteral("Albert Einstein"), + QStringLiteral("Blaise Pascal"), + QStringLiteral("Caroline Herschel"), + QStringLiteral("Dorothy Hodgkin") + }); + + const QStringList exampleHosts({ + QStringLiteral("mars"), + QStringLiteral("venus"), + QStringLiteral("saturn"), + QStringLiteral("pluto") + }); + + const QDate date( 2019, 4, 4 ); + const QTime time( 9, 36, 27 ); + + QStringList screenshots({ + Screenshot::constructFileName( exampleUsers[0], exampleHosts[0], date, time ), + Screenshot::constructFileName( exampleUsers[1], exampleHosts[1], date, time ), + Screenshot::constructFileName( exampleUsers[2], exampleHosts[2], date, time ), + Screenshot::constructFileName( exampleUsers[3], exampleHosts[3], date, time ) + }); + + constexpr int exampleScreenshotIndex = 1; + + QStringListModel screenshotsModel( screenshots, this ); + list->setModel( &screenshotsModel ); + list->selectionModel()->setCurrentIndex( screenshotsModel.index( exampleScreenshotIndex ), QItemSelectionModel::SelectCurrent ); + + QImage exampleScreenshotImage{ QStringLiteral(":/examples/example-screenshot.png" ) }; + exampleScreenshotImage.setText( Screenshot::metaDataKey( Screenshot::MetaData::User ), exampleUsers[exampleScreenshotIndex] ); + + Screenshot exampleScreenshot{ screenshots[exampleScreenshotIndex] }; + exampleScreenshot.setImage( exampleScreenshotImage ); + panel->setPreview( exampleScreenshot ); + + window->setMaximumHeight( 600 ); + panel->setMaximumWidth( 400 ); + panelButton->setChecked( true ); + + scheduleUiOperation( [this, panel, window, panelButton]() { + const auto panelPos = panel->mapTo( window, QPoint( 0, 0 ) ); + const auto panelButtonPos = panelButton->mapTo( window, panelButton->rect().bottomRight() ); + grabWindow( window, panelPos, + QSize( panel->width(), panelButtonPos.y() - panelPos.y() + 2 ), + QStringLiteral("ScreenshotManagementPanel.png") ); + m_eventLoop.quit(); + } ); + + m_eventLoop.exec(); +} + + + +void DocumentationFigureCreator::createPowerDownOptionsFigure() +{ + auto toolbar = m_master->mainWindow()->findChild(); + auto powerDownButton = toolbar->findChild( QStringLiteral("PowerDown") ); + + scheduleUiOperation( [this, powerDownButton]() { + scheduleUiOperation( [this, powerDownButton]() { + auto menu = powerDownButton->menu(); + + grabWindow( m_master->mainWindow(), powerDownButton->mapTo( m_master->mainWindow(), QPoint( 0, 0 ) ), + QSize( qMax( powerDownButton->width(), menu->width() ), + powerDownButton->height() + menu->height() ), + QStringLiteral("PowerDownOptions.png") ); + menu->close(); + } ); + auto menu = powerDownButton->menu(); + menu->close(); + + powerDownButton->click(); + + } ); + + powerDownButton->showMenu(); +} + + + +void DocumentationFigureCreator::createPowerDownTimeInputDialogFigure() +{ + scheduleUiOperation( []() { + auto dialog = qobject_cast( QApplication::activeWindow() ); + + dialog->findChild( QStringLiteral("minutesSpinBox") )->setValue( 3 ); + + grabDialog( dialog, {}, QStringLiteral("PowerDownTimeInputDialog.png") ); + }); + + m_master->runFeature( m_master->featureManager().feature( Feature::Uid( "352de795-7fc4-4850-bc57-525bcb7033f5" ) ) ); +} + + + +void DocumentationFigureCreator::createUserLoginDialogFigure() +{ + scheduleUiOperation( []() { + auto dialog = qobject_cast( QApplication::activeWindow() ); + + dialog->findChild( QStringLiteral("password") )->setText( QStringLiteral( "TeacherPassword") ); + dialog->findChild( QStringLiteral("password") )->cursorForward( false ); + dialog->findChild( QStringLiteral("username") )->setText( tr( "generic-student-user") ); + + grabDialog( dialog, {}, QStringLiteral("UserLoginDialog.png") ); + }); + + m_master->runFeature( m_master->featureManager().feature( Feature::Uid( "7310707d-3918-460d-a949-65bd152cb958" ) ) ); +} + + + +void DocumentationFigureCreator::createTextMessageDialogFigure() +{ + scheduleUiOperation( []() { + auto dialog = qobject_cast( QApplication::activeWindow() ); + + dialog->findChild()->setText( tr( "Please complete all tasks within the next 5 minutes." ) ); + + grabDialog( dialog, {}, QStringLiteral("TextMessageDialog.png") ); + }); + + m_master->runFeature( m_master->featureManager().feature( Feature::Uid( "e75ae9c8-ac17-4d00-8f0d-019348346208" ) ) ); +} + + + +void DocumentationFigureCreator::createOpenWebsiteDialogFigure() +{ + scheduleUiOperation( []() { + auto dialog = qobject_cast( QApplication::activeWindow() ); + dialog->findChild( QStringLiteral("websiteLineEdit") )-> + setText( QStringLiteral("https://veyon.io") ); + + grabDialog( dialog, {}, QStringLiteral("OpenWebsiteDialog.png") ); + }); + + m_master->runFeature( m_master->featureManager().feature( Feature::Uid( "8a11a75d-b3db-48b6-b9cb-f8422ddd5b0c" ) ) ); +} + + + +void DocumentationFigureCreator::createWebsiteMenuFigure() +{ + auto toolbar = m_master->mainWindow()->findChild(); + auto openWebsiteButton = toolbar->findChild( QStringLiteral("OpenWebsite") ); + + auto menu = new QMenu; + menu->addAction( QStringLiteral("Intranet") ); + menu->addAction( QStringLiteral("Wikipedia") ); + menu->addAction( QIcon( QStringLiteral(":/core/document-edit.png") ), tr("Custom website") ); + + scheduleUiOperation( [this, openWebsiteButton, menu]() { + scheduleUiOperation( [this, openWebsiteButton, menu]() { + + grabWindow( m_master->mainWindow(), openWebsiteButton->mapTo( m_master->mainWindow(), QPoint( 0, 0 ) ), + QSize( qMax( openWebsiteButton->width(), menu->width() ), + openWebsiteButton->height() + menu->height() ), + QStringLiteral("OpenWebsiteMenu.png") ); + menu->close(); + } ); + + menu->close(); + openWebsiteButton->showMenu(); + } ); + + openWebsiteButton->setMenu( menu ); + openWebsiteButton->showMenu(); +} + + + +void DocumentationFigureCreator::createRunProgramDialogFigure() +{ + scheduleUiOperation( []() { + auto dialog = qobject_cast( QApplication::activeWindow() ); + dialog->findChild()->setText( QStringLiteral("notepad") ); + dialog->setFocus(); + + grabDialog( dialog, {}, QStringLiteral("RunProgramDialog.png") ); + }); + + m_master->runFeature( m_master->featureManager().feature( Feature::Uid( "da9ca56a-b2ad-4fff-8f8a-929b2927b442" ) ) ); +} + + + +void DocumentationFigureCreator::createProgramMenuFigure() +{ + auto toolbar = m_master->mainWindow()->findChild(); + auto runProgramButton = toolbar->findChild( QStringLiteral("RunProgram") ); + + auto menu = new QMenu; + menu->addAction( tr("Open file manager") ); + menu->addAction( tr("Start learning tool") ); + menu->addAction( tr("Play tutorial video") ); + menu->addAction( QIcon( QStringLiteral(":/core/document-edit.png") ), tr("Custom program") ); + + scheduleUiOperation( [this, runProgramButton, menu]() { + scheduleUiOperation( [this, runProgramButton, menu]() { + + grabWindow( m_master->mainWindow(), runProgramButton->mapTo( m_master->mainWindow(), QPoint( 0, 0 ) ), + QSize( qMax( runProgramButton->width(), menu->width() ), + runProgramButton->height() + menu->height() ), + QStringLiteral("RunProgramMenu.png") ); + menu->close(); + } ); + + menu->close(); + runProgramButton->showMenu(); + } ); + + runProgramButton->setMenu( menu ); + runProgramButton->showMenu(); +} + + + +void DocumentationFigureCreator::createRemoteAccessHostDialogFigure() +{ + scheduleUiOperation( []() { + auto dialog = qobject_cast( QApplication::activeWindow() ); + dialog->setTextValue( QStringLiteral("pc27") ); + + grabDialog( dialog, {}, QStringLiteral("RemoteAccessHostDialog.png") ); + } ); + + m_master->runFeature( m_master->featureManager().feature( Feature::Uid( "a18e545b-1321-4d4e-ac34-adc421c6e9c8" ) ) ); +} + + + +void DocumentationFigureCreator::createRemoteAccessWindowFigure() +{ + scheduleUiOperation( [this]() { + auto dialog = qobject_cast( QApplication::activeWindow() ); + dialog->setTextValue( QStringLiteral("pc27") ); + dialog->setFocus(); + dialog->move( 0, 0 ); + + scheduleUiOperation( [this, dialog]() { + dialog->accept(); + + scheduleUiOperation( [this]() { + auto window = QApplication::activeWindow(); + auto vncView = window->findChild(); + Q_ASSERT(vncView != nullptr); + + for( auto timeline : window->findChildren() ) + { + timeline->stop(); + timeline->setCurrentTime( 0 ); + } + + window->showNormal(); + window->setFixedSize( 1000, 450 ); + window->move( 0, 0 ); + + scheduleUiOperation( [this, window]() { + grabWindow( window, QStringLiteral("RemoteAccessWindow.png") ); + + auto shortcuts = window->findChild( QStringLiteral("shortcuts") ); + Q_ASSERT(shortcuts != nullptr); + + scheduleUiOperation( [this, window, shortcuts]() { + auto menu = shortcuts->menu(); + + grabWindow( window, shortcuts->mapTo( window, QPoint( 0, 0 ) ), + QSize( qMax( shortcuts->width(), menu->width() ), + shortcuts->height() + menu->height() ), + QStringLiteral("RemoteAccessShortcutsMenu.png") ); + menu->close(); + window->close(); + m_eventLoop.quit(); + } ); + + shortcuts->showMenu(); + } ); + } ); + } ); + } ); + + m_master->runFeature( m_master->featureManager().feature( Feature::Uid( "ca00ad68-1709-4abe-85e2-48dff6ccf8a2" ) ) ); + m_eventLoop.exec(); +} + + + +void DocumentationFigureCreator::createFileTransferDialogFigure() +{ + scheduleUiOperation( [this]() { + + auto dialog = qobject_cast( QApplication::activeWindow() ); + dialog->setDirectory( QDir::current() ); + dialog->findChildren().first()->setText( QStringLiteral("\"%1.pdf\" \"%2.pdf\"").arg( tr("Handout"), tr("Texts to read") ) ); + dialog->setResult( QDialog::Accepted ); + dialog->setVisible( false ); + + scheduleUiOperation( [this]() { + auto dialog = qobject_cast( QApplication::activeWindow() ); + dialog->show(); + dialog->setFocus(); + dialog->move( 0, 0 ); + + grabWindow( dialog, QStringLiteral("FileTransferDialogStart.png") ); + + dialog->findChild()->button( QDialogButtonBox::Ok )->click(); + + scheduleUiOperation( [dialog]() { + grabDialog( dialog, {}, QStringLiteral("FileTransferDialogFinished.png") ); + }); + } ); + } ); + + m_master->runFeature( m_master->featureManager().feature( Feature::Uid( "4a70bd5a-fab2-4a4b-a92a-a1e81d2b75ed" ) ) ); +} + + + +void DocumentationFigureCreator::hideComputers() +{ + auto view = m_master->mainWindow()->findChild(); + view->setSearchFilter( QStringLiteral("XXXXXX") ); +} + + + +void DocumentationFigureCreator::scheduleUiOperation( const std::function& operation ) +{ + QTimer::singleShot( DialogDelay, this, operation ); +} + + + +void DocumentationFigureCreator::grabWidget(QWidget* widget, const QPoint& pos, const QSize& size, const QString& fileName) +{ + QPixmap pixmap( size ); + widget->render( &pixmap, QPoint(), QRegion( QRect( pos, size ) ) ); + pixmap.save( fileName ); +} + + + +void DocumentationFigureCreator::grabDialog(QDialog* dialog, const QSize& size, const QString& fileName) +{ + dialog->show(); + dialog->setFocus(); + dialog->move( 0, 0 ); + + if( size.isValid() ) + { + dialog->setFixedSize( size ); + } + + QTimer::singleShot( DialogDelay, dialog, [dialog, fileName]() { + grabWindow( dialog, fileName ); + dialog->close(); + } ); +} + + + +void DocumentationFigureCreator::grabWindow(QWidget* widget, const QString& fileName) +{ + widget->repaint(); + QGuiApplication::sync(); + + auto window = widget->window(); + const auto rect = window->frameGeometry(); + window->windowHandle()->screen()->grabWindow( 0, rect.x(), rect.y(), rect.width(), rect.height() ).save( fileName ); +} + + + +void DocumentationFigureCreator::grabWindow(QWidget* widget, const QPoint& pos, const QSize& size, const QString& fileName) +{ + QGuiApplication::sync(); + + widget->window()->windowHandle()->screen()->grabWindow( widget->winId(), pos.x(), pos.y(), size.width(), size.height() ).save( fileName ); +} + +#include "moc_DocumentationFigureCreator.cpp" + +#endif diff --git a/master/src/DocumentationFigureCreator.h b/master/src/DocumentationFigureCreator.h new file mode 100644 index 0000000..6867a2a --- /dev/null +++ b/master/src/DocumentationFigureCreator.h @@ -0,0 +1,76 @@ +/* + * DocumentationFigureCreator.h - helper for creating documentation figures + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "VeyonMaster.h" + +#ifdef VEYON_DEBUG + +class DocumentationFigureCreator : public QObject +{ + Q_OBJECT +public: + DocumentationFigureCreator(); + + void run(); + +private: + static constexpr int DialogDelay = 250; + + void createFeatureFigures(); + void createContextMenuFigure(); + void createLogonDialogFigure(); + void createLocationDialogFigure(); + void createScreenshotManagementPanelFigure(); + void createPowerDownOptionsFigure(); + void createPowerDownTimeInputDialogFigure(); + void createUserLoginDialogFigure(); + void createTextMessageDialogFigure(); + void createOpenWebsiteDialogFigure(); + void createWebsiteMenuFigure(); + void createRunProgramDialogFigure(); + void createProgramMenuFigure(); + void createRemoteAccessHostDialogFigure(); + void createRemoteAccessWindowFigure(); + void createFileTransferDialogFigure(); + + void hideComputers(); + + void scheduleUiOperation( const std::function& operation ); + + static void grabWidget( QWidget* widget, const QPoint& pos, const QSize& size, const QString& fileName ); + static void grabDialog( QDialog* dialog, const QSize& size, const QString& fileName ); + static void grabWindow( QWidget* widget, const QString& fileName ); + static void grabWindow( QWidget* widget, const QPoint& pos, const QSize& size, const QString& fileName ); + + VeyonMaster* m_master; + QEventLoop m_eventLoop; + +} ; + +#endif diff --git a/master/src/FlexibleListView.cpp b/master/src/FlexibleListView.cpp new file mode 100644 index 0000000..ad588d0 --- /dev/null +++ b/master/src/FlexibleListView.cpp @@ -0,0 +1,254 @@ +/* + * FlexibleListView.cpp - list view with flexible icon positions + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "FlexibleListView.h" +#include "QtCompat.h" + + +FlexibleListView::FlexibleListView( QWidget* parent ) : + QListView( parent ), + m_uidRole( Qt::UserRole ) +{ + connect( this, &QListView::indexesMoved, this, &FlexibleListView::updatePositions ); +} + + + +void FlexibleListView::setUidRole( int role ) +{ + m_uidRole = role; +} + + + +void FlexibleListView::setFlexible( bool enabled ) +{ + if( enabled ) + { + setMovement( QListView::Free ); + } + else + { + setMovement( QListView::Static ); + } + + doItemsLayout(); +} + + + +bool FlexibleListView::flexible() const +{ + return movement() == QListView::Free; +} + + + +void FlexibleListView::alignToGrid() +{ + auto m = model(); + + for( int i = 0, count = m->rowCount(); i < count; ++i ) + { + const auto index = m->index( i, 0 ); + const auto uid = m->data( index, m_uidRole ).toUuid(); + + if( uid.isNull() == false && m_positions.contains( uid ) ) + { + m_positions[uid] = QPointF( qMax( 0, qRound( m_positions[uid].x() ) ), + qMax( 0, qRound( m_positions[uid].y() ) ) ); + setPositionForIndex( toItemPosition( qAsConst(m_positions)[uid] ), index ); + } + } +} + + + +QJsonArray FlexibleListView::savePositions() +{ + QJsonArray data; + + for( auto it = m_positions.constBegin(), end = m_positions.constEnd(); it != end; ++it ) + { + QJsonObject object; + object[QStringLiteral("uid")] = it.key().toString(); + object[QStringLiteral("x")] = it.value().x(); + object[QStringLiteral("y")] = it.value().y(); + data += object; + } + + return data; +} + + + +void FlexibleListView::loadPositions( const QJsonArray& data ) +{ + m_positions.clear(); + + for( const auto& item : data ) + { + const auto object = item.toObject(); + const QUuid uid( object[QStringLiteral("uid")].toString() ); + if( uid.isNull() == false ) + { + m_positions[uid] = QPointF( object[QStringLiteral("x")].toDouble(), object[QStringLiteral("y")].toDouble() ); + } + } +} + + + +void FlexibleListView::doItemsLayout() +{ + QListView::doItemsLayout(); + + if( movement() == QListView::Free ) + { + restorePositions(); + } +} + + + +bool FlexibleListView::viewportEvent( QEvent* event ) +{ + const auto ret = QListView::viewportEvent( event ); + if( event->type() == QEvent::ToolTip ) + { + m_toolTipPos = ret ? static_cast(event)->pos() : QPoint{}; + } + + return ret; +} + + + +void FlexibleListView::dataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles ) +{ + QListView::dataChanged( topLeft, bottomRight, roles ); + + if( m_toolTipPos.isNull() == false && ( roles.isEmpty() || roles.contains(Qt::ToolTipRole) ) ) + { + if( viewport()->mapToGlobal(m_toolTipPos) != QCursor::pos() ) + { + m_toolTipPos = QPoint(); + return; + } + + const auto index = indexAt( m_toolTipPos ); + if( index.row() >= topLeft.row() && index.row() <= bottomRight.row() ) + { + QHelpEvent he( QEvent::ToolTip, m_toolTipPos, viewport()->mapToGlobal(m_toolTipPos) ); + QListView::viewportEvent( &he ); + } + } +} + + + +void FlexibleListView::restorePositions() +{ + auto m = model(); + + if( m == nullptr ) + { + return; + } + + for( int i = 0, count = m->rowCount(); i < count; ++i ) + { + const auto index = m->index( i, 0 ); + const auto uid = m->data( index, m_uidRole ).toUuid(); + + if( uid.isNull() == false && m_positions.contains( uid ) ) + { + setPositionForIndex( toItemPosition( qAsConst(m_positions)[uid] ), index ); + } + } +} + + + +void FlexibleListView::updatePositions() +{ + if( movement() == QListView::Free && model() ) + { + auto m = model(); + + for( int i = 0, count = m->rowCount(); i < count; ++i ) + { + const auto index = m->index( i, 0 ); + const auto uid = m->data( index, m_uidRole ).toUuid(); + + if( uid.isNull() == false ) + { + m_positions[uid] = toGridPoint( rectForIndex( index ).topLeft() ); + } + } + } +} + + + +QSizeF FlexibleListView::effectiveGridSize() const +{ + auto m = model(); + + if( m && m->rowCount() > 0 ) + { + return rectForIndex( m->index( 0, 0 ) ).size() + QSize( spacing(), spacing() ); + } + else if( iconSize().isEmpty() == false ) + { + return iconSize() + QSize( spacing(), spacing() ); + } + + return { spacing() + 1.0, spacing() + 1.0 }; +} + + + +QPointF FlexibleListView::toGridPoint( QPoint pos ) const +{ + const auto gridSize = effectiveGridSize(); + + return { ( pos.x() - spacing() ) / gridSize.width(), + ( pos.y() - spacing() ) / gridSize.height() }; +} + + + +QPoint FlexibleListView::toItemPosition( QPointF gridPoint ) const +{ + const auto gridSize = effectiveGridSize(); + + return { spacing() + qMax( 0, static_cast( gridPoint.x() * gridSize.width() ) ), + spacing() + qMax( 0, static_cast( gridPoint.y() * gridSize.height() ) ) }; +} diff --git a/master/src/FlexibleListView.h b/master/src/FlexibleListView.h new file mode 100644 index 0000000..bdd11c1 --- /dev/null +++ b/master/src/FlexibleListView.h @@ -0,0 +1,66 @@ +/* + * FlexibleListView.h - list view with flexible icon positions + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +class FlexibleListView : public QListView +{ + Q_OBJECT +public: + explicit FlexibleListView( QWidget *parent = nullptr ); + ~FlexibleListView() override = default; + + void setUidRole( int role ); + + void setFlexible( bool enabled ); + bool flexible() const; + + void alignToGrid(); + + QJsonArray savePositions(); + void loadPositions( const QJsonArray& data ); + +public: + void doItemsLayout() override; + +protected: + bool viewportEvent( QEvent* event ) override; + void dataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight, + const QVector& roles ) override; + +private: + void restorePositions(); + void updatePositions(); + + QSizeF effectiveGridSize() const; + QPointF toGridPoint( QPoint pos ) const; + QPoint toItemPosition( QPointF gridPoint ) const; + + int m_uidRole; + QHash m_positions; + + QPoint m_toolTipPos; +}; diff --git a/master/src/KItemModelsIntegration.cpp b/master/src/KItemModelsIntegration.cpp new file mode 100644 index 0000000..4618c25 --- /dev/null +++ b/master/src/KItemModelsIntegration.cpp @@ -0,0 +1,4 @@ +#include "kitemmodels_debug.h" + +Q_LOGGING_CATEGORY(KITEMMODELS_LOG, "kf5.kitemmodels"); + diff --git a/master/src/LocationDialog.cpp b/master/src/LocationDialog.cpp new file mode 100644 index 0000000..73226ed --- /dev/null +++ b/master/src/LocationDialog.cpp @@ -0,0 +1,72 @@ +/* + * LocationDialog.cpp - header file for LocationDialog + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "LocationDialog.h" + +#include "ui_LocationDialog.h" + +LocationDialog::LocationDialog( QAbstractItemModel* locationListModel, QWidget* parent ) : + QDialog( parent ), + ui( new Ui::LocationDialog ), + m_sortFilterProxyModel( this ) +{ + ui->setupUi( this ); + + m_sortFilterProxyModel.setSourceModel( locationListModel ); + m_sortFilterProxyModel.sort( 0 ); + + ui->listView->setModel( &m_sortFilterProxyModel ); + + connect( ui->listView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &LocationDialog::updateSelection ); + + updateSearchFilter(); +} + + + +LocationDialog::~LocationDialog() +{ + delete ui; +} + + + +void LocationDialog::updateSearchFilter() +{ + m_sortFilterProxyModel.setFilterRegExp( QRegExp( ui->filterLineEdit->text() ) ); + m_sortFilterProxyModel.setFilterCaseSensitivity( Qt::CaseInsensitive ); + + ui->listView->selectionModel()->setCurrentIndex( m_sortFilterProxyModel.index( 0, 0 ), + QItemSelectionModel::ClearAndSelect ); +} + + + +void LocationDialog::updateSelection( const QModelIndex& current, const QModelIndex& previous ) +{ + Q_UNUSED(previous) + + m_selectedLocation = m_sortFilterProxyModel.data( current ).toString(); +} diff --git a/master/src/LocationDialog.h b/master/src/LocationDialog.h new file mode 100644 index 0000000..e4d2022 --- /dev/null +++ b/master/src/LocationDialog.h @@ -0,0 +1,55 @@ +/* + * LocationDialog.h - header file for LocationDialog + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +namespace Ui { +class LocationDialog; +} + +class LocationDialog : public QDialog +{ + Q_OBJECT +public: + explicit LocationDialog( QAbstractItemModel* locationListModel, QWidget *parent = nullptr ); + ~LocationDialog() override; + + const QString& selectedLocation() const + { + return m_selectedLocation; + } + +private Q_SLOTS: + void updateSearchFilter(); + void updateSelection( const QModelIndex& current, const QModelIndex& previous ); + +private: + Ui::LocationDialog *ui; + + QSortFilterProxyModel m_sortFilterProxyModel; + QString m_selectedLocation; +}; diff --git a/master/src/LocationDialog.ui b/master/src/LocationDialog.ui new file mode 100644 index 0000000..18a4fd8 --- /dev/null +++ b/master/src/LocationDialog.ui @@ -0,0 +1,138 @@ + + + LocationDialog + + + + 0 + 0 + 640 + 480 + + + + + 640 + 480 + + + + Select location + + + + + + enter search filter... + + + + + + + QAbstractScrollArea::AdjustToContents + + + QListView::Adjust + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + LocationDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + LocationDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + filterLineEdit + textChanged(QString) + LocationDialog + updateSearchFilter() + + + 179 + 31 + + + 393 + 25 + + + + + filterLineEdit + returnPressed() + LocationDialog + accept() + + + 48 + 26 + + + 395 + 57 + + + + + listView + activated(QModelIndex) + LocationDialog + accept() + + + 312 + 134 + + + 396 + 143 + + + + + + updateSearchFilter() + + diff --git a/master/src/MainToolBar.cpp b/master/src/MainToolBar.cpp new file mode 100644 index 0000000..c300e43 --- /dev/null +++ b/master/src/MainToolBar.cpp @@ -0,0 +1,105 @@ +/* + * MainToolBar.cpp - MainToolBar for MainWindow + * + * Copyright (c) 2007-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include + +#include "MainToolBar.h" +#include "MainWindow.h" +#include "VeyonMaster.h" +#include "ToolButton.h" +#include "UserConfig.h" + + +MainToolBar::MainToolBar( QWidget* parent ) : + QToolBar( tr( "Configuration" ), parent ), + m_mainWindow( dynamic_cast( parent ) ) +{ + QPalette pal = palette(); + pal.setBrush( QPalette::Window, QPixmap( QStringLiteral(":/core/toolbar-background.png") ) ); + setPalette( pal ); + + ToolButton::setToolTipsDisabled( m_mainWindow->masterCore().userConfig().noToolTips() ); + ToolButton::setIconOnlyMode( m_mainWindow, m_mainWindow->masterCore().userConfig().toolButtonIconOnlyMode() ); +} + + + +void MainToolBar::contextMenuEvent( QContextMenuEvent* event ) +{ + QMenu menu( this ); + +#if QT_VERSION < 0x050600 +#warning Building legacy compat code for unsupported version of Qt + auto toolTipAction = menu.addAction( tr( "Disable balloon tooltips" ) ); + connect( toolTipAction, &QAction::triggered, this, &MainToolBar::toggleToolTips ); +#else + auto toolTipAction = menu.addAction( tr( "Disable balloon tooltips" ), this, &MainToolBar::toggleToolTips ); +#endif + toolTipAction->setCheckable( true ); + toolTipAction->setChecked( m_mainWindow->masterCore().userConfig().noToolTips() ); + +#if QT_VERSION < 0x050600 +#warning Building legacy compat code for unsupported version of Qt + auto iconModeAction = menu.addAction( tr( "Show icons only" ) ); + connect( iconModeAction, &QAction::triggered, this, &MainToolBar::toggleIconMode ); +#else + auto iconModeAction = menu.addAction( tr( "Show icons only" ), this, &MainToolBar::toggleIconMode ); +#endif + iconModeAction->setCheckable( true ); + iconModeAction->setChecked( m_mainWindow->masterCore().userConfig().toolButtonIconOnlyMode() ); + + menu.exec( event->globalPos() ); +} + + + +void MainToolBar::paintEvent( QPaintEvent* event ) +{ + QPainter p( this ); + p.setPen( QColor( 48, 48, 48 ) ); + p.fillRect( event->rect(), palette().brush( QPalette::Window ) ); + p.drawLine( 0, 0, width(), 0 ); + p.drawLine( 0, height()-1, width(), height()-1 ); +} + + + +void MainToolBar::toggleToolTips() +{ + bool newToolTipState = !m_mainWindow->masterCore().userConfig().noToolTips(); + + ToolButton::setToolTipsDisabled( newToolTipState ); + m_mainWindow->masterCore().userConfig().setNoToolTips( newToolTipState ); +} + + + +void MainToolBar::toggleIconMode() +{ + bool newToolButtonIconMode = !m_mainWindow->masterCore().userConfig().toolButtonIconOnlyMode(); + + ToolButton::setIconOnlyMode( m_mainWindow, newToolButtonIconMode ); + m_mainWindow->masterCore().userConfig().setToolButtonIconOnlyMode( newToolButtonIconMode ); +} diff --git a/master/src/MainToolBar.h b/master/src/MainToolBar.h new file mode 100644 index 0000000..8a69af2 --- /dev/null +++ b/master/src/MainToolBar.h @@ -0,0 +1,47 @@ +/* + * MainToolBar.h - MainToolBar for MainWindow + * + * Copyright (c) 2007-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#pragma once + +#include + +class MainWindow; + +class MainToolBar : public QToolBar +{ + Q_OBJECT +public: + explicit MainToolBar( QWidget* parent ); + ~MainToolBar() override = default; + + +private: + void toggleToolTips(); + void toggleIconMode(); + + void contextMenuEvent( QContextMenuEvent* event ) override; + void paintEvent( QPaintEvent* event ) override; + + MainWindow* m_mainWindow; + +} ; diff --git a/master/src/MainWindow.cpp b/master/src/MainWindow.cpp new file mode 100644 index 0000000..f1a29c2 --- /dev/null +++ b/master/src/MainWindow.cpp @@ -0,0 +1,533 @@ +/* + * MainWindow.cpp - implementation of MainWindow class + * + * Copyright (c) 2004-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "AboutDialog.h" +#include "AccessControlProvider.h" +#include "MainWindow.h" +#include "BuiltinFeatures.h" +#include "AuthenticationCredentials.h" +#include "ComputerControlListModel.h" +#include "ComputerManager.h" +#include "ComputerSelectPanel.h" +#include "ScreenshotManagementPanel.h" +#include "FeatureManager.h" +#include "MonitoringMode.h" +#include "NetworkObjectDirectory.h" +#include "NetworkObjectDirectoryManager.h" +#include "SlideshowPanel.h" +#include "SpotlightPanel.h" +#include "ToolButton.h" +#include "VeyonConfiguration.h" +#include "VeyonMaster.h" +#include "UserConfig.h" + +#include "ui_MainWindow.h" + + +MainWindow::MainWindow( VeyonMaster &masterCore, QWidget* parent ) : + QMainWindow( parent ), + ui( new Ui::MainWindow ), + m_master( masterCore ), + m_modeGroup( new QButtonGroup( this ) ) +{ + ui->setupUi( this ); + + setWindowTitle( QStringLiteral( "%1 Master" ).arg( VeyonCore::applicationName() ) ); + + restoreState( QByteArray::fromBase64( m_master.userConfig().windowState().toUtf8() ) ); + restoreGeometry( QByteArray::fromBase64( m_master.userConfig().windowGeometry().toUtf8() ) ); + + // add widgets to status bar + ui->statusBar->addWidget( ui->panelButtons ); + ui->statusBar->addWidget( ui->spacerLabel1 ); + ui->statusBar->addWidget( ui->filterLineEdit, 2 ); + ui->statusBar->addWidget( ui->filterPoweredOnComputersButton ); + ui->statusBar->addWidget( ui->spacerLabel2, 1 ); + ui->statusBar->addWidget( ui->gridSizeSlider, 2 ); + ui->statusBar->addWidget( ui->autoAdjustComputerIconSizeButton ); + ui->statusBar->addWidget( ui->spacerLabel3 ); + ui->statusBar->addWidget( ui->useCustomComputerArrangementButton ); + ui->statusBar->addWidget( ui->alignComputersButton ); + ui->statusBar->addWidget( ui->spacerLabel4 ); + ui->statusBar->addWidget( ui->aboutButton ); + + // create all views + auto mainSplitter = new QSplitter( Qt::Horizontal, ui->centralWidget ); + mainSplitter->setChildrenCollapsible( false ); + mainSplitter->setObjectName( QStringLiteral("MainSplitter") ); + + auto monitoringSplitter = new QSplitter( Qt::Vertical, mainSplitter ); + monitoringSplitter->setChildrenCollapsible( false ); + monitoringSplitter->setObjectName( QStringLiteral("MonitoringSplitter") ); + + auto slideshowSpotlightSplitter = new QSplitter( Qt::Horizontal, monitoringSplitter ); + slideshowSpotlightSplitter->setChildrenCollapsible( false ); + slideshowSpotlightSplitter->setObjectName( QStringLiteral("SlideshowSpotlightSplitter") ); + + auto computerSelectPanel = new ComputerSelectPanel( m_master.computerManager() ); + auto screenshotManagementPanel = new ScreenshotManagementPanel(); + auto slideshowPanel = new SlideshowPanel( m_master.userConfig(), ui->computerMonitoringWidget ); + auto spotlightPanel = new SpotlightPanel( m_master.userConfig(), ui->computerMonitoringWidget ); + + slideshowSpotlightSplitter->addWidget( slideshowPanel ); + slideshowSpotlightSplitter->addWidget( spotlightPanel ); + slideshowSpotlightSplitter->setStretchFactor( slideshowSpotlightSplitter->indexOf(slideshowPanel), 1 ); + slideshowSpotlightSplitter->setStretchFactor( slideshowSpotlightSplitter->indexOf(spotlightPanel), 1 ); + + monitoringSplitter->addWidget( slideshowSpotlightSplitter ); + monitoringSplitter->addWidget( ui->computerMonitoringWidget ); + monitoringSplitter->setStretchFactor( monitoringSplitter->indexOf(slideshowSpotlightSplitter), 1 ); + monitoringSplitter->setStretchFactor( monitoringSplitter->indexOf(ui->computerMonitoringWidget), 1 ); + + mainSplitter->addWidget( computerSelectPanel ); + mainSplitter->addWidget( screenshotManagementPanel ); + mainSplitter->addWidget( monitoringSplitter ); + + mainSplitter->setStretchFactor( mainSplitter->indexOf(monitoringSplitter), 1 ); + + + static const QMap panelButtons{ + { computerSelectPanel, ui->computerSelectPanelButton }, + { screenshotManagementPanel, ui->screenshotManagementPanelButton }, + { slideshowPanel, ui->slideshowPanelButton }, + { spotlightPanel, ui->spotlightPanelButton } + }; + + for( auto it = panelButtons.constBegin(), end = panelButtons.constEnd(); it != end; ++it ) + { + it.key()->hide(); + it.key()->installEventFilter( this ); + connect( *it, &QAbstractButton::toggled, it.key(), &QWidget::setVisible ); + } + + for( auto* splitter : { slideshowSpotlightSplitter, monitoringSplitter, mainSplitter } ) + { + splitter->setHandleWidth( 7 ); + splitter->setStyleSheet( QStringLiteral("QSplitter::handle:hover{background-color:#66a0b3;}") ); + + splitter->installEventFilter( this ); + + QList splitterSizes; + int index = 0; + + for( const auto& sizeObject : m_master.userConfig().splitterStates()[splitter->objectName()].toArray() ) + { + auto size = sizeObject.toInt(); + const auto widget = splitter->widget( index ); + const auto button = panelButtons.value( widget ); + if( widget && button ) + { + widget->setVisible( size > 0 ); + button->setChecked( size > 0 ); + } + + size = qAbs( size ); + if( splitter->orientation() == Qt::Horizontal ) + { + widget->resize( size, widget->height() ); + } + else + { + widget->resize( widget->width(), size ); + } + + widget->setProperty( originalSizePropertyName(), widget->size() ); + splitterSizes.append( size ); + ++index; + } + splitter->setSizes( splitterSizes ); + } + + const auto SplitterContentBaseSize = 500; + + if( spotlightPanel->property( originalSizePropertyName() ).isNull() || + slideshowPanel->property( originalSizePropertyName() ).isNull() ) + { + slideshowSpotlightSplitter->setSizes( { SplitterContentBaseSize, SplitterContentBaseSize } ); + } + + if( slideshowSpotlightSplitter->property( originalSizePropertyName() ).isNull() || + ui->computerMonitoringWidget->property( originalSizePropertyName() ).isNull() ) + { + monitoringSplitter->setSizes( { SplitterContentBaseSize, SplitterContentBaseSize } ); + } + + ui->centralLayout->addWidget( mainSplitter ); + + if( VeyonCore::config().autoOpenComputerSelectPanel() ) + { + ui->computerSelectPanelButton->setChecked( true ); + } + + // initialize search filter + ui->filterPoweredOnComputersButton->setChecked( m_master.userConfig().filterPoweredOnComputers() ); + connect( ui->filterLineEdit, &QLineEdit::textChanged, + this, [this]( const QString& filter ) { ui->computerMonitoringWidget->setSearchFilter( filter ); } ); + connect( ui->filterPoweredOnComputersButton, &QToolButton::toggled, + this, [this]( bool enabled ) { ui->computerMonitoringWidget->setFilterPoweredOnComputers( enabled ); } ); + + // initialize monitoring screen size slider + ui->gridSizeSlider->setMinimum( ComputerMonitoringWidget::MinimumComputerScreenSize ); + ui->gridSizeSlider->setMaximum( ComputerMonitoringWidget::MaximumComputerScreenSize ); + + ui->autoAdjustComputerIconSizeButton->setChecked( ui->computerMonitoringWidget->autoAdjustIconSize() ); + + connect( ui->gridSizeSlider, &QSlider::valueChanged, + this, [this]( int size ) { ui->computerMonitoringWidget->setComputerScreenSize( size ); } ); + connect( ui->computerMonitoringWidget, &ComputerMonitoringWidget::computerScreenSizeAdjusted, + ui->gridSizeSlider, &QSlider::setValue ); + connect( ui->autoAdjustComputerIconSizeButton, &QToolButton::toggled, + this, [this]( bool enabled ) { + ui->computerMonitoringWidget->setAutoAdjustIconSize( enabled ); + m_master.userConfig().setAutoAdjustMonitoringIconSize( enabled ); + } ); + + int size = ComputerMonitoringWidget::DefaultComputerScreenSize; + if( m_master.userConfig().monitoringScreenSize() >= ComputerMonitoringWidget::MinimumComputerScreenSize ) + { + size = m_master.userConfig().monitoringScreenSize(); + } + + ui->gridSizeSlider->setValue( size ); + ui->computerMonitoringWidget->setComputerScreenSize( size ); + + // initialize computer placement controls + ui->useCustomComputerArrangementButton->setChecked( m_master.userConfig().useCustomComputerPositions() ); + connect( ui->useCustomComputerArrangementButton, &QToolButton::toggled, + ui->computerMonitoringWidget, &ComputerMonitoringWidget::setUseCustomComputerPositions ); + connect( ui->alignComputersButton, &QToolButton::clicked, + ui->computerMonitoringWidget, &ComputerMonitoringWidget::alignComputers ); + + + // create the main toolbar + ui->toolBar->layout()->setSpacing( 2 ); + ui->toolBar->toggleViewAction()->setEnabled( false ); + + addToolBar( Qt::TopToolBarArea, ui->toolBar ); + + addFeaturesToToolBar(); + reloadSubFeatures(); + + m_modeGroup->button( static_cast( qHash( VeyonCore::builtinFeatures().monitoringMode().feature().uid() ) ) )->setChecked( true ); + + VeyonCore::enforceBranding( this ); +} + + + +MainWindow::~MainWindow() +{ + ui->computerMonitoringWidget->saveConfiguration(); + + delete ui; +} + + + +bool MainWindow::initAuthentication() +{ + if( VeyonCore::instance()->initAuthentication() ) + { + return true; + } + + if( VeyonCore::config().authenticationMethod() == VeyonCore::AuthenticationMethod::KeyFileAuthentication ) + { + QMessageBox::information( nullptr, + tr( "Authentication impossible" ), + tr( "No authentication key files were found or your current ones " + "are outdated. Please create new key files using the %1 " + "Configurator. Alternatively set up logon authentication " + "using the %1 Configurator. Otherwise you won't be " + "able to access computers using %1." ).arg( VeyonCore::applicationName() ) ); + + } + + return false; +} + + + +bool MainWindow::initAccessControl() +{ + if( VeyonCore::config().accessControlForMasterEnabled() && + VeyonCore::authenticationCredentials().hasCredentials( AuthenticationCredentials::Type::UserLogon ) ) + { + const auto accessControlResult = + AccessControlProvider().checkAccess( VeyonCore::authenticationCredentials().logonUsername(), + QHostAddress( QHostAddress::LocalHost ).toString(), + QStringList() ); + if( accessControlResult == AccessControlProvider::Access::Deny ) + { + vWarning() << "user" << VeyonCore::authenticationCredentials().logonUsername() + << "is not allowed to access computers"; + QMessageBox::critical( nullptr, tr( "Access denied" ), + tr( "According to the local configuration you're not allowed " + "to access computers in the network. Please log in with a different " + "account or let your system administrator check the local configuration." ) ); + return false; + } + } + + return true; +} + + + +void MainWindow::reloadSubFeatures() +{ + for( const auto& feature : m_master.features() ) + { + auto button = ui->toolBar->findChild( feature.name() ); + if( button ) + { + addSubFeaturesToToolButton( button, feature ); + } + } +} + + + +ComputerControlInterfaceList MainWindow::selectedComputerControlInterfaces() const +{ + return ui->computerMonitoringWidget->selectedComputerControlInterfaces(); +} + + + +void MainWindow::closeEvent( QCloseEvent* event ) +{ + if( m_master.currentMode() != VeyonCore::builtinFeatures().monitoringMode().feature().uid() ) + { + const Feature& activeFeature = m_master.featureManager().feature( m_master.currentMode() ); + + QMessageBox::information( this, tr( "Feature active" ), + tr( "The feature \"%1\" is still active. Please stop it before closing %2." ). + arg( activeFeature.displayName(), VeyonCore::applicationName() ) ); + event->ignore(); + return; + } + + QJsonObject splitterStates; + for( const auto* splitter : findChildren() ) + { + QJsonArray splitterSizes; + int i = 0; + int hiddenSize = 0; + for( auto size : splitter->sizes() ) + { + auto widget = splitter->widget(i); + const auto originalSize = widget->property( originalSizePropertyName() ).toSize(); + if( widget->size().isEmpty() && originalSize.isEmpty() == false ) + { + size = splitter->orientation() == Qt::Horizontal ? -originalSize.width() : -originalSize.height(); + hiddenSize += qAbs(size); + } + else + { + size -= hiddenSize; + } + + splitterSizes.append( size ); + ++i; + } + splitterStates[splitter->objectName()] = splitterSizes; + } + + m_master.userConfig().setSplitterStates( splitterStates ); + + m_master.userConfig().setWindowState( QString::fromLatin1( saveState().toBase64() ) ); + m_master.userConfig().setWindowGeometry( QString::fromLatin1( saveGeometry().toBase64() ) ); + + QMainWindow::closeEvent( event ); +} + + + +bool MainWindow::eventFilter( QObject* object, QEvent* event ) +{ + if( event->type() == QEvent::Resize ) + { + const auto widget = qobject_cast( object ); + const auto resizeEvent = static_cast( event ); + + if( resizeEvent->oldSize().isEmpty() == false ) + { + widget->setProperty( originalSizePropertyName(), resizeEvent->oldSize() ); + } + } + + return QMainWindow::eventFilter( object, event ); +} + + + +void MainWindow::keyPressEvent( QKeyEvent* event ) +{ + switch( event->key() ) + { + case Qt::Key_F5: + VeyonCore::networkObjectDirectoryManager().configuredDirectory()->update(); + m_master.computerControlListModel().reload(); + event->accept(); + break; + case Qt::Key_F11: + QWidget::setWindowState( QWidget::windowState() ^ Qt::WindowFullScreen ); + event->accept(); + break; + default: + QMainWindow::keyPressEvent( event ); + break; + } +} + + + +void MainWindow::showAboutDialog() +{ + AboutDialog( this ).exec(); +} + + + +void MainWindow::addFeaturesToToolBar() +{ + for( const auto& feature : m_master.features() ) + { + if( feature.testFlag( Feature::Internal ) ) + { + continue; + } + + ToolButton* btn = new ToolButton( QIcon( feature.iconUrl() ), + feature.displayName(), + feature.displayNameActive(), + feature.description(), + feature.shortcut() ); + connect( btn, &QToolButton::clicked, this, [=] () { + m_master.runFeature( feature ); + updateModeButtonGroup(); + if( feature.testFlag( Feature::Mode ) ) + { + reloadSubFeatures(); + } + } ); + btn->setObjectName( feature.name() ); + btn->addTo( ui->toolBar ); + + if( feature.testFlag( Feature::Mode ) ) + { + btn->setCheckable( true ); + m_modeGroup->addButton( btn, buttonId( feature ) ); + } + } +} + + + +void MainWindow::addSubFeaturesToToolButton( QToolButton* button, const Feature& parentFeature ) +{ + if( button->menu() ) + { + button->menu()->close(); + button->menu()->deleteLater(); + button->setMenu( nullptr ); + } + + const auto parentFeatureIsMode = parentFeature.testFlag( Feature::Mode ); + const auto subFeatures = m_master.subFeatures( parentFeature.uid() ); + + if( subFeatures.isEmpty() || + ( parentFeatureIsMode && button->isChecked() ) ) + { + return; + } + + auto menu = new QMenu( button ); + menu->setObjectName( parentFeature.name() ); + menu->setToolTipsVisible( true ); + + for( const auto& subFeature : subFeatures ) + { +#if QT_VERSION < 0x050600 +#warning Building legacy compat code for unsupported version of Qt + auto action = menu->addAction( QIcon( subFeature.iconUrl() ), subFeature.displayName() ); + action->setShortcut( subFeature.shortcut() ); + connect( action, &QAction::triggered, this, + [=]() { + m_master.runFeature( subFeature ); + if( parentFeatureIsMode ) + { + button->setChecked( true ); + reloadSubFeatures(); + } + } + ); +#else + auto action = menu->addAction( QIcon( subFeature.iconUrl() ), subFeature.displayName(), this, + [=]() { + m_master.runFeature( subFeature ); + if( parentFeatureIsMode ) + { + if( subFeature.testFlag( Feature::Option ) == false ) + { + button->setChecked( true ); + } + reloadSubFeatures(); + } + }, + subFeature.shortcut() ); +#endif + action->setToolTip( subFeature.description() ); + action->setObjectName( subFeature.uid().toString() ); + + if( subFeature.testFlag( Feature::Option ) ) + { + action->setCheckable( true ); + action->setChecked( subFeature.testFlag( Feature::Checked ) ); + } + } + + button->setMenu( menu ); + button->setPopupMode( ToolButton::InstantPopup ); +} + + + +void MainWindow::updateModeButtonGroup() +{ + const auto& monitoringMode = VeyonCore::builtinFeatures().monitoringMode().feature(); + + if( m_master.currentMode() == monitoringMode.uid() ) + { + m_modeGroup->button( buttonId( monitoringMode ) )->setChecked( true ); + } +} diff --git a/master/src/MainWindow.h b/master/src/MainWindow.h new file mode 100644 index 0000000..1466a74 --- /dev/null +++ b/master/src/MainWindow.h @@ -0,0 +1,91 @@ +/* + * MainWindow.h - main window of Veyon Master Application + * + * Copyright (c) 2004-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "ComputerControlInterface.h" + +class QButtonGroup; +class QToolButton; + +class VeyonMaster; + +namespace Ui { +class MainWindow; +} + + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MainWindow( VeyonMaster& masterCore, QWidget* parent = nullptr ); + ~MainWindow() override; + + static bool initAuthentication(); + static bool initAccessControl(); + + VeyonMaster& masterCore() + { + return m_master; + } + + void reloadSubFeatures(); + + ComputerControlInterfaceList selectedComputerControlInterfaces() const; + +protected: + void closeEvent( QCloseEvent* event ) override; + bool eventFilter( QObject* object, QEvent* event ) override; + void keyPressEvent( QKeyEvent *e ) override; + + +private Q_SLOTS: + void showAboutDialog(); + +private: + static int buttonId( const Feature& feature ) + { + return static_cast( qHash( feature.uid() ) ); + } + + static constexpr const char* originalSizePropertyName() + { + return "originalSize"; + } + + void addFeaturesToToolBar(); + void addSubFeaturesToToolButton( QToolButton* button, const Feature& parentFeature ); + + void updateModeButtonGroup(); + + Ui::MainWindow* ui; + + VeyonMaster& m_master; + + QButtonGroup* m_modeGroup; + +} ; diff --git a/master/src/MainWindow.ui b/master/src/MainWindow.ui new file mode 100644 index 0000000..d444fb8 --- /dev/null +++ b/master/src/MainWindow.ui @@ -0,0 +1,465 @@ + + + MainWindow + + + + 0 + 0 + 1377 + 692 + + + + MainWindow + + + + :/core/icon64.png:/core/icon64.png + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + 890 + 570 + 69 + 37 + + + + Adjust size of computer icons automatically + + + Auto + + + + :/master/zoom-fit-best.png:/master/zoom-fit-best.png + + + + 32 + 32 + + + + true + + + + + + 760 + 580 + 111 + 25 + + + + 50 + + + 1000 + + + 10 + + + 50 + + + 150 + + + Qt::Horizontal + + + + + + 430 + 570 + 31 + 30 + + + + + + + + + + 1260 + 570 + 71 + 37 + + + + About Veyon + + + About + + + + :/core/help-about.png:/core/help-about.png + + + + 32 + 32 + + + + + + + 940 + 570 + 99 + 30 + + + + + + + 560 + 570 + 113 + 38 + + + + Search users and computers + + + + + + 620 + 560 + 31 + 30 + + + + + + + + + false + + + + 1110 + 570 + 69 + 37 + + + + Align computers to grid + + + + :/master/align-grid.png:/master/align-grid.png + + + + 32 + 32 + + + + + + + 1010 + 570 + 69 + 37 + + + + Use custom computer arrangement + + + + :/master/exchange-positions-zorder.png:/master/exchange-positions-zorder.png + + + + 32 + 32 + + + + true + + + + + + 1170 + 570 + 99 + 30 + + + + + + + 680 + 570 + 69 + 37 + + + + Only show powered on computers + + + + :/master/powered-on.png:/master/powered-on.png + + + + 32 + 32 + + + + true + + + + + + 0 + 0 + 702 + 39 + + + + + 0 + + + QLayout::SetMinimumSize + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Locations && computers + + + + :/master/computers.png:/master/computers.png + + + + 32 + 32 + + + + true + + + Qt::ToolButtonTextBesideIcon + + + buttonGroup + + + + + + + Screenshots + + + + :/master/camera-photo.png:/master/camera-photo.png + + + + 32 + 32 + + + + true + + + Qt::ToolButtonTextBesideIcon + + + buttonGroup + + + + + + + Slideshow + + + + :/master/computer-slideshow.png:/master/computer-slideshow.png + + + + 32 + 32 + + + + true + + + Qt::ToolButtonTextBesideIcon + + + + + + + Spotlight + + + + :/master/spotlight.png:/master/spotlight.png + + + + 32 + 32 + + + + true + + + Qt::ToolButtonTextBesideIcon + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + ComputerMonitoringWidget + QListView +
ComputerMonitoringWidget.h
+ 1 +
+ + MainToolBar + QToolBar +
MainToolBar.h
+
+
+ + + + + + + aboutButton + clicked() + MainWindow + showAboutDialog() + + + 968 + 625 + + + 494 + 346 + + + + + useCustomComputerArrangementButton + toggled(bool) + alignComputersButton + setEnabled(bool) + + + 994 + 615 + + + 1134 + 615 + + + + + + showAboutDialog() + + + + + false + + + +
diff --git a/master/src/NetworkObjectFilterProxyModel.cpp b/master/src/NetworkObjectFilterProxyModel.cpp new file mode 100644 index 0000000..d146e00 --- /dev/null +++ b/master/src/NetworkObjectFilterProxyModel.cpp @@ -0,0 +1,90 @@ +/* + * NetworkObjectFilterProxyModel.cpp - implementation of NetworkObjectFilterProxyModel + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "NetworkObjectModel.h" +#include "NetworkObjectFilterProxyModel.h" + +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) +#include +#endif + + +NetworkObjectFilterProxyModel::NetworkObjectFilterProxyModel( QObject* parent ) : + QSortFilterProxyModel( parent ), + m_groupList(), + m_computerExcludeList(), + m_excludeEmptyGroups( false ) +{ +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + new QAbstractItemModelTester( this, QAbstractItemModelTester::FailureReportingMode::Warning, this ); +#endif +} + + + +void NetworkObjectFilterProxyModel::setGroupFilter( const QStringList& groupList ) +{ + beginResetModel(); + m_groupList = groupList; + endResetModel(); +} + + + +void NetworkObjectFilterProxyModel::setComputerExcludeFilter( const QStringList& computerExcludeList ) +{ + beginResetModel(); + m_computerExcludeList = computerExcludeList; + endResetModel(); +} + + + +bool NetworkObjectFilterProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const +{ + if( sourceParent.isValid() ) + { + if( m_computerExcludeList.isEmpty() ) + { + return true; + } + + const auto hostAddress = sourceModel()->data( sourceModel()->index( sourceRow, 0, sourceParent ), + NetworkObjectModel::HostAddressRole ).toString(); + + return m_computerExcludeList.contains( hostAddress, Qt::CaseInsensitive ) == false; + } + + if( m_excludeEmptyGroups && sourceModel()->rowCount( sourceModel()->index( sourceRow, 0 ) ) == 0 ) + { + return false; + } + + if( m_groupList.isEmpty() ) + { + return true; + } + + return m_groupList.contains( sourceModel()->data( sourceModel()->index( sourceRow, 0 ) ).toString() ); +} diff --git a/master/src/NetworkObjectFilterProxyModel.h b/master/src/NetworkObjectFilterProxyModel.h new file mode 100644 index 0000000..b7748c9 --- /dev/null +++ b/master/src/NetworkObjectFilterProxyModel.h @@ -0,0 +1,50 @@ +/* + * NetworkObjectFilterProxyModel.h - header file for NetworkObjectFilterProxyModel + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +class NetworkObjectFilterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + explicit NetworkObjectFilterProxyModel( QObject* parent ); + + void setGroupFilter( const QStringList& groupList ); + void setComputerExcludeFilter( const QStringList& computerExcludeList ); + void setEmptyGroupsExcluded( bool enabled ) + { + m_excludeEmptyGroups = enabled; + } + +protected: + bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const override; + +private: + QStringList m_groupList; + QStringList m_computerExcludeList; + bool m_excludeEmptyGroups; + +}; diff --git a/master/src/NetworkObjectOverlayDataModel.cpp b/master/src/NetworkObjectOverlayDataModel.cpp new file mode 100644 index 0000000..1b41b0c --- /dev/null +++ b/master/src/NetworkObjectOverlayDataModel.cpp @@ -0,0 +1,83 @@ +/* + * NetworkObjectOverlayDataModel.cpp - overlay model for NetworkObjectModel to provide extra data + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "NetworkObjectOverlayDataModel.h" +#include "NetworkObjectModel.h" + +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) +#include +#include +#endif + + +NetworkObjectOverlayDataModel::NetworkObjectOverlayDataModel( const QString& overlayDataHeader, + QObject *parent ) : + KExtraColumnsProxyModel( parent ), + m_overlayDataRole( Qt::DisplayRole ) +{ +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + // base class relies on source model being set when tested by QAbstractItemModelTester + setSourceModel( new QStandardItemModel( this ) ); + new QAbstractItemModelTester( this, QAbstractItemModelTester::FailureReportingMode::Warning, this ); +#endif + appendColumn( overlayDataHeader ); +} + + + +QVariant NetworkObjectOverlayDataModel::extraColumnData(const QModelIndex &parent, int row, int extraColumn, int role) const +{ + if( extraColumn != 0 || role != m_overlayDataRole ) + { + return QVariant(); + } + + NetworkObject::Uid networkObjectUid = data( index( row, 0, parent ), NetworkObjectModel::UidRole ).toUuid(); + + if( networkObjectUid.isNull() == false && m_overlayData.contains( networkObjectUid ) ) + { + return m_overlayData[networkObjectUid]; + } + + return QVariant(); +} + + +bool NetworkObjectOverlayDataModel::setExtraColumnData( const QModelIndex &parent, int row, int extraColumn, const QVariant &data, int role) +{ + if( extraColumn != 0 || role != m_overlayDataRole ) + { + return false; + } + + const auto networkObjectUid = KExtraColumnsProxyModel::data( index( row, 0, parent ), NetworkObjectModel::UidRole ).toUuid(); + + if( m_overlayData[networkObjectUid] != data ) + { + m_overlayData[networkObjectUid] = data; + extraColumnDataChanged( parent, row, extraColumn, { m_overlayDataRole } ); + } + + return true; +} diff --git a/master/src/NetworkObjectOverlayDataModel.h b/master/src/NetworkObjectOverlayDataModel.h new file mode 100644 index 0000000..713647d --- /dev/null +++ b/master/src/NetworkObjectOverlayDataModel.h @@ -0,0 +1,46 @@ +/* + * NetworkObjectOverlayDataModel.h - overlay model for NetworkObjectModel to provide extra data + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "kextracolumnsproxymodel.h" +#include "NetworkObject.h" + +class NetworkObjectOverlayDataModel : public KExtraColumnsProxyModel +{ + Q_OBJECT +public: + explicit NetworkObjectOverlayDataModel( const QString& overlayDataHeader, + QObject *parent = nullptr ); + + QVariant extraColumnData( const QModelIndex &parent, int row, int extraColumn, int role ) const override; + + bool setExtraColumnData( const QModelIndex &parent, int row, int extraColumn, const QVariant &data, int role ) override; + + +private: + int m_overlayDataRole; + QHash m_overlayData; + +}; diff --git a/master/src/NetworkObjectTreeModel.cpp b/master/src/NetworkObjectTreeModel.cpp new file mode 100644 index 0000000..6099ce3 --- /dev/null +++ b/master/src/NetworkObjectTreeModel.cpp @@ -0,0 +1,263 @@ +/* + * NetworkObjectTreeModel.cpp - data model returning hierarchically grouped network objects + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "NetworkObjectDirectory.h" +#include "NetworkObjectTreeModel.h" + +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) +#include +#endif + + +NetworkObjectTreeModel::NetworkObjectTreeModel( NetworkObjectDirectory* directory, QObject* parent ) : + NetworkObjectModel( parent ), + m_directory( directory ) +{ +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + new QAbstractItemModelTester( this, QAbstractItemModelTester::FailureReportingMode::Warning, this ); +#endif + + connect( m_directory, &NetworkObjectDirectory::objectsAboutToBeInserted, + this, &NetworkObjectTreeModel::beginInsertObjects ); + connect( m_directory, &NetworkObjectDirectory::objectsInserted, + this, &NetworkObjectTreeModel::endInsertObjects ); + + connect( m_directory, &NetworkObjectDirectory::objectsAboutToBeRemoved, + this, &NetworkObjectTreeModel::beginRemoveObjects ); + connect( m_directory, &NetworkObjectDirectory::objectsRemoved, + this, &NetworkObjectTreeModel::endRemoveObjects ); + + connect( m_directory, &NetworkObjectDirectory::objectChanged, + this, &NetworkObjectTreeModel::updateObject ); +} + + + +QModelIndex NetworkObjectTreeModel::index( int row, int column, const QModelIndex& parent ) const +{ + if( row < 0 || column < 0 ) + { + return {}; + } + + return createIndex( row, column, m_directory->childId( parent.internalId(), row ) ); +} + + + +QModelIndex NetworkObjectTreeModel::parent( const QModelIndex& index ) const +{ + if( index.isValid() == false || index.internalId() == m_directory->rootId() ) + { + return {}; + } + + auto parentId = m_directory->parentId( index.internalId() ); + if( parentId ) + { + return objectIndex( parentId, index.column() ); + } + + return {}; +} + + + +int NetworkObjectTreeModel::rowCount( const QModelIndex& parent ) const +{ + if( parent.isValid() == false ) + { + return m_directory->childCount( m_directory->rootId() ); + } + + return m_directory->childCount( parent.internalId() ); +} + + + +int NetworkObjectTreeModel::columnCount( const QModelIndex& parent ) const +{ + Q_UNUSED(parent) + + return 1; +} + + + +QVariant NetworkObjectTreeModel::headerData( int section, Qt::Orientation orientation, int role ) const +{ + if( section == 0 && orientation == Qt::Horizontal && role == Qt::DisplayRole ) + { + return tr( "Locations/Computers" ); + } + + return QVariant(); +} + + + +QVariant NetworkObjectTreeModel::data( const QModelIndex& index, int role ) const +{ + if( index.isValid() == false || index.internalId() == m_directory->rootId() ) + { + return {}; + } + + const auto& networkObject = object( index ); + + if( networkObject.isValid() == false ) + { + return {}; + } + + switch( role ) + { + case UidRole: return networkObject.uid(); + case NameRole: return networkObject.name(); + case TypeRole: return QVariant::fromValue( networkObject.type() ); + case HostAddressRole: return networkObject.hostAddress(); + case MacAddressRole: return networkObject.macAddress(); + case DirectoryAddressRole: return networkObject.directoryAddress(); + default: break; + } + + return {}; +} + + + +bool NetworkObjectTreeModel::hasChildren( const QModelIndex& parent ) const +{ + if( parent.isValid() == false ) + { + return NetworkObjectModel::hasChildren( parent ); + } + + const auto& networkObject = object( parent ); + + switch( networkObject.type() ) + { + case NetworkObject::Type::None: + case NetworkObject::Type::Host: + case NetworkObject::Type::Label: + return false; + default: + break; + } + + if( m_directory->childCount( networkObject.modelId() ) > 0 ) + { + return true; + } + + if( networkObject.isPopulated() ) + { + return false; + } + + return true; +} + + + +bool NetworkObjectTreeModel::canFetchMore( const QModelIndex& parent ) const +{ + if( parent.isValid() ) + { + return object( parent ).isPopulated() == false; + } + + return false; +} + + + +void NetworkObjectTreeModel::fetchMore( const QModelIndex& parent ) +{ + m_directory->fetchObjects( object( parent ) ); +} + + + +void NetworkObjectTreeModel::beginInsertObjects( const NetworkObject& parent, int index, int count ) +{ + beginInsertRows( objectIndex( parent.modelId() ), index, index+count-1 ); +} + + + +void NetworkObjectTreeModel::endInsertObjects() +{ + endInsertRows(); +} + + + +void NetworkObjectTreeModel::beginRemoveObjects( const NetworkObject& parent, int index, int count ) +{ + beginRemoveRows( objectIndex( parent.modelId() ), index, index+count-1 ); +} + + + +void NetworkObjectTreeModel::endRemoveObjects() +{ + endRemoveRows(); +} + + + +void NetworkObjectTreeModel::updateObject( const NetworkObject& parent, int row ) +{ + const auto index = createIndex( row, 0, parent.modelId() ); + + Q_EMIT dataChanged( index, index ); +} + + + +QModelIndex NetworkObjectTreeModel::objectIndex( NetworkObject::ModelId object, int column ) const +{ + if( object == m_directory->rootId() ) + { + return {}; + } + + const auto parentId = m_directory->parentId( object ); + const auto parentRow = m_directory->index( parentId, object ); + + if( parentRow >= 0 ) + { + return createIndex( parentRow, column, object ); + } + + return {}; +} + + + +const NetworkObject& NetworkObjectTreeModel::object( const QModelIndex& index ) const +{ + return m_directory->object( index.parent().internalId(), index.internalId() ); +} diff --git a/master/src/NetworkObjectTreeModel.h b/master/src/NetworkObjectTreeModel.h new file mode 100644 index 0000000..152505d --- /dev/null +++ b/master/src/NetworkObjectTreeModel.h @@ -0,0 +1,67 @@ +/* + * NetworkObjectTreeModel.h - data model returning hierarchically grouped network objects + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "NetworkObjectModel.h" + +class NetworkObjectDirectory; + +class NetworkObjectTreeModel : public NetworkObjectModel +{ + Q_OBJECT +public: + explicit NetworkObjectTreeModel( NetworkObjectDirectory* directory, QObject *parent = nullptr); + + QModelIndex index( int row, int column, + const QModelIndex& parent = QModelIndex() ) const override; + QModelIndex parent( const QModelIndex& index ) const override; + + int rowCount( const QModelIndex& parent = QModelIndex() ) const override; + int columnCount( const QModelIndex& parent = QModelIndex() ) const override; + + QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override; + + QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const override; + + bool hasChildren( const QModelIndex& parent ) const override; + bool canFetchMore( const QModelIndex& parent ) const override; + void fetchMore( const QModelIndex& parent ) override; + + +private: + void beginInsertObjects( const NetworkObject& parent, int index, int count ); + void endInsertObjects(); + + void beginRemoveObjects( const NetworkObject& parent, int index, int count ); + void endRemoveObjects(); + + void updateObject( const NetworkObject& parent, int index ); + + QModelIndex objectIndex( NetworkObject::ModelId object, int column = 0 ) const; + const NetworkObject& object( const QModelIndex& index ) const; + + NetworkObjectDirectory* m_directory; + +}; diff --git a/master/src/RecursiveFilterProxyModel.cpp b/master/src/RecursiveFilterProxyModel.cpp new file mode 100644 index 0000000..a409fa7 --- /dev/null +++ b/master/src/RecursiveFilterProxyModel.cpp @@ -0,0 +1,37 @@ +/* + * RecursiveFilterProxyModel.cpp - proxy model for recursive filtering + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "RecursiveFilterProxyModel.h" + +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) +#include +#endif + +RecursiveFilterProxyModel::RecursiveFilterProxyModel( QObject* parent ) : + KRecursiveFilterProxyModel( parent ) +{ +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + new QAbstractItemModelTester( this, QAbstractItemModelTester::FailureReportingMode::Warning, this ); +#endif +} diff --git a/master/src/RecursiveFilterProxyModel.h b/master/src/RecursiveFilterProxyModel.h new file mode 100644 index 0000000..be93285 --- /dev/null +++ b/master/src/RecursiveFilterProxyModel.h @@ -0,0 +1,34 @@ +/* + * RecursiveFilterProxyModel.h - proxy model for recursive filtering + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "krecursivefilterproxymodel.h" + +class RecursiveFilterProxyModel : public KRecursiveFilterProxyModel +{ +public: + explicit RecursiveFilterProxyModel( QObject* parent ); + +}; diff --git a/master/src/ScreenshotManagementPanel.cpp b/master/src/ScreenshotManagementPanel.cpp new file mode 100644 index 0000000..4b578d7 --- /dev/null +++ b/master/src/ScreenshotManagementPanel.cpp @@ -0,0 +1,169 @@ +/* + * ScreenshotManagementPanel.cpp - implementation of screenshot management view + * + * Copyright (c) 2004-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include "Filesystem.h" +#include "ScreenshotManagementPanel.h" +#include "VeyonConfiguration.h" +#include "VeyonCore.h" +#include "Screenshot.h" + +#include "ui_ScreenshotManagementPanel.h" + + +ScreenshotManagementPanel::ScreenshotManagementPanel( QWidget *parent ) : + QWidget( parent ), + ui( new Ui::ScreenshotManagementPanel ) +{ + ui->setupUi( this ); + + VeyonCore::filesystem().ensurePathExists( VeyonCore::config().screenshotDirectory() ); + + m_fsWatcher.addPath( VeyonCore::filesystem().screenshotDirectoryPath() ); + connect( &m_fsWatcher, &QFileSystemWatcher::directoryChanged, this, &ScreenshotManagementPanel::updateModel ); + + m_reloadTimer.setInterval( FsModelResetDelay ); + m_reloadTimer.setSingleShot( true ); + + connect( &m_reloadTimer, &QTimer::timeout, this, &ScreenshotManagementPanel::updateModel ); + connect( &VeyonCore::filesystem(), &Filesystem::screenshotDirectoryModified, + &m_reloadTimer, QOverload<>::of(&QTimer::start) ); + + ui->list->setModel( &m_model ); + + connect( ui->list->selectionModel(), &QItemSelectionModel::currentRowChanged, + this, &ScreenshotManagementPanel::updateScreenshot ); + connect( ui->list, &QListView::activated, this, &ScreenshotManagementPanel::showScreenshot ); + + connect( ui->showBtn, &QPushButton::clicked, this, &ScreenshotManagementPanel::showScreenshot ); + connect( ui->deleteBtn, &QPushButton::clicked, this, &ScreenshotManagementPanel::deleteScreenshot ); + + updateModel(); +} + + + +ScreenshotManagementPanel::~ScreenshotManagementPanel() +{ + delete ui; +} + + + +void ScreenshotManagementPanel::setPreview( const Screenshot& screenshot ) +{ + ui->previewLbl->setPixmap( QPixmap::fromImage( screenshot.image() ) ); + + ui->userLbl->setText( screenshot.user() ); + ui->hostLbl->setText( screenshot.host() ); + ui->dateLbl->setText( screenshot.date() ); + ui->timeLbl->setText( screenshot.time() ); +} + + + +void ScreenshotManagementPanel::resizeEvent( QResizeEvent* event ) +{ + int maxWidth = contentsRect().width(); + int maxHeight = maxWidth * 9 / 16; + + ui->previewLbl->setMaximumSize( maxWidth, maxHeight ); + + QWidget::resizeEvent( event ); +} + + + +void ScreenshotManagementPanel::updateModel() +{ + const auto currentFile = m_model.data( ui->list->currentIndex(), Qt::DisplayRole ).toString(); + + const QDir dir{ VeyonCore::filesystem().screenshotDirectoryPath() }; + const auto files = dir.entryList( { QStringLiteral("*.png") }, + QDir::Filter::Files, QDir::SortFlag::Name ); + + m_model.setStringList( files ); + + ui->list->setCurrentIndex( m_model.index( files.indexOf( currentFile ) ) ); +} + + + +QString ScreenshotManagementPanel::filePath( const QModelIndex& index ) const +{ + return VeyonCore::filesystem().screenshotDirectoryPath() + QDir::separator() + m_model.data( index, Qt::DisplayRole ).toString(); +} + + + +void ScreenshotManagementPanel::updateScreenshot( const QModelIndex& index ) +{ + setPreview( Screenshot( filePath( index ) ) ); +} + + + +void ScreenshotManagementPanel::screenshotDoubleClicked( const QModelIndex& index ) +{ + auto screenshotWindow = new QLabel; + screenshotWindow->setPixmap( filePath( index ) ); + screenshotWindow->setScaledContents( true ); + screenshotWindow->setWindowTitle( QFileInfo( filePath( index ) ).fileName() ); + screenshotWindow->setAttribute( Qt::WA_DeleteOnClose, true ); + screenshotWindow->showNormal(); +} + + + +void ScreenshotManagementPanel::showScreenshot() +{ + if( ui->list->currentIndex().isValid() ) + { + screenshotDoubleClicked( ui->list->currentIndex() ); + } +} + + + +void ScreenshotManagementPanel::deleteScreenshot() +{ + const auto selection = ui->list->selectionModel()->selectedIndexes(); + if( selection.size() > 1 && + QMessageBox::question( this, + tr("Screenshot"), + tr("Do you really want to delete all selected screenshots?") ) != QMessageBox::Yes ) + { + return; + } + + for( const auto& index : selection ) + { + QFile::remove( filePath( index ) ); + } + + updateModel(); +} diff --git a/master/src/ScreenshotManagementPanel.h b/master/src/ScreenshotManagementPanel.h new file mode 100644 index 0000000..fee8210 --- /dev/null +++ b/master/src/ScreenshotManagementPanel.h @@ -0,0 +1,71 @@ +/* + * ScreenshotManagementPanel.h - declaration of screenshot management view + * + * Copyright (c) 2004-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include +#include + +class QModelIndex; +class Screenshot; + +namespace Ui { +class ScreenshotManagementPanel; +} + +class ScreenshotManagementPanel : public QWidget +{ + Q_OBJECT +public: + explicit ScreenshotManagementPanel( QWidget *parent = nullptr ); + ~ScreenshotManagementPanel() override; + + void setPreview( const Screenshot& screenshot ); + +protected: + void resizeEvent( QResizeEvent* event ) override; + +private: + void updateModel(); + + QString filePath( const QModelIndex& index ) const; + + void updateScreenshot( const QModelIndex& index ); + void screenshotDoubleClicked( const QModelIndex& index ); + + void showScreenshot(); + void deleteScreenshot(); + + Ui::ScreenshotManagementPanel* ui; + + QStringListModel m_model{this}; + QFileSystemWatcher m_fsWatcher{this}; + + QTimer m_reloadTimer{this}; + + static constexpr auto FsModelResetDelay = 1000; + +} ; diff --git a/master/src/ScreenshotManagementPanel.ui b/master/src/ScreenshotManagementPanel.ui new file mode 100644 index 0000000..2784fbc --- /dev/null +++ b/master/src/ScreenshotManagementPanel.ui @@ -0,0 +1,160 @@ + + + ScreenshotManagementPanel + + + + + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ExtendedSelection + + + + + + + + 1 + 1 + + + + true + + + + + + + + + + + + + 75 + true + + + + User: + + + + + + + + + + + + + + 75 + true + + + + Computer: + + + + + + + + 75 + true + + + + Date: + + + + + + + + + + + 75 + true + + + + Time: + + + + + + + + + + + Show + + + + :/core/edit-find.png:/core/edit-find.png + + + + 22 + 22 + + + + + + + + Delete + + + + :/core/edit-delete.png:/core/edit-delete.png + + + + 22 + 22 + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 16 + 10 + + + + + + + + + + + diff --git a/master/src/SlideshowModel.cpp b/master/src/SlideshowModel.cpp new file mode 100644 index 0000000..063a41e --- /dev/null +++ b/master/src/SlideshowModel.cpp @@ -0,0 +1,202 @@ +/* + * SlideshowModel.cpp - implementation of SlideshowModel + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "ComputerListModel.h" +#include "SlideshowModel.h" + + +SlideshowModel::SlideshowModel( QAbstractItemModel* sourceModel, QObject* parent ) : + QSortFilterProxyModel( parent ) +{ + setSourceModel( sourceModel ); + + connect( sourceModel, &QAbstractItemModel::rowsInserted, this, + [this]( const QModelIndex &parent, int start, int end ) + { + Q_UNUSED(parent) + Q_UNUSED(end) + if( start <= m_currentRow ) + { + showNext(); + } + } ); + connect( sourceModel, &QAbstractItemModel::rowsRemoved, this, + [this]( const QModelIndex &parent, int start, int end ) + { + Q_UNUSED(parent) + Q_UNUSED(end) + if( start <= m_currentRow ) + { + showPrevious(); + } + } ); + + connect( &m_timer, &QTimer::timeout, this, &SlideshowModel::showNext ); +} + + + +void SlideshowModel::setIconSize( QSize size ) +{ + m_iconSize = size; + + Q_EMIT dataChanged( index( 0, 0 ), index( rowCount() - 1, 0 ), { Qt::DisplayRole, Qt::DecorationRole } ); +} + + + +QVariant SlideshowModel::data( const QModelIndex& index, int role ) const +{ + if( m_currentControlInterface.isNull() ) + { + return {}; + } + + const auto sourceIndex = mapToSource( index ); + if( sourceIndex.isValid() == false ) + { + return {}; + } + + if( role == Qt::DecorationRole ) + { + auto screen = sourceModel()->data( sourceIndex, ComputerListModel::ScreenRole ).value(); + if( screen.isNull() ) + { + screen = sourceModel()->data( sourceIndex, Qt::DecorationRole ).value(); + } + + return screen.scaled( m_iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + } + + return QSortFilterProxyModel::data( index, role ); +} + + + +void SlideshowModel::setRunning( bool running, int duration ) +{ + m_timer.stop(); + m_timer.setInterval( duration ); + + if( running ) + { + m_timer.start(); + } +} + + + +void SlideshowModel::showPrevious() +{ + auto valid = false; + + for( int i = 0; i < sourceModel()->rowCount(); ++i ) + { + setCurrentRow( m_currentRow - 1 ); + + if( m_currentControlInterface && + m_currentControlInterface->state() == ComputerControlInterface::State::Connected && + m_currentControlInterface->hasValidFramebuffer() ) + { + valid = true; + break; + } + } + + if( valid == false ) + { + m_currentRow = 0; + m_currentControlInterface.clear(); + } + + if( m_timer.isActive() ) + { + m_timer.stop(); + m_timer.start(); + } +} + + + +void SlideshowModel::showNext() +{ + auto valid = false; + + for( int i = 0; i < sourceModel()->rowCount(); ++i ) + { + setCurrentRow( m_currentRow + 1 ); + + if( m_currentControlInterface && + m_currentControlInterface->state() == ComputerControlInterface::State::Connected && + m_currentControlInterface->hasValidFramebuffer() ) + { + valid = true; + break; + } + } + + if( valid == false ) + { + m_currentRow = 0; + m_currentControlInterface.clear(); + } + + if( m_timer.isActive() ) + { + m_timer.stop(); + m_timer.start(); + } +} + + + +bool SlideshowModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const +{ + return sourceRow < sourceModel()->rowCount() && + m_currentControlInterface == sourceModel()->data( sourceModel()->index( sourceRow, 0, sourceParent ), + ComputerListModel::ControlInterfaceRole ) + .value(); +} + + + +void SlideshowModel::setCurrentRow( int row ) +{ + if( sourceModel()->rowCount() > 0 ) + { + m_currentRow = qMax( 0, row ) % qMax( 1, sourceModel()->rowCount() ); + + m_currentControlInterface = sourceModel()->data( sourceModel()->index( m_currentRow, 0 ), + ComputerListModel::ControlInterfaceRole ) + .value(); + } + else + { + m_currentRow = 0; + m_currentControlInterface.clear(); + } + + invalidateFilter(); +} diff --git a/master/src/SlideshowModel.h b/master/src/SlideshowModel.h new file mode 100644 index 0000000..d37fb29 --- /dev/null +++ b/master/src/SlideshowModel.h @@ -0,0 +1,60 @@ +/* + * SlideshowModel.h - header file for SlideshowModel + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "ComputerControlInterface.h" + +class SlideshowModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + SlideshowModel( QAbstractItemModel* sourceModel, QObject* parent = nullptr ); + + void setIconSize( QSize size ); + + QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override; + +public: + void setRunning( bool running, int duration ); + void showPrevious(); + void showNext(); + +protected: + bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const override; + +private: + void setCurrentRow( int row ); + + QSize m_iconSize; + + QTimer m_timer; + + int m_currentRow{0}; + ComputerControlInterface::Pointer m_currentControlInterface; + +}; diff --git a/master/src/SlideshowPanel.cpp b/master/src/SlideshowPanel.cpp new file mode 100644 index 0000000..d796737 --- /dev/null +++ b/master/src/SlideshowPanel.cpp @@ -0,0 +1,95 @@ +/* + * SlideshowPanel.cpp - implementation of SlideshowPanel + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "ComputerMonitoringModel.h" +#include "ComputerMonitoringWidget.h" +#include "SlideshowModel.h" +#include "SlideshowPanel.h" +#include "UserConfig.h" + +#include "ui_SlideshowPanel.h" + + +SlideshowPanel::SlideshowPanel( UserConfig& config, ComputerMonitoringWidget* computerMonitoringWidget, QWidget *parent ) : + QWidget( parent ), + ui( new Ui::SlideshowPanel ), + m_config( config ), + m_model( new SlideshowModel( computerMonitoringWidget->dataModel(), this ) ) +{ + ui->setupUi( this ); + + ui->monitoringWidget->setAutoAdjustIconSize( false ); + ui->monitoringWidget->setUseCustomComputerPositions( false ); + ui->monitoringWidget->setAcceptDrops( false ); + ui->monitoringWidget->setDragEnabled( false ); + ui->monitoringWidget->setIgnoreWheelEvent( true ); + ui->monitoringWidget->setSelectionMode( QListView::SingleSelection ); + ui->monitoringWidget->setModel( m_model ); + + connect( ui->startStopButton, &QAbstractButton::toggled, this, &SlideshowPanel::updateDuration ); + connect( ui->durationSlider, &QSlider::valueChanged, this, &SlideshowPanel::updateDuration ); + + connect( ui->showPreviousButton, &QAbstractButton::clicked, m_model, &SlideshowModel::showPrevious ); + connect( ui->showNextButton, &QAbstractButton::clicked, m_model, &SlideshowModel::showNext ); + + ui->durationSlider->setValue( m_config.slideshowDuration() ); + + updateDuration(); +} + + + +SlideshowPanel::~SlideshowPanel() +{ + delete ui; +} + + + +void SlideshowPanel::resizeEvent( QResizeEvent* event ) +{ + static constexpr auto ExtraMargin = 10; + + const auto spacing = ui->monitoringWidget->spacing(); + const auto labelHeight = ui->monitoringWidget->fontMetrics().height(); + + ui->monitoringWidget->setIconSize( { ui->monitoringWidget->width() - ExtraMargin - spacing * 2, + ui->monitoringWidget->height() - ExtraMargin - labelHeight - spacing * 2 } ); + + m_model->setIconSize( ui->monitoringWidget->iconSize() ); + + QWidget::resizeEvent( event ); +} + + + +void SlideshowPanel::updateDuration() +{ + const int duration = ui->durationSlider->value(); + + m_model->setRunning( ui->startStopButton->isChecked(), duration ); + m_config.setSlideshowDuration( duration ); + + ui->durationLabel->setText( QStringLiteral("%1 s").arg( duration / 1000 ) ); +} diff --git a/master/src/SlideshowPanel.h b/master/src/SlideshowPanel.h new file mode 100644 index 0000000..66c2bc8 --- /dev/null +++ b/master/src/SlideshowPanel.h @@ -0,0 +1,58 @@ +/* + * SlideshowPanel.h - declaration of SlideshowPanel + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +class ComputerMonitoringWidget; +class SlideshowModel; +class UserConfig; + +namespace Ui { +class SlideshowPanel; +} + +class SlideshowPanel : public QWidget +{ + Q_OBJECT +public: + explicit SlideshowPanel( UserConfig& config, ComputerMonitoringWidget* computerMonitoringWidget, + QWidget* parent = nullptr ); + ~SlideshowPanel() override; + + static constexpr auto DefaultDuration = 3000; + +protected: + void resizeEvent( QResizeEvent* event ) override; + +private: + void updateDuration(); + + Ui::SlideshowPanel* ui; + + UserConfig& m_config; + SlideshowModel* m_model; + +} ; diff --git a/master/src/SlideshowPanel.ui b/master/src/SlideshowPanel.ui new file mode 100644 index 0000000..8b6a8bd --- /dev/null +++ b/master/src/SlideshowPanel.ui @@ -0,0 +1,164 @@ + + + SlideshowPanel + + + + 0 + + + 0 + + + 4 + + + 0 + + + 0 + + + + + + + + + 4 + + + 4 + + + 4 + + + + + Previous + + + Previous + + + + :/core/go-previous.png:/core/go-previous.png + + + + 32 + 32 + + + + + + + + Start/pause + + + + :/master/media-playback-start.png + :/master/media-playback-pause.png:/master/media-playback-start.png + + + + 32 + 32 + + + + true + + + true + + + + + + + Next + + + + :/core/go-next.png:/core/go-next.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Duration: + + + + + + + 1000 + + + 10000 + + + 1000 + + + 1000 + + + 3000 + + + Qt::Horizontal + + + + + + + + + + + + + + + + + + ComputerMonitoringWidget + QListView +
ComputerMonitoringWidget.h
+ 1 +
+
+ + + + + +
diff --git a/master/src/SpotlightModel.cpp b/master/src/SpotlightModel.cpp new file mode 100644 index 0000000..706c324 --- /dev/null +++ b/master/src/SpotlightModel.cpp @@ -0,0 +1,112 @@ +/* + * SpotlightModel.cpp - implementation of SpotlightModel + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "SpotlightModel.h" + + +SpotlightModel::SpotlightModel( QAbstractItemModel* sourceModel, QObject* parent ) : + QSortFilterProxyModel( parent ) +{ + setSourceModel( sourceModel ); +} + + + +void SpotlightModel::setIconSize( QSize size ) +{ + m_iconSize = size; + + Q_EMIT dataChanged( index( 0, 0 ), index( rowCount() - 1, 0 ), { Qt::DisplayRole, Qt::DecorationRole } ); +} + + + +void SpotlightModel::setUpdateInRealtime( bool enabled ) +{ + m_updateInRealtime = enabled; + + for( const auto& controlInterface : m_controlInterfaces ) + { + controlInterface->setUpdateMode( m_updateInRealtime + ? ComputerControlInterface::UpdateMode::Live + : ComputerControlInterface::UpdateMode::Monitoring ); + } +} + + + +void SpotlightModel::add( const ComputerControlInterface::Pointer& controlInterface ) +{ + m_controlInterfaces.append( controlInterface ); + + controlInterface->setUpdateMode( m_updateInRealtime + ? ComputerControlInterface::UpdateMode::Live + : ComputerControlInterface::UpdateMode::Monitoring ); + + invalidateFilter(); +} + + + +void SpotlightModel::remove( const ComputerControlInterface::Pointer& controlInterface ) +{ + m_controlInterfaces.removeAll( controlInterface ); + + controlInterface->setUpdateMode( ComputerControlInterface::UpdateMode::Monitoring ); + + invalidateFilter(); +} + + + +QVariant SpotlightModel::data( const QModelIndex& index, int role ) const +{ + const auto sourceIndex = mapToSource( index ); + if( sourceIndex.isValid() == false ) + { + return {}; + } + + if( role == Qt::DecorationRole ) + { + auto screen = sourceModel()->data( sourceIndex, ComputerListModel::ScreenRole ).value(); + if( screen.isNull() ) + { + screen = sourceModel()->data( sourceIndex, Qt::DecorationRole ).value(); + } + + return screen.scaled( m_iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + } + + return QSortFilterProxyModel::data( index, role ); +} + + + +bool SpotlightModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const +{ + return m_controlInterfaces.contains( sourceModel()->data( sourceModel()->index( sourceRow, 0, sourceParent ), + ComputerListModel::ControlInterfaceRole ) + .value() ); +} diff --git a/master/src/SpotlightModel.h b/master/src/SpotlightModel.h new file mode 100644 index 0000000..c585fdd --- /dev/null +++ b/master/src/SpotlightModel.h @@ -0,0 +1,56 @@ +/* + * SpotlightModel.h - header file for SpotlightModel + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "ComputerControlListModel.h" + +class SpotlightModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + static constexpr auto ControlInterfaceRole = ComputerControlListModel::ControlInterfaceRole; + + SpotlightModel( QAbstractItemModel* sourceModel, QObject* parent = nullptr ); + + void setIconSize( QSize size ); + void setUpdateInRealtime( bool enabled ); + + void add( const ComputerControlInterface::Pointer& controlInterface ); + void remove( const ComputerControlInterface::Pointer& controlInterface ); + + QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override; + +protected: + bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const override; + +private: + QSize m_iconSize; + bool m_updateInRealtime; + + ComputerControlInterfaceList m_controlInterfaces; + +}; diff --git a/master/src/SpotlightPanel.cpp b/master/src/SpotlightPanel.cpp new file mode 100644 index 0000000..621f391 --- /dev/null +++ b/master/src/SpotlightPanel.cpp @@ -0,0 +1,179 @@ +/* + * SpotlightPanel.cpp - implementation of SpotlightPanel + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "ComputerMonitoringModel.h" +#include "ComputerMonitoringWidget.h" +#include "SpotlightModel.h" +#include "SpotlightPanel.h" +#include "UserConfig.h" + +#include "ui_SpotlightPanel.h" + + +SpotlightPanel::SpotlightPanel( UserConfig& config, ComputerMonitoringWidget* computerMonitoringWidget, QWidget* parent ) : + QWidget( parent ), + ui( new Ui::SpotlightPanel ), + m_config( config ), + m_globalComputerMonitoringWidget( computerMonitoringWidget ), + m_model( new SpotlightModel( m_globalComputerMonitoringWidget->dataModel(), this ) ) +{ + ui->setupUi( this ); + + ui->monitoringWidget->setAutoAdjustIconSize( false ); + ui->monitoringWidget->setUseCustomComputerPositions( false ); + ui->monitoringWidget->setAcceptDrops( false ); + ui->monitoringWidget->setDragEnabled( false ); + ui->monitoringWidget->setIgnoreWheelEvent( true ); + ui->monitoringWidget->setModel( m_model ); + + connect( ui->addButton, &QAbstractButton::clicked, this, &SpotlightPanel::add ); + connect( ui->removeButton, &QAbstractButton::clicked, this, &SpotlightPanel::remove ); + connect( ui->realtimeViewButton, &QAbstractButton::toggled, this, &SpotlightPanel::setRealtimeView ); + + connect( m_globalComputerMonitoringWidget, &QAbstractItemView::pressed, this, &SpotlightPanel::addPressedItem ); + connect( ui->monitoringWidget, &QAbstractItemView::pressed, this, &SpotlightPanel::removePressedItem ); + + connect( m_model, &QAbstractItemModel::rowsRemoved, this, [=]() { + if( m_model->rowCount() <= 0 ) + { + ui->stackedWidget->setCurrentWidget( ui->helpPage ); + } + } ); + + setRealtimeView( m_config.spotlightRealtime() ); +} + + + +SpotlightPanel::~SpotlightPanel() +{ + delete ui; +} + + + +void SpotlightPanel::resizeEvent( QResizeEvent* event ) +{ + updateIconSize(); + + QWidget::resizeEvent( event ); +} + + + +void SpotlightPanel::add() +{ + const auto selectedComputerControlInterfaces = m_globalComputerMonitoringWidget->selectedComputerControlInterfaces(); + + if( selectedComputerControlInterfaces.isEmpty() ) + { + QMessageBox::information( this, tr("Spotlight"), + tr( "Please select at least one computer to add.") ); + return; + } + + for( const auto& controlInterface : selectedComputerControlInterfaces ) + { + m_model->add( controlInterface ); + } + + if( ui->stackedWidget->currentWidget() != ui->viewPage ) + { + ui->stackedWidget->setCurrentWidget( ui->viewPage ); + + // due to a bug in QListView force relayout of all items to show decorations (thumbnails) properly + updateIconSize(); + } +} + + + +void SpotlightPanel::remove() +{ + const auto selection = ui->monitoringWidget->selectionModel()->selectedIndexes(); + if( selection.isEmpty() ) + { + QMessageBox::information( this, tr("Spotlight"), + tr( "Please select at least one computer to remove.") ); + return; + } + + for( const auto& index : selection ) + { + m_model->remove( m_model->data( index, SpotlightModel::ControlInterfaceRole ) + .value() ); + } +} + + + +void SpotlightPanel::setRealtimeView( bool enabled ) +{ + m_model->setUpdateInRealtime( enabled ); + + m_config.setSpotlightRealtime( enabled ); + + ui->realtimeViewButton->setChecked( enabled ); +} + + + +void SpotlightPanel::updateIconSize() +{ + static constexpr auto ExtraMargin = 10; + + const auto spacing = ui->monitoringWidget->spacing(); + const auto labelHeight = ui->monitoringWidget->fontMetrics().height(); + + ui->monitoringWidget->setIconSize( { ui->monitoringWidget->width() - ExtraMargin - spacing * 2, + ui->monitoringWidget->height() - ExtraMargin - labelHeight - spacing * 2 } ); + m_model->setIconSize( ui->monitoringWidget->iconSize() ); +} + + + +void SpotlightPanel::addPressedItem( const QModelIndex& index ) +{ + if( QGuiApplication::mouseButtons().testFlag( Qt::MidButton ) ) + { + m_globalComputerMonitoringWidget->selectionModel()->select( index, QItemSelectionModel::SelectCurrent ); + + add(); + } +} + + + +void SpotlightPanel::removePressedItem( const QModelIndex& index ) +{ + if( QGuiApplication::mouseButtons().testFlag( Qt::MidButton ) ) + { + ui->monitoringWidget->selectionModel()->select( index, QItemSelectionModel::SelectCurrent ); + + remove(); + } +} diff --git a/master/src/SpotlightPanel.h b/master/src/SpotlightPanel.h new file mode 100644 index 0000000..60a0fab --- /dev/null +++ b/master/src/SpotlightPanel.h @@ -0,0 +1,64 @@ +/* + * SpotlightPanel.h - declaration of SpotlightPanel + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +class ComputerMonitoringWidget; +class SpotlightModel; +class UserConfig; + +namespace Ui { +class SpotlightPanel; +} + +class SpotlightPanel : public QWidget +{ + Q_OBJECT +public: + explicit SpotlightPanel( UserConfig& config, ComputerMonitoringWidget* computerMonitoringWidget, + QWidget* parent = nullptr ); + ~SpotlightPanel() override; + +protected: + void resizeEvent( QResizeEvent* event ) override; + +private: + void add(); + void remove(); + void setRealtimeView( bool enabled ); + void updateIconSize(); + + void addPressedItem( const QModelIndex& index ); + void removePressedItem( const QModelIndex& index ); + + Ui::SpotlightPanel* ui; + + UserConfig& m_config; + + ComputerMonitoringWidget* m_globalComputerMonitoringWidget; + SpotlightModel* m_model; + +} ; diff --git a/master/src/SpotlightPanel.ui b/master/src/SpotlightPanel.ui new file mode 100644 index 0000000..6c2fc4b --- /dev/null +++ b/master/src/SpotlightPanel.ui @@ -0,0 +1,229 @@ + + + SpotlightPanel + + + + 0 + + + 0 + + + 4 + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + 128 + 128 + 128 + + + + + + + + + 128 + 128 + 128 + + + + + + + + + 119 + 120 + 120 + + + + + + + + + 20 + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + Qt::AlignCenter + + + true + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + 4 + + + 4 + + + 4 + + + + + Add selected computers + + + + :/core/go-up.png:/core/go-up.png + + + + 32 + 32 + + + + + + + + Remove selected computers + + + + :/core/go-down.png:/core/go-down.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Update computers in realtime + + + + :/master/update-realtime-disabled.png + :/master/update-realtime-enabled.png:/master/update-realtime-disabled.png + + + + 32 + 32 + + + + true + + + true + + + + + + + + + + + + + + + + + + ComputerMonitoringWidget + QListView +
ComputerMonitoringWidget.h
+ 1 +
+
+ + + + + +
diff --git a/master/src/UserConfig.cpp b/master/src/UserConfig.cpp new file mode 100644 index 0000000..4406edf --- /dev/null +++ b/master/src/UserConfig.cpp @@ -0,0 +1,43 @@ +/* + * UserConfig.cpp - Configuration object storing personal settings + * for the Veyon Master Application + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "VeyonCore.h" +#include "UserConfig.h" + + +UserConfig::UserConfig( Configuration::Store::Backend backend ) : + Configuration::Object( backend, Configuration::Store::User, QStringLiteral("VeyonMaster") ) +{ + if( isStoreWritable() == false ) + { + QMessageBox::information( nullptr, + tr( "No write access" ), + tr( "Could not save your personal settings! " + "Please check the user configuration " + "file path using the %1 Configurator." ).arg( VeyonCore::applicationName() ) ); + } +} diff --git a/master/src/UserConfig.h b/master/src/UserConfig.h new file mode 100644 index 0000000..48906eb --- /dev/null +++ b/master/src/UserConfig.h @@ -0,0 +1,62 @@ +/* + * UserConfig.h - UserConfig class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "Configuration/Object.h" +#include "Configuration/Property.h" +#include "ComputerMonitoringView.h" +#include "SlideshowPanel.h" +#include "VeyonMaster.h" + +// clazy:excludeall=ctor-missing-parent-argument,copyable-polymorphic + +class UserConfig : public Configuration::Object +{ + Q_OBJECT +public: + explicit UserConfig( Configuration::Store::Backend backend ); + +#define FOREACH_PERSONAL_CONFIG_PROPERTY(OP) \ + OP( UserConfig, VeyonMaster::userConfig(), QJsonArray, checkedNetworkObjects, setCheckedNetworkObjects, "CheckedNetworkObjects", "UI", QJsonArray(), Configuration::Property::Flag::Standard ) \ + OP( UserConfig, VeyonMaster::userConfig(), QJsonArray, computerPositions, setComputerPositions, "ComputerPositions", "UI", QJsonArray(), Configuration::Property::Flag::Standard ) \ + OP( UserConfig, VeyonMaster::userConfig(), bool, useCustomComputerPositions, setUseCustomComputerPositions, "UseCustomComputerPositions", "UI", false, Configuration::Property::Flag::Standard ) \ + OP( UserConfig, VeyonMaster::userConfig(), bool, filterPoweredOnComputers, setFilterPoweredOnComputers, "FilterPoweredOnComputers", "UI", false, Configuration::Property::Flag::Standard ) \ + OP( UserConfig, VeyonMaster::userConfig(), int, monitoringScreenSize, setMonitoringScreenSize, "MonitoringScreenSize", "UI", ComputerMonitoringView::DefaultComputerScreenSize, Configuration::Property::Flag::Standard ) \ + OP( UserConfig, VeyonMaster::userConfig(), bool, autoAdjustMonitoringIconSize, setAutoAdjustMonitoringIconSize, "AutoAdjustMonitoringIconSize", "UI", false, Configuration::Property::Flag::Standard ) \ + OP( UserConfig, VeyonMaster::userConfig(), int, slideshowDuration, setSlideshowDuration, "SlideshowDuration", "UI", SlideshowPanel::DefaultDuration, Configuration::Property::Flag::Standard ) \ + OP( UserConfig, VeyonMaster::userConfig(), bool, spotlightRealtime, setSpotlightRealtime, "SpotlightRealtime", "UI", true, Configuration::Property::Flag::Standard ) \ + OP( UserConfig, VeyonMaster::userConfig(), int, defaultRole, setDefaultRole, "DefaultRole", "Authentication", 0, Configuration::Property::Flag::Standard ) \ + OP( UserConfig, VeyonMaster::userConfig(), bool, toolButtonIconOnlyMode, setToolButtonIconOnlyMode, "ToolButtonIconOnlyMode", "UI", false, Configuration::Property::Flag::Standard ) \ + OP( UserConfig, VeyonMaster::userConfig(), bool, noToolTips, setNoToolTips, "NoToolTips", "UI", false, Configuration::Property::Flag::Standard ) \ + OP( UserConfig, VeyonMaster::userConfig(), QJsonObject, splitterStates, setSplitterStates, "SplitterStates", "UI", {}, Configuration::Property::Flag::Standard ) \ + OP( UserConfig, VeyonMaster::userConfig(), QString, windowState, setWindowState, "WindowState", "UI", QString(), Configuration::Property::Flag::Standard ) \ + OP( UserConfig, VeyonMaster::userConfig(), QString, windowGeometry, setWindowGeometry, "WindowGeometry", "UI", QString(), Configuration::Property::Flag::Standard ) \ + + FOREACH_PERSONAL_CONFIG_PROPERTY(DECLARE_CONFIG_PROPERTY) + +} ; diff --git a/master/src/VeyonMaster.cpp b/master/src/VeyonMaster.cpp new file mode 100644 index 0000000..b3cf52a --- /dev/null +++ b/master/src/VeyonMaster.cpp @@ -0,0 +1,324 @@ +/* + * VeyonMaster.cpp - management of application-global instances + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "VeyonMaster.h" +#include "BuiltinFeatures.h" +#include "ComputerControlListModel.h" +#include "ComputerMonitoringModel.h" +#include "FeatureManager.h" +#include "VncConnection.h" +#include "VeyonConfiguration.h" +#include "VeyonConnection.h" +#include "MainWindow.h" +#include "ComputerManager.h" +#include "MonitoringMode.h" +#include "UserConfig.h" +#include "PluginManager.h" + + +VeyonMaster::VeyonMaster( QObject* parent ) : + VeyonMasterInterface( parent ), + m_userConfig( new UserConfig( Configuration::Store::JsonFile ) ), + m_featureManager( new FeatureManager() ), + m_features( featureList() ), + m_computerManager( new ComputerManager( *m_userConfig, this ) ), + m_computerControlListModel( new ComputerControlListModel( this, this ) ), + m_computerMonitoringModel( new ComputerMonitoringModel( this ) ), + m_localSessionControlInterface( Computer( NetworkObject::Uid::createUuid(), + QStringLiteral("localhost"), + QStringLiteral("%1:%2"). + arg( QHostAddress( QHostAddress::LocalHost ).toString() ). + arg( VeyonCore::config().veyonServerPort() + VeyonCore::sessionId() ) ), + this ), + m_mainWindow( nullptr ), + m_currentMode( VeyonCore::builtinFeatures().monitoringMode().feature().uid() ) +{ + if( VeyonCore::config().enforceSelectedModeForClients() ) + { + connect( m_computerControlListModel, &ComputerControlListModel::activeFeaturesChanged, + this, &VeyonMaster::enforceDesignatedMode ); + } + + connect( &m_localSessionControlInterface, &ComputerControlInterface::featureMessageReceived, + this, [=]( const FeatureMessage& featureMessage, const ComputerControlInterface::Pointer& computerControlInterface ) { + m_featureManager->handleFeatureMessage( computerControlInterface, featureMessage ); + } ); + + m_localSessionControlInterface.start(); + + // attach computer list model to proxy model + m_computerMonitoringModel->setSourceModel( m_computerControlListModel ); + m_computerMonitoringModel->setSortRole( Qt::InitialSortOrderRole ); + m_computerMonitoringModel->setStateRole( ComputerControlListModel::StateRole ); + m_computerMonitoringModel->sort( 0 ); + + m_mainWindow = new MainWindow( *this ); +} + + + +VeyonMaster::~VeyonMaster() +{ + stopAllModeFeatures( m_computerControlListModel->computerControlInterfaces() ); + + delete m_mainWindow; + + delete m_computerManager; + + m_userConfig->flushStore(); + delete m_userConfig; + + delete m_featureManager; +} + + + +FeatureList VeyonMaster::subFeatures( Feature::Uid parentFeatureUid ) const +{ + FeatureList features; + + const auto disabledFeatures = VeyonCore::config().disabledFeatures(); + const auto pluginUids = VeyonCore::pluginManager().pluginUids(); + + for( const auto& pluginUid : pluginUids ) + { + for( const auto& feature : m_featureManager->features( pluginUid ) ) + { + if( feature.testFlag( Feature::Master ) && + feature.parentUid() == parentFeatureUid && + disabledFeatures.contains( parentFeatureUid.toString() ) == false && + disabledFeatures.contains( feature.uid().toString() ) == false ) + { + features += feature; + } + } + } + + return features; +} + + + +FeatureUidList VeyonMaster::subFeaturesUids( Feature::Uid parentFeatureUid ) const +{ + FeatureUidList featureUids; + + const auto disabledFeatures = VeyonCore::config().disabledFeatures(); + const auto pluginUids = VeyonCore::pluginManager().pluginUids(); + + for( const auto& pluginUid : pluginUids ) + { + for( const auto& feature : m_featureManager->features( pluginUid ) ) + { + if( feature.testFlag( Feature::Master ) && + feature.parentUid() == parentFeatureUid && + disabledFeatures.contains( parentFeatureUid.toString() ) == false && + disabledFeatures.contains( feature.uid().toString() ) == false ) + { + featureUids += feature.uid(); + } + } + } + + return featureUids; +} + + + +FeatureList VeyonMaster::modeFeatures() const +{ + FeatureList featureList; + + for( const auto& feature : qAsConst( features() ) ) + { + if( feature.testFlag( Feature::Mode ) ) + { + featureList.append( feature ); + const auto modeSubFeatures = subFeatures( feature.uid() ); + for( const auto& subFeature : modeSubFeatures ) + { + featureList.append( subFeature ); + } + } + } + + return featureList; +} + + + +QWidget* VeyonMaster::mainWindow() +{ + return m_mainWindow; +} + + + +Configuration::Object* VeyonMaster::userConfigurationObject() +{ + return m_userConfig; +} + + + +void VeyonMaster::reloadSubFeatures() +{ + m_mainWindow->reloadSubFeatures(); +} + + + +ComputerControlInterfaceList VeyonMaster::selectedComputerControlInterfaces() const +{ + return m_mainWindow->selectedComputerControlInterfaces(); +} + + + +ComputerControlInterfaceList VeyonMaster::filteredComputerControlInterfaces() +{ + const auto rowCount = m_computerMonitoringModel->rowCount(); + + ComputerControlInterfaceList computerControlInterfaces; + computerControlInterfaces.reserve( rowCount ); + + for( int i = 0; i < rowCount; ++i ) + { + const auto index = m_computerMonitoringModel->index( i, 0 ); + const auto sourceIndex = m_computerMonitoringModel->mapToSource( index ); + computerControlInterfaces.append( m_computerControlListModel->computerControlInterface( sourceIndex ) ); + } + + return computerControlInterfaces; +} + + + +void VeyonMaster::runFeature( const Feature& feature ) +{ + const auto computerControlInterfaces = filteredComputerControlInterfaces(); + + if( feature.testFlag( Feature::Mode ) ) + { + stopAllModeFeatures( computerControlInterfaces ); + + if( m_currentMode == feature.uid() || + subFeatures( feature.uid() ).contains( m_featureManager->feature( m_currentMode ) ) ) + { + const Feature& monitoringModeFeature = VeyonCore::builtinFeatures().monitoringMode().feature(); + + m_featureManager->startFeature( *this, monitoringModeFeature, computerControlInterfaces ); + m_currentMode = monitoringModeFeature.uid(); + } + else + { + m_featureManager->startFeature( *this, feature, computerControlInterfaces ); + m_currentMode = feature.uid(); + } + } + else + { + m_featureManager->startFeature( *this, feature, computerControlInterfaces ); + } +} + + + +void VeyonMaster::enforceDesignatedMode( const QModelIndex& index ) +{ + auto controlInterface = m_computerControlListModel->computerControlInterface( index ); + if( controlInterface ) + { + auto designatedModeFeature = m_featureManager->feature( controlInterface->designatedModeFeature() ); + + // stop all other active mode feature + const auto features = modeFeatures(); + for( const auto& currentFeature : features ) + { + if( currentFeature != designatedModeFeature ) + { + featureManager().stopFeature( *this, currentFeature, { controlInterface } ); + } + } + + if( designatedModeFeature != VeyonCore::builtinFeatures().monitoringMode().feature() ) + { + featureManager().startFeature( *this, designatedModeFeature, { controlInterface } ); + } + } +} + + + +void VeyonMaster::stopAllModeFeatures( const ComputerControlInterfaceList& computerControlInterfaces ) +{ + const auto features = modeFeatures(); + + for( const auto& feature : features ) + { + m_featureManager->stopFeature( *this, feature, computerControlInterfaces ); + } +} + + + +FeatureList VeyonMaster::featureList() const +{ + FeatureList features; + + const auto disabledFeatures = VeyonCore::config().disabledFeatures(); + const auto pluginUids = VeyonCore::pluginManager().pluginUids(); + + for( const auto& pluginUid : pluginUids ) + { + for( const auto& feature : m_featureManager->features( pluginUid ) ) + { + if( feature.testFlag( Feature::Master ) && + feature.testFlag( Feature::Mode ) && + feature.parentUid().isNull() && + disabledFeatures.contains( feature.uid().toString() ) == false ) + { + features += feature; + } + } + } + + for( const auto& pluginUid : pluginUids ) + { + for( const auto& feature : m_featureManager->features( pluginUid ) ) + { + if( feature.testFlag( Feature::Master ) && + feature.testFlag( Feature::Mode ) == false && + feature.parentUid().isNull() && + disabledFeatures.contains( feature.uid().toString() ) == false ) + { + features += feature; + } + } + } + + return features; +} diff --git a/master/src/VeyonMaster.h b/master/src/VeyonMaster.h new file mode 100644 index 0000000..3b0aa7f --- /dev/null +++ b/master/src/VeyonMaster.h @@ -0,0 +1,125 @@ +/* + * VeyonMaster.h - global instances + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "Feature.h" +#include "Computer.h" +#include "ComputerControlInterface.h" +#include "VeyonMasterInterface.h" + +class QModelIndex; + +class BuiltinFeatures; +class ComputerControlListModel; +class ComputerManager; +class ComputerMonitoringModel; +class FeatureManager; +class MainWindow; +class UserConfig; + +class VeyonMaster : public VeyonMasterInterface +{ + Q_OBJECT +public: + explicit VeyonMaster( QObject* parent = nullptr ); + ~VeyonMaster() override; + + FeatureManager& featureManager() + { + return *m_featureManager; + } + + UserConfig& userConfig() + { + return *m_userConfig; + } + + ComputerManager& computerManager() + { + return *m_computerManager; + } + + ComputerControlListModel& computerControlListModel() + { + return *m_computerControlListModel; + } + + ComputerMonitoringModel* computerMonitoringModel() const + { + return m_computerMonitoringModel; + } + + const FeatureList& features() const + { + return m_features; + } + + FeatureList subFeatures( Feature::Uid parentFeatureUid ) const; + FeatureUidList subFeaturesUids( Feature::Uid parentFeatureUid ) const; + + FeatureList modeFeatures() const; + + const Feature::Uid& currentMode() const + { + return m_currentMode; + } + + QWidget* mainWindow() override; + Configuration::Object* userConfigurationObject() override; + void reloadSubFeatures() override; + + ComputerControlInterface& localSessionControlInterface() override + { + return m_localSessionControlInterface; + } + + ComputerControlInterfaceList selectedComputerControlInterfaces() const override; + + ComputerControlInterfaceList filteredComputerControlInterfaces(); + +public Q_SLOTS: + void runFeature( const Feature& feature ); + void enforceDesignatedMode( const QModelIndex& index ); + void stopAllModeFeatures( const ComputerControlInterfaceList& computerControlInterfaces ); + +private: + FeatureList featureList() const; + + UserConfig* m_userConfig; + FeatureManager* m_featureManager; + const FeatureList m_features; + ComputerManager* m_computerManager; + ComputerControlListModel* m_computerControlListModel; + ComputerMonitoringModel* m_computerMonitoringModel; + + ComputerControlInterface m_localSessionControlInterface; + + MainWindow* m_mainWindow; + + Feature::Uid m_currentMode; + +} ; diff --git a/master/src/kitemmodels_debug.h b/master/src/kitemmodels_debug.h new file mode 100644 index 0000000..b637c75 --- /dev/null +++ b/master/src/kitemmodels_debug.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(KITEMMODELS_LOG); + diff --git a/master/src/kitemmodels_export.h b/master/src/kitemmodels_export.h new file mode 100644 index 0000000..b7c411b --- /dev/null +++ b/master/src/kitemmodels_export.h @@ -0,0 +1,2 @@ +#define KITEMMODELS_EXPORT + diff --git a/master/src/main.cpp b/master/src/main.cpp new file mode 100644 index 0000000..669341f --- /dev/null +++ b/master/src/main.cpp @@ -0,0 +1,67 @@ +/* + * main.cpp - startup routine for Veyon Master Application + * + * Copyright (c) 2004-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "DocumentationFigureCreator.h" +#include "VeyonMaster.h" +#include "MainWindow.h" + + +int main( int argc, char** argv ) +{ + VeyonCore::setupApplicationParameters(); + + QApplication app( argc, argv ); + app.connect( &app, &QApplication::lastWindowClosed, &QApplication::quit ); + + VeyonCore core( &app, VeyonCore::Component::Master, QStringLiteral("Master") ); + +#ifdef VEYON_DEBUG + if( qEnvironmentVariableIsSet( "VEYON_MASTER_CREATE_DOC_FIGURES") ) + { + DocumentationFigureCreator().run(); + return 0; + } +#endif + + QSplashScreen splashScreen( QPixmap( QStringLiteral(":/master/splash.png") ) ); + splashScreen.show(); + + if( MainWindow::initAuthentication() == false || + MainWindow::initAccessControl() == false ) + { + return -1; + } + + VeyonMaster masterCore( &core ); + + // hide splash-screen as soon as main-window is shown + splashScreen.finish( masterCore.mainWindow() ); + + masterCore.mainWindow()->show(); + + return core.exec(); +} diff --git a/master/veyon-master.1 b/master/veyon-master.1 new file mode 100644 index 0000000..f7735c3 --- /dev/null +++ b/master/veyon-master.1 @@ -0,0 +1,31 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH VEYON-MASTER 1 2018-12-07 Veyon +.SH NAME +veyon-master \- Veyon Master Application +.SH SYNOPSIS +.B veyon-master +.SH DESCRIPTION +\fBVEYON MASTER\fR is an application program that can be used for +monitoring and controlling other computers as well as for accessing Veyon +features. +.PP +Usually the program is started by the end user. It accesses other +computers through the Veyon Service. +.PP +Using the program you can remote control, lock, shutdown/reboot computers +as well as show a demo, send text messages to users and much more. + +.SH SEE ALSO +veyon-service(1), veyon-configurator(8), veyon-auth-helper(1) +.PP +https://veyon.io + +.SH AUTHOR +Veyon has been written by Tobias Junghans. +.PP +This manual page has been written by Tobias Junghans and Mike Gabriel. It +was originally written for the Debian project (but may be used by +others). diff --git a/master/veyon-master.rc.in b/master/veyon-master.rc.in new file mode 100644 index 0000000..ebd5b60 --- /dev/null +++ b/master/veyon-master.rc.in @@ -0,0 +1,32 @@ +veyonmastericon ICON data/veyon-master.ico +#include + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST data/veyon-master.exe.manifest + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@,@VERSION_BUILD@ + FILEFLAGSMASK 0x0L + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, charset = Windows, Multilingual + BEGIN + VALUE "Comments", "Virtual Eye On Networks (https://veyon.io)\0" + VALUE "CompanyName", "Veyon Solutions\0" + VALUE "ProductName", "Veyon\0" + VALUE "ProductVersion", "@VERSION_STRING@\0" + VALUE "FileDescription", "Veyon Master\0" + VALUE "FileVersion", "@VERSION_STRING@\0" + VALUE "LegalCopyright", "Copyright (c) 2017-2021 Veyon Solutions / Tobias Junghans\0" + VALUE "OriginalFilename", "veyon-master.exe\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04E4 + END +END diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt new file mode 100644 index 0000000..d171cc0 --- /dev/null +++ b/plugins/CMakeLists.txt @@ -0,0 +1,7 @@ +FILE(GLOB plugins RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*) + +FOREACH(plugin ${plugins}) + IF(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${plugin}) + ADD_SUBDIRECTORY(${plugin}) + ENDIF() +ENDFOREACH() diff --git a/plugins/authkeys/AuthKeysConfigurationPage.cpp b/plugins/authkeys/AuthKeysConfigurationPage.cpp new file mode 100644 index 0000000..0a7908d --- /dev/null +++ b/plugins/authkeys/AuthKeysConfigurationPage.cpp @@ -0,0 +1,288 @@ +/* + * AuthKeysConfigurationPage.cpp - implementation of the authentication configuration page + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include "AuthKeysConfigurationPage.h" +#include "AuthKeysManager.h" +#include "FileSystemBrowser.h" +#include "PlatformUserFunctions.h" +#include "VeyonConfiguration.h" +#include "Configuration/UiMapping.h" + +#include "ui_AuthKeysConfigurationPage.h" + + +AuthKeysConfigurationPage::AuthKeysConfigurationPage() : + ConfigurationPage(), + ui(new Ui::AuthKeysConfigurationPage), + m_authKeyTableModel( this ), + m_keyFilesFilter( tr( "Key files (*.pem)" ) ) +{ + ui->setupUi(this); + + Configuration::UiMapping::setFlags( ui->keyFileDirectories, Configuration::Property::Flag::Advanced ); + +#define CONNECT_BUTTON_SLOT(name) \ + connect( ui->name, &QAbstractButton::clicked, this, &AuthKeysConfigurationPage::name ); + + CONNECT_BUTTON_SLOT( openPublicKeyBaseDir ); + CONNECT_BUTTON_SLOT( openPrivateKeyBaseDir ); + CONNECT_BUTTON_SLOT( createKeyPair ); + CONNECT_BUTTON_SLOT( deleteKey ); + CONNECT_BUTTON_SLOT( importKey ); + CONNECT_BUTTON_SLOT( exportKey ); + CONNECT_BUTTON_SLOT( setAccessGroup ); + + reloadKeyTable(); + + ui->keyTable->setModel( &m_authKeyTableModel ); +} + + +AuthKeysConfigurationPage::~AuthKeysConfigurationPage() +{ + delete ui; +} + + + +void AuthKeysConfigurationPage::resetWidgets() +{ + FOREACH_VEYON_KEY_AUTHENTICATION_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); + + reloadKeyTable(); +} + + + +void AuthKeysConfigurationPage::connectWidgetsToProperties() +{ + FOREACH_VEYON_KEY_AUTHENTICATION_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY); +} + + + +void AuthKeysConfigurationPage::applyConfiguration() +{ +} + + + +void AuthKeysConfigurationPage::openPublicKeyBaseDir() +{ + FileSystemBrowser( FileSystemBrowser::ExistingDirectory ). + exec( ui->publicKeyBaseDir ); +} + + + +void AuthKeysConfigurationPage::openPrivateKeyBaseDir() +{ + FileSystemBrowser( FileSystemBrowser::ExistingDirectory ). + exec( ui->privateKeyBaseDir ); +} + + + +void AuthKeysConfigurationPage::createKeyPair() +{ + const auto keyName = QInputDialog::getText( this, tr( "Authentication key name" ), + tr( "Please enter the name of the user group or role for which to create an authentication key pair:") ); + if( keyName.isEmpty() == false ) + { + AuthKeysManager authKeysManager; + const auto success = authKeysManager.createKeyPair( keyName ); + + showResultMessage( success, tr( "Create key pair" ), authKeysManager.resultMessage() ); + + reloadKeyTable(); + } +} + + + +void AuthKeysConfigurationPage::deleteKey() +{ + const auto title = ui->deleteKey->text(); + + const auto nameAndType = selectedKey().split(QLatin1Char('/')); + + if( nameAndType.size() > 1 ) + { + const auto name = nameAndType[0]; + const auto type = nameAndType[1]; + + if( QMessageBox::question( this, title, tr( "Do you really want to delete authentication key \"%1/%2\"?" ).arg( name, type ) ) == + QMessageBox::Yes ) + { + AuthKeysManager authKeysManager; + const auto success = authKeysManager.deleteKey( name, type ); + + showResultMessage( success, title, authKeysManager.resultMessage() ); + + reloadKeyTable(); + } + } + else + { + showResultMessage( false, title, tr( "Please select a key to delete!" ) ); + } +} + + + +void AuthKeysConfigurationPage::importKey() +{ + const auto title = ui->importKey->text(); + + const auto inputFile = QFileDialog::getOpenFileName( this, title, {}, m_keyFilesFilter ); + if( inputFile.isEmpty() ) + { + return; + } + + const auto keyName = QInputDialog::getText( this, tr( "Authentication key name" ), + tr( "Please enter the name of the user group or role for which to import the authentication key:"), + QLineEdit::Normal, + AuthKeysManager::keyNameFromExportedKeyFile( inputFile ) ); + if( keyName.isEmpty() ) + { + return; + } + + AuthKeysManager authKeysManager; + const auto keyType = authKeysManager.detectKeyType( inputFile ); + const auto success = authKeysManager.importKey( keyName, keyType, inputFile ); + + showResultMessage( success, title, authKeysManager.resultMessage() ); + + reloadKeyTable(); +} + + + +void AuthKeysConfigurationPage::exportKey() +{ + const auto title = ui->exportKey->text(); + + const auto nameAndType = selectedKey().split(QLatin1Char('/')); + + if( nameAndType.size() > 1 ) + { + const auto name = nameAndType[0]; + const auto type = nameAndType[1]; + + const auto outputFile = QFileDialog::getSaveFileName( this, title, QDir::homePath() + QDir::separator() + + AuthKeysManager::exportedKeyFileName( name, type ), + m_keyFilesFilter ); + if( outputFile.isEmpty() == false ) + { + AuthKeysManager authKeysManager; + const auto success = authKeysManager.exportKey( name, type, outputFile, true ); + + showResultMessage( success, title, authKeysManager.resultMessage() ); + } + } + else + { + showResultMessage( false, title, tr( "Please select a key to export!" ) ); + } +} + + + +void AuthKeysConfigurationPage::setAccessGroup() +{ + const auto title = ui->setAccessGroup->text(); + + const auto key = selectedKey(); + + if( key.isEmpty() == false ) + { + const auto userGroups = VeyonCore::platform().userFunctions().userGroups( VeyonCore::config().domainGroupsForAccessControlEnabled() ); + const auto currentGroup = AuthKeysManager().accessGroup( key ); + + bool ok = false; + const auto selectedGroup = QInputDialog::getItem( this, title, + tr( "Please select a user group which to grant access to key \"%1\":" ).arg( key ), + userGroups, userGroups.indexOf( currentGroup ), true, &ok ); + + if( ok && selectedGroup.isEmpty() == false ) + { + AuthKeysManager manager; + const auto success = manager.setAccessGroup( key, selectedGroup ); + + showResultMessage( success, title, manager.resultMessage() ); + + reloadKeyTable(); + } + } + else + { + showResultMessage( false, title, tr( "Please select a key which to set the access group for!" ) ); + } +} + + + +void AuthKeysConfigurationPage::reloadKeyTable() +{ + m_authKeyTableModel.reload(); + ui->keyTable->resizeColumnsToContents(); +} + + + +QString AuthKeysConfigurationPage::selectedKey() const +{ + const auto row = ui->keyTable->currentIndex().row(); + if( row >= 0 && row < m_authKeyTableModel.rowCount() ) + { + return m_authKeyTableModel.key( row ); + } + + return {}; +} + + + +void AuthKeysConfigurationPage::showResultMessage( bool success, const QString& title, const QString& message ) +{ + if( message.isEmpty() ) + { + return; + } + + if( success ) + { + QMessageBox::information( this, title, message ); + } + else + { + QMessageBox::critical( this, title, message ); + } +} diff --git a/plugins/authkeys/AuthKeysConfigurationPage.h b/plugins/authkeys/AuthKeysConfigurationPage.h new file mode 100644 index 0000000..1300d9a --- /dev/null +++ b/plugins/authkeys/AuthKeysConfigurationPage.h @@ -0,0 +1,63 @@ +/* + * AuthKeysConfigurationPage.h - header for the AuthKeysConfigurationPage class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "AuthKeysTableModel.h" +#include "ConfigurationPage.h" + +namespace Ui { +class AuthKeysConfigurationPage; +} + +class AuthKeysConfigurationPage : public ConfigurationPage +{ + Q_OBJECT +public: + AuthKeysConfigurationPage(); + ~AuthKeysConfigurationPage() override; + + void resetWidgets() override; + void connectWidgetsToProperties() override; + void applyConfiguration() override; + +private Q_SLOTS: + void openPublicKeyBaseDir(); + void openPrivateKeyBaseDir(); + void createKeyPair(); + void deleteKey(); + void importKey(); + void exportKey(); + void setAccessGroup(); + void reloadKeyTable(); + +private: + QString selectedKey() const; + void showResultMessage( bool success, const QString& title, const QString& message ); + + Ui::AuthKeysConfigurationPage *ui; + AuthKeysTableModel m_authKeyTableModel; + const QString m_keyFilesFilter; + +}; diff --git a/plugins/authkeys/AuthKeysConfigurationPage.ui b/plugins/authkeys/AuthKeysConfigurationPage.ui new file mode 100644 index 0000000..cd41d1b --- /dev/null +++ b/plugins/authkeys/AuthKeysConfigurationPage.ui @@ -0,0 +1,244 @@ + + + AuthKeysConfigurationPage + + + Authentication keys + + + + :/core/application-x-pem-key.png:/core/application-x-pem-key.png + + + + 0 + + + 0 + + + + + Introduction + + + + 10 + + + + + Please perform the following steps to set up key file authentication: + + + true + + + + + + + 1) Create a key pair on the master computer. + + + true + + + + + + + 2) Set an access group whose members should be allowed to access other computers. + + + true + + + + + + + 3) Export the public key and import it on all client computers with the same name. + + + true + + + + + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + true + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + + Key file directories + + + + 2 + + + + + 10 + + + 7 + + + + + Public key file base directory + + + + + + + + + + Private key file base directory + + + + + + + + + + + :/core/document-open.png:/core/document-open.png + + + + + + + + :/core/document-open.png:/core/document-open.png + + + + + + + + + + + + Available authentication keys + + + + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + + + + + + + Create key pair + + + + + + + Delete key + + + + + + + Import key + + + + + + + Export key + + + + + + + Set access group + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + diff --git a/plugins/authkeys/AuthKeysManager.cpp b/plugins/authkeys/AuthKeysManager.cpp new file mode 100644 index 0000000..b73319a --- /dev/null +++ b/plugins/authkeys/AuthKeysManager.cpp @@ -0,0 +1,554 @@ +/* + * AuthKeysManager.cpp - implementation of AuthKeysManager class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "AuthKeysManager.h" +#include "CommandLineIO.h" +#include "CryptoCore.h" +#include "Filesystem.h" +#include "PlatformFilesystemFunctions.h" +#include "VeyonConfiguration.h" + + +AuthKeysManager::AuthKeysManager( QObject* parent ) : + QObject( parent ), + m_keyTypePrivate( QStringLiteral("private") ), + m_keyTypePublic( QStringLiteral("public") ), + m_checkPermissions( tr( "Please check your permissions." ) ), + m_invalidKeyName( tr( "Key name contains invalid characters!" ) ), + m_invalidKeyType( tr( "Invalid key type specified! Please specify \"%1\" or \"%2\"." ).arg( m_keyTypePrivate, m_keyTypePublic ) ), + m_keyDoesNotExist( tr( "Specified key does not exist! Please use the \"list\" command to list all installed keys." ) ), + m_keysAlreadyExists( tr( "One or more key files already exist! Please delete them using the \"delete\" command." ) ), + m_resultMessage() +{ +} + + + +bool AuthKeysManager::createKeyPair( const QString& name ) +{ + if( VeyonCore::isAuthenticationKeyNameValid( name ) == false) + { + m_resultMessage = m_invalidKeyName; + return false; + } + + const auto privateKeyFileName = VeyonCore::filesystem().privateKeyPath( name ); + const auto publicKeyFileName = VeyonCore::filesystem().publicKeyPath( name ); + + if( QFileInfo::exists( privateKeyFileName ) || QFileInfo::exists( publicKeyFileName ) ) + { + m_resultMessage = m_keysAlreadyExists; + return false; + } + + CommandLineIO::print( tr( "Creating new key pair for \"%1\"" ).arg( name ) ); + + const auto privateKey = CryptoCore::KeyGenerator().createRSA( CryptoCore::RsaKeySize ); + const auto publicKey = privateKey.toPublicKey(); + + if( privateKey.isNull() || publicKey.isNull() ) + { + m_resultMessage = tr( "Failed to create public or private key!" ); + return false; + } + + if( writePrivateKeyFile( privateKey, privateKeyFileName ) == false || + writePublicKeyFile( publicKey, publicKeyFileName ) == false ) + { + // m_resultMessage already set by write functions + return false; + } + + m_resultMessage = tr( "Newly created key pair has been saved to \"%1\" and \"%2\"." ).arg( privateKeyFileName, publicKeyFileName ); + + return true; +} + + + +bool AuthKeysManager::deleteKey( const QString& name, const QString& type ) +{ + if( checkKey( name, type ) == false ) + { + return false; + } + + const auto keyFileName = keyFilePathFromType( name, type ); + + QFile keyFile( keyFileName ); + keyFile.setPermissions( QFile::WriteOwner | QFile::WriteGroup | QFile::WriteOther ); + + if( keyFile.remove() == false ) + { + m_resultMessage = tr( "Could not remove key file \"%1\"!" ).arg( keyFileName ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + auto keyFileDirectory = QFileInfo( keyFileName ).absoluteDir(); + auto keyFileBaseDirectory = keyFileDirectory; + keyFileBaseDirectory.cdUp(); + + if( keyFileBaseDirectory.rmdir( keyFileDirectory.dirName() ) == false ) + { + m_resultMessage = tr( "Could not remove key file directory \"%1\"!" ).arg( keyFileDirectory.path() ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + return true; +} + + + +bool AuthKeysManager::exportKey( const QString& name, const QString& type, const QString& outputFile, bool overwriteExisting ) +{ + if( checkKey( name, type ) == false ) + { + return false; + } + + const auto keyFileName = keyFilePathFromType( name, type ); + + if( VeyonCore::filesystem().ensurePathExists( QFileInfo( outputFile ).path() ) == false ) + { + m_resultMessage = tr( "Failed to create directory for output file." ); + return false; + } + + if( overwriteExisting == false && QFileInfo::exists( outputFile ) ) + { + m_resultMessage = tr( "File \"%1\" already exists." ).arg( outputFile ); + return false; + } + + if( QFile::copy( keyFileName, outputFile ) == false ) + { + m_resultMessage = tr( "Failed to write output file." ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + m_resultMessage = tr( "Key \"%1/%2\" has been exported to \"%3\" successfully." ).arg( name, type, outputFile ); + + return true; +} + + + +bool AuthKeysManager::importKey( const QString& name, const QString& type, const QString& inputFile ) +{ + if( VeyonCore::isAuthenticationKeyNameValid( name ) == false) + { + m_resultMessage = m_invalidKeyName; + return false; + } + + if( QFileInfo( inputFile ).isReadable() == false ) + { + m_resultMessage = tr( "Failed read input file." ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + QString keyFileName; + + if( type == m_keyTypePrivate ) + { + const auto privateKey = CryptoCore::PrivateKey( inputFile ); + if( privateKey.isNull() || privateKey.isPrivate() == false ) + { + m_resultMessage = tr( "File \"%1\" does not contain a valid private key!" ).arg( inputFile ); + return false; + } + + keyFileName = VeyonCore::filesystem().privateKeyPath( name ); + } + else if( type == m_keyTypePublic ) + { + const auto publicKey = CryptoCore::PublicKey( inputFile ); + if( publicKey.isNull() || publicKey.isPublic() == false ) + { + m_resultMessage = tr( "File \"%1\" does not contain a valid public key!" ).arg( inputFile ); + return false; + } + + keyFileName = VeyonCore::filesystem().publicKeyPath( name ); + } + else + { + m_resultMessage = m_invalidKeyType; + return false; + } + + if( QFileInfo::exists( keyFileName ) ) + { + m_resultMessage = m_keysAlreadyExists; + return false; + } + + if( VeyonCore::filesystem().ensurePathExists( QFileInfo( keyFileName ).path() ) == false ) + { + m_resultMessage = tr( "Failed to create directory for key file." ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + if( QFile::copy( inputFile, keyFileName ) == false ) + { + m_resultMessage = tr( "Failed to write key file \"%1\"." ).arg( keyFileName ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + if( setKeyFilePermissions( name, type ) == false ) + { + m_resultMessage = tr( "Failed to set permissions for key file \"%1\"!" ).arg( keyFileName ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + m_resultMessage = tr( "Key \"%1/%2\" has been imported successfully. Please check file permissions of \"%3\" " + "in order to prevent unauthorized accesses." ).arg( name, type, keyFileName ); + + return true; +} + + + +QStringList AuthKeysManager::listKeys() +{ + const auto privateKeyBaseDir = VeyonCore::filesystem().expandPath( VeyonCore::config().privateKeyBaseDir() ); + const auto privateKeyDirs = QDir( privateKeyBaseDir ).entryList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name ); + + const auto publicKeyBaseDir = VeyonCore::filesystem().expandPath( VeyonCore::config().publicKeyBaseDir() ); + const auto publicKeyDirs = QDir( publicKeyBaseDir ).entryList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name ); + + QStringList keys; + keys.reserve( privateKeyDirs.size() + publicKeyDirs.size() ); + + for( const auto& privateKeyDir : privateKeyDirs ) + { + if( QFileInfo( keyFilePathFromType( privateKeyDir, m_keyTypePrivate) ).isFile() ) + { + keys.append( QStringLiteral("%1/%2").arg( privateKeyDir, m_keyTypePrivate ) ); + } + } + + for( const auto& publicKeyDir : publicKeyDirs ) + { + if( QFileInfo( keyFilePathFromType( publicKeyDir, m_keyTypePublic ) ).isFile() ) + { + keys.append( QStringLiteral("%1/%2").arg( publicKeyDir, m_keyTypePublic ) ); + } + } + + std::sort( keys.begin(), keys.end() ); + + return keys; +} + + + +bool AuthKeysManager::extractPublicFromPrivateKey( const QString& name ) +{ + if( VeyonCore::isAuthenticationKeyNameValid( name ) == false) + { + m_resultMessage = m_invalidKeyName; + return false; + } + + const auto privateKeyFileName = VeyonCore::filesystem().privateKeyPath( name ); + const auto publicKeyFileName = VeyonCore::filesystem().publicKeyPath( name ); + + if( QFileInfo::exists( privateKeyFileName ) == false ) + { + m_resultMessage = m_keyDoesNotExist; + return false; + } + + if( QFileInfo::exists( publicKeyFileName ) ) + { + m_resultMessage = m_keysAlreadyExists; + return false; + } + + const auto publicKey = CryptoCore::PrivateKey( privateKeyFileName ).toPublicKey(); + if( publicKey.isNull() || publicKey.isPublic() == false ) + { + m_resultMessage = tr( "Failed to convert private key to public key" ); + return false; + } + + return writePublicKeyFile( publicKey, publicKeyFileName ); +} + + + +bool AuthKeysManager::writePrivateKeyFile( const CryptoCore::PrivateKey& privateKey, const QString& privateKeyFileName ) +{ + if( VeyonCore::filesystem().ensurePathExists( QFileInfo( privateKeyFileName ).path() ) == false ) + { + m_resultMessage = tr( "Failed to create directory for private key file \"%1\"." ).arg( privateKeyFileName) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + if( privateKey.toPEMFile( privateKeyFileName ) == false ) + { + m_resultMessage = tr( "Failed to save private key in file \"%1\"!" ).arg( privateKeyFileName ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + if( setPrivateKeyFilePermissions( privateKeyFileName ) == false ) + { + m_resultMessage = tr( "Failed to set permissions for private key file \"%1\"!" ).arg( privateKeyFileName ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + return true; +} + + + +bool AuthKeysManager::writePublicKeyFile( const CryptoCore::PublicKey& publicKey, const QString& publicKeyFileName ) +{ + if( VeyonCore::filesystem().ensurePathExists( QFileInfo( publicKeyFileName ).path() ) == false ) + { + m_resultMessage = tr( "Failed to create directory for public key file \"%1\"." ).arg( publicKeyFileName ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + if( publicKey.toPEMFile( publicKeyFileName ) == false ) + { + m_resultMessage = tr( "Failed to save public key in file \"%1\"!" ).arg( publicKeyFileName ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + if( setPublicKeyFilePermissions( publicKeyFileName ) == false ) + { + m_resultMessage = tr( "Failed to set permissions for public key file \"%1\"!" ).arg( publicKeyFileName ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + return true; +} + + + +QString AuthKeysManager::detectKeyType( const QString& keyFile ) +{ + const auto privateKey = CryptoCore::PrivateKey( keyFile ); + if( privateKey.isNull() == false && privateKey.isPrivate() ) + { + return m_keyTypePrivate; + } + + const auto publicKey = CryptoCore::PublicKey( keyFile ); + if( publicKey.isNull() == false && publicKey.isPublic() ) + { + return m_keyTypePublic; + } + + return {}; +} + + + +bool AuthKeysManager::setAccessGroup( const QString& key, const QString& group ) +{ + const auto nameAndType = key.split( QLatin1Char('/') ); + const auto name = nameAndType.value( 0 ); + const auto type = nameAndType.value( 1 ); + + if( checkKey( name, type ) == false ) + { + return false; + } + + const auto keyFileName = keyFilePathFromType( name, type ); + + if( VeyonCore::platform().filesystemFunctions().setFileOwnerGroup( keyFileName, group ) == false ) + { + m_resultMessage = tr( "Failed to set owner of key file \"%1\" to \"%2\"." ). + arg( keyFileName, group ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + if( VeyonCore::platform().filesystemFunctions(). + setFileOwnerGroupPermissions( keyFileName, QFile::ReadOwner | QFile::ReadGroup ) == false ) + { + m_resultMessage = tr( "Failed to set permissions for key file \"%1\"." ).arg( keyFileName ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + m_resultMessage = tr( "Key \"%1\" is now accessible by user group \"%2\"." ).arg( key, group ); + + return true; +} + + + +QString AuthKeysManager::accessGroup( const QString& key ) +{ + const auto nameAndType = key.split( QLatin1Char('/') ); + const auto name = nameAndType.value( 0 ); + const auto type = nameAndType.value( 1 ); + + if( checkKey( name, type, false ) == false ) + { + return {}; + } + + return VeyonCore::platform().filesystemFunctions().fileOwnerGroup( keyFilePathFromType( name, type ) ); +} + + + +QString AuthKeysManager::keyPairId( const QString& key ) +{ + const auto nameAndType = key.split( QLatin1Char('/') ); + const auto name = nameAndType.value( 0 ); + const auto type = nameAndType.value( 1 ); + + if( checkKey( name, type ) == false ) + { + return tr(""); + } + + const auto keyFileName = keyFilePathFromType( name, type ); + + const auto privateKey = CryptoCore::PrivateKey( keyFileName ); + if( privateKey.isNull() == false && privateKey.isPrivate() ) + { + return QStringLiteral("%1").arg( qHash( privateKey.toPublicKey().toDER() ), 8, 16, QLatin1Char('0') ); + } + + const auto publicKey = CryptoCore::PublicKey( keyFileName ); + if( publicKey.isNull() == false && publicKey.isPublic() ) + { + return QStringLiteral("%1").arg( qHash( publicKey.toDER() ), 8, 16, QLatin1Char('0') ); + } + + return QStringLiteral("???"); +} + + + +QString AuthKeysManager::exportedKeyFileName( const QString& name, const QString& type ) +{ + return QStringLiteral("%1_%2_key.pem").arg( name, type ); +} + + + +QString AuthKeysManager::keyNameFromExportedKeyFile( const QString& keyFile ) +{ + QRegExp rx( QStringLiteral("^(.*)_(.*)_key.pem$") ); + + if( rx.indexIn( QFileInfo( keyFile ).fileName() ) == 0 ) + { + return rx.cap( 1 ); + } + + return {}; +} + + + +bool AuthKeysManager::checkKey( const QString& name, const QString& type, bool checkIsReadable ) +{ + if( VeyonCore::isAuthenticationKeyNameValid( name ) == false ) + { + m_resultMessage = m_invalidKeyName; + return false; + } + + const auto keyFileName = keyFilePathFromType( name, type ); + + if( keyFileName.isEmpty() ) + { + m_resultMessage = m_invalidKeyType; + return false; + } + + QFileInfo keyFileInfo( keyFileName ); + + if( keyFileInfo.exists() == false ) + { + m_resultMessage = m_keyDoesNotExist; + return false; + } + + if( checkIsReadable && keyFileInfo.isReadable() == false ) + { + m_resultMessage = tr( "Failed to read key file." ) + QLatin1Char(' ') + m_checkPermissions; + return false; + } + + return true; +} + + + +QString AuthKeysManager::keyFilePathFromType( const QString& name, const QString& type ) const +{ + if( type == m_keyTypePrivate ) + { + return VeyonCore::filesystem().privateKeyPath( name ); + } + else if( type == m_keyTypePublic ) + { + return VeyonCore::filesystem().publicKeyPath( name ); + } + + return {}; +} + + + +bool AuthKeysManager::setKeyFilePermissions( const QString& name, const QString& type ) const +{ + const auto keyFilePath = keyFilePathFromType( name, type ); + + if( type == m_keyTypePrivate ) + { + return setPrivateKeyFilePermissions( keyFilePath ); + } + else if( type == m_keyTypePublic ) + { + return setPublicKeyFilePermissions( keyFilePath ); + } + + return false; +} + + + +bool AuthKeysManager::setPrivateKeyFilePermissions( const QString& fileName ) const +{ + return QFile::setPermissions( fileName, QFile::ReadOwner | QFile::ReadUser | QFile::ReadGroup ); +} + + + +bool AuthKeysManager::setPublicKeyFilePermissions( const QString& fileName ) const +{ + return QFile::setPermissions( fileName, QFile::ReadOwner | QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther ); +} diff --git a/plugins/authkeys/AuthKeysManager.h b/plugins/authkeys/AuthKeysManager.h new file mode 100644 index 0000000..d73c370 --- /dev/null +++ b/plugins/authkeys/AuthKeysManager.h @@ -0,0 +1,78 @@ +/* + * AuthKeysManager.h - declaration of AuthKeysManager class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CryptoCore.h" + +class AuthKeysManager : public QObject +{ + Q_OBJECT +public: + explicit AuthKeysManager( QObject* parent = nullptr ); + ~AuthKeysManager() override = default; + + const QString& resultMessage() const + { + return m_resultMessage; + } + + bool createKeyPair( const QString& name ); + bool deleteKey( const QString& name, const QString& type ); + bool exportKey( const QString& name, const QString& type, const QString& outputFile, bool overwriteExisting ); + bool importKey( const QString& name, const QString& type, const QString& inputFile ); + QStringList listKeys(); + bool extractPublicFromPrivateKey( const QString& name ); + + bool writePrivateKeyFile( const CryptoCore::PrivateKey& privateKey, const QString& privateKeyFileName ); + bool writePublicKeyFile( const CryptoCore::PublicKey& publicKey, const QString& publicKeyFileName ); + + QString detectKeyType( const QString& keyFile ); + + bool setAccessGroup( const QString& key, const QString& group ); + QString accessGroup( const QString& key ); + + QString keyPairId( const QString& key ); + + static QString exportedKeyFileName( const QString& name, const QString& type ); + static QString keyNameFromExportedKeyFile( const QString& keyFile ); + +private: + bool checkKey( const QString& name, const QString& type, bool checkIsReadable = true ); + + QString keyFilePathFromType( const QString& name, const QString& type ) const; + bool setKeyFilePermissions( const QString& name, const QString& type ) const; + bool setPrivateKeyFilePermissions( const QString& fileName ) const; + bool setPublicKeyFilePermissions( const QString& fileName ) const; + + const QString m_keyTypePrivate; + const QString m_keyTypePublic; + const QString m_checkPermissions; + const QString m_invalidKeyName; + const QString m_invalidKeyType; + const QString m_keyDoesNotExist; + const QString m_keysAlreadyExists; + QString m_resultMessage; + +}; diff --git a/plugins/authkeys/AuthKeysPlugin.cpp b/plugins/authkeys/AuthKeysPlugin.cpp new file mode 100644 index 0000000..c171736 --- /dev/null +++ b/plugins/authkeys/AuthKeysPlugin.cpp @@ -0,0 +1,335 @@ +/* + * AuthKeysPlugin.cpp - implementation of AuthKeysPlugin class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "AuthKeysConfigurationPage.h" +#include "AuthKeysPlugin.h" +#include "AuthKeysManager.h" + + +AuthKeysPlugin::AuthKeysPlugin( QObject* parent ) : + QObject( parent ), + m_commands( { +{ QStringLiteral("create"), tr( "Create new authentication key pair" ) }, +{ QStringLiteral("delete"), tr( "Delete authentication key" ) }, +{ QStringLiteral("list"), tr( "List authentication keys" ) }, +{ QStringLiteral("import"), tr( "Import public or private key" ) }, +{ QStringLiteral("export"), tr( "Export public or private key" ) }, +{ QStringLiteral("extract"), tr( "Extract public key from existing private key" ) }, +{ QStringLiteral("setaccessgroup"), tr( "Set user group allowed to access a key" ) }, + } ) +{ +} + + + +QStringList AuthKeysPlugin::commands() const +{ + return m_commands.keys(); +} + + + +QString AuthKeysPlugin::commandHelp( const QString& command ) const +{ + return m_commands.value( command ); +} + + + +ConfigurationPage* AuthKeysPlugin::createConfigurationPage() +{ + return new AuthKeysConfigurationPage(); +} + + + +CommandLinePluginInterface::RunResult AuthKeysPlugin::handle_help( const QStringList& arguments ) +{ + const auto command = arguments.value( 0 ); + + const QMap commands = { + { QStringLiteral("create"), + QStringList( { QStringLiteral("<%1>").arg( tr("NAME") ), + tr( "This command creates a new authentication key pair with name and saves private and " + "public key to the configured key directories. The parameter must be a name for the key, which " + "may only contain letters." ) } ) }, + { QStringLiteral("delete"), + QStringList( { QStringLiteral("<%1>").arg( tr("KEY") ), + tr( "This command deletes the authentication key from the configured key directory. " + "Please note that a key can't be recovered once it has been deleted." ) } ) }, + { QStringLiteral("export"), + QStringList( { QStringLiteral("<%1> [<%2>]").arg( tr("KEY"), tr("FILE") ), + tr( "This command exports the authentication key to . " + "If is not specified a name will be constructed from name and type of ." ) } ) }, + { QStringLiteral("extract"), + QStringList( { QStringLiteral("<%1>").arg( tr("KEY") ), + tr( "This command extracts the public key part from the private key and saves it as the " + "corresponding public key. When setting up another master computer, it is therefore sufficient " + "to transfer the private key only. The public key can then be extracted." ) } ) }, + { QStringLiteral("import"), + QStringList( { QStringLiteral("<%1> [<%2>]").arg( tr( "KEY" ), tr( "FILE" ) ), + tr( "This command imports the authentication key from . " + "If is not specified a name will be constructed from name and type of ." ) } ) }, + { QStringLiteral("list"), + QStringList( { QStringLiteral("[details]"), + tr( "This command lists all available authentication keys in the configured key directory. " + "If the option \"%1\" is specified a table with key details will be displayed instead. " + "Some details might be missing if a key is not accessible e.g. due to the lack of read permissions." ).arg( QLatin1String("details") ) } ) }, + { QStringLiteral("setaccessgroup"), + QStringList( { QStringLiteral("<%1> <%2>").arg( tr("KEY"), tr("ACCESS GROUP") ), + tr( "This command adjusts file access permissions to such that only the " + "user group has read access to it." ) } ) }, + }; + + if( commands.contains( command ) ) + { + const auto& helpString = commands[command]; + print( QStringLiteral("\n%1 %2\n\n%3\n\n").arg( command, helpString[0], helpString[1] ) ); + + return NoResult; + } + + print( tr("Please specify the command to display help for!") ); + + return Unknown; +} + + + +CommandLinePluginInterface::RunResult AuthKeysPlugin::handle_setaccessgroup( const QStringList& arguments ) +{ + if( arguments.size() < 2 ) + { + return NotEnoughArguments; + } + + const auto key = arguments[0]; + const auto accessGroup = arguments[1]; + + AuthKeysManager manager; + if( manager.setAccessGroup( key, accessGroup ) == false ) + { + error( manager.resultMessage() ); + + return Failed; + } + + info( manager.resultMessage() ); + + return Successful; +} + + + +CommandLinePluginInterface::RunResult AuthKeysPlugin::handle_create( const QStringList& arguments ) +{ + if( arguments.isEmpty() ) + { + return NotEnoughArguments; + } + + AuthKeysManager manager; + if( manager.createKeyPair( arguments.first() ) == false ) + { + error( manager.resultMessage() ); + + return Failed; + } + + info( manager.resultMessage() ); + + return Successful; +} + + + +CommandLinePluginInterface::RunResult AuthKeysPlugin::handle_delete( const QStringList& arguments ) +{ + if( arguments.size() < 1 ) + { + return NotEnoughArguments; + } + + const auto nameAndType = arguments.first().split( QLatin1Char('/') ); + const auto name = nameAndType.value( 0 ); + const auto type = nameAndType.value( 1 ); + + AuthKeysManager manager; + if( manager.deleteKey( name, type ) == false ) + { + error( manager.resultMessage() ); + + return Failed; + } + + info( manager.resultMessage() ); + + return Successful; +} + + + +CommandLinePluginInterface::RunResult AuthKeysPlugin::handle_export( const QStringList& arguments ) +{ + if( arguments.size() < 1 ) + { + return NotEnoughArguments; + } + + const auto nameAndType = arguments[0].split( QLatin1Char('/') ); + const auto name = nameAndType.value( 0 ); + const auto type = nameAndType.value( 1 ); + + auto outputFile = arguments.value( 1 ); + + if( outputFile.isEmpty() ) + { + outputFile = AuthKeysManager::exportedKeyFileName( name, type ); + } + + AuthKeysManager manager; + if( manager.exportKey( name, type, outputFile, arguments.contains( QLatin1String("-f") ) ) == false ) + { + error( manager.resultMessage() ); + + return Failed; + } + + info( manager.resultMessage() ); + + return Successful; +} + + + +CommandLinePluginInterface::RunResult AuthKeysPlugin::handle_import( const QStringList& arguments ) +{ + if( arguments.size() < 1 ) + { + return NotEnoughArguments; + } + + const auto nameAndType = arguments[0].split( QLatin1Char('/') ); + const auto name = nameAndType.value( 0 ); + const auto type = nameAndType.value( 1 ); + + auto inputFile = arguments.value( 1 ); + + if( inputFile.isEmpty() ) + { + inputFile = AuthKeysManager::exportedKeyFileName( name, type ); + } + + AuthKeysManager manager; + if( manager.importKey( name, type, inputFile ) == false ) + { + error( manager.resultMessage() ); + + return Failed; + } + + info( manager.resultMessage() ); + + return Successful; +} + + + +CommandLinePluginInterface::RunResult AuthKeysPlugin::handle_list( const QStringList& arguments ) +{ + if( arguments.value( 0 ) == QLatin1String("details") ) + { + printAuthKeyTable(); + } + else + { + printAuthKeyList(); + } + + return NoResult; +} + + + +CommandLinePluginInterface::RunResult AuthKeysPlugin::handle_extract( const QStringList& arguments ) +{ + if( arguments.isEmpty() ) + { + return NotEnoughArguments; + } + + AuthKeysManager manager; + if( manager.extractPublicFromPrivateKey( arguments.first() ) == false ) + { + error( manager.resultMessage() ); + + return Failed; + } + + info( manager.resultMessage() ); + + return Successful; +} + + + +void AuthKeysPlugin::printAuthKeyTable() +{ + AuthKeysTableModel tableModel; + tableModel.reload(); + + CommandLineIO::TableHeader tableHeader( { tr("NAME"), tr("TYPE"), tr("PAIR ID"), tr("ACCESS GROUP") } ); + CommandLineIO::TableRows tableRows; + + tableRows.reserve( tableModel.rowCount() ); + + for( int i = 0; i < tableModel.rowCount(); ++i ) + { + tableRows.append( { authKeysTableData( tableModel, i, AuthKeysTableModel::ColumnKeyName ), + authKeysTableData( tableModel, i, AuthKeysTableModel::ColumnKeyType ), + authKeysTableData( tableModel, i, AuthKeysTableModel::ColumnKeyPairID ), + authKeysTableData( tableModel, i, AuthKeysTableModel::ColumnAccessGroup ) } ); + } + + CommandLineIO::printTable( CommandLineIO::Table( tableHeader, tableRows ) ); +} + + + +QString AuthKeysPlugin::authKeysTableData( const AuthKeysTableModel& tableModel, int row, int column ) +{ + return tableModel.data( tableModel.index( row, column ), Qt::DisplayRole ).toString(); +} + + + +void AuthKeysPlugin::printAuthKeyList() +{ + const auto keys = AuthKeysManager().listKeys(); + + for( const auto& key : keys ) + { + print( key ); + } +} diff --git a/plugins/authkeys/AuthKeysPlugin.h b/plugins/authkeys/AuthKeysPlugin.h new file mode 100644 index 0000000..29ea3eb --- /dev/null +++ b/plugins/authkeys/AuthKeysPlugin.h @@ -0,0 +1,110 @@ +/* + * AuthKeysPlugin.h - declaration of AuthKeysPlugin class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CommandLineIO.h" +#include "CommandLinePluginInterface.h" +#include "ConfigurationPagePluginInterface.h" + +class AuthKeysTableModel; + +class AuthKeysPlugin : public QObject, + CommandLinePluginInterface, + PluginInterface, + CommandLineIO, + ConfigurationPagePluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.AuthKeys") + Q_INTERFACES(PluginInterface + CommandLinePluginInterface + ConfigurationPagePluginInterface) +public: + explicit AuthKeysPlugin( QObject* parent = nullptr ); + ~AuthKeysPlugin() override = default; + + Plugin::Uid uid() const override + { + return QStringLiteral("4790bad8-4c56-40d5-8361-099a68f0c24b"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral( "AuthKeys" ); + } + + QString description() const override + { + return tr( "Command line support for managing authentication keys" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + QString commandLineModuleName() const override + { + return QStringLiteral( "authkeys" ); + } + + QString commandLineModuleHelp() const override + { + return tr( "Commands for managing authentication keys" ); + } + + QStringList commands() const override; + QString commandHelp( const QString& command ) const override; + + ConfigurationPage* createConfigurationPage() override; + +public Q_SLOTS: + CommandLinePluginInterface::RunResult handle_help( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_setaccessgroup( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_create( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_delete( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_export( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_import( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_list( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_extract( const QStringList& arguments ); + +private: + static void printAuthKeyTable(); + static QString authKeysTableData( const AuthKeysTableModel& tableModel, int row, int column ); + static void printAuthKeyList(); + + QMap m_commands; + +}; diff --git a/plugins/authkeys/AuthKeysTableModel.cpp b/plugins/authkeys/AuthKeysTableModel.cpp new file mode 100644 index 0000000..0c80b89 --- /dev/null +++ b/plugins/authkeys/AuthKeysTableModel.cpp @@ -0,0 +1,114 @@ +/* + * AuthKeysTableModel.cpp - implementation of AuthKeysTableModel class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "AuthKeysTableModel.h" +#include "AuthKeysManager.h" + +AuthKeysTableModel::AuthKeysTableModel( QObject* parent ) : + QAbstractTableModel( parent ), + m_manager( new AuthKeysManager( this ) ), + m_keys() +{ +} + + + +AuthKeysTableModel::~AuthKeysTableModel() +{ + delete m_manager; +} + + + +void AuthKeysTableModel::reload() +{ + beginResetModel(); + + m_keys = m_manager->listKeys(); + + endResetModel(); +} + + + +int AuthKeysTableModel::columnCount( const QModelIndex& parent ) const +{ + Q_UNUSED(parent) + + return ColumnCount; +} + + + +int AuthKeysTableModel::rowCount( const QModelIndex& parent ) const +{ + Q_UNUSED(parent) + + return m_keys.size(); +} + + + +QVariant AuthKeysTableModel::data( const QModelIndex& index, int role ) const +{ + if( index.isValid() == false || role != Qt::DisplayRole ) + { + return QVariant(); + } + + const auto& key = m_keys[index.row()]; + + switch( index.column() ) + { + case ColumnKeyName: return key.split( QLatin1Char('/') ).value( 0 ); + case ColumnKeyType: return key.split( QLatin1Char('/') ).value( 1 ); + case ColumnAccessGroup: return m_manager->accessGroup( key ); + case ColumnKeyPairID: return m_manager->keyPairId( key ); + default: break; + } + + return QVariant(); +} + + + +QVariant AuthKeysTableModel::headerData( int section, Qt::Orientation orientation, int role ) const +{ + if( orientation != Qt::Horizontal || role != Qt::DisplayRole ) + { + return QVariant(); + } + + switch( section ) + { + case ColumnKeyName: return tr( "Name" ); + case ColumnKeyType: return tr( "Type" ); + case ColumnAccessGroup: return tr( "Access group"); + case ColumnKeyPairID: return tr( "Pair ID"); + default: + break; + } + + return QVariant(); +} diff --git a/plugins/authkeys/AuthKeysTableModel.h b/plugins/authkeys/AuthKeysTableModel.h new file mode 100644 index 0000000..970d315 --- /dev/null +++ b/plugins/authkeys/AuthKeysTableModel.h @@ -0,0 +1,62 @@ +/* + * AuthKeysTableModel.h - declaration of AuthKeysTableModel class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +class AuthKeysManager; + +class AuthKeysTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + enum Columns { + ColumnKeyName, + ColumnKeyType, + ColumnKeyPairID, + ColumnAccessGroup, + ColumnCount + }; + + explicit AuthKeysTableModel( QObject* parent = nullptr ); + ~AuthKeysTableModel() override; + + void reload(); + + const QString& key( int row ) const + { + return m_keys[row]; + } + + int columnCount( const QModelIndex& parent = QModelIndex() ) const override; + int rowCount( const QModelIndex& parent = QModelIndex() ) const override; + QVariant data( const QModelIndex& index, int role ) const override; + QVariant headerData( int section, Qt::Orientation orientation, int role ) const override; + +private: + AuthKeysManager* m_manager; + QStringList m_keys; + +}; diff --git a/plugins/authkeys/CMakeLists.txt b/plugins/authkeys/CMakeLists.txt new file mode 100644 index 0000000..bb54487 --- /dev/null +++ b/plugins/authkeys/CMakeLists.txt @@ -0,0 +1,13 @@ +INCLUDE(BuildVeyonPlugin) + +build_veyon_plugin(authkeys + AuthKeysPlugin.cpp + AuthKeysConfigurationPage.cpp + AuthKeysConfigurationPage.ui + AuthKeysTableModel.cpp + AuthKeysManager.cpp + AuthKeysPlugin.h + AuthKeysConfigurationPage.h + AuthKeysTableModel.h + AuthKeysManager.h +) diff --git a/plugins/builtindirectory/BuiltinDirectory.cpp b/plugins/builtindirectory/BuiltinDirectory.cpp new file mode 100644 index 0000000..2abc1b7 --- /dev/null +++ b/plugins/builtindirectory/BuiltinDirectory.cpp @@ -0,0 +1,85 @@ +/* + * BuiltinDirectory.cpp - NetworkObjects from VeyonConfiguration + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "BuiltinDirectoryConfiguration.h" +#include "BuiltinDirectory.h" + + +BuiltinDirectory::BuiltinDirectory( BuiltinDirectoryConfiguration& configuration, QObject* parent ) : + NetworkObjectDirectory( parent ), + m_configuration( configuration ) +{ +} + + + +void BuiltinDirectory::update() +{ + m_configuration.reloadFromStore(); + + const auto networkObjects = m_configuration.networkObjects(); + + NetworkObjectUidList groupUids; + + for( const auto& networkObjectValue : networkObjects ) + { + const NetworkObject networkObject( networkObjectValue.toObject() ); + + if( networkObject.type() == NetworkObject::Type::Location ) + { + groupUids.append( networkObject.uid() ); // clazy:exclude=reserve-candidates + + addOrUpdateObject( networkObject, NetworkObject( NetworkObject::Type::Root ) ); + + updateLocation( networkObject, networkObjects ); + } + } + + removeObjects( NetworkObject( NetworkObject::Type::Root ), [groupUids]( const NetworkObject& object ) { + return object.type() == NetworkObject::Type::Location && groupUids.contains( object.uid() ) == false; } ); +} + + + +void BuiltinDirectory::updateLocation( const NetworkObject& locationObject, const QJsonArray& networkObjects ) +{ + NetworkObjectUidList computerUids; + + for( const auto& networkObjectValue : networkObjects ) + { + NetworkObject networkObject( networkObjectValue.toObject() ); + + if( networkObject.parentUid() == locationObject.uid() ) + { + computerUids.append( networkObject.uid() ); // clazy:exclude=reserve-candidates + addOrUpdateObject( networkObject, locationObject ); + } + } + + removeObjects( locationObject, [computerUids]( const NetworkObject& object ) { + return object.type() == NetworkObject::Type::Host && computerUids.contains( object.uid() ) == false; } ); +} diff --git a/plugins/builtindirectory/BuiltinDirectory.h b/plugins/builtindirectory/BuiltinDirectory.h new file mode 100644 index 0000000..5731792 --- /dev/null +++ b/plugins/builtindirectory/BuiltinDirectory.h @@ -0,0 +1,44 @@ +/* + * BuiltinDirectory.h - NetworkObjects from VeyonConfiguration + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "NetworkObjectDirectory.h" + +class BuiltinDirectoryConfiguration; + +class BuiltinDirectory : public NetworkObjectDirectory +{ + Q_OBJECT +public: + BuiltinDirectory( BuiltinDirectoryConfiguration& configuration, QObject* parent ); + + void update() override; + +private: + void updateLocation( const NetworkObject& locationObject, const QJsonArray& networkObjects ); + + BuiltinDirectoryConfiguration& m_configuration; + +}; diff --git a/plugins/builtindirectory/BuiltinDirectoryConfiguration.h b/plugins/builtindirectory/BuiltinDirectoryConfiguration.h new file mode 100644 index 0000000..9ff4617 --- /dev/null +++ b/plugins/builtindirectory/BuiltinDirectoryConfiguration.h @@ -0,0 +1,36 @@ +/* + * BuiltinDirectoryConfiguration.h - configuration values for BuiltinDirectory plugin + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "Configuration/Proxy.h" + +#define FOREACH_BUILTIN_DIRECTORY_CONFIG_PROPERTY(OP) \ + OP( BuiltinDirectoryConfiguration, m_configuration, QJsonArray, networkObjects, setNetworkObjects, "NetworkObjects", "BuiltinDirectory", QJsonArray(), Configuration::Property::Flag::Standard ) \ + /* legacy properties required for upgrade */ \ + OP( BuiltinDirectoryConfiguration, m_configuration, QJsonArray, legacyLocalDataNetworkObjects, setLegacyLocalDataNetworkObjects, "NetworkObjects", "LocalData", QJsonArray(), Configuration::Property::Flag::Legacy ) \ + +DECLARE_CONFIG_PROXY(BuiltinDirectoryConfiguration, FOREACH_BUILTIN_DIRECTORY_CONFIG_PROPERTY) diff --git a/plugins/builtindirectory/BuiltinDirectoryConfigurationPage.cpp b/plugins/builtindirectory/BuiltinDirectoryConfigurationPage.cpp new file mode 100644 index 0000000..86ba756 --- /dev/null +++ b/plugins/builtindirectory/BuiltinDirectoryConfigurationPage.cpp @@ -0,0 +1,271 @@ +/* + * BuiltinDirectoryConfigurationPage.cpp - implementation of BuiltinDirectoryConfigurationPage + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "BuiltinDirectoryConfiguration.h" +#include "BuiltinDirectoryConfigurationPage.h" +#include "Configuration/UiMapping.h" +#include "NetworkObjectModel.h" +#include "ObjectManager.h" + +#include "ui_BuiltinDirectoryConfigurationPage.h" + +BuiltinDirectoryConfigurationPage::BuiltinDirectoryConfigurationPage( BuiltinDirectoryConfiguration& configuration, QWidget* parent ) : + ConfigurationPage( parent ), + ui(new Ui::BuiltinDirectoryConfigurationPage), + m_configuration( configuration ) +{ + ui->setupUi(this); + + populateLocations(); + + connect( ui->locationTableWidget, &QTableWidget::currentItemChanged, + this, &BuiltinDirectoryConfigurationPage::populateComputers ); +} + + + +BuiltinDirectoryConfigurationPage::~BuiltinDirectoryConfigurationPage() +{ + delete ui; +} + + + +void BuiltinDirectoryConfigurationPage::resetWidgets() +{ + populateLocations(); + + ui->locationTableWidget->setCurrentCell( 0, 0 ); +} + + + +void BuiltinDirectoryConfigurationPage::connectWidgetsToProperties() +{ +} + + + +void BuiltinDirectoryConfigurationPage::applyConfiguration() +{ +} + + + +void BuiltinDirectoryConfigurationPage::addLocation() +{ + ObjectManager objectManager( m_configuration.networkObjects() ); + objectManager.add( NetworkObject( NetworkObject::Type::Location, tr( "New location" ), + {}, {}, {}, QUuid::createUuid() ) ); + m_configuration.setNetworkObjects( objectManager.objects() ); + + populateLocations(); + + ui->locationTableWidget->setCurrentCell( ui->locationTableWidget->rowCount()-1, 0 ); +} + + + +void BuiltinDirectoryConfigurationPage::updateLocation() +{ + auto currentLocationIndex = ui->locationTableWidget->currentIndex(); + if( currentLocationIndex.isValid() == false ) + { + return; + } + + ObjectManager objectManager( m_configuration.networkObjects() ); + objectManager.update( currentLocationObject() ); + m_configuration.setNetworkObjects( objectManager.objects() ); + + populateLocations(); + + ui->locationTableWidget->setCurrentIndex( currentLocationIndex ); +} + + + +void BuiltinDirectoryConfigurationPage::removeLocation() +{ + ObjectManager objectManager( m_configuration.networkObjects() ); + objectManager.remove( currentLocationObject().uid(), true ); + m_configuration.setNetworkObjects( objectManager.objects() ); + + populateLocations(); +} + + + +void BuiltinDirectoryConfigurationPage::addComputer() +{ + auto currentLocationUid = currentLocationObject().uid(); + if( currentLocationUid.isNull() ) + { + return; + } + + ObjectManager objectManager( m_configuration.networkObjects() ); + objectManager.add( NetworkObject( NetworkObject::Type::Host, tr( "New computer" ), + {}, {}, {}, + QUuid::createUuid(), + currentLocationUid ) ); + m_configuration.setNetworkObjects( objectManager.objects() ); + + populateComputers(); + + ui->computerTableWidget->setCurrentCell( ui->computerTableWidget->rowCount()-1, 0 ); +} + + + +void BuiltinDirectoryConfigurationPage::updateComputer() +{ + auto currentComputerIndex = ui->computerTableWidget->currentIndex(); + if( currentComputerIndex.isValid() == false ) + { + return; + } + + ObjectManager objectManager( m_configuration.networkObjects() ); + objectManager.update( currentComputerObject() ); + m_configuration.setNetworkObjects( objectManager.objects() ); + + populateComputers(); + + ui->computerTableWidget->setCurrentIndex( currentComputerIndex ); +} + + + +void BuiltinDirectoryConfigurationPage::removeComputer() +{ + ObjectManager objectManager( m_configuration.networkObjects() ); + objectManager.remove( currentComputerObject().uid() ); + m_configuration.setNetworkObjects( objectManager.objects() ); + + populateComputers(); +} + + + +void BuiltinDirectoryConfigurationPage::populateLocations() +{ + ui->locationTableWidget->setUpdatesEnabled( false ); + ui->locationTableWidget->clear(); + + int rowCount = 0; + + const auto networkObjects = m_configuration.networkObjects(); + for( const auto& networkObjectValue : networkObjects ) + { + const NetworkObject networkObject( networkObjectValue.toObject() ); + if( networkObject.type() == NetworkObject::Type::Location ) + { + auto item = new QTableWidgetItem( networkObject.name() ); + item->setData( NetworkObjectModel::UidRole, networkObject.uid() ); + ui->locationTableWidget->setRowCount( ++rowCount ); + ui->locationTableWidget->setItem( rowCount-1, 0, item ); + } + } + + ui->locationTableWidget->setUpdatesEnabled( true ); +} + + + +void BuiltinDirectoryConfigurationPage::populateComputers() +{ + auto parentUid = currentLocationObject().uid(); + + ui->computerTableWidget->setUpdatesEnabled( false ); + ui->computerTableWidget->setRowCount( 0 ); + + int rowCount = 0; + + const auto networkObjects = m_configuration.networkObjects(); + for( const auto& networkObjectValue : networkObjects ) + { + const NetworkObject networkObject( networkObjectValue.toObject() ); + + if( networkObject.type() == NetworkObject::Type::Host && + networkObject.parentUid() == parentUid ) + { + auto nameItem = new QTableWidgetItem( networkObject.name() ); + nameItem->setData( NetworkObjectModel::UidRole, networkObject.uid() ); + nameItem->setData( NetworkObjectModel::ParentUidRole, networkObject.parentUid() ); + + ui->computerTableWidget->setRowCount( rowCount+1 ); + ui->computerTableWidget->setItem( rowCount, 0, nameItem ); + ui->computerTableWidget->setItem( rowCount, 1, new QTableWidgetItem( networkObject.hostAddress() ) ); + ui->computerTableWidget->setItem( rowCount, 2, new QTableWidgetItem( networkObject.macAddress() ) ); + ++rowCount; + } + } + + ui->computerTableWidget->setUpdatesEnabled( true ); +} + + + +NetworkObject BuiltinDirectoryConfigurationPage::currentLocationObject() const +{ + const auto selectedLocation = ui->locationTableWidget->currentItem(); + if( selectedLocation ) + { + return NetworkObject( NetworkObject::Type::Location, + selectedLocation->text(), + {}, + {}, + {}, + selectedLocation->data( NetworkObjectModel::UidRole ).toUuid(), + selectedLocation->data( NetworkObjectModel::ParentUidRole ).toUuid() ); + } + + return NetworkObject(); +} + + + +NetworkObject BuiltinDirectoryConfigurationPage::currentComputerObject() const +{ + const int row = ui->computerTableWidget->currentRow(); + if( row >= 0 ) + { + auto nameItem = ui->computerTableWidget->item( row, 0 ); + auto hostAddressItem = ui->computerTableWidget->item( row, 1 ); + auto macAddressItem = ui->computerTableWidget->item( row, 2 ); + + return NetworkObject( NetworkObject::Type::Host, + nameItem->text(), + hostAddressItem->text().trimmed(), + macAddressItem->text().trimmed(), + {}, + nameItem->data( NetworkObjectModel::UidRole ).toUuid(), + nameItem->data( NetworkObjectModel::ParentUidRole ).toUuid() ); + } + + return NetworkObject(); +} diff --git a/plugins/builtindirectory/BuiltinDirectoryConfigurationPage.h b/plugins/builtindirectory/BuiltinDirectoryConfigurationPage.h new file mode 100644 index 0000000..1259f5a --- /dev/null +++ b/plugins/builtindirectory/BuiltinDirectoryConfigurationPage.h @@ -0,0 +1,66 @@ +/* + * BuiltinDirectoryConfigurationPage.h - header for the BuiltinDirectoryConfigurationPage class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "ConfigurationPage.h" +#include "NetworkObject.h" + +namespace Ui { +class BuiltinDirectoryConfigurationPage; +} + +class BuiltinDirectoryConfiguration; + +class BuiltinDirectoryConfigurationPage : public ConfigurationPage +{ + Q_OBJECT +public: + explicit BuiltinDirectoryConfigurationPage( BuiltinDirectoryConfiguration& configuration, QWidget* parent = nullptr ); + ~BuiltinDirectoryConfigurationPage() override; + + void resetWidgets() override; + void connectWidgetsToProperties() override; + void applyConfiguration() override; + +private Q_SLOTS: + void addLocation(); + void updateLocation(); + void removeLocation(); + void addComputer(); + void updateComputer(); + void removeComputer(); + +private: + void populateLocations(); + void populateComputers(); + + NetworkObject currentLocationObject() const; + NetworkObject currentComputerObject() const; + + Ui::BuiltinDirectoryConfigurationPage *ui; + + BuiltinDirectoryConfiguration& m_configuration; + +}; diff --git a/plugins/builtindirectory/BuiltinDirectoryConfigurationPage.ui b/plugins/builtindirectory/BuiltinDirectoryConfigurationPage.ui new file mode 100644 index 0000000..7d0c78b --- /dev/null +++ b/plugins/builtindirectory/BuiltinDirectoryConfigurationPage.ui @@ -0,0 +1,308 @@ + + + BuiltinDirectoryConfigurationPage + + + Locations & computers + + + + :/builtindirectory/builtindirectory.png:/builtindirectory/builtindirectory.png + + + + 0 + + + 0 + + + + + Builtin directory + + + + + + Computers + + + + + + + Locations + + + + + + + + + Add new computer + + + + + + + :/core/list-add.png:/core/list-add.png + + + + + + + Remove selected computer + + + + + + + :/core/edit-delete.png:/core/edit-delete.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QAbstractItemView::SelectRows + + + 150 + + + true + + + false + + + + Name + + + + + Host address/IP + + + + + MAC address + + + + + + + + + + Add new location + + + + + + + :/core/list-add.png:/core/list-add.png + + + + + + + Remove selected location + + + + + + + :/core/edit-delete.png:/core/edit-delete.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + false + + + true + + + false + + + + Locations + + + + + + + + + true + + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + true + + + + + + + + + + + + + + + addComputerButton + clicked() + BuiltinDirectoryConfigurationPage + addComputer() + + + 646 + 528 + + + 507 + 351 + + + + + removeComputerButton + clicked() + BuiltinDirectoryConfigurationPage + removeComputer() + + + 698 + 528 + + + 507 + 351 + + + + + removeLocationButton + clicked() + BuiltinDirectoryConfigurationPage + removeLocation() + + + 108 + 528 + + + 507 + 351 + + + + + addLocationButton + clicked() + BuiltinDirectoryConfigurationPage + addLocation() + + + 56 + 528 + + + 507 + 351 + + + + + locationTableWidget + cellChanged(int,int) + BuiltinDirectoryConfigurationPage + updateLocation() + + + 150 + 364 + + + 507 + 351 + + + + + computerTableWidget + cellChanged(int,int) + BuiltinDirectoryConfigurationPage + updateComputer() + + + 630 + 364 + + + 507 + 351 + + + + + + addLocation() + removeLocation() + addComputer() + removeComputer() + updateLocation() + updateComputer() + + diff --git a/plugins/builtindirectory/BuiltinDirectoryPlugin.cpp b/plugins/builtindirectory/BuiltinDirectoryPlugin.cpp new file mode 100644 index 0000000..214d072 --- /dev/null +++ b/plugins/builtindirectory/BuiltinDirectoryPlugin.cpp @@ -0,0 +1,770 @@ +/* + * BuiltinDirectoryPlugin.cpp - implementation of BuiltinDirectoryPlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "BuiltinDirectoryConfigurationPage.h" +#include "BuiltinDirectory.h" +#include "BuiltinDirectoryPlugin.h" +#include "CommandLineIO.h" +#include "ConfigurationManager.h" +#include "ObjectManager.h" +#include "PlatformFilesystemFunctions.h" +#include "VeyonConfiguration.h" + + +BuiltinDirectoryPlugin::BuiltinDirectoryPlugin( QObject* parent ) : + QObject( parent ), + m_configuration( &VeyonCore::config() ), + m_commands( { +{ QStringLiteral("help"), tr( "Show help for specific command" ) }, +{ addCommand(), tr( "Add a location or computer" ) }, +{ QStringLiteral("clear"), tr( "Clear all locations and computers" ) }, +{ QStringLiteral("dump"), tr( "Dump all or individual locations and computers" ) }, +{ QStringLiteral("list"), tr( "List all locations and computers" ) }, +{ removeCommand(), tr( "Remove a location or computer" ) }, +{ importCommand(), tr( "Import objects from given file" ) }, +{ exportCommand(), tr( "Export objects to given file" ) }, + } ) +{ +} + + + +void BuiltinDirectoryPlugin::upgrade( const QVersionNumber& oldVersion ) +{ + if( oldVersion < QVersionNumber( 1, 1 ) && + m_configuration.legacyLocalDataNetworkObjects().isEmpty() == false ) + { + m_configuration.setNetworkObjects( m_configuration.legacyLocalDataNetworkObjects() ); + } +} + + + +NetworkObjectDirectory *BuiltinDirectoryPlugin::createNetworkObjectDirectory( QObject* parent ) +{ + return new BuiltinDirectory( m_configuration, parent ); +} + + + +ConfigurationPage *BuiltinDirectoryPlugin::createConfigurationPage() +{ + return new BuiltinDirectoryConfigurationPage( m_configuration ); +} + + + +QStringList BuiltinDirectoryPlugin::commands() const +{ + return m_commands.keys(); +} + + + +QString BuiltinDirectoryPlugin::commandHelp( const QString& command ) const +{ + return m_commands.value( command ); +} + + + +CommandLinePluginInterface::RunResult BuiltinDirectoryPlugin::handle_help( const QStringList& arguments ) +{ + const auto command = arguments.value( 0 ); + + if( command == importCommand() ) + { + printUsage( commandLineModuleName(), importCommand(), { { tr("FILE"), {} } }, { + { tr("LOCATION"), locationArgument() }, + { tr("FORMAT-STRING-WITH-PLACEHOLDERS"), formatArgument() }, + { tr("REGULAR-EXPRESSION-WITH-PLACEHOLDER"), regexArgument() } } ); + + printDescription( tr("Imports objects from the specified text file using the given format string or " + "regular expression containing one or multiple placeholders. " + "Valid placeholders are: %1").arg( importExportPlaceholders().join(QLatin1Char(' ') ) ) ); + + printExamples( commandLineModuleName(), importCommand(), { + { tr( "Import simple CSV file to a single room" ), + { QStringLiteral("computers.csv"), + locationArgument(), exampleRoom(), + formatArgument(), QStringLiteral("\"%name%;%host%;%mac%\"") } }, + { tr( "Import CSV file with location name in first column" ), + { QStringLiteral("computers-with-rooms.csv"), + formatArgument(), QStringLiteral("\"%location%,%name%,%mac%\"") } }, + { tr( "Import text file with with key/value pairs using regular expressions" ), + { QStringLiteral("hostlist.txt"), + locationArgument(), exampleRoom(), + regexArgument(), QStringLiteral("\"^NAME:(%name%:.*)\\s+HOST:(%host%:.*)$\"") } }, + { tr( "Import arbitrarily formatted data" ), + { QStringLiteral("data.txt"), + regexArgument(), QStringLiteral("'^\"(%location%:[^\"]+)\";\"(%host%:[a-z\\d\\.]+)\".*$'") } } + } ); + + return NoResult; + } + else if( command == exportCommand() ) + { + printUsage( commandLineModuleName(), exportCommand(), { { tr("FILE"), {} } }, { + { tr("LOCATION"), locationArgument() }, + { tr("FORMAT-STRING-WITH-PLACEHOLDERS"), formatArgument() } } ); + + printDescription( tr("Exports objects to the specified text file using the given format string containing " + "one or multiple placeholders. " + "Valid placeholders are: %1").arg( importExportPlaceholders().join(QLatin1Char(' ') ) ) ); + + printExamples( commandLineModuleName(), exportCommand(), { + { tr( "Export all objects to a CSV file" ), + { QStringLiteral("objects.csv"), + formatArgument(), QStringLiteral("\"%type%;%name%;%host%;%mac%\"") } }, + { tr( "Export all computers in a specific location to a CSV file" ), + { QStringLiteral("computers.csv"), + formatArgument(), QStringLiteral("\"%name%;%host%;%mac%\"") } } + } ); + + return NoResult; + } + else if( command == addCommand() ) + { + printUsage( commandLineModuleName(), addCommand(), { { tr("TYPE"), {} }, { tr("NAME"), {} } }, { + { tr("HOST ADDRESS"), {} }, + { tr("MAC ADDRESS"), {} }, + { tr("PARENT"), {} } } ); + + printDescription( tr("Adds an object where %1 can be one of \"%2\" or \"%3\". %4 can be specified by name or UUID."). + arg( tr("TYPE"), typeLocation(), typeComputer(), tr("PARENT") ) ); + + printExamples( commandLineModuleName(), addCommand(), { + { tr( "Add a room" ), { typeLocation(), exampleRoom() } }, + { tr( "Add a computer to room %1" ).arg( exampleRoom() ), { typeComputer(), exampleComputer(), QStringLiteral("comp01.example.com"), QStringLiteral("11:22:33:44:55:66"), exampleRoom() } } + } ); + + return NoResult; + } + else if( command == removeCommand() ) + { + printUsage( commandLineModuleName(), removeCommand(), { { tr("OBJECT"), {} } } ); + + printDescription( tr("Removes the specified object from the directory. %1 can be specified by name or UUID. " + "Removing a location will also remove all related computers.").arg( tr("OBJECT") ) ); + + printExamples( commandLineModuleName(), removeCommand(), { + { tr( "Remove a computer by name" ), { exampleComputer() } }, + { tr( "Remove an object by UUID" ), { QStringLiteral("068914fc-0f87-45df-a5b9-099a2a6d9141") } } + } ); + + return NoResult; + } + + return Unknown; +} + + + +CommandLinePluginInterface::RunResult BuiltinDirectoryPlugin::handle_add( const QStringList& arguments ) +{ + if( arguments.count() < 2 ) + { + return NotEnoughArguments; + } + + NetworkObject object; + + const auto type = arguments[0]; + const auto name = arguments[1]; + + if( type == typeLocation() ) + { + object = NetworkObject( NetworkObject::Type::Location, name ); + } + else if( type == typeComputer() ) + { + auto hostAddress = arguments.value( 2 ); + if( hostAddress.isEmpty() ) + { + hostAddress = name; + } + const auto macAddress = arguments.value( 3 ); + const auto parent = findNetworkObject( arguments.value( 4 ) ); + object = NetworkObject( NetworkObject::Type::Host, name, hostAddress, macAddress, + {}, NetworkObject::Uid(), parent.isValid() ? parent.uid() : NetworkObject::Uid() ); + } + else + { + error( tr("Invalid type specified. Valid values are \"%1\" or \"%2\"." ).arg( typeComputer(), typeLocation() ) ); + return Failed; + } + + ObjectManager objectManager( m_configuration.networkObjects() ); + objectManager.add( object ); + m_configuration.setNetworkObjects( objectManager.objects() ); + + return saveConfiguration(); +} + + + +CommandLinePluginInterface::RunResult BuiltinDirectoryPlugin::handle_clear( const QStringList& arguments ) +{ + Q_UNUSED(arguments); + + m_configuration.setNetworkObjects( {} ); + + return saveConfiguration(); +} + + + +CommandLinePluginInterface::RunResult BuiltinDirectoryPlugin::handle_dump( const QStringList& arguments ) +{ + TableHeader tableHeader( { tr("Object UUID"), tr("Parent UUID"), tr("Type"), tr("Name"), tr("Host address"), tr("MAC address") } ); + TableRows tableRows; + + const auto objects = m_configuration.networkObjects(); + + tableRows.reserve( objects.size() ); + + if( arguments.isEmpty() ) + { + for( const auto& networkObjectValue : objects ) + { + tableRows.append( dumpNetworkObject( NetworkObject( networkObjectValue.toObject() ) ) ); + } + } + else + { + tableRows.append( dumpNetworkObject( findNetworkObject( arguments.first() ) ) ); + } + + printTable( Table( tableHeader, tableRows ) ); + + return NoResult; +} + + + +CommandLinePluginInterface::RunResult BuiltinDirectoryPlugin::handle_list( const QStringList& arguments ) +{ + if( arguments.isEmpty() ) + { + listObjects( m_configuration.networkObjects(), NetworkObject( NetworkObject::Type::None ) ); + } + else + { + const auto parents = BuiltinDirectory( m_configuration, this ).queryObjects( NetworkObject::Type::Location, + NetworkObject::Attribute::Name, + arguments.first() ); + + for( const auto& parent : parents ) + { + listObjects( m_configuration.networkObjects(), parent ); + } + } + + return NoResult; +} + + + +CommandLinePluginInterface::RunResult BuiltinDirectoryPlugin::handle_remove( const QStringList& arguments ) +{ + if( arguments.isEmpty() ) + { + return NotEnoughArguments; + } + + const auto object = findNetworkObject( arguments.first() ); + + if( object.isValid() ) + { + ObjectManager objectManager( m_configuration.networkObjects() ); + objectManager.remove( object.uid(), true ); + m_configuration.setNetworkObjects( objectManager.objects() ); + + return saveConfiguration(); + } + + error( tr( "Specified object not found." ) ); + + return Failed; +} + + + +CommandLinePluginInterface::RunResult BuiltinDirectoryPlugin::handle_import( const QStringList& arguments ) +{ + if( arguments.count() < 3 ) + { + return NotEnoughArguments; + } + + const auto& inputFileName = arguments.first(); + QFile inputFile( inputFileName ); + + if( inputFile.exists() == false ) + { + error( tr( "File \"%1\" does not exist!" ).arg( inputFileName ) ); + return Failed; + } + else if( inputFile.open( QFile::ReadOnly | QFile::Text ) == false ) + { + error( tr( "Can't open file \"%1\" for reading!" ).arg( inputFileName ) ); + return Failed; + } + + QString location; + QString formatString; + QString regularExpression; + + for( int i = 1; i < arguments.count(); i += 2 ) + { + if( i+1 >= arguments.count() ) + { + return NotEnoughArguments; + } + + const auto key = arguments[i]; + const auto value = arguments[i+1]; + if( key == locationArgument() ) + { + location = value; + } + else if( key == formatArgument() ) + { + formatString = value; + } + else if( key == regexArgument() ) + { + regularExpression = value; + } + else + { + error( tr( "Unknown argument \"%1\"." ).arg( key ) ); + return InvalidArguments; + } + } + + if( formatString.isEmpty() == false ) + { + regularExpression = formatString; + + const auto placeholders = importExportPlaceholders(); + + for( const auto& placeholder : placeholders ) + { + regularExpression.replace( placeholder, QStringLiteral("(%1:[^\\n\\r]*)").arg( placeholder ) ); + } + } + + if( regularExpression.isEmpty() == false ) + { + if( importFile( inputFile, regularExpression, location ) ) + { + return saveConfiguration(); + } + + return Failed; + } + + error( tr("No format string or regular expression specified!") ); + + return InvalidArguments; +} + + + +CommandLinePluginInterface::RunResult BuiltinDirectoryPlugin::handle_export( const QStringList& arguments ) +{ + if( arguments.count() < 3 ) + { + return NotEnoughArguments; + } + + const auto& outputFileName = arguments.first(); + QFile outputFile( outputFileName ); + + if( VeyonCore::platform().filesystemFunctions().openFileSafely( + &outputFile, + QFile::WriteOnly | QFile::Truncate | QFile::Text, + QFile::ReadOwner | QFile::WriteOwner ) == false ) + { + error( tr( "Can't open file \"%1\" for writing!" ).arg( outputFileName ) ); + return Failed; + } + + QString location; + QString formatString; + + for( int i = 1; i < arguments.count(); i += 2 ) + { + if( i+1 >= arguments.count() ) + { + return NotEnoughArguments; + } + + const auto key = arguments[i]; + const auto value = arguments[i+1]; + if( key == locationArgument() ) + { + location = value; + } + else if( key == formatArgument() ) + { + formatString = value; + } + else + { + error( tr( "Unknown argument \"%1\"." ).arg( key ) ); + return InvalidArguments; + } + } + + if( formatString.isEmpty() == false ) + { + if( exportFile( outputFile, formatString, location ) ) + { + return Successful; + } + + return Failed; + } + + error( tr("No format string specified!") ); + + return InvalidArguments; +} + + + +void BuiltinDirectoryPlugin::listObjects( const QJsonArray& objects, const NetworkObject& parent ) +{ + for( const auto& networkObjectValue : objects ) + { + const NetworkObject networkObject( networkObjectValue.toObject() ); + + if( ( parent.type() == NetworkObject::Type::None && networkObject.parentUid().isNull() ) || + networkObject.parentUid() == parent.uid() ) + { + printf( "%s\n", qUtf8Printable( listNetworkObject( networkObject ) ) ); + listObjects( objects, networkObject ); + } + } +} + + + +QStringList BuiltinDirectoryPlugin::dumpNetworkObject( const NetworkObject& object ) +{ + return { VeyonCore::formattedUuid( object.uid() ), + VeyonCore::formattedUuid( object.parentUid() ), + networkObjectTypeName( object ), + object.name(), + object.hostAddress(), + object.macAddress() }; +} + + + +QString BuiltinDirectoryPlugin::listNetworkObject( const NetworkObject& object ) +{ + switch( object.type() ) + { + case NetworkObject::Type::Location: + return tr( "Location \"%1\"" ).arg( object.name() ); + case NetworkObject::Type::Host: + return QLatin1Char('\t') + + tr( "Computer \"%1\" (host address: \"%2\" MAC address: \"%3\")" ). + arg( object.name(), object.hostAddress(), object.macAddress() ); + default: + break; + } + + return tr( "Unclassified object \"%1\" with ID \"%2\"" ).arg( object.name(), object.uid().toString() ); +} + + + +QString BuiltinDirectoryPlugin::networkObjectTypeName( const NetworkObject& object ) +{ + switch( object.type() ) + { + case NetworkObject::Type::None: return tr( "None" ); + case NetworkObject::Type::Location: return typeNameLocation(); + case NetworkObject::Type::Host: return typeNameComputer(); + case NetworkObject::Type::Root: return typeNameRoot(); + default: + break; + } + + return tr( "Invalid" ); +} + + + +NetworkObject::Type BuiltinDirectoryPlugin::parseNetworkObjectType( const QString& typeName ) +{ + if( typeName == typeNameLocation() ) + { + return NetworkObject::Type::Location; + } + else if( typeName == typeNameComputer() ) + { + return NetworkObject::Type::Host; + } + else if( typeName == typeNameRoot() ) + { + return NetworkObject::Type::Root; + } + + return NetworkObject::Type::None; +} + + + +CommandLinePluginInterface::RunResult BuiltinDirectoryPlugin::saveConfiguration() +{ + ConfigurationManager configurationManager; + if( configurationManager.saveConfiguration() == false ) + { + error( configurationManager.errorString() ); + return Failed; + } + + return Successful; +} + + + +bool BuiltinDirectoryPlugin::importFile( QFile& inputFile, + const QString& regExWithPlaceholders, + const QString& location ) +{ + int lineCount = 0; + QMap networkObjects; + while( inputFile.atEnd() == false ) + { + ++lineCount; + + auto targetLocation = location; + + const auto line = inputFile.readLine(); + const auto networkObject = toNetworkObject( QString::fromUtf8( line ), regExWithPlaceholders, targetLocation ); + + if( networkObject.isValid() ) + { + networkObjects[targetLocation].append( networkObject ); + } + else + { + error( tr( "Error while parsing line %1." ).arg( lineCount ) ); + return false; + } + } + + ObjectManager objectManager( m_configuration.networkObjects() ); + + for( auto it = networkObjects.constBegin(), end = networkObjects.constEnd(); it != end; ++it ) + { + auto parentLocation = objectManager.findByName( it.key() ); + auto parentLocationUid = parentLocation.uid(); + if( it.key().isEmpty() ) + { + parentLocationUid = QUuid(); + } + else if( parentLocation.isValid() == false ) + { + parentLocation = NetworkObject( NetworkObject::Type::Location, it.key() ); + objectManager.update( parentLocation, true ); + parentLocationUid = parentLocation.uid(); + } + + for( const NetworkObject& networkObject : qAsConst(it.value()) ) + { + objectManager.update( NetworkObject( networkObject.type(), + networkObject.name(), + networkObject.hostAddress(), + networkObject.macAddress(), + {}, NetworkObject::Uid(), + parentLocationUid ), true ); + } + } + + m_configuration.setNetworkObjects( objectManager.objects() ); + + return true; +} + + + +bool BuiltinDirectoryPlugin::exportFile( QFile& outputFile, const QString& formatString, const QString& location ) +{ + ObjectManager objectManager( m_configuration.networkObjects() ); + + const auto& networkObjects = objectManager.objects(); + + NetworkObject locationObject; + if( location.isEmpty() == false ) + { + locationObject = objectManager.findByName( location ); + } + + QStringList lines; + lines.reserve( networkObjects.count() ); + + for( auto it = networkObjects.constBegin(), end = networkObjects.constEnd(); it != end; ++it ) + { + const NetworkObject networkObject( it->toObject() ); + + auto currentLocation = location; + + if( locationObject.isValid() ) + { + if( networkObject.parentUid() != locationObject.uid() ) + { + continue; + } + } + else + { + currentLocation = objectManager.findByUid( networkObject.parentUid() ).name(); + } + + lines.append( toFormattedString( networkObject, formatString, currentLocation ) ); + } + + // append empty string to generate final newline at end of file + lines += QString(); + + outputFile.write( lines.join( QStringLiteral("\n") ).toUtf8() ); + + return true; +} + + + +NetworkObject BuiltinDirectoryPlugin::findNetworkObject( const QString& uidOrName ) const +{ + const ObjectManager objectManager( m_configuration.networkObjects() ); + + if( QUuid( uidOrName ).isNull() ) + { + return objectManager.findByName( uidOrName ); + } + + return objectManager.findByUid( uidOrName ); +} + + + +NetworkObject BuiltinDirectoryPlugin::toNetworkObject( const QString& line, const QString& regExWithPlaceholders, + QString& location ) +{ + QStringList placeholders; + QRegExp varDetectionRX( QStringLiteral("\\((%\\w+%):[^)]+\\)") ); + int pos = 0; + + while( ( pos = varDetectionRX.indexIn( regExWithPlaceholders, pos ) ) != -1 ) + { + placeholders.append( varDetectionRX.cap(1) ); + pos += varDetectionRX.matchedLength(); + } + + QString rxString = regExWithPlaceholders; + for( const auto& var : qAsConst(placeholders) ) + { + rxString.replace( QStringLiteral("%1:").arg( var ), QString() ); + } + + QRegExp rx( rxString ); + if( rx.indexIn( line ) != -1 ) + { + auto objectType = NetworkObject::Type::Host; + const auto typeIndex = placeholders.indexOf( QStringLiteral("%type%") ); + if( typeIndex != -1 ) + { + objectType = parseNetworkObjectType( rx.cap( 1 + typeIndex ) ); + } + + const auto locationIndex = placeholders.indexOf( QStringLiteral("%location%") ); + const auto nameIndex = placeholders.indexOf( QStringLiteral("%name%") ); + const auto hostIndex = placeholders.indexOf( QStringLiteral("%host%") ); + const auto macIndex = placeholders.indexOf( QStringLiteral("%mac%") ); + + auto name = ( nameIndex != -1 ) ? rx.cap( 1 + nameIndex ).trimmed() : QString(); + auto host = ( hostIndex != -1 ) ? rx.cap( 1 + hostIndex ).trimmed() : QString(); + auto mac = ( macIndex != -1 ) ? rx.cap( 1 + macIndex ).trimmed() : QString(); + + if( objectType == NetworkObject::Type::Location ) + { + return NetworkObject( NetworkObject::Type::Location, name ); + } + + if( location.isEmpty() && locationIndex != -1 ) + { + location = rx.cap( 1 + locationIndex ).trimmed(); + } + + if( host.isEmpty() ) + { + host = name; + } + else if( name.isEmpty() ) + { + name = host; + } + return NetworkObject( NetworkObject::Type::Host, name, host, mac ); + } + + return NetworkObject( NetworkObject::Type::None ); +} + + + +QString BuiltinDirectoryPlugin::toFormattedString( const NetworkObject& networkObject, + const QString& formatString, + const QString& location ) +{ + return QString( formatString ). + replace( QStringLiteral("%location%"), location ). + replace( QStringLiteral("%name%"), networkObject.name() ). + replace( QStringLiteral("%host%"), networkObject.hostAddress() ). + replace( QStringLiteral("%mac%"), networkObject.macAddress() ). + replace( QStringLiteral("%type%"), networkObjectTypeName( networkObject ) ); +} + + + +QStringList BuiltinDirectoryPlugin::importExportPlaceholders() +{ + return { QStringLiteral("%location%"), + QStringLiteral("%name%"), + QStringLiteral("%host%"), + QStringLiteral("%mac%"), + QStringLiteral("%type%") }; +} + + + +IMPLEMENT_CONFIG_PROXY(BuiltinDirectoryConfiguration) diff --git a/plugins/builtindirectory/BuiltinDirectoryPlugin.h b/plugins/builtindirectory/BuiltinDirectoryPlugin.h new file mode 100644 index 0000000..236c434 --- /dev/null +++ b/plugins/builtindirectory/BuiltinDirectoryPlugin.h @@ -0,0 +1,214 @@ +/* + * BuiltinDirectoryPlugin.h - declaration of BuiltinDirectoryPlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CommandLinePluginInterface.h" +#include "CommandLineIO.h" +#include "ConfigurationPagePluginInterface.h" +#include "BuiltinDirectoryConfiguration.h" +#include "NetworkObject.h" +#include "NetworkObjectDirectoryPluginInterface.h" + +class QFile; + +class BuiltinDirectoryPlugin : public QObject, + PluginInterface, + NetworkObjectDirectoryPluginInterface, + ConfigurationPagePluginInterface, + CommandLinePluginInterface, + CommandLineIO +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.BuiltinDirectory") + Q_INTERFACES(PluginInterface + NetworkObjectDirectoryPluginInterface + ConfigurationPagePluginInterface + CommandLinePluginInterface) +public: + explicit BuiltinDirectoryPlugin( QObject* paren = nullptr ); + ~BuiltinDirectoryPlugin() override = default; + + Plugin::Uid uid() const override + { + return QStringLiteral("14bacaaa-ebe5-449c-b881-5b382f952571"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral( "BuiltinDirectory" ); + } + + QString description() const override + { + return tr( "Network object directory which stores objects in local configuration" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + Plugin::Flags flags() const override + { + return Plugin::ProvidesDefaultImplementation; + } + + void upgrade( const QVersionNumber& oldVersion ) override; + + QString directoryName() const override + { + return tr( "Builtin (computers and locations in local configuration)" ); + } + + NetworkObjectDirectory* createNetworkObjectDirectory( QObject* parent ) override; + + ConfigurationPage* createConfigurationPage() override; + + QString commandLineModuleName() const override + { + return QStringLiteral( "networkobjects" ); + } + + QString commandLineModuleHelp() const override + { + return tr( "Commands for managing the builtin network object directory" ); + } + + QStringList commands() const override; + QString commandHelp( const QString& command ) const override; + +public Q_SLOTS: + CommandLinePluginInterface::RunResult handle_help( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_add( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_clear( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_dump( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_list( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_remove( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_import( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_export( const QStringList& arguments ); + +private: + void listObjects( const QJsonArray& objects, const NetworkObject& parent ); + static QStringList dumpNetworkObject( const NetworkObject& object ); + static QString listNetworkObject( const NetworkObject& object ); + static QString networkObjectTypeName( const NetworkObject& object ); + static NetworkObject::Type parseNetworkObjectType( const QString& typeName ); + + CommandLinePluginInterface::RunResult saveConfiguration(); + + bool importFile( QFile& inputFile, const QString& regExWithVariables, const QString& location ); + bool exportFile( QFile& outputFile, const QString& formatString, const QString& location ); + + NetworkObject findNetworkObject( const QString& uidOrName ) const; + + static NetworkObject toNetworkObject( const QString& line, const QString& regExWithVariables, QString& location ); + static QString toFormattedString( const NetworkObject& networkObject, const QString& formatString, const QString& location ); + + static QStringList importExportPlaceholders(); + + static QString typeNameLocation() + { + return tr( "Location" ); + } + + static QString typeNameComputer() + { + return tr( "Computer" ); + } + + static QString typeNameRoot() + { + return tr( "Root" ); + } + + static QString importCommand() + { + return QStringLiteral("import"); + } + + static QString exportCommand() + { + return QStringLiteral("export"); + } + + static QString addCommand() + { + return QStringLiteral("add"); + } + + static QString removeCommand() + { + return QStringLiteral("remove"); + } + + static QString locationArgument() + { + return QStringLiteral("location"); + } + + static QString formatArgument() + { + return QStringLiteral("format"); + } + + static QString regexArgument() + { + return QStringLiteral("regex"); + } + + static QString typeLocation() + { + return QStringLiteral("location"); + } + + static QString typeComputer() + { + return QStringLiteral("computer"); + } + + static QString exampleRoom() + { + return tr( "\"Room 01\"" ); + } + + static QString exampleComputer() + { + return tr( "\"Computer 01\"" ); + } + + BuiltinDirectoryConfiguration m_configuration; + QMap m_commands; + +}; diff --git a/plugins/builtindirectory/CMakeLists.txt b/plugins/builtindirectory/CMakeLists.txt new file mode 100644 index 0000000..84e09b1 --- /dev/null +++ b/plugins/builtindirectory/CMakeLists.txt @@ -0,0 +1,13 @@ +INCLUDE(BuildVeyonPlugin) + +build_veyon_plugin(builtindirectory + BuiltinDirectoryPlugin.cpp + BuiltinDirectoryConfigurationPage.cpp + BuiltinDirectoryConfigurationPage.ui + BuiltinDirectory.cpp + BuiltinDirectoryPlugin.h + BuiltinDirectoryConfiguration.h + BuiltinDirectoryConfigurationPage.h + BuiltinDirectory.h + builtindirectory.qrc +) diff --git a/plugins/builtindirectory/builtindirectory.png b/plugins/builtindirectory/builtindirectory.png new file mode 100644 index 0000000000000000000000000000000000000000..fc3a4317771a57197a616c58bd0d9f78f353b0f2 GIT binary patch literal 1654 zcmbtUdo+}39De7*Fo;qvQ7$EPv7?k?Vib*G%#2~ESf(x0g=z{J=TtK;+a_z(X*$7{7S`eM$1`tRTT23CLpompcR#~E|wshI@ z6&hOFI=ZX%3=G$-HCktEV!D3A#!Y58^UYfvqncx%mZ!#dq)jRaRN`xaMhH{j-L~=P#OHzG`W0 zYwr-e?(FLB=@s_%4~Pcez5np>)6nPPkC9{f{-{6vyniIw+qrXI z4oYo5D4lV2)lZ!9bivho3(Brl z#E#&GEyZueGk%tWYe|?|$F)RkO~0IA{0IL*Y2VnVDLrYm+cYE2Nb=|n{>GiS;=YGt z=5qy_{>6pT3f=6c;o~M&b-`A)0_xn@t*-R^;7S4Jn4~mn+8;GL$+tKamh2=Qxs1mo z^ooZjk+Wy-RZBz#Vb6NEr4{XHuO=8;)q2gDHnQv9Tp#b4oL;L{X3_0D7gG?%XiDWi zW~5cICs?X{F3{?}YAHP=-a5j>B@f2TwP*-qbM-K3A|f~97ye;RpYN#r-m~3V-14C{ z9O;SZkK)i~J0|;JzWGmWj7DTka69+4gG6wzlkImiWj3WJ{I+dCu-1hHDKT!JNjWaX ze~wYDH1)M)s%X3S`6Rsut*65H^-GOyaHDkmfSjlN{txX}1zA!2xagk5Z9);Qwb_Vr zM8e$Z{X_{{=QDb=aT~d+qGIH?FL>uNl?ncVo}DAz@sYl6Hd1`%18Mp|Xj4|pCX@7j zb&*u_e)J_9GgDVZfoZGLq}O;#LIoBi8}7duPh%@T&)gto0D_J|-jw!{@uwf9L7s;Rs3)+5l z)7HONGIA)iwCz*t<8%50c8R4*RT|xLuT*u-?=HJUA?a#UK+w~a%J{-*?ea3Q5-S8< z)j{DrJd>yCqqE96$r1!qAFp0QQLcLY&8@Gbl6 z%jK9^c^<(0IE=ur7 zQZDwi?CRI%`5aIBpd(2Q&Nmq9!W#NSZVkHt+J8`>bZ zFGr{W#DEPb(YqLPU#@E1>YPzDQ<3)`lk=n#$;7I~bCCMr-{i6#K_@75sG aHP4oOs()r8_5Aq!U!UYavCrSd4F3nV@VICI literal 0 HcmV?d00001 diff --git a/plugins/builtindirectory/builtindirectory.qrc b/plugins/builtindirectory/builtindirectory.qrc new file mode 100644 index 0000000..d3a90fc --- /dev/null +++ b/plugins/builtindirectory/builtindirectory.qrc @@ -0,0 +1,5 @@ + + + builtindirectory.png + + diff --git a/plugins/config/CMakeLists.txt b/plugins/config/CMakeLists.txt new file mode 100644 index 0000000..b11a408 --- /dev/null +++ b/plugins/config/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildVeyonPlugin) + +build_veyon_plugin(config ConfigCommandLinePlugin.cpp ConfigCommandLinePlugin.h) diff --git a/plugins/config/ConfigCommandLinePlugin.cpp b/plugins/config/ConfigCommandLinePlugin.cpp new file mode 100644 index 0000000..2dfc59a --- /dev/null +++ b/plugins/config/ConfigCommandLinePlugin.cpp @@ -0,0 +1,363 @@ +/* + * ConfigCommandLinePlugin.cpp - implementation of ConfigCommandLinePlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "CommandLineIO.h" +#include "Configuration/JsonStore.h" +#include "ConfigCommandLinePlugin.h" +#include "ConfigurationManager.h" +#include "CryptoCore.h" + + +ConfigCommandLinePlugin::ConfigCommandLinePlugin( QObject* parent ) : + QObject( parent ), + m_commands( { +{ QStringLiteral("clear"), tr( "Clear system-wide Veyon configuration" ) }, +{ QStringLiteral("list"), tr( "List all configuration keys and values" ) }, +{ QStringLiteral("import"), tr( "Import configuration from given file" ) }, +{ QStringLiteral("export"), tr( "Export configuration to given file" ) }, +{ QStringLiteral("get"), tr( "Read and output configuration value for given key" ) }, +{ QStringLiteral("set"), tr( "Write given value to given configuration key" ) }, +{ QStringLiteral("unset"), tr( "Unset (remove) given configuration key" ) }, +{ QStringLiteral("upgrade"), tr( "Upgrade and save configuration of program and plugins" ) }, + } ) +{ +} + + + +QStringList ConfigCommandLinePlugin::commands() const +{ + return m_commands.keys(); +} + + + +QString ConfigCommandLinePlugin::commandHelp( const QString& command ) const +{ + return m_commands.value( command ); +} + + + +CommandLinePluginInterface::RunResult ConfigCommandLinePlugin::handle_clear( const QStringList& arguments ) +{ + Q_UNUSED(arguments); + + if( ConfigurationManager().clearConfiguration() ) + { + return Successful; + } + + return Failed; +} + + + +CommandLinePluginInterface::RunResult ConfigCommandLinePlugin::handle_list( const QStringList& arguments ) +{ + auto listMode = ListMode::Values; + + if( arguments.value( 0 ) == QLatin1String("defaults") ) + { + listMode = ListMode::Defaults; + } + else if( arguments.value( 0 ) == QLatin1String("types") ) + { + listMode = ListMode::Types; + } + + listConfiguration( listMode ); + + return NoResult; +} + + + +CommandLinePluginInterface::RunResult ConfigCommandLinePlugin::handle_import( const QStringList& arguments ) +{ + QString fileName = arguments.value( 0 ); + + if( fileName.isEmpty() || QFile( fileName ).exists() == false ) + { + return operationError( tr( "Please specify an existing configuration file to import." ) ); + } + + if( QFileInfo( fileName ).isReadable() == false ) + { + return operationError( tr( "Configuration file is not readable!" ) ); + } + + Configuration::JsonStore xs( Configuration::JsonStore::System, fileName ); + + // merge configuration + VeyonCore::config() += VeyonConfiguration( &xs ); + + return applyConfiguration(); +} + + + +CommandLinePluginInterface::RunResult ConfigCommandLinePlugin::handle_export( const QStringList& arguments ) +{ + QString fileName = arguments.value( 0 ); + + if( fileName.isEmpty() ) + { + return operationError( tr( "Please specify a valid filename for the configuration export." ) ); + } + + QFileInfo fileInfo( fileName ); + if( fileInfo.exists() && fileInfo.isWritable() == false ) + { + return operationError( tr( "Output file is not writable!" ) ); + } + + if( fileInfo.exists() == false && QFileInfo( fileInfo.dir().path() ).isWritable() == false ) + { + return operationError( tr( "Output directory is not writable!" ) ); + } + + // write current configuration to output file + Configuration::JsonStore( Configuration::JsonStore::System, fileName ).flush( &VeyonCore::config() ); + + return Successful; +} + + + +CommandLinePluginInterface::RunResult ConfigCommandLinePlugin::handle_get( const QStringList& arguments ) +{ + QString key = arguments.value( 0 ); + + if( key.isEmpty() ) + { + return operationError( tr( "Please specify a valid key." ) ); + } + + QStringList keyParts = key.split( QLatin1Char('/') ); + key = keyParts.last(); + QString parentKey; + + if( keyParts.size() > 1 ) + { + parentKey = keyParts.mid( 0, keyParts.size()-1).join( QLatin1Char('/') ); + } + + if( VeyonCore::config().hasValue( key, parentKey ) == false ) + { + return operationError( tr( "Specified key does not exist in current configuration!" ) ); + } + + CommandLineIO::print( printableConfigurationValue( VeyonCore::config().value( key, parentKey, QVariant() ) ) ); + + return NoResult; +} + + + +CommandLinePluginInterface::RunResult ConfigCommandLinePlugin::handle_set( const QStringList& arguments ) +{ + auto key = arguments.value( 0 ); + auto value = arguments.value( 1 ); + auto type = arguments.value( 2 ); + + if( key.isEmpty() ) + { + return operationError( tr( "Please specify a valid key." ) ); + } + + if( value.isEmpty() ) + { + return operationError( tr( "Please specify a valid value." ) ); + } + + const auto keyParts = key.split( QLatin1Char('/') ); + key = keyParts.last(); + QString parentKey; + + if( keyParts.size() > 1 ) + { + parentKey = keyParts.mid( 0, keyParts.size()-1).join( QLatin1Char('/') ); + } + + auto valueType = QMetaType::UnknownType; + const auto property = Configuration::Property::find( &VeyonCore::config(), key, parentKey ); + + if( property ) + { + valueType = static_cast( property->variantValue().userType() ); + } + + QVariant configValue = value; + + if( type == QLatin1String("json") || + valueType == QMetaType::QJsonArray ) + { + configValue = QJsonDocument::fromJson( value.toUtf8() ).array(); + } + else if( key.contains( QStringLiteral("password"), Qt::CaseInsensitive ) || + type == QLatin1String("password") ) + { + configValue = VeyonCore::cryptoCore().encryptPassword( value.toUtf8() ); + } + else if( type == QLatin1String("list") || + valueType == QMetaType::QStringList ) + { + configValue = value.split( QLatin1Char( ';' ) ); + } + + VeyonCore::config().setValue( key, configValue, parentKey ); + + return applyConfiguration(); +} + + + +CommandLinePluginInterface::RunResult ConfigCommandLinePlugin::handle_unset( const QStringList& arguments ) +{ + QString key = arguments.value( 0 ); + + if( key.isEmpty() ) + { + return operationError( tr( "Please specify a valid key." ) ); + } + + QStringList keyParts = key.split( QLatin1Char('/') ); + key = keyParts.last(); + QString parentKey; + + if( keyParts.size() > 1 ) + { + parentKey = keyParts.mid( 0, keyParts.size()-1).join( QLatin1Char('/') ); + } + + VeyonCore::config().removeValue( key, parentKey ); + + return applyConfiguration(); +} + + + +CommandLinePluginInterface::RunResult ConfigCommandLinePlugin::handle_upgrade( const QStringList& arguments ) +{ + Q_UNUSED(arguments); + + // upgrade already happened while loading plugins so only save upgraded configuration + return applyConfiguration(); +} + + + +void ConfigCommandLinePlugin::listConfiguration( ListMode listMode ) const +{ + QTextStream stdoutStream( stdout ); + + const auto properties = VeyonCore::config().findChildren(); + + for( auto property : properties ) + { + if( property->flags().testFlag( Configuration::Property::Flag::Legacy ) ) + { + continue; + } + + stdoutStream << property->absoluteKey() << "="; + switch( listMode ) + { + case ListMode::Values: + stdoutStream << printableConfigurationValue( property->variantValue() ); + break; + case ListMode::Defaults: + stdoutStream << printableConfigurationValue( property->defaultValue() ); + break; + case ListMode::Types: + stdoutStream << QStringLiteral("[%1]").arg( QString::fromLatin1( + property->defaultValue().typeName() ). + replace( QLatin1Char('Q'), QString() ).toLower() ); + break; + } + stdoutStream << endl; + } +} + + + +CommandLinePluginInterface::RunResult ConfigCommandLinePlugin::applyConfiguration() +{ + ConfigurationManager configurationManager; + + if( configurationManager.saveConfiguration() == false || + configurationManager.applyConfiguration() == false ) + { + return operationError( configurationManager.errorString() ); + } + + return Successful; +} + + + +QString ConfigCommandLinePlugin::printableConfigurationValue( const QVariant& value ) +{ + if( value.type() == QVariant::String || + value.type() == QVariant::Uuid || + value.type() == QVariant::UInt || + value.type() == QVariant::Int || + value.type() == QVariant::Bool ) + { + return value.toString(); + } + else if( value.type() == QVariant::StringList ) + { + return value.toStringList().join( QLatin1Char(';') ); + } + else if( value.userType() == QMetaType::QJsonArray ) + { + return QString::fromUtf8( QJsonDocument( value.toJsonArray() ).toJson( QJsonDocument::Compact ) ); + } + else if( value.userType() == QMetaType::QJsonObject ) + { + return QString::fromUtf8( QJsonDocument( value.toJsonObject() ).toJson( QJsonDocument::Compact ) ); + } + else if( QMetaType( value.userType() ).flags().testFlag( QMetaType::IsEnumeration ) ) + { + return QString::number( value.toInt() ); + } + + return {}; +} + + + +CommandLinePluginInterface::RunResult ConfigCommandLinePlugin::operationError( const QString& message ) +{ + CommandLineIO::error( message ); + + return Failed; +} diff --git a/plugins/config/ConfigCommandLinePlugin.h b/plugins/config/ConfigCommandLinePlugin.h new file mode 100644 index 0000000..0c6a66f --- /dev/null +++ b/plugins/config/ConfigCommandLinePlugin.h @@ -0,0 +1,109 @@ +/* + * ConfigCommandLinePlugin.h - declaration of ConfigCommandLinePlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CommandLinePluginInterface.h" +#include "VeyonConfiguration.h" + +class ConfigCommandLinePlugin : public QObject, CommandLinePluginInterface, PluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.ConfigCommandLineInterface") + Q_INTERFACES(PluginInterface CommandLinePluginInterface) +public: + explicit ConfigCommandLinePlugin( QObject* parent = nullptr ); + ~ConfigCommandLinePlugin() override = default; + + Plugin::Uid uid() const override + { + return QStringLiteral("1bdb0d1c-f8eb-4d21-a093-d555a10f3975"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral( "Config" ); + } + + QString description() const override + { + return tr( "Configure Veyon at command line" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + QString commandLineModuleName() const override + { + return QStringLiteral( "config" ); + } + + QString commandLineModuleHelp() const override + { + return tr( "Commands for managing the configuration of Veyon" ); + } + + QStringList commands() const override; + QString commandHelp( const QString& command ) const override; + +public Q_SLOTS: + CommandLinePluginInterface::RunResult handle_clear( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_list( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_import( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_export( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_get( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_set( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_unset( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_upgrade( const QStringList& arguments ); + +private: + enum class ListMode { + Values, + Defaults, + Types + }; + + void listConfiguration( ListMode listMode ) const; + + CommandLinePluginInterface::RunResult applyConfiguration(); + + static QString printableConfigurationValue( const QVariant& value ); + + CommandLinePluginInterface::RunResult operationError( const QString& message ); + + QMap m_commands; + +}; diff --git a/plugins/demo/CMakeLists.txt b/plugins/demo/CMakeLists.txt new file mode 100644 index 0000000..375ed88 --- /dev/null +++ b/plugins/demo/CMakeLists.txt @@ -0,0 +1,21 @@ +INCLUDE(BuildVeyonPlugin) + +build_veyon_plugin(demo + DemoFeaturePlugin.cpp + DemoConfigurationPage.cpp + DemoConfigurationPage.ui + DemoServer.cpp + DemoServerConnection.cpp + DemoServerProtocol.cpp + DemoClient.cpp + DemoFeaturePlugin.h + DemoConfiguration.h + DemoConfigurationPage.h + DemoServer.h + DemoServerConnection.h + DemoServerProtocol.h + DemoClient.h + demo.qrc +) + +TARGET_LINK_LIBRARIES(demo ${LZO_LIBRARIES}) diff --git a/plugins/demo/DemoClient.cpp b/plugins/demo/DemoClient.cpp new file mode 100644 index 0000000..033b319 --- /dev/null +++ b/plugins/demo/DemoClient.cpp @@ -0,0 +1,121 @@ +/* + * DemoClient.cpp - client widget for demo mode + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "DemoClient.h" +#include "VeyonConfiguration.h" +#include "LockWidget.h" +#include "PlatformCoreFunctions.h" +#include "VncViewWidget.h" + + +DemoClient::DemoClient( const QString& host, int port, bool fullscreen, const QRect& viewport, QObject* parent ) : + QObject( parent ), + m_toplevel( nullptr ) +{ + if( fullscreen ) + { + m_toplevel = new LockWidget( LockWidget::NoBackground ); + } + else + { + m_toplevel = new QWidget(); + } + + m_toplevel->setWindowTitle( tr( "%1 Demo" ).arg( VeyonCore::applicationName() ) ); + m_toplevel->setWindowIcon( QPixmap( QStringLiteral(":/core/icon64.png") ) ); + m_toplevel->setAttribute( Qt::WA_DeleteOnClose, false ); + + if( fullscreen == false ) + { + m_toplevel->setWindowFlags( Qt::Window | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint ); + m_toplevel->resize( QApplication::desktop()->availableGeometry( m_toplevel ).size() - QSize( 10, 30 ) ); + } + + m_vncView = new VncViewWidget( host, port, m_toplevel, VncView::DemoMode, viewport ); + + auto toplevelLayout = new QVBoxLayout; + toplevelLayout->setContentsMargins( 0, 0, 0, 0 ); + toplevelLayout->setSpacing( 0 ); + toplevelLayout->addWidget( m_vncView ); + + m_toplevel->setLayout( toplevelLayout ); + + connect( m_toplevel, &QObject::destroyed, this, &DemoClient::viewDestroyed ); + connect( m_vncView, &VncViewWidget::sizeHintChanged, this, &DemoClient::resizeToplevelWidget ); + + m_toplevel->move( 0, 0 ); + if( fullscreen ) + { + m_toplevel->showFullScreen(); + } + else + { + m_toplevel->show(); + } + + VeyonCore::platform().coreFunctions().raiseWindow( m_toplevel, fullscreen ); + + VeyonCore::platform().coreFunctions().disableScreenSaver(); +} + + + +DemoClient::~DemoClient() +{ + VeyonCore::platform().coreFunctions().restoreScreenSaverSettings(); + + delete m_toplevel; +} + + + +void DemoClient::viewDestroyed( QObject* obj ) +{ + // prevent double deletion of toplevel widget + if( m_toplevel == obj ) + { + m_toplevel = nullptr; + } + + deleteLater(); +} + + + +void DemoClient::resizeToplevelWidget() +{ + if( m_toplevel->windowState() & Qt::WindowFullScreen ) + { + m_vncView->resize( m_toplevel->size() ); + } + else + { + m_toplevel->resize( m_vncView->sizeHint() ); + } +} diff --git a/plugins/demo/DemoClient.h b/plugins/demo/DemoClient.h new file mode 100644 index 0000000..5672441 --- /dev/null +++ b/plugins/demo/DemoClient.h @@ -0,0 +1,45 @@ +/* + * DemoClient.h - client for demo server + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +class VncViewWidget; + +class DemoClient : public QObject +{ + Q_OBJECT +public: + DemoClient( const QString& host, int port, bool fullscreen, const QRect& viewport, QObject* parent = nullptr ); + ~DemoClient() override; + +private: + void viewDestroyed( QObject* obj ); + void resizeToplevelWidget(); + + QWidget* m_toplevel; + VncViewWidget* m_vncView; + +} ; diff --git a/plugins/demo/DemoConfiguration.h b/plugins/demo/DemoConfiguration.h new file mode 100644 index 0000000..d6e9701 --- /dev/null +++ b/plugins/demo/DemoConfiguration.h @@ -0,0 +1,36 @@ +/* + * DemoConfiguration.h - configuration values for Demo plugin + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonConfiguration.h" +#include "Configuration/Proxy.h" + +#define FOREACH_DEMO_CONFIG_PROPERTY(OP) \ + OP( DemoConfiguration, m_configuration, bool, slowDownThumbnailUpdates, setSlowDownThumbnailUpdates, "SlowDownThumbnailUpdates", "Demo", true, Configuration::Property::Flag::Advanced ) \ + OP( DemoConfiguration, m_configuration, int, framebufferUpdateInterval, setFramebufferUpdateInterval, "FramebufferUpdateInterval", "Demo", 100, Configuration::Property::Flag::Advanced ) \ + OP( DemoConfiguration, m_configuration, int, keyFrameInterval, setKeyFrameInterval, "KeyFrameInterval", "Demo", 10, Configuration::Property::Flag::Advanced ) \ + OP( DemoConfiguration, m_configuration, int, memoryLimit, setMemoryLimit, "MemoryLimit", "Demo", 128, Configuration::Property::Flag::Advanced ) \ + +DECLARE_CONFIG_PROXY(DemoConfiguration, FOREACH_DEMO_CONFIG_PROPERTY) diff --git a/plugins/demo/DemoConfigurationPage.cpp b/plugins/demo/DemoConfigurationPage.cpp new file mode 100644 index 0000000..d5a838e --- /dev/null +++ b/plugins/demo/DemoConfigurationPage.cpp @@ -0,0 +1,66 @@ +/* + * DemoConfigurationPage.cpp - implementation of DemoConfigurationPage + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "DemoConfiguration.h" +#include "DemoConfigurationPage.h" +#include "Configuration/UiMapping.h" + +#include "ui_DemoConfigurationPage.h" + +DemoConfigurationPage::DemoConfigurationPage( DemoConfiguration& configuration, QWidget* parent ) : + ConfigurationPage( parent ), + ui( new Ui::DemoConfigurationPage ), + m_configuration( configuration ) +{ + ui->setupUi(this); + + Configuration::UiMapping::setFlags( this, Configuration::Property::Flag::Advanced ); +} + + + +DemoConfigurationPage::~DemoConfigurationPage() +{ + delete ui; +} + + + +void DemoConfigurationPage::resetWidgets() +{ + FOREACH_DEMO_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); +} + + + +void DemoConfigurationPage::connectWidgetsToProperties() +{ + FOREACH_DEMO_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY) +} + + + +void DemoConfigurationPage::applyConfiguration() +{ +} diff --git a/plugins/demo/DemoConfigurationPage.h b/plugins/demo/DemoConfigurationPage.h new file mode 100644 index 0000000..0c23691 --- /dev/null +++ b/plugins/demo/DemoConfigurationPage.h @@ -0,0 +1,52 @@ +/* + * DemoConfigurationPage.h - header for the DemoConfigurationPage class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "ConfigurationPage.h" + +namespace Ui { +class DemoConfigurationPage; +} + +class DemoConfiguration; + +class DemoConfigurationPage : public ConfigurationPage +{ + Q_OBJECT +public: + explicit DemoConfigurationPage( DemoConfiguration& configuration, QWidget* parent = nullptr ); + ~DemoConfigurationPage() override; + + void resetWidgets() override; + void connectWidgetsToProperties() override; + void applyConfiguration() override; + + +private: + Ui::DemoConfigurationPage *ui; + + DemoConfiguration& m_configuration; + +}; diff --git a/plugins/demo/DemoConfigurationPage.ui b/plugins/demo/DemoConfigurationPage.ui new file mode 100644 index 0000000..d97fd46 --- /dev/null +++ b/plugins/demo/DemoConfigurationPage.ui @@ -0,0 +1,129 @@ + + + DemoConfigurationPage + + + Demo server + + + + :/demo/window-duplicate.png:/demo/window-duplicate.png + + + + 0 + + + 0 + + + + + Tunables + + + + + + Update interval + + + + + + + Slow down thumbnail updates while demo is running + + + + + + + MB + + + 64 + + + 512 + + + 16 + + + 128 + + + + + + + s + + + 1 + + + 30 + + + 10 + + + + + + + ms + + + 20 + + + 500 + + + 20 + + + 100 + + + + + + + Memory limit + + + + + + + Key frame interval + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + diff --git a/plugins/demo/DemoFeaturePlugin.cpp b/plugins/demo/DemoFeaturePlugin.cpp new file mode 100644 index 0000000..548bb1f --- /dev/null +++ b/plugins/demo/DemoFeaturePlugin.cpp @@ -0,0 +1,629 @@ +/* + * DemoFeaturePlugin.cpp - implementation of DemoFeaturePlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "AuthenticationCredentials.h" +#include "Computer.h" +#include "CryptoCore.h" +#include "DemoClient.h" +#include "DemoConfigurationPage.h" +#include "DemoFeaturePlugin.h" +#include "DemoServer.h" +#include "FeatureWorkerManager.h" +#include "HostAddress.h" +#include "Logger.h" +#include "FeatureWorkerManager.h" +#include "VeyonConfiguration.h" +#include "VeyonMasterInterface.h" +#include "VeyonServerInterface.h" + + +DemoFeaturePlugin::DemoFeaturePlugin( QObject* parent ) : + QObject( parent ), + m_demoFeature( QStringLiteral( "Demo" ), + Feature::Mode | Feature::AllComponents, + Feature::Uid( "6f4cd922-b63e-40bf-9451-536065c7cdf9" ), + Feature::Uid(), + tr( "Demo" ), tr( "Stop demo" ), + tr( "Share your screen or allow a user to share his screen with other users." ), + QStringLiteral(":/demo/demo.png") ), + m_demoClientFullScreenFeature( QStringLiteral( "FullScreenDemo" ), + Feature::Internal | Feature::AllComponents, + Feature::Uid( "7b6231bd-eb89-45d3-af32-f70663b2f878" ), + {}, tr("Full screen demo"), {}, {} ), + m_demoClientWindowFeature( QStringLiteral( "WindowDemo" ), + Feature::Internal | Feature::AllComponents, + Feature::Uid( "ae45c3db-dc2e-4204-ae8b-374cdab8c62c" ), + {}, tr("Window demo"), {}, {} ), + m_shareOwnScreenFullScreenFeature( QStringLiteral( "ShareOwnScreenFullScreen" ), + Feature::Mode | Feature::AllComponents, + Feature::Uid( "7b6231bd-eb89-45d3-af32-f70663b2f878" ), + m_demoFeature.uid(), + tr( "Share your own screen in fullscreen mode" ), {}, + tr( "In this mode your screen is being displayed in " + "full screen mode on all computers while the input " + "devices of the users are locked." ), + QStringLiteral(":/demo/presentation-fullscreen.png") ), + m_shareOwnScreenWindowFeature( QStringLiteral( "ShareOwnScreenWindow" ), + Feature::Mode | Feature::AllComponents, + Feature::Uid( "ae45c3db-dc2e-4204-ae8b-374cdab8c62c" ), + m_demoFeature.uid(), + tr( "Share your own screen in a window" ), {}, + tr( "In this mode your screen being displayed in a " + "window on all computers. The users are " + "able to switch to other windows as needed." ), + QStringLiteral(":/demo/presentation-window.png") ), + m_shareUserScreenFullScreenFeature( QStringLiteral( "ShareUserScreenFullScreen" ), + Feature::Mode | Feature::AllComponents, + Feature::Uid( "b4e542e2-1deb-48ac-910a-bbf8ac9a0bde" ), + m_demoFeature.uid(), + tr( "Share selected user's screen in fullscreen mode" ), {}, + tr( "In this mode the screen of the selected user is being displayed " + "in full screen mode on all computers while the input " + "devices of the users are locked." ), + QStringLiteral(":/demo/presentation-fullscreen.png") ), + m_shareUserScreenWindowFeature( QStringLiteral( "ShareUserScreenWindow" ), + Feature::Mode | Feature::AllComponents, + Feature::Uid( "ebfc5ec4-f725-4bfc-a93a-c6d4864c6806" ), + m_demoFeature.uid(), + tr( "Share selected user's screen in a window" ), {}, + tr( "In this mode the screen of the selected user being displayed " + "in a window on all computers. The users are " + "able to switch to other windows as needed." ), + QStringLiteral(":/demo/presentation-window.png") ), + m_demoServerFeature( QStringLiteral( "DemoServer" ), + Feature::Session | Feature::Service | Feature::Worker, + Feature::Uid( "e4b6e743-1f5b-491d-9364-e091086200f4" ), + m_demoFeature.uid(), + {}, {}, {} ), + m_staticFeatures( { + m_demoFeature, m_demoServerFeature, + m_demoClientFullScreenFeature, m_demoClientWindowFeature, + m_shareOwnScreenFullScreenFeature, m_shareOwnScreenWindowFeature, + m_shareUserScreenFullScreenFeature, m_shareUserScreenWindowFeature + } ), + m_demoAccessToken( CryptoCore::generateChallenge() ), + m_demoClientHosts(), + m_configuration( &VeyonCore::config() ), + m_demoServer( nullptr ), + m_demoClient( nullptr ) +{ + connect( qGuiApp, &QGuiApplication::screenAdded, this, &DemoFeaturePlugin::addScreen ); + connect( qGuiApp, &QGuiApplication::screenRemoved, this, &DemoFeaturePlugin::removeScreen ); + + updateFeatures(); +} + + + +bool DemoFeaturePlugin::controlFeature( Feature::Uid featureUid, + Operation operation, + const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( featureUid == m_demoServerFeature.uid() ) + { + return controlDemoServer( operation, arguments, computerControlInterfaces ); + } + + if( featureUid == m_demoClientFullScreenFeature.uid() || featureUid == m_demoClientWindowFeature.uid() ) + { + return controlDemoClient( featureUid, operation, arguments, computerControlInterfaces ); + } + + return false; +} + + + +bool DemoFeaturePlugin::startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( feature == m_shareOwnScreenWindowFeature || feature == m_shareOwnScreenFullScreenFeature ) + { + // start demo server + controlFeature( m_demoServerFeature.uid(), Operation::Start, {}, + { master.localSessionControlInterface().weakPointer() } ); + + // start demo clients + controlFeature( feature == m_shareOwnScreenFullScreenFeature ? m_demoClientFullScreenFeature.uid() + : m_demoClientWindowFeature.uid(), + Operation::Start, {}, computerControlInterfaces ); + + return true; + } + + if( feature == m_shareUserScreenWindowFeature || feature == m_shareUserScreenFullScreenFeature ) + { + if( master.selectedComputerControlInterfaces().size() < 1 ) + { + QMessageBox::critical( master.mainWindow(), feature.name(), + tr( "Please select a user screen to share.") ); + return true; + } + + if( master.selectedComputerControlInterfaces().size() > 1 ) + { + QMessageBox::critical( master.mainWindow(), feature.name(), + tr( "Please select only one user screen to share.") ); + return true; + } + + auto vncServerPortOffset = 0; + auto demoServerPort = VeyonCore::config().demoServerPort(); + + const auto demoServerInterface = master.selectedComputerControlInterfaces().first(); + const auto demoServerHost = demoServerInterface->computer().hostAddress(); + const auto primaryServerPort = HostAddress::parsePortNumber( demoServerHost ); + + if( primaryServerPort > 0 ) + { + const auto sessionId = primaryServerPort - VeyonCore::config().veyonServerPort(); + vncServerPortOffset = sessionId; + demoServerPort += sessionId; + } + + // start demo server + controlFeature( m_demoServerFeature.uid(), Operation::Start, + { + { argToString(Argument::VncServerPortOffset), vncServerPortOffset }, + { argToString(Argument::DemoServerPort), demoServerPort }, + }, + master.selectedComputerControlInterfaces() ); + + // start demo clients + auto userDemoControlInterfaces = computerControlInterfaces; + userDemoControlInterfaces.removeAll( demoServerInterface ); + + const QVariantMap demoClientArgs{ + { argToString(Argument::DemoServerHost), HostAddress::parseHost(demoServerHost) }, + { argToString(Argument::DemoServerPort), demoServerPort }, + }; + + controlFeature( feature == m_shareUserScreenFullScreenFeature ? m_demoClientFullScreenFeature.uid() + : m_demoClientWindowFeature.uid(), + Operation::Start, demoClientArgs, userDemoControlInterfaces ); + + controlFeature( m_demoClientWindowFeature.uid(), Operation::Start, demoClientArgs, + { master.localSessionControlInterface().weakPointer() } ); + + return true; + } + + const auto screenIndex = m_screenSelectionFeatures.indexOf( feature ); + if( screenIndex >= 0 ) + { + m_screenSelection = screenIndex; + + updateFeatures(); + } + + return false; +} + + + +bool DemoFeaturePlugin::stopFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( feature == m_demoFeature || + feature == m_shareOwnScreenWindowFeature || feature == m_shareOwnScreenFullScreenFeature || + feature == m_shareUserScreenWindowFeature || feature == m_shareUserScreenFullScreenFeature ) + { + controlFeature( m_demoClientWindowFeature.uid(), Operation::Stop, {}, computerControlInterfaces ); + + controlFeature( m_demoClientWindowFeature.uid(), Operation::Stop, {}, + { master.localSessionControlInterface().weakPointer() } ); + + // no demo clients left? + if( m_demoClientHosts.isEmpty() ) + { + // then we can stop the server + controlFeature( m_demoServerFeature.uid(), Operation::Stop, {}, + { master.localSessionControlInterface().weakPointer() } ); + + controlFeature( m_demoServerFeature.uid(), Operation::Stop, {}, computerControlInterfaces ); + + // reset demo access token + m_demoAccessToken = CryptoCore::generateChallenge(); + } + + return true; + } + + return false; +} + + + +bool DemoFeaturePlugin::handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) +{ + if( message.featureUid() == m_demoServerFeature.uid() ) + { + if( message.command() == StartDemoServer ) + { + // add VNC server password to message + server.featureWorkerManager(). + sendMessageToManagedSystemWorker( + FeatureMessage{ message } + .addArgument( Argument::VncServerPassword, VeyonCore::authenticationCredentials().internalVncServerPassword().toByteArray() ) + .addArgument( Argument::VncServerPort, server.vncServerBasePort() + message.argument(Argument::VncServerPortOffset).toInt() ) ); + } + else if( message.command() != StopDemoServer || + server.featureWorkerManager().isWorkerRunning( m_demoServerFeature.uid() ) ) + { + // forward message to worker + server.featureWorkerManager().sendMessageToManagedSystemWorker( message ); + } + + return true; + } + + if( message.featureUid() == m_demoClientFullScreenFeature.uid() || + message.featureUid() == m_demoClientWindowFeature.uid() ) + { + // if a demo server is started, it's likely that the demo accidentally was + // started on master computer as well therefore we deny starting a demo on + // hosts on which a demo server is running - exception: debug mode + if( message.featureUid() == m_demoClientFullScreenFeature.uid() && + server.featureWorkerManager().isWorkerRunning( m_demoServerFeature.uid() ) && + VeyonCore::config().logLevel() < Logger::LogLevel::Debug ) + { + return false; + } + + if( message.command() == StopDemoClient && + server.featureWorkerManager().isWorkerRunning( message.featureUid() ) == false ) + { + return true; + } + + auto socket = qobject_cast( messageContext.ioDevice() ); + if( socket == nullptr ) + { + vCritical() << "invalid socket"; + return false; + } + + if( message.command() == StartDemoClient && + message.argument( Argument::DemoServerHost ).toString().isEmpty() ) + { + // set the peer address as demo server host + server.featureWorkerManager().sendMessageToManagedSystemWorker( + FeatureMessage{ message } + .addArgument( Argument::DemoServerHost, socket->peerAddress().toString() ) ); + } + else + { + // forward message to worker + server.featureWorkerManager().sendMessageToManagedSystemWorker( message ); + } + + return true; + } + + return false; +} + + + +bool DemoFeaturePlugin::handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) +{ + Q_UNUSED(worker) + + if( message.featureUid() == m_demoServerFeature.uid() ) + { + switch( message.command() ) + { + case StartDemoServer: + if( m_demoServer == nullptr ) + { + m_demoServer = new DemoServer( message.argument( Argument::VncServerPort ).toInt(), + message.argument( Argument::VncServerPassword ).toByteArray(), + message.argument( Argument::DemoAccessToken ).toByteArray(), + m_configuration, + message.argument( Argument::DemoServerPort ).toInt(), + this ); + } + return true; + + case StopDemoServer: + delete m_demoServer; + m_demoServer = nullptr; + + QCoreApplication::quit(); + + return true; + + default: + break; + } + } + else if( message.featureUid() == m_demoClientFullScreenFeature.uid() || + message.featureUid() == m_demoClientWindowFeature.uid() ) + { + switch( message.command() ) + { + case StartDemoClient: + VeyonCore::authenticationCredentials().setToken( message.argument( Argument::DemoAccessToken ).toByteArray() ); + + if( m_demoClient == nullptr ) + { + const auto demoServerHost = message.argument( Argument::DemoServerHost ).toString(); + const auto demoServerPort = message.argument( Argument::DemoServerPort ).toInt(); + const auto isFullscreenDemo = message.featureUid() == m_demoClientFullScreenFeature.uid(); + const auto viewport = message.argument( Argument::Viewport ).toRect(); + + vDebug() << "connecting with master" << demoServerHost; + m_demoClient = new DemoClient( demoServerHost, demoServerPort, isFullscreenDemo, viewport ); + } + return true; + + case StopDemoClient: + delete m_demoClient; + m_demoClient = nullptr; + + QCoreApplication::quit(); + + return true; + + default: + break; + } + } + + return false; +} + + + +ConfigurationPage* DemoFeaturePlugin::createConfigurationPage() +{ + return new DemoConfigurationPage( m_configuration ); +} + + + +void DemoFeaturePlugin::addScreen( QScreen* screen ) +{ + m_screens = QGuiApplication::screens(); + + const auto screenIndex = m_screens.indexOf( screen ) + 1; + if( m_screenSelection > ScreenSelectionNone && + screenIndex <= m_screenSelection ) + { + m_screenSelection++; + } + + updateFeatures(); +} + + + +void DemoFeaturePlugin::removeScreen( QScreen* screen ) +{ + const auto screenIndex = m_screens.indexOf( screen ) + 1; + if( screenIndex == m_screenSelection ) + { + m_screenSelection = ScreenSelectionNone; + } + + m_screens = QGuiApplication::screens(); + + m_screenSelection = qMin( m_screenSelection, m_screens.size() ); + + updateFeatures(); +} + + + +void DemoFeaturePlugin::updateFeatures() +{ + m_screenSelectionFeatures.clear(); + + if( m_screens.size() > 1 ) + { + m_screenSelectionFeatures.reserve( m_screens.size() + 1 ); + + auto allScreensFlags = Feature::Option | Feature::Master; + if( m_screenSelection <= ScreenSelectionNone ) + { + allScreensFlags |= Feature::Checked; + } + + m_screenSelectionFeatures.append( Feature{ QStringLiteral("DemoAllScreens"), + allScreensFlags, + Feature::Uid("2aca1e9f-25f9-4d0f-9729-01b03c80ab28"), + m_demoFeature.uid(), tr("All screens"), {}, {}, {} } ); + + int index = 1; + for( auto screen : qAsConst(m_screens) ) + { + const auto name = QStringLiteral( "DemoScreen%1" ).arg( index ); + + auto displayName = tr( "Screen %1 [%2]" ).arg( index ).arg( screen->name() ); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) + if( screen->manufacturer().isEmpty() == false && + screen->model().isEmpty() == false ) + { + displayName += QStringLiteral(" – %1 %2").arg( screen->manufacturer(), screen->model() ); + } +#endif + + auto flags = Feature::Option | Feature::Master; + if( index == m_screenSelection ) + { + flags |= Feature::Checked; + } + + m_screenSelectionFeatures.append( Feature{ name, flags, + Feature::Uid::createUuidV5( m_demoFeature.uid(), name ), + m_demoFeature.uid(), displayName, {}, {}, {} } ); + ++index; + } + } + + m_features = m_staticFeatures + m_screenSelectionFeatures; + + const auto master = VeyonCore::instance()->findChild(); + if( master ) + { + master->reloadSubFeatures(); + } +} + + + + +QRect DemoFeaturePlugin::viewportFromScreenSelection() const +{ + if( m_screenSelection <= ScreenSelectionNone ) + { + return {}; + } + + QPoint minimumScreenPosition{}; + for( const auto* screen : m_screens ) + { + minimumScreenPosition.setX( qMin( minimumScreenPosition.x(), screen->geometry().x() ) ); + minimumScreenPosition.setY( qMin( minimumScreenPosition.y(), screen->geometry().y() ) ); + } + + const auto screen = m_screens.value( m_screenSelection - 1 ); + if( screen ) + { + return screen->geometry().translated( -minimumScreenPosition ); + } + + return {}; +} + + + +bool DemoFeaturePlugin::controlDemoServer( Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( operation == Operation::Start ) + { + const auto demoServerPort = arguments.value( argToString(Argument::DemoServerPort), + VeyonCore::config().demoServerPort() + VeyonCore::sessionId() ).toInt(); + const auto vncServerPortOffset = arguments.value( argToString(Argument::VncServerPortOffset), + VeyonCore::sessionId() ).toInt(); + const auto demoAccessToken = arguments.value( argToString(Argument::DemoAccessToken), + m_demoAccessToken.toByteArray() ).toByteArray(); + + sendFeatureMessage( FeatureMessage{ m_demoServerFeature.uid(), StartDemoServer } + .addArgument( Argument::DemoAccessToken, demoAccessToken ) + .addArgument( Argument::VncServerPortOffset, vncServerPortOffset ) + .addArgument( Argument::DemoServerPort, demoServerPort ), + computerControlInterfaces ); + + return true; + } + + if( operation == Operation::Stop ) + { + sendFeatureMessage( FeatureMessage{ m_demoServerFeature.uid(), StopDemoServer }, + computerControlInterfaces ); + + return true; + } + + return false; +} + + + +bool DemoFeaturePlugin::controlDemoClient( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( operation == Operation::Start ) + { + const auto demoAccessToken = arguments.value( argToString(Argument::DemoAccessToken), + m_demoAccessToken.toByteArray() ).toByteArray(); + const auto demoServerHost = arguments.value( argToString(Argument::DemoServerHost) ).toString(); + const auto demoServerPort = arguments.value( argToString(Argument::DemoServerPort), + VeyonCore::config().demoServerPort() + VeyonCore::sessionId() ).toInt(); + + QRect viewport{ + arguments.value( argToString(Argument::ViewportX) ).toInt(), + arguments.value( argToString(Argument::ViewportY) ).toInt(), + arguments.value( argToString(Argument::ViewportWidth) ).toInt(), + arguments.value( argToString(Argument::ViewportHeight) ).toInt() + }; + + if( viewport.isNull() || viewport.isEmpty() ) + { + viewport = viewportFromScreenSelection(); + } + + const auto disableUpdates = m_configuration.slowDownThumbnailUpdates(); + + for( const auto& computerControlInterface : computerControlInterfaces ) + { + m_demoClientHosts += computerControlInterface->computer().hostAddress(); + if( disableUpdates ) + { + computerControlInterface->setUpdateMode( ComputerControlInterface::UpdateMode::Disabled ); + } + } + + sendFeatureMessage( FeatureMessage{ featureUid, StartDemoClient } + .addArgument( Argument::DemoAccessToken, demoAccessToken ) + .addArgument( Argument::DemoServerHost, demoServerHost ) + .addArgument( Argument::DemoServerPort, demoServerPort ) + .addArgument( Argument::Viewport, viewport ), + computerControlInterfaces ); + + return true; + } + + if( operation == Operation::Stop ) + { + const auto enableUpdates = m_configuration.slowDownThumbnailUpdates(); + + for( const auto& computerControlInterface : computerControlInterfaces ) + { + m_demoClientHosts.removeAll( computerControlInterface->computer().hostAddress() ); + if( enableUpdates ) + { + computerControlInterface->setUpdateMode( ComputerControlInterface::UpdateMode::Monitoring ); + } + } + + sendFeatureMessage( FeatureMessage{ featureUid, StopDemoClient }, computerControlInterfaces ); + + return true; + } + + return false; +} + + +IMPLEMENT_CONFIG_PROXY(DemoConfiguration) diff --git a/plugins/demo/DemoFeaturePlugin.h b/plugins/demo/DemoFeaturePlugin.h new file mode 100644 index 0000000..4c6a748 --- /dev/null +++ b/plugins/demo/DemoFeaturePlugin.h @@ -0,0 +1,161 @@ +/* + * DemoFeaturePlugin.h - declaration of DemoFeaturePlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "ConfigurationPagePluginInterface.h" +#include "DemoConfiguration.h" +#include "FeatureProviderInterface.h" + +class QScreen; + +class DemoServer; +class DemoClient; + +class DemoFeaturePlugin : public QObject, FeatureProviderInterface, PluginInterface, ConfigurationPagePluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.Demo") + Q_INTERFACES(PluginInterface FeatureProviderInterface ConfigurationPagePluginInterface) +public: + using Token = CryptoCore::PlaintextPassword; + + enum class Argument { + DemoAccessToken, + VncServerPort, + VncServerPassword, + DemoServerHost, + DemoServerPort, + Viewport, + ViewportX, + ViewportY, + ViewportWidth, + ViewportHeight, + VncServerPortOffset + }; + Q_ENUM(Argument) + + explicit DemoFeaturePlugin( QObject* parent = nullptr ); + ~DemoFeaturePlugin() override = default; + + Plugin::Uid uid() const override + { + return QStringLiteral("1b08265b-348f-4978-acaa-45d4f6b90bd9"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral("Demonstration"); + } + + QString description() const override + { + return tr( "Give a demonstration by screen broadcasting" ); + } + + QString vendor() const override + { + return QStringLiteral("Veyon Community"); + } + + QString copyright() const override + { + return QStringLiteral("Tobias Junghans"); + } + + const FeatureList& featureList() const override + { + return m_features; + } + + bool controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool stopFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) override; + + bool handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) override; + + ConfigurationPage* createConfigurationPage() override; + +private: + static constexpr auto ScreenSelectionNone = 0; + + void addScreen( QScreen* screen ); + void removeScreen( QScreen* screen ); + + void updateFeatures(); + + QRect viewportFromScreenSelection() const; + + bool controlDemoServer( Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ); + bool controlDemoClient( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ); + + enum Commands { + StartDemoServer, + StopDemoServer, + StartDemoClient, + StopDemoClient + }; + + const Feature m_demoFeature; + const Feature m_demoClientFullScreenFeature; + const Feature m_demoClientWindowFeature; + const Feature m_shareOwnScreenFullScreenFeature; + const Feature m_shareOwnScreenWindowFeature; + const Feature m_shareUserScreenFullScreenFeature; + const Feature m_shareUserScreenWindowFeature; + const Feature m_demoServerFeature; + const FeatureList m_staticFeatures{}; + FeatureList m_features{}; + + FeatureList m_screenSelectionFeatures{}; + int m_screenSelection{ScreenSelectionNone}; + QList m_screens{QGuiApplication::screens()}; + + Token m_demoAccessToken; + QStringList m_demoClientHosts; + + DemoConfiguration m_configuration; + + DemoServer* m_demoServer; + DemoClient* m_demoClient; + +}; diff --git a/plugins/demo/DemoServer.cpp b/plugins/demo/DemoServer.cpp new file mode 100644 index 0000000..c79612c --- /dev/null +++ b/plugins/demo/DemoServer.cpp @@ -0,0 +1,341 @@ +/* + * DemoServer.cpp - multi-threaded slim VNC-server for demo-purposes (optimized + * for lot of clients accessing server in read-only-mode) + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "rfb/rfbproto.h" + +#include + +#include "DemoConfiguration.h" +#include "DemoServer.h" +#include "DemoServerConnection.h" +#include "VeyonConfiguration.h" +#include "VncClientProtocol.h" + + +DemoServer::DemoServer( int vncServerPort, const Password& vncServerPassword, const Password& demoAccessToken, + const DemoConfiguration& configuration, int demoServerPort, QObject *parent ) : + QTcpServer( parent ), + m_configuration( configuration ), + m_memoryLimit( m_configuration.memoryLimit() * 1024*1024 ), + m_keyFrameInterval( m_configuration.keyFrameInterval() * 1000 ), + m_vncServerPort( vncServerPort ), + m_demoAccessToken( demoAccessToken ), + m_vncServerSocket( new QTcpSocket( this ) ), + m_vncClientProtocol( new VncClientProtocol( m_vncServerSocket, vncServerPassword ) ), + m_framebufferUpdateTimer( this ), + m_lastFullFramebufferUpdate(), + m_requestFullFramebufferUpdate( false ), + m_keyFrame( 0 ) +{ + connect( m_vncServerSocket, &QTcpSocket::readyRead, this, &DemoServer::readFromVncServer ); + connect( m_vncServerSocket, &QTcpSocket::disconnected, this, &DemoServer::reconnectToVncServer ); + + connect( &m_framebufferUpdateTimer, &QTimer::timeout, this, &DemoServer::requestFramebufferUpdate ); + + if( listen( QHostAddress::Any, demoServerPort ) == false ) + { + vCritical() << "could not listen to demo server port"; + return; + } + + m_framebufferUpdateTimer.start( m_configuration.framebufferUpdateInterval() ); + + reconnectToVncServer(); +} + + + +DemoServer::~DemoServer() +{ + vDebug() << "disconnecting signals"; + m_vncServerSocket->disconnect( this ); + + vDebug() << "deleting connections"; + + QList l; + while( !( l = findChildren() ).isEmpty() ) + { + l.front()->quit(); + l.front()->wait( ConnectionThreadWaitTime ); + l.front()->terminate(); + l.front()->deleteLater(); + } + + vDebug() << "deleting VNC client protocol"; + delete m_vncClientProtocol; + + vDebug() << "deleting server socket"; + delete m_vncServerSocket; + + vDebug() << "finished"; +} + + + +const QByteArray& DemoServer::serverInitMessage() const +{ + return m_vncClientProtocol->serverInitMessage(); +} + + + +void DemoServer::lockDataForRead() +{ + QElapsedTimer readLockTimer; + readLockTimer.restart(); + + m_dataLock.lockForRead(); + + if( readLockTimer.elapsed() > 100 ) + { + vDebug() << "locking for read took" << readLockTimer.elapsed() << "ms in thread" + << QThread::currentThreadId(); + } +} + + + +void DemoServer::incomingConnection( qintptr socketDescriptor ) +{ + vDebug() << socketDescriptor; + + m_pendingConnections.append( socketDescriptor ); + + if( m_vncClientProtocol->state() == VncClientProtocol::State::Running ) + { + acceptPendingConnections(); + } +} + + + +void DemoServer::acceptPendingConnections() +{ + while( m_pendingConnections.isEmpty() == false ) + { + new DemoServerConnection( this, m_demoAccessToken, m_pendingConnections.takeFirst() ); + } +} + + + +void DemoServer::reconnectToVncServer() +{ + m_vncClientProtocol->start(); + + m_vncServerSocket->connectToHost( QHostAddress::LocalHost, static_cast( m_vncServerPort ) ); +} + + + +void DemoServer::readFromVncServer() +{ + if( m_vncClientProtocol->state() != VncClientProtocol::Running ) + { + while( m_vncClientProtocol->read() ) + { + } + + if( m_vncClientProtocol->state() == VncClientProtocol::Running ) + { + start(); + } + } + else + { + while( receiveVncServerMessage() ) + { + } + } +} + + + +void DemoServer::requestFramebufferUpdate() +{ + if( m_vncClientProtocol->state() != VncClientProtocol::Running ) + { + return; + } + + if( m_requestFullFramebufferUpdate || + m_lastFullFramebufferUpdate.elapsed() >= m_keyFrameInterval ) + { + vDebug() << "Requesting full framebuffer update"; + m_vncClientProtocol->requestFramebufferUpdate( false ); + m_lastFullFramebufferUpdate.restart(); + m_requestFullFramebufferUpdate = false; + } + else + { + m_vncClientProtocol->requestFramebufferUpdate( true ); + } +} + + + +bool DemoServer::receiveVncServerMessage() +{ + if( m_vncClientProtocol->receiveMessage() ) + { + if( m_vncClientProtocol->lastMessageType() == rfbFramebufferUpdate ) + { + enqueueFramebufferUpdateMessage( m_vncClientProtocol->lastMessage() ); + } + else + { + vWarning() << "skipping server message of type" << static_cast( m_vncClientProtocol->lastMessageType() ); + } + + return true; + } + + return false; +} + + + +void DemoServer::enqueueFramebufferUpdateMessage( const QByteArray& message ) +{ + QElapsedTimer writeLockTime; + writeLockTime.start(); + + m_dataLock.lockForWrite(); + + if( writeLockTime.elapsed() > 10 ) + { + vDebug() << "locking for write took" << writeLockTime.elapsed() << "ms"; + } + + const auto lastUpdatedRect = m_vncClientProtocol->lastUpdatedRect(); + + const bool isFullUpdate = ( lastUpdatedRect.x() == 0 && lastUpdatedRect.y() == 0 && + lastUpdatedRect.width() == m_vncClientProtocol->framebufferWidth() && + lastUpdatedRect.height() == m_vncClientProtocol->framebufferHeight() ); + + const auto queueSize = framebufferUpdateMessageQueueSize(); + + if( isFullUpdate || queueSize > m_memoryLimit*2 ) + { + if( m_keyFrameTimer.elapsed() > 1 ) + { + const auto memTotal = queueSize / 1024; + vDebug() + << " MEMTOTAL:" << memTotal + << " KB/s:" << ( memTotal * 1000 ) / m_keyFrameTimer.elapsed(); + } + m_keyFrameTimer.restart(); + ++m_keyFrame; + + m_framebufferUpdateMessages.clear(); + } + + m_framebufferUpdateMessages.append( message ); + + m_dataLock.unlock(); + + // we're about to reach memory limits? + if( framebufferUpdateMessageQueueSize() > m_memoryLimit ) + { + // then request a full update so we can clear our queue + m_requestFullFramebufferUpdate = true; + } +} + + + +qint64 DemoServer::framebufferUpdateMessageQueueSize() const +{ + qint64 size = 0; + + for( const auto& message : qAsConst( m_framebufferUpdateMessages ) ) + { + size += message.size(); + } + + return size; +} + + + +void DemoServer::start() +{ + vDebug(); + + setVncServerPixelFormat(); + setVncServerEncodings(); + + m_requestFullFramebufferUpdate = true; + + requestFramebufferUpdate(); + + while( receiveVncServerMessage() ) + { + } + + acceptPendingConnections(); +} + + + +bool DemoServer::setVncServerPixelFormat() +{ + rfbPixelFormat format; + + format.bitsPerPixel = 32; + format.depth = 32; + format.bigEndian = qFromBigEndian( 1 ) == 1 ? true : false; + format.trueColour = 1; + format.redShift = 16; + format.greenShift = 8; + format.blueShift = 0; + format.redMax = 0xff; + format.greenMax = 0xff; + format.blueMax = 0xff; + format.pad1 = 0; + format.pad2 = 0; + + return m_vncClientProtocol->setPixelFormat( format ); +} + + + +bool DemoServer::setVncServerEncodings() +{ + return m_vncClientProtocol-> + setEncodings( { + rfbEncodingUltraZip, + rfbEncodingUltra, + rfbEncodingCopyRect, + rfbEncodingHextile, + rfbEncodingCoRRE, + rfbEncodingRRE, + rfbEncodingRaw, + rfbEncodingCompressLevel9, + rfbEncodingQualityLevel7, + rfbEncodingNewFBSize, + rfbEncodingLastRect + } ); +} diff --git a/plugins/demo/DemoServer.h b/plugins/demo/DemoServer.h new file mode 100644 index 0000000..c9842ea --- /dev/null +++ b/plugins/demo/DemoServer.h @@ -0,0 +1,112 @@ +/* + * DemoServer.h - multi-threaded slim VNC-server for demo-purposes (optimized + * for lot of clients accessing server in read-only-mode) + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include +#include + +#include "CryptoCore.h" + +class DemoConfiguration; +class QTcpServer; +class QTcpSocket; +class VncClientProtocol; + +class DemoServer : public QTcpServer +{ + Q_OBJECT +public: + using Password = CryptoCore::SecureArray; + using MessageList = QVector; + + DemoServer( int vncServerPort, const Password& vncServerPassword, const Password& demoAccessToken, + const DemoConfiguration& configuration, int demoServerPort, QObject *parent ); + ~DemoServer() override; + + const DemoConfiguration& configuration() const + { + return m_configuration; + } + + const QByteArray& serverInitMessage() const; + + void lockDataForRead(); + + void unlockData() + { + m_dataLock.unlock(); + } + + int keyFrame() const + { + return m_keyFrame; + } + + const MessageList& framebufferUpdateMessages() const + { + return m_framebufferUpdateMessages; + } + +private: + void incomingConnection( qintptr socketDescriptor ) override; + void acceptPendingConnections(); + void reconnectToVncServer(); + void readFromVncServer(); + void requestFramebufferUpdate(); + + bool receiveVncServerMessage(); + void enqueueFramebufferUpdateMessage( const QByteArray& message ); + + qint64 framebufferUpdateMessageQueueSize() const; + + void start(); + bool setVncServerPixelFormat(); + bool setVncServerEncodings(); + + static constexpr auto ConnectionThreadWaitTime = 5000; + + const DemoConfiguration& m_configuration; + const qint64 m_memoryLimit; + const int m_keyFrameInterval; + const int m_vncServerPort; + const Password m_demoAccessToken; + + QList m_pendingConnections; + QTcpSocket* m_vncServerSocket; + VncClientProtocol* m_vncClientProtocol; + + QReadWriteLock m_dataLock; + QTimer m_framebufferUpdateTimer; + QElapsedTimer m_lastFullFramebufferUpdate; + QElapsedTimer m_keyFrameTimer; + bool m_requestFullFramebufferUpdate; + + int m_keyFrame; + MessageList m_framebufferUpdateMessages; + +} ; diff --git a/plugins/demo/DemoServerConnection.cpp b/plugins/demo/DemoServerConnection.cpp new file mode 100644 index 0000000..23ceed5 --- /dev/null +++ b/plugins/demo/DemoServerConnection.cpp @@ -0,0 +1,199 @@ +/* + * DemoServer.cpp - multi-threaded slim VNC-server for demo-purposes (optimized + * for lot of clients accessing server in read-only-mode) + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "rfb/rfbproto.h" + +#include + +#include "DemoConfiguration.h" +#include "DemoServer.h" +#include "DemoServerConnection.h" + + +DemoServerConnection::DemoServerConnection( DemoServer* demoServer, + const Password& demoAccessToken, + quintptr socketDescriptor ) : + QThread(), + m_demoAccessToken( demoAccessToken ), + m_demoServer( demoServer ), + m_socketDescriptor( socketDescriptor ), + m_vncServerClient(), + m_rfbClientToServerMessageSizes( { + std::pair( rfbSetPixelFormat, sz_rfbSetPixelFormatMsg ), + std::pair( rfbFramebufferUpdateRequest, sz_rfbFramebufferUpdateRequestMsg ), + std::pair( rfbKeyEvent, sz_rfbKeyEventMsg ), + std::pair( rfbPointerEvent, sz_rfbPointerEventMsg ), + } ), + m_keyFrame( -1 ), + m_framebufferUpdateMessageIndex( 0 ), + m_framebufferUpdateInterval( m_demoServer->configuration().framebufferUpdateInterval() ) +{ + start(); +} + + + +void DemoServerConnection::run() +{ + vDebug() << m_socketDescriptor; + + m_socket = new QTcpSocket; + + if( m_socket->setSocketDescriptor( m_socketDescriptor ) == false ) + { + vCritical() << "failed to set socket descriptor"; + delete m_socket; + m_socket = nullptr; + deleteLater(); + return; + } + + connect( m_socket, &QTcpSocket::readyRead, this, &DemoServerConnection::processClient, Qt::DirectConnection ); + connect( m_socket, &QTcpSocket::disconnected, this, &DemoServerConnection::quit ); + + m_serverProtocol = new DemoServerProtocol( m_demoAccessToken, m_socket, &m_vncServerClient ), + + m_serverProtocol->setServerInitMessage( m_demoServer->serverInitMessage() ); + m_serverProtocol->start(); + + exec(); + + delete m_serverProtocol; + delete m_socket; + + m_socket = nullptr; + + deleteLater(); +} + + + +void DemoServerConnection::processClient() +{ + if( m_serverProtocol->state() != VncServerProtocol::State::Running ) + { + while( m_serverProtocol->read() ) + { + } + + // try again later in case we could not proceed because of + // external protocol dependencies or in case we're finished + // and already have RFB messages in receive queue + QTimer::singleShot( ProtocolRetryTime, [this]() { processClient(); } ); + } + else + { + while( receiveClientMessage() ) + { + } + } +} + + + +bool DemoServerConnection::receiveClientMessage() +{ + char messageType = 0; + if( m_socket->peek( &messageType, sizeof(messageType) ) != sizeof(messageType) ) + { + return false; + } + + switch( messageType ) + { + case rfbSetEncodings: + if( m_socket->bytesAvailable() >= sz_rfbSetEncodingsMsg ) + { + rfbSetEncodingsMsg setEncodingsMessage; + if( m_socket->peek( reinterpret_cast( &setEncodingsMessage ), sz_rfbSetEncodingsMsg ) == sz_rfbSetEncodingsMsg ) + { + const qint64 totalSize = sz_rfbSetEncodingsMsg + qFromBigEndian(setEncodingsMessage.nEncodings) * sizeof(uint32_t); + if( m_socket->bytesAvailable() >= totalSize ) + { + return m_socket->read( totalSize ).size() == totalSize; + } + } + } + break; + + default: + if( m_rfbClientToServerMessageSizes.contains( messageType ) == false ) + { + vCritical() << "received unknown message type:" << static_cast( messageType ); + m_socket->close(); + return false; + } + + // do not yet read any data if not enough is available for reading + if( m_socket->bytesAvailable() < m_rfbClientToServerMessageSizes[messageType] ) + { + return false; + } + + m_socket->read( m_rfbClientToServerMessageSizes[messageType] ); + + if( messageType == rfbFramebufferUpdateRequest ) + { + sendFramebufferUpdate(); + } + + return true; + } + + return false; +} + + + +void DemoServerConnection::sendFramebufferUpdate() +{ + m_demoServer->lockDataForRead(); + + const auto& framebufferUpdateMessages = m_demoServer->framebufferUpdateMessages(); + + const int framebufferUpdateMessageCount = framebufferUpdateMessages.count(); + + if( m_demoServer->keyFrame() != m_keyFrame || + m_framebufferUpdateMessageIndex > framebufferUpdateMessageCount ) + { + m_framebufferUpdateMessageIndex = 0; + m_keyFrame = m_demoServer->keyFrame(); + } + + bool sentUpdates = false; + for( ; m_framebufferUpdateMessageIndex < framebufferUpdateMessageCount; ++m_framebufferUpdateMessageIndex ) + { + m_socket->write( framebufferUpdateMessages[m_framebufferUpdateMessageIndex] ); + sentUpdates = true; + } + + m_demoServer->unlockData(); + + if( sentUpdates == false ) + { + // did not send updates but client still waiting for update? then try again soon + QTimer::singleShot( m_framebufferUpdateInterval, [this]() { sendFramebufferUpdate(); } ); + } +} diff --git a/plugins/demo/DemoServerConnection.h b/plugins/demo/DemoServerConnection.h new file mode 100644 index 0000000..1c55d45 --- /dev/null +++ b/plugins/demo/DemoServerConnection.h @@ -0,0 +1,73 @@ +/* + * DemoServerConnection.h - header file for DemoServerConnection class + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "DemoServerProtocol.h" + +class DemoServer; + +// clazy:excludeall=ctor-missing-parent-argument + +// the demo server creates an instance of this class for each client connection, +// i.e. each client is connected to a different server thread for best +// performance +class DemoServerConnection : public QThread +{ + Q_OBJECT +public: + using Password = CryptoCore::PlaintextPassword; + + enum { + ProtocolRetryTime = 250, + }; + + DemoServerConnection( DemoServer* demoServer, const Password& demoAccessToken, quintptr socketDescriptor ); + ~DemoServerConnection() = default; + +private: + void run() override; + + void processClient(); + void sendFramebufferUpdate(); + + bool receiveClientMessage(); + + const Password m_demoAccessToken; + DemoServer* m_demoServer; + + quintptr m_socketDescriptor; + QTcpSocket* m_socket{nullptr}; + + VncServerClient m_vncServerClient; + DemoServerProtocol* m_serverProtocol{nullptr}; + + const QMap m_rfbClientToServerMessageSizes; + + int m_keyFrame; + int m_framebufferUpdateMessageIndex; + + const int m_framebufferUpdateInterval; + +} ; diff --git a/plugins/demo/DemoServerProtocol.cpp b/plugins/demo/DemoServerProtocol.cpp new file mode 100644 index 0000000..93e34a6 --- /dev/null +++ b/plugins/demo/DemoServerProtocol.cpp @@ -0,0 +1,88 @@ +/* + * DemoServerProtocol.cpp - implementation of DemoServerProtocol class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "DemoServerProtocol.h" +#include "VariantArrayMessage.h" +#include "VncServerClient.h" + + +DemoServerProtocol::DemoServerProtocol( const Token& demoAccessToken, QTcpSocket* socket, VncServerClient* client ) : + VncServerProtocol( socket, client ), + m_demoAccessToken( demoAccessToken ) +{ +} + + + +QVector DemoServerProtocol::supportedAuthTypes() const +{ + return { RfbVeyonAuth::Token }; +} + + + +void DemoServerProtocol::processAuthenticationMessage( VariantArrayMessage& message ) +{ + if( client()->authType() == RfbVeyonAuth::Token ) + { + client()->setAuthState( performTokenAuthentication( message ) ); + } + else + { + client()->setAuthState( VncServerClient::AuthState::Failed ); + } +} + + + +void DemoServerProtocol::performAccessControl() +{ + client()->setAccessControlState( VncServerClient::AccessControlState::Successful ); +} + + + +VncServerClient::AuthState DemoServerProtocol::performTokenAuthentication( VariantArrayMessage& message ) +{ + switch( client()->authState() ) + { + case VncServerClient::AuthState::Init: + return VncServerClient::AuthState::Token; + + case VncServerClient::AuthState::Token: + if( Token( message.read().toByteArray() ) == m_demoAccessToken ) + { + vDebug() << "SUCCESS"; + return VncServerClient::AuthState::Successful; + } + + vDebug() << "FAIL"; + return VncServerClient::AuthState::Failed; + + default: + break; + } + + return VncServerClient::AuthState::Failed; +} diff --git a/plugins/demo/DemoServerProtocol.h b/plugins/demo/DemoServerProtocol.h new file mode 100644 index 0000000..d688f11 --- /dev/null +++ b/plugins/demo/DemoServerProtocol.h @@ -0,0 +1,49 @@ +/* + * DemoServerProtocol.h - header file for DemoServerProtocol class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VncServerClient.h" +#include "VncServerProtocol.h" + +// clazy:excludeall=copyable-polymorphic + +class DemoServerProtocol : public VncServerProtocol +{ +public: + using Token = CryptoCore::PlaintextPassword; + + DemoServerProtocol( const Token& demoAccessToken, QTcpSocket* socket, VncServerClient* client ); + +protected: + QVector supportedAuthTypes() const override; + void processAuthenticationMessage( VariantArrayMessage& message ) override; + void performAccessControl() override; + +private: + VncServerClient::AuthState performTokenAuthentication( VariantArrayMessage& message ); + + const Token m_demoAccessToken; + +} ; diff --git a/plugins/demo/demo.png b/plugins/demo/demo.png new file mode 100644 index 0000000000000000000000000000000000000000..4c7629e86fa76a4972a11ebbffa95ab1065a9b84 GIT binary patch literal 5302 zcmV;n6iMreP)k8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H16f;RgK~#9!?Ol14RMnaPeXo`V*-_#KsAyMrg95>IJY%HMXjDWo z(g>}%#3f2lG|9vqXPnlUxP%}kAx>sH5tXF{QIyHx(%m{Uj>o9%bXT{4;26;Zbju=L zRqx$z{^+XiTJBrxy{hWweVjT6iuc}q@BV(@dcSuI@WmHjeDTE>UwrY!7hinw#TQ?W z7NNfQP<4$69v)qGT2Mm;fJ#A#5c4Sjh5#7AzyJb<0vMP)T9G2~dLa;3fH3^(7DW117HdW69Fg%z6AlodKPFE0M< zA;tbzqQwkc&_~nmj|KDGg<})%`vgdn`Rg$>*e~=5nC1cqpl^!+@FEA|5B#PAdyjeo z{G>TN8%&EBJOW3P5PwBX_bnJ3Uw%{)pl&Dn9rz~nB!TY5(JjxD8oztN1ROqU2ykCZ zS%o6Fo*3id3jj1Lgqj6o<6HVX0e-wWbQ2Rj3t)hcdxjhW3K@d0z60Zdf;y9x1SB};(08_FU9hz|h_^6~OS4k;R)x9IG6bIB3lmYwJ~ z=_F*?Z zZtH(qSgyvbSps5y$f*@@WC2E;8or*HBhCoUeM9pLWKlE=mVklEJs*ar1MyYf6M#YD zL;gE&Z6+7h3YVHkQDa?Jz?cO5_h7ti_gzHWX5+5EluZR=lG7NSX(mHIfB1)L;- zc0@lrAjZVIJ`7w@02d(U=c92CZ_Mlje{{me9QW|T#s{%+&zpwjDk6!tkdZmyjB~A& z#O#g1S?6Y6aQwF}L+!ZSe%J@!nPhk`oc2{BX$b=Ta;d}j025_;v zCa*{HvlGU?0)7%5-Ne`m_^B_{PU?ya7b;_9UhPb=iay z0fHryCUTFHfx~PLs=YCC50s73^-Q^dXMB}*FP!~V+Hr|afSF{LKv5_$3&5{EiUDZ0 z`%#Z1<;znm^}u&i^Yy?t)@WJ$VomciEeW8;pkoQ>W=~we9Up|wWyUzy({V}Amk|u5 zSxS*}uADn5ZA<^YUvO~FaruqGSuko z-Y#?ay!mf-AL8Zp#00w@Sy42I%O-9tYxVwq>i$`(oHEFmqFbjKHi%Z|gH z=Pbs!Z(VF-jI6js2EI;Q(ep_17$D<0GRDr0g^b3~Qtp5e2=ptv$Tb2mbG28sFP+FR zw<^SCT5rHe^v&ygMQ zN#?+Cj8t|ICxAt@YcT+`yTu=~7$EyQLaslSCdj7lA@{nMj=2pZjv0-W&5t9Y#w25~ zX`V=WDJEw-dp5QjS*f1%IJ3f3GB6|!m$+g9>KYMbLX~G0K)M9-gd9-ehQ7c@WTdXl6!{rQOV93%Nrsj{xoT)|BV@OiLo3>YEr);{zXL`c>Y zWYyjc^CXEP99VtXPw0UlEt{TM|&k>i>&*T#4eT?k@X!j!mWw2)AA-j z0;ni-gH^X}u%P)c$m#7iat$gw7I&We491SQ$XxTuqJ<-~@$=Vwy*Ut?T9r2eh_nh% zS3vbrK-SYNwiFS|FK2#3;Xps!IPp{lBYqv&ZZ4Of3 z5yJ%wz{Rov?l*JTwE&y;G-B1Zg&9F0k;SKxEbOpJ3z%D)-xFZEVFB3uBCeN$SsE}q z()?5jIcKTZ)y&*K)#ya?Z++5 zc0%h!{g$V&^{f9e*FcM!Pn2wTXSO}RcH$lFP6w?LA`>Cw@Ao%=iFf(xL701q#u1+dUzrC*dWXy!1o zET45Wz`&vr@gxQ#1&EC!?DlVxbw zzBIQ7!J_6HwQjo>PdTgv3Ub8);6TAGKvFAo3px%OIJ13lLhZzgO~1jmul`p~FpHYc zGVl#Lmuzdl6U|QxYg{M<@hj00|)3} z^-pp_G;abZ;HcojS^yc^Qt+jHg8B`6oA&${R&9O;sus6oYNcjhuXWqZB3pzpz2 zk;lA*%$z>~SOlZ2c=@xp=F2v0wK8VVt=YD312*mXZ_CTAdT;EoH1W4`J(j-+>!RjU z&XB4aNb43uXZf8iQ`nUtnyR5Pp}CMffvG-8*}DPqK0!9`n}hw%S^?PZ+t*83MfW*Ervp;| zT)WRE00`#hmuk#^vE15w0Rr!kr>};Y+Xel11K759c%k{Gn#!E>04by3#+S|d*jfL$BmM05lnh%52(N1SAXKSDfZ|w9`ID0-R^4jkC*BLk6Vv@#c=5JBLDW3I%7xDtIwEChcRvJo@0-~A8bkJnB`yK4kUUiWRV`vfSZ zUH-TTS7x@a`OF63cOBDc_0BBs5F&``y_uTcwVSv7kJ*OTE^2=AF9cfdIssN)miQ0? zTik97?|r*@yS>%Dy*Brpmh+4~wip1KS6!1>?>YgJ#f7E4!2*=VuAf%7x_3|WlP<&7 zPxjf0ooBp;lSLr7%gt;+>m;a06odTTLbR_lX;aF-kLvR4bjNOJTLo1la9M@^?^?gR zV-4PjE_YNywwo#B4L%^CsHeVqEk3h4|F(eKwhRZy)+*ltFvcwcD9JFCRdN~p>rB9Z z84aJjj9u-mP(>W4A9tqoxKoBzAQ%W^$G$D44nBy`@alB&dpq-QTazni(|(Y<8%8Rj zt_cuH666bhb@_FuE}w1!{_5`e>mk879TG$k41}_^Z*-wmN54ceh7PNe~Kzdq_}X;1jTE&2`$3 zVe=Q89r^N#+0+5h47US9O1cA~lA}qRQa&Bkk!wxBeD&zw z-n{2T<$C}|dO?C5OP~w>HJD-u{;RgY?~V!fw?Wks7^6di(Zk9SR6^LXcUw`xX9XXf zy~UaK=S6^~CU9kKU_AprfQvsuooxw-0M_LFPg5e-pgJJ8Tj`6_U>+XP$jKnjviKykP^afS+AkqyAey^P6`??~Gc0k65`C zzyQxk5KOBR5Kvt)4b_pW4K#lZ8e3n>Wrf2&g5B+HP^Bc`q&qkRp+E@R_ca#`dF08%jhgB0oshm!iCn#re6Lv@7#_|ezUln?ki0Pb%81PPJAm~Ikq zR!q=Ef(%RGndSol=G$JM#UJH6uddYeqsw_qWHyZiXp)yCxcIbbs4h1Kzp?do$Kacl zZFjeSlC%VsI)ULC$?TxTQ}99j$F()ulWxqZYl}K3-N|qE>r|!!IKd0>FB)|{+bWn*I!?AX`hmgciUeA}OE)CN~$ zD=*CTRWAo00^uKc90Qf)8dqv~HIAE7yZ-uL8EKRtKL3c)nXC z5OF$c!6(8${duN#w--1Cydpr`a`8ravvL|-c%vwl0oR)gc zMDdCMAn*WHyMqbqT)ELOLkaUy1wIkp7#z@Us;iUlc~;UcASE;E5k4FY{D~Q}TyY0+ zO8zL4G5B6LX;{6UF8Jmx53j^x+N`Eq^Q-g~od78}cg3pUQy~6{Yb1zKp9LSlA1-Xw z=6R|4g(W~zu3V|y$DpU(7`NLJ$bMnW%2T?%N3T2JGl3pkSF1i;C`<}U008*j%D^lD z{|w~62XPk(`Udy}93;jM*WIWsEhHWVCjbCkxf~HKpsXO!#Jow6AZW2(uK5ZzvruNf zbz^*Up|J=Qg8*$Sv2WCs{G1XF3X+qqL}kvbOHdtGcMCIm|2KCZA(re9M9m~{^F=$vIi&=C$ytDO zsjGwXURB^K2L1tn?WR+a=yD0g)qLIpK?{#PMq6TK%Ql|?@|mXZ)(5U&#{B@M*s)ri zAZUsF!6ym$&<9@tXjFhjZ_m-zf=Xx)oL)(Q^cZ0oP68=27?@+V|4<}!UNdtWd{7Gm zE5nNV?{Cb(&fYSoy@~+oa_O?rw*_+z5nqC?-OB<-*#WEeXTk)04aDmi)WAf`-@V!T zwk$pY+$KXkh6pwA9Yvuj3^5r@W0AGDu^|a8fG-GfV$sa3yhDN;gPPX()?N6zx6fUl zAV9Xvs7GL*z^SYSDiq>M0g5QVsagy}SQkeDTE>UwrY!7hinw#TP5`|D-;iZu?00egFUf07*qo IM6N<$f&|&}s{jB1 literal 0 HcmV?d00001 diff --git a/plugins/demo/demo.qrc b/plugins/demo/demo.qrc new file mode 100644 index 0000000..c3c5fc5 --- /dev/null +++ b/plugins/demo/demo.qrc @@ -0,0 +1,8 @@ + + + demo.png + presentation-fullscreen.png + presentation-window.png + window-duplicate.png + + diff --git a/plugins/demo/presentation-fullscreen.png b/plugins/demo/presentation-fullscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..bcaf791f62c9d0f6588527201e5bd7a30b848f46 GIT binary patch literal 354 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&F&8^|hH!9j+9gP2NC6ck2l#}z z0_h)9*ndpr_%V&^$29IA(|Lc);Quj0;Kxj%pRgRTJ_f5TnXCj>CcTO_Cz zmRz#>$Hc(!FK@M#ec$@;bKmr@s{YY^GOmGvk%@&vK*0gRh>+XB&ah!dbt9^pQ{<C00003b3#c}2nYz< z;ZNWI000SaNLh0L01ejw01ejxLMWSf0000PbVXQnQ*UN;cVTj60C#tHE@^ISb7Ns} zWiD@WXPfRk8UO$Rr%+5(MF0Q*8GZ2?fAJcD@*0Ek8in#2hw~bV^Barv8;$cDkMtXn z^c<7)9F_GPm-QT(^&Fe^9i8?apY|Q0_8p`49i{gkr}rMI_a3YG9ErkVvnQNUxDeuaQcxl1i|WOR$qnu#-%&lTERdPO+6wvX)S? zmQk{nQL~p)vzJq|m{YWvRJ52?w3$`3nO3!$SGAg0wwqbD#uOvR6(qh$rmTe z7%0jaDa#ou%o;1r8!XKnEzTS+&mAw%9x%`zG0`6~(I7I?Av4nmPK-?=q_>~CwmJ9fo4)~c7`I;2@n-}?=8u^|b z`JW;Apd|UCC;6i)`lK!TrZD=aGy14DvT5~p00001bW%=J06^y0W&i*Hs7XXYRCwC$ z*|816Fcd}67l}Q>B1r5(Hi42+7z8m%HZe`g6xbFbBIh)&zI5Xhj+_Jl0NA@BNzRSK z&t;t&=O0N9Nm4qWqG$d;06pEf1*rQ1 z!-fd8S2c_(r@! + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "DesktopServiceObject.h" + + +DesktopServiceObject::DesktopServiceObject( const DesktopServiceObject& other ) : + m_type( other.type() ), + m_name( other.name() ), + m_path( other.path() ), + m_uid( other.uid() ) +{ +} + + + +DesktopServiceObject::DesktopServiceObject( DesktopServiceObject::Type type, + const Name& name, + const QString& path, + Uid uid ) : + m_type( type ), + m_name( name ), + m_path( path ), + m_uid( uid ) +{ + if( m_uid.isNull() ) + { + m_uid = QUuid::createUuid(); + } +} + + + +DesktopServiceObject::DesktopServiceObject( const QJsonObject& jsonObject ) : + m_type( static_cast( jsonObject.value( QStringLiteral( "Type" ) ).toInt() ) ), + m_name( jsonObject.value( QStringLiteral( "Name" ) ).toString() ), + m_path( jsonObject.value( QStringLiteral( "Path" ) ).toString() ), + m_uid( jsonObject.value( QStringLiteral( "Uid" ) ).toString() ) +{ +} + + + +bool DesktopServiceObject::operator==( const DesktopServiceObject& other ) const +{ + return uid() == other.uid(); +} + + + +QJsonObject DesktopServiceObject::toJson() const +{ + QJsonObject json; + json[QStringLiteral("Type")] = static_cast( type() ); + json[QStringLiteral("Name")] = name(); + json[QStringLiteral("Path")] = path(); + json[QStringLiteral("Uid")] = uid().toString(); + + return json; +} diff --git a/plugins/desktopservices/DesktopServiceObject.h b/plugins/desktopservices/DesktopServiceObject.h new file mode 100644 index 0000000..77626f5 --- /dev/null +++ b/plugins/desktopservices/DesktopServiceObject.h @@ -0,0 +1,90 @@ +/* + * DesktopServiceObject.h - data class representing a desktop service object + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include + +class QJsonObject; + +class DesktopServiceObject +{ +public: + using Uid = QUuid; + using Name = QString; + + enum class Type + { + None, + Program, + Website, + } ; + + DesktopServiceObject( const DesktopServiceObject& other ); + explicit DesktopServiceObject( Type type = Type::None, + const Name& name = {}, + const QString& path = {}, + Uid uid = Uid() ); + explicit DesktopServiceObject( const QJsonObject& jsonObject ); + + bool operator==( const DesktopServiceObject& other ) const; + + const Uid& uid() const + { + return m_uid; + } + + Uid parentUid() const + { + return Uid(); + } + + Type type() const + { + return m_type; + } + + const Name& name() const + { + return m_name; + } + + const QString& path() const + { + return m_path; + } + + QJsonObject toJson() const; + +private: + Type m_type; + QString m_name; + QString m_path; + Uid m_uid; + +}; + +Q_DECLARE_METATYPE(DesktopServiceObject::Type) diff --git a/plugins/desktopservices/DesktopServicesConfiguration.h b/plugins/desktopservices/DesktopServicesConfiguration.h new file mode 100644 index 0000000..c31137b --- /dev/null +++ b/plugins/desktopservices/DesktopServicesConfiguration.h @@ -0,0 +1,34 @@ +/* + * DesktopServicesConfiguration.h - configuration values for DesktopServices + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonConfiguration.h" +#include "Configuration/Proxy.h" + +#define FOREACH_DESKTOP_SERVICES_CONFIG_PROPERTY(OP) \ + OP( DesktopServicesConfiguration, m_configuration, QJsonArray, predefinedPrograms, setPredefinedPrograms, "PredefinedPrograms", "DesktopServices", QJsonArray(), Configuration::Property::Flag::Standard ) \ + OP( DesktopServicesConfiguration, m_configuration, QJsonArray, predefinedWebsites, setPredefinedWebsites, "PredefinedWebsites", "DesktopServices", QJsonArray(), Configuration::Property::Flag::Standard ) \ + +DECLARE_CONFIG_PROXY(DesktopServicesConfiguration, FOREACH_DESKTOP_SERVICES_CONFIG_PROPERTY) diff --git a/plugins/desktopservices/DesktopServicesConfigurationPage.cpp b/plugins/desktopservices/DesktopServicesConfigurationPage.cpp new file mode 100644 index 0000000..bec0905 --- /dev/null +++ b/plugins/desktopservices/DesktopServicesConfigurationPage.cpp @@ -0,0 +1,219 @@ +/* + * DesktopServicesConfigurationPage.cpp - implementation of the DesktopServicesConfigurationPage class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "DesktopServicesConfiguration.h" +#include "DesktopServicesConfigurationPage.h" +#include "ObjectManager.h" +#include "Configuration/UiMapping.h" + +#include "ui_DesktopServicesConfigurationPage.h" + +DesktopServicesConfigurationPage::DesktopServicesConfigurationPage( DesktopServicesConfiguration& configuration, QWidget* parent ) : + ConfigurationPage( parent ), + ui( new Ui::DesktopServicesConfigurationPage ), + m_configuration( configuration ) +{ + ui->setupUi( this ); +} + + + +DesktopServicesConfigurationPage::~DesktopServicesConfigurationPage() +{ + delete ui; +} + + + +void DesktopServicesConfigurationPage::resetWidgets() +{ + loadObjects( m_configuration.predefinedPrograms(), ui->programTable ); + loadObjects( m_configuration.predefinedWebsites(), ui->websiteTable ); +} + + + +void DesktopServicesConfigurationPage::connectWidgetsToProperties() +{ +} + + + +void DesktopServicesConfigurationPage::applyConfiguration() +{ +} + + + +void DesktopServicesConfigurationPage::addProgram() +{ + auto programs = m_configuration.predefinedPrograms(); + + addServiceObject( ui->programTable, DesktopServiceObject::Type::Program, tr( "New program" ), programs ); + + m_configuration.setPredefinedPrograms( programs ); +} + + + +void DesktopServicesConfigurationPage::updateProgram() +{ + auto programs = m_configuration.predefinedPrograms(); + + updateServiceObject( ui->programTable, DesktopServiceObject::Type::Program, programs ); + + m_configuration.setPredefinedPrograms( programs ); +} + + + +void DesktopServicesConfigurationPage::removeProgram() +{ + auto programs = m_configuration.predefinedPrograms(); + + removeServiceObject( ui->programTable, DesktopServiceObject::Type::Program, programs ); + + m_configuration.setPredefinedPrograms( programs ); +} + + + +void DesktopServicesConfigurationPage::addWebsite() +{ + auto websites = m_configuration.predefinedWebsites(); + + addServiceObject( ui->websiteTable, DesktopServiceObject::Type::Website, tr( "New website" ), websites ); + + m_configuration.setPredefinedWebsites( websites ); + +} + +void DesktopServicesConfigurationPage::updateWebsite() +{ + auto websites = m_configuration.predefinedWebsites(); + + updateServiceObject( ui->websiteTable, DesktopServiceObject::Type::Website, websites ); + + m_configuration.setPredefinedWebsites( websites ); +} + + + +void DesktopServicesConfigurationPage::removeWebsite() +{ + auto websites = m_configuration.predefinedWebsites(); + + removeServiceObject( ui->websiteTable, DesktopServiceObject::Type::Website, websites ); + + m_configuration.setPredefinedWebsites( websites ); +} + + + +void DesktopServicesConfigurationPage::addServiceObject( QTableWidget* tableWidget, DesktopServiceObject::Type type, + const QString& name, QJsonArray& objects ) +{ + ObjectManager objectManager( objects ); + objectManager.add( DesktopServiceObject( type, name ) ); + objects = objectManager.objects(); + + loadObjects( objects, tableWidget ); + + tableWidget->setCurrentCell( tableWidget->rowCount()-1, 0 ); +} + + + +void DesktopServicesConfigurationPage::updateServiceObject( QTableWidget* tableWidget, DesktopServiceObject::Type type, QJsonArray& objects ) +{ + auto currentServiceObjectIndex = tableWidget->currentIndex(); + if( currentServiceObjectIndex.isValid() == false ) + { + return; + } + + ObjectManager objectManager( objects ); + objectManager.update( currentServiceObject( tableWidget, type ) ); + objects = objectManager.objects(); + + loadObjects( objects, tableWidget ); + + tableWidget->setCurrentIndex( currentServiceObjectIndex ); +} + + + +void DesktopServicesConfigurationPage::removeServiceObject( QTableWidget* tableWidget, DesktopServiceObject::Type type, QJsonArray& objects ) +{ + ObjectManager objectManager( objects ); + objectManager.remove( currentServiceObject( tableWidget, type ).uid() ); + objects = objectManager.objects(); + + loadObjects( objects, tableWidget ); +} + + + +DesktopServiceObject DesktopServicesConfigurationPage::currentServiceObject( QTableWidget* tableWidget, DesktopServiceObject::Type type ) +{ + const auto row = tableWidget->currentRow(); + + if( row >= 0 ) + { + auto nameItem = tableWidget->item( row, 0 ); + auto pathItem = tableWidget->item( row, 1 ); + + return DesktopServiceObject( type, + nameItem->text(), + pathItem->text(), + nameItem->data( Qt::UserRole ).toUuid() ); + } + + return DesktopServiceObject(); +} + + + +void DesktopServicesConfigurationPage::loadObjects( const QJsonArray& objects, QTableWidget* tableWidget ) +{ + tableWidget->setUpdatesEnabled( false ); + tableWidget->setRowCount( 0 ); + + int rowCount = 0; + + for( const auto& jsonValue : objects ) + { + const auto serviceObject = DesktopServiceObject( jsonValue.toObject() ); + + auto item = new QTableWidgetItem( serviceObject.name() ); + item->setData( Qt::UserRole, serviceObject.uid() ); + + tableWidget->setRowCount( rowCount+1 ); + tableWidget->setItem( rowCount, 0, item ); + tableWidget->setItem( rowCount, 1, new QTableWidgetItem( serviceObject.path() ) ); + ++rowCount; + } + + tableWidget->setUpdatesEnabled( true ); +} diff --git a/plugins/desktopservices/DesktopServicesConfigurationPage.h b/plugins/desktopservices/DesktopServicesConfigurationPage.h new file mode 100644 index 0000000..093f720 --- /dev/null +++ b/plugins/desktopservices/DesktopServicesConfigurationPage.h @@ -0,0 +1,69 @@ +/* + * DesktopServicesConfigurationPage.h - header for the DesktopServicesConfigurationPage class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "ConfigurationPage.h" +#include "DesktopServiceObject.h" + +class QTableWidget; +class DesktopServicesConfiguration; + +namespace Ui { +class DesktopServicesConfigurationPage; +} + +class DesktopServicesConfigurationPage : public ConfigurationPage +{ + Q_OBJECT +public: + explicit DesktopServicesConfigurationPage( DesktopServicesConfiguration& configuration, QWidget* parent = nullptr ); + ~DesktopServicesConfigurationPage() override; + + void resetWidgets() override; + void connectWidgetsToProperties() override; + void applyConfiguration() override; + +private Q_SLOTS: + void addProgram(); + void updateProgram(); + void removeProgram(); + void addWebsite(); + void updateWebsite(); + void removeWebsite(); + +private: + static void addServiceObject( QTableWidget* tableWidget, DesktopServiceObject::Type type, const QString& name, QJsonArray& objects ); + static void updateServiceObject( QTableWidget* tableWidget, DesktopServiceObject::Type type, QJsonArray& objects ); + static void removeServiceObject( QTableWidget* tableWidget, DesktopServiceObject::Type type, QJsonArray& objects ); + + static DesktopServiceObject currentServiceObject( QTableWidget* tableWidget, DesktopServiceObject::Type type ); + + static void loadObjects( const QJsonArray& objects, QTableWidget* tableWidget ); + + Ui::DesktopServicesConfigurationPage *ui; + + DesktopServicesConfiguration& m_configuration; + +}; diff --git a/plugins/desktopservices/DesktopServicesConfigurationPage.ui b/plugins/desktopservices/DesktopServicesConfigurationPage.ui new file mode 100644 index 0000000..3a20e90 --- /dev/null +++ b/plugins/desktopservices/DesktopServicesConfigurationPage.ui @@ -0,0 +1,283 @@ + + + DesktopServicesConfigurationPage + + + Programs & websites + + + + :/desktopservices/desktop-services.png:/desktopservices/desktop-services.png + + + + 0 + + + 0 + + + + + Predefined programs + + + + + + QAbstractItemView::SelectRows + + + 150 + + + true + + + false + + + + Name + + + + + Path + + + + + + + + Add new program + + + + + + + :/core/list-add.png:/core/list-add.png + + + + + + + Remove selected program + + + + + + + :/core/edit-delete.png:/core/edit-delete.png + + + + + + + Qt::Vertical + + + + 25 + 161 + + + + + + + + + + + Predefined websites + + + + + + Add new program + + + + + + + :/core/list-add.png:/core/list-add.png + + + + + + + Remove selected website + + + + + + + :/core/edit-delete.png:/core/edit-delete.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + QAbstractItemView::SelectRows + + + 150 + + + true + + + false + + + + Name + + + + + URL + + + + + + + + + + + + + + + + addProgramButton + clicked() + DesktopServicesConfigurationPage + addProgram() + + + 296 + 49 + + + 165 + 259 + + + + + removeProgramButton + clicked() + DesktopServicesConfigurationPage + removeProgram() + + + 296 + 79 + + + 165 + 259 + + + + + addWebsiteButton + clicked() + DesktopServicesConfigurationPage + addWebsite() + + + 296 + 317 + + + 165 + 259 + + + + + removeWebsiteButton + clicked() + DesktopServicesConfigurationPage + removeWebsite() + + + 296 + 347 + + + 165 + 259 + + + + + programTable + cellChanged(int,int) + DesktopServicesConfigurationPage + updateProgram() + + + 148 + 148 + + + 165 + 259 + + + + + websiteTable + cellChanged(int,int) + DesktopServicesConfigurationPage + updateWebsite() + + + 148 + 401 + + + 165 + 259 + + + + + + addProgram() + removeProgram() + addWebsite() + removeWebsite() + updateProgram() + updateWebsite() + + diff --git a/plugins/desktopservices/DesktopServicesFeaturePlugin.cpp b/plugins/desktopservices/DesktopServicesFeaturePlugin.cpp new file mode 100644 index 0000000..3324b18 --- /dev/null +++ b/plugins/desktopservices/DesktopServicesFeaturePlugin.cpp @@ -0,0 +1,508 @@ +/* + * DesktopServicesFeaturePlugin.cpp - implementation of DesktopServicesFeaturePlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include + +#include "Computer.h" +#include "ComputerControlInterface.h" +#include "DesktopServicesConfigurationPage.h" +#include "DesktopServicesFeaturePlugin.h" +#include "FeatureWorkerManager.h" +#include "ObjectManager.h" +#include "OpenWebsiteDialog.h" +#include "PlatformCoreFunctions.h" +#include "PlatformUserFunctions.h" +#include "RunProgramDialog.h" +#include "VeyonConfiguration.h" +#include "VeyonMasterInterface.h" +#include "VeyonServerInterface.h" + + +DesktopServicesFeaturePlugin::DesktopServicesFeaturePlugin( QObject* parent ) : + QObject( parent ), + m_configuration( &VeyonCore::config() ), + m_runProgramFeature( QStringLiteral( "RunProgram" ), + Feature::Action | Feature::AllComponents, + Feature::Uid( "da9ca56a-b2ad-4fff-8f8a-929b2927b442" ), + Feature::Uid(), + tr( "Run program" ), {}, + tr( "Click this button to run a program on all computers." ), + QStringLiteral(":/desktopservices/preferences-desktop-launch-feedback.png") ), + m_openWebsiteFeature( QStringLiteral( "OpenWebsite" ), + Feature::Action | Feature::AllComponents, + Feature::Uid( "8a11a75d-b3db-48b6-b9cb-f8422ddd5b0c" ), + Feature::Uid(), + tr( "Open website" ), {}, + tr( "Click this button to open a website on all computers." ), + QStringLiteral(":/desktopservices/internet-web-browser.png") ), + m_predefinedProgramsFeatures(), + m_predefinedWebsitesFeatures(), + m_features( { m_runProgramFeature, m_openWebsiteFeature } ) +{ + connect( VeyonCore::instance(), &VeyonCore::applicationLoaded, + this, &DesktopServicesFeaturePlugin::updateFeatures ); +} + + + +bool DesktopServicesFeaturePlugin::controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( operation != Operation::Start ) + { + return false; + } + + if( featureUid == m_runProgramFeature.uid() ) + { + const auto programs = arguments.value( argToString(Argument::Programs) ).toStringList(); + + sendFeatureMessage( FeatureMessage{ featureUid, FeatureMessage::DefaultCommand } + .addArgument( Argument::Programs, programs ), + computerControlInterfaces ); + + return true; + } + + if( featureUid == m_openWebsiteFeature.uid() ) + { + const auto websites = arguments.value( argToString(Argument::WebsiteUrl) ).toStringList(); + + sendFeatureMessage( FeatureMessage{ featureUid, FeatureMessage::DefaultCommand } + .addArgument( Argument::WebsiteUrl, websites ), + computerControlInterfaces ); + + return true; + } + + return false; +} + + + +bool DesktopServicesFeaturePlugin::startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( feature == m_runProgramFeature ) + { + runProgram( master, computerControlInterfaces ); + } + else if( feature == m_openWebsiteFeature ) + { + openWebsite( master, computerControlInterfaces ); + } + else if( m_predefinedProgramsFeatures.contains( feature ) ) + { + sendFeatureMessage( FeatureMessage( m_runProgramFeature.uid(), FeatureMessage::DefaultCommand ). + addArgument( Argument::Programs, predefinedServicePath( feature.uid() ) ), computerControlInterfaces ); + + } + else if( m_predefinedWebsitesFeatures.contains( feature ) ) + { + sendFeatureMessage( FeatureMessage( m_openWebsiteFeature.uid(), FeatureMessage::DefaultCommand ). + addArgument( Argument::WebsiteUrl, predefinedServicePath( feature.uid() ) ), computerControlInterfaces ); + + } + else + { + return false; + } + + return true; +} + + + +bool DesktopServicesFeaturePlugin::handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) +{ + Q_UNUSED(messageContext) + + if( message.featureUid() == m_runProgramFeature.uid() ) + { + const auto programs = message.argument( Argument::Programs ).toStringList(); + for( const auto& program : programs ) + { + runProgramAsUser( program ); + } + } + else if( message.featureUid() == m_openWebsiteFeature.uid() ) + { + // forward message to worker running with user privileges + server.featureWorkerManager().sendMessageToUnmanagedSessionWorker( message ); + } + else + { + return false; + } + + return true; +} + + + +bool DesktopServicesFeaturePlugin::handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) +{ + Q_UNUSED(worker) + + if( message.featureUid() == m_openWebsiteFeature.uid() ) + { + openWebsite( message.argument( Argument::WebsiteUrl ).toString() ); + return true; + } + + return false; +} + + + +ConfigurationPage* DesktopServicesFeaturePlugin::createConfigurationPage() +{ + return new DesktopServicesConfigurationPage( m_configuration ); + +} + + + +bool DesktopServicesFeaturePlugin::eventFilter( QObject* object, QEvent* event ) +{ + auto menu = qobject_cast( object ); + auto master = VeyonCore::instance()->findChild(); + + if( master != nullptr && + menu != nullptr && + menu->activeAction() != nullptr && + event->type() == QEvent::KeyPress && + static_cast( event )->key() == Qt::Key_Delete ) + { + DesktopServicesConfiguration userConfig( master->userConfigurationObject() ); + + if( menu->objectName() == m_runProgramFeature.name() ) + { + ObjectManager objectManager( userConfig.predefinedPrograms() ); + objectManager.remove( DesktopServiceObject::Uid( menu->activeAction()->objectName() ) ); + userConfig.setPredefinedPrograms( objectManager.objects() ); + } + else if( menu->objectName() == m_openWebsiteFeature.name() ) + { + ObjectManager objectManager( userConfig.predefinedWebsites() ); + objectManager.remove( DesktopServiceObject::Uid( menu->activeAction()->objectName() ) ); + userConfig.setPredefinedWebsites( objectManager.objects() ); + } + + userConfig.flushStore(); + + QTimer::singleShot( 0, this, &DesktopServicesFeaturePlugin::updateFeatures ); + QTimer::singleShot( 0, this, [=]() { openMenu( menu->objectName() ); } ); + + return true; + } + + return QObject::eventFilter( object, event ); +} + + + +void DesktopServicesFeaturePlugin::updateFeatures() +{ + updatePredefinedProgramFeatures(); + updatePredefinedWebsiteFeatures(); + + m_features = FeatureList( { m_runProgramFeature, m_openWebsiteFeature } ) + + m_predefinedProgramsFeatures + m_predefinedWebsitesFeatures; + + auto master = VeyonCore::instance()->findChild(); + if( master ) + { + master->reloadSubFeatures(); + + auto runProgramButton = master->mainWindow()->findChild( m_runProgramFeature.name() ); + if( runProgramButton && runProgramButton->menu() ) + { + runProgramButton->menu()->installEventFilter( this ); + } + + auto openWebsiteButton = master->mainWindow()->findChild( m_openWebsiteFeature.name() ); + if( openWebsiteButton && openWebsiteButton->menu() ) + { + openWebsiteButton->menu()->installEventFilter( this ); + } + } +} + + + +void DesktopServicesFeaturePlugin::openMenu( const QString& objectName ) +{ + auto master = VeyonCore::instance()->findChild(); + if( master ) + { + auto button = master->mainWindow()->findChild( objectName ); + if( button && button->menu() ) + { + button->showMenu(); + } + } +} + + + +void DesktopServicesFeaturePlugin::runProgram( VeyonMasterInterface& master, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + RunProgramDialog runProgramDialog( master.mainWindow() ); + + if( runProgramDialog.exec() == QDialog::Accepted ) + { + const auto programs = runProgramDialog.programs().split( QLatin1Char('\n') ); + + controlFeature( m_runProgramFeature.uid(), Operation::Start, + { { argToString(Argument::Programs), programs } }, + computerControlInterfaces ); + + if( runProgramDialog.remember() ) + { + DesktopServicesConfiguration userConfig( master.userConfigurationObject() ); + + ObjectManager objectManager( userConfig.predefinedPrograms() ); + objectManager.add( DesktopServiceObject( DesktopServiceObject::Type::Program, + runProgramDialog.presetName(), + runProgramDialog.programs() ) ); + userConfig.setPredefinedPrograms( objectManager.objects() ); + userConfig.flushStore(); + + updateFeatures(); + } + } +} + + + +void DesktopServicesFeaturePlugin::openWebsite( VeyonMasterInterface& master, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + OpenWebsiteDialog openWebsiteDialog( master.mainWindow() ); + + if( openWebsiteDialog.exec() == QDialog::Accepted ) + { + controlFeature( m_openWebsiteFeature.uid(), Operation::Start, + { { argToString(Argument::WebsiteUrl), openWebsiteDialog.website() } }, + computerControlInterfaces ); + + if( openWebsiteDialog.remember() ) + { + DesktopServicesConfiguration userConfig( master.userConfigurationObject() ); + + ObjectManager objectManager( userConfig.predefinedWebsites() ); + objectManager.add( DesktopServiceObject( DesktopServiceObject::Type::Website, + openWebsiteDialog.presetName(), + openWebsiteDialog.website() ) ); + userConfig.setPredefinedWebsites( objectManager.objects() ); + userConfig.flushStore(); + + updateFeatures(); + } + } +} + + + +void DesktopServicesFeaturePlugin::runProgramAsUser( const QString& commandLine ) +{ + vDebug() << "launching" << commandLine; + + QString program; + QStringList parameters; + + // parse command line format "C:\Program Files\..." -foo -bar + if( commandLine.startsWith( QLatin1Char('"') ) && commandLine.count( QLatin1Char('"') ) > 1 ) + { + const auto commandLineSplit = commandLine.split( QLatin1Char('"') ); + program = commandLineSplit.value( 1 ); + parameters = commandLine.mid( program.size() + 2 ).split( QLatin1Char(' ') ); + } + // parse command line format program.exe -foo -bar + else if( commandLine.contains( QLatin1Char(' ') ) ) + { + const auto commandLineSplit = commandLine.split( QLatin1Char(' ') ); + program = commandLineSplit.first(); + parameters = commandLineSplit.mid( 1 ); + } + else + { + // no arguments so use command line as program name + program = commandLine; + } + + VeyonCore::platform().coreFunctions().runProgramAsUser( program, parameters, + VeyonCore::platform().userFunctions().currentUser(), + VeyonCore::platform().coreFunctions().activeDesktopName() ); +} + + + +bool DesktopServicesFeaturePlugin::openWebsite( const QString& urlString ) +{ + QUrl url( urlString, QUrl::TolerantMode ); + if( url.scheme().isEmpty() ) + { + url = QUrl( QStringLiteral("http://") + urlString, QUrl::TolerantMode ); + } + + if( url.isEmpty() || url.isValid() == false ) + { + vWarning() << "empty or invalid URL"; + return false; + } + + if( QDesktopServices::openUrl( url ) == false ) + { + vWarning() << "could not open URL" << url << "via QDesktopServices - trying native generic URL handler"; + + runProgramAsUser( QStringLiteral("%1 %2").arg( + VeyonCore::platform().coreFunctions().genericUrlHandler(), + url.toString() ) ); + } + + return true; +} + + + +void DesktopServicesFeaturePlugin::updatePredefinedPrograms() +{ + m_predefinedPrograms = m_configuration.predefinedPrograms(); + + auto master = VeyonCore::instance()->findChild(); + if( master ) + { + const auto userPrograms = DesktopServicesConfiguration( master->userConfigurationObject() ).predefinedPrograms(); + for( const auto& userProgram : userPrograms ) + { + m_predefinedPrograms.append( userProgram ); + } + } +} + + + +void DesktopServicesFeaturePlugin::updatePredefinedWebsites() +{ + m_predefinedWebsites = m_configuration.predefinedWebsites(); + + auto master = VeyonCore::instance()->findChild(); + if( master ) + { + const auto userWebsites = DesktopServicesConfiguration( master->userConfigurationObject() ).predefinedWebsites(); + for( const auto& userWebsite : userWebsites ) + { + m_predefinedWebsites.append( userWebsite ); + } + } +} + + + +void DesktopServicesFeaturePlugin::updatePredefinedProgramFeatures() +{ + m_predefinedProgramsFeatures.clear(); + + updatePredefinedPrograms(); + + if( m_predefinedPrograms.isEmpty() == false ) + { + m_predefinedProgramsFeatures.reserve( m_predefinedPrograms.size()+1 ); + + for( const auto& program : qAsConst(m_predefinedPrograms) ) + { + const auto programObject = DesktopServiceObject( program.toObject() ); + m_predefinedProgramsFeatures.append( Feature( m_runProgramFeature.name(), Feature::Action | Feature::Master, + programObject.uid(), m_runProgramFeature.uid(), + programObject.name(), {}, + tr("Run program \"%1\"").arg( programObject.path() ) ) ); + } + + auto primaryFeature = m_runProgramFeature; + primaryFeature.setIconUrl( QStringLiteral(":/core/document-edit.png") ); + primaryFeature.setParentUid( m_runProgramFeature.uid() ); + primaryFeature.setDisplayName( tr("Custom program") ); + m_predefinedProgramsFeatures.append( primaryFeature ); + } +} + + + +void DesktopServicesFeaturePlugin::updatePredefinedWebsiteFeatures() +{ + m_predefinedWebsitesFeatures.clear(); + + updatePredefinedWebsites(); + + if( m_predefinedWebsites.isEmpty() == false ) + { + m_predefinedWebsitesFeatures.reserve( m_predefinedWebsites.size()+1 ); + + for( const auto& website : qAsConst(m_predefinedWebsites) ) + { + const auto websiteObject = DesktopServiceObject( website.toObject() ); + m_predefinedWebsitesFeatures.append( Feature( m_openWebsiteFeature.name(), Feature::Action | Feature::Master, + websiteObject.uid(), m_openWebsiteFeature.uid(), + websiteObject.name(), {}, + tr("Open website \"%1\"").arg( websiteObject.path() ) ) ); + } + + auto primaryFeature = m_openWebsiteFeature; + primaryFeature.setIconUrl( QStringLiteral(":/core/document-edit.png") ); + primaryFeature.setParentUid( m_openWebsiteFeature.uid() ); + primaryFeature.setDisplayName( tr("Custom website") ); + m_predefinedWebsitesFeatures.append( primaryFeature ); + } +} + + + +QString DesktopServicesFeaturePlugin::predefinedServicePath( Feature::Uid subFeatureUid ) const +{ + for( const auto& services : { m_predefinedPrograms, m_predefinedWebsites } ) + { + for( const auto& service : services ) + { + const auto serviceObject = DesktopServiceObject( service.toObject() ); + if( serviceObject.uid() == subFeatureUid ) + { + return serviceObject.path(); + } + } + } + + return {}; +} + + +IMPLEMENT_CONFIG_PROXY(DesktopServicesConfiguration) diff --git a/plugins/desktopservices/DesktopServicesFeaturePlugin.h b/plugins/desktopservices/DesktopServicesFeaturePlugin.h new file mode 100644 index 0000000..364cebf --- /dev/null +++ b/plugins/desktopservices/DesktopServicesFeaturePlugin.h @@ -0,0 +1,134 @@ +/* + * DesktopServicesFeaturePlugin.h - declaration of DesktopServicesFeaturePlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "ConfigurationPagePluginInterface.h" +#include "DesktopServicesConfiguration.h" +#include "DesktopServiceObject.h" +#include "FeatureProviderInterface.h" + +class DesktopServicesFeaturePlugin : public QObject, PluginInterface, + FeatureProviderInterface, + ConfigurationPagePluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.DesktopServices") + Q_INTERFACES(PluginInterface + FeatureProviderInterface + ConfigurationPagePluginInterface) +public: + enum class Argument + { + Programs, + WebsiteUrl + }; + Q_ENUM(Argument) + + explicit DesktopServicesFeaturePlugin( QObject* parent = nullptr ); + ~DesktopServicesFeaturePlugin() override = default; + + Plugin::Uid uid() const override + { + return QStringLiteral("a54ee018-42bf-4569-90c7-0d8470125ccf"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral("DesktopServices"); + } + + QString description() const override + { + return tr( "Start programs and services in user desktop" ); + } + + QString vendor() const override + { + return QStringLiteral("Veyon Community"); + } + + QString copyright() const override + { + return QStringLiteral("Tobias Junghans"); + } + + const FeatureList& featureList() const override + { + return m_features; + } + + bool controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) override; + + bool handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) override; + + ConfigurationPage* createConfigurationPage() override; + +private: + bool eventFilter( QObject* object, QEvent* event ) override; + + void updateFeatures(); + void openMenu( const QString& objectName ); + + void runProgram( VeyonMasterInterface& master, const ComputerControlInterfaceList& computerControlInterfaces ); + void openWebsite( VeyonMasterInterface& master, const ComputerControlInterfaceList& computerControlInterfaces ); + + void runProgramAsUser( const QString& commandLine ); + bool openWebsite( const QString& urlString ); + + void updatePredefinedPrograms(); + void updatePredefinedWebsites(); + + void updatePredefinedProgramFeatures(); + void updatePredefinedWebsiteFeatures(); + + QString predefinedServicePath( Feature::Uid subFeatureUid ) const; + + DesktopServicesConfiguration m_configuration; + + QJsonArray m_predefinedPrograms; + QJsonArray m_predefinedWebsites; + + const Feature m_runProgramFeature; + const Feature m_openWebsiteFeature; + + FeatureList m_predefinedProgramsFeatures; + FeatureList m_predefinedWebsitesFeatures; + + FeatureList m_features; + +}; diff --git a/plugins/desktopservices/OpenWebsiteDialog.cpp b/plugins/desktopservices/OpenWebsiteDialog.cpp new file mode 100644 index 0000000..f983412 --- /dev/null +++ b/plugins/desktopservices/OpenWebsiteDialog.cpp @@ -0,0 +1,81 @@ +/* + * OpenWebsiteDialog.cpp - implementation of OpenWebsiteDialog + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "OpenWebsiteDialog.h" + +#include "ui_OpenWebsiteDialog.h" + +OpenWebsiteDialog::OpenWebsiteDialog( QWidget* parent ) : + QDialog( parent ), + ui( new Ui::OpenWebsiteDialog ), + m_website(), + m_remember( false ), + m_presetName() +{ + ui->setupUi( this ); + + connect( ui->websiteLineEdit, &QLineEdit::textChanged, this, &OpenWebsiteDialog::validate ); + connect( ui->rememberCheckBox, &QCheckBox::toggled, this, &OpenWebsiteDialog::validate ); + connect( ui->presetNameEdit, &QLineEdit::textChanged, this, &OpenWebsiteDialog::validate ); + + validate(); + + ui->websiteLineEdit->setFocus(); +} + + + +OpenWebsiteDialog::~OpenWebsiteDialog() +{ + delete ui; +} + + + +void OpenWebsiteDialog::validate() +{ + QUrl url( ui->websiteLineEdit->text(), QUrl::TolerantMode ); + if( url.scheme().isEmpty() ) + { + url = QUrl( QStringLiteral("http://") + ui->websiteLineEdit->text(), QUrl::TolerantMode ); + } + + ui->buttonBox->button( QDialogButtonBox::Ok )->setEnabled( + url.isEmpty() == false && url.isValid() && + ( ui->rememberCheckBox->isChecked() == false || ui->presetNameEdit->text().isEmpty() == false ) ); +} + + + +void OpenWebsiteDialog::accept() +{ + m_website = ui->websiteLineEdit->text(); + m_remember = ui->rememberCheckBox->isChecked(); + m_presetName = ui->presetNameEdit->text(); + + QDialog::accept(); +} diff --git a/plugins/desktopservices/OpenWebsiteDialog.h b/plugins/desktopservices/OpenWebsiteDialog.h new file mode 100644 index 0000000..37068a4 --- /dev/null +++ b/plugins/desktopservices/OpenWebsiteDialog.h @@ -0,0 +1,60 @@ +/* + * OpenWebsiteDialog.h - declaration of class OpenWebsiteDialog + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +namespace Ui { class OpenWebsiteDialog; } + +class OpenWebsiteDialog : public QDialog +{ + Q_OBJECT +public: + explicit OpenWebsiteDialog( QWidget *parent ); + ~OpenWebsiteDialog() override; + + const QString& website() const + { + return m_website; + } + + bool remember() const + { + return m_remember; + } + + const QString& presetName() const + { + return m_presetName; + } + +private: + void validate(); + void accept() override; + + Ui::OpenWebsiteDialog* ui; + QString m_website; + bool m_remember; + QString m_presetName; + +} ; diff --git a/plugins/desktopservices/OpenWebsiteDialog.ui b/plugins/desktopservices/OpenWebsiteDialog.ui new file mode 100644 index 0000000..06bbb60 --- /dev/null +++ b/plugins/desktopservices/OpenWebsiteDialog.ui @@ -0,0 +1,145 @@ + + + Tobias Junghans + OpenWebsiteDialog + + + Open website + + + + :/desktopservices/internet-web-browser.png:/desktopservices/internet-web-browser.png + + + + + + false + + + e.g. Veyon + + + + + + + Remember and add to website menu + + + + + + + Qt::Vertical + + + + 20 + 10 + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + e.g. www.veyon.io + + + + + + + Please enter the URL of the website to open: + + + + + + + Name: + + + + + + + Qt::Vertical + + + + 20 + 15 + + + + + + + + websiteLineEdit + rememberCheckBox + presetNameEdit + + + + + + + buttonBox + accepted() + OpenWebsiteDialog + accept() + + + 199 + 384 + + + 199 + 206 + + + + + buttonBox + rejected() + OpenWebsiteDialog + reject() + + + 199 + 384 + + + 199 + 206 + + + + + rememberCheckBox + toggled(bool) + presetNameEdit + setEnabled(bool) + + + 225 + 273 + + + 225 + 315 + + + + + diff --git a/plugins/desktopservices/RunProgramDialog.cpp b/plugins/desktopservices/RunProgramDialog.cpp new file mode 100644 index 0000000..67c9688 --- /dev/null +++ b/plugins/desktopservices/RunProgramDialog.cpp @@ -0,0 +1,74 @@ +/* + * RunProgramDialog.cpp - implementation of RunProgramDialog + * + * Copyright (c) 2004-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "RunProgramDialog.h" + +#include "ui_RunProgramDialog.h" + +RunProgramDialog::RunProgramDialog( QWidget* parent ) : + QDialog( parent ), + ui( new Ui::RunProgramDialog ), + m_programs(), + m_remember( false ), + m_presetName() +{ + ui->setupUi( this ); + + connect( ui->programInputTextEdit, &QTextEdit::textChanged, this, &RunProgramDialog::validate ); + connect( ui->rememberCheckBox, &QCheckBox::toggled, this, &RunProgramDialog::validate ); + connect( ui->presetNameEdit, &QLineEdit::textChanged, this, &RunProgramDialog::validate ); + + validate(); + + ui->programInputTextEdit->setFocus(); +} + + + +RunProgramDialog::~RunProgramDialog() +{ + delete ui; +} + + + +void RunProgramDialog::validate() +{ + ui->buttonBox->button( QDialogButtonBox::Ok )->setEnabled( + ui->programInputTextEdit->toPlainText().isEmpty() == false && + ( ui->rememberCheckBox->isChecked() == false || ui->presetNameEdit->text().isEmpty() == false ) ); +} + + + +void RunProgramDialog::accept() +{ + m_programs = ui->programInputTextEdit->toPlainText(); + m_remember = ui->rememberCheckBox->isChecked(); + m_presetName = ui->presetNameEdit->text(); + + QDialog::accept(); +} diff --git a/plugins/desktopservices/RunProgramDialog.h b/plugins/desktopservices/RunProgramDialog.h new file mode 100644 index 0000000..7569055 --- /dev/null +++ b/plugins/desktopservices/RunProgramDialog.h @@ -0,0 +1,60 @@ +/* + * RunProgramDialog.h - declaration of class RunProgramDialog + * + * Copyright (c) 2004-2021 Tobias Junghans + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +namespace Ui { class RunProgramDialog; } + +class RunProgramDialog : public QDialog +{ + Q_OBJECT +public: + explicit RunProgramDialog( QWidget *parent ); + ~RunProgramDialog() override; + + const QString& programs() const + { + return m_programs; + } + + bool remember() const + { + return m_remember; + } + + const QString& presetName() const + { + return m_presetName; + } + +private: + void validate(); + void accept() override; + + Ui::RunProgramDialog* ui; + QString m_programs; + bool m_remember; + QString m_presetName; + +} ; diff --git a/plugins/desktopservices/RunProgramDialog.ui b/plugins/desktopservices/RunProgramDialog.ui new file mode 100644 index 0000000..f4ccba3 --- /dev/null +++ b/plugins/desktopservices/RunProgramDialog.ui @@ -0,0 +1,143 @@ + + + Tobias Junghans + RunProgramDialog + + + Run programs + + + + :/desktopservices/preferences-desktop-launch-feedback.png:/desktopservices/preferences-desktop-launch-feedback.png + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Qt::Vertical + + + + 20 + 10 + + + + + + + + Name: + + + + + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + + + + Remember and add to program menu + + + + + + + false + + + e.g. VLC + + + + + + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + true + + + + + + + Qt::Vertical + + + + 20 + 15 + + + + + + + + + + + + buttonBox + accepted() + RunProgramDialog + accept() + + + 199 + 384 + + + 199 + 206 + + + + + buttonBox + rejected() + RunProgramDialog + reject() + + + 199 + 384 + + + 199 + 206 + + + + + rememberCheckBox + toggled(bool) + presetNameEdit + setEnabled(bool) + + + 225 + 273 + + + 225 + 315 + + + + + diff --git a/plugins/desktopservices/desktop-services.png b/plugins/desktopservices/desktop-services.png new file mode 100644 index 0000000000000000000000000000000000000000..fede805a797e3aef88258a4ec4e2fc75d3003e5c GIT binary patch literal 1292 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&F&8^|hH!9j+vV<2B`qVXrlF;8Xk=_+YG!U>X=P*Y;OG<_l3iF-TvA$A zUQt<9-_Y39+ScAVVbYYDv*yfQzGCI-wd>Yz*t%`|j-9)9?>TVr(BX@huUx%${q>u- z@7{m-_~q-jA3uNn{`2?N*Zv9y2BzblE{-7;x8BamjK1X{aD2Ov59^))LH#9iE2mv* ze$;wx+qK2zdmXeEtrcP2|DQc$_vXeWCzsXE^7$0;?7!9aU9|Yx z`5UiI77bqfz_3BHPRMGvVS4yQ837YF8`g@fPk(t@C4bM7Yy3KQI>U!QRoXq>|E8O6 zl3@3I&+y=9ah2-qJsYm+Grwrvx#6K?Lq*+PskXoAo^}_*e!MR}s@xD(lQ_54@tQJw z$03Ei&v_lJ_icDq`ElRnsPc?CmU@glA6rZNrLZ>HAM)cy-743Cn^Hr{DkAc6bmQ@b9)`gcDOk=8gwXCOy=7BizXMU%{)~PEJ|E<5~QkW`W9Xu;b-j842=cv=d2(98PR&(lRfBI9f zDsqzX@_%yYO|$hyQa|>y&y;8I(KUHuc50EpxrKD>}V&U+{~uI57NUnsF&_o&EEjcX>BEG8Z&CFxCmO zaDSMrkiamFTdF~jfw6>-;qT-w1}n)17p03<4h$>~z*MIqwDR#6=AO^nPHcZuCkZNx NJYD@<);T3K0RTV^Ad&z8 literal 0 HcmV?d00001 diff --git a/plugins/desktopservices/desktopservices.qrc b/plugins/desktopservices/desktopservices.qrc new file mode 100644 index 0000000..9fdbc2f --- /dev/null +++ b/plugins/desktopservices/desktopservices.qrc @@ -0,0 +1,7 @@ + + + internet-web-browser.png + preferences-desktop-launch-feedback.png + desktop-services.png + + diff --git a/plugins/desktopservices/internet-web-browser.png b/plugins/desktopservices/internet-web-browser.png new file mode 100644 index 0000000000000000000000000000000000000000..f6861574d567201fadd2f9d79e6652c9430aae06 GIT binary patch literal 5425 zcmV-170&93P)Dg}YJ-xj>O2SS+ZS@>$rM1?2y4=>< zTKC;*)z$?;Hrc@i6+vrpLBv{6K@bEH1VxZtwvdE`utPvX*k#`WVY8+0`G$%`Wyvzx zVE)hZd-M?|^S%H7JKuaWnM4&8LWBqrB1DJ~Awq-*5h6s05FtW@2oahz+Rbes*>EdA zbgVAja;T1LVpGXI1h zJV`+OiU5rHX#xo9Wx(xznpn6s7r^jq2?^TwKs;)O9mHuv+5&h*H{q8r^x~h~pd>u$ z9xV{7N-l(;AjKu1hXFGZ2SjaxA)b!OpA!Ge5^StQV0g(H{-ht@znT*NBnyQPV=+Hf z0D--9xYeH_5sNbTFy4NIq(DFJ$#*KD@Mj`GF|~jVf8ATkbGz8a?lb3dVP*n4GUJ|% zi6yTLg8Qfvvp%1OG)NDuQ-$s;y2p<@N@lK!!QfoCEKsb|GU2 zUSfVqH{9rRfhfJ$K0PAo7nK5kF)S$$zSc$m?PhNpj7K2Cl=}!~ z#P<)G`c85;_zS2o(RRY(*G&cexPIw1LF|A)H)g;QtC;K20qL8a7!Cz{p!N#0wnDGFe_oD~ohFUbbR)CJPmm1q#HTA2%*r_MuXq7wxFS-d z71OIdsls1Iubt&m&+5A_cnI>sw>ueZEAqi0kq?yn1Bwoh65gXRDZ<}eT&eET!0h%F zB!Rp*Qp1Kt$!*FI{74FQqv0P)9sau9$~Ro=Gdq2{lRzGvc+7-P(wc!1*{_sr$*w5f z&gH(Yoav*>8i&f9N}ujTkTq^i8L*sD3wlZ#JcfQ$A9%K>fJ0?=jZ)qoQB3hT2fK|it&DEIo6nkCeRXLTLm=BFxE_`3>A?Y;Sla1{++7I=0f!?xTqn03Dg zD0llO1*VS*p4E2+VN#NJOgX|YDq85#mOOZ+D;}iH<&H$yQ^bKe(Sk`6JO(y{d9*!z zu2zrshl7SC1q&4Z7VqI|W~+Bs97q!{emq!mb6|dKCr~2#Cmo-s)sI@YOC7CqIhU)r z50~sNEWR!6@a-zH;QfSF&_zZ~R(2I~My>sFX96rrY;{%4>wQ|8fqU!2-@N#-APtVz z(%|FdM$irKpY+VS*8_*D7%zFSuebpFd5?a0A4TCBz_BW`?q@Veyg6B)247KYfN}?! zGliIW_xC0k_Tf@TJiHy<`TNhq`ok1!;a5<6@G?IJ@Wib-305;IKrg&+N(DcX4-%T6 z^J5jG5@tsVhJJ<)2*r4gU~VY<&8a4>|EJCefCuMW<6#TC6lR9^V4Xf?q1@?1GeE{m z{&=>>fGsZ*)@5=VH`mt6|Lx=LImMor(7+_+VtXtY7UjX5$nGf}{4g6>WfOX4!eRT! zHT=)eWuJ3ha{r~yXpoex*&NUf?IS=KJZOUp=zy5Wv(rt9Pj*$Nyehv3JPLC?JUgNQ z58ivw0=l>RraY9;UU00W3hkR|DeJO)(g%3uNNuX%A{xY4oOqmsx-<^P`yGs9bty#n zPuY<9XhOf9_b$0tE}unQOXKbK=(rCr;@%zwHsv%}npy>Vw|n8O@NU>skOQOr?l9kE z6@zX_-;`&5R2!UYdoZq7qw!sSzNO3?z@#*jbD<*=hB^K?5jL_)V9s6ssKsFoajGc> zMs73AyU{IEK70x~SnMm)M)hYR>?+bLWc`TOw9}0-znyQp57y-wurwJR?IFFOd#i6; z-nxU$2H9wV6Zs6Mnuil^Hx*dND1FWsQ8?FnAB?KlvU`MVU1q7vhD;93xr2`O;69Q0 z0yWIZ#u#+ExCdKUg<`$NWMclkRyfldHROl5>?>w^%d7#=pc5o58f%{Y90t65zhz2? zPr20#w!Bn0k3y6LhpLnonS`(B*YSpKP~WczAdsB3^b)l2yDKqgS|Z`QygccD)=(#~ ze~k_rIDzE6bGH>vqu|cAMc~5sc`WI_RjhNf|ANjkBH6m z>{-(Ld>V0!4GEcN|)waAP&bGL*eb)=u~jM zmtX`tvS}c$r{l3bhqej#2H47A>~m>CI}j7Y!er3#?Ijq&r?KTAiKk<+BP+ug|0jT> z%eU^#Nr$5iXmQsREHcL%Lg3ASP9pdb%naZoUv7!z=Xh*o(;V@?4Sb<`la5a>%<$*K z=W(TARGbLMn}Wbu1eV5g0DBFBKQJkY6Ujqi*p!;!hI<2Op5}a`?b8DznH$msYtpm9 zw&ETfZw#6c!-7P3@ioR`K|~Gg&VLAoMG5duXoJw(DI2R&bA|Fw6xJog;NJl1uDL6; zuc1MYf{u@1D6nss8F2V<2wL>r92KYfAeeQd1IBtCj0NE}uq!|5x$TZN1i`oLG?*38 zKJK>4$o$ayG5r#kl`+xyp90ifSpTnmMF84Yd&WiIj}M>4mcZV^csSY^_!3LvibvgU z!5u94#8I~y=FsC?usV$m6rZjM$DvHvR)vjQw@5Su!0M<6hWM`moLL51UY#NpHaDno zC@^bY1R7Z25PJ#|o)10@1)rDr?B~K@Q+WqIiz%5j4X~A!E>ivDaiqo{mPOoOi+cm8 zxia;&JexuLN{`6sTou53Vb!oXlM0SC!7$6O0|j3V-=pB2P{)eP{*tJn*A-_>ip0lJ zk0kP8Bo5_QVeP#Ki*Rp%Y0li&HQj3^E>{0~C#+4+gd+_%aAP|=8Ri7EC~X7G@a;sW zw*Xwcu;E;WPeRj4Qh7f>!>yuO+Zzp9Lf#H)fW!4SaOL3RKv++sgFZ47k1@&`!=eYc z_+ZTT`BiG~2CARt(zU(NfN{XEC>jpc`{RyHRWN)Rp9i|wf)%H+%7Wz&vvH|^XGTDW z)EYp|r7#e$fX z_O&;Ks(VXf@FVHX08R{NEl+eE|8tMT=y-R7Wkm=atoM^D-{;4`yqop7j9K2yxP}@e z7&S;FYLLWi*ho)<9jHN!IQPJ=CJ-E|ePC@=aGdlS;4o{8=Gj`@3R;Q31l?Vr#B3n#W_#e@B0UzuW7ehWT{ECM|1 zPz2PDunVRB$so1E%xhW~T5%ing4;Z>tMicydy4LY!L6!E5k8g)`5?nf`VDZ9xlO~h z474u^aARLdnB1BGV;egbX8ShcT9d57GEqO2xk>5`pzTbbMn1$G)IvMOi_c?nz_#w1 zT(Pe4h1F?kpy$;Ac-AH>P$s{;VU_ZROui$4>`0?(o^1pzPXR9Id3AzC1zM!lUX?rM zyc_WOgKW_GsSCIIqz#q@$&ryFvu_9?J23aFA1?;2ivrwOlb!RhCafz zMrna%p=!@Clvx9)I+Xq|*?}R@y3mPR5&!gi3|6&Q6pn4#k?=47I>l`OlmXeEE+9D< zzb4af3PTMLu6}|GS{?%2*pV9nRy9`?4$JDRu%4CxGrd}Itrril4AdMLw`Kl4fohJd z)u{78%bkx~F(2M30?Qh2g~hDG2fmD_gN`R(F&Y4Qk7b~4pDCZOjwjn?6sx=N&@?2# z1#OQmFeD+4?7!(e_uC2qBg1oE70mx*Qo=65P`lXJ>n$TqY}b!RSah0XM82D2(}Ft7eu zY1ou`AKtvuAkzj=KVAl8n+%?cGyF+Ko+8;ZEXdYW&~$4-UD=Hb8|YEU*vm@Azr*uz ztA8Ri-C6x+U{L4YXHn^T@hH* z;>k8>LFm>2T4%c@=Hq)zFsZtv@T^RY#id@MK;B{=t69^dRTNEX2eW37EvY?f4!NLp zrc2^tDHqzou-qH=Ba;;sY&Z@k1NBWa*ZOO<5se zRC!V1FbLpCT$?ap9%5dS4`j4yIu`4yD1*pWtPLa!>LA&g37S82faZ_gV)K4*KJ2UT zP$ZTqphTk!P59n1>`Qx`&F=37xA*mh>2sIo1TZsGGY`QQczkS#%2Qt+=2NEQv`H zyAGlG0lUwi4@2Hr&=VE@yg!!ocBQN#+Sp5){sn56Iil9!v!5hr@BPq{h%D! z%{wnQHn0Lf3!A}29JjDwo?zZ!9wD!)v8ySS$t0{GR2&B@kaPfDB04vTWYD{Td9aIj zPHz0uyIgwVBQG#dhP*MSm1)>2=$$@^o4_LTb&^>|#!#VVR8T*}L(^WDP`rC1556xy zCl|g5_r%zLv%+hnNe3fKMiymqMgqjUn9J+K&Xq3T?T==g1 ztjzhg)C2U+H{u_@YbU7L7Q(O(Wb=#!HM{)3t4s-@%NjK^nkUI5eGq{zb(o-W6q)SW zH6{lB@@as@cc|lzOB}FTv6`{khdeN)dC2|A9=W`EWX4=YHc4+G?MFuJ$7X{f&~!#u z2`*hPfi=KR?irb~Dc2jc(aLw>3E zfQ|-GcjS%Q06VyDGG(4mA#UNTAFLXxZMaTV6Dm{FQc3RZ%jDrwGx`aVF|~(ej0`fy zP77?bNUQ_R6J0~h_sfOtC=^+-oEr3O-;#jUfYmZw7phS}Hl;Z$?cF`;-Y+QDC7aSC z{xAz$9I?=hPzCd^XT!H;Kgxt{rDtK5dyVA5n`gnuT2xIklF1gdnMBePy^&-_Uq9W5 zTBT})Onb^~*D%*$KPN9%aOO=l!bO&RiiX`>sSyhi!g_;?^?jBL_64+bquyW`T@3~!0=>a{zcF^)dCvF z`9ji(&hO}Gw=4uzV=7={eTH#*jj9RFMq!@bRn|sOHKcBvbLwXGR}p3~lCL8zV0E%R ztY?fKA$g3ln-5ioxX| z%fjaB4&6 z^9D4vV}Tc;6+8n}_oa=7K-GvEN;1w^fsK{OONv@>C0O+Xf1hig+&a~^^o|95iNYU? zuOiK1d7L$@OtynnsSdE3>IiFS2VpJaFsx%a4T1fAO~yf(=}HB%6$i)%3V~$CMn^Jj ztgwgqRdruR3YrD>sqLI9ckvWV?|4O!7y9@dTO8<-{b|UtzAwT}VQGZuyytlaR1MQ6 zVo=pEyt<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H14vjZ19D z?{=_v{j%Qm?tPy*XHGxNeVe=2>rL=_XYM_Zbv4)b&d#2l=Q;oXbN+MY3>X*~7#J8B z7#J8B7#J8B3<|{EpZQa5PQ&2BGN-@@z`zK=U{K+hQ_z1{JHCdUcihhPTQ>vn`k^B{ zy89{4oI7s>FbL%T^%wt)TsG7C?7hEnBiC)&%s+kOTk}LHEDsfI**=uSM&< z^ri6tgR2B#>HvcX4TuK$2w^ILgW2(Qa^Yd1}IA*F?b2ixyQS02@R*4K@)3K`0c!my%k&{hcKQnM|5OE=x9@ zZV#Ulo&$sst!-aYYr^dftJQUNn_B`y}rj1=;W4i^#|5kzDd znD891`;9tZIiNY`+n3yDETy?=$Y)#L=j|zrj!6 zVs4)KX>PR+eLxTdC@CY~x#HJ|}f^cv~a`&^6i_(f1yY(5xgBxbJ)+)A# zIai4MUP8#ETs)G##MT6cFH7st2sK}^G<;I?nV;1; zSBsK=*SN=DtvN*odDX~oZ#7mR$)?lzfxr*I*HDx2>ZuvL#+0krd4R6|o`{OZ{V$X` zQnDM34wd?e@fv@!@)WjU8OU#MQ-Md?)<~_90Z0u+sd@E{3&_T(B32QgD;&gL0FNE5 z^5`ii8unNEd~Vg`k`Ve<_Fdsxq-}vj;A@Sq;dBiSpPi1S2s&~>yaiD4HUF?zN63F$ zTJaa_&Mte;@BVCU+QQNr83>dT$N=`w1YEc{8$S_rwA<2J)A@=MzWQ>7$y(c|I%xiW z>kJtoV?{oIV+%GEER+t@fFEc|5?-J2nVXwKb=41+vkuVYS?d^237e`a9zGG0-!!QA zTw7}+gA(An{7t7*{DP}2+F=#)bumY^Oj5mJv>0X z6j1e}?aA6fBFPs*grk5U^Z>quinqAqHbt-`g}M0?LSm+XuI4NiIZI7`6H0+pjmaW` zepAO?8WBWKaO^xl*6wtvP*oza+CDihF?bn^F2V! zaMQJE@^(87%+xFn7qf{XUkJg$DapmE)tVcawl&wSPF=A!T&YwLI}@0)1;0HmTf;yC zc=Dv1DDnZGIpK%7Uxjl6-#HfGSWDA}{wf3kpS;0|+y(UX$rLqZC5HT(uX+BoXcU1! zNzHAWo#iJ#2_698W83l!r-D|a<~+e;C$fnkUkJey2kXq%!|*Qy$U1P#CN~D;C%^*$ zxQ^ftt_vb9{oa8TwMA3~mz(^G7qI&VGOjpkFonT-$mNx3_Cb;04D8&u?W=I_7ma%j$tE%}G0 zeE#dW9p?SUl<<#V4`Vqic0Ir2T0rz)obcuC!;EKDt28VF!Iz)OP?qr{zv^qgw#TO~ z8#95GVAR$8@%z)UBEOsOSl=C-Ph0%$2RjBT0h}(`Jn&o=rQ<_>paoxjrpoD}gOs{e z`1#_^7KMxx%bp*5575NU_Y85@RzK3lXHUEQT5j?|@QpncUOsDwdB0Kn|L7LQ zZ8v1&M}87l2>F*gM%gmrwTnX<9zT-i!Dk(0uzciuN^s{cpC2Aeb>w~6Fr>JDdp_1Z zzrzE>e~GAUO7Pu#hZxVQ$du+Mj%E4MPf`>;ebvc7SAiXmNDj_AOe=?JWh0efEFJL8 zJF~csh$ZHx`DC2akQ6yLok!>AiX?i&7|i8P;jEW_Tj`l^xtamEk6V1GN4Yn4Iv$;r;O1ltW~hC(-^zH?ENU z$IdkQv`9qHf9e1BNB*DK<>K?16c2rJjGJGa=IbxGOjV-`HCH@~r;oWjeGKw$!1XI7 z+g2;Kj0v`k3EnnrGwgZE8HK#Z@1^X|+Yg2-gvo?O<%oA4YdW9A6`S&Qk@VhY_ z>RmPjL{)NJNA!NoNVDLW zvNSiZlYHg2G}jJiV_VyQlL4SVo*87@HX|b=2rnq+2<)0=h}}JW1&?rI^K6Z z#;x;b+xv9>T-Pz_bDwg`1{Dnj?2;>7erBZo;JZVv`loudsG2}D<#M9$swoWjM5C-#p z-$Dd;KQH%^gf<^k(G~Z z6Ws?Ox>~o^I^qZVmHb7G0y3E_*-UmI$Zx*4ZF?ZdU-U3AoyoLC(8uHtLe8I;0t$uv zqIN74^3>}!s@3YE30~ZHm(S~$&-qc(#Ucp4+;;{B1_lNO1_lNO1_lNO1_u3x{|CIT VDM_1na|HkZ002ovPDHLkV1hp5OM(CZ literal 0 HcmV?d00001 diff --git a/plugins/filetransfer/CMakeLists.txt b/plugins/filetransfer/CMakeLists.txt new file mode 100644 index 0000000..a61eddb --- /dev/null +++ b/plugins/filetransfer/CMakeLists.txt @@ -0,0 +1,21 @@ +INCLUDE(BuildVeyonPlugin) + +build_veyon_plugin(filetransfer + FileTransferPlugin.cpp + FileTransferPlugin.h + FileTransferConfiguration.h + FileTransferConfigurationPage.cpp + FileTransferConfigurationPage.h + FileTransferConfigurationPage.ui + FileTransferController.cpp + FileTransferController.h + FileTransferListModel.cpp + FileTransferListModel.h + FileTransferDialog.cpp + FileTransferDialog.h + FileTransferDialog.ui + FileTransferUserConfiguration.h + FileReadThread.cpp + FileReadThread.h + filetransfer.qrc +) diff --git a/plugins/filetransfer/FileReadThread.cpp b/plugins/filetransfer/FileReadThread.cpp new file mode 100644 index 0000000..c41694f --- /dev/null +++ b/plugins/filetransfer/FileReadThread.cpp @@ -0,0 +1,128 @@ +/* + * FileReadThread.cpp - implementation of FileReadThread class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "FileReadThread.h" + + +FileReadThread::FileReadThread( const QString& fileName, QObject* parent ) : + QObject( parent ), + m_mutex(), + m_thread( new QThread ), + m_file( nullptr ), + m_currentChunk(), + m_timer( new QTimer ), + m_fileName( fileName ), + m_chunkReady( false ), + m_filePos( 0 ), + m_fileSize( 0 ) +{ + m_timer->moveToThread( m_thread ); + m_thread->start(); + + connect( m_thread, &QThread::finished, m_timer, &QObject::deleteLater ); + connect( m_thread, &QThread::finished, m_thread, &QObject::deleteLater ); +} + + + +FileReadThread::~FileReadThread() +{ + m_thread->quit(); +} + + + +bool FileReadThread::start() +{ + if( QFile( m_fileName ).open( QFile::ReadOnly ) == false ) + { + return false; + } + + m_timer->singleShot( 0, this, [this]() { + m_file = new QFile( m_fileName ); + m_file->open( QFile::ReadOnly ); + connect( m_thread, &QThread::finished, m_file, &QObject::deleteLater ); + + m_mutex.lock(); + m_filePos = 0; + m_fileSize = m_file->size(); + m_mutex.unlock(); + } ); + + return true; +} + + + +QByteArray FileReadThread::currentChunk() +{ + QMutexLocker lock( &m_mutex ); + return m_currentChunk; +} + + + +void FileReadThread::readNextChunk( qint64 chunkSize ) +{ + m_mutex.lock(); + m_chunkReady = false; + m_mutex.unlock(); + + m_timer->singleShot( 0, this, [this, chunkSize]() { + if( m_file ) + { + const auto chunk = m_file->read( chunkSize ); + m_mutex.lock(); + m_currentChunk = chunk; + m_chunkReady = true; + m_filePos = m_file->pos(); + m_mutex.unlock(); + } + } ); +} + + + +bool FileReadThread::isChunkReady() +{ + QMutexLocker lock( &m_mutex ); + return m_chunkReady; +} + + + +bool FileReadThread::atEnd() +{ + QMutexLocker lock( &m_mutex ); + return m_filePos >= m_fileSize; +} + + + +int FileReadThread::progress() +{ + QMutexLocker lock( &m_mutex ); + return m_fileSize > 0 ? static_cast( m_filePos * 100 / m_fileSize ) : 0; +} diff --git a/plugins/filetransfer/FileReadThread.h b/plugins/filetransfer/FileReadThread.h new file mode 100644 index 0000000..b9f4d72 --- /dev/null +++ b/plugins/filetransfer/FileReadThread.h @@ -0,0 +1,61 @@ +/* + * FileReadThread.h - declaration of FileReadThread class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include +#include +#include + +class FileReadThread : public QObject +{ + Q_OBJECT +public: + explicit FileReadThread( const QString& fileName, QObject* parent = nullptr ); + ~FileReadThread() override; + + bool start(); + + QByteArray currentChunk(); + void readNextChunk( qint64 chunkSize ); + bool isChunkReady(); + + bool atEnd(); + int progress(); + +private: + QMutex m_mutex; + QThread* m_thread; + QFile* m_file; + QByteArray m_currentChunk; + + QTimer* m_timer; + + QString m_fileName; + bool m_chunkReady; + qint64 m_filePos; + qint64 m_fileSize; + +}; diff --git a/plugins/filetransfer/FileTransferConfiguration.h b/plugins/filetransfer/FileTransferConfiguration.h new file mode 100644 index 0000000..d1365d4 --- /dev/null +++ b/plugins/filetransfer/FileTransferConfiguration.h @@ -0,0 +1,38 @@ +/* + * FileTransferConfiguration.h - configuration values for FileTransfer plugin + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonConfiguration.h" +#include "Configuration/Proxy.h" + +#define FOREACH_FILE_TRANSFER_CONFIG_PROPERTY(OP) \ + OP( FileTransferConfiguration, m_configuration, bool, rememberLastFileTransferSourceDirectory, setRememberLastFileTransferSourceDirectory, "RememberLastSourceDirectory", "FileTransfer", true, Configuration::Property::Flag::Advanced ) \ + OP( FileTransferConfiguration, m_configuration, bool, fileTransferCreateDestinationDirectory, setFileTransferCreateDestinationDirectory, "CreateDestinationDirectory", "FileTransfer", true, Configuration::Property::Flag::Advanced ) \ + OP( FileTransferConfiguration, m_configuration, QString, fileTransferDefaultSourceDirectory, setFileTransferDefaultSourceDirectory, "DefaultSourceDirectory", "FileTransfer", QStringLiteral("%HOME%"), Configuration::Property::Flag::Advanced ) \ + OP( FileTransferConfiguration, m_configuration, QString, fileTransferDestinationDirectory, setFileTransferDestinationDirectory, "DestinationDirectory", "FileTransfer", QStringLiteral("%HOME%"), Configuration::Property::Flag::Advanced ) \ + +// clazy:excludeall=missing-qobject-macro + +DECLARE_CONFIG_PROXY(FileTransferConfiguration, FOREACH_FILE_TRANSFER_CONFIG_PROPERTY) diff --git a/plugins/filetransfer/FileTransferConfigurationPage.cpp b/plugins/filetransfer/FileTransferConfigurationPage.cpp new file mode 100644 index 0000000..acc7a0c --- /dev/null +++ b/plugins/filetransfer/FileTransferConfigurationPage.cpp @@ -0,0 +1,86 @@ +/* + * FileTransferConfigurationPage.cpp - implementation of FileTransferConfigurationPage + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "FileSystemBrowser.h" +#include "FileTransferConfiguration.h" +#include "FileTransferConfigurationPage.h" +#include "Configuration/UiMapping.h" + +#include "ui_FileTransferConfigurationPage.h" + +FileTransferConfigurationPage::FileTransferConfigurationPage( FileTransferConfiguration& configuration, QWidget* parent ) : + ConfigurationPage( parent ), + ui( new Ui::FileTransferConfigurationPage ), + m_configuration( configuration ) +{ + ui->setupUi(this); + + connect( ui->browseDefaultSourceDirectory, &QAbstractButton::clicked, + this, &FileTransferConfigurationPage::browseDefaultSourceDirectory ); + + connect( ui->browseDestinationDirectory, &QAbstractButton::clicked, + this, &FileTransferConfigurationPage::browseDestinationDirectory ); + + Configuration::UiMapping::setFlags( this, Configuration::Property::Flag::Advanced ); +} + + + +FileTransferConfigurationPage::~FileTransferConfigurationPage() +{ + delete ui; +} + + + +void FileTransferConfigurationPage::resetWidgets() +{ + FOREACH_FILE_TRANSFER_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); +} + + + +void FileTransferConfigurationPage::connectWidgetsToProperties() +{ + FOREACH_FILE_TRANSFER_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY) +} + + + +void FileTransferConfigurationPage::applyConfiguration() +{ +} + + +void FileTransferConfigurationPage::browseDefaultSourceDirectory() +{ + FileSystemBrowser( FileSystemBrowser::ExistingDirectory ).exec( ui->fileTransferDefaultSourceDirectory ); +} + + + +void FileTransferConfigurationPage::browseDestinationDirectory() +{ + FileSystemBrowser( FileSystemBrowser::ExistingDirectory ).exec( ui->fileTransferDestinationDirectory ); +} diff --git a/plugins/filetransfer/FileTransferConfigurationPage.h b/plugins/filetransfer/FileTransferConfigurationPage.h new file mode 100644 index 0000000..1d7e487 --- /dev/null +++ b/plugins/filetransfer/FileTransferConfigurationPage.h @@ -0,0 +1,54 @@ +/* + * FileTransferConfigurationPage.h - header for the FileTransferConfigurationPage class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "ConfigurationPage.h" + +namespace Ui { +class FileTransferConfigurationPage; +} + +class FileTransferConfiguration; + +class FileTransferConfigurationPage : public ConfigurationPage +{ + Q_OBJECT +public: + explicit FileTransferConfigurationPage( FileTransferConfiguration& configuration, QWidget* parent = nullptr ); + ~FileTransferConfigurationPage() override; + + void resetWidgets() override; + void connectWidgetsToProperties() override; + void applyConfiguration() override; + +private: + void browseDefaultSourceDirectory(); + void browseDestinationDirectory(); + + Ui::FileTransferConfigurationPage *ui; + + FileTransferConfiguration& m_configuration; + +}; diff --git a/plugins/filetransfer/FileTransferConfigurationPage.ui b/plugins/filetransfer/FileTransferConfigurationPage.ui new file mode 100644 index 0000000..67c7cf9 --- /dev/null +++ b/plugins/filetransfer/FileTransferConfigurationPage.ui @@ -0,0 +1,107 @@ + + + FileTransferConfigurationPage + + + File transfer + + + + :/filetransfer/document-share.png:/filetransfer/document-share.png + + + + 0 + + + 0 + + + + + Directories + + + + + + Destination directory + + + + + + + + + + + :/core/document-open.png:/core/document-open.png + + + + + + + + :/core/document-open.png:/core/document-open.png + + + + + + + + + + Default source directory + + + + + + + + + + Options + + + + + + Remember last source directory + + + + + + + Create destination directory if it does not exist + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + diff --git a/plugins/filetransfer/FileTransferController.cpp b/plugins/filetransfer/FileTransferController.cpp new file mode 100644 index 0000000..4d3857b --- /dev/null +++ b/plugins/filetransfer/FileTransferController.cpp @@ -0,0 +1,280 @@ +/* + * FileTransferController.cpp - implementation of FileTransferController class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "FileReadThread.h" +#include "FileTransferController.h" +#include "FileTransferPlugin.h" + + +FileTransferController::FileTransferController( FileTransferPlugin* plugin ) : + QObject( plugin ), + m_plugin( plugin ), + m_currentFileIndex( -1 ), + m_currentTransferId(), + m_files(), + m_flags( Transfer ), + m_interfaces(), + m_fileReadThread( nullptr ), + m_fileState( FileStateFinished ), + m_processTimer( this ) +{ + m_processTimer.setInterval( ProcessInterval ); + connect( &m_processTimer, &QTimer::timeout, this, &FileTransferController::process ); +} + + + +FileTransferController::~FileTransferController() +{ + if( m_fileReadThread ) + { + delete m_fileReadThread; + } +} + + + +void FileTransferController::setFiles( const QStringList& files ) +{ + m_files = files; + m_currentFileIndex = 0; + Q_EMIT filesChanged(); +} + + + +void FileTransferController::setInterfaces( const ComputerControlInterfaceList& interfaces ) +{ + m_interfaces = interfaces; +} + + + +void FileTransferController::setFlags( Flags flags ) +{ + m_flags = flags; +} + + + +void FileTransferController::start() +{ + if( isRunning() == false && m_files.isEmpty() == false ) + { + m_currentFileIndex = 0; + m_fileState = FileStateOpen; + m_processTimer.start(); + + Q_EMIT started(); + } +} + + + +void FileTransferController::stop() +{ + if( isRunning() ) + { + m_processTimer.stop(); + + if( m_fileReadThread ) + { + delete m_fileReadThread; + m_fileReadThread = nullptr; + } + + m_plugin->sendCancelMessage( m_currentTransferId, m_interfaces ); + } + + Q_EMIT finished(); +} + + + +const QStringList& FileTransferController::files() const +{ + return m_files; +} + + + +int FileTransferController::currentFileIndex() const +{ + return m_currentFileIndex; +} + + + +bool FileTransferController::isRunning() const +{ + return m_processTimer.isActive(); +} + + + +void FileTransferController::process() +{ + switch( m_fileState ) + { + case FileStateOpen: + if( openFile() ) + { + m_fileState = FileStateTransferring; + } + else + { + m_fileState = FileStateFinished; + } + break; + + case FileStateTransferring: + if( transferFile() ) + { + m_fileState = FileStateFinished; + } + break; + + case FileStateFinished: + finishFile(); + + if( ++m_currentFileIndex >= m_files.count() ) + { + if( m_flags.testFlag( OpenTransferFolder ) ) + { + m_plugin->sendOpenTransferFolderMessage( m_interfaces ); + } + + m_processTimer.stop(); + Q_EMIT finished(); + } + else + { + m_fileState = FileStateOpen; + } + break; + } + + updateProgress(); +} + + + +bool FileTransferController::openFile() +{ + if( m_currentFileIndex >= m_files.count() ) + { + return false; + } + + m_fileReadThread = new FileReadThread( m_files[m_currentFileIndex], this ); + + if( m_fileReadThread->start() == false ) + { + delete m_fileReadThread; + m_fileReadThread = nullptr; + Q_EMIT errorOccured( tr( "Could not open file \"%1\" for reading! Please check your permissions!" ).arg( m_currentFileIndex ) ); + return false; + } + + // start reading initial chunk in background + m_fileReadThread->readNextChunk( ChunkSize ); + + m_currentTransferId = QUuid::createUuid(); + + m_plugin->sendStartMessage( m_currentTransferId, QFileInfo( m_files[m_currentFileIndex] ).fileName(), + m_flags.testFlag( OverwriteExistingFiles ), m_interfaces ); + + return true; +} + + + +bool FileTransferController::transferFile() +{ + if( m_fileReadThread == nullptr ) + { + // something went wrong so finish this file + return true; + } + + // wait for all clients to catch up and current data chunk to be read completely + if( allQueuesEmpty() == false || m_fileReadThread->isChunkReady() == false ) + { + return false; + } + + m_plugin->sendDataMessage( m_currentTransferId, m_fileReadThread->currentChunk(), m_interfaces ); + + m_fileReadThread->readNextChunk( ChunkSize ); + + return m_fileReadThread->atEnd(); +} + + + +void FileTransferController::finishFile() +{ + if( m_fileReadThread ) + { + delete m_fileReadThread; + m_fileReadThread = nullptr; + + m_plugin->sendFinishMessage( m_currentTransferId, QFileInfo( m_files[m_currentFileIndex] ).fileName(), + m_flags.testFlag( OpenFilesInApplication ), m_interfaces ); + + m_currentTransferId = QUuid(); + } +} + + + +void FileTransferController::updateProgress() +{ + if( m_files.isEmpty() == false && m_fileReadThread ) + { + Q_EMIT progressChanged( m_currentFileIndex * 100 / m_files.count() + + m_fileReadThread->progress() / m_files.count() ); + } + else if( m_files.count() > 0 && m_currentFileIndex >= m_files.count() ) + { + Q_EMIT progressChanged( 100 ); + } +} + + + +bool FileTransferController::allQueuesEmpty() +{ + for( const auto& controlInterface : qAsConst(m_interfaces) ) + { + if( controlInterface->isMessageQueueEmpty() == false ) + { + return false; + } + } + + return true; +} diff --git a/plugins/filetransfer/FileTransferController.h b/plugins/filetransfer/FileTransferController.h new file mode 100644 index 0000000..8902df7 --- /dev/null +++ b/plugins/filetransfer/FileTransferController.h @@ -0,0 +1,104 @@ +/* + * FileTransferController.h - declaration of FileTransferController class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "ComputerControlInterface.h" + +class FileReadThread; +class FileTransferPlugin; + +class FileTransferController : public QObject +{ + Q_OBJECT +public: + enum Flag { + Transfer = 0x00, + OpenFilesInApplication = 0x01, + OpenTransferFolder = 0x02, + OverwriteExistingFiles = 0x04, + }; + Q_DECLARE_FLAGS(Flags, Flag) + Q_FLAG(Flags) + + explicit FileTransferController( FileTransferPlugin* plugin ); + ~FileTransferController() override; + + void setFiles( const QStringList& files ); + void setInterfaces( const ComputerControlInterfaceList& interfaces ); + void setFlags( Flags flags ); + + void start(); + void stop(); + + const QStringList& files() const; + + int currentFileIndex() const; + + bool isRunning() const; + +Q_SIGNALS: + void errorOccured( const QString& message ); + void filesChanged(); + void progressChanged( int progress ); + void started(); + void finished(); + +private: + enum FileState { + FileStateOpen, + FileStateTransferring, + FileStateFinished + }; + + void process(); + + bool openFile(); + bool transferFile(); + void finishFile(); + + void updateProgress(); + + bool allQueuesEmpty(); + + static constexpr int ProcessInterval = 25; + static constexpr int ChunkSize = 256*1024; + + FileTransferPlugin* m_plugin; + + int m_currentFileIndex; + QUuid m_currentTransferId; + QStringList m_files; + Flags m_flags; + ComputerControlInterfaceList m_interfaces; + + FileReadThread* m_fileReadThread; + + FileState m_fileState; + + QTimer m_processTimer; + +}; diff --git a/plugins/filetransfer/FileTransferDialog.cpp b/plugins/filetransfer/FileTransferDialog.cpp new file mode 100644 index 0000000..99e61cc --- /dev/null +++ b/plugins/filetransfer/FileTransferDialog.cpp @@ -0,0 +1,114 @@ +/* + * FileTransferDialog.cpp - implementation of FileTransferDialog + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "FileTransferController.h" +#include "FileTransferDialog.h" +#include "FileTransferListModel.h" + +#include "ui_FileTransferDialog.h" + +FileTransferDialog::FileTransferDialog( FileTransferController* controller, QWidget* parent ) : + QDialog( parent ), + ui( new Ui::FileTransferDialog ), + m_controller( controller ), + m_listModel( new FileTransferListModel( m_controller, this ) ) +{ + ui->setupUi( this ); + ui->buttonBox->button( QDialogButtonBox::Ok )->setText( tr( "Start" ) ); + + ui->fileListView->setModel( m_listModel ); + + connect( m_controller, &FileTransferController::progressChanged, + this, &FileTransferDialog::updateProgress ); + + connect( m_controller, &FileTransferController::finished, + this, &FileTransferDialog::finish ); +} + + + +FileTransferDialog::~FileTransferDialog() +{ + delete ui; + + delete m_listModel; +} + + + +void FileTransferDialog::accept() +{ + ui->optionsGroupBox->setDisabled( true ); + ui->buttonBox->setStandardButtons( QDialogButtonBox::Cancel ); + + FileTransferController::Flags flags( FileTransferController::Transfer ); + + if( ui->transferAndOpenFolder->isChecked() ) + { + flags |= FileTransferController::OpenTransferFolder; + } + + if( ui->transferAndOpenProgram->isChecked() ) + { + flags |= FileTransferController::OpenFilesInApplication; + } + + if( ui->overwriteExistingFiles->isChecked() ) + { + flags |= FileTransferController::OverwriteExistingFiles; + } + + m_controller->setFlags( flags ); + m_controller->start(); +} + + + +void FileTransferDialog::reject() +{ + if( m_controller->isRunning() ) + { + m_controller->stop(); + } + + QDialog::reject(); +} + + + +void FileTransferDialog::finish() +{ + ui->buttonBox->setStandardButtons( QDialogButtonBox::Close ); + connect( ui->buttonBox->button( QDialogButtonBox::Close ), &QPushButton::clicked, + this, &FileTransferDialog::close ); +} + + + +void FileTransferDialog::updateProgress( int progress ) +{ + ui->progressBar->setValue( progress ); +} diff --git a/plugins/filetransfer/FileTransferDialog.h b/plugins/filetransfer/FileTransferDialog.h new file mode 100644 index 0000000..4874f09 --- /dev/null +++ b/plugins/filetransfer/FileTransferDialog.h @@ -0,0 +1,51 @@ +/* + * FileTransferDialog.h - declaration of class FileTransferDialog + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +namespace Ui { class FileTransferDialog; } + +class FileTransferController; +class FileTransferListModel; + +class FileTransferDialog : public QDialog +{ + Q_OBJECT +public: + FileTransferDialog( FileTransferController* controller, QWidget* parent ); + ~FileTransferDialog() override; + +private: + void accept() override; + void reject() override; + void finish(); + + void updateProgress( int progress ); + + Ui::FileTransferDialog* ui; + + FileTransferController* m_controller; + FileTransferListModel* m_listModel; + +} ; diff --git a/plugins/filetransfer/FileTransferDialog.ui b/plugins/filetransfer/FileTransferDialog.ui new file mode 100644 index 0000000..2b0f82b --- /dev/null +++ b/plugins/filetransfer/FileTransferDialog.ui @@ -0,0 +1,138 @@ + + + Tobias Junghans + FileTransferDialog + + + File transfer + + + + :/filetransfer/applications-office.png:/filetransfer/applications-office.png + + + + + + Options + + + + + + Overwrite existing files + + + + + + + Transfer only + + + true + + + buttonGroup + + + + + + + Transfer and open file(s) with associated program + + + buttonGroup + + + + + + + Transfer and open destination folder + + + buttonGroup + + + + + + + + + + Files + + + + + + QAbstractItemView::NoEditTriggers + + + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + overwriteExistingFiles + transferOnly + transferAndOpenProgram + transferAndOpenFolder + fileListView + + + + + + + buttonBox + rejected() + FileTransferDialog + reject() + + + 251 + 508 + + + 251 + 268 + + + + + buttonBox + accepted() + FileTransferDialog + accept() + + + 251 + 508 + + + 251 + 268 + + + + + + + + diff --git a/plugins/filetransfer/FileTransferListModel.cpp b/plugins/filetransfer/FileTransferListModel.cpp new file mode 100644 index 0000000..3e95362 --- /dev/null +++ b/plugins/filetransfer/FileTransferListModel.cpp @@ -0,0 +1,69 @@ +/* + * FileTransferListModel.cpp - implementation of FileTransferListModel class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "FileTransferListModel.h" +#include "FileTransferController.h" + + +FileTransferListModel::FileTransferListModel( FileTransferController* controller, QObject* parent ) : + QStringListModel( parent ), + m_controller( controller ), + m_scheduledPixmap( QIcon( QStringLiteral( ":/filetransfer/file-scheduled.png" ) ) ), + m_transferringPixmap( QIcon( QStringLiteral( ":/filetransfer/file-transferring.png" ) ) ), + m_finishedPixmap( QIcon( QStringLiteral( ":/filetransfer/file-finished.png" ) ) ) +{ + setStringList( m_controller->files() ); + + connect( m_controller, &FileTransferController::filesChanged, + this, [this]() { setStringList( m_controller->files() ); } ); + + connect( m_controller, &FileTransferController::progressChanged, + this, [this]() { Q_EMIT dataChanged( index( 0 ), index( rowCount() ), { Qt::DecorationRole } ); } ); + + connect( m_controller, &FileTransferController::started, + this, [this]() { setStringList( m_controller->files() ); } ); +} + + + +QVariant FileTransferListModel::data( const QModelIndex& index, int role ) const +{ + if( index.isValid() && role == Qt::DecorationRole ) + { + int currentRow = m_controller->currentFileIndex(); + + if( index.row() < currentRow ) + { + return m_finishedPixmap; + } + else if( index.row() > currentRow || m_controller->isRunning() == false ) + { + return m_scheduledPixmap; + } + + return m_transferringPixmap; + } + + return QStringListModel::data( index, role ); +} diff --git a/plugins/filetransfer/FileTransferListModel.h b/plugins/filetransfer/FileTransferListModel.h new file mode 100644 index 0000000..381f495 --- /dev/null +++ b/plugins/filetransfer/FileTransferListModel.h @@ -0,0 +1,47 @@ +/* + * FileTransferListModel.h - declaration of FileTransferListModel class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +class FileTransferController; + +class FileTransferListModel : public QStringListModel +{ + Q_OBJECT +public: + FileTransferListModel( FileTransferController* controller, QObject* parent ); + ~FileTransferListModel() override = default; + + QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const override; + +private: + FileTransferController* m_controller; + QIcon m_scheduledPixmap; + QIcon m_transferringPixmap; + QIcon m_finishedPixmap; + +}; diff --git a/plugins/filetransfer/FileTransferPlugin.cpp b/plugins/filetransfer/FileTransferPlugin.cpp new file mode 100644 index 0000000..a2bea56 --- /dev/null +++ b/plugins/filetransfer/FileTransferPlugin.cpp @@ -0,0 +1,346 @@ +/* + * FileTransferPlugin.cpp - implementation of FileTransferPlugin class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "BuiltinFeatures.h" +#include "Filesystem.h" +#include "FileTransferConfigurationPage.h" +#include "FileTransferController.h" +#include "FileTransferDialog.h" +#include "FileTransferPlugin.h" +#include "FileTransferUserConfiguration.h" +#include "FeatureWorkerManager.h" +#include "PlatformFilesystemFunctions.h" +#include "SystemTrayIcon.h" +#include "VeyonMasterInterface.h" +#include "VeyonServerInterface.h" +#include "VeyonWorkerInterface.h" + + +FileTransferPlugin::FileTransferPlugin( QObject* parent ) : + QObject( parent ), + m_fileTransferFeature( QStringLiteral( "FileTransfer" ), + Feature::Action | Feature::AllComponents, + Feature::Uid( "4a70bd5a-fab2-4a4b-a92a-a1e81d2b75ed" ), + Feature::Uid(), + tr( "File transfer" ), {}, + tr( "Click this button to transfer files from your computer to all computers." ), + QStringLiteral(":/filetransfer/applications-office.png") ), + m_features( { m_fileTransferFeature } ), + m_configuration( &VeyonCore::config() ) +{ +} + + + +FileTransferPlugin::~FileTransferPlugin() +{ + delete m_fileTransferController; +} + + + +bool FileTransferPlugin::controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( hasFeature( featureUid ) == false ) + { + return false; + } + + if( operation == Operation::Initialize ) + { + auto files = arguments.value( argToString(Argument::Files) ).toStringList(); + if( files.isEmpty() ) + { + return false; + } + + for( auto& file : files ) + { + QFileInfo fileInfo( file ); + if( fileInfo.dir() == QDir::current() ) + { + file = fileInfo.fileName(); + } + } + + if( m_fileTransferController == nullptr ) + { + m_fileTransferController = new FileTransferController( this ); + } + + m_fileTransferController->setFiles( files ); + m_fileTransferController->setInterfaces( computerControlInterfaces ); + + return true; + } + + if( operation == Operation::Start ) + { + if( m_fileTransferController ) + { + m_fileTransferController->start(); + } + + return true; + } + + if( operation == Operation::Stop ) + { + if( m_fileTransferController ) + { + m_fileTransferController->stop(); + } + + return true; + } + + return false; +} + + + +bool FileTransferPlugin::startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( feature == m_fileTransferFeature ) + { + FileTransferUserConfiguration config( master.userConfigurationObject() ); + + m_lastFileTransferSourceDirectory = config.lastFileTransferSourceDirectory(); + if( m_configuration.rememberLastFileTransferSourceDirectory() == false || + QDir( m_lastFileTransferSourceDirectory ).exists() == false ) + { + m_lastFileTransferSourceDirectory = VeyonCore::filesystem().expandPath( m_configuration.fileTransferDefaultSourceDirectory() ); + } + const auto files = QFileDialog::getOpenFileNames( master.mainWindow(), + tr( "Select one or more files to transfer" ), + m_lastFileTransferSourceDirectory ); + + if( files.isEmpty() == false ) + { + config.setLastFileTransferSourceDirectory( QFileInfo( files.first() ).absolutePath() ); + + if( controlFeature( feature.uid(), Operation::Initialize, + { { argToString(Argument::Files), files } }, computerControlInterfaces ) ) + { + auto dialog = new FileTransferDialog( m_fileTransferController, master.mainWindow() ); + connect( dialog, &QDialog::finished, dialog, &QDialog::deleteLater ); + dialog->exec(); + } + } + + return true; + } + + return false; +} + + + +bool FileTransferPlugin::handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) +{ + Q_UNUSED(messageContext) + + if( m_fileTransferFeature.uid() == message.featureUid() ) + { + if( message.command() == FileTransferFinishCommand ) + { + VeyonCore::builtinFeatures().systemTrayIcon().showMessage( m_fileTransferFeature.displayName(), + tr( "Received file \"%1\"." ). + arg( message.argument( Argument::Filename ).toString() ), + server.featureWorkerManager() ); + } + + // forward message to worker + server.featureWorkerManager().sendMessageToUnmanagedSessionWorker( message ); + + return true; + } + + return false; +} + + + +bool FileTransferPlugin::handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) +{ + Q_UNUSED(worker) + + if( m_fileTransferFeature.uid() == message.featureUid() ) + { + switch( message.command() ) + { + case FileTransferStartCommand: + m_currentFile.close(); + + m_currentFileName = destinationDirectory() + QDir::separator() + message.argument( Argument::Filename ).toString(); + m_currentFile.setFileName( m_currentFileName ); + if( m_currentFile.exists() && message.argument( Argument::OverwriteExistingFile ).toBool() == false ) + { + QMessageBox::critical( nullptr, m_fileTransferFeature.displayName(), + tr( "Could not receive file \"%1\" as it already exists." ). + arg( m_currentFile.fileName() ) ); + return true; + } + + if( VeyonCore::platform().filesystemFunctions().openFileSafely( + &m_currentFile, + QFile::WriteOnly | QFile::Truncate, + QFile::ReadOwner | QFile::WriteOwner | QFile::ReadGroup | QFile::WriteGroup | QFile::ReadOther ) ) + { + m_currentTransferId = message.argument( Argument::TransferId ).toUuid(); + } + else + { + QMessageBox::critical( nullptr, m_fileTransferFeature.displayName(), + tr( "Could not receive file \"%1\" as it could not be opened for writing!" ). + arg( m_currentFile.fileName() ) ); + } + return true; + + case FileTransferContinueCommand: + if( message.argument( Argument::TransferId ).toUuid() == m_currentTransferId ) + { + m_currentFile.write( message.argument( Argument::DataChunk ).toByteArray() ); + } + else + { + vWarning() << "received chunk for unknown transfer ID"; + } + return true; + + case FileTransferCancelCommand: + if( message.argument( Argument::TransferId ).toUuid() == m_currentTransferId ) + { + m_currentFile.remove(); + } + else + { + vWarning() << "received chunk for unknown transfer ID"; + } + return true; + + case FileTransferFinishCommand: + m_currentFile.close(); + if( message.argument( Argument::OpenFileInApplication ).toBool() ) + { + QDesktopServices::openUrl( QUrl::fromLocalFile( m_currentFileName ) ); + } + m_currentFile.setFileName( {} ); + return true; + + case OpenTransferFolder: + QDesktopServices::openUrl( QUrl::fromLocalFile( destinationDirectory() ) ); + return true; + + default: + break; + } + } + + return false; +} + + + +void FileTransferPlugin::sendStartMessage( QUuid transferId, const QString& fileName, + bool overwriteExistingFile, const ComputerControlInterfaceList& interfaces ) +{ + sendFeatureMessage( FeatureMessage( m_fileTransferFeature.uid(), FileTransferStartCommand ). + addArgument( Argument::TransferId, transferId ). + addArgument( Argument::Filename, fileName ). + addArgument( Argument::OverwriteExistingFile, overwriteExistingFile ), + interfaces ); +} + + + +void FileTransferPlugin::sendDataMessage( QUuid transferId, const QByteArray& data, + const ComputerControlInterfaceList& interfaces ) +{ + sendFeatureMessage( FeatureMessage( m_fileTransferFeature.uid(), FileTransferContinueCommand ). + addArgument( Argument::TransferId, transferId ). + addArgument( Argument::DataChunk, data ), + interfaces ); +} + + + +void FileTransferPlugin::sendCancelMessage( QUuid transferId, + const ComputerControlInterfaceList& interfaces ) +{ + sendFeatureMessage( FeatureMessage( m_fileTransferFeature.uid(), FileTransferCancelCommand ). + addArgument( Argument::TransferId, transferId ), interfaces ); +} + + + +void FileTransferPlugin::sendFinishMessage( QUuid transferId, const QString& fileName, + bool openFileInApplication, const ComputerControlInterfaceList& interfaces ) +{ + sendFeatureMessage( FeatureMessage( m_fileTransferFeature.uid(), FileTransferFinishCommand ). + addArgument( Argument::TransferId, transferId ). + addArgument( Argument::Filename, fileName ). + addArgument( Argument::OpenFileInApplication, openFileInApplication ), interfaces ); +} + + + +void FileTransferPlugin::sendOpenTransferFolderMessage( const ComputerControlInterfaceList& interfaces ) +{ + sendFeatureMessage( FeatureMessage( m_fileTransferFeature.uid(), OpenTransferFolder ), interfaces ); +} + + + +QString FileTransferPlugin::destinationDirectory() const +{ + auto dir = VeyonCore::filesystem().expandPath( m_configuration.fileTransferDestinationDirectory() ); + if( QDir( dir ).exists() == false && + m_configuration.fileTransferCreateDestinationDirectory() && + VeyonCore::filesystem().ensurePathExists( dir ) == false ) + { + return QDir::homePath(); + } + + return dir; +} + + + +ConfigurationPage* FileTransferPlugin::createConfigurationPage() +{ + return new FileTransferConfigurationPage( m_configuration ); +} + + +IMPLEMENT_CONFIG_PROXY(FileTransferConfiguration) diff --git a/plugins/filetransfer/FileTransferPlugin.h b/plugins/filetransfer/FileTransferPlugin.h new file mode 100644 index 0000000..2c96dda --- /dev/null +++ b/plugins/filetransfer/FileTransferPlugin.h @@ -0,0 +1,140 @@ +/* + * FileTransferPlugin.h - declaration of FileTransferPlugin class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "ConfigurationPagePluginInterface.h" +#include "FeatureProviderInterface.h" +#include "FileTransferConfiguration.h" + +class FileTransferController; + +class FileTransferPlugin : public QObject, FeatureProviderInterface, PluginInterface, ConfigurationPagePluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.FileTransfer") + Q_INTERFACES(PluginInterface + FeatureProviderInterface + ConfigurationPagePluginInterface) +public: + enum class Argument + { + TransferId, + Filename, + DataChunk, + OpenFileInApplication, + OverwriteExistingFile, + Files + }; + Q_ENUM(Argument) + + explicit FileTransferPlugin( QObject* parent = nullptr ); + ~FileTransferPlugin() override; + + Plugin::Uid uid() const override + { + return QStringLiteral("d4bb9c42-9eef-4ecb-8dd5-dfd84b355481"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 0 ); + } + + QString name() const override + { + return QStringLiteral("FileTransfer"); + } + + QString description() const override + { + return tr( "Transfer files to remote computer" ); + } + + QString vendor() const override + { + return QStringLiteral("Veyon Community"); + } + + QString copyright() const override + { + return QStringLiteral("Tobias Junghans"); + } + + const FeatureList& featureList() const override + { + return m_features; + } + + bool controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) override; + + bool handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) override; + + void sendStartMessage( QUuid transferId, const QString& fileName, + bool overwriteExistingFile, const ComputerControlInterfaceList& interfaces ); + void sendDataMessage( QUuid transferId, const QByteArray& data, const ComputerControlInterfaceList& interfaces ); + void sendCancelMessage( QUuid transferId, const ComputerControlInterfaceList& interfaces ); + void sendFinishMessage( QUuid transferId, const QString& fileName, + bool openFileInApplication, const ComputerControlInterfaceList& interfaces ); + void sendOpenTransferFolderMessage( const ComputerControlInterfaceList& interfaces ); + + ConfigurationPage* createConfigurationPage() override; + +private: + QString destinationDirectory() const; + + enum Commands + { + FileTransferStartCommand, + FileTransferContinueCommand, + FileTransferCancelCommand, + FileTransferFinishCommand, + OpenTransferFolder, + CommandCount + }; + + const Feature m_fileTransferFeature; + const FeatureList m_features; + + FileTransferConfiguration m_configuration; + + QString m_lastFileTransferSourceDirectory; + + FileTransferController* m_fileTransferController{nullptr}; + + QFile m_currentFile{}; + QString m_currentFileName{}; + QUuid m_currentTransferId{}; + +}; diff --git a/plugins/filetransfer/FileTransferUserConfiguration.h b/plugins/filetransfer/FileTransferUserConfiguration.h new file mode 100644 index 0000000..23d6465 --- /dev/null +++ b/plugins/filetransfer/FileTransferUserConfiguration.h @@ -0,0 +1,45 @@ +/* + * FileTransferUserConfiguration.h - user config values for file transfer + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "Configuration/Proxy.h" +#include "Configuration/Property.h" + +#define FOREACH_FILE_TRANSFER_USER_CONFIG_PROPERTIES(OP) \ + OP( FileTransferUserConfiguration, config, QString, lastFileTransferSourceDirectory, setLastFileTransferSourceDirectory, "LastSourceDirectory", "FileTransfer", QDir::homePath(), Configuration::Property::Flag::Standard ) + +class FileTransferUserConfiguration : public Configuration::Proxy +{ +public: + explicit FileTransferUserConfiguration( Configuration::Object* object ) : + Configuration::Proxy( object ) + { + } + + FOREACH_FILE_TRANSFER_USER_CONFIG_PROPERTIES(DECLARE_CONFIG_PROPERTY) + +}; diff --git a/plugins/filetransfer/applications-office.png b/plugins/filetransfer/applications-office.png new file mode 100644 index 0000000000000000000000000000000000000000..d2100bd09d60bfb143bbda45377b2651e3953092 GIT binary patch literal 2322 zcmV+t3GMcYP)k8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H12#-lbK~#9!?VL+*8&wp?|HqG{NeyjSv0wq4ERc{Y=p!u)HU;qs z5}>S*5bX!x8z8Y{38)e~7DYm=fYO$h0*#XBGfS3RvCIE;D0Ad1wm;fLq02ubl{0`^o zguL|QM|ktYOV-xE`^C$1m%m&-ogD?>d_}~@Ps_N)GdN=jg0+Q-sdp|+R72!W)49&GdS3aV@wg7cHrD_^AtfdJ4e1i!4HifK&Q zf?#oC>h1HB)vGB%AZ=iu5X`YX2p*&efha(~5L{Wo9Hwz9;}8e{2ZUfA(>V1(b|H`y zkPrkXGYWwyKtd4Q=4?VB03-#$)fL>%pb|(5NEm{7OylJH79sd#vU>IF!0C&5x}v>;jw5v=HR*Dbr1@h>%hk2*7|Rq`xXGV4g&xp4woW73dz_c zv3@)Mt%Aql0xjoF;i9B~o{TFmfI*^q->B-9{NLdS8{x4GB9bDoFFQN|Rse0(;Cnpq1~_;eI3I@deF7K+DC2byD0rR;7<*o? zUw_m(jD6>;$guT=BpFcGe5g8bC!OKN{iTTZhN)j{I)w_kjbTTu(6! zVC;zgUVj{weLrNG+BiP~1tSu&zVic~$`ua)u=A9l_K1AMB42fi{798}Kmcs}$8(&3 z*6zlS=LKnXV2qq6VAK!LP#%=t#-M+?NLOqa$Ln6(nH@&1tl&{zDR5Y+pK zr33%?WQsuBHGp!f>)3ZcV@LE!-+U6bg6v0rJLelYKQ`S#yFRUaSb#D2z}*Js#=Adr2I%f6}Ek?tV$XuCqKIId7a0nNHrh=7+MxY=}m zm{t;r6MCy9^!XZWx zKy#aR4TTUJ{RYoxVic?#O92PkPkCIAe;Zp}IUm&9`o&RwaC19$dqA7WXO4}LT}=VI zA};Iu85z^*pz*A;>>KH!SKbSwj1m!=+j~bo5sLy0Y(J$#`uBDkdqjQ@m+ayYlo>?^ z)DCryd`_welrEqzqtYQg6>M&Ic77iU(1Kt@-w>i2)Hd>okkAbwY4_bjv>z&u>I**2 zM(3{J0KgYZ05L-~sprV=8UVJg0PNBrF!}uyNAw!rPMgAz4lH<7_U|F^+Gm;sQh;5L zD91*@HugisvUarBG=@OQ?YU6)neukkn9>EfvL7mHFR}8B6p$S6R18y?zF`#E7zGby z7tk?OOZrYBR{pgcN*FSrKme;~XW(`hFf5b69=t>#KU69CjAvYtPG$~}cEsgjKUC16 zYKe9#VBaf^G6xv=eyD3F*)#|42Vl2qfK>NGcHclIf$s0eeFcyjV5m*HfC2AEYJfb= z0WzroX-8bn_LBhxkZS?CRsgvgXjuXBo&s#&5AgZx<-YfGZ8e2_koO|ccHiCa=MP+8 zRk*(D4ZHt`zYFN6ufMUXa3clhXF~xpUUm1n{%`jbZr<}!`};)!98!Rr_Y{6l(fLvX z!2aZ|TLt)IP2rEUoiAO0{r?T@X8k|c6#iTr&_qhQfWdYFx7HM9*Bl#1=Bfs;oC5Zm zGtAyss1DfqxgQ1F{)CWD3NW|kVeWo@Bj2GKApP~{?|Ybc#Q7{;K)PMP?V5)LiF|kG z02f{@TU&qOzK6w{Th4c00fyz1LYHa^iw`97omT)G_SXZ2rJ9@0&;2Oa_NRs4sVUrX zIr8&@aOfLMC=Kj4@BOLwg^_W%F@07*qoM6N<$f($Yl?EnA( literal 0 HcmV?d00001 diff --git a/plugins/filetransfer/document-share.png b/plugins/filetransfer/document-share.png new file mode 100644 index 0000000000000000000000000000000000000000..068481cfcbd006c64c86e226be6448d3453e642f GIT binary patch literal 614 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&F&8^|hH!9j+#=yXs>*?YcQgQ3; zEyuDZ2LYA~jL8m*C0#eZufMGGX+o&j%T)n_tN0%LJ}-BC(?*fwg)+wr=g13n%3Nhw zrEsMwfLlq)=)qzs2jL}*I*xJ7+4rN4zOx4h#g=EX<&9>WNhG+ zVDNBYRAAy`5SYMF$;drH>;gk1BmV>u0Y*zk;R!+kjFODv69gtOCLZ|3_~p9M0S0Cs zgM`qAr&rki*gI5P*vmcN7s^;R{k>I4xs*foOs>dW)rP|{oYtHVirMCSa9edgTvf@o*ntcEPHKP)-`JP>3W5w$0Obc+rGfvli8dDuZ>1l!XhH`ti%X;2ZaUkF%~2b+^s=Zu=Os1Mpl1f#VC~oWON_(3s7D&e&4Gkn3l_2XUqTSRTWW4;pfq zcrJ+N0XPl=#|5z*e*zc4^O*Qj5C20z{4V|mM2(866v+_%iuz zYFaWgJ13pj4cXubeW7Izwq%d^?vXq%cXzTz(k7(I3WAZYD_=iN6{$>3cQYx$r?uoS zqUDio(Q0RX9jrGr5M%4K48w(8Tc5W|S8E4Xt-m`u>bGX!kLT=xb5=1JXCW_KdaI)S zW3j?RogIFuSR$j#q8yVRpD+~lvM0LdI^|4OQ(%6xy!}WTTCG&D!|%jYQuQcWD$a0M*umD#z$j1*H_z@+p$rLUKx|t-xYGYk2B0>rjd!-E1J$`~JH- z5lxucu9hV8?8GFs5YiP_;LXCCUU^N_Zn|^A?6WoVDJE>kUGR8hRf^sgBTSJ;{}D8? zk8GJUYHfc|+O%GEU`Im1mAu4wYNBqj&ZnD_WxRg#hC`=9CCgA*>a$sstJ)^k{T=Af zi2cW|O`1dgyiof^<093FWjpgR-YGxlSb)LR$9f|3wE|5K6VkM+dpSy2V|G)oj#l1p zMI#8NFKcU_QK#Wal1ycdNJmT1iukBj(vKSeO^Jsy8_S$bNHbo|E0ss(ZXiCVVoRCU z9RxI;2;SHggQ!9brS5vf3^XZHZki8jjH)bI6o~A`d_t!u`XYN5j(^mWBsw8`$As(T z(}wid3ueU?`jZqN()d)1bnQ^()g)1&mn6nm>CX^l#rYAr%eIpJd2%VNyS{k0$_uxW zL>Pu<86m`svFpG(-sySWps%+@SNl*qA4ieRvYVYdJ@~zY8gtewwIUDmY}|9IT`f%f zo-a{p%jxc%yHKFeA5zhYuJ?&L7J16^+Y5u)s^Jli4PnWN;jEcg$%{|NwcfI-yqKQ+ hYPVrmP2Klrdphym*$hE~4Ye8m9XQxI+j1#D%6|hc{7nD= literal 0 HcmV?d00001 diff --git a/plugins/filetransfer/file-scheduled.png b/plugins/filetransfer/file-scheduled.png new file mode 100644 index 0000000000000000000000000000000000000000..d54eb9f710a8219cf27fa91ed0f49befe06fcb6d GIT binary patch literal 1507 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&F&8^|hH!9j+_AR{0kASx;f$OsG!3nKKmg=INT4i4 z3PQrUa9NQ4K@9%?|G%=l$QBq|JtaYY!3@l-Z0sCd+&sK|`~reP!je)lvU2Jgnp!4i z=63cDj!rJFZtfnwe*OW0!J%Q{5s^{RF|lz8iODHx=^2?>**UrSjZMuhtsQ;+6DQ4_ zJ!jscrOQ{WT(x@5+I8zUY(9MC=&|D`&YZt+@zUihSFc^aar4&gJ9qEhfAH|p-V3(|70@_y@9Dx*we)^q~g}wS&`vEfg%m&3&Px1OifP9Y)&+h zytRNch>tgHN}8pAXT&nLl~cK9-C-&Fzw`PvqrK@jZofYKb6(`@-(m&tZJ*!aO)!w) zVQW6Ppk=$-4Py&g|GYoj%qD$&dGK9A@AnH&)L&Jnf7&tKC4NE6{WzgnrylNmqUOAY zcil2U?%WM+vtxX!%1?`nC`oFjTo8BJx7Z+iu3Xmt*7F)iKcxoMW{ND8wDo?@pSO_d zWtvj$!{QK?s-8pfrv0bRgueKFetAIiTejH$TjN)_K5Lt|zf|1xwRzU zEjWRD#jn?86Ii@I&X1VtrW7>Q>-+pjmE?UMiVsYMJ_X&Y=l0}0^~*p1BHNPFD$Ms3 z4fCJ=;q^EtD!D)X{j2xq?cY4N_+aeKD#R$bg#W;X9ryR165OD+FQB$ThOxr`%KNBC zoIkX7C1*O#WB9_&)>FXB)A_SjEWAN=TQRS4I7652`DH(tj6A#>G9-QA&mhrA8< z7shU*<6i^%pTw|e{#yAr|H+?#Y0JJY_f}kdSg!hSRvMotC@JtrS=b(NGB_s8;QNJbrdhV*>{FJd^=o7} zE=Z(lGfB;wI?re2Z3nkZ6aBaHw#-wm)K0prAIp?v^DwmVuUEVDi^A zRDPRm&N-f+JK1J4G937x@uxv>_nU~_{5LG)?puG}|NV65z5|*TI$f)0?`Bg=>MT7S p<$pwtZP&XH@#ce&6f9KFc-N~;c9p1AJTTibc)I$ztaD0e0szRRc+>y@ literal 0 HcmV?d00001 diff --git a/plugins/filetransfer/file-transferring.png b/plugins/filetransfer/file-transferring.png new file mode 100644 index 0000000000000000000000000000000000000000..2bee0b624752c90bc605d978372d7af883e53027 GIT binary patch literal 3077 zcmV+g4EpnlP)Hq)$8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H13xY{RK~#9!?Ol6tTvZw08OO(nZ~v;J{wwI8I5JLmHv!tTpx9!k zyb9WaLZQ}<;|q0s0CmJcV2a2PVNh|9lIHGB8ca(I^qrPc+lHj1P1>YQNOJGp>?^O` zB%2)1ce}Ak+hD$P&)q%e?)RHHLufX4@A~?Fp1+#)Ud0=F=yrVvG+bp z6juPi2)k%Aj2{~&yc4_| zamXiJUClno&*<-Pf}d1?>6af|kBFMqhRVNneS|nB2{$x$d>Dwz3EZPB<1ZOjcf@ek zf2HQchlqn7P+|06%Qg6S^dK)?PxKL?U9u7eq|^MQs^>$#5FNHGN+s&dvkp z?RI-GIL!aw$Z%_A6>)ej#|>AUqA_bz02x1f@SUydHR}rE_{=T8uK&xNIIV$a{)O>Q zW$nO~#Cc$TEMCD0k}f0WPmiIBn7z2nnQtxQgc$Xc(0A+@U>lEQ*x%cR*^cfkYfX%^ z-r+p!AIY=9@hNvCvXiW5DDVDmXHS;> zsNZ-s&i;6GknQfxu|#f?c}03`(iMt_-_E$t_CW~pe5@@i=0^?tKAA+UG;Q{qx6*8X ze~yh$PBFg#&j!JIuqn+dOxZpIXMH(s`*|5eTU_t3P0fSuc3y12k@48EL3TZV2U!6o zh%^S<#y9&b`9!l`i3O|gaZY^Lo=Xjj3*RIBc#yOsj0ILPESP#3uS9xaPm;B#CYWky zOOCVEZzN<0K{^`iyDD%_9zL<-UI=q-oy}fuA7N8WT}51cmGK8S@>W03L2I<^? z!3#lFZXaMR+(c6y_Gn(B>F7Kc6z1RNg%Cg7n_@%bQ_3?Rbb(tX`wbv2qW9KsBtGvY zu=C4z5z{`0oglbWWjJ^sviBsn#TUzS(Xy(oj=M7?bCacEej(9R zizL&CVSP_zg0VBBo}Dld#+AwV%L&4_w|R>Qg78$g4vFdlYm{a_6h^>#g`L>CJxNa< zzH@_lwnmN{*4F{dhaCh|ocnhR9Zjc|EOiAW{y{6|g*jha}QkQGX_QD?u% z>hB)Pv!xoG`R}*#FE(2t)Qx=peigTWRDJ&M4rkEJhq%z)Zzck<@@E3_8L^3^N_8y} z(f*Ic@$vnCo*Ge}`75@?QOpM%jgR4#LBsl%Lg&Br6z1h`Zj7VtenEw)=;MY4$8|$v z0Dghxjd84N_(_$_KU7)QpN@#$-})W_%ltnA;(=EJ z?<^xF&>5Y?nt`3#nSp0AI5Y5PP5PRFKW{>sfhn{Z*sPr+2|KhL#A_I|411 zHKOPCgw};2$B~60P;9YcoB9HGPMVT*J z@5oJVBcv*1G>OuM`OsZdblV3&SX|)jSG!46p-tQ=ky}7Od)7(YhdYBr0do~9cQfn` zrQ5{0%Ajf8BU)1JmHE)b(a$Pk(DeVs?R>8t>}y4fT}@|oNOicaVv%WLGTX9%7Y_6a znaw2NS%2owOi8$huq`RrA5fQ8vFGaro%N;e$piHVu+pF-XH-zZgAS(9!WNszIv3!U z(p$ER?Q&eddLX0=KvKgot4)=LOkiq4ktqaeesUniR_z#I-z>xx;L=I>dDYGV_GA9L zzqAaoUHr2$R{O;{|4=e6IL}jBwTmxU1dCK@#a3#7(kIXfY=*DFx-O(6AZJaLR&1qY zzCft=!q#uvFNJgkTzDkovPfN5K5YA5R=!I+BfcP{GoprdYdNlbNC@?u%V^qnhJt{w z;XGeXEB|C_!n02!YS`7Gpa8Cp<)+v%uL1z6WaA27CkC&!xno4o&Y$J)Zwb`Zv91u* z4qOQrBYPEs+qnrR(q`6goK8=$`*um`W|rc*)0aX)MAUGivaXMZE8BaAOMe_dQVU!d zT`8w{B?~kP1sTxfm``*SX>(Vya! zyplJ76eokoTE-_o>UaNs5QyjB9cGQ^vTizhXJ*3HA0Rjb1Ac~Mq3G` z_HafxaD!>_5{@}5L*|Mt5+TIPj8eF&GSJR&+iYpH2papHzz^@QCoJZh-C1|Y*UN~T8q09MLVI&Iw z)(oG4&%)nC4eJhghBDo_2m}IwKp+qZ1OkCTAP@)y0)apv5C{YUfj}S-=7IkL)e7zt T3ez8~00000NkvXXu0mjf@qx>8 literal 0 HcmV?d00001 diff --git a/plugins/filetransfer/filetransfer.qrc b/plugins/filetransfer/filetransfer.qrc new file mode 100644 index 0000000..065b1c5 --- /dev/null +++ b/plugins/filetransfer/filetransfer.qrc @@ -0,0 +1,9 @@ + + + applications-office.png + file-scheduled.png + file-transferring.png + file-finished.png + document-share.png + + diff --git a/plugins/ldap/CMakeLists.txt b/plugins/ldap/CMakeLists.txt new file mode 100644 index 0000000..dbe1269 --- /dev/null +++ b/plugins/ldap/CMakeLists.txt @@ -0,0 +1,11 @@ +INCLUDE(BuildVeyonPlugin) + +ADD_SUBDIRECTORY(kldap) +ADD_SUBDIRECTORY(common) + +build_veyon_plugin(ldap + LdapPlugin.cpp + LdapPlugin.h +) + +TARGET_LINK_LIBRARIES(ldap ldap-common) diff --git a/plugins/ldap/LdapPlugin.cpp b/plugins/ldap/LdapPlugin.cpp new file mode 100644 index 0000000..2c2cfb7 --- /dev/null +++ b/plugins/ldap/LdapPlugin.cpp @@ -0,0 +1,309 @@ +/* + * LdapPlugin.cpp - implementation of LdapPlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "CommandLineIO.h" +#include "ConfigurationManager.h" +#include "LdapNetworkObjectDirectory.h" +#include "LdapPlugin.h" +#include "LdapConfigurationPage.h" +#include "LdapDirectory.h" +#include "VeyonConfiguration.h" + + +LdapPlugin::LdapPlugin( QObject* parent ) : + QObject( parent ), + m_configuration( &VeyonCore::config() ), + m_ldapClient( nullptr ), + m_ldapDirectory( nullptr ), + m_commands( { +{ QStringLiteral("autoconfigurebasedn"), tr( "Auto-configure the base DN via naming context" ) }, +{ QStringLiteral("query"), tr( "Query objects from LDAP directory" ) }, +{ QStringLiteral("help"), tr( "Show help about command" ) }, + } ) +{ +} + + + +LdapPlugin::~LdapPlugin() +{ + delete m_ldapDirectory; + m_ldapDirectory = nullptr; + + delete m_ldapClient; + m_ldapClient = nullptr; +} + + + +void LdapPlugin::upgrade( const QVersionNumber& oldVersion ) +{ + if( oldVersion < QVersionNumber( 1, 1 ) ) + { + const auto upgradeFilter = []( const QString& filter ) { + if( filter.isEmpty() == false && + filter.startsWith( QLatin1Char('(') ) == false ) + { + return QStringLiteral("(%1)").arg( filter ); + } + // already upgraded or special filter so don't touch + return filter; + }; + + m_configuration.setComputerContainersFilter( upgradeFilter( m_configuration.computerContainersFilter() ) ); + m_configuration.setComputerGroupsFilter( upgradeFilter( m_configuration.computerGroupsFilter() ) ); + m_configuration.setComputersFilter( upgradeFilter( m_configuration.computersFilter() ) ); + m_configuration.setUserGroupsFilter( upgradeFilter( m_configuration.userGroupsFilter() ) ); + m_configuration.setUsersFilter( upgradeFilter( m_configuration.usersFilter() ) ); + + // bind password not encrypted yet? + const auto rawBindPassword = m_configuration.bindPasswordProperty().variantValue().toString(); + if( rawBindPassword.size() < MaximumPlaintextPasswordLength ) + { + // setting it again will encrypt it + m_configuration.setBindPassword( Configuration::Password::fromPlainText( rawBindPassword.toUtf8() ) ); + } + } + else if( oldVersion < QVersionNumber( 1, 2 ) ) + { + m_configuration.setUserLoginNameAttribute( m_configuration.legacyUserLoginAttribute() ); + m_configuration.setComputerLocationsByAttribute( m_configuration.legacyComputerRoomMembersByAttribute() ); + m_configuration.setComputerLocationsByContainer( m_configuration.legacyComputerRoomMembersByContainer() ); + m_configuration.setComputerLocationAttribute( m_configuration.legacyComputerRoomAttribute() ); + m_configuration.setLocationNameAttribute( m_configuration.legacyComputerRoomNameAttribute() ); + } +} + + + +QStringList LdapPlugin::commands() const +{ + return m_commands.keys(); +} + + + +QString LdapPlugin::commandHelp( const QString& command ) const +{ + return m_commands.value( command ); +} + + + +NetworkObjectDirectory *LdapPlugin::createNetworkObjectDirectory( QObject* parent ) +{ + return new LdapNetworkObjectDirectory( m_configuration, parent ); +} + + + +void LdapPlugin::reloadConfiguration() +{ + delete m_ldapDirectory; + m_ldapDirectory = new LdapDirectory( m_configuration ); +} + + + +QStringList LdapPlugin::userGroups( bool queryDomainGroups ) +{ + Q_UNUSED(queryDomainGroups); + + return LdapClient::stripBaseDn( ldapDirectory().userGroups(), ldapClient().baseDn() ); +} + + + +QStringList LdapPlugin::groupsOfUser( const QString& username, bool queryDomainGroups ) +{ + Q_UNUSED(queryDomainGroups); + + const auto strippedUsername = VeyonCore::stripDomain( username ); + + const QString userDn = ldapDirectory().users( strippedUsername ).value( 0 ); + + if( userDn.isEmpty() ) + { + vWarning() << "empty user DN for user" << strippedUsername; + return QStringList(); + } + + return LdapClient::stripBaseDn( ldapDirectory().groupsOfUser( userDn ), ldapClient().baseDn() ); +} + + + +ConfigurationPage *LdapPlugin::createConfigurationPage() +{ + return new LdapConfigurationPage( m_configuration ); +} + + + +CommandLinePluginInterface::RunResult LdapPlugin::handle_autoconfigurebasedn( const QStringList& arguments ) +{ + QUrl ldapUrl; + ldapUrl.setUrl( arguments.value( 0 ), QUrl::StrictMode ); + + if( ldapUrl.isValid() == false || ldapUrl.host().isEmpty() ) + { + CommandLineIO::error( tr("Please specify a valid LDAP url following the schema " + "\"ldap[s]://[user[:password]@]hostname[:port]\"") ); + return InvalidArguments; + } + + QString namingContextAttribute = arguments.value( 1 ); + + if( namingContextAttribute.isEmpty() ) + { + CommandLineIO::warning( tr("No naming context attribute name given - falling back to configured value." ) ); + } + else + { + m_configuration.setNamingContextAttribute( namingContextAttribute ); + } + + LdapClient ldapClient( m_configuration, ldapUrl ); + QString baseDn = ldapClient.queryNamingContexts().value( 0 ); + + if( baseDn.isEmpty() ) + { + CommandLineIO::error( tr("Could not query base DN. Please check your LDAP configuration." ) ); + return Failed; + } + + CommandLineIO::info( tr( "Configuring %1 as base DN and disabling naming context queries." ).arg( baseDn ) ); + + m_configuration.setBaseDn( baseDn ); + m_configuration.setQueryNamingContext( false ); + + // write configuration + ConfigurationManager configurationManager; + if( configurationManager.saveConfiguration() == false ) + { + CommandLineIO::error( configurationManager.errorString() ); + return Failed; + } + + return Successful; +} + + + +CommandLinePluginInterface::RunResult LdapPlugin::handle_query( const QStringList& arguments ) +{ + QString objectType = arguments.value( 0 ); + QString filter = arguments.value( 1 ); + QStringList results; + + if( objectType == QLatin1String("locations") ) + { + results = ldapDirectory().computerLocations( filter ); + } + else if( objectType == QLatin1String("computers") ) + { + results = ldapDirectory().computersByHostName( filter ); + } + else if( objectType == QLatin1String("groups") ) + { + results = ldapDirectory().groups( filter ); + } + else if( objectType == QLatin1String("users") ) + { + results = ldapDirectory().users( filter ); + } + else + { + return InvalidArguments; + } + + for( const auto& result : qAsConst( results ) ) + { + printf( "%s\n", qUtf8Printable( result ) ); + } + + return Successful; +} + + + + +CommandLinePluginInterface::RunResult LdapPlugin::handle_help( const QStringList& arguments ) +{ + QString command = arguments.value( 0 ); + + if( command == QLatin1String("autoconfigurebasedn") ) + { + printf( "\n" + "ldap autoconfigurebasedn []\n" + "\n" + "Automatically configures the LDAP base DN configuration setting by querying\n" + "the naming context attribute from given LDAP server. The LDAP url parameter\n" + "needs to follow the schema:\n" + "\n" + " ldap[s]://[user[:password]@]hostname[:port]\n\n" ); + return NoResult; + } + else if( command == QLatin1String("query") ) + { + printf( "\n" + "ldap query [filter]\n" + "\n" + "Query objects from configured LDAP directory where may be one\n" + "of \"locations\", \"computers\", \"groups\" or \"users\". You can optionally\n" + "specify a filter such as \"foo*\".\n" + "\n" ); + return NoResult; + } + + return InvalidCommand; +} + + + +LdapClient& LdapPlugin::ldapClient() +{ + if( m_ldapClient == nullptr ) + { + m_ldapClient = new LdapClient( m_configuration ); + } + + // TODO: check whether still connected and reconnect if neccessary + + return *m_ldapClient; +} + + + +LdapDirectory& LdapPlugin::ldapDirectory() +{ + if( m_ldapDirectory == nullptr ) + { + m_ldapDirectory = new LdapDirectory( m_configuration ); + } + + // TODO: check whether still connected and reconnect if neccessary + + return *m_ldapDirectory; +} diff --git a/plugins/ldap/LdapPlugin.h b/plugins/ldap/LdapPlugin.h new file mode 100644 index 0000000..27661ef --- /dev/null +++ b/plugins/ldap/LdapPlugin.h @@ -0,0 +1,135 @@ +/* + * LdapPlugin.h - declaration of LdapPlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CommandLinePluginInterface.h" +#include "ConfigurationPagePluginInterface.h" +#include "LdapConfiguration.h" +#include "NetworkObjectDirectoryPluginInterface.h" +#include "UserGroupsBackendInterface.h" + +class LdapDirectory; + +class LdapPlugin : public QObject, PluginInterface, + CommandLinePluginInterface, + NetworkObjectDirectoryPluginInterface, + UserGroupsBackendInterface, + ConfigurationPagePluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.Ldap") + Q_INTERFACES(PluginInterface + CommandLinePluginInterface + NetworkObjectDirectoryPluginInterface + UserGroupsBackendInterface + ConfigurationPagePluginInterface) +public: + explicit LdapPlugin( QObject* parent = nullptr ); + ~LdapPlugin() override; + + Plugin::Uid uid() const override + { + return QStringLiteral("6f0a491e-c1c6-4338-8244-f823b0bf8670"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 2 ); + } + + QString name() const override + { + return QStringLiteral( "LDAP Basic" ); + } + + QString description() const override + { + return tr( "Basic LDAP/AD support for Veyon" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + void upgrade( const QVersionNumber& oldVersion ) override; + + QString commandLineModuleName() const override + { + return QStringLiteral( "ldap" ); + } + + QString commandLineModuleHelp() const override + { + return tr( "Commands for configuring and testing LDAP/AD integration" ); + } + + QStringList commands() const override; + QString commandHelp( const QString& command ) const override; + + QString directoryName() const override + { + return tr( "%1 (load computers and locations from LDAP/AD)" ).arg( name() ); + } + + NetworkObjectDirectory* createNetworkObjectDirectory( QObject* parent ) override; + + QString userGroupsBackendName() const override + { + return tr( "%1 (load users and groups from LDAP/AD)" ).arg( name() ); + } + + void reloadConfiguration() override; + + QStringList userGroups( bool queryDomainGroups ) override; + QStringList groupsOfUser( const QString& username, bool queryDomainGroups ) override; + + ConfigurationPage* createConfigurationPage() override; + + +public Q_SLOTS: + CommandLinePluginInterface::RunResult handle_autoconfigurebasedn( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_query( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_help( const QStringList& arguments ); + +private: + enum { + MaximumPlaintextPasswordLength = 64 + }; + + LdapClient& ldapClient(); + LdapDirectory& ldapDirectory(); + + LdapConfiguration m_configuration; + LdapClient* m_ldapClient; + LdapDirectory* m_ldapDirectory; + QMap m_commands; + +}; diff --git a/plugins/ldap/common/CMakeLists.txt b/plugins/ldap/common/CMakeLists.txt new file mode 100644 index 0000000..a9980fd --- /dev/null +++ b/plugins/ldap/common/CMakeLists.txt @@ -0,0 +1,29 @@ +SET(ldap_common_SOURCES + LdapBrowseDialog.cpp + LdapBrowseDialog.ui + LdapBrowseDialog.h + LdapBrowseModel.cpp + LdapBrowseModel.h + LdapClient.cpp + LdapClient.h + LdapConfiguration.cpp + LdapConfiguration.h + LdapConfigurationPage.h + LdapConfigurationPage.cpp + LdapConfigurationPage.ui + LdapDirectory.cpp + LdapDirectory.h + LdapNetworkObjectDirectory.cpp + LdapNetworkObjectDirectory.h + ldap.qrc + ) + +ADD_LIBRARY(ldap-common SHARED ${ldap_common_SOURCES}) +TARGET_INCLUDE_DIRECTORIES(ldap-common PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +TARGET_INCLUDE_DIRECTORIES(ldap-common PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +TARGET_LINK_LIBRARIES(ldap-common kldap-light veyon-core) +set_default_target_properties(ldap-common) +SET_TARGET_PROPERTIES(ldap-common PROPERTIES LINK_FLAGS "-Wl,-no-undefined") +if(NOT WITH_CORE_ONLY) +INSTALL(TARGETS ldap-common DESTINATION ${VEYON_LIB_DIR}) +endif() diff --git a/plugins/ldap/common/LdapBrowseDialog.cpp b/plugins/ldap/common/LdapBrowseDialog.cpp new file mode 100644 index 0000000..741d2ce --- /dev/null +++ b/plugins/ldap/common/LdapBrowseDialog.cpp @@ -0,0 +1,96 @@ +/* + * LdapBrowseDialog.cpp - dialog for browsing LDAP directories + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "LdapBrowseDialog.h" +#include "LdapBrowseModel.h" +#include "LdapConfiguration.h" + +#include "ui_LdapBrowseDialog.h" + + +LdapBrowseDialog::LdapBrowseDialog( const LdapConfiguration& configuration, QWidget* parent ) : + QDialog( parent ), + ui( new Ui::LdapBrowseDialog ), + m_configuration( configuration ) +{ + ui->setupUi( this ); +} + + + +LdapBrowseDialog::~LdapBrowseDialog() +{ + delete ui; +} + + + +QString LdapBrowseDialog::browseBaseDn( const QString& dn ) +{ + LdapBrowseModel model( LdapBrowseModel::BrowseBaseDN, m_configuration, this ); + + return browse( &model, dn, false ); +} + + + +QString LdapBrowseDialog::browseDn( const QString& dn ) +{ + LdapBrowseModel model( LdapBrowseModel::BrowseObjects, m_configuration, this ); + + return browse( &model, dn, dn.toLower() == model.baseDn().toLower() ); +} + + + +QString LdapBrowseDialog::browseAttribute( const QString& dn ) +{ + LdapBrowseModel model( LdapBrowseModel::BrowseAttributes, m_configuration, this ); + + return browse( &model, dn, true ); +} + + + +QString LdapBrowseDialog::browse( LdapBrowseModel* model, const QString& dn, bool expandSelected ) +{ + ui->treeView->setModel( model ); + + if( dn.isEmpty() == false ) + { + const auto dnIndex = model->dnToIndex( dn ); + ui->treeView->selectionModel()->setCurrentIndex( dnIndex, QItemSelectionModel::SelectCurrent ); + if( expandSelected ) + { + ui->treeView->expand( dnIndex ); + } + } + + if( exec() == QDialog::Accepted ) + { + return model->data( ui->treeView->selectionModel()->currentIndex(), LdapBrowseModel::ItemNameRole ).toString(); + } + + return {}; +} diff --git a/plugins/ldap/common/LdapBrowseDialog.h b/plugins/ldap/common/LdapBrowseDialog.h new file mode 100644 index 0000000..dec49f2 --- /dev/null +++ b/plugins/ldap/common/LdapBrowseDialog.h @@ -0,0 +1,55 @@ +/* + * LdapBrowseDialog.h - dialog for browsing LDAP directories + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "VeyonCore.h" + +namespace Ui { +class LdapBrowseDialog; +} + +class LdapConfiguration; +class LdapBrowseModel; + +class LdapBrowseDialog : public QDialog +{ + Q_OBJECT +public: + explicit LdapBrowseDialog( const LdapConfiguration& configuration, QWidget* parent = nullptr ); + ~LdapBrowseDialog() override; + + QString browseBaseDn( const QString& dn ); + QString browseDn( const QString& dn ); + QString browseAttribute( const QString& dn ); + +private: + QString browse( LdapBrowseModel* model, const QString& dn, bool expandSelected ); + + Ui::LdapBrowseDialog* ui; + const LdapConfiguration& m_configuration; + +}; diff --git a/plugins/ldap/common/LdapBrowseDialog.ui b/plugins/ldap/common/LdapBrowseDialog.ui new file mode 100644 index 0000000..ee722ce --- /dev/null +++ b/plugins/ldap/common/LdapBrowseDialog.ui @@ -0,0 +1,80 @@ + + + LdapBrowseDialog + + + + 0 + 0 + 600 + 800 + + + + Browse LDAP + + + + + + + 32 + 32 + + + + true + + + true + + + false + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + LdapBrowseDialog + accept() + + + 414 + 697 + + + 414 + 363 + + + + + buttonBox + rejected() + LdapBrowseDialog + reject() + + + 414 + 697 + + + 414 + 363 + + + + + diff --git a/plugins/ldap/common/LdapBrowseModel.cpp b/plugins/ldap/common/LdapBrowseModel.cpp new file mode 100644 index 0000000..1b5be5f --- /dev/null +++ b/plugins/ldap/common/LdapBrowseModel.cpp @@ -0,0 +1,421 @@ +/* + * LdapModel.cpp - item model for browsing LDAP directories + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "LdapClient.h" +#include "LdapConfiguration.h" +#include "LdapBrowseModel.h" + +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) +#include +#endif + +// clazy:excludeall=rule-of-three + +class LdapBrowseModelNode { +public: + enum Type { + Root, + DN, + Attribute + }; + + explicit LdapBrowseModelNode( Type type, const QString& name, LdapBrowseModelNode *parent ) : + m_parent( parent ), + m_name( name ), + m_type( type ), + m_populated( false ) + { + if( type == Attribute ) + { + m_populated = true; + } + } + + ~LdapBrowseModelNode() + { + qDeleteAll(m_childItems); + } + + Q_DISABLE_COPY(LdapBrowseModelNode) + Q_DISABLE_MOVE(LdapBrowseModelNode) + + Type type() const + { + return m_type; + } + + LdapBrowseModelNode* parent() const + { + return m_parent; + } + + int row() const + { + if( m_parent ) + { + return m_parent->children().indexOf( const_cast(this) ); + } + + return 0; + } + + void setPopulated( bool populated ) + { + m_populated = populated; + } + + bool isPopulated() const + { + return m_populated; + } + + void appendChild( LdapBrowseModelNode* item ) + { + m_childItems.append( item ); + } + + const QList& children() const + { + return m_childItems; + } + + const QString& name() const + { + return m_name; + } + +private: + LdapBrowseModelNode* m_parent; + QList m_childItems; + QString m_name; + Type m_type; + bool m_populated; + +}; + + + +LdapBrowseModel::LdapBrowseModel( Mode mode, const LdapConfiguration& configuration, QObject* parent ) : + QAbstractItemModel( parent ), + m_mode( mode ), + m_client( new LdapClient( configuration, QUrl(), this ) ), + m_root( new Node( Node::Root, {}, nullptr ) ), + m_objectIcon( QStringLiteral(":/core/document-open.png") ), + m_ouIcon( QStringLiteral( ":/ldap/folder-stash.png") ), + m_attributeIcon( QStringLiteral(":/ldap/attribute.png") ) +{ + populateRoot(); + +#if defined(QT_TESTLIB_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + new QAbstractItemModelTester( this, QAbstractItemModelTester::FailureReportingMode::Warning, this ); +#endif +} + + + +LdapBrowseModel::~LdapBrowseModel() +{ + delete m_root; + delete m_client; +} + + + +QModelIndex LdapBrowseModel::index( int row, int col, const QModelIndex& parent ) const +{ + auto childNode = toNode( parent )->children().value( row ); + if( childNode ) + { + return createIndex( row, col, childNode ); + } + + return {}; +} + + + +QModelIndex LdapBrowseModel::parent( const QModelIndex& child ) const +{ + if( child.isValid() == false ) + { + return {}; + } + + auto childItem = static_cast( child.internalPointer() ); + auto parentItem = childItem->parent(); + + if( parentItem == m_root ) + { + return {}; + } + + return createIndex( parentItem->row(), 0, parentItem ); +} + + + +QVariant LdapBrowseModel::data( const QModelIndex& index, int role ) const +{ + if( index.isValid() == false ) + { + return {}; + } + + const auto node = toNode( index ); + + switch( role ) + { + case Qt::DisplayRole: + return LdapClient::toRDNs( node->name() ).value( 0 ); + + case Qt::DecorationRole: + switch( node->type() ) + { + case Node::Root: break; + case Node::DN: return node->name().startsWith( QLatin1String("ou="), Qt::CaseInsensitive ) ? m_ouIcon : m_objectIcon; + case Node::Attribute: return m_attributeIcon; + } + break; + + case ItemNameRole: + return node->name(); + + default: + break; + } + + return {}; +} + + + +Qt::ItemFlags LdapBrowseModel::flags( const QModelIndex& index ) const +{ + if( index.isValid() == false ) + { + return QAbstractItemModel::flags(index); + } + + return QAbstractItemModel::flags(index) | Qt::ItemIsSelectable; +} + + + +int LdapBrowseModel::columnCount( const QModelIndex& parent ) const +{ + Q_UNUSED(parent); + return 1; +} + + + +int LdapBrowseModel::rowCount( const QModelIndex& parent ) const +{ + if( parent.column() > 0 ) + { + return 0; + } + + return toNode( parent )->children().count(); +} + + + +bool LdapBrowseModel::hasChildren( const QModelIndex& parent ) const +{ + auto parentNode = toNode( parent ); + + if( parent.isValid() && parentNode && parentNode->isPopulated() ) + { + return parentNode->children().isEmpty() == false; + } + + return true; +} + + + +bool LdapBrowseModel::canFetchMore( const QModelIndex& parent ) const +{ + return toNode( parent )->isPopulated() == false; +} + + + +void LdapBrowseModel::fetchMore( const QModelIndex& parent ) +{ + populateNode( parent ); +} + + + +QModelIndex LdapBrowseModel::dnToIndex( const QString& dn ) +{ + auto rdns = LdapClient::toRDNs( dn ); + + auto node = m_root; + QModelIndex index; + QStringList fullDn; + + while( rdns.isEmpty() == false ) + { + populateNode( index ); + + fullDn.prepend( rdns.takeLast() ); + const auto currentDn = fullDn.join( QLatin1Char(',') ).toLower(); + + int row = 0; + const int count = node->children().count(); + + for( auto child : node->children() ) + { + if( child->name().toLower() == currentDn ) + { + node = child; + index = createIndex( row, 0, node ); + break; + } + ++row; + } + + if( row >= count ) + { + return index; + } + } + + return index; +} + + + +QString LdapBrowseModel::baseDn() const +{ + return m_client->baseDn(); +} + + + +void LdapBrowseModel::populateRoot() const +{ + QStringList namingContexts; + const auto baseDn = m_client->configuration().baseDn(); + + if( baseDn.isEmpty() || m_mode == BrowseBaseDN ) + { + namingContexts.append( m_client->queryNamingContexts() ); + namingContexts.append( m_client->queryNamingContexts( QStringLiteral("namingContexts") ) ); + namingContexts.append( m_client->queryNamingContexts( QStringLiteral("defaultNamingContext") ) ); + namingContexts.removeDuplicates(); + + // remove sub naming contexts + for( const auto& context : namingContexts ) + { + if( context.isEmpty() == false ) + { + namingContexts.replaceInStrings( QRegExp( QStringLiteral(".*,%1").arg( context ) ), {} ); + } + } + namingContexts.removeAll( {} ); + namingContexts.sort(); + } + else + { + namingContexts.append( baseDn ); + } + + for( const auto& namingContext : namingContexts ) + { + auto parent = m_root; + QStringList fullDn; + const auto dns = namingContext.split( QLatin1Char(',') ); +#if QT_VERSION < 0x050600 +#warning Building compat code for unsupported version of Qt + using QStringListReverseIterator = std::reverse_iterator; + for( auto it = QStringListReverseIterator(dns.cend()), + end = QStringListReverseIterator(dns.cbegin()); it != end; ++it ) +#else + for( auto it = dns.crbegin(), end = dns.crend(); it != end; ++it ) +#endif + { + fullDn.prepend( *it ); + auto node = new Node( Node::DN, fullDn.join( QLatin1Char(',') ), parent ); + parent->appendChild( node ); + parent = node; + } + } + + m_root->setPopulated( true ); +} + + + +void LdapBrowseModel::populateNode( const QModelIndex& parent ) +{ + auto node = toNode( parent ); + + if( node->isPopulated() == false ) + { + auto dns = m_client->queryDistinguishedNames( node->name(), {}, LdapClient::Scope::One ); + dns.sort(); + + QStringList attributes; + + if( m_mode == BrowseAttributes ) + { + attributes = m_client->queryObjectAttributes( node->name() ); + attributes.sort(); + } + + const auto itemCount = ( dns + attributes ).size(); + + if( itemCount > 0 ) + { + beginInsertRows( parent, 0, itemCount - 1 ); + + for( const auto& dn : dns ) + { + node->appendChild( new Node( Node::DN, dn, node ) ); + } + + for( const auto& attribute : qAsConst(attributes) ) + { + node->appendChild( new Node( Node::Attribute, attribute, node ) ); + } + + endInsertRows(); + + Q_EMIT layoutChanged(); + } + + node->setPopulated( true ); + } +} + + + +LdapBrowseModel::Node*LdapBrowseModel::toNode( const QModelIndex& index ) const +{ + return index.isValid() ? static_cast( index.internalPointer() ) : m_root; +} diff --git a/plugins/ldap/common/LdapBrowseModel.h b/plugins/ldap/common/LdapBrowseModel.h new file mode 100644 index 0000000..01125c0 --- /dev/null +++ b/plugins/ldap/common/LdapBrowseModel.h @@ -0,0 +1,79 @@ +/* + * LdapModel.h - item model for browsing LDAP directories + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +class LdapClient; +class LdapConfiguration; +class LdapBrowseModelNode; + +class LdapBrowseModel : public QAbstractItemModel +{ + Q_OBJECT +public: + using Node = LdapBrowseModelNode; + + enum Mode { + BrowseBaseDN, + BrowseObjects, + BrowseAttributes + }; + + static constexpr int ItemNameRole = Qt::UserRole+1; + + LdapBrowseModel( Mode mode, const LdapConfiguration& configuration, QObject* parent = nullptr ); + ~LdapBrowseModel() override; + + QModelIndex index(int row, int col, const QModelIndex &parent) const override; + QModelIndex parent( const QModelIndex& child ) const override; + QVariant data( const QModelIndex& index, int role ) const override; + Qt::ItemFlags flags( const QModelIndex& index ) const override; + int columnCount( const QModelIndex& parent ) const override; + int rowCount( const QModelIndex& parent ) const override; + bool hasChildren( const QModelIndex& parent ) const override; + bool canFetchMore( const QModelIndex& parent ) const override; + void fetchMore( const QModelIndex& parent ) override; + + QModelIndex dnToIndex( const QString& dn ); + + QString baseDn() const; + +private: + void populateRoot() const; + void populateNode( const QModelIndex& parent ); + + Node* toNode( const QModelIndex& index ) const; + + const Mode m_mode; + LdapClient* m_client; + Node* m_root; + + QIcon m_objectIcon; + QIcon m_ouIcon; + QIcon m_attributeIcon; + +}; diff --git a/plugins/ldap/common/LdapClient.cpp b/plugins/ldap/common/LdapClient.cpp new file mode 100644 index 0000000..04f6880 --- /dev/null +++ b/plugins/ldap/common/LdapClient.cpp @@ -0,0 +1,657 @@ +/* + * LdapClient.cpp - class representing the LDAP directory and providing access to directory entries + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "LdapConfiguration.h" +#include "LdapClient.h" + +#include + +#include "ldapconnection.h" +#include "ldapoperation.h" +#include "ldapserver.h" + + +static inline KLDAP::LdapUrl::Scope kldapUrlScope( LdapClient::Scope scope ) +{ + switch( scope ) + { + case LdapClient::Scope::Base: return KLDAP::LdapUrl::Base; + case LdapClient::Scope::One: return KLDAP::LdapUrl::One; + case LdapClient::Scope::Sub: return KLDAP::LdapUrl::Sub; + } + + return KLDAP::LdapUrl::Base; +} + + + +LdapClient::LdapClient( const LdapConfiguration& configuration, const QUrl& url, QObject* parent ) : + QObject( parent ), + m_configuration( configuration ), + m_server( new KLDAP::LdapServer ), + m_connection( new KLDAP::LdapConnection ), + m_operation( new KLDAP::LdapOperation ) +{ + connectAndBind( url ); +} + + + +LdapClient::~LdapClient() +{ + delete m_connection; + delete m_operation; + delete m_server; +} + + + +QString LdapClient::errorString() const +{ + if( m_connection->handle() == nullptr ) + { + return m_connection->connectionError(); + } + + return m_connection->ldapErrorString(); +} + + + +QString LdapClient::errorDescription() const +{ + const auto string = errorString(); + if( string.isEmpty() == false ) + { + return tr( "LDAP error description: %1" ).arg( string ); + } + + return {}; +} + + + +LdapClient::Objects LdapClient::queryObjects( const QString& dn, const QStringList& attributes, + const QString& filter, LdapClient::Scope scope ) +{ + vDebug() << "called with" << dn << attributes << filter << scope; + + if( m_state != Bound && reconnect() == false ) + { + vCritical() << "not bound to server!"; + return {}; + } + + if( dn.isEmpty() ) + { + vCritical() << "DN is empty!"; + return {}; + } + + if( attributes.isEmpty() ) + { + vCritical() << "attributes empty!"; + return {}; + } + + Objects entries; + + int result = -1; + auto id = m_operation->search( KLDAP::LdapDN( dn ), kldapUrlScope( scope ), filter, QStringList( attributes ) ); + + if( id != -1 ) + { + auto realAttributeNames = attributes; + for( auto& attribute : realAttributeNames ) + { + attribute = attribute.toLower(); + } + + auto isFirstResult = true; + + while( ( result = m_operation->waitForResult( id, LdapQueryTimeout ) ) == KLDAP::LdapOperation::RES_SEARCH_ENTRY ) + { + if( isFirstResult ) + { + isFirstResult = false; + + // match attribute name from result with requested attribute name in order + // to keep result aggregation below case-insensitive + const auto attributes = m_operation->object().attributes(); + for( auto it = attributes.constBegin(), end = attributes.constEnd(); it != end; ++it ) + { + for( auto& attribute : realAttributeNames ) + { + if( QString::compare( it.key().toLower(), attribute, Qt::CaseInsensitive ) == 0 ) + { + attribute = it.key(); + break; + } + } + } + } + + // convert result list from type QList to QStringList + const auto dn = m_operation->object().dn().toString(); + for( const auto& attribute : realAttributeNames ) + { + const auto values = m_operation->object().values( attribute ); + for( const auto& value : values ) + { + entries[dn][attribute] += QString::fromUtf8( value ); + } + } + } + + vDebug() << "results:" << entries; + } + + if( result == -1 ) + { + vWarning() << "LDAP search failed with code" << m_connection->ldapErrorCode(); + + if( m_state == Bound && m_queryRetry == false ) + { + // close connection and try again + m_queryRetry = true; + m_state = Disconnected; + entries = queryObjects( dn, attributes, filter, scope ); + m_queryRetry = false; + } + } + + return entries; + +} + + + +QStringList LdapClient::queryAttributeValues( const QString& dn, const QString& attribute, + const QString& filter, Scope scope ) +{ + vDebug() << "called with" << dn << attribute << filter << scope; + + if( m_state != Bound && reconnect() == false ) + { + vCritical() << "not bound to server!"; + return {}; + } + + if( dn.isEmpty() && attribute != m_namingContextAttribute && + attribute.contains( QLatin1String("namingcontext"), Qt::CaseInsensitive ) == false ) + { + vCritical() << "DN is empty!"; + return {}; + } + + if( attribute.isEmpty() ) + { + vCritical() << "attribute is empty!"; + return {}; + } + + QStringList entries; + + int result = -1; + int id = m_operation->search( KLDAP::LdapDN( dn ), kldapUrlScope( scope ), filter, QStringList( attribute ) ); + + if( id != -1 ) + { + bool isFirstResult = true; + QString realAttributeName = attribute.toLower(); + + while( ( result = m_operation->waitForResult( id, LdapQueryTimeout ) ) == KLDAP::LdapOperation::RES_SEARCH_ENTRY ) + { + if( isFirstResult ) + { + isFirstResult = false; + + // match attribute name from result with requested attribute name in order + // to keep result aggregation below case-insensitive + const auto attributes = m_operation->object().attributes(); + for( auto it = attributes.constBegin(), end = attributes.constEnd(); it != end; ++it ) + { + if( it.key().toLower() == realAttributeName ) + { + realAttributeName = it.key(); + break; + } + } + } + + // convert result list from type QList to QStringList + const auto values = m_operation->object().values( realAttributeName ); + for( const auto& value : values ) + { + entries += QString::fromUtf8( value ); + } + } + + vDebug() << "results:" << entries; + } + + if( result == -1 ) + { + vWarning() << "LDAP search failed with code" << m_connection->ldapErrorCode(); + + if( m_state == Bound && m_queryRetry == false ) + { + // close connection and try again + m_queryRetry = true; + m_state = Disconnected; + entries = queryAttributeValues( dn, attribute, filter, scope ); + m_queryRetry = false; + } + } + + return entries; +} + + + +QStringList LdapClient::queryDistinguishedNames( const QString& dn, const QString& filter, Scope scope ) +{ + vDebug() << dn << filter << scope; + + if( m_state != Bound && reconnect() == false ) + { + vCritical() << "not bound to server!"; + return {}; + } + + if( dn.isEmpty() ) + { + vCritical() << "DN is empty!"; + return {}; + } + + QStringList distinguishedNames; + + int result = -1; + int id = m_operation->search( KLDAP::LdapDN( dn ), kldapUrlScope( scope ), filter, QStringList() ); + + if( id != -1 ) + { + while( ( result = m_operation->waitForResult( id, LdapQueryTimeout ) ) == KLDAP::LdapOperation::RES_SEARCH_ENTRY ) + { + distinguishedNames += m_operation->object().dn().toString(); + } + vDebug() << "results" << distinguishedNames; + } + + if( result == -1 ) + { + vWarning() << "LDAP search failed with code" << m_connection->ldapErrorCode(); + + if( m_state == Bound && m_queryRetry == false ) + { + // close connection and try again + m_queryRetry = true; + m_state = Disconnected; + distinguishedNames = queryDistinguishedNames( dn, filter, scope ); + m_queryRetry = false; + } + } + + return distinguishedNames; +} + + + +QStringList LdapClient::queryObjectAttributes( const QString& dn ) +{ + vDebug() << "called with" << dn; + + if( m_state != Bound && reconnect() == false ) + { + vCritical() << "not bound to server!"; + return {}; + } + + if( dn.isEmpty() ) + { + vCritical() << "DN is empty!"; + return {}; + } + + int id = 0; + if( ldap_search_ext( static_cast( m_connection->handle() ), + dn.toUtf8().data(), LDAP_SCOPE_BASE, "objectClass=*", + nullptr, 1, nullptr, nullptr, nullptr, + m_connection->sizeLimit(), &id ) != 0 ) + { + return {}; + } + + if( m_operation->waitForResult( id, LdapQueryTimeout ) == KLDAP::LdapOperation::RES_SEARCH_ENTRY ) + { + const auto keys = m_operation->object().attributes().keys(); + vDebug() << "results" << keys; + return keys; + } + + return {}; +} + + + +QStringList LdapClient::queryBaseDn() +{ + return queryDistinguishedNames( baseDn(), QStringLiteral( "(objectclass=*)" ), Scope::Base ); +} + + + +QStringList LdapClient::queryNamingContexts( const QString& attribute ) +{ + return queryAttributeValues( {}, attribute.isEmpty() ? m_namingContextAttribute : attribute ); +} + + + +QString LdapClient::baseDn() +{ + if( m_baseDn.isEmpty() ) + { + // query base DN via naming context if configured + if( m_configuration.queryNamingContext() ) + { + m_baseDn = queryNamingContexts().value( 0 ); + } + else + { + // use the configured base DN + m_baseDn = m_configuration.baseDn(); + } + } + + return m_baseDn; +} + + + +QString LdapClient::parentDn( const QString& dn ) +{ + auto rdns = toRDNs( dn ); + if( rdns.size() > 1 ) + { + return rdns.mid( 1 ).join( QLatin1Char( ',') ); + } + + return {}; +} + + + +QString LdapClient::stripBaseDn( const QString& dn, const QString& baseDn ) +{ + const auto fullDnLower = dn.toLower(); + const auto baseDnLower = baseDn.toLower(); + + if( fullDnLower.endsWith( QLatin1Char( ',' ) + baseDnLower ) && dn.length() > baseDn.length()+1 ) + { + // cut off comma and base DN + return dn.left( dn.length() - baseDn.length() - 1 ); + } + else if( fullDnLower == baseDnLower ) + { + return {}; + } + + return dn; +} + + + +QString LdapClient::addBaseDn( const QString& relativeDn, const QString& baseDn ) +{ + if( relativeDn.isEmpty() ) + { + return baseDn; + } + + return relativeDn + QLatin1Char( ',' ) + baseDn; +} + + + +QStringList LdapClient::stripBaseDn( const QStringList& dns, const QString& baseDn ) +{ + QStringList strippedDns; + + strippedDns.reserve( dns.size() ); + + for( const auto& dn : dns ) + { + strippedDns += stripBaseDn( dn, baseDn ); + } + + return strippedDns; +} + + + +bool LdapClient::connectAndBind( const QUrl& url ) +{ + if( url.isValid() ) + { + m_server->setUrl( KLDAP::LdapUrl( url ) ); + } + else + { + m_server->setHost( m_configuration.serverHost() ); + m_server->setPort( m_configuration.serverPort() ); + + if( m_configuration.useBindCredentials() ) + { + m_server->setBindDn( m_configuration.bindDn() ); + m_server->setPassword( QString::fromUtf8( m_configuration.bindPassword().plainText().toByteArray() ) ); + m_server->setAuth( KLDAP::LdapServer::Simple ); + } + else + { + m_server->setAuth( KLDAP::LdapServer::Anonymous ); + } + + const auto security = static_cast( m_configuration.connectionSecurity() ); + switch( security ) + { + case ConnectionSecurityTLS: + m_server->setSecurity( KLDAP::LdapServer::TLS ); + break; + case ConnectionSecuritySSL: + m_server->setSecurity( KLDAP::LdapServer::SSL ); + break; + default: + m_server->setSecurity( KLDAP::LdapServer::None ); + break; + } + } + + if( m_configuration.connectionSecurity() != ConnectionSecurityNone ) + { + initTLS(); + } + + if( reconnect() == false ) + { + return false; + } + + m_namingContextAttribute = m_configuration.namingContextAttribute(); + + if( m_namingContextAttribute.isEmpty() ) + { + // fallback to AD default value + m_namingContextAttribute = QStringLiteral( "defaultNamingContext" ); + } + + return true; +} + + + +void LdapClient::initTLS() +{ + switch( m_configuration.tlsVerifyMode() ) + { + case TLSVerifyDefault: + m_server->setTLSRequireCertificate( KLDAP::LdapServer::TLSReqCertDefault ); + break; + case TLSVerifyNever: + m_server->setTLSRequireCertificate( KLDAP::LdapServer::TLSReqCertNever ); + break; + case TLSVerifyCustomCert: + m_server->setTLSRequireCertificate( KLDAP::LdapServer::TLSReqCertHard ); + m_server->setTLSCACertFile( m_configuration.tlsCACertificateFile() ); + break; + default: + vCritical() << "invalid TLS verify mode specified!"; + m_server->setTLSRequireCertificate( KLDAP::LdapServer::TLSReqCertDefault ); + break; + } +} + + + +bool LdapClient::reconnect() +{ + m_connection->close(); + m_state = Disconnected; + + m_connection->setServer( *m_server ); + + if( qEnvironmentVariableIsSet( "VEYON_DEBUG_LDAP_LIBRARY") ) + { + const auto debugLevel = LdapLibraryDebugAny; + ldap_set_option( nullptr, LDAP_OPT_DEBUG_LEVEL, &debugLevel ); + ber_set_option( nullptr, LDAP_OPT_DEBUG_LEVEL, &debugLevel ); + } + + if( m_connection->connect() != 0 ) + { + vWarning() << "LDAP connect failed:" << errorString(); + return false; + } + + m_state = Connected; + + m_operation->setConnection( *m_connection ); + if( m_operation->bind_s() != 0 ) + { + vWarning() << "LDAP bind failed:" << errorString(); + return false; + } + + m_state = Bound; + + return true; +} + + + +QString LdapClient::constructSubDn( const QString& subtree, const QString& baseDn ) +{ + if( baseDn.isEmpty() ) + { + return {}; + } + + if( subtree.isEmpty() ) + { + return baseDn; + } + + return subtree + QLatin1Char(',') + baseDn; +} + + + +QString LdapClient::constructQueryFilter( const QString& filterAttribute, + const QString& filterValue, + const QString& extraFilter ) +{ + QString queryFilter; + + if( filterAttribute.isEmpty() == false ) + { + if( filterValue.isEmpty() ) + { + queryFilter = QStringLiteral( "(%1=*)" ).arg( filterAttribute ); + } + else + { + queryFilter = QStringLiteral( "(%1=%2)" ).arg( filterAttribute, + escapeFilterValue( filterValue ) ); + } + } + + if( extraFilter.isEmpty() == false ) + { + if( queryFilter.isEmpty() ) + { + queryFilter = extraFilter; + } + else + { + queryFilter = QStringLiteral( "(&%1%2)" ).arg( extraFilter, queryFilter ); + } + } + + return queryFilter; +} + + + +QString LdapClient::escapeFilterValue( const QString& filterValue ) +{ + return QString( filterValue ) + .replace( QStringLiteral("\\"), QStringLiteral("\\\\") ) + .replace( QStringLiteral("("), QStringLiteral("\\(") ) + .replace( QStringLiteral(")"), QStringLiteral("\\)") ); +} + + + +QStringList LdapClient::toRDNs( const QString& dn ) +{ + QStringList strParts; + int index = 0; + int searchFrom = 0; + int strPartStartIndex = 0; + while( ( index = dn.indexOf( QLatin1Char(','), searchFrom ) ) != -1) + { + const auto prev = dn[std::max(0, index - 1)]; + if (prev != QLatin1Char('\\')) { + strParts.append( dn.mid(strPartStartIndex, index - strPartStartIndex) ); + strPartStartIndex = index + 1; + } + + searchFrom = index + 1; + } + + strParts.append( dn.mid(strPartStartIndex) ); + + return strParts; +} diff --git a/plugins/ldap/common/LdapClient.h b/plugins/ldap/common/LdapClient.h new file mode 100644 index 0000000..f80a2a6 --- /dev/null +++ b/plugins/ldap/common/LdapClient.h @@ -0,0 +1,153 @@ +/* + * LdapClient.h - class implementing an LDAP client + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "LdapCommon.h" + +namespace KLDAP { +class LdapConnection; +class LdapOperation; +class LdapServer; +} + +class LdapConfiguration; + +class LDAP_COMMON_EXPORT LdapClient : public QObject +{ + Q_OBJECT +public: + enum class Scope { + Base, + One, + Sub + }; + Q_ENUM(Scope) + + enum ConnectionSecurity + { + ConnectionSecurityNone, + ConnectionSecurityTLS, + ConnectionSecuritySSL, + ConnectionSecurityCount, + }; + Q_ENUM(ConnectionSecurity) + + enum TLSVerifyMode + { + TLSVerifyDefault, + TLSVerifyNever, + TLSVerifyCustomCert, + TLSVerifyModeCount + }; + Q_ENUM(TLSVerifyMode) + + using Objects = QMap >; + + explicit LdapClient( const LdapConfiguration& configuration, const QUrl& url = QUrl(), QObject* parent = nullptr ); + ~LdapClient() override; + + const LdapConfiguration& configuration() const + { + return m_configuration; + } + + bool isConnected() const + { + return m_state >= Connected; + } + + bool isBound() const + { + return m_state >= Bound; + } + + QString errorString() const; + QString errorDescription() const; + + Objects queryObjects( const QString& dn, const QStringList& attributes, const QString& filter, Scope scope ); + + QStringList queryAttributeValues( const QString &dn, const QString &attribute, + const QString& filter = QStringLiteral( "(objectclass=*)" ), + Scope scope = Scope::Base ); + + QStringList queryDistinguishedNames( const QString& dn, const QString& filter, Scope scope ); + + QStringList queryObjectAttributes( const QString& dn ); + + QStringList queryBaseDn(); + + QStringList queryNamingContexts( const QString& attribute = {} ); + + QString baseDn(); + + static QString parentDn( const QString& dn ); + static QString stripBaseDn( const QString& dn, const QString& baseDn ); + static QString addBaseDn( const QString& rdns, const QString& baseDn ); + + static QStringList stripBaseDn( const QStringList& dns, const QString& baseDn ); + + static QString constructSubDn( const QString& subtree, const QString& baseDn ); + + static QString constructQueryFilter( const QString& filterAttribute, + const QString& filterValue, + const QString& extraFilter = {} ); + + static QString escapeFilterValue( const QString& filterValue ); + + static QStringList toRDNs( const QString& dn ); + +private: + static constexpr int LdapQueryTimeout = 3000; + static constexpr int LdapConnectionTimeout = 60*1000; + static constexpr auto LdapLibraryDebugAny = -1; + + bool reconnect(); + bool connectAndBind( const QUrl& url ); + void initTLS(); + + const LdapConfiguration& m_configuration; + KLDAP::LdapServer* m_server; + KLDAP::LdapConnection* m_connection; + KLDAP::LdapOperation* m_operation; + + enum State + { + Disconnected, + Connected, + Bound, + StateCount + } ; + + State m_state = Disconnected; + + bool m_queryRetry = false; + + QString m_baseDn; + QString m_namingContextAttribute; + +}; diff --git a/plugins/ldap/common/LdapCommon.h b/plugins/ldap/common/LdapCommon.h new file mode 100644 index 0000000..1087380 --- /dev/null +++ b/plugins/ldap/common/LdapCommon.h @@ -0,0 +1,32 @@ +/* + * LdapCommon.h - common definitions for the LDAP library + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#if defined(ldap_common_EXPORTS) +#define LDAP_COMMON_EXPORT Q_DECL_EXPORT +#else +#define LDAP_COMMON_EXPORT Q_DECL_IMPORT +#endif + diff --git a/plugins/ldap/common/LdapConfiguration.cpp b/plugins/ldap/common/LdapConfiguration.cpp new file mode 100644 index 0000000..cebe68e --- /dev/null +++ b/plugins/ldap/common/LdapConfiguration.cpp @@ -0,0 +1,28 @@ +/* + * LdapConfiguration.cpp - LDAP-specific configuration values + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "VeyonConfiguration.h" +#include "LdapConfiguration.h" + +IMPLEMENT_CONFIG_PROXY(LdapConfiguration) diff --git a/plugins/ldap/common/LdapConfiguration.h b/plugins/ldap/common/LdapConfiguration.h new file mode 100644 index 0000000..439ac36 --- /dev/null +++ b/plugins/ldap/common/LdapConfiguration.h @@ -0,0 +1,77 @@ +/* + * LdapConfiguration.h - LDAP-specific configuration values + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "Configuration/Proxy.h" +#include "CryptoCore.h" +#include "LdapClient.h" +#include "LdapCommon.h" + +#define FOREACH_LDAP_CONFIG_PROPERTY(OP) \ + OP( LdapConfiguration, m_configuration, QString, serverHost, setServerHost, "ServerHost", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, int, serverPort, setServerPort, "ServerPort", "LDAP", 389, Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, int, connectionSecurity, setConnectionSecurity, "ConnectionSecurity", "LDAP", LdapClient::ConnectionSecurityNone, Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, int, tlsVerifyMode, setTLSVerifyMode, "TLSVerifyMode", "LDAP", LdapClient::TLSVerifyDefault, Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, tlsCACertificateFile, setTLSCACertificateFile, "TLSCACertificateFile", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, bool, useBindCredentials, setUseBindCredentials, "UseBindCredentials", "LDAP", false, Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, bindDn, setBindDn, "BindDN", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, Configuration::Password, bindPassword, setBindPassword, "BindPassword", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, bool, queryNamingContext, setQueryNamingContext, "QueryNamingContext", "LDAP", false, Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, baseDn, setBaseDn, "BaseDN", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, namingContextAttribute, setNamingContextAttribute, "NamingContextAttribute", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, userTree, setUserTree, "UserTree", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, groupTree, setGroupTree, "GroupTree", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, computerTree, setComputerTree, "ComputerTree", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, computerGroupTree, setComputerGroupTree, "ComputerGroupTree", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, bool, recursiveSearchOperations, setRecursiveSearchOperations, "RecursiveSearchOperations", "LDAP", false, Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, userLoginNameAttribute, setUserLoginNameAttribute, "UserLoginNameAttribute", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, groupMemberAttribute, setGroupMemberAttribute, "GroupMemberAttribute", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, computerDisplayNameAttribute, setComputerDisplayNameAttribute, "ComputerDisplayNameAttribute", "LDAP", QStringLiteral("cn"), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, computerHostNameAttribute, setComputerHostNameAttribute, "ComputerHostNameAttribute", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, bool, computerHostNameAsFQDN, setComputerHostNameAsFQDN, "ComputerHostNameAsFQDN", "LDAP", false, Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, computerMacAddressAttribute, setComputerMacAddressAttribute, "ComputerMacAddressAttribute", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, locationNameAttribute, setLocationNameAttribute, "LocationNameAttribute", "LDAP", QStringLiteral("cn"), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, usersFilter, setUsersFilter, "UsersFilter", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, userGroupsFilter, setUserGroupsFilter, "UserGroupsFilter", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, computersFilter, setComputersFilter, "ComputersFilter", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, bool, identifyGroupMembersByNameAttribute, setIdentifyGroupMembersByNameAttribute, "IdentifyGroupMembersByNameAttribute", "LDAP", false, Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, computerGroupsFilter, setComputerGroupsFilter, "ComputerGroupsFilter", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, computerContainersFilter, setComputerContainersFilter, "ComputerContainersFilter", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, bool, computerLocationsByContainer, setComputerLocationsByContainer, "ComputerLocationsByContainer", "LDAP", false, Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, bool, computerLocationsByAttribute, setComputerLocationsByAttribute, "ComputerLocationsByAttribute", "LDAP", false, Configuration::Property::Flag::Standard ) \ + OP( LdapConfiguration, m_configuration, QString, computerLocationAttribute, setComputerLocationAttribute, "ComputerLocationAttribute", "LDAP", QString(), Configuration::Property::Flag::Standard ) \ + +#define FOREACH_LDAP_LEGACY_CONFIG_PROPERTY(OP) \ + OP( LdapConfiguration, m_configuration, QString, legacyUserLoginAttribute, setLegacyUserLoginAttribute, "UserLoginAttribute", "LDAP", QString(), Configuration::Property::Flag::Legacy ) \ + OP( LdapConfiguration, m_configuration, bool, legacyComputerRoomMembersByContainer, setCompatComputerRoomMembersByContainer, "ComputerRoomMembersByContainer", "LDAP", false, Configuration::Property::Flag::Legacy ) \ + OP( LdapConfiguration, m_configuration, bool, legacyComputerRoomMembersByAttribute, setCompatComputerRoomMembersByAttribute, "ComputerRoomMembersByAttribute", "LDAP", false, Configuration::Property::Flag::Legacy ) \ + OP( LdapConfiguration, m_configuration, QString, legacyComputerRoomAttribute, setCompatComputerRoomAttribute, "ComputerRoomAttribute", "LDAP", QString(), Configuration::Property::Flag::Legacy ) \ + OP( LdapConfiguration, m_configuration, QString, legacyComputerRoomNameAttribute, setCompatComputerRoomNameAttribute, "ComputerRoomNameAttribute", "LDAP", QString(), Configuration::Property::Flag::Legacy ) \ + +#define FOREACH_LDAP_PROXIES_CONFIG_PROPERTY(OP) \ + FOREACH_LDAP_CONFIG_PROPERTY(OP) \ + FOREACH_LDAP_LEGACY_CONFIG_PROPERTY(OP) + +DECLARE_CONFIG_PROXY(LDAP_COMMON_EXPORT LdapConfiguration, FOREACH_LDAP_PROXIES_CONFIG_PROPERTY) diff --git a/plugins/ldap/common/LdapConfigurationPage.cpp b/plugins/ldap/common/LdapConfigurationPage.cpp new file mode 100644 index 0000000..e95a515 --- /dev/null +++ b/plugins/ldap/common/LdapConfigurationPage.cpp @@ -0,0 +1,783 @@ +/* + * LdapConfigurationPage.cpp - implementation of the LdapConfigurationPage class + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include "LdapConfiguration.h" +#include "LdapConfigurationPage.h" +#include "Configuration/UiMapping.h" +#include "LdapDirectory.h" +#include "LdapBrowseDialog.h" + +#include "ui_LdapConfigurationPage.h" + +LdapConfigurationPage::LdapConfigurationPage( LdapConfiguration& configuration, QWidget* parent ) : + ConfigurationPage( parent ), + ui(new Ui::LdapConfigurationPage), + m_configuration( configuration ) +{ + ui->setupUi(this); + +#define CONNECT_BUTTON_SLOT(name) connect( ui->name, &QPushButton::clicked, this, &LdapConfigurationPage::name ); + + connect( ui->browseBaseDn, &QPushButton::clicked, this, &LdapConfigurationPage::browseBaseDn ); + connect( ui->browseUserTree, &QPushButton::clicked, this, [this]() { browseObjectTree( ui->userTree ); } ); + connect( ui->browseGroupTree, &QPushButton::clicked, this, [this]() { browseObjectTree( ui->groupTree ); } ); + connect( ui->browseComputerTree, &QPushButton::clicked, this, [this]() { browseObjectTree( ui->computerTree ); } ); + connect( ui->browseComputerGroupTree, &QPushButton::clicked, this, [this]() { browseObjectTree( ui->computerGroupTree ); } ); + + connect( ui->browseUserLoginNameAttribute, &QPushButton::clicked, this, [this]() { browseAttribute( ui->userLoginNameAttribute, m_configuration.userTree() ); } ); + connect( ui->browseGroupMemberAttribute, &QPushButton::clicked, this, [this]() { browseAttribute( ui->groupMemberAttribute, m_configuration.groupTree() ); } ); + connect( ui->browseComputerDisplayNameAttribute, &QPushButton::clicked, this, [this]() { browseAttribute( ui->computerDisplayNameAttribute, m_configuration.computerTree() ); } ); + connect( ui->browseComputerHostNameAttribute, &QPushButton::clicked, this, [this]() { browseAttribute( ui->computerHostNameAttribute, m_configuration.computerTree() ); } ); + connect( ui->browseComputerMacAddressAttribute, &QPushButton::clicked, this, [this]() { browseAttribute( ui->computerMacAddressAttribute, m_configuration.computerTree() ); } ); + connect( ui->browseComputerLocationAttribute, &QPushButton::clicked, this, [this]() { browseAttribute( ui->computerLocationAttribute, m_configuration.computerTree() ); } ); + connect( ui->browseLocationNameAttribute, &QPushButton::clicked, this, [this]() { browseAttribute( ui->locationNameAttribute, m_configuration.computerTree() ); } ); + + CONNECT_BUTTON_SLOT( testBindInteractively ) + CONNECT_BUTTON_SLOT( testBaseDn ) + CONNECT_BUTTON_SLOT( testNamingContext ) + CONNECT_BUTTON_SLOT( testUserTree ) + CONNECT_BUTTON_SLOT( testGroupTree ) + CONNECT_BUTTON_SLOT( testComputerTree ) + CONNECT_BUTTON_SLOT( testComputerGroupTree ) + + CONNECT_BUTTON_SLOT( testUserLoginNameAttribute ) + CONNECT_BUTTON_SLOT( testGroupMemberAttribute ) + CONNECT_BUTTON_SLOT( testComputerDisplayNameAttribute ) + CONNECT_BUTTON_SLOT( testComputerHostNameAttribute ) + CONNECT_BUTTON_SLOT( testComputerMacAddressAttribute ) + CONNECT_BUTTON_SLOT( testComputerLocationAttribute ) + CONNECT_BUTTON_SLOT( testLocationNameAttribute ) + + CONNECT_BUTTON_SLOT( testUsersFilter ) + CONNECT_BUTTON_SLOT( testUserGroupsFilter ) + CONNECT_BUTTON_SLOT( testComputersFilter ) + CONNECT_BUTTON_SLOT( testComputerGroupsFilter ) + CONNECT_BUTTON_SLOT( testComputerContainersFilter ) + + CONNECT_BUTTON_SLOT( testGroupsOfUser ) + CONNECT_BUTTON_SLOT( testGroupsOfComputer ) + CONNECT_BUTTON_SLOT( testComputerObjectByIpAddress ) + CONNECT_BUTTON_SLOT( testLocationEntries ) + CONNECT_BUTTON_SLOT( testLocations ) + + CONNECT_BUTTON_SLOT( browseCACertificateFile ) + + connect( ui->tlsVerifyMode, QOverload::of( &QComboBox::currentIndexChanged ), ui->tlsCACertificateFile, [=]() { + ui->tlsCACertificateFile->setEnabled( ui->tlsVerifyMode->currentIndex() == LdapClient::TLSVerifyCustomCert ); + } ); + + const auto browseButtons = findChildren( QRegularExpression( QStringLiteral("browse.*") ) ); + for( auto button : browseButtons ) + { + button->setToolTip( tr( "Browse" ) ); + } + + const auto testButtons = findChildren( QRegularExpression( QStringLiteral("test.*") ) ); + for( auto button : testButtons ) + { + button->setToolTip( tr( "Test" ) ); + } +} + + + +LdapConfigurationPage::~LdapConfigurationPage() +{ + delete ui; +} + + + +void LdapConfigurationPage::resetWidgets() +{ + FOREACH_LDAP_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY) +} + + + +void LdapConfigurationPage::connectWidgetsToProperties() +{ + FOREACH_LDAP_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY) +} + + + +void LdapConfigurationPage::applyConfiguration() +{ +} + + + +void LdapConfigurationPage::browseBaseDn() +{ + const auto baseDn = LdapBrowseDialog( m_configuration, this ).browseBaseDn( m_configuration.baseDn() ); + + if( baseDn.isEmpty() == false ) + { + ui->baseDn->setText( baseDn ); + } +} + + + +void LdapConfigurationPage::browseObjectTree( QLineEdit* lineEdit ) +{ + auto dn = LdapClient::addBaseDn( lineEdit->text(), m_configuration.baseDn() ); + + dn = LdapBrowseDialog( m_configuration, this ).browseDn( dn ); + + if( dn.isEmpty() == false ) + { + dn = LdapClient::stripBaseDn( dn, m_configuration.baseDn() ); + + lineEdit->setText( dn ); + } +} + + + +void LdapConfigurationPage::browseAttribute( QLineEdit* lineEdit, const QString& tree ) +{ + const auto treeDn = LdapClient::addBaseDn( tree, m_configuration.baseDn() ); + + const auto attribute = LdapBrowseDialog( m_configuration, this ).browseAttribute( treeDn ); + + if( attribute.isEmpty() == false ) + { + lineEdit->setText( attribute ); + } +} + + + +void LdapConfigurationPage::testBindInteractively() +{ + testBind( false ); +} + + + +void LdapConfigurationPage::testBaseDn() +{ + if( testBindQuietly() ) + { + vDebug() << "[TEST][LDAP] Testing base DN"; + + LdapClient ldapClient( m_configuration ); + QStringList entries = ldapClient.queryBaseDn(); + + if( entries.isEmpty() ) + { + QMessageBox::critical( this, tr( "LDAP base DN test failed"), + tr( "Could not query the configured base DN. " + "Please check the base DN parameter.\n\n" + "%1" ).arg( ldapClient.errorDescription() ) ); + } + else + { + QMessageBox::information( this, tr( "LDAP base DN test successful" ), + tr( "The LDAP base DN has been queried successfully. " + "The following entries were found:\n\n%1" ). + arg( entries.join(QLatin1Char('\n')) ) ); + } + } +} + + + +void LdapConfigurationPage::testNamingContext() +{ + if( testBindQuietly() ) + { + vDebug() << "[TEST][LDAP] Testing naming context"; + + LdapClient ldapClient( m_configuration ); + const auto baseDn = ldapClient.queryNamingContexts().value( 0 ); + + if( baseDn.isEmpty() ) + { + QMessageBox::critical( this, tr( "LDAP naming context test failed"), + tr( "Could not query the base DN via naming contexts. " + "Please check the naming context attribute parameter.\n\n" + "%1" ).arg( ldapClient.errorDescription() ) ); + } + else + { + QMessageBox::information( this, tr( "LDAP naming context test successful" ), + tr( "The LDAP naming context has been queried successfully. " + "The following base DN was found:\n%1" ). + arg( baseDn ) ); + } + } +} + + + +void LdapConfigurationPage::testUserTree() +{ + if( testBindQuietly() ) + { + vDebug() << "[TEST][LDAP] Testing user tree"; + + LdapDirectory ldapDirectory( m_configuration ); + ldapDirectory.disableAttributes(); + ldapDirectory.disableFilters(); + int count = ldapDirectory.users().count(); + + reportLdapTreeQueryResult( tr( "user tree" ), count, ui->userTreeLabel->text(), + ldapDirectory.client().errorDescription() ); + } +} + + + +void LdapConfigurationPage::testGroupTree() +{ + if( testBindQuietly() ) + { + vDebug() << "[TEST][LDAP] Testing group tree"; + + LdapDirectory ldapDirectory( m_configuration ); + ldapDirectory.disableAttributes(); + ldapDirectory.disableFilters(); + int count = ldapDirectory.groups().count(); + + reportLdapTreeQueryResult( tr( "group tree" ), count, ui->groupTreeLabel->text(), + ldapDirectory.client().errorDescription() ); + } +} + + + +void LdapConfigurationPage::testComputerTree() +{ + if( testBindQuietly() ) + { + vDebug() << "[TEST][LDAP] Testing computer tree"; + + LdapDirectory ldapDirectory( m_configuration ); + ldapDirectory.disableAttributes(); + ldapDirectory.disableFilters(); + int count = ldapDirectory.computersByHostName().count(); + + reportLdapTreeQueryResult( tr( "computer tree" ), count, ui->computerTreeLabel->text(), + ldapDirectory.client().errorDescription() ); + } +} + + + +void LdapConfigurationPage::testComputerGroupTree() +{ + if( testBindQuietly() ) + { + vDebug() << "[TEST][LDAP] Testing computer group tree"; + + LdapDirectory ldapDirectory( m_configuration ); + ldapDirectory.disableAttributes(); + ldapDirectory.disableFilters(); + int count = ldapDirectory.computerGroups().count(); + + reportLdapTreeQueryResult( tr( "computer group tree" ), count, ui->computerGroupTreeLabel->text(), + ldapDirectory.client().errorDescription() ); + } +} + + + +void LdapConfigurationPage::testUserLoginNameAttribute() +{ + QString userFilter = QInputDialog::getText( this, tr( "Enter username" ), + tr( "Please enter a user login name (wildcards allowed) which to query:") ); + if( userFilter.isEmpty() == false ) + { + vDebug() << "[TEST][LDAP] Testing user login attribute for" << userFilter; + + LdapDirectory ldapDirectory( m_configuration ); + ldapDirectory.disableFilters(); + + reportLdapObjectQueryResults( tr( "user objects" ), { ui->userLoginNameAttributeLabel->text() }, + ldapDirectory.users( userFilter ), ldapDirectory ); + } +} + + + +void LdapConfigurationPage::testGroupMemberAttribute() +{ + QString groupFilter = QInputDialog::getText( this, tr( "Enter group name" ), + tr( "Please enter a group name whose members to query:") ); + if( groupFilter.isEmpty() == false ) + { + vDebug() << "[TEST][LDAP] Testing group member attribute for" << groupFilter; + + LdapDirectory ldapDirectory( m_configuration ); + ldapDirectory.disableFilters(); + + QStringList groups = ldapDirectory.groups( groupFilter ); + + if( groups.isEmpty() == false ) + { + reportLdapObjectQueryResults( tr( "group members" ), { ui->groupMemberAttributeLabel->text() }, + ldapDirectory.groupMembers( groups.first() ), ldapDirectory ); + } + else + { + QMessageBox::warning( this, tr( "Group not found"), + tr( "Could not find a group with the name \"%1\". " + "Please check the group name or the group " + "tree parameter.").arg( groupFilter ) ); + } + } +} + + + +void LdapConfigurationPage::testComputerDisplayNameAttribute() +{ + auto computerName = QInputDialog::getText( this, tr( "Enter computer display name" ), + tr( "Please enter a computer display name to query:") ); + if( computerName.isEmpty() == false ) + { + vDebug() << "[TEST][LDAP] Testing computer display name attribute"; + + LdapDirectory ldapDirectory( m_configuration ); + ldapDirectory.disableFilters(); + + reportLdapObjectQueryResults( tr( "computer objects" ), { ui->computerDisplayNameAttributeLabel->text() }, + ldapDirectory.computersByDisplayName( computerName ), ldapDirectory ); + } + +} + + + +void LdapConfigurationPage::testComputerHostNameAttribute() +{ + QString computerName = QInputDialog::getText( this, tr( "Enter computer name" ), + tr( "Please enter a computer hostname to query:") ); + if( computerName.isEmpty() == false ) + { + if( m_configuration.computerHostNameAsFQDN() && + computerName.contains( QLatin1Char('.') ) == false ) + { + QMessageBox::critical( this, tr( "Invalid hostname" ), + tr( "You configured computer hostnames to be stored " + "as fully qualified domain names (FQDN) but entered " + "a hostname without domain." ) ); + return; + } + else if( m_configuration.computerHostNameAsFQDN() == false && + computerName.contains( QLatin1Char('.') ) ) + { + QMessageBox::critical( this, tr( "Invalid hostname" ), + tr( "You configured computer hostnames to be stored " + "as simple hostnames without a domain name but " + "entered a hostname with a domain name part." ) ); + return; + } + + vDebug() << "[TEST][LDAP] Testing computer hostname attribute"; + + LdapDirectory ldapDirectory( m_configuration ); + ldapDirectory.disableFilters(); + + reportLdapObjectQueryResults( tr( "computer objects" ), { ui->computerHostNameAttributeLabel->text() }, + ldapDirectory.computersByHostName( computerName ), ldapDirectory ); + } +} + + + +void LdapConfigurationPage::testComputerMacAddressAttribute() +{ + QString computerDn = QInputDialog::getText( this, tr( "Enter computer DN" ), + tr( "Please enter the DN of a computer whose MAC address to query:") ); + if( computerDn.isEmpty() == false ) + { + vDebug() << "[TEST][LDAP] Testing computer MAC address attribute"; + + LdapDirectory ldapDirectory( m_configuration ); + ldapDirectory.disableFilters(); + + QString macAddress = ldapDirectory.computerMacAddress( computerDn ); + + reportLdapObjectQueryResults( tr( "computer MAC addresses" ), { ui->computerMacAddressAttributeLabel->text() }, + macAddress.isEmpty() ? QStringList() : QStringList( macAddress ), + ldapDirectory ); + } +} + + + +void LdapConfigurationPage::testComputerLocationAttribute() +{ + const auto locationName = QInputDialog::getText( this, tr( "Enter computer location name" ), + tr( "Please enter the name of a computer location (wildcards allowed):") ); + if( locationName.isEmpty() == false ) + { + vDebug() << "[TEST][LDAP] Testing computer location attribute for" << locationName; + + LdapDirectory ldapDirectory( m_configuration ); + + reportLdapObjectQueryResults( tr( "computer locations" ), { ui->computerLocationAttributeLabel->text() }, + ldapDirectory.computerLocations( locationName ), ldapDirectory ); + } +} + + + +void LdapConfigurationPage::testLocationNameAttribute() +{ + const auto locationName = QInputDialog::getText( this, tr( "Enter location name" ), + tr( "Please enter the name of a computer location (wildcards allowed):") ); + if( locationName.isEmpty() == false ) + { + vDebug() << "[TEST][LDAP] Testing location name attribute for" << locationName; + + LdapDirectory ldapDirectory( m_configuration ); + + reportLdapObjectQueryResults( tr( "computer locations" ), { ui->locationNameAttributeLabel->text() }, + ldapDirectory.computerLocations( locationName ), ldapDirectory ); + } +} + + + +void LdapConfigurationPage::testUsersFilter() +{ + vDebug() << "[TEST][LDAP] Testing users filter"; + + LdapDirectory ldapDirectory( m_configuration ); + int count = ldapDirectory.users().count(); + + reportLdapFilterTestResult( tr( "users" ), count, ldapDirectory.client().errorDescription() ); +} + + + +void LdapConfigurationPage::testUserGroupsFilter() +{ + vDebug() << "[TEST][LDAP] Testing user groups filter"; + + LdapDirectory ldapDirectory( m_configuration ); + int count = ldapDirectory.userGroups().count(); + + reportLdapFilterTestResult( tr( "user groups" ), count, ldapDirectory.client().errorDescription() ); +} + + + +void LdapConfigurationPage::testComputersFilter() +{ + vDebug() << "[TEST][LDAP] Testing computers filter"; + + LdapDirectory ldapDirectory( m_configuration ); + const auto count = ldapDirectory.computersByHostName().count(); + + reportLdapFilterTestResult( tr( "computers" ), count, ldapDirectory.client().errorDescription() ); +} + + + +void LdapConfigurationPage::testComputerGroupsFilter() +{ + vDebug() << "[TEST][LDAP] Testing computer groups filter"; + + LdapDirectory ldapDirectory( m_configuration ); + int count = ldapDirectory.computerGroups().count(); + + reportLdapFilterTestResult( tr( "computer groups" ), count, ldapDirectory.client().errorDescription() ); +} + + + +void LdapConfigurationPage::testComputerContainersFilter() +{ + vDebug() << "[TEST][LDAP] Testing computer containers filter"; + + LdapDirectory ldapDirectory( m_configuration ); + const auto count = ldapDirectory.computerLocations().count(); + + reportLdapFilterTestResult( tr( "computer containers" ), count, ldapDirectory.client().errorDescription() ); +} + + + +void LdapConfigurationPage::testGroupsOfUser() +{ + QString username = QInputDialog::getText( this, tr( "Enter username" ), + tr( "Please enter a user login name whose group memberships to query:") ); + if( username.isEmpty() == false ) + { + vDebug() << "[TEST][LDAP] Testing groups of user" << username; + + LdapDirectory ldapDirectory( m_configuration ); + + QStringList userObjects = ldapDirectory.users(username); + + if( userObjects.isEmpty() == false ) + { + reportLdapObjectQueryResults( tr( "groups of user" ), { ui->userLoginNameAttributeLabel->text(), + ui->groupMemberAttributeLabel->text() }, + ldapDirectory.groupsOfUser( userObjects.first() ), ldapDirectory ); + } + else + { + QMessageBox::warning( this, tr( "User not found" ), + tr( "Could not find a user with the name \"%1\". Please check the username " + "or the user tree parameter.").arg( username ) ); + } + } +} + + + +void LdapConfigurationPage::testGroupsOfComputer() +{ + QString computerHostName = QInputDialog::getText( this, tr( "Enter hostname" ), + tr( "Please enter a computer hostname whose group memberships to query:") ); + if( computerHostName.isEmpty() == false ) + { + vDebug() << "[TEST][LDAP] Testing groups of computer for" << computerHostName; + + LdapDirectory ldapDirectory( m_configuration ); + + QStringList computerObjects = ldapDirectory.computersByHostName(computerHostName); + + if( computerObjects.isEmpty() == false ) + { + reportLdapObjectQueryResults( tr( "groups of computer" ), { ui->computerHostNameAttributeLabel->text(), + ui->groupMemberAttributeLabel->text() }, + ldapDirectory.groupsOfComputer( computerObjects.first() ), ldapDirectory ); + } + else + { + QMessageBox::warning( this, tr( "Computer not found" ), + tr( "Could not find a computer with the hostname \"%1\". " + "Please check the hostname or the computer tree " + "parameter.").arg( computerHostName ) ); + } + } +} + + + +void LdapConfigurationPage::testComputerObjectByIpAddress() +{ + QString computerIpAddress = QInputDialog::getText( this, tr( "Enter computer IP address" ), + tr( "Please enter a computer IP address which to resolve to an computer object:") ); + if( computerIpAddress.isEmpty() == false ) + { + vDebug() << "[TEST][LDAP] Testing computer object resolve by IP address" << computerIpAddress; + + LdapDirectory ldapDirectory( m_configuration ); + + QString computerName = ldapDirectory.hostToLdapFormat( computerIpAddress ); + + vDebug() << "[TEST][LDAP] Resolved IP address to computer name" << computerName; + + if( computerName.isEmpty() ) + { + QMessageBox::critical( this, tr( "Hostname lookup failed" ), + tr( "Could not lookup hostname for IP address %1. " + "Please check your DNS server settings." ).arg( computerIpAddress ) ); + } + else + { + reportLdapObjectQueryResults( tr( "computers" ), { ui->computerHostNameAttributeLabel->text() }, + ldapDirectory.computersByHostName( computerName ), ldapDirectory ); + } + + } +} + + + +void LdapConfigurationPage::testLocationEntries() +{ + const auto locationName = QInputDialog::getText( this, tr( "Enter location name" ), + tr( "Please enter the name of a location whose entries to query:") ); + if( locationName.isEmpty() == false ) + { + vDebug() << "[TEST][LDAP] Testing location entries for" << locationName; + + LdapDirectory ldapDirectory( m_configuration ); + reportLdapObjectQueryResults( tr( "location entries" ), { ui->computerGroupsFilterLabel->text(), + ui->computerLocationsIdentifications->title() }, + ldapDirectory.computerLocationEntries( locationName ), ldapDirectory ); + } +} + + + +void LdapConfigurationPage::testLocations() +{ + vDebug() << "[TEST][LDAP] Querying all locations"; + + LdapDirectory ldapDirectory( m_configuration ); + reportLdapObjectQueryResults( tr( "location entries" ), { ui->computerGroupsFilterLabel->text(), + ui->computerLocationsIdentifications->title() }, + ldapDirectory.computerLocations(), ldapDirectory ); +} + + + +void LdapConfigurationPage::browseCACertificateFile() +{ + auto caCertFile = QFileDialog::getOpenFileName( this, tr( "Custom CA certificate file" ), {}, + tr( "Certificate files (*.pem)" ) ); + if( caCertFile.isEmpty() == false ) + { + ui->tlsCACertificateFile->setText( caCertFile ); + } +} + + + +bool LdapConfigurationPage::testBind( bool quiet ) +{ + vDebug() << "[TEST][LDAP] Testing bind"; + + LdapClient ldapClient( m_configuration ); + + if( ldapClient.isConnected() == false ) + { + QMessageBox::critical( this, tr( "LDAP connection failed"), + tr( "Could not connect to the LDAP server. " + "Please check the server parameters.\n\n" + "%1" ).arg( ldapClient.errorDescription() ) ); + } + else if( ldapClient.isBound() == false ) + { + QMessageBox::critical( this, tr( "LDAP bind failed"), + tr( "Could not bind to the LDAP server. " + "Please check the server parameters " + "and bind credentials.\n\n" + "%1" ).arg( ldapClient.errorDescription() ) ); + } + else if( quiet == false ) + { + QMessageBox::information( this, tr( "LDAP bind successful"), + tr( "Successfully connected to the LDAP " + "server and performed an LDAP bind. " + "The basic LDAP settings are " + "configured correctly." ) ); + } + + return ldapClient.isConnected() && ldapClient.isBound(); +} + + + + +void LdapConfigurationPage::reportLdapTreeQueryResult( const QString& name, int count, + const QString& parameter, const QString& errorDescription ) +{ + if( count <= 0 ) + { + QMessageBox::critical( this, tr( "LDAP %1 test failed").arg( name ), + tr( "Could not query any entries in configured %1. " + "Please check the parameter \"%2\".\n\n" + "%3" ).arg( name, parameter, errorDescription ) ); + } + else + { + QMessageBox::information( this, tr( "LDAP %1 test successful" ).arg( name ), + tr( "The %1 has been queried successfully and " + "%2 entries were found." ).arg( name ).arg( count ) ); + } +} + + + + + +void LdapConfigurationPage::reportLdapObjectQueryResults( const QString &objectsName, const QStringList& parameterNames, + const QStringList& results, const LdapDirectory &directory ) +{ + if( results.isEmpty() ) + { + QStringList parameters; + parameters.reserve( parameterNames.count() ); + + for( const auto& parameterName : parameterNames ) + { + parameters += QStringLiteral("\"%1\"").arg( parameterName ); + } + + QMessageBox::critical( this, tr( "LDAP test failed"), + tr( "Could not query any %1. " + "Please check the parameter(s) %2 and enter the name of an existing object.\n\n" + "%3" ).arg( objectsName, parameters.join( QStringLiteral(" %1 ").arg( tr("and") ) ), + directory.client().errorDescription() ) ); + } + else + { + QMessageBox::information( this, tr( "LDAP test successful" ), + tr( "%1 %2 have been queried successfully:\n\n%3" ). + arg( results.count() ). + arg( objectsName, formatResultsString( results ) ) ); + } +} + + + + + +void LdapConfigurationPage::reportLdapFilterTestResult( const QString &filterObjects, int count, const QString &errorDescription ) +{ + if( count <= 0 ) + { + QMessageBox::critical( this, tr( "LDAP filter test failed"), + tr( "Could not query any %1 using the configured filter. " + "Please check the LDAP filter for %1.\n\n" + "%2" ).arg( filterObjects, errorDescription ) ); + } + else + { + QMessageBox::information( this, tr( "LDAP filter test successful" ), + tr( "%1 %2 have been queried successfully using the configured filter." ). + arg( count ).arg( filterObjects ) ); + } +} + + + +QString LdapConfigurationPage::formatResultsString( const QStringList &results ) +{ + static constexpr auto FirstResult = 0; + static constexpr auto MaxResults = 3; + + if( results.count() <= MaxResults ) + { + return results.join(QLatin1Char('\n')); + } + + return QStringLiteral( "%1\n[...]" ).arg( results.mid( FirstResult, MaxResults ).join( QLatin1Char('\n') ) ); +} diff --git a/plugins/ldap/common/LdapConfigurationPage.h b/plugins/ldap/common/LdapConfigurationPage.h new file mode 100644 index 0000000..01ae8e6 --- /dev/null +++ b/plugins/ldap/common/LdapConfigurationPage.h @@ -0,0 +1,100 @@ +/* + * LdapConfigurationPage.h - header for the LdapConfigurationPage class + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "ConfigurationPage.h" +#include "LdapCommon.h" + +class LdapConfiguration; +class LdapDirectory; + +namespace Ui { +class LdapConfigurationPage; +} + +class QLineEdit; + +class LDAP_COMMON_EXPORT LdapConfigurationPage : public ConfigurationPage +{ + Q_OBJECT +public: + explicit LdapConfigurationPage( LdapConfiguration& configuration, QWidget* parent = nullptr ); + ~LdapConfigurationPage() override; + + void resetWidgets() override; + void connectWidgetsToProperties() override; + void applyConfiguration() override; + +private: + void browseBaseDn(); + void browseObjectTree( QLineEdit* lineEdit ); + void browseAttribute( QLineEdit* lineEdit, const QString& tree ); + + void testBindInteractively(); + void testBaseDn(); + void testNamingContext(); + void testUserTree(); + void testGroupTree(); + void testComputerTree(); + void testComputerGroupTree(); + void testUserLoginNameAttribute(); + void testGroupMemberAttribute(); + void testComputerDisplayNameAttribute(); + void testComputerHostNameAttribute(); + void testComputerMacAddressAttribute(); + void testComputerLocationAttribute(); + void testLocationNameAttribute(); + void testUsersFilter(); + void testUserGroupsFilter(); + void testComputersFilter(); + void testComputerGroupsFilter(); + void testComputerContainersFilter(); + void testGroupsOfUser(); + void testGroupsOfComputer(); + void testComputerObjectByIpAddress(); + void testLocationEntries(); + void testLocations(); + + void browseCACertificateFile(); + + bool testBindQuietly() + { + return testBind( true ); + } + + bool testBind( bool quiet ); + void reportLdapTreeQueryResult( const QString& name, int count, const QString& parameter, + const QString& errorDescription ); + void reportLdapObjectQueryResults( const QString &objectsName, const QStringList& parameterNames, + const QStringList &results, const LdapDirectory& directory ); + void reportLdapFilterTestResult( const QString &filterObjects, int count, const QString &errorDescription ); + + static QString formatResultsString( const QStringList& results ); + + Ui::LdapConfigurationPage *ui; + + LdapConfiguration& m_configuration; + +}; diff --git a/plugins/ldap/common/LdapConfigurationPage.ui b/plugins/ldap/common/LdapConfigurationPage.ui new file mode 100644 index 0000000..dc11bec --- /dev/null +++ b/plugins/ldap/common/LdapConfigurationPage.ui @@ -0,0 +1,1369 @@ + + + LdapConfigurationPage + + + LDAP Basic + + + + :/ldap/application-x-kexi-connectiondata.png:/ldap/application-x-kexi-connectiondata.png + + + + 0 + + + 0 + + + + + 0 + + + + Basic settings + + + + + + General + + + + + + + + Anonymous bind + + + true + + + bindConfigurationGroup + + + + + + + Use bind credentials + + + bindConfigurationGroup + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + + + Bind DN + + + + + + + 65536 + + + 389 + + + + + + + Bind password + + + + + + + + + + false + + + QLineEdit::Password + + + + + + + LDAP server and port + + + + + + + false + + + + + + + + + + Connection security + + + + + + TLS certificate verification + + + + + + + false + + + + + + + Encryption protocol + + + + + + + + System defaults + + + + + Never (insecure!) + + + + + Custom CA certificate file + + + + + + + + + None + + + + + TLS + + + + + SSL + + + + + + + + + :/core/document-open.png:/core/document-open.png + + + + + + + Custom CA certificate file + + + + + + + + + + Base DN + + + + + + Discover base DN by naming context + + + baseDnConfigGroup + + + + + + + false + + + e.g. namingContexts or defaultNamingContext + + + + + + + Fixed base DN + + + true + + + baseDnConfigGroup + + + + + + + e.g. dc=example,dc=org + + + + + + + + :/core/edit-find.png:/core/edit-find.png + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + false + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Environment settings + + + + + + Object trees + + + + + + (only if different from group tree) + + + + + + + Computer tree + + + + + + + Perform recursive search operations in object trees + + + + + + + User tree + + + + + + + e.g. OU=Computers + + + + + + + Group tree + + + + + + + Computer group tree + + + + + + + e.g. OU=Users + + + + + + + e.g. OU=Groups + + + + + + + + :/core/edit-find.png:/core/edit-find.png + + + + + + + + :/core/edit-find.png:/core/edit-find.png + + + + + + + + :/core/edit-find.png:/core/edit-find.png + + + + + + + + :/core/edit-find.png:/core/edit-find.png + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + + + + Object attributes + + + + + + e.g. name or description + + + + + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + + + + + :/core/edit-find.png:/core/edit-find.png + + + + + + + false + + + e.g. room or computerLab + + + + + + + User login name attribute + + + + + + + Computer location attribute + + + + + + + e.g. member or memberUid + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + Group member attribute + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + e.g. hwAddress + + + + + + + Computer MAC address attribute + + + + + + + e.g. dNSHostName + + + + + + + + :/core/edit-find.png:/core/edit-find.png + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + false + + + + :/core/edit-find.png:/core/edit-find.png + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + false + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + e.g. uid or sAMAccountName + + + + + + + Computer display name attribute + + + + + + + Computer hostname attribute + + + + + + + Location name attribute + + + + + + + + :/core/edit-find.png:/core/edit-find.png + + + + + + + + :/core/edit-find.png:/core/edit-find.png + + + + + + + + :/core/edit-find.png:/core/edit-find.png + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + e.g. cn or displayName + + + + + + + + :/core/edit-find.png:/core/edit-find.png + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Advanced settings + + + + + + Optional object filters + + + + + + Filter for computer groups + + + + + + + e.g. (objectClass=computer) + + + + + + + e.g. (objectClass=group) + + + + + + + e.g. (objectClass=person) + + + + + + + Filter for users + + + + + + + Filter for computers + + + + + + + Filter for user groups + + + + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + + + + Filter for computer containers + + + + + + + false + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + false + + + + :/core/dialog-ok-apply.png:/core/dialog-ok-apply.png + + + + + + + + + + Group member identification + + + + + + Distinguished name (Samba/AD) + + + true + + + groupMemberMatchingGroup + + + + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + groupMemberMatchingGroup + + + + + + + + + + Computer locations identification + + + + + + Identify computer locations (e.g. rooms) via: + + + + + + + Computer groups + + + true + + + computerGroupingGroup + + + + + + + Computer containers or OUs + + + computerGroupingGroup + + + + + + + Location attribute in computer objects + + + computerGroupingGroup + + + + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 20 + 0 + + + + + + + + + Integration tests + + + + + + List all groups of a user + + + + :/ldap/user-group-properties.png:/ldap/user-group-properties.png + + + + + + + List all groups of a computer + + + + :/ldap/computer.png:/ldap/computer.png + + + + + + + Get computer object by IP address + + + + :/ldap/computer.png:/ldap/computer.png + + + + + + + List all entries of a location + + + + :/ldap/configure.png:/ldap/configure.png + + + + + + + List all locations + + + + :/ldap/distribute-vertical-margin.png:/ldap/distribute-vertical-margin.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + tabWidget + serverHost + serverPort + anonymousBind + useBindCredentials + testBindInteractively + bindDn + bindPassword + connectionSecurity + tlsVerifyMode + tlsCACertificateFile + browseCACertificateFile + isFixedBaseDn + baseDn + testBaseDn + queryNamingContext + namingContextAttribute + testNamingContext + userTree + groupTree + computerTree + computerGroupTree + browseUserTree + testUserTree + browseGroupTree + testGroupTree + browseComputerTree + testComputerTree + browseComputerGroupTree + testComputerGroupTree + recursiveSearchOperations + userLoginNameAttribute + groupMemberAttribute + computerDisplayNameAttribute + computerHostNameAttribute + computerHostNameAsFQDN + computerMacAddressAttribute + computerLocationAttribute + locationNameAttribute + browseUserLoginNameAttribute + testUserLoginNameAttribute + browseGroupMemberAttribute + testGroupMemberAttribute + browseComputerDisplayNameAttribute + testComputerDisplayNameAttribute + browseComputerHostNameAttribute + testComputerHostNameAttribute + browseComputerMacAddressAttribute + testComputerMacAddressAttribute + browseComputerLocationAttribute + testComputerLocationAttribute + browseLocationNameAttribute + testLocationNameAttribute + usersFilter + userGroupsFilter + computersFilter + computerGroupsFilter + computerContainersFilter + testUsersFilter + testUserGroupsFilter + testComputersFilter + testComputerGroupsFilter + testComputerContainersFilter + identifyGroupMembersByDN + identifyGroupMembersByNameAttribute + computerLocationsByGroups + computerLocationsByContainer + computerLocationsByAttribute + testGroupsOfUser + testGroupsOfComputer + testComputerObjectByIpAddress + testLocationEntries + testLocations + + + + + + + + useBindCredentials + toggled(bool) + bindPassword + setEnabled(bool) + + + 506 + 142 + + + 842 + 241 + + + + + useBindCredentials + toggled(bool) + bindDn + setEnabled(bool) + + + 506 + 142 + + + 842 + 193 + + + + + isFixedBaseDn + toggled(bool) + testBaseDn + setEnabled(bool) + + + 467 + 423 + + + 825 + 425 + + + + + queryNamingContext + toggled(bool) + namingContextAttribute + setEnabled(bool) + + + 467 + 474 + + + 730 + 475 + + + + + isFixedBaseDn + toggled(bool) + baseDn + setEnabled(bool) + + + 467 + 423 + + + 730 + 424 + + + + + queryNamingContext + toggled(bool) + testNamingContext + setEnabled(bool) + + + 467 + 474 + + + 825 + 476 + + + + + computerLocationsByAttribute + toggled(bool) + computerLocationAttribute + setEnabled(bool) + + + 438 + 657 + + + 545 + 608 + + + + + computerLocationsByAttribute + toggled(bool) + browseComputerLocationAttribute + setEnabled(bool) + + + 438 + 657 + + + 770 + 608 + + + + + computerLocationsByAttribute + toggled(bool) + testComputerLocationAttribute + setEnabled(bool) + + + 438 + 657 + + + 815 + 608 + + + + + computerLocationsByAttribute + toggled(bool) + locationNameAttribute + setDisabled(bool) + + + 438 + 657 + + + 545 + 651 + + + + + computerLocationsByAttribute + toggled(bool) + browseLocationNameAttribute + setDisabled(bool) + + + 438 + 657 + + + 770 + 651 + + + + + computerLocationsByAttribute + toggled(bool) + testLocationNameAttribute + setDisabled(bool) + + + 438 + 657 + + + 815 + 651 + + + + + computerLocationsByGroups + toggled(bool) + computerGroupTree + setEnabled(bool) + + + 438 + 575 + + + 492 + 249 + + + + + computerLocationsByGroups + toggled(bool) + browseComputerGroupTree + setEnabled(bool) + + + 438 + 575 + + + 770 + 249 + + + + + computerLocationsByGroups + toggled(bool) + testComputerGroupTree + setEnabled(bool) + + + 438 + 575 + + + 815 + 249 + + + + + computerLocationsByContainer + toggled(bool) + computerContainersFilter + setEnabled(bool) + + + 436 + 616 + + + 551 + 292 + + + + + computerLocationsByContainer + toggled(bool) + testComputerContainersFilter + setEnabled(bool) + + + 436 + 616 + + + 810 + 292 + + + + + + + + + + + diff --git a/plugins/ldap/common/LdapDirectory.cpp b/plugins/ldap/common/LdapDirectory.cpp new file mode 100644 index 0000000..e3c763a --- /dev/null +++ b/plugins/ldap/common/LdapDirectory.cpp @@ -0,0 +1,489 @@ +/* + * LdapDirectory.cpp - class representing the LDAP directory and providing access to directory entries + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "HostAddress.h" +#include "LdapConfiguration.h" +#include "LdapDirectory.h" + + +LdapDirectory::LdapDirectory( const LdapConfiguration& configuration, QObject* parent ) : + QObject( parent ), + m_configuration( configuration ), + m_client( configuration, QUrl(), this ) +{ + if( m_configuration.recursiveSearchOperations() ) + { + m_defaultSearchScope = LdapClient::Scope::Sub; + } + else + { + m_defaultSearchScope = LdapClient::Scope::One; + } + + m_userLoginNameAttribute = m_configuration.userLoginNameAttribute(); + m_groupMemberAttribute = m_configuration.groupMemberAttribute(); + m_computerDisplayNameAttribute = m_configuration.computerDisplayNameAttribute(); + m_computerHostNameAttribute = m_configuration.computerHostNameAttribute(); + m_computerHostNameAsFQDN = m_configuration.computerHostNameAsFQDN(); + m_computerMacAddressAttribute = m_configuration.computerMacAddressAttribute(); + m_locationNameAttribute = m_configuration.locationNameAttribute(); + + if( m_computerDisplayNameAttribute.isEmpty() ) + { + m_computerDisplayNameAttribute = QStringLiteral("cn"); + } + + if( m_locationNameAttribute.isEmpty() ) + { + m_locationNameAttribute = QStringLiteral("cn"); + } + + m_usersFilter = m_configuration.usersFilter(); + m_userGroupsFilter = m_configuration.userGroupsFilter(); + m_computersFilter = m_configuration.computersFilter(); + m_computerGroupsFilter = m_configuration.computerGroupsFilter(); + m_computerContainersFilter = m_configuration.computerContainersFilter(); + + m_identifyGroupMembersByNameAttribute = m_configuration.identifyGroupMembersByNameAttribute(); + + m_computerLocationsByContainer = m_configuration.computerLocationsByContainer(); + m_computerLocationsByAttribute = m_configuration.computerLocationsByAttribute(); + m_computerLocationAttribute = m_configuration.computerLocationAttribute(); +} + + + +const QString& LdapDirectory::configInstanceId() const +{ + return m_configuration.instanceId(); +} + + + +QString LdapDirectory::usersDn() +{ + if( m_usersDn.isEmpty() ) + { + m_usersDn = LdapClient::constructSubDn( m_configuration.userTree(), m_client.baseDn() ); + } + + return m_usersDn; +} + + + +QString LdapDirectory::groupsDn() +{ + if( m_groupsDn.isEmpty() ) + { + m_groupsDn = LdapClient::constructSubDn( m_configuration.groupTree(), m_client.baseDn() ); + } + + return m_groupsDn; +} + + + +QString LdapDirectory::computersDn() +{ + if( m_computersDn.isEmpty() ) + { + m_computersDn = LdapClient::constructSubDn( m_configuration.computerTree(), m_client.baseDn() ); + } + + return m_computersDn; +} + + + +QString LdapDirectory::computerGroupsDn() +{ + if( m_computerGroupsDn.isEmpty() ) + { + const auto computerGroupTree = m_configuration.computerGroupTree(); + + if( computerGroupTree.isEmpty() == false ) + { + m_computerGroupsDn = LdapClient::constructSubDn( computerGroupTree, m_client.baseDn() ); + } + else + { + m_computerGroupsDn = groupsDn(); + } + } + + return m_computerGroupsDn; +} + + + +/*! + * \brief Disables any configured attributes which is required for some test scenarious + */ +void LdapDirectory::disableAttributes() +{ + m_userLoginNameAttribute.clear(); + m_computerDisplayNameAttribute.clear(); + m_computerHostNameAttribute.clear(); + m_computerMacAddressAttribute.clear(); +} + + + +/*! + * \brief Disables any configured filters which is required for some test scenarious + */ +void LdapDirectory::disableFilters() +{ + m_usersFilter.clear(); + m_userGroupsFilter.clear(); + m_computersFilter.clear(); + m_computerGroupsFilter.clear(); + m_computerContainersFilter.clear(); +} + + + + +QStringList LdapDirectory::users( const QString& filterValue ) +{ + return m_client.queryDistinguishedNames( usersDn(), + LdapClient::constructQueryFilter( m_userLoginNameAttribute, filterValue, m_usersFilter ), + m_defaultSearchScope ); +} + + + +QStringList LdapDirectory::groups( const QString& filterValue ) +{ + return m_client.queryDistinguishedNames( groupsDn(), + LdapClient::constructQueryFilter( QStringLiteral( "cn" ), filterValue ), + m_defaultSearchScope ); +} + + + +QStringList LdapDirectory::userGroups( const QString& filterValue ) +{ + return m_client.queryDistinguishedNames( groupsDn(), + LdapClient::constructQueryFilter( QStringLiteral( "cn" ), filterValue, m_userGroupsFilter ), + m_defaultSearchScope ); +} + + + +QStringList LdapDirectory::computersByDisplayName( const QString& filterValue ) +{ + return m_client.queryDistinguishedNames( computersDn(), + LdapClient::constructQueryFilter( m_computerDisplayNameAttribute, filterValue, m_computersFilter ), + computerSearchScope() ); +} + + + +/*! + * \brief Returns list of computer object names matching the given hostname filter + * \param filterValue A filter value which is used to query the hostname attribute + * \return List of DNs of all matching computer objects + */ +QStringList LdapDirectory::computersByHostName( const QString& filterValue ) +{ + return m_client.queryDistinguishedNames( computersDn(), + LdapClient::constructQueryFilter( m_computerHostNameAttribute, filterValue, m_computersFilter ), + computerSearchScope() ); +} + + + +QStringList LdapDirectory::computerGroups( const QString& filterValue ) +{ + return m_client.queryDistinguishedNames( computerGroupsDn(), + LdapClient::constructQueryFilter( m_locationNameAttribute, filterValue, m_computerGroupsFilter ) , + m_defaultSearchScope ); +} + + + +QStringList LdapDirectory::computerLocations( const QString& filterValue ) +{ + QStringList locations; + + if( m_computerLocationsByAttribute ) + { + locations = m_client.queryAttributeValues( computersDn(), + m_computerLocationAttribute, + LdapClient::constructQueryFilter( m_computerLocationAttribute, filterValue, m_computersFilter ), + m_defaultSearchScope ); + } + else if( m_computerLocationsByContainer ) + { + locations = m_client.queryAttributeValues( computersDn(), + m_locationNameAttribute, + LdapClient::constructQueryFilter( m_locationNameAttribute, filterValue, m_computerContainersFilter ) , + m_defaultSearchScope ); + } + else + { + locations = m_client.queryAttributeValues( computerGroupsDn(), + m_locationNameAttribute, + LdapClient::constructQueryFilter( m_locationNameAttribute, filterValue, m_computerGroupsFilter ) , + m_defaultSearchScope ); + } + + locations.removeDuplicates(); + + std::sort( locations.begin(), locations.end() ); + + return locations; +} + + + +QStringList LdapDirectory::groupMembers( const QString& groupDn ) +{ + return m_client.queryAttributeValues( groupDn, m_groupMemberAttribute ); +} + + + +QStringList LdapDirectory::groupsOfUser( const QString& userDn ) +{ + const auto userId = groupMemberUserIdentification( userDn ); + if( m_groupMemberAttribute.isEmpty() || userId.isEmpty() ) + { + return {}; + } + + return m_client.queryDistinguishedNames( groupsDn(), + LdapClient::constructQueryFilter( m_groupMemberAttribute, userId, m_userGroupsFilter ), + m_defaultSearchScope ); +} + + + +QStringList LdapDirectory::groupsOfComputer( const QString& computerDn ) +{ + const auto computerId = groupMemberComputerIdentification( computerDn ); + if( m_groupMemberAttribute.isEmpty() || computerId.isEmpty() ) + { + return {}; + } + + return m_client.queryDistinguishedNames( computerGroupsDn(), + LdapClient::constructQueryFilter( m_groupMemberAttribute, computerId, m_computerGroupsFilter ), + m_defaultSearchScope ); +} + + + +QStringList LdapDirectory::locationsOfComputer( const QString& computerDn ) +{ + if( m_computerLocationsByAttribute ) + { + return m_client.queryAttributeValues( computerDn, m_computerLocationAttribute ); + } + else if( m_computerLocationsByContainer ) + { + return m_client.queryAttributeValues( LdapClient::parentDn( computerDn ), m_locationNameAttribute ); + } + + const auto computerId = groupMemberComputerIdentification( computerDn ); + if( m_groupMemberAttribute.isEmpty() || computerId.isEmpty() ) + { + return {}; + } + + return m_client.queryAttributeValues( computerGroupsDn(), + m_locationNameAttribute, + LdapClient::constructQueryFilter( m_groupMemberAttribute, computerId, m_computerGroupsFilter ), + m_defaultSearchScope ); +} + + + +QString LdapDirectory::userLoginName( const QString& userDn ) +{ + return m_client.queryAttributeValues( userDn, m_userLoginNameAttribute ).value( 0 ); +} + + + +QString LdapDirectory::computerDisplayName( const QString& computerDn ) +{ + return m_client.queryAttributeValues( computerDn, m_computerDisplayNameAttribute ).value( 0 ); + +} + + + +QString LdapDirectory::computerHostName( const QString& computerDn ) +{ + if( computerDn.isEmpty() ) + { + return {}; + } + + return m_client.queryAttributeValues( computerDn, m_computerHostNameAttribute ).value( 0 ); +} + + + +QString LdapDirectory::computerMacAddress( const QString& computerDn ) +{ + if( computerDn.isEmpty() ) + { + return {}; + } + + return m_client.queryAttributeValues( computerDn, m_computerMacAddressAttribute ).value( 0 ); +} + + + +QString LdapDirectory::groupMemberUserIdentification( const QString& userDn ) +{ + if( m_identifyGroupMembersByNameAttribute ) + { + return userLoginName( userDn ); + } + + return userDn; +} + + + +QString LdapDirectory::groupMemberComputerIdentification( const QString& computerDn ) +{ + if( m_identifyGroupMembersByNameAttribute ) + { + return computerHostName( computerDn ); + } + + return computerDn; +} + + + +QStringList LdapDirectory::computerLocationEntries( const QString& locationName ) +{ + if( m_computerLocationsByAttribute ) + { + return m_client.queryDistinguishedNames( computersDn(), + LdapClient::constructQueryFilter( m_computerLocationAttribute, locationName, m_computersFilter ), + m_defaultSearchScope ); + } + else if( m_computerLocationsByContainer ) + { + const auto locationDnFilter = LdapClient::constructQueryFilter( m_locationNameAttribute, locationName, m_computerContainersFilter ); + const auto locationDns = m_client.queryDistinguishedNames( computersDn(), locationDnFilter, m_defaultSearchScope ); + + return m_client.queryDistinguishedNames( locationDns.value( 0 ), + LdapClient::constructQueryFilter( {}, {}, m_computersFilter ), + m_defaultSearchScope ); + } + + const auto groups = computerGroups( locationName ); + if( groups.size() != 1 ) + { + vWarning() << "location" << locationName << "does not resolve to exactly one computer group:" << groups; + } + + if( groups.isEmpty() ) + { + return {}; + } + + auto memberComputers = groupMembers( groups.value( 0 ) ); + + // computer filter configured? + if( m_computersFilter.isEmpty() == false ) + { + const auto computerHostNames = computersByHostName(); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + auto memberComputersSet = QSet( memberComputers.begin(), memberComputers.end() ); + const auto computerHostNameSet = QSet( computerHostNames.begin(), computerHostNames.end() ); +#else + auto memberComputersSet = memberComputers.toSet(); + const auto computerHostNameSet = computersByHostName().toSet(); +#endif + + // then return intersection of filtered computer list and group members + const auto computerIntersection = memberComputersSet.intersect( computerHostNameSet ); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + return { computerIntersection.begin(), computerIntersection.end() }; +#else + return computerIntersection.toList(); +#endif + } + + return memberComputers; +} + + + +QString LdapDirectory::hostToLdapFormat( const QString& host ) +{ + if( m_computerHostNameAsFQDN ) + { + return HostAddress( host ).convert( HostAddress::Type::FullyQualifiedDomainName ); + } + + return HostAddress( host ).convert( HostAddress::Type::HostName ); +} + + + +QString LdapDirectory::computerObjectFromHost( const QString& host ) +{ + const auto hostName = hostToLdapFormat( host ); + if( hostName.isEmpty() ) + { + vWarning() << "could not resolve hostname, returning empty computer object"; + return {}; + } + + const auto computerObjects = computersByHostName( hostName ); + if( computerObjects.count() == 1 ) + { + return computerObjects.first(); + } + + // return empty result if not exactly one object was found + vWarning() << "more than one computer object found, returning empty computer object!"; + return {}; +} + + + +LdapClient::Scope LdapDirectory::computerSearchScope() const +{ + // when using containers/OUs as locations computer objects are not located directly below the configured computer DN + if( m_computerLocationsByContainer ) + { + return LdapClient::Scope::Sub; + } + + return m_defaultSearchScope; +} diff --git a/plugins/ldap/common/LdapDirectory.h b/plugins/ldap/common/LdapDirectory.h new file mode 100644 index 0000000..f2e99c5 --- /dev/null +++ b/plugins/ldap/common/LdapDirectory.h @@ -0,0 +1,154 @@ +/* + * LdapDirectory.h - class representing the LDAP directory and providing access to directory entries + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "LdapClient.h" +#include "LdapCommon.h" +#include "VeyonCore.h" + +class LdapConfiguration; +class LdapClient; + +class LDAP_COMMON_EXPORT LdapDirectory : public QObject +{ + Q_OBJECT +public: + explicit LdapDirectory( const LdapConfiguration& configuration, QObject* parent = nullptr ); + ~LdapDirectory() override = default; + + const QString& configInstanceId() const; + + const LdapClient& client() const + { + return m_client; + } + + LdapClient& client() + { + return m_client; + } + + QString usersDn(); + QString groupsDn(); + QString computersDn(); + QString computerGroupsDn(); + + void disableAttributes(); + void disableFilters(); + + QStringList users( const QString& filterValue = {} ); + QStringList groups( const QString& filterValue = {} ); + QStringList userGroups( const QString& filterValue = {} ); + QStringList computersByDisplayName( const QString& filterValue = {} ); + QStringList computersByHostName( const QString& filterValue = {} ); + QStringList computerGroups( const QString& filterValue = {} ); + QStringList computerLocations( const QString& filterValue = {} ); + + QStringList groupMembers( const QString& groupDn ); + QStringList groupsOfUser( const QString& userDn ); + QStringList groupsOfComputer( const QString& computerDn ); + QStringList locationsOfComputer( const QString& computerDn ); + + QString userLoginName( const QString& userDn ); + QString computerDisplayName( const QString& computerDn ); + QString computerHostName( const QString& computerDn ); + QString computerMacAddress( const QString& computerDn ); + QString groupMemberUserIdentification( const QString& userDn ); + QString groupMemberComputerIdentification( const QString& computerDn ); + + QStringList computerLocationEntries( const QString& locationName ); + + QString hostToLdapFormat( const QString& host ); + QString computerObjectFromHost( const QString& host ); + + const QString& computersFilter() const + { + return m_computersFilter; + } + + const QString& computerContainersFilter() const + { + return m_computerContainersFilter; + } + + const QString& locationNameAttribute() const + { + return m_locationNameAttribute; + } + + const QString& computerDisplayNameAttribute() const + { + return m_computerDisplayNameAttribute; + } + + const QString& computerHostNameAttribute() const + { + return m_computerHostNameAttribute; + } + + const QString& computerMacAddressAttribute() const + { + return m_computerMacAddressAttribute; + } + + bool computerLocationsByContainer() const + { + return m_computerLocationsByContainer; + } + +private: + LdapClient::Scope computerSearchScope() const; + + const LdapConfiguration& m_configuration; + LdapClient m_client; + + LdapClient::Scope m_defaultSearchScope = LdapClient::Scope::Base; + + QString m_usersDn; + QString m_groupsDn; + QString m_computersDn; + QString m_computerGroupsDn; + + QString m_userLoginNameAttribute; + QString m_groupMemberAttribute; + QString m_computerDisplayNameAttribute; + QString m_computerHostNameAttribute; + QString m_computerMacAddressAttribute; + QString m_locationNameAttribute; + + QString m_usersFilter; + QString m_userGroupsFilter; + QString m_computersFilter; + QString m_computerGroupsFilter; + QString m_computerContainersFilter; + + QString m_computerLocationAttribute; + + bool m_identifyGroupMembersByNameAttribute = false; + bool m_computerLocationsByContainer = false; + bool m_computerLocationsByAttribute = false; + bool m_computerHostNameAsFQDN = false; + +}; diff --git a/plugins/ldap/common/LdapNetworkObjectDirectory.cpp b/plugins/ldap/common/LdapNetworkObjectDirectory.cpp new file mode 100644 index 0000000..8cb1f09 --- /dev/null +++ b/plugins/ldap/common/LdapNetworkObjectDirectory.cpp @@ -0,0 +1,229 @@ +/* + * LdapNetworkObjectDirectory.cpp - provides a NetworkObjectDirectory for LDAP + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "LdapConfiguration.h" +#include "LdapDirectory.h" +#include "LdapNetworkObjectDirectory.h" + + +LdapNetworkObjectDirectory::LdapNetworkObjectDirectory( const LdapConfiguration& ldapConfiguration, + QObject* parent ) : + NetworkObjectDirectory( parent ), + m_ldapDirectory( ldapConfiguration ) +{ +} + + + +NetworkObjectList LdapNetworkObjectDirectory::queryObjects( NetworkObject::Type type, + NetworkObject::Attribute attribute, const QVariant& value ) +{ + switch( type ) + { + case NetworkObject::Type::Location: return queryLocations( attribute, value ); + case NetworkObject::Type::Host: return queryHosts( attribute, value ); + default: break; + } + + return {}; +} + + + +NetworkObjectList LdapNetworkObjectDirectory::queryParents( const NetworkObject& object ) +{ + switch( object.type() ) + { + case NetworkObject::Type::Host: + return { NetworkObject( NetworkObject::Type::Location, + m_ldapDirectory.locationsOfComputer( object.directoryAddress() ).value( 0 ) ) }; + case NetworkObject::Type::Location: + return { NetworkObject( NetworkObject::Type::Root ) }; + default: + break; + } + + return { NetworkObject( NetworkObject::Type::None ) }; +} + + + +void LdapNetworkObjectDirectory::update() +{ + const auto locations = m_ldapDirectory.computerLocations(); + const NetworkObject rootObject( NetworkObject::Type::Root ); + + for( const auto& location : qAsConst( locations ) ) + { + const NetworkObject locationObject( NetworkObject::Type::Location, location ); + + addOrUpdateObject( locationObject, rootObject ); + + updateLocation( locationObject ); + } + + removeObjects( NetworkObject( NetworkObject::Type::Root ), [locations]( const NetworkObject& object ) { + return object.type() == NetworkObject::Type::Location && locations.contains( object.name() ) == false; } ); +} + + + +void LdapNetworkObjectDirectory::updateLocation( const NetworkObject& locationObject ) +{ + const auto computers = m_ldapDirectory.computerLocationEntries( locationObject.name() ); + + for( const auto& computer : qAsConst( computers ) ) + { + const auto hostObject = computerToObject( &m_ldapDirectory, computer ); + if( hostObject.type() == NetworkObject::Type::Host ) + { + addOrUpdateObject( hostObject, locationObject ); + } + } + + removeObjects( locationObject, [computers]( const NetworkObject& object ) { + return object.type() == NetworkObject::Type::Host && computers.contains( object.directoryAddress() ) == false; } ); +} + + + +NetworkObjectList LdapNetworkObjectDirectory::queryLocations( NetworkObject::Attribute attribute, const QVariant& value ) +{ + QString name; + + switch( attribute ) + { + case NetworkObject::Attribute::None: + break; + + case NetworkObject::Attribute::Name: + name = value.toString(); + break; + + default: + vCritical() << "Can't query locations by attribute" << attribute; + return {}; + } + + const auto locations = m_ldapDirectory.computerLocations( name ); + + NetworkObjectList locationObjects; + locationObjects.reserve( locations.size() ); + + for( const auto& location : locations ) + { + locationObjects.append( NetworkObject( NetworkObject::Type::Location, location ) ); + } + + return locationObjects; +} + + + +NetworkObjectList LdapNetworkObjectDirectory::queryHosts( NetworkObject::Attribute attribute, const QVariant& value ) +{ + QStringList computers; + + switch( attribute ) + { + case NetworkObject::Attribute::None: + computers = m_ldapDirectory.computersByHostName( {} ); + break; + + case NetworkObject::Attribute::Name: + computers = m_ldapDirectory.computersByDisplayName( value.toString() ); + break; + + case NetworkObject::Attribute::HostAddress: + { + const auto hostName = m_ldapDirectory.hostToLdapFormat( value.toString() ); + if( hostName.isEmpty() ) + { + return {}; + } + computers = m_ldapDirectory.computersByHostName( hostName ); + break; + } + + default: + vCritical() << "Can't query hosts by attribute" << attribute; + return {}; + } + + NetworkObjectList hostObjects; + hostObjects.reserve( computers.size() ); + + for( const auto& computer : computers ) + { + const auto hostObject = computerToObject( &m_ldapDirectory, computer ); + if( hostObject.isValid() ) + { + hostObjects.append( hostObject ); + } + } + + return hostObjects; +} + + + +NetworkObject LdapNetworkObjectDirectory::computerToObject( LdapDirectory* directory, const QString& computerDn ) +{ + auto displayNameAttribute = directory->computerDisplayNameAttribute(); + if( displayNameAttribute.isEmpty() ) + { + displayNameAttribute = QStringLiteral("cn"); + } + + auto hostNameAttribute = directory->computerHostNameAttribute(); + if( hostNameAttribute.isEmpty() ) + { + hostNameAttribute = QStringLiteral("cn"); + } + + QStringList computerAttributes{ displayNameAttribute, hostNameAttribute }; + + auto macAddressAttribute = directory->computerMacAddressAttribute(); + if( macAddressAttribute.isEmpty() == false ) + { + computerAttributes.append( macAddressAttribute ); + } + + computerAttributes.removeDuplicates(); + + const auto computers = directory->client().queryObjects( computerDn, computerAttributes, + directory->computersFilter(), LdapClient::Scope::Base ); + if( computers.isEmpty() == false ) + { + const auto& computerDn = computers.firstKey(); + const auto& computer = computers.first(); + const auto displayName = computer[displayNameAttribute].value( 0 ); + const auto hostName = computer[hostNameAttribute].value( 0 ); + const auto macAddress = ( macAddressAttribute.isEmpty() == false ) ? computer[macAddressAttribute].value( 0 ) : QString(); + + return NetworkObject( NetworkObject::Type::Host, displayName, hostName, macAddress, computerDn ); + } + + return NetworkObject( NetworkObject::Type::None ); +} diff --git a/plugins/ldap/common/LdapNetworkObjectDirectory.h b/plugins/ldap/common/LdapNetworkObjectDirectory.h new file mode 100644 index 0000000..b3b9e2a --- /dev/null +++ b/plugins/ldap/common/LdapNetworkObjectDirectory.h @@ -0,0 +1,50 @@ +/* + * LdapNetworkObjectDirectory.h - provides a NetworkObjectDirectory for LDAP + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "LdapDirectory.h" +#include "NetworkObjectDirectory.h" + +class LDAP_COMMON_EXPORT LdapNetworkObjectDirectory : public NetworkObjectDirectory +{ + Q_OBJECT +public: + LdapNetworkObjectDirectory( const LdapConfiguration& ldapConfiguration, QObject* parent ); + + NetworkObjectList queryObjects( NetworkObject::Type type, + NetworkObject::Attribute attribute, const QVariant& value ) override; + NetworkObjectList queryParents( const NetworkObject& childId ) override; + + static NetworkObject computerToObject( LdapDirectory* directory, const QString& computerDn ); + +private: + void update() override; + void updateLocation( const NetworkObject& locationObject ); + + NetworkObjectList queryLocations( NetworkObject::Attribute attribute, const QVariant& value ); + NetworkObjectList queryHosts( NetworkObject::Attribute attribute, const QVariant& value ); + + LdapDirectory m_ldapDirectory; +}; diff --git a/plugins/ldap/common/application-x-kexi-connectiondata.png b/plugins/ldap/common/application-x-kexi-connectiondata.png new file mode 100644 index 0000000000000000000000000000000000000000..de182435638490eed3ca58504f89d615bf3a4a53 GIT binary patch literal 2255 zcmdr~i8s{i8~-wtku?$OTBeXC6j{fedc!5I7Kx-zzjvQDC3%C2ZeajjXeC0!&- z_Ul#%lYPlnS&N8Ve&73ZPN$=D`U8HSbKd9qJkR@iKJWLu?>7dGGUMhH>a$2s{S|$Hs{>_D;~u~@9Ftu0voNN_kDfj|JazL5jvPx{~# z2>kzGV`BqmFz$Eu%WwSW!V^(IDmx}!p45+FbDJq7dHJ)|loc9-#o?`O2zK^F2S*oIH+QnfrC&WS zdsBRT{R601g02OJgoQ`YBBP>j-io;s8yBCDn3SAy_g-q+eR_Jv@7X!IdHIhD3X6(M zN-HWKS3UW&y5?zJeM4i@^On}O_KwcKx?a5O?tR_YKfru5I5a#mIyOErIrVmWW_E7= z-TQ^brR9~?kLw$oU$(Zt?)t<-@DhE`31=i@sDYRC&Sm5NN*i5lJYRh{4O@ShH zP8xM}X=!CznNC-|yEVJ#>jQYZxBU6pI5|1Hl%Oe_atYR?en%CqVdDCe7Vo+Z1v5*wWW^o7Ks$&2Gu^SKIdTJ{8sRsA z=$o(ww*R*Mhj|1!mePd`5I(}VkSe{sk`Xwk+n&9giTK>1)3C_X{`BU>X1yao)2Sfrs6sV~XGZ=H+H-fXVPA}-TUV_K(Y^%^p%0~_R-bEjNa;f;M%b~0o$I= zyiZ2Z(0t}jl+kROX{QQ=6&@lcv{cv((SA#l$D>E%Z$v-RDy0&&BLLsT2_)&|XPMiT zDx2n5V;CkET_a?`?{%h8x7zC zSoHypRXAi&C&^WjMfYI=vGJkHHU3zh1-`&_~2jn_R@L%QVb2z%JGLu zPCm2E;lzd4vR<(>Fw}B=N8_%+>AK~lq2LwBh<25LJLYk{U>&o^ zJwJQB?_*O>xP91ZBSw{EY*EPI&D{0aQy+!{TZ|5qvtQZT``5Bht}ieuA??8N;v zu~#4bVTsQO!3_&c;iZUMUW+!H(UqH0tWnw@bE+)_xs6v2MXO3`3EN3!83V`cEDZn- zfMxd%KxI#rr8#Lz?q7dGfA(IH(4NIbwB9*4y$V4R>Nnu-9O1km0tLz?ngX~0{H>Bo&9s;fF2vtWsHA2mhqA0z!69X5}Dx4gd&25{pBJ#lYzV5+_&br0JZ| z%KNmc?u4smn$k+r_AjZo(KPm@%BM@T7MN1&`eIckYOR&5?uRL@c7Uojkv09KT6kn3 zYo1fpW=gGvthSOgR#-+=nPE&;Tc{c{MP;U_&18**s&Z2etaoFzg`%=hU=8eb%iMZT zldQ5*T_ZTttx;JiYF7u=Q{V(OOyMis)Dz+{x@2(Le@YLSqOJ-X-L9$X_D*HOfiUn~ z;huW(-`Bs7-+A9}{vXeHx&0FS&ceK8b_zfsQC?`w2Uy=3IKSC*fUdkZ0<(>WX1cotPYch`R*Lul^?K=)T_O=#t&nef@sEK5aH`wRLnHS}m1l7`Yo3 zPZ+y%&p8Im+uF_@PY(C2_3VvJ#JeXQHOFI?2{$7EfFhUZSLy|VZ-bx!KOpuULU!Fm z>W$(x64Dkh*8IIc6cPeV!Y`0rctFdI4%MT404s}!l7%7k&(H)3BL*}!%e^7uOrYFP z2B$cnv5b!>h(k+K7J&R%a6%@J1$9R()h`R6;qLjR979}Slma+6rzl8n=H zOENkzAqfPa8Ofi%GG$&4Z|?=?FVi|WX^xJ9YT9(!13T?iDxJlO)l2s5sB0zdK8ih` z_BhL=4d(HTcDr;>?WYlnGxb}JF_a4;6rxVK&7@5oXc?A2kMIIxmn)HX9@KN%_?ZSa zE4NdM?xR2ZMDd|R`jw)3UJhR2rLXN-8riU8neheBvH8N&3aHp}rGI*Hjm+dr+P2}L zwlHY4x-bN<&?tg!ckiDt_5_3-A`$m5TDX;i?oEygA$NenB;I;3dA{Y>NFed+ka~dx z)-pQ4;T3`)u#Y$qfUKL0&Zhh5*RaB7H|bs`^bv|K$`1e7hZ&f>MXHNrq>Ff+q=k%h zykbKZQNkCt^T@V_wCdaWx``E8zxBhgmkUExlNp9Lmcp=)J!Z?Cg;G_m!rl`ThK zv_Qu8aCC!ML~MNBRC4`V_e$*C2?7tYTq-T9srn4vEUpOray*KcKFBC7x)vIRE)t&( zjCaiB;j9e-hf;MH1HHyoEn%J6;pm=8IS*&Ihabi|QVeCx10{i%t!ZClAB|KR$^<$l z_R(v+p)6lVI6N@kWPmQ{Qm|sjS_4$$tO|TP;+%D;U3@Wz-^xv#S{TX!aEvc3V>Ltt PxPCh(ogJ@^OF#H8%~`VL literal 0 HcmV?d00001 diff --git a/plugins/ldap/common/computer.png b/plugins/ldap/common/computer.png new file mode 100644 index 0000000000000000000000000000000000000000..5022c4b1291da7e874ba8f6f0dc6da9593b5a5c1 GIT binary patch literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=EX7WqAsj$Z!;#Vf4nJ zn8$)Jqhx~{kij5X;u=vBoS#-wo>-L1P+nfHmzkGcoSayYs+V7sKKq@G6j0GxPZ!6K ziaBp@I&w8T2)G0a7uov1W}I9rCg}KF`q|#CPLW@DobKm{W+5x!d(Zl>HvC8KFU|m<60@4~+soT~?%Dd;GlI1H+2@D@jRF&8^|hH!9j+(yEr+qAXP8FD1G)j8!4b7wg8_H zS0L@{>pNloYD*xCu_VYZn8D%MjWi%f$J50zL}Oxd!UC}${A^zmYZxL=_&0opUXO@geCws5@jRF&8^|hH!9j+(yEr+qAXP8FD1G)j8!4b7wg8_H zS0L@{>pNloYD*xCu_VYZn8D%MjWi%f*VDx@L}Oxdf&$Zr|Lo2D6_Nr=StJy;wn)fJ r9AG)hlk`UE*xAEZ;$4nt3NkRP5Z~^(+@UufsFA_b)z4*}Q$iB}|CTyd literal 0 HcmV?d00001 diff --git a/plugins/ldap/common/folder-stash.png b/plugins/ldap/common/folder-stash.png new file mode 100644 index 0000000000000000000000000000000000000000..e09a87ac0edbc4c4a37bab9dad246cff7e4719b9 GIT binary patch literal 388 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?I3?vN&YJLDI=3*z$5DpHG+YkL80J)q69+AZi z3}rGP%((ne#VeqoWQl7;NpOBzNqJ&XDnogBxn5>oc5!lIL8@MUQTpt6Hc~)E(g8jp zu0R?HLPJA6Jw1I + + user-group-properties.png + computer.png + configure.png + distribute-vertical-margin.png + application-x-kexi-connectiondata.png + attribute.png + folder-stash.png + + diff --git a/plugins/ldap/common/user-group-properties.png b/plugins/ldap/common/user-group-properties.png new file mode 100644 index 0000000000000000000000000000000000000000..810cf8f698d0256b6009165de1efc0817230321c GIT binary patch literal 888 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!n2Vh}LpV4%Za?&Y0OWEOctjR6 zFqp@JFr#FH8<4>uS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjT8d|qildr zh$~Pr5HvJ2gocJXI5?!Gr2!dkZf<^leqLT)K|w)4hL4YrudlDSw>OXh6a z1407DfdI$`0w4n-j?92aLBt_!I0;vVrUtGUZZbpxTs@jNTs@jOaEtJZqbY-M&m`sF z0ftUoNswPK10xeN3o9G9xP+vXw4#!#n!2WiwT+#ZuYW*bP;f|SSa@tgW>$GsbzMhi zS9j06g^Lz1S-Ncbnzb7??LKtm=&=(gPo2MX_4qGVuR&x&7pgi`PTN`K&UKkeId78VTS?~p z_db2SzIywTA7uJId^hRW#eiqe{DZW)%APX(uvk}rZfj%nX|9l;3^kT(71Q@Iz1QX{ zX13U~O7J>g<7=+XtTsXCk{>K>>QSHIqwze5>11T<4&I!TAGvlc6_6IGS|O0H@g+*J kO!G=`hb`CFP_4hm<-Z6h${zBz`V30zopr0DJIS?f?J) literal 0 HcmV?d00001 diff --git a/plugins/ldap/kldap/CMakeLists.txt b/plugins/ldap/kldap/CMakeLists.txt new file mode 100644 index 0000000..19e08bc --- /dev/null +++ b/plugins/ldap/kldap/CMakeLists.txt @@ -0,0 +1,43 @@ +INCLUDE(BuildVeyonPlugin) + +# libraries and functions for LDAP support +FIND_PACKAGE(Ldap REQUIRED) +FIND_PACKAGE(Sasl2 REQUIRED) + +SET(CMAKE_REQUIRED_INCLUDES lber.h ldap.h) +SET(CMAKE_REQUIRED_LIBRARIES ${Ldap_LIBRARIES} ${Sasl2_LIBRARIES} ${OPENSSL_LIBRARIES}) +CHECK_FUNCTION_EXISTS(ldap_start_tls_s HAVE_LDAP_START_TLS_S) +CHECK_FUNCTION_EXISTS(ldap_initialize HAVE_LDAP_INITIALIZE) +CHECK_FUNCTION_EXISTS(ber_memfree HAVE_BER_MEMFREE) +CHECK_FUNCTION_EXISTS(ldap_unbind_ext HAVE_LDAP_UNBIND_EXT) +CHECK_FUNCTION_EXISTS(ldap_extended_operation HAVE_LDAP_EXTENDED_OPERATION) +CHECK_FUNCTION_EXISTS(ldap_extended_operation_s HAVE_LDAP_EXTENDED_OPERATION_S) +CHECK_SYMBOL_EXISTS(ldap_extended_operation ldap.h HAVE_LDAP_EXTENDED_OPERATION_PROTOTYPE) +CHECK_SYMBOL_EXISTS(ldap_extended_operation_s ldap.h HAVE_LDAP_EXTENDED_OPERATION_S_PROTOTYPE) +CHECK_INCLUDE_FILES(ldap.h HAVE_LDAP_H) +SET(LDAP_FOUND TRUE) + +set(kldap_SOURCE_DIR ${CMAKE_SOURCE_DIR}/3rdparty/kldap/src/core) +configure_file(${kldap_SOURCE_DIR}/../kldap_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kldap_config.h) + +SET(kldap_SOURCES + ${kldap_SOURCE_DIR}/ber.cpp + ${kldap_SOURCE_DIR}/ldif.cpp + ${kldap_SOURCE_DIR}/ldapurl.cpp + ${kldap_SOURCE_DIR}/ldapserver.cpp + ${kldap_SOURCE_DIR}/ldapobject.cpp + ${kldap_SOURCE_DIR}/ldapconnection.cpp + ${kldap_SOURCE_DIR}/ldapoperation.cpp + ${kldap_SOURCE_DIR}/ldapcontrol.cpp + ${kldap_SOURCE_DIR}/ldapdn.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/KLdapIntegration.cpp +) + +ADD_LIBRARY(kldap-light SHARED ${kldap_SOURCES}) +TARGET_LINK_LIBRARIES(kldap-light Qt5::Core ${Ldap_LIBRARIES} ${Sasl2_LIBRARIES}) +TARGET_INCLUDE_DIRECTORIES(kldap-light PRIVATE ${Ldap_INCLUDE_DIRS}) +TARGET_INCLUDE_DIRECTORIES(kldap-light PUBLIC ${kldap_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) +SET_TARGET_PROPERTIES(kldap-light PROPERTIES LINK_FLAGS "-Wl,-no-undefined") +if(NOT WITH_CORE_ONLY) +INSTALL(TARGETS kldap-light DESTINATION ${VEYON_LIB_DIR}) +endif() diff --git a/plugins/ldap/kldap/KLdapIntegration.cpp b/plugins/ldap/kldap/KLdapIntegration.cpp new file mode 100644 index 0000000..35402e0 --- /dev/null +++ b/plugins/ldap/kldap/KLdapIntegration.cpp @@ -0,0 +1,27 @@ +/* + * KLdapIntegration.cpp - definition of logging category for kldap + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "ldap_debug.h" + +Q_LOGGING_CATEGORY(LDAP_LOG, "KLDAP"); diff --git a/plugins/ldap/kldap/KLocalizedString b/plugins/ldap/kldap/KLocalizedString new file mode 100644 index 0000000..2ba476a --- /dev/null +++ b/plugins/ldap/kldap/KLocalizedString @@ -0,0 +1 @@ +#include "klocalizedstring.h" diff --git a/plugins/ldap/kldap/kldap_export.h b/plugins/ldap/kldap/kldap_export.h new file mode 100644 index 0000000..91de1d5 --- /dev/null +++ b/plugins/ldap/kldap/kldap_export.h @@ -0,0 +1,28 @@ +/* + * kldap_export.h - definition of symbol visibility macros for kldap + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#define KLDAP_EXPORT __attribute__((visibility("default"))) +#define KLDAP_NO_EXPORT __attribute__((visibility("hidden"))) diff --git a/plugins/ldap/kldap/klocalizedstring.h b/plugins/ldap/kldap/klocalizedstring.h new file mode 100644 index 0000000..c232ff0 --- /dev/null +++ b/plugins/ldap/kldap/klocalizedstring.h @@ -0,0 +1,49 @@ +/* + * klocalizedstring.h - dummy replacements for i18n() functions of KDE + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +static inline QString i18n(const char* str) +{ + return QObject::tr(str); +} + +template +static inline QString i18n(const char* str, T arg) +{ + return QObject::tr(str).arg(arg); +} + +template +static inline QString i18np(const char* singular, const char* plural, T arg) +{ + if( arg > 1 ) + { + return QObject::tr(plural).arg(arg); + } + return QObject::tr(singular).arg(arg); +} + diff --git a/plugins/ldap/kldap/ldap_debug.h b/plugins/ldap/kldap/ldap_debug.h new file mode 100644 index 0000000..118b7f1 --- /dev/null +++ b/plugins/ldap/kldap/ldap_debug.h @@ -0,0 +1,31 @@ +/* + * ldap_debug.h - declaration of logging category for kldap + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(LDAP_LOG); + diff --git a/plugins/platform/CMakeLists.txt b/plugins/platform/CMakeLists.txt new file mode 100644 index 0000000..3500ea4 --- /dev/null +++ b/plugins/platform/CMakeLists.txt @@ -0,0 +1,7 @@ +IF(VEYON_BUILD_WIN32) +ADD_SUBDIRECTORY(windows) +ENDIF() + +IF(VEYON_BUILD_LINUX) +ADD_SUBDIRECTORY(linux) +ENDIF() diff --git a/plugins/platform/common/LogonHelper.cpp b/plugins/platform/common/LogonHelper.cpp new file mode 100644 index 0000000..595f693 --- /dev/null +++ b/plugins/platform/common/LogonHelper.cpp @@ -0,0 +1,69 @@ +/* + * LogonHelper.cpp - implementation of LogonHelper class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "LogonHelper.h" +#include "PersistentLogonCredentials.h" +#include "PlatformPluginInterface.h" +#include "PlatformUserFunctions.h" +#include "ServiceDataManager.h" + + +LogonHelper::LogonHelper( QObject* parent ) : + QObject( parent ) +{ + connect( VeyonCore::instance(), &VeyonCore::applicationLoaded, + this, &LogonHelper::checkPendingLogonTasks ); +} + + + +bool LogonHelper::prepare( const QString& username, const Password& password ) +{ + if( VeyonCore::platform().userFunctions().isAnyUserLoggedOn() ) + { + vInfo() << "Skipping user logon as a user is already logged on"; + return false; + } + + return PersistentLogonCredentials::write( username, password ); +} + + + +void LogonHelper::checkPendingLogonTasks() +{ + if( ServiceDataManager::serviceDataTokenFromEnvironment().isEmpty() == false && + VeyonCore::platform().userFunctions().isAnyUserLoggedOn() == false ) + { + vDebug() << "Reading logon credentials"; + QString username; + Password password; + if( PersistentLogonCredentials::read( &username, &password ) ) + { + PersistentLogonCredentials::clear(); + + VeyonCore::platform().userFunctions().performLogon( username, password ); + } + } +} diff --git a/plugins/platform/common/LogonHelper.h b/plugins/platform/common/LogonHelper.h new file mode 100644 index 0000000..c0c0aa8 --- /dev/null +++ b/plugins/platform/common/LogonHelper.h @@ -0,0 +1,44 @@ +/* + * LogonHelper.h - declaration of LogonHelper class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CryptoCore.h" + +// clazy:exclude=copyable-polymorphic + +class LogonHelper : public QObject +{ + Q_OBJECT +public: + using Password = CryptoCore::SecureArray; + + LogonHelper( QObject* parent = nullptr ); + + bool prepare( const QString& username, const Password& password ); + +private: + void checkPendingLogonTasks(); + +}; diff --git a/plugins/platform/common/PersistentLogonCredentials.cpp b/plugins/platform/common/PersistentLogonCredentials.cpp new file mode 100644 index 0000000..f4a1eff --- /dev/null +++ b/plugins/platform/common/PersistentLogonCredentials.cpp @@ -0,0 +1,92 @@ +/* + * PersistentLogonCredentials.cpp - implementation of PersistentLogonCredentials class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "PersistentLogonCredentials.h" +#include "ServiceDataManager.h" +#include "VariantStream.h" + + +bool PersistentLogonCredentials::read( QString* username, Password* password ) +{ + if( username == nullptr || password == nullptr ) + { + vCritical() << "Invalid input pointers"; + return false; + } + + auto logonData = ServiceDataManager::read( ServiceDataManager::serviceDataTokenFromEnvironment() ); + if( logonData.isEmpty() ) + { + vCritical() << "Empty data"; + return false; + } + + QBuffer logonDataBuffer( &logonData ); + if( logonDataBuffer.open( QBuffer::ReadOnly ) == false ) + { + vCritical() << "Failed to open buffer"; + return false; + } + + VariantStream stream( &logonDataBuffer ); + *username = stream.read().toString(); + *password = VeyonCore::cryptoCore().decryptPassword( stream.read().toString() ); + + return username->isEmpty() == false && password->isEmpty() == false; +} + + + +bool PersistentLogonCredentials::write( const QString& username, const Password& password ) +{ + QBuffer logonDataBuffer; + if( logonDataBuffer.open( QBuffer::WriteOnly ) == false ) + { + vCritical() << "Failed to open buffer"; + return false; + } + + VariantStream stream( &logonDataBuffer ); + stream.write( username ); + stream.write( VeyonCore::cryptoCore().encryptPassword( password ) ); + + if( ServiceDataManager::write( ServiceDataManager::serviceDataTokenFromEnvironment(), + logonDataBuffer.data() ) == false ) + { + vCritical() << "Failed to write persistent service data"; + return false; + } + + return true; +} + + + +bool PersistentLogonCredentials::clear() +{ + return ServiceDataManager::write( ServiceDataManager::serviceDataTokenFromEnvironment(), {} ); +} + diff --git a/plugins/platform/common/PersistentLogonCredentials.h b/plugins/platform/common/PersistentLogonCredentials.h new file mode 100644 index 0000000..4161f79 --- /dev/null +++ b/plugins/platform/common/PersistentLogonCredentials.h @@ -0,0 +1,40 @@ +/* + * PersistentLogonCredentials.h - declaration of PersistentLogonCredentials class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CryptoCore.h" + +// clazy:exclude=copyable-polymorphic + +class PersistentLogonCredentials +{ +public: + using Password = CryptoCore::SecureArray; + + static bool read( QString* username, Password* password ); + static bool write( const QString& username, const Password& password ); + static bool clear(); + +}; diff --git a/plugins/platform/common/PlatformSessionManager.cpp b/plugins/platform/common/PlatformSessionManager.cpp new file mode 100644 index 0000000..25d16fb --- /dev/null +++ b/plugins/platform/common/PlatformSessionManager.cpp @@ -0,0 +1,159 @@ +/* + * PlatformSessionManager.cpp - implementation of PlatformSessionManager class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include "PlatformSessionManager.h" +#include "VariantArrayMessage.h" +#include "VeyonConfiguration.h" + + +PlatformSessionManager::PlatformSessionManager( QObject* parent ) : + QThread( parent ), + m_multiSession( VeyonCore::config().multiSessionModeEnabled() ), + m_maximumSessionCount( VeyonCore::config().maximumSessionCount() ) +{ + vDebug(); + + start(); +} + + + +PlatformSessionManager::~PlatformSessionManager() +{ + vDebug(); + + quit(); + wait(); +} + + + +void PlatformSessionManager::run() +{ + if( m_multiSession ) + { + auto server = new QLocalServer; + server->setSocketOptions( QLocalServer::WorldAccessOption ); + server->listen( serverName() ); + + connect( server, &QLocalServer::newConnection, server, [this, server]() { + auto connection = server->nextPendingConnection(); + connect( connection, &QLocalSocket::disconnected, connection, &QLocalSocket::deleteLater ); + + m_mutex.lock(); + VariantArrayMessage(connection).write( m_sessions ).send(); + m_mutex.unlock(); + + connection->flush(); + connection->disconnectFromServer(); + } ); + } + + QThread::run(); +} + +PlatformSessionManager::SessionId PlatformSessionManager::openSession( const PlatformSessionId& platformSessionId ) +{ + QMutexLocker l( &m_mutex ); + + const auto sessionIds = m_sessions.values(); + + for( SessionId i = 0; i < m_maximumSessionCount; ++i ) + { + if( sessionIds.contains( i ) == false ) + { + vDebug() << "Opening session" << i << "for platform session" << platformSessionId; + m_sessions[platformSessionId] = i; + + return i; + } + } + + return PlatformSessionFunctions::InvalidSessionId; +} + + + +void PlatformSessionManager::closeSession( const PlatformSessionId& platformSessionId ) +{ + QMutexLocker l( &m_mutex ); + + vDebug() << "Closing session" << m_sessions.take( platformSessionId ).toInt() << "for platform session" << platformSessionId; +} + + + +PlatformSessionManager::SessionId PlatformSessionManager::resolveSessionId( const PlatformSessionId& platformSessionId ) +{ + if( VeyonCore::component() == VeyonCore::Component::Service ) + { + return PlatformSessionFunctions::DefaultSessionId; + } + + QLocalSocket socket; + socket.connectToServer( serverName(), QLocalSocket::ReadOnly ); + if( socket.waitForConnected( ServerConnectTimeout ) == false ) + { + if( VeyonCore::component() != VeyonCore::Component::CLI && + VeyonCore::component() != VeyonCore::Component::Configurator ) + { + vCritical() << "could not read session map"; + } + + return PlatformSessionFunctions::InvalidSessionId; + } + + if( waitForMessage( &socket ) ) + { + VariantArrayMessage message( &socket ); + message.receive(); + return message.read().toMap().value( platformSessionId, PlatformSessionFunctions::InvalidSessionId ).toInt(); + } + + vCritical() << "could not receive session map"; + + return PlatformSessionFunctions::InvalidSessionId; +} + + + +bool PlatformSessionManager::waitForMessage(QLocalSocket* socket) +{ + // wait for acknowledge + QElapsedTimer messageTimeoutTimer; + messageTimeoutTimer.start(); + + VariantArrayMessage inMessage( socket ); + while( messageTimeoutTimer.elapsed() < MessageReadTimeout && + inMessage.isReadyForReceive() == false ) + { + socket->waitForReadyRead( SocketWaitTimeout ); + } + + return inMessage.isReadyForReceive(); +} diff --git a/plugins/platform/common/PlatformSessionManager.h b/plugins/platform/common/PlatformSessionManager.h new file mode 100644 index 0000000..9bceb10 --- /dev/null +++ b/plugins/platform/common/PlatformSessionManager.h @@ -0,0 +1,78 @@ +/* + * PlatformSessionManager.h - declaration of PlatformSessionManager class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "PlatformSessionFunctions.h" + +class QLocalSocket; + +// clazy:excludeall=copyable-polymorphic + +class PlatformSessionManager : public QThread +{ +public: + using SessionId = PlatformSessionFunctions::SessionId; + using PlatformSessionId = QString; + + PlatformSessionManager( QObject* parent = nullptr ); + ~PlatformSessionManager() override; + + SessionId openSession( const PlatformSessionId& platformSessionId ); + void closeSession( const PlatformSessionId& platformSessionId ); + + static SessionId resolveSessionId( const PlatformSessionId& platformSessionId ); + + bool multiSession() const + { + return m_multiSession; + } + + using SessionMap = QMap; + +protected: + void run() override; + +private: + static constexpr auto ServerConnectTimeout = 5000; + static constexpr auto SocketWaitTimeout = 1000; + static constexpr auto MessageReadTimeout = 10000; + + static QString serverName() + { + return QStringLiteral("VeyonSessionManager"); + } + + static bool waitForMessage( QLocalSocket* socket ); + + const bool m_multiSession; + const int m_maximumSessionCount; + + QMutex m_mutex; + QVariantMap m_sessions; + +}; diff --git a/plugins/platform/common/ServiceDataManager.cpp b/plugins/platform/common/ServiceDataManager.cpp new file mode 100644 index 0000000..5473dd5 --- /dev/null +++ b/plugins/platform/common/ServiceDataManager.cpp @@ -0,0 +1,206 @@ +/* + * ServiceDataManager.cpp - implementation of ServiceDataManager class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "ServiceDataManager.h" + + +ServiceDataManager::ServiceDataManager( QObject* parent ) : + QThread( parent ), + m_token( CryptoCore::generateChallenge().toHex() ) +{ + vDebug(); + + start(); +} + + + +ServiceDataManager::~ServiceDataManager() +{ + vDebug(); + + quit(); + wait(); +} + + + +QByteArray ServiceDataManager::read( const Token& token ) +{ + QLocalSocket socket; + socket.connectToServer( serverName() ); + if( socket.waitForConnected() == false ) + { + vCritical() << "connection timed out"; + return {}; + } + + VariantArrayMessage outMessage( &socket ); + outMessage.write( token.toByteArray() ); + outMessage.write( static_cast( Command::ReadData ) ); + + sendMessage( &socket, outMessage ); + + if( waitForMessage( &socket ) == false ) + { + vCritical() << "no response"; + return {}; + } + + VariantArrayMessage response( &socket ); + response.receive(); + + return response.read().toByteArray(); +} + + + +bool ServiceDataManager::write( const Token& token, const Data& data ) +{ + QLocalSocket socket; + socket.connectToServer( serverName() ); + + if( socket.waitForConnected() == false ) + { + vCritical() << "connection timed out"; + return false; + } + + VariantArrayMessage outMessage( &socket ); + outMessage.write( token.toByteArray() ); + outMessage.write( static_cast( Command::WriteData ) ); + outMessage.write( data.toByteArray() ); + + sendMessage( &socket, outMessage ); + + return waitForMessage( &socket ); +} + + + +ServiceDataManager::Token ServiceDataManager::serviceDataTokenFromEnvironment() +{ + return QProcessEnvironment::systemEnvironment().value( + QLatin1String( serviceDataTokenEnvironmentVariable() ) ).toUtf8(); +} + + + +void ServiceDataManager::run() +{ + m_server = new QLocalServer; + m_server->setSocketOptions( QLocalServer::UserAccessOption ); + + if( m_server->listen( serverName() ) == false ) + { + vCritical() << "can't listen" << m_server->errorString(); + return; + } + + connect( m_server, &QLocalServer::newConnection, m_server, [this]() { acceptConnection(); } ); + + QThread::run(); +} + + + +bool ServiceDataManager::waitForMessage( QLocalSocket* socket ) +{ + // wait for acknowledge + QElapsedTimer messageTimeoutTimer; + messageTimeoutTimer.start(); + + VariantArrayMessage inMessage( socket ); + while( messageTimeoutTimer.elapsed() < MessageReadTimeout && + inMessage.isReadyForReceive() == false ) + { + socket->waitForReadyRead( SocketWaitTimeout ); + } + + return inMessage.isReadyForReceive(); +} + + + +void ServiceDataManager::sendMessage( QLocalSocket* socket, VariantArrayMessage& message ) +{ + message.send(); + socket->flush(); + socket->waitForBytesWritten(); +} + + + +void ServiceDataManager::acceptConnection() +{ + auto socket = m_server->nextPendingConnection(); + + connect( socket, &QLocalSocket::readyRead, + socket, [this, socket]() { handleConnection( socket ); } ); +} + + + +void ServiceDataManager::handleConnection( QLocalSocket* socket ) +{ + VariantArrayMessage inMessage( socket ); + + if( inMessage.isReadyForReceive() && inMessage.receive() ) + { + const auto token = inMessage.read().toByteArray(); + if( token != m_token.toByteArray() ) + { + vCritical() << "Invalid token"; + socket->close(); + return; + } + + const auto command = static_cast( inMessage.read().toInt() ); + + if( command == Command::WriteData ) + { + m_data = inMessage.read().toByteArray(); + + VariantArrayMessage acknowledge( socket ); + acknowledge.send(); + } + else if( command == Command::ReadData ) + { + VariantArrayMessage response( socket ); + response.write( m_data.toByteArray() ); + response.send(); + } + else + { + vCritical() << "unknown command"; + socket->close(); + } + socket->flush(); + } +} diff --git a/plugins/platform/common/ServiceDataManager.h b/plugins/platform/common/ServiceDataManager.h new file mode 100644 index 0000000..0d78fac --- /dev/null +++ b/plugins/platform/common/ServiceDataManager.h @@ -0,0 +1,90 @@ +/* + * ServiceDataManager.h - header file for ServiceDataManager class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "CryptoCore.h" +#include "VariantArrayMessage.h" + +class QLocalServer; +class QLocalSocket; + +class ServiceDataManager : public QThread +{ + Q_OBJECT +public: + enum class Command { + NoCommand, + ReadData, + WriteData, + }; + Q_ENUM(Command) + + using Data = CryptoCore::PlaintextPassword; + using Token = CryptoCore::PlaintextPassword; + + ServiceDataManager( QObject* parent = nullptr ); + ~ServiceDataManager() override; + + const Token& token() const + { + return m_token; + } + + static QByteArray read( const Token& token ); + static bool write( const Token& token, const Data& data ); + + static const char* serviceDataTokenEnvironmentVariable() + { + return "VEYON_SERVICE_DATA_TOKEN"; + } + + static Token serviceDataTokenFromEnvironment(); + +protected: + void run() override; + +private: + static constexpr auto SocketWaitTimeout = 1000; + static constexpr auto MessageReadTimeout = 10000; + + static bool waitForMessage( QLocalSocket* socket ); + static void sendMessage( QLocalSocket* socket, VariantArrayMessage& message ); + + void acceptConnection(); + void handleConnection( QLocalSocket* socket ); + + static QString serverName() + { + return QStringLiteral("VeyonServiceDataManager"); + } + + QLocalServer* m_server{nullptr}; + + Token m_token; + Data m_data; + +}; diff --git a/plugins/platform/linux/CMakeLists.txt b/plugins/platform/linux/CMakeLists.txt new file mode 100644 index 0000000..7ce1aa9 --- /dev/null +++ b/plugins/platform/linux/CMakeLists.txt @@ -0,0 +1,77 @@ +ADD_SUBDIRECTORY(auth-helper) + +FIND_PACKAGE(X11 REQUIRED) +FIND_PACKAGE(Qt5DBus REQUIRED) +FIND_PACKAGE(PkgConfig QUIET) +pkg_check_modules(procps REQUIRED libprocps) +pkg_check_modules(fakekey libfakekey) + +INCLUDE(BuildVeyonPlugin) + +if(NOT fakekey_FOUND) + set(libfakekey_SOURCES ${libfakekey_DIR}/src/libfakekey.c) + set_source_files_properties(${libfakekey_SOURCES} PROPERTIES + COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-pointer-sign" + SKIP_PRECOMPILE_HEADERS TRUE) +endif() + +build_veyon_plugin(linux-platform + LinuxPlatformPlugin.cpp + LinuxCoreFunctions.cpp + LinuxPlatformConfigurationPage.h + LinuxPlatformConfigurationPage.cpp + LinuxPlatformConfigurationPage.ui + LinuxFilesystemFunctions.cpp + LinuxInputDeviceFunctions.cpp + LinuxNetworkFunctions.cpp + LinuxServiceCore.cpp + LinuxServiceFunctions.cpp + LinuxSessionFunctions.cpp + LinuxUserFunctions.cpp + LinuxPlatformPlugin.h + LinuxPlatformConfiguration.h + LinuxCoreFunctions.h + LinuxDesktopIntegration.h + LinuxFilesystemFunctions.h + LinuxInputDeviceFunctions.h + LinuxKeyboardInput.h + LinuxKeyboardInput.cpp + LinuxKeyboardShortcutTrapper.h + LinuxNetworkFunctions.h + LinuxServiceCore.h + LinuxServiceFunctions.h + LinuxSessionFunctions.h + LinuxUserFunctions.h + linux.qrc + ../common/LogonHelper.h + ../common/LogonHelper.cpp + ../common/PersistentLogonCredentials.h + ../common/PersistentLogonCredentials.cpp + ../common/PlatformSessionManager.h + ../common/PlatformSessionManager.cpp + ../common/ServiceDataManager.h + ../common/ServiceDataManager.cpp + ${libfakekey_SOURCES} +) + +set_source_files_properties(LinuxCoreFunctions.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION TRUE) + +TARGET_INCLUDE_DIRECTORIES(linux-platform PRIVATE + ../common + ${libfakekey_DIR} + ${procps_INCLUDE_DIRS} +) + +TARGET_LINK_LIBRARIES(linux-platform + ${X11_LIBRARIES} + Qt5::DBus + ${procps_LDFLAGS} + ) + +IF(fakekey_FOUND) +TARGET_INCLUDE_DIRECTORIES(linux-platform PRIVATE ${fakekey_INCLUDE_DIRS}) +TARGET_LINK_LIBRARIES(linux-platform ${fakekey_LDFLAGS}) +ELSE() +TARGET_INCLUDE_DIRECTORIES(linux-platform PRIVATE ${libfakekey_DIR}) +TARGET_LINK_LIBRARIES(linux-platform ${X11_XTest_LIB}) +ENDIF() diff --git a/plugins/platform/linux/LinuxCoreFunctions.cpp b/plugins/platform/linux/LinuxCoreFunctions.cpp new file mode 100644 index 0000000..9add605 --- /dev/null +++ b/plugins/platform/linux/LinuxCoreFunctions.cpp @@ -0,0 +1,390 @@ +/* + * LinuxCoreFunctions.cpp - implementation of LinuxCoreFunctions class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "LinuxCoreFunctions.h" +#include "LinuxDesktopIntegration.h" +#include "LinuxUserFunctions.h" +#include "PlatformUserFunctions.h" + +#include +#include + + +bool LinuxCoreFunctions::applyConfiguration() +{ + return true; +} + + + +void LinuxCoreFunctions::initNativeLoggingSystem( const QString& appName ) +{ + Q_UNUSED(appName) +} + + + +void LinuxCoreFunctions::writeToNativeLoggingSystem( const QString& message, Logger::LogLevel loglevel ) +{ + Q_UNUSED(message) + Q_UNUSED(loglevel) +} + + + +void LinuxCoreFunctions::reboot() +{ + systemdLoginManager()->asyncCall( QStringLiteral("Reboot"), false ); + consoleKitManager()->asyncCall( QStringLiteral("Restart") ); + + if( isRunningAsAdmin() ) + { + for( const auto& file : { QStringLiteral("/sbin/reboot"), QStringLiteral("/usr/sbin/reboot") } ) + { + if( QFileInfo::exists( file ) ) + { + QProcess::startDetached( file, {} ); + return; + } + } + + QProcess::startDetached( QStringLiteral("reboot"), {} ); + } + else + { + kdeSessionManager()->asyncCall( QStringLiteral("logout"), + static_cast( LinuxDesktopIntegration::KDE::ShutdownConfirmNo ), + static_cast( LinuxDesktopIntegration::KDE::ShutdownTypeReboot ), + static_cast( LinuxDesktopIntegration::KDE::ShutdownModeForceNow ) ); + gnomeSessionManager()->asyncCall( QStringLiteral("RequestReboot") ); + mateSessionManager()->asyncCall( QStringLiteral("RequestReboot") ); + xfcePowerManager()->asyncCall( QStringLiteral("Reboot") ); + } +} + + + +void LinuxCoreFunctions::powerDown( bool installUpdates ) +{ + Q_UNUSED(installUpdates) + + systemdLoginManager()->asyncCall( QStringLiteral("PowerOff"), false ); + consoleKitManager()->asyncCall( QStringLiteral("Stop") ); + + if( isRunningAsAdmin() ) + { + for( const auto& file : { QStringLiteral("/sbin/poweroff"), QStringLiteral("/usr/sbin/poweroff") } ) + { + if( QFileInfo::exists( file ) ) + { + QProcess::startDetached( file, {} ); + return; + } + } + + QProcess::startDetached( QStringLiteral("poweroff"), {} ); + } + else + { + kdeSessionManager()->asyncCall( QStringLiteral("logout"), + static_cast( LinuxDesktopIntegration::KDE::ShutdownConfirmNo ), + static_cast( LinuxDesktopIntegration::KDE::ShutdownTypeHalt ), + static_cast( LinuxDesktopIntegration::KDE::ShutdownModeForceNow ) ); + gnomeSessionManager()->asyncCall( QStringLiteral("RequestShutdown") ); + mateSessionManager()->asyncCall( QStringLiteral("RequestShutdown") ); + xfcePowerManager()->asyncCall( QStringLiteral("Shutdown") ); + } +} + + + +void LinuxCoreFunctions::raiseWindow( QWidget* widget, bool stayOnTop ) +{ + widget->activateWindow(); + widget->raise(); + + if( stayOnTop ) + { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) + widget->setWindowFlag( Qt::WindowStaysOnTopHint, true ); +#else + widget->setWindowFlags( widget->windowFlags() | Qt::WindowStaysOnTopHint ); +#endif + } +} + + +void LinuxCoreFunctions::disableScreenSaver() +{ + auto display = XOpenDisplay( nullptr ); + + // query and disable screen saver + int interval; + int allowExposures; + XGetScreenSaver( display, &m_screenSaverTimeout, &interval, &m_screenSaverPreferBlanking, &allowExposures ); + XSetScreenSaver( display, 0, interval, 0, allowExposures ); + + // query and disable DPMS + int dummy; + if( DPMSQueryExtension( display, &dummy, &dummy ) ) + { + CARD16 powerLevel; + BOOL state; + if( DPMSInfo( display, &powerLevel, &state ) && state ) + { + m_dpmsEnabled = true; + DPMSDisable( display ); + } + else + { + m_dpmsEnabled = false; + } + + DPMSGetTimeouts( display, &m_dpmsStandbyTimeout, &m_dpmsSuspendTimeout, &m_dpmsOffTimeout ); + DPMSSetTimeouts( display, 0, 0, 0 ); + } + else + { + vWarning() << "DPMS extension not supported!"; + } + + XFlush( display ); + XCloseDisplay( display ); +} + + + +void LinuxCoreFunctions::restoreScreenSaverSettings() +{ + auto display = XOpenDisplay( nullptr ); + + // restore screensaver settings + int timeout; + int interval; + int preferBlanking; + int allowExposures; + XGetScreenSaver( display, &timeout, &interval, &preferBlanking, &allowExposures ); + XSetScreenSaver( display, m_screenSaverTimeout, interval, m_screenSaverPreferBlanking, allowExposures ); + + // restore DPMS settings + int dummy; + if( DPMSQueryExtension( display, &dummy, &dummy ) ) + { + if( m_dpmsEnabled ) + { + DPMSEnable( display ); + } + + DPMSSetTimeouts( display, m_dpmsStandbyTimeout, m_dpmsSuspendTimeout, m_dpmsOffTimeout ); + } + + XFlush( display ); + XCloseDisplay( display ); +} + + + +void LinuxCoreFunctions::setSystemUiState( bool enabled ) +{ + Q_UNUSED(enabled) +} + + + +QString LinuxCoreFunctions::activeDesktopName() +{ + return QString(); +} + + + +bool LinuxCoreFunctions::isRunningAsAdmin() const +{ + return getuid() == 0 || geteuid() == 0; +} + + + +bool LinuxCoreFunctions::runProgramAsAdmin( const QString& program, const QStringList& parameters ) +{ + const auto commandLine = QStringList( program ) + parameters; + + return QProcess::execute( QStringLiteral("pkexec"), commandLine ) == 0; +} + + + +bool LinuxCoreFunctions::runProgramAsUser( const QString& program, const QStringList& parameters, + const QString& username, const QString& desktop ) +{ + Q_UNUSED(desktop); + + class UserProcess : public QProcess // clazy:exclude=missing-qobject-macro + { + public: + explicit UserProcess( uid_t uid, QObject* parent = nullptr ) : + QProcess( parent ), + m_uid( uid ) + { + } + + void setupChildProcess() override + { + if( setuid( m_uid ) != 0 ) + { + qFatal( "Could not set UID for child process!" ); + } + } + + private: + const uid_t m_uid; + }; + + const auto uid = LinuxUserFunctions::userIdFromName( username ); + if( uid <= 0 ) + { + return false; + } + + auto process = new UserProcess( uid ); + QObject::connect( process, QOverload::of( &QProcess::finished ), &QProcess::deleteLater ); + process->start( program, parameters ); + + return true; +} + + + +QString LinuxCoreFunctions::genericUrlHandler() const +{ + return QStringLiteral( "xdg-open" ); +} + + + +/*! Returns DBus interface for session manager of KDE desktop */ +LinuxCoreFunctions::DBusInterfacePointer LinuxCoreFunctions::kdeSessionManager() +{ + return DBusInterfacePointer::create( QStringLiteral("org.kde.ksmserver"), + QStringLiteral("/KSMServer"), + QStringLiteral("org.kde.KSMServerInterface"), + QDBusConnection::sessionBus() ); +} + + + +/*! Returns DBus interface for session manager of Gnome desktop */ +LinuxCoreFunctions::DBusInterfacePointer LinuxCoreFunctions::gnomeSessionManager() +{ + return DBusInterfacePointer::create( QStringLiteral("org.gnome.SessionManager"), + QStringLiteral("/org/gnome/SessionManager"), + QStringLiteral("org.gnome.SessionManager"), + QDBusConnection::sessionBus() ); +} + + + +/*! Returns DBus interface for session manager of Mate desktop */ +LinuxCoreFunctions::DBusInterfacePointer LinuxCoreFunctions::mateSessionManager() +{ + return DBusInterfacePointer::create( QStringLiteral("org.mate.SessionManager"), + QStringLiteral("/org/mate/SessionManager"), + QStringLiteral("org.mate.SessionManager"), + QDBusConnection::sessionBus() ); +} + + + +/*! Returns DBus interface for Xfce/LXDE power manager */ +LinuxCoreFunctions::DBusInterfacePointer LinuxCoreFunctions::xfcePowerManager() +{ + return DBusInterfacePointer::create( QStringLiteral("org.freedesktop.PowerManagement"), + QStringLiteral("/org/freedesktop/PowerManagement"), + QStringLiteral("org.freedesktop.PowerManagement"), + QDBusConnection::sessionBus() ); +} + + + +/*! Returns DBus interface for systemd login manager */ +LinuxCoreFunctions::DBusInterfacePointer LinuxCoreFunctions::systemdLoginManager() +{ + return DBusInterfacePointer::create( QStringLiteral("org.freedesktop.login1"), + QStringLiteral("/org/freedesktop/login1"), + QStringLiteral("org.freedesktop.login1.Manager"), + QDBusConnection::systemBus() ); +} + + + +/*! Returns DBus interface for ConsoleKit manager */ +LinuxCoreFunctions::DBusInterfacePointer LinuxCoreFunctions::consoleKitManager() +{ + return DBusInterfacePointer::create( QStringLiteral("org.freedesktop.ConsoleKit"), + QStringLiteral("/org/freedesktop/ConsoleKit/Manager"), + QStringLiteral("org.freedesktop.ConsoleKit.Manager"), + QDBusConnection::systemBus() ); +} + + + +int LinuxCoreFunctions::systemctl( const QStringList& arguments ) +{ + QProcess process; + process.start( QStringLiteral("systemctl"), + QStringList( { QStringLiteral("--no-pager"), QStringLiteral("-q") } ) + arguments ); + + if( process.waitForFinished() && process.exitStatus() == QProcess::NormalExit ) + { + return process.exitCode(); + } + + return -1; +} + + + +void LinuxCoreFunctions::restartDisplayManagers() +{ + for( const auto& displayManager : { + QStringLiteral("gdm"), + QStringLiteral("lightdm"), + QStringLiteral("lxdm"), + QStringLiteral("nodm"), + QStringLiteral("sddm"), + QStringLiteral("wdm"), + QStringLiteral("xdm") } ) + { + systemctl( { QStringLiteral("restart"), displayManager } ); + } +} diff --git a/plugins/platform/linux/LinuxCoreFunctions.h b/plugins/platform/linux/LinuxCoreFunctions.h new file mode 100644 index 0000000..b4937c1 --- /dev/null +++ b/plugins/platform/linux/LinuxCoreFunctions.h @@ -0,0 +1,86 @@ +/* + * LinuxCoreFunctions.h - declaration of LinuxCoreFunctions class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "PlatformCoreFunctions.h" + +// clazy:excludeall=copyable-polymorphic + +class LinuxCoreFunctions : public PlatformCoreFunctions +{ +public: + LinuxCoreFunctions() = default; + + bool applyConfiguration() override; + + void initNativeLoggingSystem( const QString& appName ) override; + void writeToNativeLoggingSystem( const QString& message, Logger::LogLevel loglevel ) override; + + void reboot() override; + void powerDown( bool installUpdates ) override; + + void raiseWindow( QWidget* widget, bool stayOnTop ) override; + + void disableScreenSaver() override; + void restoreScreenSaverSettings() override; + + void setSystemUiState( bool enabled ) override; + + QString activeDesktopName() override; + + bool isRunningAsAdmin() const override; + bool runProgramAsAdmin( const QString& program, const QStringList& parameters ) override; + + bool runProgramAsUser( const QString& program, const QStringList& parameters, + const QString& username, + const QString& desktop = {} ) override; + + QString genericUrlHandler() const override; + + using DBusInterfacePointer = QSharedPointer; + + static DBusInterfacePointer kdeSessionManager(); + static DBusInterfacePointer gnomeSessionManager(); + static DBusInterfacePointer mateSessionManager(); + static DBusInterfacePointer xfcePowerManager(); + static DBusInterfacePointer systemdLoginManager(); + static DBusInterfacePointer consoleKitManager(); + + static int systemctl( const QStringList& arguments ); + + static void restartDisplayManagers(); + +private: + int m_screenSaverTimeout{0}; + int m_screenSaverPreferBlanking{0}; + bool m_dpmsEnabled{false}; + unsigned short m_dpmsStandbyTimeout{0}; + unsigned short m_dpmsSuspendTimeout{0}; + unsigned short m_dpmsOffTimeout{0}; + +}; diff --git a/plugins/platform/linux/LinuxDesktopIntegration.h b/plugins/platform/linux/LinuxDesktopIntegration.h new file mode 100644 index 0000000..c2bf75b --- /dev/null +++ b/plugins/platform/linux/LinuxDesktopIntegration.h @@ -0,0 +1,71 @@ +/* + * LinuxDesktopIntegration.h - declaration of LinuxDesktopIntegration class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +class LinuxDesktopIntegration +{ +public: + class KDE { + public: + enum ShutdownConfirm { + ShutdownConfirmDefault = -1, + ShutdownConfirmNo = 0, + ShutdownConfirmYes = 1 + }; + enum ShutdownMode { + ShutdownModeDefault = -1, + ShutdownModeSchedule = 0, + ShutdownModeTryNow = 1, + ShutdownModeForceNow = 2, + ShutdownModeInteractive = 3 + }; + enum ShutdownType { + ShutdownTypeDefault = -1, + ShutdownTypeNone = 0, + ShutdownTypeReboot = 1, + ShutdownTypeHalt = 2, + ShutdownTypeLogout = 3 + }; + }; + + class Gnome { + public: + enum GsmManagerLogoutMode { + GSM_MANAGER_LOGOUT_MODE_NORMAL = 0, + GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION, + GSM_MANAGER_LOGOUT_MODE_FORCE + } ; + }; + + class Mate { + public: + enum { + GSM_LOGOUT_MODE_NORMAL = 0, + GSM_LOGOUT_MODE_NO_CONFIRMATION, + GSM_LOGOUT_MODE_FORCE + }; + }; +}; + diff --git a/plugins/platform/linux/LinuxFilesystemFunctions.cpp b/plugins/platform/linux/LinuxFilesystemFunctions.cpp new file mode 100644 index 0000000..ed57386 --- /dev/null +++ b/plugins/platform/linux/LinuxFilesystemFunctions.cpp @@ -0,0 +1,185 @@ +/* + * LinuxFilesystemFunctions.cpp - implementation of LinuxFilesystemFunctions class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include +#include +#include +#include +#include + +#include "LinuxFilesystemFunctions.h" + + +QString LinuxFilesystemFunctions::personalAppDataPath() const +{ + return QDir::homePath() + QDir::separator() + QStringLiteral(".veyon"); +} + + + +QString LinuxFilesystemFunctions::globalAppDataPath() const +{ + return QStringLiteral( "/etc/veyon" ); +} + + + +QString LinuxFilesystemFunctions::globalTempPath() const +{ + return QStringLiteral( "/tmp" ); + +} + + + +QString LinuxFilesystemFunctions::fileOwnerGroup( const QString& filePath ) +{ + return QFileInfo( filePath ).group(); +} + + + +bool LinuxFilesystemFunctions::setFileOwnerGroup( const QString& filePath, const QString& ownerGroup ) +{ + struct stat statBuffer{}; + if( stat( filePath.toUtf8().constData(), &statBuffer ) != 0 ) + { + vCritical() << "failed to stat file" << filePath; + return false; + } + + const auto grp = getgrnam( ownerGroup.toUtf8().constData() ); + if( grp == nullptr ) + { + vCritical() << "failed to get gid for" << ownerGroup; + return false; + } + + const auto gid = grp->gr_gid; + + if( chown( filePath.toUtf8().constData(), statBuffer.st_uid, gid ) != 0 ) // Flawfinder:ignore + { + vCritical() << "failed to change owner group of file" << filePath; + return false; + } + + return true; +} + + + +bool LinuxFilesystemFunctions::setFileOwnerGroupPermissions( const QString& filePath, QFile::Permissions permissions ) +{ + QFile file( filePath ); + + auto currentPermissions = file.permissions(); + + for( auto permissionFlag : { QFile::ReadGroup, QFile::WriteGroup, QFile::ExeGroup } ) + { + if( permissions & permissionFlag ) + { + currentPermissions |= permissionFlag; + } + else + { + currentPermissions &= ~permissionFlag; + } + } + + return QFile( filePath ).setPermissions( permissions ); +} + + + +bool LinuxFilesystemFunctions::openFileSafely( QFile* file, QIODevice::OpenMode openMode, QFile::Permissions permissions ) +{ + if( file == nullptr ) + { + return false; + } + + int flags = O_NOFOLLOW | O_CLOEXEC; + if( openMode & QFile::ReadOnly ) + { + flags |= O_RDONLY; + } + + if( openMode & QFile::WriteOnly ) + { + flags |= O_WRONLY; + + if( permissions ) + { + flags |= O_CREAT; + } + } + + if( openMode & QFile::Append ) + { + flags |= O_APPEND; + } + else if( openMode & QFile::Truncate ) + { + flags |= O_TRUNC; + } + + int fileMode = + ( permissions.testFlag( QFile::ReadOwner ) || permissions.testFlag( QFile::ReadUser ) ? S_IRUSR : 0 ) | + ( permissions.testFlag( QFile::WriteOwner ) || permissions.testFlag( QFile::WriteUser ) ? S_IWUSR : 0 ) | + ( permissions.testFlag( QFile::ExeOwner ) || permissions.testFlag( QFile::ExeUser ) ? S_IXUSR : 0 ) | + ( permissions.testFlag( QFile::ReadGroup ) ? S_IRGRP : 0 ) | + ( permissions.testFlag( QFile::WriteGroup ) ? S_IWGRP : 0 ) | + ( permissions.testFlag( QFile::ExeGroup ) ? S_IXGRP : 0 ) | + ( permissions.testFlag( QFile::ReadOther ) ? S_IROTH : 0 ) | + ( permissions.testFlag( QFile::WriteOther ) ? S_IWOTH : 0 ) | + ( permissions.testFlag( QFile::ExeOther ) ? S_IXOTH : 0 ); + + int fd = open( file->fileName().toUtf8().constData(), flags, fileMode ); + if( fd == -1 ) + { + return false; + } + + struct stat s; + if( fstat(fd, &s) != 0 ) + { + close(fd); + return false; + } + + if( s.st_uid != getuid() ) + { + close(fd); + return false; + } + + if( fileMode ) + { + (void) fchmod( fd, fileMode ); + } + + return file->open( fd, openMode, QFileDevice::AutoCloseHandle ); +} diff --git a/plugins/platform/linux/LinuxFilesystemFunctions.h b/plugins/platform/linux/LinuxFilesystemFunctions.h new file mode 100644 index 0000000..e46225f --- /dev/null +++ b/plugins/platform/linux/LinuxFilesystemFunctions.h @@ -0,0 +1,44 @@ +/* + * LinuxFilesystemFunctions.h - declaration of LinuxFilesystemFunctions class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PlatformFilesystemFunctions.h" + +// clazy:excludeall=copyable-polymorphic + +class LinuxFilesystemFunctions : public PlatformFilesystemFunctions +{ +public: + QString personalAppDataPath() const override; + QString globalAppDataPath() const override; + QString globalTempPath() const override; + + QString fileOwnerGroup( const QString& filePath ) override; + bool setFileOwnerGroup( const QString& filePath, const QString& ownerGroup ) override; + bool setFileOwnerGroupPermissions( const QString& filePath, QFile::Permissions permissions ) override; + + bool openFileSafely( QFile* file, QFile::OpenMode openMode, QFile::Permissions permissions ) override; + +}; diff --git a/plugins/platform/linux/LinuxInputDeviceFunctions.cpp b/plugins/platform/linux/LinuxInputDeviceFunctions.cpp new file mode 100644 index 0000000..050c884 --- /dev/null +++ b/plugins/platform/linux/LinuxInputDeviceFunctions.cpp @@ -0,0 +1,106 @@ +/* + * LinuxInputDeviceFunctions.cpp - implementation of LinuxInputDeviceFunctions class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "PlatformServiceFunctions.h" +#include "LinuxInputDeviceFunctions.h" +#include "LinuxKeyboardShortcutTrapper.h" + +#include + + +void LinuxInputDeviceFunctions::enableInputDevices() +{ + if( m_inputDevicesDisabled ) + { + restoreKeyMapTable(); + + m_inputDevicesDisabled = false; + } +} + + + +void LinuxInputDeviceFunctions::disableInputDevices() +{ + if( m_inputDevicesDisabled == false ) + { + setEmptyKeyMapTable(); + + m_inputDevicesDisabled = true; + } +} + + + +KeyboardShortcutTrapper* LinuxInputDeviceFunctions::createKeyboardShortcutTrapper( QObject* parent ) +{ + return new LinuxKeyboardShortcutTrapper( parent ); +} + + + +void LinuxInputDeviceFunctions::setEmptyKeyMapTable() +{ + if( m_origKeyTable ) + { + XFree( m_origKeyTable ); + } + + Display* display = XOpenDisplay( nullptr ); + XDisplayKeycodes( display, &m_keyCodeMin, &m_keyCodeMax ); + m_keyCodeCount = m_keyCodeMax - m_keyCodeMin; + + m_origKeyTable = XGetKeyboardMapping( display, static_cast( m_keyCodeMin ), + m_keyCodeCount, &m_keySymsPerKeyCode ); + + KeySym* newKeyTable = XGetKeyboardMapping( display, static_cast( m_keyCodeMin ), + m_keyCodeCount, &m_keySymsPerKeyCode ); + + for( int i = 0; i < m_keyCodeCount * m_keySymsPerKeyCode; i++ ) + { + newKeyTable[i] = 0; + } + + XChangeKeyboardMapping( display, m_keyCodeMin, m_keySymsPerKeyCode, + newKeyTable, m_keyCodeCount ); + XFlush( display ); + XFree( newKeyTable ); + XCloseDisplay( display ); +} + + + +void LinuxInputDeviceFunctions::restoreKeyMapTable() +{ + Display* display = XOpenDisplay( nullptr ); + + XChangeKeyboardMapping( display, m_keyCodeMin, m_keySymsPerKeyCode, + static_cast( m_origKeyTable ), m_keyCodeCount ); + + XFlush( display ); + XCloseDisplay( display ); + + XFree( m_origKeyTable ); + m_origKeyTable = nullptr; +} diff --git a/plugins/platform/linux/LinuxInputDeviceFunctions.h b/plugins/platform/linux/LinuxInputDeviceFunctions.h new file mode 100644 index 0000000..acc3746 --- /dev/null +++ b/plugins/platform/linux/LinuxInputDeviceFunctions.h @@ -0,0 +1,53 @@ +/* + * LinuxInputDeviceFunctions.h - declaration of LinuxInputDeviceFunctions class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PlatformInputDeviceFunctions.h" + +// clazy:excludeall=copyable-polymorphic + +class LinuxInputDeviceFunctions : public PlatformInputDeviceFunctions +{ +public: + LinuxInputDeviceFunctions() = default; + virtual ~LinuxInputDeviceFunctions() = default; + + void enableInputDevices() override; + void disableInputDevices() override; + + KeyboardShortcutTrapper* createKeyboardShortcutTrapper( QObject* parent ) override; + +private: + void setEmptyKeyMapTable(); + void restoreKeyMapTable(); + + bool m_inputDevicesDisabled{false}; + void* m_origKeyTable{nullptr}; + int m_keyCodeMin{0}; + int m_keyCodeMax{0}; + int m_keyCodeCount{0}; + int m_keySymsPerKeyCode{0}; + +}; diff --git a/plugins/platform/linux/LinuxKeyboardInput.cpp b/plugins/platform/linux/LinuxKeyboardInput.cpp new file mode 100644 index 0000000..6af8af8 --- /dev/null +++ b/plugins/platform/linux/LinuxKeyboardInput.cpp @@ -0,0 +1,70 @@ +/* + * LinuxKeyboardInput.cpp - implementation of LinuxKeyboardInput class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "LinuxKeyboardInput.h" + +#include + +#include + + +LinuxKeyboardInput::LinuxKeyboardInput() : + m_display( XOpenDisplay( nullptr ) ), + m_fakeKeyHandle( fakekey_init( m_display ) ) +{ +} + + + +LinuxKeyboardInput::~LinuxKeyboardInput() +{ + free( m_fakeKeyHandle ); + XCloseDisplay( m_display ); +} + + + +void LinuxKeyboardInput::pressAndReleaseKey( uint32_t keysym ) +{ + fakekey_press_keysym( m_fakeKeyHandle, keysym, 0 ); + fakekey_release( m_fakeKeyHandle ); +} + + + +void LinuxKeyboardInput::pressAndReleaseKey( const QByteArray& utf8Data ) +{ + fakekey_press( m_fakeKeyHandle, reinterpret_cast( utf8Data.constData() ), utf8Data.size(), 0 ); + fakekey_release( m_fakeKeyHandle ); +} + + + +void LinuxKeyboardInput::sendString( const QString& string ) +{ + for( int i = 0; i < string.size(); ++i ) + { + pressAndReleaseKey( string.midRef( i ).toUtf8() ); + } +} diff --git a/plugins/platform/linux/LinuxKeyboardInput.h b/plugins/platform/linux/LinuxKeyboardInput.h new file mode 100644 index 0000000..69688f6 --- /dev/null +++ b/plugins/platform/linux/LinuxKeyboardInput.h @@ -0,0 +1,50 @@ +/* + * LinuxKeyboardInput.h - declaration of LinuxKeyboardInput class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +// clazy:excludeall=copyable-polymorphic + +class LinuxKeyboardInput +{ +public: + using FakeKey = struct FakeKey; + using Display = struct _XDisplay; + + LinuxKeyboardInput(); + ~LinuxKeyboardInput(); + + void pressAndReleaseKey( uint32_t keysym ); + + void pressAndReleaseKey( const QByteArray& utf8Data ); + + void sendString( const QString& string ); + +private: + Display* m_display; + FakeKey* m_fakeKeyHandle; + +}; diff --git a/plugins/platform/linux/LinuxKeyboardShortcutTrapper.h b/plugins/platform/linux/LinuxKeyboardShortcutTrapper.h new file mode 100644 index 0000000..bb5f3a0 --- /dev/null +++ b/plugins/platform/linux/LinuxKeyboardShortcutTrapper.h @@ -0,0 +1,45 @@ +/* + * LinuxKeyboardShortcutTrapper.h - dummy KeyboardShortcutTrapper implementation + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "KeyboardShortcutTrapper.h" + +class LinuxKeyboardShortcutTrapper : public KeyboardShortcutTrapper +{ + Q_OBJECT +public: + explicit LinuxKeyboardShortcutTrapper( QObject* parent = nullptr ) : + KeyboardShortcutTrapper( parent ) + { + } + + ~LinuxKeyboardShortcutTrapper() override = default; + + void setEnabled( bool on ) override + { + Q_UNUSED(on); + } + +} ; diff --git a/plugins/platform/linux/LinuxNetworkFunctions.cpp b/plugins/platform/linux/LinuxNetworkFunctions.cpp new file mode 100644 index 0000000..f6db364 --- /dev/null +++ b/plugins/platform/linux/LinuxNetworkFunctions.cpp @@ -0,0 +1,90 @@ +/* + * LinuxNetworkFunctions.cpp - implementation of LinuxNetworkFunctions class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include + +#include "LinuxNetworkFunctions.h" + +bool LinuxNetworkFunctions::ping( const QString& hostAddress ) +{ + QProcess pingProcess; + pingProcess.start( QStringLiteral("ping"), { QStringLiteral("-W"), QStringLiteral("1"), QStringLiteral("-c"), QString::number( PingTimeout / 1000 ), hostAddress } ); + pingProcess.waitForFinished( PingProcessTimeout ); + + return pingProcess.exitCode() == 0; +} + + + +bool LinuxNetworkFunctions::configureFirewallException( const QString& applicationPath, const QString& description, bool enabled ) +{ + Q_UNUSED(applicationPath) + Q_UNUSED(description) + Q_UNUSED(enabled) + + return true; +} + + + +bool LinuxNetworkFunctions::configureSocketKeepalive( Socket socket, bool enabled, int idleTime, int interval, int probes ) +{ + int optval; + socklen_t optlen = sizeof(optval); + auto fd = static_cast( socket ); + + // Try to set the option active + optval = enabled ? 1 : 0; + if( setsockopt( fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen ) < 0 ) + { + vWarning() << "could not set SO_KEEPALIVE"; + return false; + } + + optval = std::max( 1, idleTime / 1000 ); + if( setsockopt( fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, optlen ) < 0 ) + { + vWarning() << "could not set TCP_KEEPIDLE"; + return false; + } + + optval = std::max( 1, interval / 1000 ); + if( setsockopt( fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen ) < 0 ) + { + vWarning() << "could not set TCP_KEEPINTVL"; + return false; + } + + optval = probes; + if( setsockopt( fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen ) < 0 ) + { + vWarning() << "could not set TCP_KEEPCNT"; + return false; + } + + return true; +} diff --git a/plugins/platform/linux/LinuxNetworkFunctions.h b/plugins/platform/linux/LinuxNetworkFunctions.h new file mode 100644 index 0000000..fa88688 --- /dev/null +++ b/plugins/platform/linux/LinuxNetworkFunctions.h @@ -0,0 +1,39 @@ +/* + * LinuxNetworkFunctions.h - declaration of LinuxNetworkFunctions class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PlatformNetworkFunctions.h" + +// clazy:excludeall=copyable-polymorphic + +class LinuxNetworkFunctions : public PlatformNetworkFunctions +{ +public: + bool ping( const QString& hostAddress ) override; + bool configureFirewallException( const QString& applicationPath, const QString& description, bool enabled ) override; + + bool configureSocketKeepalive( Socket socket, bool enabled, int idleTime, int interval, int probes ) override; + +}; diff --git a/plugins/platform/linux/LinuxPlatformConfiguration.h b/plugins/platform/linux/LinuxPlatformConfiguration.h new file mode 100644 index 0000000..714f812 --- /dev/null +++ b/plugins/platform/linux/LinuxPlatformConfiguration.h @@ -0,0 +1,34 @@ +/* + * LinuxPlatformConfiguration.h - configuration values for LinuxPlatform plugin + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "Configuration/Proxy.h" + +#define FOREACH_LINUX_PLATFORM_CONFIG_PROPERTY(OP) \ + OP( LinuxPlatformConfiguration, m_configuration, QString, pamServiceName, setPamServiceName, "PamServiceName", "Linux", QString(), Configuration::Property::Flag::Advanced ) \ + OP( LinuxPlatformConfiguration, m_configuration, QString, displayManagerUsers, setDisplayManagerUsers, "DisplayManagerUsers", "Linux", QStringLiteral("gdm,lightdm,sddm,mdm,Debian-gdm"), Configuration::Property::Flag::Advanced ) \ + + +DECLARE_CONFIG_PROXY(LinuxPlatformConfiguration, FOREACH_LINUX_PLATFORM_CONFIG_PROPERTY) diff --git a/plugins/platform/linux/LinuxPlatformConfigurationPage.cpp b/plugins/platform/linux/LinuxPlatformConfigurationPage.cpp new file mode 100644 index 0000000..a8e68a1 --- /dev/null +++ b/plugins/platform/linux/LinuxPlatformConfigurationPage.cpp @@ -0,0 +1,67 @@ +/* + * LinuxPlatformConfigurationPage.cpp - page for configuring service application + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "VeyonConfiguration.h" +#include "LinuxPlatformConfiguration.h" +#include "LinuxPlatformConfigurationPage.h" +#include "Configuration/UiMapping.h" + +#include "ui_LinuxPlatformConfigurationPage.h" + + +LinuxPlatformConfigurationPage::LinuxPlatformConfigurationPage() : + ui( new Ui::LinuxPlatformConfigurationPage ), + m_configuration( &VeyonCore::config() ) +{ + ui->setupUi(this); + + Configuration::UiMapping::setFlags( this, Configuration::Property::Flag::Advanced ); +} + + + +LinuxPlatformConfigurationPage::~LinuxPlatformConfigurationPage() +{ + delete ui; +} + + + +void LinuxPlatformConfigurationPage::resetWidgets() +{ + FOREACH_LINUX_PLATFORM_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); +} + + + +void LinuxPlatformConfigurationPage::connectWidgetsToProperties() +{ + FOREACH_LINUX_PLATFORM_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY); +} + + + +void LinuxPlatformConfigurationPage::applyConfiguration() +{ +} diff --git a/plugins/platform/linux/LinuxPlatformConfigurationPage.h b/plugins/platform/linux/LinuxPlatformConfigurationPage.h new file mode 100644 index 0000000..2cbc22c --- /dev/null +++ b/plugins/platform/linux/LinuxPlatformConfigurationPage.h @@ -0,0 +1,49 @@ +/* + * LinuxPlatformConfigurationPage.h - header for the LinuxPlatformConfigurationPage class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "ConfigurationPage.h" +#include "LinuxPlatformConfiguration.h" + +namespace Ui { +class LinuxPlatformConfigurationPage; +} + +class LinuxPlatformConfigurationPage : public ConfigurationPage +{ + Q_OBJECT +public: + LinuxPlatformConfigurationPage(); + ~LinuxPlatformConfigurationPage() override; + + void resetWidgets() override; + void connectWidgetsToProperties() override; + void applyConfiguration() override; + +private: + Ui::LinuxPlatformConfigurationPage *ui; + LinuxPlatformConfiguration m_configuration; + +}; diff --git a/plugins/platform/linux/LinuxPlatformConfigurationPage.ui b/plugins/platform/linux/LinuxPlatformConfigurationPage.ui new file mode 100644 index 0000000..2c7cb3f --- /dev/null +++ b/plugins/platform/linux/LinuxPlatformConfigurationPage.ui @@ -0,0 +1,85 @@ + + + LinuxPlatformConfigurationPage + + + Linux + + + + :/linux/tux.png:/linux/tux.png + + + + 0 + + + 0 + + + + + User authentication + + + + + + Custom PAM service for user authentication + + + + + + + 16 + + + + + + + + + + Session management + + + + + + Display manager users + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + updateVncServerPluginConfigurationWidget() + startLinuxPlatform() + stopLinuxPlatform() + + diff --git a/plugins/platform/linux/LinuxPlatformPlugin.cpp b/plugins/platform/linux/LinuxPlatformPlugin.cpp new file mode 100644 index 0000000..78d046f --- /dev/null +++ b/plugins/platform/linux/LinuxPlatformPlugin.cpp @@ -0,0 +1,56 @@ +/* + * LinuxPlatformPlugin.cpp - implementation of LinuxPlatformPlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "LinuxPlatformPlugin.h" +#include "LinuxPlatformConfiguration.h" +#include "LinuxPlatformConfigurationPage.h" + +LinuxPlatformPlugin::LinuxPlatformPlugin( QObject* parent ) : + QObject( parent ) +{ + // make sure to load global config from default config dirs independent of environment variables + qunsetenv( "XDG_CONFIG_DIRS" ); + + // don't abort with SIGPIPE when writing to closed sockets e.g. while shutting down VncConnection + signal( SIGPIPE, SIG_IGN ); +} + + + +LinuxPlatformPlugin::~LinuxPlatformPlugin() +{ + m_linuxInputDeviceFunctions.enableInputDevices(); +} + + + +ConfigurationPage* LinuxPlatformPlugin::createConfigurationPage() +{ + return new LinuxPlatformConfigurationPage(); +} + + +IMPLEMENT_CONFIG_PROXY(LinuxPlatformConfiguration) diff --git a/plugins/platform/linux/LinuxPlatformPlugin.h b/plugins/platform/linux/LinuxPlatformPlugin.h new file mode 100644 index 0000000..4a7ca11 --- /dev/null +++ b/plugins/platform/linux/LinuxPlatformPlugin.h @@ -0,0 +1,128 @@ +/* + * LinuxPlatformPlugin.h - declaration of LinuxPlatformPlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "ConfigurationPagePluginInterface.h" +#include "PluginInterface.h" +#include "PlatformPluginInterface.h" +#include "LinuxCoreFunctions.h" +#include "LinuxFilesystemFunctions.h" +#include "LinuxInputDeviceFunctions.h" +#include "LinuxNetworkFunctions.h" +#include "LinuxServiceFunctions.h" +#include "LinuxSessionFunctions.h" +#include "LinuxUserFunctions.h" + +class LinuxPlatformPlugin : public QObject, PlatformPluginInterface, PluginInterface, ConfigurationPagePluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.LinuxPlatform") + Q_INTERFACES(PluginInterface PlatformPluginInterface ConfigurationPagePluginInterface) +public: + explicit LinuxPlatformPlugin( QObject* parent = nullptr ); + ~LinuxPlatformPlugin() override; + + Plugin::Uid uid() const override + { + return QStringLiteral("63928a8a-4c51-4bfd-888e-9e13c6f3907a"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral( "LinuxPlatformPlugin" ); + } + + QString description() const override + { + return tr( "Plugin implementing abstract functions for the Linux platform" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + Plugin::Flags flags() const override + { + return Plugin::ProvidesDefaultImplementation; + } + + PlatformCoreFunctions& coreFunctions() override + { + return m_linuxCoreFunctions; + } + + PlatformFilesystemFunctions& filesystemFunctions() override + { + return m_linuxFilesystemFunctions; + } + + PlatformInputDeviceFunctions& inputDeviceFunctions() override + { + return m_linuxInputDeviceFunctions; + } + + PlatformNetworkFunctions& networkFunctions() override + { + return m_linuxNetworkFunctions; + } + + PlatformServiceFunctions& serviceFunctions() override + { + return m_linuxServiceFunctions; + } + + PlatformSessionFunctions& sessionFunctions() override + { + return m_linuxSessionFunctions; + } + + PlatformUserFunctions& userFunctions() override + { + return m_linuxUserFunctions; + } + + ConfigurationPage* createConfigurationPage() override; + +private: + LinuxCoreFunctions m_linuxCoreFunctions{}; + LinuxFilesystemFunctions m_linuxFilesystemFunctions{}; + LinuxInputDeviceFunctions m_linuxInputDeviceFunctions{}; + LinuxNetworkFunctions m_linuxNetworkFunctions{}; + LinuxServiceFunctions m_linuxServiceFunctions{}; + LinuxSessionFunctions m_linuxSessionFunctions{}; + LinuxUserFunctions m_linuxUserFunctions{}; + +}; diff --git a/plugins/platform/linux/LinuxServiceCore.cpp b/plugins/platform/linux/LinuxServiceCore.cpp new file mode 100644 index 0000000..7cb0bbb --- /dev/null +++ b/plugins/platform/linux/LinuxServiceCore.cpp @@ -0,0 +1,293 @@ +/* + * LinuxServiceFunctions.cpp - implementation of LinuxServiceFunctions class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "Filesystem.h" +#include "LinuxCoreFunctions.h" +#include "LinuxServiceCore.h" +#include "LinuxSessionFunctions.h" +#include "ProcessHelper.h" +#include "VeyonConfiguration.h" + + +LinuxServiceCore::LinuxServiceCore( QObject* parent ) : + QObject( parent ) +{ + connectToLoginManager(); +} + + + +LinuxServiceCore::~LinuxServiceCore() +{ + stopAllServers(); +} + + + +void LinuxServiceCore::run() +{ + const auto sessions = listSessions(); + + for( const auto& s : sessions ) + { + startServer( s, QDBusObjectPath( s ) ); + } + + QEventLoop eventLoop; + eventLoop.exec(); +} + + + +void LinuxServiceCore::startServer( const QString& login1SessionId, const QDBusObjectPath& sessionObjectPath ) +{ + const auto sessionPath = sessionObjectPath.path(); + + const auto sessionType = LinuxSessionFunctions::getSessionType( sessionPath ); + + if( sessionType == QLatin1String("wayland") ) + { + vCritical() << "Can't start Veyon Server in Wayland sessions as this is not yet supported. Please switch to X11-based sessions!"; + return; + } + + // do not start server for non-graphical sessions + if( sessionType != QLatin1String("x11") ) + { + return; + } + + const auto sessionState = LinuxSessionFunctions::getSessionState( sessionPath ); + if( sessionState == LinuxSessionFunctions::State::Opening ) + { + vDebug() << "Session" << sessionPath << "still opening - retrying in" << SessionStateProbingInterval << "msecs"; + QTimer::singleShot( SessionStateProbingInterval, this, [=]() { startServer( login1SessionId, sessionObjectPath ); } ); + return; + } + + // only start server for online or active sessions + if( sessionState != LinuxSessionFunctions::State::Online && + sessionState != LinuxSessionFunctions::State::Active ) + { + vDebug() << "Not starting server for session" << sessionPath << "in state" << sessionState; + return; + } + + const auto sessionLeader = LinuxSessionFunctions::getSessionLeaderPid( sessionPath ); + if( sessionLeader < 0 ) + { + vCritical() << "No leader available for session" << sessionPath; + return; + } + + auto sessionEnvironment = LinuxSessionFunctions::getSessionEnvironment( sessionLeader ); + + if( sessionEnvironment.isEmpty() ) + { + vWarning() << "Environment for session" << sessionPath << "not yet available - retrying in" + << SessionEnvironmentProbingInterval << "msecs"; + QTimer::singleShot( SessionEnvironmentProbingInterval, this, + [=]() { startServer( login1SessionId, sessionObjectPath ); } ); + return; + } + + if( m_sessionManager.multiSession() == false && m_serverProcesses.isEmpty() == false ) + { + // make sure no other server is still running + stopAllServers(); + } + + const auto sessionUptime = LinuxSessionFunctions::getSessionUptimeSeconds( sessionPath ); + + if( sessionUptime >= 0 && sessionUptime < SessionUptimeSecondsMinimum ) + { + vDebug() << "Session" << sessionPath << "too young - retrying in" << SessionUptimeProbingInterval << "msecs"; + QTimer::singleShot( SessionUptimeProbingInterval, this, [=]() { startServer( login1SessionId, sessionObjectPath ); } ); + return; + } + + const auto seat = LinuxSessionFunctions::getSessionSeat( sessionPath ); + const auto sessionId = LinuxSessionFunctions::getSessionId( sessionPath ); + + // if pam-systemd is not in use, we have to set the XDG_SESSION_ID environment variable manually + if( sessionEnvironment.contains( LinuxSessionFunctions::xdgSessionIdEnvVarName() ) == false ) + { + sessionEnvironment.insert( LinuxSessionFunctions::xdgSessionIdEnvVarName(), sessionId ); + } + + const auto veyonSessionId = m_sessionManager.openSession( sessionId ); + + vInfo() << "Starting server for new" << qUtf8Printable(sessionType) << "session" << sessionPath + << "with ID" << veyonSessionId + << "at seat" << seat.path; + + sessionEnvironment.insert( QLatin1String( ServiceDataManager::serviceDataTokenEnvironmentVariable() ), + QString::fromUtf8( m_dataManager.token().toByteArray() ) ); + + auto process = new QProcess( this ); + process->setProcessEnvironment( sessionEnvironment ); + + if( VeyonCore::config().logToSystem() ) + { + process->setProcessChannelMode( QProcess::ForwardedChannels ); + } + + const auto catchsegv{ QStringLiteral("/usr/bin/catchsegv") }; + if( VeyonCore::isDebugging() && QFileInfo::exists( catchsegv ) ) + { + process->start( catchsegv, { VeyonCore::filesystem().serverFilePath() } ); + } + else + { + process->start( VeyonCore::filesystem().serverFilePath(), QStringList{} ); + } + + m_serverProcesses[sessionPath] = process; +} + + + +void LinuxServiceCore::stopServer( const QString& login1SessionId, const QDBusObjectPath& sessionObjectPath ) +{ + Q_UNUSED(login1SessionId) + + const auto sessionPath = sessionObjectPath.path(); + + if( m_serverProcesses.contains( sessionPath ) ) + { + stopServer( sessionPath ); + } +} + + + +void LinuxServiceCore::connectToLoginManager() +{ + bool success = true; + + const auto service = m_loginManager->service(); + const auto path = m_loginManager->path(); + const auto interface = m_loginManager->interface(); + + success &= QDBusConnection::systemBus().connect( service, path, interface, QStringLiteral("SessionNew"), + this, SLOT(startServer(QString,QDBusObjectPath)) ); + + success &= QDBusConnection::systemBus().connect( service, path, interface, QStringLiteral("SessionRemoved"), + this, SLOT(stopServer(QString,QDBusObjectPath)) ); + + if( success == false ) + { + vWarning() << "could not connect to login manager! retrying in" << LoginManagerReconnectInterval << "msecs"; + QTimer::singleShot( LoginManagerReconnectInterval, this, &LinuxServiceCore::connectToLoginManager ); + } + else + { + vDebug() << "connected to login manager"; + } +} + + + +void LinuxServiceCore::stopServer( const QString& sessionPath ) +{ + m_sessionManager.closeSession( LinuxSessionFunctions::getSessionId( sessionPath ) ); + + if( m_serverProcesses.contains( sessionPath ) == false ) + { + return; + } + + vInfo() << "stopping server for removed session" << sessionPath; + + auto process = qAsConst(m_serverProcesses)[sessionPath]; + + // tell x11vnc to shutdown + kill( pid_t(process->processId()), SIGINT ); + + if( ProcessHelper::waitForProcess( process, ServerShutdownTimeout, ServerWaitSleepInterval ) == false ) + { + process->terminate(); + + if( ProcessHelper::waitForProcess( process, ServerTerminateTimeout, ServerWaitSleepInterval ) == false ) + { + vWarning() << "server for session" << sessionPath << "still running - killing now"; + process->kill(); + ProcessHelper::waitForProcess( process, ServerKillTimeout, ServerWaitSleepInterval ); + } + } + + delete process; + m_serverProcesses.remove( sessionPath ); +} + + + +void LinuxServiceCore::stopAllServers() +{ + while( m_serverProcesses.isEmpty() == false ) + { + stopServer( m_serverProcesses.firstKey() ); + } +} + + + +QStringList LinuxServiceCore::listSessions() +{ + QStringList sessions; + + const QDBusReply reply = m_loginManager->call( QStringLiteral("ListSessions") ); + + if( reply.isValid() ) + { + const auto data = reply.value(); + + data.beginArray(); + while( data.atEnd() == false ) + { + LinuxSessionFunctions::LoginDBusSession session; + + data.beginStructure(); + data >> session.id >> session.uid >> session.name >> session.seatId >> session.path; + data.endStructure(); + + sessions.append( session.path.path() ); + } + return sessions; + } + + vCritical() << "Could not query sessions:" << reply.error().message(); + + return sessions; +} + diff --git a/plugins/platform/linux/LinuxServiceCore.h b/plugins/platform/linux/LinuxServiceCore.h new file mode 100644 index 0000000..5a12f48 --- /dev/null +++ b/plugins/platform/linux/LinuxServiceCore.h @@ -0,0 +1,71 @@ +/* + * LinuxServiceCore.h - declaration of LinuxServiceCore class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "LinuxCoreFunctions.h" +#include "PlatformSessionManager.h" +#include "ServiceDataManager.h" + +class QProcess; + +// clazy:excludeall=copyable-polymorphic + +class LinuxServiceCore : public QObject +{ + Q_OBJECT +public: + explicit LinuxServiceCore( QObject* parent = nullptr ); + ~LinuxServiceCore() override; + + void run(); + +private Q_SLOTS: + void startServer( const QString& login1SessionId, const QDBusObjectPath& sessionObjectPath ); + void stopServer( const QString& login1SessionId, const QDBusObjectPath& sessionObjectPath ); + +private: + static constexpr auto LoginManagerReconnectInterval = 3000; + static constexpr auto ServerShutdownTimeout = 1000; + static constexpr auto ServerTerminateTimeout = 3000; + static constexpr auto ServerKillTimeout = 10000; + static constexpr auto ServerWaitSleepInterval = 100; + static constexpr auto SessionEnvironmentProbingInterval = 1000; + static constexpr auto SessionStateProbingInterval = 1000; + static constexpr auto SessionUptimeSecondsMinimum = 3; + static constexpr auto SessionUptimeProbingInterval = 1000; + + void connectToLoginManager(); + void stopServer( const QString& sessionPath ); + void stopAllServers(); + + QStringList listSessions(); + + LinuxCoreFunctions::DBusInterfacePointer m_loginManager{LinuxCoreFunctions::systemdLoginManager()}; + QMap m_serverProcesses; + + ServiceDataManager m_dataManager{}; + PlatformSessionManager m_sessionManager{}; + +}; diff --git a/plugins/platform/linux/LinuxServiceFunctions.cpp b/plugins/platform/linux/LinuxServiceFunctions.cpp new file mode 100644 index 0000000..b407d90 --- /dev/null +++ b/plugins/platform/linux/LinuxServiceFunctions.cpp @@ -0,0 +1,122 @@ +/* + * LinuxServiceFunctions.cpp - implementation of LinuxServiceFunctions class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "LinuxCoreFunctions.h" +#include "LinuxServiceCore.h" +#include "LinuxServiceFunctions.h" + + +QString LinuxServiceFunctions::veyonServiceName() const +{ + return QStringLiteral("veyon"); +} + + + +bool LinuxServiceFunctions::isRegistered( const QString& name ) +{ + Q_UNUSED(name); + + vCritical() << "Querying service registration is not supported on this platform."; + + return false; +} + + + +bool LinuxServiceFunctions::isRunning( const QString& name ) +{ + return LinuxCoreFunctions::systemctl( { QStringLiteral("status"), name } ) == 0; +} + + + +bool LinuxServiceFunctions::start( const QString& name ) +{ + return LinuxCoreFunctions::systemctl( { QStringLiteral("start"), name } ) == 0; +} + + + +bool LinuxServiceFunctions::stop( const QString& name ) +{ + return LinuxCoreFunctions::systemctl( { QStringLiteral("stop"), name } ) == 0; +} + + + +bool LinuxServiceFunctions::install( const QString& name, const QString& filePath, + StartMode startMode, const QString& displayName ) +{ + Q_UNUSED(name) + Q_UNUSED(filePath) + Q_UNUSED(startMode) + Q_UNUSED(displayName) + + vCritical() << "Registering services is not supported on this platform."; + + return false; +} + + + +bool LinuxServiceFunctions::uninstall( const QString& name ) +{ + Q_UNUSED(name) + + vCritical() << "Unregistering services is not supported on this platform."; + + return false; +} + + + +bool LinuxServiceFunctions::setStartMode( const QString& name, PlatformServiceFunctions::StartMode startMode ) +{ + if( startMode == StartMode::Auto ) + { + return LinuxCoreFunctions::systemctl( { QStringLiteral("enable"), name } ) == 0; + } + + return LinuxCoreFunctions::systemctl( { QStringLiteral("disable"), name } ) == 0; +} + + + +bool LinuxServiceFunctions::runAsService( const QString& name, const ServiceEntryPoint& serviceEntryPoint ) +{ + Q_UNUSED(name); + + serviceEntryPoint(); + + return true; +} + + + +void LinuxServiceFunctions::manageServerInstances() +{ + LinuxServiceCore serviceCore; + serviceCore.run(); +} diff --git a/plugins/platform/linux/LinuxServiceFunctions.h b/plugins/platform/linux/LinuxServiceFunctions.h new file mode 100644 index 0000000..ad38551 --- /dev/null +++ b/plugins/platform/linux/LinuxServiceFunctions.h @@ -0,0 +1,47 @@ +/* + * LinuxServiceFunctions.h - declaration of LinuxServiceFunctions class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PlatformServiceFunctions.h" + +// clazy:excludeall=copyable-polymorphic + +class LinuxServiceFunctions : public PlatformServiceFunctions +{ +public: + QString veyonServiceName() const override; + + bool isRegistered( const QString& name ) override; + bool isRunning( const QString& name ) override; + bool start( const QString& name ) override; + bool stop( const QString& name ) override; + bool install( const QString& name, const QString& serviceFilePath, + StartMode startMode, const QString& displayName) override; + bool uninstall( const QString& name ) override; + bool setStartMode( const QString& name, StartMode startMode ) override; + bool runAsService( const QString& name, const ServiceEntryPoint& serviceEntryPoint ) override; + void manageServerInstances() override; + +}; diff --git a/plugins/platform/linux/LinuxSessionFunctions.cpp b/plugins/platform/linux/LinuxSessionFunctions.cpp new file mode 100644 index 0000000..1446dcf --- /dev/null +++ b/plugins/platform/linux/LinuxSessionFunctions.cpp @@ -0,0 +1,210 @@ +/* + * LinuxSessionFunctions.cpp - implementation of LinuxSessionFunctions class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include + +#include "LinuxSessionFunctions.h" +#include "PlatformSessionManager.h" + + +LinuxSessionFunctions::SessionId LinuxSessionFunctions::currentSessionId() +{ + return PlatformSessionManager::resolveSessionId( getSessionId( currentSessionPath() ) ); +} + + + +QVariant LinuxSessionFunctions::getSessionProperty( const QString& session, const QString& property ) +{ + QDBusInterface loginManager( QStringLiteral("org.freedesktop.login1"), + session, + QStringLiteral("org.freedesktop.DBus.Properties"), + QDBusConnection::systemBus() ); + + const QDBusReply reply = loginManager.call( QStringLiteral("Get"), + QStringLiteral("org.freedesktop.login1.Session"), + property ); + + if( reply.isValid() == false ) + { + vCritical() << "Could not query session property" << property << reply.error().message(); + return {}; + } + + return reply.value().variant(); +} + + + +int LinuxSessionFunctions::getSessionLeaderPid( const QString& session ) +{ + const auto leader = getSessionProperty( session, QStringLiteral("Leader") ); + + if( leader.isNull() ) + { + return -1; + } + + return leader.toInt(); +} + + + +qint64 LinuxSessionFunctions::getSessionUptimeSeconds( const QString& session ) +{ + const auto sessionUptimeUsec = getSessionProperty( session, QStringLiteral("Timestamp") ); + + if( sessionUptimeUsec.isNull() ) + { + return -1; + } + +#if QT_VERSION < QT_VERSION_CHECK(5, 8, 0) + const auto currentTimestamp = QDateTime::currentMSecsSinceEpoch() / 1000; +#else + const auto currentTimestamp = QDateTime::currentSecsSinceEpoch(); +#endif + + return currentTimestamp - qint64( sessionUptimeUsec.toLongLong() / ( 1000 * 1000 ) ); +} + + + +QString LinuxSessionFunctions::getSessionType( const QString& session ) +{ + return getSessionProperty( session, QStringLiteral("Type") ).toString(); +} + + + +QString LinuxSessionFunctions::getSessionId( const QString& session ) +{ + return getSessionProperty( session, QStringLiteral("Id") ).toString(); +} + + + +LinuxSessionFunctions::State LinuxSessionFunctions::getSessionState( const QString& session ) +{ + static const QMap stateMap{ + { QStringLiteral("offline"), State::Offline }, + { QStringLiteral("lingering"), State::Lingering }, + { QStringLiteral("online"), State::Online }, + { QStringLiteral("active"), State::Active }, + { QStringLiteral("opening"), State::Opening }, + { QStringLiteral("closing"), State::Closing }, + }; + + const auto stateString = getSessionProperty( session, QStringLiteral("State") ).toString(); + const auto state = stateMap.value( stateString, State::Unknown ); + if( state == State::Unknown ) + { + vDebug() << stateString; + } + + return state; +} + + + +LinuxSessionFunctions::LoginDBusSessionSeat LinuxSessionFunctions::getSessionSeat( const QString& session ) +{ + const auto seatArgument = getSessionProperty( session, QStringLiteral("Seat") ).value(); + + LoginDBusSessionSeat seat; + seatArgument.beginStructure(); + seatArgument >> seat.id; + seatArgument >> seat.path; + seatArgument.endStructure(); + + return seat; +} + + + +QProcessEnvironment LinuxSessionFunctions::getSessionEnvironment( int sessionLeaderPid ) +{ + QProcessEnvironment sessionEnv; + + PROCTAB* proc = openproc( PROC_FILLSTATUS | PROC_FILLENV ); + proc_t* procInfo = nullptr; + + QList ppids; + + while( ( procInfo = readproc( proc, nullptr ) ) ) + { + if( ( procInfo->ppid == sessionLeaderPid || ppids.contains( procInfo->ppid ) ) && + procInfo->environ != nullptr ) + { + for( int i = 0; procInfo->environ[i]; ++i ) + { + const auto env = QString::fromUtf8( procInfo->environ[i] ).split( QLatin1Char('=') ); + sessionEnv.insert( env.first(), env.mid( 1 ).join( QLatin1Char('=') ) ); + } + + ppids.append( procInfo->tid ); + } + + freeproc( procInfo ); + } + + closeproc( proc ); + + return sessionEnv; +} + + + +QString LinuxSessionFunctions::currentSessionType() const +{ + const auto env = QProcessEnvironment::systemEnvironment(); + + if( env.contains( QStringLiteral("WAYLAND_DISPLAY") ) ) + { + return QStringLiteral("wayland"); + } + else if( env.contains( QStringLiteral("DISPLAY") ) ) + { + return QStringLiteral("x11"); + } + + return getSessionType( currentSessionPath() ); +} + + + +QString LinuxSessionFunctions::currentSessionPath() +{ + const auto xdgSessionId = QProcessEnvironment::systemEnvironment().value( xdgSessionIdEnvVarName() ); + if( xdgSessionId.isEmpty() ) + { + return QStringLiteral("/org/freedesktop/login1/session/self"); + } + + return QStringLiteral("/org/freedesktop/login1/session/%1").arg( xdgSessionId ); +} diff --git a/plugins/platform/linux/LinuxSessionFunctions.h b/plugins/platform/linux/LinuxSessionFunctions.h new file mode 100644 index 0000000..0df778a --- /dev/null +++ b/plugins/platform/linux/LinuxSessionFunctions.h @@ -0,0 +1,85 @@ +/* + * LinuxSessionFunctions.h - declaration of LinuxSessionFunctions class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "PlatformSessionFunctions.h" + +// clazy:excludeall=copyable-polymorphic + +class LinuxSessionFunctions : public PlatformSessionFunctions +{ + Q_GADGET +public: + using LoginDBusSession = struct { + QString id; + quint32 uid{0}; + QString name; + QString seatId; + QDBusObjectPath path; + } ; + + using LoginDBusSessionSeat = struct { + QString id; + QString path; + } ; + + enum class State + { + Unknown, + Offline, + Lingering, + Online, + Active, + Opening, + Closing + }; + Q_ENUM(State) + + SessionId currentSessionId() override; + + QString currentSessionType() const override; + + static QVariant getSessionProperty( const QString& session, const QString& property ); + + static int getSessionLeaderPid( const QString& session ); + static qint64 getSessionUptimeSeconds( const QString& session ); + static QString getSessionType( const QString& session ); + static QString getSessionId( const QString& session ); + static State getSessionState( const QString& session ); + static LoginDBusSessionSeat getSessionSeat( const QString& session ); + + static QProcessEnvironment getSessionEnvironment( int sessionLeaderPid ); + + static QString currentSessionPath(); + + static QString xdgSessionIdEnvVarName() + { + return QStringLiteral("XDG_SESSION_ID"); + } + +}; diff --git a/plugins/platform/linux/LinuxUserFunctions.cpp b/plugins/platform/linux/LinuxUserFunctions.cpp new file mode 100644 index 0000000..6e0c9f1 --- /dev/null +++ b/plugins/platform/linux/LinuxUserFunctions.cpp @@ -0,0 +1,374 @@ +/* + * LinuxUserFunctions.cpp - implementation of LinuxUserFunctions class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include "LinuxCoreFunctions.h" +#include "LinuxDesktopIntegration.h" +#include "LinuxKeyboardInput.h" +#include "LinuxPlatformConfiguration.h" +#include "LinuxSessionFunctions.h" +#include "LinuxUserFunctions.h" +#include "VeyonConfiguration.h" + +#define XK_MISCELLANY + +#include + +#include +#include + + +QString LinuxUserFunctions::fullName( const QString& username ) +{ + auto pw_entry = getpwnam( VeyonCore::stripDomain( username ).toUtf8().constData() ); + + if( pw_entry ) + { + auto shell = QString::fromUtf8( pw_entry->pw_shell ); + + // Skip not real users + if ( !( shell.endsWith( QStringLiteral( "/false" ) ) || + shell.endsWith( QStringLiteral( "/true" ) ) || + shell.endsWith( QStringLiteral( "/null" ) ) || + shell.endsWith( QStringLiteral( "/nologin" ) ) ) ) + { + return QString::fromUtf8( pw_entry->pw_gecos ).split( QLatin1Char(',') ).first(); + } + } + + return QString(); +} + + + +QStringList LinuxUserFunctions::userGroups( bool queryDomainGroups ) +{ + Q_UNUSED(queryDomainGroups) + + QStringList groupList; + + QProcess getentProcess; + getentProcess.start( QStringLiteral("getent"), { QStringLiteral("group") } ); + getentProcess.waitForFinished(); + + const auto groups = QString::fromUtf8( getentProcess.readAll() ).split( QLatin1Char('\n') ); + + groupList.reserve( groups.size() ); + + for( const auto& group : groups ) + { + groupList += group.split( QLatin1Char(':') ).first(); + } + + const QStringList ignoredGroups( { + QStringLiteral("daemon"), + QStringLiteral("bin"), + QStringLiteral("tty"), + QStringLiteral("disk"), + QStringLiteral("lp"), + QStringLiteral("mail"), + QStringLiteral("news"), + QStringLiteral("uucp"), + QStringLiteral("man"), + QStringLiteral("proxy"), + QStringLiteral("kmem"), + QStringLiteral("dialout"), + QStringLiteral("fax"), + QStringLiteral("voice"), + QStringLiteral("cdrom"), + QStringLiteral("tape"), + QStringLiteral("audio"), + QStringLiteral("dip"), + QStringLiteral("www-data"), + QStringLiteral("backup"), + QStringLiteral("list"), + QStringLiteral("irc"), + QStringLiteral("src"), + QStringLiteral("gnats"), + QStringLiteral("shadow"), + QStringLiteral("utmp"), + QStringLiteral("video"), + QStringLiteral("sasl"), + QStringLiteral("plugdev"), + QStringLiteral("games"), + QStringLiteral("nogroup"), + QStringLiteral("libuuid"), + QStringLiteral("syslog"), + QStringLiteral("fuse"), + QStringLiteral("lpadmin"), + QStringLiteral("ssl-cert"), + QStringLiteral("messagebus"), + QStringLiteral("crontab"), + QStringLiteral("mlocate"), + QStringLiteral("avahi-autoipd"), + QStringLiteral("netdev"), + QStringLiteral("saned"), + QStringLiteral("sambashare"), + QStringLiteral("haldaemon"), + QStringLiteral("polkituser"), + QStringLiteral("mysql"), + QStringLiteral("avahi"), + QStringLiteral("klog"), + QStringLiteral("floppy"), + QStringLiteral("oprofile"), + QStringLiteral("netdev"), + QStringLiteral("dirmngr"), + QStringLiteral("vboxusers"), + QStringLiteral("bluetooth"), + QStringLiteral("colord"), + QStringLiteral("libvirtd"), + QStringLiteral("nm-openvpn"), + QStringLiteral("input"), + QStringLiteral("kvm"), + QStringLiteral("pulse"), + QStringLiteral("pulse-access"), + QStringLiteral("rtkit"), + QStringLiteral("scanner"), + QStringLiteral("sddm"), + QStringLiteral("systemd-bus-proxy"), + QStringLiteral("systemd-journal"), + QStringLiteral("systemd-network"), + QStringLiteral("systemd-resolve"), + QStringLiteral("systemd-timesync"), + QStringLiteral("utempter"), + QStringLiteral("uuidd"), + } ); + + for( const auto& ignoredGroup : ignoredGroups ) + { + groupList.removeAll( ignoredGroup ); + } + + // remove all empty entries + groupList.removeAll( QString() ); + + return groupList; +} + + + +QStringList LinuxUserFunctions::groupsOfUser( const QString& username, bool queryDomainGroups ) +{ + Q_UNUSED(queryDomainGroups) + + QStringList groupList; + + const auto strippedUsername = VeyonCore::stripDomain( username ); + + QProcess getentProcess; + getentProcess.start( QStringLiteral("getent"), { QStringLiteral("group") } ); + getentProcess.waitForFinished(); + + const auto groups = QString::fromUtf8( getentProcess.readAll() ).split( QLatin1Char('\n') ); + for( const auto& group : groups ) + { + const auto groupComponents = group.split( QLatin1Char(':') ); + if( groupComponents.size() == 4 && + groupComponents.last().split( QLatin1Char(',') ).contains( strippedUsername ) ) + { + groupList += groupComponents.first(); // clazy:exclude=reserve-candidates + } + } + + groupList.removeAll( QString() ); + + return groupList; +} + + + +bool LinuxUserFunctions::isAnyUserLoggedOn() +{ + QProcess whoProcess; + whoProcess.start( QStringLiteral("who"), QStringList{} ); + whoProcess.waitForFinished( WhoProcessTimeout ); + + if( whoProcess.exitCode() != 0 ) + { + return false; + } + + const auto displayManagerUsers = LinuxPlatformConfiguration( &VeyonCore::config() ).displayManagerUsers(). + split( QLatin1Char(',') ); + + const auto lines = whoProcess.readAll().split( '\n' ); + for( const auto& line : lines ) + { + const auto user = QString::fromUtf8( line.split( ' ' ).value( 0 ) ); + if( user.isEmpty() == false && + displayManagerUsers.contains( user ) == false ) + { + return true; + } + } + + return false; +} + + + +QString LinuxUserFunctions::currentUser() +{ + QString username; + + const auto envUser = qgetenv( "USER" ); + + struct passwd * pw_entry = nullptr; + if( envUser.isEmpty() == false ) + { + pw_entry = getpwnam( envUser.constData() ); + } + + if( pw_entry == nullptr ) + { + pw_entry = getpwuid( getuid() ); + } + + if( pw_entry ) + { + const auto shell = QString::fromUtf8( pw_entry->pw_shell ); + + // Skip not real users + if ( !( shell.endsWith( QStringLiteral( "/false" ) ) || + shell.endsWith( QStringLiteral( "/true" ) ) || + shell.endsWith( QStringLiteral( "/null" ) ) || + shell.endsWith( QStringLiteral( "/nologin" ) ) ) ) + { + username = QString::fromUtf8( pw_entry->pw_name ); + } + } + + if( username.isEmpty() ) + { + return QString::fromUtf8( envUser ); + } + + return username; +} + + + +bool LinuxUserFunctions::prepareLogon( const QString& username, const Password& password ) +{ + if( m_logonHelper.prepare( username, password ) ) + { + LinuxCoreFunctions::restartDisplayManagers(); + return true; + } + + return false; +} + + + +bool LinuxUserFunctions::performLogon( const QString& username, const Password& password ) +{ + LinuxKeyboardInput input; + + input.sendString( username ); + input.pressAndReleaseKey( XK_Tab ); + input.sendString( QString::fromUtf8( password.toByteArray() ) ); + input.pressAndReleaseKey( XK_Return ); + + return true; +} + + + +void LinuxUserFunctions::logoff() +{ + // logout via common session managers + LinuxCoreFunctions::kdeSessionManager()->asyncCall( QStringLiteral("logout"), + static_cast( LinuxDesktopIntegration::KDE::ShutdownConfirmNo ), + static_cast( LinuxDesktopIntegration::KDE::ShutdownTypeLogout ), + static_cast( LinuxDesktopIntegration::KDE::ShutdownModeForceNow ) ); + LinuxCoreFunctions::gnomeSessionManager()->asyncCall( QStringLiteral("Logout"), + static_cast( LinuxDesktopIntegration::Gnome::GSM_MANAGER_LOGOUT_MODE_FORCE ) ); + LinuxCoreFunctions::mateSessionManager()->asyncCall( QStringLiteral("Logout"), + static_cast( LinuxDesktopIntegration::Mate::GSM_LOGOUT_MODE_FORCE ) ); + + // Xfce logout + QProcess::startDetached( QStringLiteral("xfce4-session-logout --logout"), {} ); + + // LXDE logout + QProcess::startDetached( QStringLiteral("kill -TERM %1"). + arg( QProcessEnvironment::systemEnvironment().value( QStringLiteral("_LXSESSION_PID") ).toInt() ), {} ); + + // terminate session via systemd + LinuxCoreFunctions::systemdLoginManager()->asyncCall( QStringLiteral("TerminateSession"), + QProcessEnvironment::systemEnvironment().value( LinuxSessionFunctions::xdgSessionIdEnvVarName() ) ); + + // close session via ConsoleKit as a last resort + LinuxCoreFunctions::consoleKitManager()->asyncCall( QStringLiteral("CloseSession"), + QProcessEnvironment::systemEnvironment().value( QStringLiteral("XDG_SESSION_COOKIE") ) ); +} + + + +bool LinuxUserFunctions::authenticate( const QString& username, const Password& password ) +{ + QProcess p; + p.start( QStringLiteral( "veyon-auth-helper" ), QStringList{}, QProcess::ReadWrite | QProcess::Unbuffered ); + if( p.waitForStarted() == false ) + { + vCritical() << "failed to start VeyonAuthHelper"; + return false; + } + + const auto pamService = LinuxPlatformConfiguration( &VeyonCore::config() ).pamServiceName(); + + QDataStream ds( &p ); + ds << VeyonCore::stripDomain( username ).toUtf8(); + ds << password.toByteArray(); + ds << pamService.toUtf8(); + + p.waitForFinished( AuthHelperTimeout ); + + if( p.state() != QProcess::NotRunning || p.exitCode() != 0 ) + { + vCritical() << "VeyonAuthHelper failed:" << p.exitCode() + << p.readAllStandardOutput().trimmed() << p.readAllStandardError().trimmed(); + return false; + } + + vDebug() << "User authenticated successfully"; + return true; +} + + + +uid_t LinuxUserFunctions::userIdFromName( const QString& username ) +{ + const auto pw_entry = getpwnam( username.toUtf8().constData() ); + + if( pw_entry ) + { + return pw_entry->pw_uid; + } + + return 0; +} diff --git a/plugins/platform/linux/LinuxUserFunctions.h b/plugins/platform/linux/LinuxUserFunctions.h new file mode 100644 index 0000000..4f88e8d --- /dev/null +++ b/plugins/platform/linux/LinuxUserFunctions.h @@ -0,0 +1,59 @@ +/* + * LinuxUserFunctions.h - declaration of LinuxUserFunctions class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "LogonHelper.h" +#include "PlatformUserFunctions.h" + +#include + +// clazy:excludeall=copyable-polymorphic + +class LinuxUserFunctions : public PlatformUserFunctions +{ +public: + QString fullName( const QString& username ) override; + + QStringList userGroups( bool queryDomainGroups ) override; + QStringList groupsOfUser( const QString& username, bool queryDomainGroups ) override; + + bool isAnyUserLoggedOn() override; + QString currentUser() override; + + bool prepareLogon( const QString& username, const Password& password ) override; + bool performLogon( const QString& username, const Password& password ) override; + void logoff() override; + + bool authenticate( const QString& username, const Password& password ) override; + + static uid_t userIdFromName( const QString& username ); + +private: + static constexpr auto WhoProcessTimeout = 3000; + static constexpr auto AuthHelperTimeout = 10000; + + LogonHelper m_logonHelper{}; + +}; diff --git a/plugins/platform/linux/auth-helper/CMakeLists.txt b/plugins/platform/linux/auth-helper/CMakeLists.txt new file mode 100644 index 0000000..b71f90d --- /dev/null +++ b/plugins/platform/linux/auth-helper/CMakeLists.txt @@ -0,0 +1,12 @@ +FIND_PACKAGE(PAM REQUIRED) + +SET(CMAKE_SKIP_BUILD_RPATH TRUE) +SET(CMAKE_INSTALL_RPATH "") +SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) + +ADD_EXECUTABLE(veyon-auth-helper ${CMAKE_CURRENT_SOURCE_DIR}/VeyonAuthHelper.cpp) + +TARGET_INCLUDE_DIRECTORIES(veyon-auth-helper PRIVATE ${PAM_INCLUDE_DIR}) +TARGET_LINK_LIBRARIES(veyon-auth-helper Qt5::Core ${PAM_LIBRARY}) + +INSTALL(TARGETS veyon-auth-helper RUNTIME DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE SETUID GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) diff --git a/plugins/platform/linux/auth-helper/VeyonAuthHelper.cpp b/plugins/platform/linux/auth-helper/VeyonAuthHelper.cpp new file mode 100644 index 0000000..05a8396 --- /dev/null +++ b/plugins/platform/linux/auth-helper/VeyonAuthHelper.cpp @@ -0,0 +1,104 @@ +/* + * VeyonAuthHelper.cpp - main file for Veyon Authentication Helper + * + * Copyright (c) 2010-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include + +static QByteArray pam_username; // clazy:exclude=non-pod-global-static +static QByteArray pam_password; // clazy:exclude=non-pod-global-static +static QByteArray pam_service; // clazy:exclude=non-pod-global-static + +static int pam_conv( int num_msg, const struct pam_message** msg, struct pam_response** resp, void * ) +{ + auto reply = reinterpret_cast( + malloc( sizeof(struct pam_response) * static_cast( num_msg ) ) ); + if( reply == nullptr ) + { + return PAM_CONV_ERR; + } + + for( int replies = 0; replies < num_msg; ++replies ) + { + switch( msg[replies]->msg_style ) + { + case PAM_PROMPT_ECHO_ON: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = strdup( pam_username.constData() ); + break; + case PAM_PROMPT_ECHO_OFF: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = strdup( pam_password.constData() ); + break; + case PAM_TEXT_INFO: + case PAM_ERROR_MSG: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = nullptr; + break; + default: + free( reply ); + return PAM_CONV_ERR; + } + } + + *resp = reply; + return PAM_SUCCESS; +} + + +int main() +{ + QFile stdIn; + stdIn.open( 0, QFile::ReadOnly | QFile::Unbuffered ); + QDataStream ds( &stdIn ); + ds >> pam_username; + ds >> pam_password; + ds >> pam_service; + + if( pam_service.isEmpty() ) + { + pam_service = QByteArrayLiteral("login"); + } + + struct pam_conv pconv = { &pam_conv, nullptr }; + pam_handle_t* pamh = nullptr; + auto err = pam_start( pam_service.constData(), nullptr, &pconv, &pamh ); + if( err == PAM_SUCCESS ) + { + err = pam_authenticate( pamh, PAM_SILENT ); + if( err != PAM_SUCCESS ) + { + printf( "pam_authenticate: %s\n", pam_strerror( pamh, err ) ); + } + } + else + { + printf( "pam_start: %s\n", pam_strerror( pamh, err ) ); + } + + pam_end( pamh, err ); + + return err == PAM_SUCCESS ? 0 : -1; +} diff --git a/plugins/platform/linux/linux.qrc b/plugins/platform/linux/linux.qrc new file mode 100644 index 0000000..057bfff --- /dev/null +++ b/plugins/platform/linux/linux.qrc @@ -0,0 +1,5 @@ + + + tux.png + + diff --git a/plugins/platform/linux/tux.png b/plugins/platform/linux/tux.png new file mode 100644 index 0000000000000000000000000000000000000000..5969c848c6c895c4cd313e2d6f2e5bd824fe1f8f GIT binary patch literal 3296 zcmV<63?K7}P)MzCV_|S* zE^l&Yo9;Xs000bMNklmLVO90eC&Zd8_J^F&|aJB%`Z3{rcT}~EYmwf@0k&dPR+3TcZQye9A=SEYG^O;xKjm~rqAY#WSA+r z=Q)lC4a^HAz_U)B1Lk=M4AV&NWncic7ihwWmFB@vo>P55sUW0sLMM2XBm=Qj>Ig)E zSr3@wQ~~A-!a_fS3k}4|2*%fDePFdyhd_xS`WW}%l!2JHXRIfe<+N*mjSd!Ir&9%Z zRY;)?ILBC~YU4dyqcvC~yh;1}J(E77JylMf1&V}W0L(@ZmI5$cqm=_dJ43aUn8S10{O^yg3}G>C+Fvc?^P*V~_{fR$Z}*x%kvd>6>C83cfX?s+Ls({r z3u+k8BAVzMEI_;b%6S8zzt{Yk3}Gt(l{`;2qsS(UiEPmWAvjJ6L)ivEp;=n2(0Oyf zQm^qxI7cqC4e#0?`^jYt8})TUx!E9SNbcjT0dRv{@*3X&xG5!%?}ErTP|WCnSmC@` zFoaBZ*89)$<$k?b!w9MjL@%<6A*?nG1TV&z3DDyDgJbAtTr0=LZ<*`C5hjzxEZ$%? zlgQ*9)0x3EDS{xoGOl%0D#Y^y20`^yv6-Dzhgl(u2aVU8K2R6u!1~u0b12Z zoFG7n&*Wq-TN2>IP+O&^yM6Vb`iQjq3@*ePE|U^Ug34~~37)=LP8iMIgjE!U-Y%cn zoxT57|Gocu%L42TS3EbMyN187`5Rh*9b|`=;IiD?9i3lFprPAr39v3)Wl3$~E!~X1 z>(6r8-^gcZwk1Ggn6eo`HsxK_jp+aL`%)EX;(YAFS2UAm<_X4jr^#jw1uUhh2#rt+F@Tvb#A_)7o1 zf$TQ%2}*HynEd8@%_(7o_9?s5n;{+H1)c0@vy{2J%a7Tq4|+r20fvdIf@oo%f$n08 zgC*GBrKPm;J=yOyWi=!5O3_U8;M*}wCDtM1WQ@^cG&CiGIO-K#h0!^OCReT7B)FO{}oUu*x*uKHCuQ)!%t<(_?7R z2f({A6X2+U?0GhrERE#n@tprvW9EOi9;nn2JB-v@+{@vvK4~m#eGTu|cXINGC6?8U<^&6Gzv-Z&cIW2 z?Ky>#VYX#5CO!h(DfkPpQVWM-jbo(YVj9qb%WNK)6(0cUw{?y5#Swv0oDMgNHVGQi6&kPc-yIz(a1%83Kj%)L3{+L@Hb@y?ZE1o(r?t> zzW+0TwY2kgyer9MeV`%b5aSqNPslyOf77BLv{EwTBfy&y)E&>nkbE1f%#YIODY!G* z%0PWEK5<}?r{R8n!=OH9&hG^z=-|EaNutRU7PZO^@p%8YOHDE>xd_x#p~vUhoQ{bA zGZ0pUtrrm*Jey{faiIUik7Q3W`~1q83iV9Q_Lz+Q1wehcf*EbwR9PH{%h!}8!Uq9H zx|ymQGs~nN*bZ{(Pa^<1p{`hA0{M*d49GpMJ7PQfN112+FY}ykP&zxJM*lo2rP}8$ zru0+zT=x}g=!bk0!#YCcQE43?qUe3UpIHl{6ckk;`fzh=XV8t$=zt8wJ`!V<7m^#4 zNz$AVCfuFsXWGQ51jZ#K8n#*M{OZOAu*O8@Vx%p~_YUPOz#Kjf(G&L~NOm;e{8MsBNakgBcWCaMfDW3W-3VYE19>^(4U|-5 zO;k5OGFjJ2&we4OzIU*#fzX8Tdc@zFihTR zx86r^-5;H?U!4_UrN?EHOkJ6c1embV(zj$n6a;YHFRYEY`QQ^@$zfe*M^yop7Lwi}JAj z@;A0vGJH0H86#^bL%3k+n{{L$XA*yDuLu0r>SlKnqx=LYvM9iRv|EU*-~frpTDuhh zZC1;jstpmrNXt%eFu69Oh*AW3%3dF+dC_w7J5fnu3EM4~OxD`0H*6#oDYqxU$0KbR z@TN@yYq2s58SyI8W~+2cDo5C8zKzMKomfGH!W2?zwI#q?2uU|t=iggS+S~8F$Y9bf zCQGa7$5flu1Ubkl8zmF2yM>iDS9AMNDTvsUH&aHE&5Cn3a}kv1?8E_ioG#D1cKT145pW>JN(}Nf#i&S<kfFpBi@lh~4H?RHRiYjZ&$U)J~34a=X~lmFkBh8ff!yA+74dgx^Ik8@MPr zN`>$5S`wh4)4#vpvorK*l>ar8{9Y~%;nOH=doQ;nKwBq&eItp;Idwn`iX@ds<&==Y zOqR2aBN4n4e-v2~ps7>8y$(S>v$^mMjVG^@Ve*0RP;_5LQou5Ha5jRF#Be>DTfUOF zl}Sopc_`tos|$qj>@^IrW-1wnAoc3t?)T4kdMErzNmmP`M4hV^ zNw)eo+nW=;m1pIGFo;5CQ_KX + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include + +#include "Computer.h" +#include "ComputerControlInterface.h" +#include "FeatureWorkerManager.h" +#include "PlatformCoreFunctions.h" +#include "PlatformUserFunctions.h" +#include "PowerControlFeaturePlugin.h" +#include "PowerDownTimeInputDialog.h" +#include "VeyonConfiguration.h" +#include "VeyonMasterInterface.h" +#include "VeyonServerInterface.h" + + +PowerControlFeaturePlugin::PowerControlFeaturePlugin( QObject* parent ) : + QObject( parent ), + m_commands( { +{ QStringLiteral("on"), tr( "Power on a computer via Wake-on-LAN (WOL)" ) }, + } ), + m_powerOnFeature( QStringLiteral( "PowerOn" ), + Feature::Action | Feature::AllComponents, + Feature::Uid( "f483c659-b5e7-4dbc-bd91-2c9403e70ebd" ), + Feature::Uid(), + tr( "Power on" ), {}, + tr( "Click this button to power on all computers. " + "This way you do not have to power on each computer by hand." ), + QStringLiteral(":/powercontrol/preferences-system-power-management.png") ), + m_rebootFeature( QStringLiteral( "Reboot" ), + Feature::Action | Feature::AllComponents, + Feature::Uid( "4f7d98f0-395a-4fff-b968-e49b8d0f748c" ), + Feature::Uid(), + tr( "Reboot" ), {}, + tr( "Click this button to reboot all computers." ), + QStringLiteral(":/powercontrol/system-reboot.png") ), + m_powerDownFeature( QStringLiteral( "PowerDown" ), + Feature::Action | Feature::AllComponents, + Feature::Uid( "6f5a27a0-0e2f-496e-afcc-7aae62eede10" ), + Feature::Uid(), + tr( "Power down" ), {}, + tr( "Click this button to power down all computers. " + "This way you do not have to power down each computer by hand." ), + QStringLiteral(":/powercontrol/system-shutdown.png") ), + m_powerDownNowFeature( QStringLiteral( "PowerDownNow" ), + Feature::Action | Feature::AllComponents, + Feature::Uid( "a88039f2-6716-40d8-b4e1-9f5cd48e91ed" ), + m_powerDownFeature.uid(), + tr( "Power down now" ), {}, {} ), + m_installUpdatesAndPowerDownFeature( QStringLiteral( "InstallUpdatesAndPowerDown" ), + Feature::Action | Feature::AllComponents, + Feature::Uid( "09bcb3a1-fc11-4d03-8cf1-efd26be8655b" ), + m_powerDownFeature.uid(), + tr( "Install updates and power down" ), {}, {} ), + m_powerDownConfirmedFeature( QStringLiteral( "PowerDownConfirmed" ), + Feature::Action | Feature::AllComponents, + Feature::Uid( "ea2406be-d5c7-42b8-9f04-53469d3cc34c" ), + m_powerDownFeature.uid(), + tr( "Power down after user confirmation" ), {}, {} ), + m_powerDownDelayedFeature( QStringLiteral( "PowerDownDelayed" ), + Feature::Action | Feature::AllComponents, + Feature::Uid( "352de795-7fc4-4850-bc57-525bcb7033f5" ), + m_powerDownFeature.uid(), + tr( "Power down after timeout" ), {}, {} ), + m_features( { m_powerOnFeature, m_rebootFeature, m_powerDownFeature, m_powerDownNowFeature, + m_installUpdatesAndPowerDownFeature, m_powerDownConfirmedFeature, m_powerDownDelayedFeature } ) +{ +} + + + +QStringList PowerControlFeaturePlugin::commands() const +{ + return m_commands.keys(); +} + + + +QString PowerControlFeaturePlugin::commandHelp( const QString& command ) const +{ + return m_commands.value( command ); +} + + + +const FeatureList &PowerControlFeaturePlugin::featureList() const +{ + return m_features; +} + + + +bool PowerControlFeaturePlugin::controlFeature( Feature::Uid featureUid, + Operation operation, + const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( hasFeature( featureUid ) == false || operation != Operation::Start ) + { + return false; + } + + if( featureUid == m_powerOnFeature.uid() ) + { + for( const auto& controlInterface : computerControlInterfaces ) + { + broadcastWOLPacket( controlInterface->computer().macAddress() ); + } + } + else if( featureUid == m_powerDownDelayedFeature.uid() ) + { + const auto shutdownTimeout = arguments.value( argToString(Argument::ShutdownTimeout), 60 ).toInt(); + + sendFeatureMessage( FeatureMessage{ featureUid, FeatureMessage::DefaultCommand } + .addArgument( Argument::ShutdownTimeout, shutdownTimeout ), + computerControlInterfaces ); + } + else + { + sendFeatureMessage( FeatureMessage{ featureUid, FeatureMessage::DefaultCommand }, computerControlInterfaces ); + } + + return true; +} + + + +bool PowerControlFeaturePlugin::startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( feature == m_powerOnFeature ) + { + return controlFeature( feature.uid(), Operation::Start, {}, computerControlInterfaces ); + } + + if( feature == m_powerDownDelayedFeature ) + { + PowerDownTimeInputDialog dialog( master.mainWindow() ); + + if( dialog.exec() ) + { + return controlFeature( feature.uid(), Operation::Start, + { { argToString(Argument::ShutdownTimeout), dialog.seconds() } }, + computerControlInterfaces ); + } + + return true; + } + + if( confirmFeatureExecution( feature, master.mainWindow() ) == false ) + { + return false; + } + + return controlFeature( feature.uid(), Operation::Start, {}, computerControlInterfaces ); +} + + + +bool PowerControlFeaturePlugin::handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) +{ + Q_UNUSED(messageContext) + + auto& featureWorkerManager = server.featureWorkerManager(); + + if( message.featureUid() == m_powerDownFeature.uid() || + message.featureUid() == m_powerDownNowFeature.uid() || + message.featureUid() == m_installUpdatesAndPowerDownFeature.uid() ) + { + const auto installUpdates = message.featureUid() == m_installUpdatesAndPowerDownFeature.uid(); + VeyonCore::platform().coreFunctions().powerDown( installUpdates ); + } + else if( message.featureUid() == m_powerDownConfirmedFeature.uid() ) + { + if( VeyonCore::platform().userFunctions().isAnyUserLoggedOn() == false ) + { + VeyonCore::platform().coreFunctions().powerDown( false ); + } + else + { + featureWorkerManager.sendMessageToManagedSystemWorker( message ); + } + } + else if( message.featureUid() == m_powerDownDelayedFeature.uid() ) + { + featureWorkerManager.sendMessageToManagedSystemWorker( message ); + } + else if( message.featureUid() == m_rebootFeature.uid() ) + { + VeyonCore::platform().coreFunctions().reboot(); + } + else + { + return false; + } + + return true; +} + + + +bool PowerControlFeaturePlugin::handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) +{ + Q_UNUSED(worker) + + if( message.featureUid() == m_powerDownConfirmedFeature.uid() ) + { + confirmShutdown(); + return true; + } + else if( message.featureUid() == m_powerDownDelayedFeature.uid() ) + { + displayShutdownTimeout( message.argument( Argument::ShutdownTimeout ).toInt() ); + return true; + } + + return false; +} + + + +CommandLinePluginInterface::RunResult PowerControlFeaturePlugin::handle_help( const QStringList& arguments ) +{ + const auto command = arguments.value( 0 ); + + const QMap commands = { + { QStringLiteral("on"), + QStringList( { QStringLiteral("<%1>").arg( tr("MAC ADDRESS") ), + tr( "This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address." ) } ) }, + }; + + if( commands.contains( command ) ) + { + const auto& helpString = commands[command]; + print( QStringLiteral("\n%1 %2 %3\n\n%4\n\n").arg( commandLineModuleName(), command, helpString[0], helpString[1] ) ); + + return NoResult; + } + + print( tr("Please specify the command to display help for!") ); + + return Unknown; +} + + + +CommandLinePluginInterface::RunResult PowerControlFeaturePlugin::handle_on( const QStringList& arguments ) +{ + if( arguments.size() < 1 ) + { + return NotEnoughArguments; + } + + return broadcastWOLPacket( arguments.first() ) ? Successful : Failed; +} + + + +bool PowerControlFeaturePlugin::confirmFeatureExecution( const Feature& feature, QWidget* parent ) +{ + if( VeyonCore::config().confirmUnsafeActions() == false ) + { + return true; + } + + if( feature == m_rebootFeature ) + { + return QMessageBox::question( parent, tr( "Confirm reboot" ), + tr( "Do you really want to reboot the selected computers?" ) ) == + QMessageBox::Yes; + } + else if( feature == m_powerDownFeature || + feature == m_powerDownNowFeature || + feature == m_installUpdatesAndPowerDownFeature || + feature == m_powerDownConfirmedFeature || + feature == m_powerDownDelayedFeature ) + { + return QMessageBox::question( parent, tr( "Confirm power down" ), + tr( "Do you really want to power down the selected computer?" ) ) == + QMessageBox::Yes; + } + + return true; +} + + + +bool PowerControlFeaturePlugin::broadcastWOLPacket( QString macAddress ) +{ + const int MAC_SIZE = 6; + unsigned int mac[MAC_SIZE]; // Flawfinder: ignore + + if( macAddress.isEmpty() ) + { + return false; + } + + const auto originalMacAddress = macAddress; + + // remove all possible delimiters + macAddress.replace( QLatin1Char(':'), QString() ); + macAddress.replace( QLatin1Char('-'), QString() ); + macAddress.replace( QLatin1Char('.'), QString() ); + + if( sscanf( macAddress.toUtf8().constData(), + "%2x%2x%2x%2x%2x%2x", + &mac[0], + &mac[1], + &mac[2], + &mac[3], + &mac[4], + &mac[5] ) != MAC_SIZE ) + { + CommandLineIO::error( tr( "Invalid MAC address specified!" ) ); + vWarning() << "invalid MAC address" << originalMacAddress; + return false; + } + + QByteArray datagram( MAC_SIZE*17, static_cast( 0xff ) ); + + for( int i = 1; i < 17; ++i ) + { + for(int j = 0; j < MAC_SIZE; ++j ) + { + datagram[i*MAC_SIZE+j] = static_cast( mac[j] ); + } + } + + QUdpSocket udpSocket; + + bool success = ( udpSocket.writeDatagram( datagram, QHostAddress::Broadcast, 9 ) == datagram.size() ); + + const auto networkInterfaces = QNetworkInterface::allInterfaces(); + for( const auto& networkInterface : networkInterfaces ) + { + const auto addressEntries = networkInterface.addressEntries(); + for( const auto& addressEntry : addressEntries ) + { + if( addressEntry.broadcast().isNull() == false ) + { + success &= ( udpSocket.writeDatagram( datagram, addressEntry.broadcast(), 9 ) == datagram.size() ); + } + } + } + + return success; +} + + + +void PowerControlFeaturePlugin::confirmShutdown() +{ + QMessageBox m( QMessageBox::Question, tr( "Confirm power down" ), + tr( "The computer was remotely requested to power down. Do you want to power down the computer now?" ), + QMessageBox::Yes | QMessageBox::No ); + m.show(); + VeyonCore::platform().coreFunctions().raiseWindow( &m, true ); + + if( m.exec() == QMessageBox::Yes ) + { + VeyonCore::platform().coreFunctions().powerDown( false ); + } +} + + + +static void updateDialog( QProgressDialog* dialog, int newValue ) +{ + dialog->setValue( newValue ); + + const auto remainingSeconds = dialog->maximum() - newValue; + + dialog->setLabelText( PowerControlFeaturePlugin::tr( + "The computer will be powered down in %1 minutes, %2 seconds.\n\n" + "Please save your work and close all programs."). + arg( remainingSeconds / 60, 2, 10, QLatin1Char('0') ). + arg( remainingSeconds % 60, 2, 10, QLatin1Char('0') ) ); + + if( remainingSeconds <= 0 ) + { + VeyonCore::platform().coreFunctions().powerDown( false ); + } + +} + +void PowerControlFeaturePlugin::displayShutdownTimeout( int shutdownTimeout ) +{ + QProgressDialog dialog; + dialog.setAutoReset( false ); + dialog.setMinimum( 0 ); + dialog.setMaximum( shutdownTimeout ); + dialog.setCancelButton( nullptr ); + dialog.setWindowFlags( Qt::Window | Qt::CustomizeWindowHint | Qt::WindowTitleHint ); + + auto progressBar = dialog.findChild(); + if( progressBar ) + { + progressBar->setTextVisible( false ); + } + + updateDialog( &dialog, 0 ); + + dialog.show(); + VeyonCore::platform().coreFunctions().raiseWindow( &dialog, true ); + + QTimer powerdownTimer; + powerdownTimer.start( 1000 ); + + connect( &powerdownTimer, &QTimer::timeout, this, [&dialog]() { + updateDialog( &dialog, dialog.value()+1 ); + } ); + + dialog.exec(); +} diff --git a/plugins/powercontrol/PowerControlFeaturePlugin.h b/plugins/powercontrol/PowerControlFeaturePlugin.h new file mode 100644 index 0000000..ad3e7fa --- /dev/null +++ b/plugins/powercontrol/PowerControlFeaturePlugin.h @@ -0,0 +1,130 @@ +/* + * PowerControlFeaturePlugin.h - declaration of PowerControlFeaturePlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CommandLineIO.h" +#include "CommandLinePluginInterface.h" +#include "Feature.h" +#include "FeatureProviderInterface.h" + +class PowerControlFeaturePlugin : public QObject, + PluginInterface, + CommandLineIO, + CommandLinePluginInterface, + FeatureProviderInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.PowerControl") + Q_INTERFACES(PluginInterface CommandLinePluginInterface FeatureProviderInterface) +public: + enum class Argument + { + ShutdownTimeout + }; + Q_ENUM(Argument) + + explicit PowerControlFeaturePlugin( QObject* parent = nullptr ); + ~PowerControlFeaturePlugin() override = default; + + Plugin::Uid uid() const override + { + return QStringLiteral("4122e8ca-b617-4e36-b851-8e050ed2d82e"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 2 ); + } + + QString name() const override + { + return QStringLiteral("PowerControl"); + } + + QString description() const override + { + return tr( "Power on/down or reboot a computer" ); + } + + QString vendor() const override + { + return QStringLiteral("Veyon Community"); + } + + QString copyright() const override + { + return QStringLiteral("Tobias Junghans"); + } + + QString commandLineModuleName() const override + { + return QStringLiteral( "power" ); + } + + QString commandLineModuleHelp() const override + { + return tr( "Commands for controlling power status of computers" ); + } + + QStringList commands() const override; + QString commandHelp( const QString& command ) const override; + + const FeatureList& featureList() const override; + + bool controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) override; + + bool handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) override; + +public Q_SLOTS: + CommandLinePluginInterface::RunResult handle_help( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_on( const QStringList& arguments ); + +private: + bool confirmFeatureExecution( const Feature& feature, QWidget* parent ); + static bool broadcastWOLPacket( QString macAddress ); + + void confirmShutdown(); + void displayShutdownTimeout( int shutdownTimeout ); + + QMap m_commands; + + const Feature m_powerOnFeature; + const Feature m_rebootFeature; + const Feature m_powerDownFeature; + const Feature m_powerDownNowFeature; + const Feature m_installUpdatesAndPowerDownFeature; + const Feature m_powerDownConfirmedFeature; + const Feature m_powerDownDelayedFeature; + const FeatureList m_features; + +}; diff --git a/plugins/powercontrol/PowerDownTimeInputDialog.cpp b/plugins/powercontrol/PowerDownTimeInputDialog.cpp new file mode 100644 index 0000000..512f3f1 --- /dev/null +++ b/plugins/powercontrol/PowerDownTimeInputDialog.cpp @@ -0,0 +1,67 @@ +/* + * PowerDownTimeInputDialog.h - implementation of PowerDownTimeInputDialog class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "QtCompat.h" +#include "PowerDownTimeInputDialog.h" + +#include "ui_PowerDownTimeInputDialog.h" + + +PowerDownTimeInputDialog::PowerDownTimeInputDialog( QWidget *parent ) : + QDialog( parent ), + ui( new Ui::PowerDownTimeInputDialog ), + m_seconds( 0 ) +{ + ui->setupUi( this ); + + updateSeconds(); + + connect( ui->minutesSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &PowerDownTimeInputDialog::updateSeconds ); + connect( ui->secondsSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &PowerDownTimeInputDialog::updateSeconds ); +} + + + +PowerDownTimeInputDialog::~PowerDownTimeInputDialog() +{ + delete ui; +} + + + +int PowerDownTimeInputDialog::seconds() const +{ + return m_seconds; +} + + + +void PowerDownTimeInputDialog::updateSeconds() +{ + m_seconds = ui->minutesSpinBox->value() * 60 + ui->secondsSpinBox->value(); + + ui->buttonBox->button( QDialogButtonBox::Ok )->setEnabled( m_seconds > 0 ); +} diff --git a/plugins/powercontrol/PowerDownTimeInputDialog.h b/plugins/powercontrol/PowerDownTimeInputDialog.h new file mode 100644 index 0000000..941034f --- /dev/null +++ b/plugins/powercontrol/PowerDownTimeInputDialog.h @@ -0,0 +1,49 @@ +/* + * PowerDownTimeInputDialog.h - declaration of PowerDownTimeInputDialog class + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +namespace Ui +{ + class PowerDownTimeInputDialog; +} + +class PowerDownTimeInputDialog : public QDialog +{ + Q_OBJECT +public: + explicit PowerDownTimeInputDialog( QWidget *parent ); + ~PowerDownTimeInputDialog() override; + + int seconds() const; + +private: + void updateSeconds(); + + Ui::PowerDownTimeInputDialog* ui; + int m_seconds; + +} ; diff --git a/plugins/powercontrol/PowerDownTimeInputDialog.ui b/plugins/powercontrol/PowerDownTimeInputDialog.ui new file mode 100644 index 0000000..03074cf --- /dev/null +++ b/plugins/powercontrol/PowerDownTimeInputDialog.ui @@ -0,0 +1,128 @@ + + + Tobias Junghans + PowerDownTimeInputDialog + + + Power down + + + + :/powercontrol/system-shutdown.png:/powercontrol/system-shutdown.png + + + + + + Please specify a timeout for powering down the selected computers: + + + true + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 1440 + + + + + + + minutes + + + + + + + 59 + + + + + + + seconds + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + PowerDownTimeInputDialog + accept() + + + 199 + 384 + + + 199 + 206 + + + + + buttonBox + rejected() + PowerDownTimeInputDialog + reject() + + + 199 + 384 + + + 199 + 206 + + + + + diff --git a/plugins/powercontrol/powercontrol.qrc b/plugins/powercontrol/powercontrol.qrc new file mode 100644 index 0000000..586bc49 --- /dev/null +++ b/plugins/powercontrol/powercontrol.qrc @@ -0,0 +1,7 @@ + + + preferences-system-power-management.png + system-shutdown.png + system-reboot.png + + diff --git a/plugins/powercontrol/preferences-system-power-management.png b/plugins/powercontrol/preferences-system-power-management.png new file mode 100644 index 0000000000000000000000000000000000000000..48f092eca3d199287414d0a6bcb768f424924417 GIT binary patch literal 4217 zcmV-<5QguGP)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H15B5n!K~#9!?VWjaRArjRpZ6}6KnMvvw(V)>$fgj&5+H=NTUi>A zut?BE1wm*LS(;r@+NDdUTS1UTL}Zgd43GqgqyWJNku4w!1j3TgGZLg{&U8<5T$;7& z-uuoUSqNmSr0Q1PFWl#xoKva0*7y9r^?vvL2)6EOk(~rK0JdxZY}o+VvH`GV17OPr zz?Kbw;BxMRa`l0b=8BHi0HO>+D`IX6AOb)b17QS20%%ln$Q1^DB;YEF|9)bkOJpFQ z2>F_f{IKtDUUc?>W&>bBy>c2&j1b2o5_2CA^=6L$T6fV zc_D)d{04w7g4R^IU|v|z#*>;s&}{&`H2!LyI)*@V8E92c8r%5302<&(-Z(Q>bDF4sVuu^z1JFrcRc_v zo_CA{(-H>%5_gk`|4B^K7e>1`-<1G3FCz5kYwCOgO~c)-6w=(27RKP}T?4@MypYzC z!Rf^409ycXULqtdjCP-?_W;b!RYx^jnUq=JOI;j)X@Mo*oOI^ z045r>EXKXbvH(oU327z6_!dC8?UyOKBI(q1Nv!+4km0=|Klz(rGl2 zH8mf=FKt@_s(&ctiCWSLKL!744G9{r-tj>1Y|=<5o|=)vSW~pyD!BKnM)o zP@1mke*N#J05IWe<&OaUMO5=!H12^(QMb2Ur?^^FYCchM&8jv86rmvCIjg#93rqq) zJp-Vvr~teWwFxa6-~Qt9RM&%5nqMlVAw(((&aUd_Y3FwUo!N4(5&2QPbJ0`T* zSmFDru7|2Mzqp40$PEO7KtiY0T|GJe1>mS-0l+Vc3c!Rmsc6x-;;8jBSL+(hFHI>q zl%a;P<$o)K=B?@Op6gct#vf5n0O%xU-QNupqEjl}HqF(hqWN@3;!w#J0@P53($Mk{ z99z@F)6H)HJeQ4M5UKwM>j!A6y!49RfEJCrR=RC^R$H&lF9k)hJ_NdU!*fq^pPJ_i zcm=@o*^UGTZxu85cg6VV^;K@4>1tOi^Go+t8URWI>q5XJ4O`perpqq?j6bZt&xEOB z=KghPUPbON2B3Yl=2MCJxUG~Z-b)E(D?_k&ZBNfEzX0&uA@vkUykHK^BjPYV`r~T% znUxiD%X})|kgDGN(p}{S3?}-z0M30-zIUkC;Q!dWrfYok8Z?iHs~!MX zr#hQo`aur0Bn0jU+@7$S0$kS!!1D(~TIpouiGH)4$)hU4`ZmH3t}d$<@VY^SR_0#bOXJjKRsjss8C; zjR1^0q`U~G1!Cp?Sd448(yM*vWOcpO;6vTbFFi&Hfps8Y%v$%bXR%KJJbOr8O^op( z0WhxJ3N(+1^$q}6_bSaNqDoV#D)UQsm8}E8%H-cx{{~UbRlotqZV(R;#r$Rwu^89x zL$CLrt4BcM#O@u}emR;&ct2b1<*Z&cn}5f@ zT-mdON&zBK|zeW_Tw z8#p><0h%^3FZZv@o`Z{pr~GPu>2^sr83IObYCo-muK*l@g%@$su1nCaReVfa)l8nZ z61;zN>SEz(Bxk>Diut7<$y^AsQ~PSZ4FrwctLz3~>7iilIzhiKl>;AD&h&k2#CGp< z|KYhPC^~JL`DNn-axn=077=!(_SFXY1i;gK3S42%<6ws+j5frxBT6TT4k$T5kN^jqiK{p!Yr~_i4&i2rwL60fHM;p}u{6#FlE0yHt1vDTn`5Y3c;T zd@6I#R|wqt4_d>Fr0N%~_UaN(?oo2U;st@$ZxZx!B>@O{J)!#Oswsz^xKwzy($XVf z=9k~M?hu?z>#x-~F~Y0UCw-wTB|yj(KJ}Xf{l+Z_UK>$$Y^(1J&m!f}Ti%&Z<+_|E zn195XwSrK0oo!F(|Ab;dx=tKoaEFOj%gc^8bAAh=%apHfNPe;%oa-tpc1 znq~+E!2FVPzmq;t`=eh0c>D{si)8ROq609y|N2UD|2$kS%&QmX7xy5kW7?yhQ+@>i z01|g9xlCv$GynsV@5udm*mP)qjYYU9<^wppWsv6oAW;DD#OKOf0B?&7z{~-+zb?M{ z(1KgJUwrdHz?_UhCWVPhbm4wOR)h`|IdkBxH-;}4=4120h4sdK00u*Ax-}l=1d0TJ z$9F1w0Q3_TfEfcY-xCoaE^XrTGAmFcC;Yx7k3;+O(*r^-`i#aAR{m~W0x&Oct{Jro(y)mDFoKNF5^TWjh z!0;V%5`ogh1YkKX7yb?D2bNZ9QmOe=$xI%rnqPe5{CMUNeQO=SslA*|NT*_zb0D-9 z4S;EbmLX&RGF&eFTSfCrr;`4tSbj`=zp6qogw=cEsfN8dEeC4WdvlSC>746PUfpUkgjnsUa9mO4EMg248iL^E{9R$1{#Zs5+~(CRCMz)=j;o5C%#5cYBn2fP1lW9H zY`yj`ebiUuaKqfDMPL$<27Ri$3lenYQ7#vBBrvJAtXr-=!>nRI@Box3ev34nL5oG~ zB9=7@P44Ou(_1?eHD{oHR=|yTbc_5nfK>n*!B%hvw(VMBGRELZ-RLW1b*$zOBoEL8j3!1?+6qHzW}n3`lqWM ziH#2d^r)xiA19I~?@sa@vrY|UF*%)(4oAzQz%&=&Hua^EN6c^c%+ogoqHixW0Odi* zZX*w7;B^3TLfTGz+A!$7CX$|7Q@lPKfU3lx{^{}{X3PZ87l35(J2r^07r>IgCh4Eo z_FX+2fa=-^oA7Ipl*qssfGqZMDxCu(LxGmO>uD5-(+d?;zuTmCGMJNyxF3q6mj$}_ zpaVD#&|ZTmZQoPg#pJO8Faw5kL>QXiHO8PZo^-WOu%`#*mM zKMUVHt&e@J#B|6-2AZ)hx0Z;b3{WcxXvRQefWrW41fU6ku#!Wr0{8*oD*#<3;AaCU zkiZvMqVs@$;ol?_ShXQ%17OPrz?KbwEgJw^HUPG40BqR+*s=kz<&FLyvP#0to`Gr= P00000NkvXXu0mjfRd(l? literal 0 HcmV?d00001 diff --git a/plugins/powercontrol/system-reboot.png b/plugins/powercontrol/system-reboot.png new file mode 100644 index 0000000000000000000000000000000000000000..30bb99b4f5b1424f9a2ca8ef9eba7058c200b561 GIT binary patch literal 6038 zcmZu#cTm&s?|-)xC}kC76c8wTZ)AuFq5M&QUM5;1m zBgjy;f|UK`^Y8C|xl3M8?sB;#PoBFcNxZM8P6=a%0RTX$siA6kjhO!!N^&g+6dgvd z0pf?&G=^SNIP^inwNB=vVc`b=vNr!QhzWD=`T#KbtC{;7dAs-rIXrd-f`WoX-MrlW zoE&_dMZF)p7VO?(1^~tznySji!LvKhLxPRhj^6ide4d|?%;Vlpr+=mkrzxB00JRPg z*IGiHh4{V9X?%uEQ$0=SgdT30{;sbmgPL9|X7FbwkWo=Vj=V%N2wL*&xx(@JWv-4( zQ|5#9#kYO(;{z(~pXNV>br;F+g^YJvqh}*0?zu6(0%=$=W8RegAK3gU^N5l~TcxiX zooEllAvUOdmT`=T5+2am9+gT?G=r~IvIqS z$Q}VGucj5l9SZu-$%Nj#^sjMqX9k{$>^a%hNqF3(whw*28QRokZ`*g@_O{$qD3TJ@ zl#+KE3BUOsz78Os;k-l7xQLu6Ga3kIcFd;Ogl~ZVV3NgVSm3@PaX%5Ha8fg@ighq} z1y$Qlaf82*q&T&oF_l7>=pY=p&`*C5#-6|?GK1{KIU0Ti-q^=hm;F#Zr$8v(RgPb< zQ!cU67M1YSA%eTAc|VpBE{R8k3jsuW9!7vL`N->#wH$-VG&W5CWS^$JEyewy^NJKI z8p6NKs3>>eCZ1tZ$a{0mQt_=ExCJ;)x1y|U$wDRD^6gTj-=8{~kC zV7TldBzL2X$1AyKGcVd|XvT7a!kE;4UrQ#W=BqYPrztci8uoPiwI)xg0NxFRc$cZ` zRei_+a{3k;$Su-)mKXVLt&=x>;soG#DS6obrVvZ!CU661ulCFAwdSJ$(kjsXLpwK# zXm5S(Tsn#u3Iw6i8a*OZUlfiSexTy^BeRn3Vt09MUEQA=)mLy?se1U>Qi6V%MCYk? zowV4r2*B4xv&qh|&x85&#}w<|gu8ruAGLVVO;0s-4|dj%TBW=HF>ZsiDx4dc?Po&hnUQR)xD zzC9E@HRZ_ORcgIAU3H4UC*uZ50#EP25rWeDrhRWJQ9ioi${gYT@%(!bH|DDosGW#o zg2Y*z$n*)zuf#R3fZ~_}=N4`~{er!>rPkD-d6kfXrHK)y&8{ZZr=GY11 zH>YW*SZ+HkWLUs2VOW@zm&pWjyJi!g$6o*lRb$z#m#=q`;9j{FS6JZ;4pOQKkhe?sG$=-skduABWYlL|18~nO8WmiFhTGXySKPCh(y>(cH@)I;;TT6&s1XcHoBbAl zYiQk=7<0aMc?Jz`mom|+6N3Hd==*ma%o(035#mY5u9|Vhi@w^Gx#O=FME9M=&`0<~ zZi_9tQY>}>I9MR9Zxp$9fLptK4v>^Sok{7IbfI{}OtwqF$1a#)0~6UJp6g1l9B=#^ z4v84#9r;ls2Cm37416+F_tm{tC6fwuS-nW}3R>_*(=#+9WvOtM4%ew9_9I21tb`&<-)-Cqvr^3|)4TOypvPB;!2aM|PU z(0Yg#c(CS41Ak1X{5eMa5vNMUmAWz$7J? zBBebFzfQ)Ls|Uq$?7BCFM*-SbW|U1XZZHs;f5Bs)0)W z_}X%v=eilp!tGR62W}1@-?9%?;<`MtL0p~cby?&bgh;l^S3l)c1k80wgx^+BKVDg< zvAq~hGYh;)tN~cGU@@4TG9yn{ZmM2pnf08L2#w2`PLX_NAiL1LswlT;|UWfLW%YgF!RU0-Vc%*8SgZ`B`L68>7{XnW)eNV(Kh#n3JVK+)W^(5kW8 zF`)u4t1NSf`ER@a)H4;-fhP2VT6m|w9?Oj89nl7XxJZfd+(3wU|sEs3=fay>JR*s%qGcIzn|62VGa1hW1r#$rTjxn=h! z(Wrl^%}lny2?a;-S{ZWK+jsfJ?Gz3I1f?rI%&h|O<%JK;MYAKj`@w5L47wpL>ZHNz_+G%#Ox&eojiY{%0x_Oh_Kk9ZFk_<{yFMpEPaNs9cwRPtA@D z6l)1;uz!_c>#N{=#?BYhGtT0GQba#jMj^gcXsObs){DJ1AuY(pR zgvWV$?v$4SWP8X8!m9}ZFK(-Qh(~ruOoD=OCD}u14BiuQs6SI`xYk5!u%zz2I~U1b zM`A(oh-Rfb;qw}3Rm?+O5{UZ8y0!pq3QW%#4$!d#?Vq3UB35~gye`Mvph8;J)j?bJ z7B>Y|mXiZWQWoMHd+j zv|^xnKhw|U9u5#bcr;6V=R}HzY54p5{9`Y&cP01zt>>x;M`prLG^Fp-fMd>Ulc?c0 z8Ms~`7FsT~QU}}Jbxr05(`0hU9yIyW;kZ7tNOthRP-N0NV0JbKbWZ!E33ZLYfJ0F6 z*ou0x3=LFrG8MuRmApcP@{T8?z7#g?sN0uFQd0U#b+85J0&%ypgL$*sU0k@cA?p`o z)xWfs-Yb9g zB!9ikjy<;@S=yEj_2>~!Q4p9SuJ?=GpRLQMO0=WKewk;3PAPbQ)Tn`H9pjE8fc$4T z^)*=gOYfTS(@738F>k@GbixrlSCZ=}@D>M@IKSP0JRBYrt$Vi*H1QdzU(-J4C6-B#1zhRNYNs#USe4mc~Lt+dT zqm%-7c)~(yVdcEkY6prS!M`;;P_2{J;lg9p=nbsnFs6 zt92Mt{f;l6?)hmaVlbP--g>3**?}$2235>1cb=LJQ0$^}ncI4n_@vluj7T)8_;@3w zarxUAi!N8wye#Cd(%hmw>4COF#CIEdo$FT1K#9wcV|(MCYP(u=>q7y$W{LXv@oMK= zcvNqfE0aU=7RlnmUf;$NYaM$KyV?2a4~M^hPadiGICNtqLNX-1bm8Xhjm)q%WTaJ$ zSFI2d`~i;ukIam@C@iW}&$UYYwAc%5XpKi*-O=wQ4r#m{Tno9=Qb(aIU)VqZO7veR z7=_;6w%Zo$mM#4)$%fA0htDaXx zSTgs{vQI=~){y+d*Oin?)~FbYv4o zo03Q?G1o=O{P=xIDa*5?z`2_0Da!9j-;?3z$I7e2JVn2L4<2sH(A&Xo9EZGVXb}#? z=zgBN=_$C>b`g&031xE~w$Z@a7(eXYXQParG3EHtWkbKip!B=fHjrkTYU?XEt%nbB z<7;bODB2fZfsA}*NT}G7Ig8LFMqY_6Hq)((R06#ky;;il4n6$Ox2B)bE-v};(~?=v zR8PUETUW35rojR638EHF+5s2GB<3lJSU5J6l?j^*UD31@g{cH$BChdQx}$X*en zp3b@D$f0x7KBo3dV8?Id>hp!kikgEK?emAR4Q(oelh^;mhD8Afm9ixLX_E&&=6dV0 z>^kk8;jK2$omT$5I9zKgesx&6%k%G*;`=>XWJOV7bcS;C?+CRuKeikg zB_cIsgj4k}=S|lAu1XaYSX#X#0zd~vm$yh2 zE)M(Z(fH?emKqLax-057UxbVXO;nofC8gi(-7AmLjh~1?Qmp&DTp{X>uao~5Si7(Z z0E@o%kt_l-M5A)D(+MSa3zXV8;V#?NnT>5&!RNm5+T-c;OzW%6;oe z4J2@X^Kkek?Z*6+>lN+w2InzjNGm7tE$ljNC1)6d@vw#K+r(p|aLMJzqZR(~_jhs( zR&##j^h0Brz#x}W5Q71#bu6Zi>0V)>ld75fx6P)v=st!?j{CQ{6)`(?@=U+k8L5G{ z`W<}08*r$&TAV@E&^}JMaB37{fHa9(xR-<5i8l$q2MA>-?c88jJ$K)j{}fpCb4tRS zzB%3fc$>rlV~a!;lQ3{OXm~N`?YLoiMo7Ked4`{8k)2TyBO5uIe`!qxgO~RYM z#JNTtQQ4C~r-nEa$N(v>PE`W?n1cBvw?bYzE4G@zmm*j{v8=O9qL9Ue`BFgx5lN=r zXUO3*I(t71LhbRrbA{n&qPey>l`nGl>F7moXuPdg#%>i;N?V#o+2U~j_h{<@&zV*pMUUQ~jHMfO< zgcWzSD-p)wG^RIseL9{`|9rCr3aMHfQh{3@Sz-T?-HKWz2C%K7_EfxM0w&>LsAX*bb(VhHy0+q)O-HuNdFbVw$@U;gBxajbD5+}YJwZzlLSb)s z(v_zf;nFO~xu*F0l9eb5$0h;;HCL@n^TpvfGNd9`j(Xa{yH^{3mfsMx*aaVRy1XdW zhMHF@tj5nFBc((iRTF&G`*=w}N>++WTc!bjG>-($C&I8{0urx(F@WXo7u~4fjXbnm zc=DVo9B($t^Zw_P>`)_80Mj?*QN?iA8+6BX96qsCmrXdTFa4+7YyO|1Ex<={5?w5=G89lqN+to zlh)*~dq53aY+%o!py*%2e@HVGK2Ng(K>C@6XB5?680OGS#)Tw4daP4Z^3t(Y1Ggc8aov)njZvvhfo1Y3(%AlJ*jWFwqS$RLeQPbkcb9$sM(&9l zaQBFjjQiG5JreIR9i3NS)|A(wF`SfC-OPhv2Orepzx|PPX`t0=CBQ(VNXNP@#rGM; zvIb@|!CsB7=7^8YkiuF%C}(HzTn<7;OwrSszgaM;S!Sy&l1*Sg!Lp9FAm)O+fECncX}g#+c8}C4hKfJo6SoY!{m9vc;*>q%%DQp1 zk)qzfJk4uL43LmDtY4W;PLyEmMF40mj)(6&t1AgGD7#cc`^)bKZ20c3P&dG`@!Zjy z-6Er&M4VD!uJEIXi0qPXuX|Fla4r95fnUOkJI27ERnb}XxIKaw2zOwmU`}I`qv$t9 z5zLqOVbkn>-McA~+i$LoKHpVuu5npC`E-XQ#Q<5D^s~rQ81$XE-x6F)1})J~V+g9< z+3r^003JG;HJ7}{#Wa2B-`E&-j$7ref90MJy^Q>{bWt<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H16}3r3K~#9!?VSm96m_1*dv)Htnceqxbq2^uNIHF{bDsnPNgNMk zab-LaMQ3(K-8pu_V~157$GJpd6@eK<(RJ{|1Jv>0N;;iBs;WDkbnZJG9v}#cBq50p z{O|8y{qO33b$4Pys;euZe(!x=SGuZx|Kt0u|NmdrNk^kmLk%_5P(uwh)KEhWHPlc; z4K>u5!ckLE@tr>NLVaIm#gg94%kJ;BR6g5hsoV>2y_uCQ@Ol8=`vUly@CE;#e;9RNRMi;9NMT+OrCc!&zmQ_L?h}_GVOW1mT*9s7PYI=5s?CJsFkP;xg5qk74v# z%8TF%UEiBgUJsmuZ6q&HrlrCQrdZc&sw_|yks-PamnC(ZD^~VoRQLd-Fgk!oI*kj{ zRHY;gq|y9CkEQ&Yo{aKp;G6=5525^AcSc23H>@3S-Bkt8TlhyV;)OR?)G1%|QLt1r z_nIqjN17WiRuvup6@O8;rQ#=WC3OL4I?!t=@9ecKyaTsQRd|f(F)zsPF_*Idnx@pl zh&ESMIwTI}R$S6;D&N>`E%n-=U*#XQgH0QwI1;go%(+hr_!9Y89RVZn|mIURHw%bo&|iX?2AVxQYD zcvuxpS~^S%9-JcLm#t6*iwfU#n96QZwD?d#L#J{6X#lB2h5l~+T(e@I?ROgHHv&i{ zisfEcRo2k2FJ0bYn1@v41EzzU6d`_dR@tRcq2sD3 zvh$fQwc?W4U;Qu~sba*6(#^3IKfSD=LqG2c*u{nzN^bFYLOpy%M2ROyPr1SP`pC$_$X4lVyB z>M)fl)&y<3dAG@Y_1}1LyKY{W(mJB6=qmKt@7AL;ZXX)yKEM-Z0P_B5RD%Jkbw$Wc zOb~eg3|Ehiu6Yb~*m(CrGeWEQ{9R1(&(Un%mx}jYq zi_z)#t3ZIBFwU?rSO0b$>MXn};<=H|p`pdEBHQ--%hBP7*Fo-vi06Fu&K}fRv?%r_ zNT0hu${+QXCbj8G&H^Y_^i*Dl25VU~($+1ewF@18@)^{TQ#t1OVdsT_r+_K~KOJZb zVJg5BpnAyt`G#$LS)u0*T3x7n;k781eFL0p(v>7f@#-&rFs9;{FGXjVI_U5&5d&3r zbYSTn^IV(nY)a&ntoqxL8iP{RU zM6v28tWk6pf7+s=Rwy7Aw3*A$SGztyBP|^wPCoMzzi%SBLUV+|j{sV~0jfdxYTf{T zGt^MHwMD4SG#{PV^0)AMz4abyHqJ*e>o0v#ZWONmqV=um#V8gWf8uF00;_r&Jm0tK zN+z5uG*2j1MD%KHK2LB22x23zZZJZtel9w?aa&lPk00BFn!qG6=`&r)TIolC^x}qC zH0I#VccT&ffQRDLj(1U;ao&053(XbEc*V!BwzLA|0TZ+tO3}$zcZzd0wxO0Aei|DS z=t|tuUj50XKWNn!p97Gb;CuZ+tCR1{00Vn%u+sn0dFP~aMWQ+_pT`Q~VrnRC)T=F( z0LQx13Q>FR<*0w3O_ZyDe=TCbs7+}_vi4h>w)m_sHCMXd6=+E>{;}NRV~(tO3=R8Q zgVZ;p-m8{PDyJx41STqe01ud;6Y7TRNaySO%^#36wNQ2ww5AnZBb@=#3OBXDP$nmB z*_He)-*7{Vz_G_SPdYE1FA~8KAV>}MT9Zmh3qqUV({+ClfoY6Y+3L_+;f8NpjOUBQY8AF?OJL)kC73{nZZSgtGA{ z;DY^4T3JjmBFPtAH)@LwEiiw{$-!HGfrh^n&O2oFNhKNsL zBh*sZ2~5^PbcXec^0wUYGvt_)&*Oq)n(#;v?Y`6%q)-0#4;B+Gd}Zb1#OCMFa9yJS z{=`kH+;rYZSX1*r;5`3pntwYK8=;oMPQX_==F=y(i1L2AVJnEA2h)HbhBrY$RQs`6 zTew()1z^$r%{ZA%wCGA;_-E0u$0yKz)lHJ+r}IVv3tx>5kmfgmHFazg*hD7qj{tZy zXjpW!C@=o(z^N?&bBImQ2quVHe`*Wwk-`Amb3pB!LAZ>rBW-fXF;$k=;UfliXSTo7oa8rHUbtKV|$>X=(fNyf`mBIip1;}WC!4Iuy*v<*GTFN9ZgH4IP_!tiAPPb*?gQ{_h&k-hR zf)i30;1Gc1qJQW6Xc#{UB(xV@5tVW{ywFR@7O06pP<%ha_G~P6b{MqyPN1&na#34O zzx5u7p92EZ1jG**0gOX%6Fh+L{MtGBMPTEcyw*m1dmG%1g3?cF}MguXqadlQ}ddo zFaRs30gnBCJ#>~!;P9RIM72CNWF(MEd^`g5K;7xKeDb@;&s%)|NRT^G6HNX=P-9B| z-bR?0{d?nA?{SPF3b(T!0r6VJ8Q+1WfbATq{A;I{+f!~OtvfgAw`!AUss_$Ej$0`@(S;<1dhTT0S_GS8*kg@@QtfoIZsP51>7g60h-d_PT+(6Xt>%T&{ck|EM;^2l`et!KqGz%NVQ?$ z?|b>_MhfvU-f@@!_OFs)3dmXF^Cy#y&)026!}y6H;m8AjlC5lP0)8q;35*6=0zo5$ zPX&$;BYXsP7PVpRM&ts~T=@9_euZNg0vo{yVgm$*{~VDKym(fRaGy=! z@W1~TlCuu@DzqhM^3)1s3D^k^3MZlWXRAbYAK3Xm|GB>_8N`SE41uNK2yQqg6c0rm z!89tWnO+$9(di52E3{Hi3a1@1iDMk*syVSKdU$=b(-QZ~k3W z_XEFL9aZuDgw#Jdr$Ms63usKv*(kRGn$4x?%)5J_18W36z2_k$cRlA)DU-)RZ~}N$fArA}VfAde`X=PU`vHc6 zCE$FaxG6k@y5t~Ufoy?#LZF_$_;UxK*Q|J!UiZj7T{l*KDFggw_k*i_Vus;qWG9K2Q(CQmkmrzYO)ivJ(x#trg7|&%KPAN*2jhkCxJ_ z(5dHN39ARLn>Vi#ITJy23WmTYW`@C0xCtJ56cES~@Dd`{FEKlMUlZ4uoRb8FoQ-2hY3!MpB-vOWka|I`bwg7_7&6+bEKTvJkZ z^u8#Pp9YdFRrOG*SkhpghfZwXhKAnV9meUGUPnDEet{aa`O=jKx%yW85^}#0vCNa3 zwjsA^E{LB2q6c9JEN=iY9EF?UNeWUY5Hy09Fls%MGWSLOyMm#hC9yUDrVbWa2jGey zdS`b;`d@hq9bUBtwamNPKR-^i3^=E?Y%%%>${2Wc=cwfzf-)Fbh{jrc-T=v2S8L=5 zs7Sq+s#L6SLnk#Xy1_pm!xS(Yr(b*n9eHRi>bdPdP}}9#p{D!_)SxfMPs@1lHh!-i z_TgjjvHft)SUFE^dl@xfaRYLI$QUO@&;*t+A{haCJ$K)W2435RhIV}rRR&(&h5GKi2RZR?0VkMwoD?nslaLVxOTiIv{R4Oe zECO|eZ|jHpgv{$?{fALdXs41)Pz{}F(-)xbJ6EB8e7m^weM#xxz616Bd?j+hBGXQU zSBm&RyX^ls%9~`lLn#LE@*RdvU`ADmW@N=5%v^ar`eeeO+*@7tX;S^5FU1j!~-kNtW3B+cI9V@fr_bQK@?O!CkC&o}B4&C8T#fPe{VrlLU;?qjVJO%HKOBXd)Q84Hm`IyE ziRLXDrA19@>i3`_J`dF>rN9vrSg`?wg`X+R2>j1@bb#Y(33ibY0wX{;A|oXDVH0R0 zn8OG)!MI2?H#5n}-`6NHxS3|XC*FJ-KuUuHY=PmQqL#q_9ESnn33dtx?87)C;Gz&l z01A$jPva&`qz!O~ErbFQA0OqmvN;iRGuKYN|rYSq@kNKP+&bSg9_>EFc z;E28W-@v^8h-Ny6J5GNK7iZ`PkkVlB%AcPG^!#fm&Nx3V3MRx149UYzy!_L^GR=h$ z?inTnH_PAykkVlBRX$$f7CtGc%w!QjmCFL>Lpy99!3NGSpX>omKR(ng5eDy zh9!(M?>T(cyOT^mRFw}2*GzpbbP&T*of>Nxegx3*xI#t(R|FG`Nu0sU#2boLrN@3| za1l34w}PFiZwHVPU;tMH7{a&an5A!HXXtL7miPA2Gy-(mHPC?`cBT%+8U}DtFodzs z(>F1*(rQ + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "AuthenticationCredentials.h" +#include "RemoteAccessFeaturePlugin.h" +#include "RemoteAccessWidget.h" +#include "VeyonConfiguration.h" +#include "VeyonMasterInterface.h" + + +RemoteAccessFeaturePlugin::RemoteAccessFeaturePlugin( QObject* parent ) : + QObject( parent ), + m_remoteViewFeature( QStringLiteral( "RemoteView" ), + Feature::Session | Feature::Master, + Feature::Uid( "a18e545b-1321-4d4e-ac34-adc421c6e9c8" ), + Feature::Uid(), + tr( "Remote view" ), {}, + tr( "Open a remote view for a computer without interaction." ), + QStringLiteral(":/remoteaccess/kmag.png") ), + m_remoteControlFeature( QStringLiteral( "RemoteControl" ), + Feature::Session | Feature::Master, + Feature::Uid( "ca00ad68-1709-4abe-85e2-48dff6ccf8a2" ), + Feature::Uid(), + tr( "Remote control" ), {}, + tr( "Open a remote control window for a computer." ), + QStringLiteral(":/remoteaccess/krdc.png") ), + m_features( { m_remoteViewFeature, m_remoteControlFeature } ), + m_commands( { +{ QStringLiteral("view"), m_remoteViewFeature.displayName() }, +{ QStringLiteral("control"), m_remoteControlFeature.displayName() }, +{ QStringLiteral("help"), tr( "Show help about command" ) }, + } ) +{ +} + + + +const FeatureList &RemoteAccessFeaturePlugin::featureList() const +{ + return m_features; +} + + + +bool RemoteAccessFeaturePlugin::controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( hasFeature( featureUid ) == false || + operation != Operation::Start ) + { + return false; + } + + auto viewOnly = featureUid == m_remoteViewFeature.uid(); + if( remoteControlEnabled() == false ) + { + viewOnly = true; + } + + Computer computer; + computer.setHostAddress( arguments.value( argToString(Argument::HostName) ).toString() ); + computer.setName( computer.hostAddress() ); + + if( computer.hostAddress().isEmpty() ) + { + if( computerControlInterfaces.isEmpty() == false ) + { + computer = computerControlInterfaces.first()->computer(); + } + else + { + return false; + } + } + + new RemoteAccessWidget( ComputerControlInterface::Pointer::create( computer ), viewOnly, + remoteViewEnabled() && remoteControlEnabled() ); + + return true; +} + + + +bool RemoteAccessFeaturePlugin::startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( hasFeature( feature.uid() ) == false ) + { + return false; + } + + // determine which computer to access and ask if neccessary + ComputerControlInterface::Pointer remoteAccessComputer; + + if( computerControlInterfaces.count() != 1 ) + { + QString hostName = QInputDialog::getText( master.mainWindow(), + tr( "Remote access" ), + tr( "Please enter the hostname or IP address of the computer to access:" ) ); + if( hostName.isEmpty() ) + { + return false; + } + + Computer customComputer; + customComputer.setHostAddress( hostName ); + customComputer.setName( hostName ); + remoteAccessComputer = ComputerControlInterface::Pointer::create( customComputer ); + } + else if( computerControlInterfaces.count() >= 1 ) + { + remoteAccessComputer = computerControlInterfaces.first(); + } + + if( remoteAccessComputer.isNull() ) + { + return false; + } + + auto viewOnly = feature.uid() == m_remoteViewFeature.uid(); + if( remoteControlEnabled() == false ) + { + viewOnly = true; + } + + new RemoteAccessWidget( remoteAccessComputer, viewOnly, + remoteViewEnabled() && remoteControlEnabled() ); + + return false; +} + + + +QStringList RemoteAccessFeaturePlugin::commands() const +{ + return m_commands.keys(); +} + + + +QString RemoteAccessFeaturePlugin::commandHelp( const QString& command ) const +{ + return m_commands.value( command ); +} + + + +CommandLinePluginInterface::RunResult RemoteAccessFeaturePlugin::handle_view( const QStringList& arguments ) +{ + if( arguments.count() < 1 ) + { + return NotEnoughArguments; + } + + if( remoteViewEnabled() == false ) + { + return InvalidCommand; + } + + return remoteAccess( arguments.first(), true ) ? Successful : Failed; +} + + + +CommandLinePluginInterface::RunResult RemoteAccessFeaturePlugin::handle_control( const QStringList& arguments ) +{ + if( arguments.count() < 1 ) + { + return NotEnoughArguments; + } + + if( remoteControlEnabled() == false ) + { + return InvalidCommand; + } + + return remoteAccess( arguments.first(), false ) ? Successful : Failed; +} + + + +CommandLinePluginInterface::RunResult RemoteAccessFeaturePlugin::handle_help( const QStringList& arguments ) +{ + if( arguments.value( 0 ) == QLatin1String("view") ) + { + printf( "\nremoteaccess view \n\n" ); + return NoResult; + } + else if( arguments.value( 0 ) == QLatin1String("control") ) + { + printf( "\nremoteaccess control \n}n" ); + return NoResult; + } + + return InvalidCommand; +} + + + +bool RemoteAccessFeaturePlugin::remoteViewEnabled() const +{ + return VeyonCore::config().disabledFeatures().contains( m_remoteViewFeature.uid().toString() ) == false; + +} + + + +bool RemoteAccessFeaturePlugin::remoteControlEnabled() const +{ + return VeyonCore::config().disabledFeatures().contains( m_remoteControlFeature.uid().toString() ) == false; +} + + + +bool RemoteAccessFeaturePlugin::initAuthentication() +{ + if( VeyonCore::instance()->initAuthentication() == false ) + { + vWarning() << "Could not initialize authentication"; + return false; + } + + return true; +} + + + +bool RemoteAccessFeaturePlugin::remoteAccess( const QString& hostAddress, bool viewOnly ) +{ + if( initAuthentication() == false ) + { + return false; + } + + Computer remoteComputer; + remoteComputer.setName( hostAddress ); + remoteComputer.setHostAddress( hostAddress ); + + if( remoteControlEnabled() == false ) + { + viewOnly = true; + } + + new RemoteAccessWidget( ComputerControlInterface::Pointer::create( remoteComputer ), viewOnly, + remoteViewEnabled() && remoteControlEnabled() ); + + qApp->exec(); + + return true; +} diff --git a/plugins/remoteaccess/RemoteAccessFeaturePlugin.h b/plugins/remoteaccess/RemoteAccessFeaturePlugin.h new file mode 100644 index 0000000..1366f0c --- /dev/null +++ b/plugins/remoteaccess/RemoteAccessFeaturePlugin.h @@ -0,0 +1,116 @@ +/* + * RemoteAccessFeaturePlugin.h - declaration of RemoteAccessFeaturePlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "Computer.h" +#include "FeatureProviderInterface.h" +#include "CommandLinePluginInterface.h" + + +class RemoteAccessFeaturePlugin : public QObject, CommandLinePluginInterface, FeatureProviderInterface, PluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.RemoteAccess") + Q_INTERFACES(PluginInterface FeatureProviderInterface CommandLinePluginInterface) +public: + enum class Argument + { + HostName + }; + Q_ENUM(Argument) + + explicit RemoteAccessFeaturePlugin( QObject* parent = nullptr ); + ~RemoteAccessFeaturePlugin() override = default; + + Plugin::Uid uid() const override + { + return QStringLiteral("387a0c43-1355-4ff6-9e1f-d098e9ce5127"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral("RemoteAccess"); + } + + QString description() const override + { + return tr( "Remote view or control a computer" ); + } + + QString vendor() const override + { + return QStringLiteral("Veyon Community"); + } + + QString copyright() const override + { + return QStringLiteral("Tobias Junghans"); + } + + const FeatureList& featureList() const override; + + bool controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + QString commandLineModuleName() const override + { + return QStringLiteral( "remoteaccess" ); + } + + QString commandLineModuleHelp() const override + { + return description(); + } + + QStringList commands() const override; + + QString commandHelp( const QString& command ) const override; + +private Q_SLOTS: + CommandLinePluginInterface::RunResult handle_view( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_control( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_help( const QStringList& arguments ); + +private: + bool remoteViewEnabled() const; + bool remoteControlEnabled() const; + bool initAuthentication(); + bool remoteAccess( const QString& hostAddress, bool viewOnly ); + + const Feature m_remoteViewFeature; + const Feature m_remoteControlFeature; + const FeatureList m_features; + + QMap m_commands; + +}; diff --git a/plugins/remoteaccess/RemoteAccessWidget.cpp b/plugins/remoteaccess/RemoteAccessWidget.cpp new file mode 100644 index 0000000..52470b4 --- /dev/null +++ b/plugins/remoteaccess/RemoteAccessWidget.cpp @@ -0,0 +1,421 @@ +/* + * RemoteAccessWidget.cpp - widget containing a VNC-view and controls for it + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include +#include + +#include "rfb/keysym.h" + +#include "RemoteAccessWidget.h" +#include "VncViewWidget.h" +#include "VeyonConfiguration.h" +#include "VeyonConnection.h" +#include "VeyonMasterInterface.h" +#include "Computer.h" +#include "ComputerControlInterface.h" +#include "PlatformCoreFunctions.h" +#include "ToolButton.h" +#include "Screenshot.h" + + +// toolbar for remote-control-widget +RemoteAccessWidgetToolBar::RemoteAccessWidgetToolBar( RemoteAccessWidget* parent, + bool startViewOnly, bool showViewOnlyToggleButton ) : + QWidget( parent ), + m_parent( parent ), + m_showHideTimeLine( ShowHideAnimationDuration, this ), + m_iconStateTimeLine( 0, this ), + m_connecting( false ), + m_viewOnlyButton( showViewOnlyToggleButton ? new ToolButton( QPixmap( QStringLiteral(":/remoteaccess/kmag.png") ), tr( "View only" ), tr( "Remote control" ) ) : nullptr ), + m_sendShortcutButton( new ToolButton( QPixmap( QStringLiteral(":/remoteaccess/preferences-desktop-keyboard.png") ), tr( "Send shortcut" ) ) ), + m_screenshotButton( new ToolButton( QPixmap( QStringLiteral(":/remoteaccess/camera-photo.png") ), tr( "Screenshot" ) ) ), + m_fullScreenButton( new ToolButton( QPixmap( QStringLiteral(":/remoteaccess/view-fullscreen.png") ), tr( "Fullscreen" ), tr( "Window" ) ) ), + m_exitButton( new ToolButton( QPixmap( QStringLiteral(":/remoteaccess/application-exit.png") ), tr( "Exit" ) ) ) +{ + QPalette pal = palette(); + pal.setBrush( QPalette::Window, QPixmap( QStringLiteral(":/core/toolbar-background.png") ) ); + setPalette( pal ); + + setAttribute( Qt::WA_NoSystemBackground, true ); + move( 0, 0 ); + show(); + startConnection(); + + if( m_viewOnlyButton ) + { + m_viewOnlyButton->setCheckable( true ); + m_viewOnlyButton->setChecked( startViewOnly ); + connect( m_viewOnlyButton, &ToolButton::toggled, this, &RemoteAccessWidgetToolBar::updateControls ); + connect( m_viewOnlyButton, &QAbstractButton::toggled, parent, &RemoteAccessWidget::toggleViewOnly ); + } + + m_fullScreenButton->setCheckable( true ); + m_fullScreenButton->setChecked( false ); + + connect( m_fullScreenButton, &QAbstractButton::toggled, parent, &RemoteAccessWidget::toggleFullScreen ); + connect( m_screenshotButton, &QAbstractButton::clicked, parent, &RemoteAccessWidget::takeScreenshot ); + connect( m_exitButton, &QAbstractButton::clicked, parent, &QWidget::close ); + + auto vncView = parent->vncView(); + + auto shortcutMenu = new QMenu(); +#if QT_VERSION < 0x050600 +#warning Building legacy compat code for unsupported version of Qt + connect( shortcutMenu->addAction( tr( "Ctrl+Alt+Del" ) ), &QAction::triggered, + vncView, [=]() { vncView->sendShortcut( VncView::ShortcutCtrlAltDel ); } ); + connect( shortcutMenu->addAction( tr( "Ctrl+Esc" ) ), &QAction::triggered, + vncView, [=]() { vncView->sendShortcut( VncView::ShortcutCtrlEscape ); } ); + connect( shortcutMenu->addAction( tr( "Alt+Tab" ) ), &QAction::triggered, + vncView, [=]() { vncView->sendShortcut( VncView::ShortcutAltTab ); } ); + connect( shortcutMenu->addAction( tr( "Alt+F4" ) ), &QAction::triggered, + vncView, [=]() { vncView->sendShortcut( VncView::ShortcutAltF4 ); } ); + connect( shortcutMenu->addAction( tr( "Win+Tab" ) ), &QAction::triggered, + vncView, [=]() { vncView->sendShortcut( VncView::ShortcutWinTab ); } ); + connect( shortcutMenu->addAction( tr( "Win" ) ), &QAction::triggered, + vncView, [=]() { vncView->sendShortcut( VncView::ShortcutWin ); } ); + connect( shortcutMenu->addAction( tr( "Menu" ) ), &QAction::triggered, + vncView, [=]() { vncView->sendShortcut( VncView::ShortcutMenu ); } ); + connect( shortcutMenu->addAction( tr( "Alt+Ctrl+F1" ) ), &QAction::triggered, + vncView, [=]() { vncView->sendShortcut( VncView::ShortcutAltCtrlF1 ); } ); +#else + shortcutMenu->addAction( tr( "Ctrl+Alt+Del" ), vncView, [=]() { vncView->sendShortcut( VncView::ShortcutCtrlAltDel ); } ); + shortcutMenu->addAction( tr( "Ctrl+Esc" ), vncView, [=]() { vncView->sendShortcut( VncView::ShortcutCtrlEscape ); } ); + shortcutMenu->addAction( tr( "Alt+Tab" ), vncView, [=]() { vncView->sendShortcut( VncView::ShortcutAltTab ); } ); + shortcutMenu->addAction( tr( "Alt+F4" ), vncView, [=]() { vncView->sendShortcut( VncView::ShortcutAltF4 ); } ); + shortcutMenu->addAction( tr( "Win+Tab" ), vncView, [=]() { vncView->sendShortcut( VncView::ShortcutWinTab ); } ); + shortcutMenu->addAction( tr( "Win" ), vncView, [=]() { vncView->sendShortcut( VncView::ShortcutWin ); } ); + shortcutMenu->addAction( tr( "Menu" ), vncView, [=]() { vncView->sendShortcut( VncView::ShortcutMenu ); } ); + shortcutMenu->addAction( tr( "Alt+Ctrl+F1" ), vncView, [=]() { vncView->sendShortcut( VncView::ShortcutAltCtrlF1 ); } ); +#endif + + m_sendShortcutButton->setMenu( shortcutMenu ); + m_sendShortcutButton->setPopupMode( QToolButton::InstantPopup ); + m_sendShortcutButton->setObjectName( QStringLiteral("shortcuts") ); + + auto layout = new QHBoxLayout( this ); + layout->setContentsMargins( 1, 1, 1, 1 ); + layout->setSpacing( 1 ); + layout->addStretch( 0 ); + layout->addWidget( m_sendShortcutButton ); + if( m_viewOnlyButton ) + { + layout->addWidget( m_viewOnlyButton ); + } + layout->addWidget( m_screenshotButton ); + layout->addWidget( m_fullScreenButton ); + layout->addWidget( m_exitButton ); + layout->addSpacing( 5 ); + connect( vncView, &VncViewWidget::startConnection, this, &RemoteAccessWidgetToolBar::startConnection ); + connect( vncView, &VncViewWidget::connectionEstablished, this, &RemoteAccessWidgetToolBar::connectionEstablished ); + + setFixedHeight( m_exitButton->height() ); + + connect( &m_showHideTimeLine, &QTimeLine::valueChanged, this, &RemoteAccessWidgetToolBar::updatePosition ); + + m_iconStateTimeLine.setFrameRange( 0, 100 ); + m_iconStateTimeLine.setDuration( 1500 ); + m_iconStateTimeLine.setUpdateInterval( 60 ); + m_iconStateTimeLine.easingCurve().setType( QEasingCurve::SineCurve ); + connect( &m_iconStateTimeLine, &QTimeLine::valueChanged, this, &RemoteAccessWidgetToolBar::updateConnectionAnimation ); + connect( &m_iconStateTimeLine, &QTimeLine::finished, &m_iconStateTimeLine, &QTimeLine::start ); +} + + + +void RemoteAccessWidgetToolBar::appear() +{ + m_showHideTimeLine.setDirection( QTimeLine::Backward ); + if( m_showHideTimeLine.state() != QTimeLine::Running ) + { + m_showHideTimeLine.resume(); + } +} + + + +void RemoteAccessWidgetToolBar::disappear() +{ + if( !m_connecting && !rect().contains( mapFromGlobal( QCursor::pos() ) ) ) + { + QTimer::singleShot( DisappearDelay, this, [this]() { + if( m_showHideTimeLine.state() != QTimeLine::Running ) + { + m_showHideTimeLine.setDirection( QTimeLine::Forward ); + m_showHideTimeLine.resume(); + } + } ); + } +} + + + +void RemoteAccessWidgetToolBar::updateControls( bool viewOnly ) +{ + m_sendShortcutButton->setVisible( viewOnly == false ); +} + + + +void RemoteAccessWidgetToolBar::leaveEvent( QEvent *event ) +{ + disappear(); + QWidget::leaveEvent( event ); +} + + + +void RemoteAccessWidgetToolBar::paintEvent( QPaintEvent *paintEv ) +{ + QPainter p( this ); + QFont f = p.font(); + + p.setOpacity( 0.8-0.8*m_showHideTimeLine.currentValue() ); + p.fillRect( paintEv->rect(), palette().brush( QPalette::Window ) ); + p.setOpacity( 1 ); + + f.setPointSize( 12 ); + f.setBold( true ); + p.setFont( f ); + + //p.setPen( Qt::white ); + //p.drawText( 64, 22, m_parent->windowTitle() ); + + p.setPen( QColor( 192, 192, 192 ) ); + f.setPointSize( 10 ); + p.setFont( f ); + + if( m_connecting ) + { + QString dots; + for( int i = 0; i < ( m_iconStateTimeLine.currentTime() / 120 ) % 6; ++i ) + { + dots += QLatin1Char('.'); + } + p.drawText( 32, height() / 2 + fontMetrics().height(), tr( "Connecting %1" ).arg( dots ) ); + } + else + { + p.drawText( 32, height() / 2 + fontMetrics().height(), tr( "Connected." ) ); + } +} + + + +void RemoteAccessWidgetToolBar::updateConnectionAnimation() +{ + repaint(); +} + + + +void RemoteAccessWidgetToolBar::updatePosition() +{ + const auto newY = static_cast( m_showHideTimeLine.currentValue() * height() ); + + if( newY != -y() ) + { + move( x(), qMax( -height(), -newY ) ); + } +} + + + +void RemoteAccessWidgetToolBar::startConnection() +{ + m_connecting = true; + m_iconStateTimeLine.start(); + appear(); + update(); +} + + + + +void RemoteAccessWidgetToolBar::connectionEstablished() +{ + m_connecting = false; + m_iconStateTimeLine.stop(); + disappear(); + + // within the next 1000ms the username should be known and therefore we update + QTimer::singleShot( 1000, this, QOverload<>::of( &RemoteAccessWidgetToolBar::update ) ); +} + + + + +RemoteAccessWidget::RemoteAccessWidget( const ComputerControlInterface::Pointer& computerControlInterface, + bool startViewOnly, bool showViewOnlyToggleButton ) : + QWidget( nullptr ), + m_computerControlInterface( computerControlInterface ), + m_vncView( new VncViewWidget( computerControlInterface->computer().hostAddress(), -1, this, VncView::RemoteControlMode ) ), + m_toolBar( new RemoteAccessWidgetToolBar( this, startViewOnly, showViewOnlyToggleButton ) ) +{ + const auto openOnMasterScreen = VeyonCore::config().showFeatureWindowsOnSameScreen(); + const auto master = VeyonCore::instance()->findChild(); + if( master && openOnMasterScreen ) + { + const auto masterWindow = master->mainWindow(); + move( masterWindow->x(), masterWindow->y() ); + } else { + move( 0, 0 ); + } + + updateRemoteAccessTitle(); + connect( m_computerControlInterface.data(), &ComputerControlInterface::userChanged, this, &RemoteAccessWidget::updateRemoteAccessTitle ); + + setWindowIcon( QPixmap( QStringLiteral(":/remoteaccess/kmag.png") ) ); + setAttribute( Qt::WA_DeleteOnClose, true ); + + m_vncView->move( 0, 0 ); + m_vncView->installEventFilter( this ); + connect( m_vncView, &VncViewWidget::mouseAtBorder, m_toolBar, &RemoteAccessWidgetToolBar::appear ); + connect( m_vncView, &VncViewWidget::sizeHintChanged, this, &RemoteAccessWidget::updateSize ); + + showMaximized(); + VeyonCore::platform().coreFunctions().raiseWindow( this, false ); + + showNormal(); + + toggleViewOnly( startViewOnly ); +} + + + +RemoteAccessWidget::~RemoteAccessWidget() +{ + delete m_vncView; +} + + + +bool RemoteAccessWidget::eventFilter( QObject* object, QEvent* event ) +{ + if( event->type() == QEvent::KeyRelease && + dynamic_cast( event )->key() == Qt::Key_Escape && + m_vncView->connection()->isConnected() == false ) + { + close(); + return true; + } + + if( object == m_vncView && event->type() == QEvent::FocusOut ) + { + m_toolBar->disappear(); + } + + return QWidget::eventFilter( object, event ); +} + + + +void RemoteAccessWidget::enterEvent( QEvent* event ) +{ + m_toolBar->disappear(); + + QWidget::enterEvent( event ); +} + + + +void RemoteAccessWidget::leaveEvent( QEvent* event ) +{ + QTimer::singleShot( AppearDelay, this, [this]() { + if( underMouse() == false && window()->isActiveWindow() ) + { + m_toolBar->appear(); + } + } ); + + QWidget::leaveEvent( event ); +} + + + +void RemoteAccessWidget::resizeEvent( QResizeEvent* event ) +{ + m_vncView->resize( size() ); + m_toolBar->setFixedSize( width(), m_toolBar->height() ); + + QWidget::resizeEvent( event ); +} + + + +void RemoteAccessWidget::updateSize() +{ + if( !( windowState() & Qt::WindowFullScreen ) && + m_vncView->sizeHint().isEmpty() == false ) + { + resize( m_vncView->sizeHint() ); + } +} + + + +void RemoteAccessWidget::toggleFullScreen( bool _on ) +{ + if( _on ) + { + setWindowState( windowState() | Qt::WindowFullScreen ); + } + else + { + setWindowState( windowState() & ~Qt::WindowFullScreen ); + } +} + + + +void RemoteAccessWidget::toggleViewOnly( bool viewOnly ) +{ + m_vncView->setViewOnly( viewOnly ); + m_toolBar->updateControls( viewOnly ); + m_toolBar->update(); +} + + + +void RemoteAccessWidget::takeScreenshot() +{ + Screenshot().take( m_computerControlInterface ); +} + + + +void RemoteAccessWidget::updateRemoteAccessTitle() +{ + if ( m_computerControlInterface->userFullName().isEmpty() ) + { + setWindowTitle( tr( "%1 - %2 Remote Access" ).arg( m_computerControlInterface->computer().name(), + VeyonCore::applicationName() ) ); + } else + { + setWindowTitle( tr( "%1 - %2 - %3 Remote Access" ).arg( m_computerControlInterface->userFullName(), + m_computerControlInterface->computer().name(), + VeyonCore::applicationName() ) ); + } +} diff --git a/plugins/remoteaccess/RemoteAccessWidget.h b/plugins/remoteaccess/RemoteAccessWidget.h new file mode 100644 index 0000000..6800f9f --- /dev/null +++ b/plugins/remoteaccess/RemoteAccessWidget.h @@ -0,0 +1,115 @@ +/* + * RemoteAccessWidget.h - widget containing a VNC view and controls for it + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#pragma once + +#include "ComputerControlInterface.h" + +#include +#include + +class VncViewWidget; +class RemoteAccessWidget; +class ToolButton; + +// clazy:excludeall=ctor-missing-parent-argument + +class RemoteAccessWidgetToolBar : public QWidget +{ + Q_OBJECT +public: + RemoteAccessWidgetToolBar( RemoteAccessWidget* parent, + bool startViewOnly, bool showViewOnlyToggleButton ); + ~RemoteAccessWidgetToolBar() override = default; + + void appear(); + void disappear(); + void updateControls( bool viewOnly ); + + +protected: + void leaveEvent( QEvent* event ) override; + void paintEvent( QPaintEvent* event ) override; + + +private: + void updateConnectionAnimation(); + void updatePosition(); + void startConnection(); + void connectionEstablished(); + + RemoteAccessWidget * m_parent; + QTimeLine m_showHideTimeLine; + QTimeLine m_iconStateTimeLine; + + bool m_connecting; + + ToolButton* m_viewOnlyButton; + ToolButton* m_sendShortcutButton; + ToolButton* m_screenshotButton; + ToolButton* m_fullScreenButton; + ToolButton* m_exitButton; + + static constexpr int ShowHideAnimationDuration = 300; + static constexpr int DisappearDelay = 500; + +} ; + + + + + +class RemoteAccessWidget : public QWidget +{ + Q_OBJECT +public: + explicit RemoteAccessWidget( const ComputerControlInterface::Pointer& computerControlInterface, + bool startViewOnly, bool showViewOnlyToggleButton ); + ~RemoteAccessWidget() override; + + VncViewWidget* vncView() const + { + return m_vncView; + } + + void toggleFullScreen( bool ); + void toggleViewOnly( bool viewOnly ); + void takeScreenshot(); + +protected: + bool eventFilter( QObject* object, QEvent* event ) override; + void enterEvent( QEvent* event ) override; + void leaveEvent( QEvent* event ) override; + void resizeEvent( QResizeEvent* event ) override; + +private: + void updateSize(); + void updateRemoteAccessTitle(); + + ComputerControlInterface::Pointer m_computerControlInterface; + VncViewWidget* m_vncView; + RemoteAccessWidgetToolBar* m_toolBar; + + static constexpr int AppearDelay = 500; + +} ; diff --git a/plugins/remoteaccess/application-exit.png b/plugins/remoteaccess/application-exit.png new file mode 100644 index 0000000000000000000000000000000000000000..5b0389ee70403945b5e0862131baae9642e89c26 GIT binary patch literal 207 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?O3?zSk_}l@cn2Vh}LpV4%Za?&Y0OWEOctjR6 zFqp@JFr#FH8<4>uS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjTBH3TYyi9 zE0DhB670;^oCahumIV0)GdMiE0pjR+x;Tb-98XS=V9j`7FC-x7#Nba9P+vDEItjWlfl!~&t;ucLK6UZ+Bhr# literal 0 HcmV?d00001 diff --git a/plugins/remoteaccess/camera-photo.png b/plugins/remoteaccess/camera-photo.png new file mode 100644 index 0000000000000000000000000000000000000000..964a472cf67e542f92e2353737fee7b49c8bc671 GIT binary patch literal 2986 zcmai$c{J1w7sr3oFh&gHF_uADLXveT_1O2dh>|5{$d-M}6caNO8I5%qvWp~pq7-3z z>=arQS!RrsWEo2t(Re+7y??#uz31NhJ@=mbIp_ZQO>sDH2|Fxt7ytm6wUwFUf#Uxy zDCD4b8+Z*L2v?Me^+o7G;GjOFgPAwn$~6iAjwJtEpp1oe--9JG+T11DDalNt> zV6j-WYoS3=SG~f0)xskE3s?0d0D$kfwVCn78=qH-cbqO>7VX~*du_kz?sM&Rv3`?d;TvI$ND~t22_Yuhm>HKFU6d;)lFH?tkhFvNX0~SQ zVA0J*FTR~!Df`F0Ahb=o$bP)~iOX;O+3np`-ES}B8XG$-;9SQ4J2iQB#t9}Yzsy5E z0FmD)69h#8#t@kSfD4H^&AE^8JT2g%jRHK*pa5fy;Az)ymf&G*Ct0-e?ME_>&GS&xbQO^_9)7&(pMqMyjDu~RZ-1S41_XuAs-4VxStwKQxXtOpn_vd~cx$*TUcxZ$%;zuHxO3+ zJr?4C$|~hkH{Kgd9EP|3Ih?r6<~G54+=&+y4E%hul&Xolc6Mr7FV9Ot{IY zP+Fst+Thyt@W7C+MRFITy+7Dx|GW8|2g1;&{}OcaCX9%s8coc)vn-rz7dNfySMRJx ztQFGq0+M^H!iKO0G>?wrldXxiD1Ec4oG#^;l0Aa6*N+f|!%SGKj-)fr>TPB!PTMzT z0JO(Vc{;Cbcd&S`*eRlzo;}ZnT^l{M_;5!gITC7L@74c-ujJE`oUr(7=SB&PXJ8f% zhEF@HiH-lz8Kk8cKUOjYZ`l^mGFH{?e9Zfdhe;2x^r zzsEY`aQ2&H4kdx2dh}9htZ`J7mUx;43J>R{>c13=nww0wF4U83zSJm=>3$)aaaa0A zndqUe`s903oC`9m&ya3zZe>rOip0jn4FXPtO_f|e`GmKYt9At|mVFDa(D@AA)d|-# zn5&^DRs0RxcP!M&1}?E|wZza70>kh)ktvvvTvi$M!r|pEk}G6_?^P4G8VSt3K$SqkDeyg>FL7ByIC8CMZP& zL8kNcmQ%C)nSy7Iaoq>Wlcd8RgNW&mxTL`g%Bsf=EG#W~&PWNMXv1H$r`Yr*-&3|V zoAa&$EInD3COcJ5BJ_8-u+p!KBvC_|1zs+xz(k#qnDvr4zqAx#biH%;`zrhUwY7YG zeSHIf@Z|pEj0CoQ`|;|*X6A(H#0aH3@tuLoFY_l#;P56{R|*@HTp%OYoV`80J=?4& zCT)oy89A?ZV#~)@Mi-28?c4vTuDnywB0zr_40ZeG&ad9u9`dNZz(j@ITus3GA3+_{ zffJk(T3%(YSWo{GhuU!yELI0e8?-dv>)0n1Cs97P8wa)83def~Pak)~?H{X$=Zus> zOCN`fVjyt0neOrJ zzYgB;mGsb|-R(*drDvq9l{N?kL)KF=yiOOsIDfxg89a$(m=fe2@?GSsTMdilH8QD0 z@7`_Q8UihQZv|r)p48LoqB5<7X`cN05EP!pO*c=SMl091cAXVNgf&n?fnU0yDO)r0 zT3$m^CVw#osbqmu;fsrnou(j$Mu5Rf9ecO%6_+UJWhH}@yioj4Yf8_y?I*$p4`fB$~Yp+!?nCXr>cOn4C>k?W%Jhq}7zK5B8OvmdnU$!Ifc*veM!+_#?l9z+dn zK45lt&w?3+rkAr1DBzRROD9@_X2rXjD+#4t3et!lF+^r@m!1~5?+Fb4JYI*Z2Tj#m zu3&GbOUi1Ky9h}M*&{L@I6KH&py4a{NfG?7xV!|D0b=tJJ2vyX{Z}?R&)%k5C;tE_ zlu^v1w@fM5sgW}@wY9%E8pzm!r^lf7ykxx%8cgfv(nD3w54*iFmAR*Umka9O|0Qh+ z6uGLLlFqCIEia#y`2csjS3$^p;MhoB!@lF!{Fc;8eOvhYy{Po@Oooz-f^_Ce3rqOt zKR-YfS{FS<_J0cdkp@IP8%j8jrb3wWA0kgw;Dce9BS&x3zw-Hi9Qq*9m?ACby@(#- z+&SIR8^Y9u$;2QsfBL<({9#sic;vn5#M}~63?1=VE-))VqJ6hRp>r9AkK0I@6!P(E zNY7`yeVtP3MM(OV)Z5Q3E0H}O#60%IQ|fL`v_Q(0?8ao)79@i#U34Nok20*P?$R1uaXRmze36wQhV6Itw+pvAk8z^2%}X_bcki_1o(Bil*m!Y} zFeRJV8wYGU2I333GS|7`U3o`|-;4{ajW<-`}~V0}&H>^`OjW0#fDWdJR_;6{<4IGMA=3JpVV`IB3h$w^rI z^l73^C9(gUYNk`|?F4H6+zRf~8+_O9S|lIhQC-=of|=hopYnS0IzaH369I?(8ISN8 zJeO=G5r*H1Tgi%~`p2{?g;7rX^Ay`L7M*=+&vWm{$)ZP1Cf5p)EwS@0RS|>6d#+vp z7q`(|F?0To-H858#nsnySIX;GKhT`goIga?PpWY0Gy!5cNvJf8x_U)8!5Ik{zp~mp z8+?GtuiqK~5F7j`0Ey=}`ma?y7-gWu|C1zFS^}ti<3YFkNWsE^uLi8m&zm)vV2J+# DBGZD1 literal 0 HcmV?d00001 diff --git a/plugins/remoteaccess/kmag.png b/plugins/remoteaccess/kmag.png new file mode 100644 index 0000000000000000000000000000000000000000..814ac4a70c4552989eabd90f6da9727f23fe37df GIT binary patch literal 6369 zcmV<77#`<|P)YyJshdq$I8)71=T!$rfeAP7ou2 z5g-T<#76Rx*W{nb-x1_BPl+95d5Hrnu>sqV1Y3}8SysfIG(~duneOSW)|-d!uCA)C z>aL!B%sOC$)iu@CRrmYuIo~<=)@{fvv&=HfEVIlq%Ph0ZGRrKp%reU?v&???P5kkn zeCyj;GrYK_A;fpHW_W=KAf?P&;RQA&Yl4gbnPmjXEYko1`|H2|HcJa05()i354L6h zUGHE2o%D}@$oAlv-f?o|T))bHwYM$%JLK@V@%#H<9NcEo{@#YajZEAxB+M2{7^>F4 z2I(K8&6W^`*4yXxPwshaeDaiy+s}t7mJq{NZ->7fyFc(Z_IjKU;qUHmJBrEA|Lq@Q zvVb@nqPKVOnG8OhTmk9C}Ym*!ey6nsbopXmPnd-g+;81NAy zv8lC$TN-p67D~%8;78&0QAPsw?iO9UJsJE{kbu;?9Zbh%CZCJ_yW6&S_`kow=~(gL z2Z4%!AA4K^Nh+m0%k!1M`3C18L=qT$zPJCuvu*!2v7Ylw|Z)P0$V@R?*U*TI{doA!i417jp zUwHq+|NQyzd@)#SNg&8c@W{zdJPiIYFr0=E5H-j3Slex~P%43x?A(6N%14hQ0F00K zL;y}CNs=d-68z{nW!exR@rZt+(cR|r`n^QA9}NL<;73EiX{$kbNH*FnDw`Xue7rdd zegKVF@Kfr7IPti$+2jWw-0rz}5hh5`?IQ$Vyn2>1vtUlj z@RNFeGz6T+0;H~}%ohvf#Z&a_lF#p}xQ-j&>w^L;sn<_Il2*s${^!x+@Or

cyd(f%D#s3QZoOk`N1i>QZn}wg2e;e~b}&lfjo`z&~y;V4yIAjyUAYD3u!MuN2vQ zRz%v~o=U;fkmSc7J>n7^z{ zQQcpaZ_IH?BncIFBCpsG;K|Wa;B2<<_u=t+jEsK^*n+o_In8d(+}<>i9Z>s9~_PdsOUP^YcpJ|)HqWt5Yfb_ zQlLm^m?qB~O&)CSu-I{k)9m^1X#XE@i)m)D1Vvg&J^K7=;{PO84PwTr!S8pgc(DX9RH61(- zrW8C$xW-Kgi96KpwCHr_$miO78r&H>nKYqXv>*G@`~vSRo$Za(`C&}*wAEt0-NrN> z=sM(7je@RG&F5IIRmiEmO5c33$TzQD;_-H!_Z~jMcD;j00w(K%T#M?tJgv1No-NZ? z`Nrsisk0!N;HKz2qv@WZT(JBJB54M9M!=6PFGq#s-ede9E}!SMnK}Plb<5(zwN>ud z>zIy9!G@9x$Z+E*LU5*1;`OsjoU4@qcxARqt#Fke-Fe8aX&pWT08KNgE_*aLD|l_W zr&+;i!Ix=6Krg09G;5WnJ4d0U^hhzq;7)Yb@aWjW{_+4-2>$G)i<~d^z;mSJ{nh7u zyt#?zc?by=Q;>7xKkrG&^Lmr#^(JSlW!}Dco?1S~Og_h-{@yj-{pAUEfti0Mkf`>leGUW@EK7%?D$ z>NT<34NNsMZ$dN63;x}ex5zi1=0bfw_bmT=jz!@&dKLnM$kN@tyRw>WulXDs;p z3jq~f=hosG09@(u{U?uU+IFw!T9R5D6mM^hFD3l+{%_fA^<4i_rNrfh*`ptu(7WX3 zJ1B-SwiG<|{Sh}DoWO9OWdRa^M736FwR66xiUVIxF`r;sdwbz5n$mMz@2{<}+G!(z zVpmdb(|=TJ?=RQ&`0?GxcwX-!Z(dkNjT=lqY!aw8x%nnaK4q0JEyL4{28y5;2#qcE z-s|oLowkB2l|6zl*-I&~tm#~<)BtGM7Qf!!M3GQwx)i!{f5-=3yWK9g*EV|JY3N*A znmbzbhf)=7W*4>M?jL-n2>~)fg3$YZQN^ocw`^?JNCck=;7{9>*K0F=1AMr-hHxa+ zx{Gcd820|_*PpNA^$B`qVeWYFA5<@kZA8sR?DJXfDMEnk3Bdpff|Do3cjb2tTUn){GG>(l0fRZy2GbX5V@9^(0 z1cXK5V84QkVsFwlOH+V9{Y|Jsu$V6Zu)5nq-!j2F4CsfiC);&@d$CeJCg_8WU?XZ> z&=2k^64Jn+0Gvz$Atk!ENw${sc$t87y15cvpFmT)e1R| zp8>w58yI*zEVC`3Lb{s!8A*IYBK>C4Ts{EjqoJqpw~+%==zapfMVc#x|VYi@gD~A85fMbd^9!$ zNZ-4QNUSFm4kS{Fm}B}(KwyX+Y`cP{TL_VEbVf)hY*=XR;qmoO*Wa!tEZ1_-RxRWM zdcfvHn<%EzP0VL9o?QwEK_{YYH!r-PZc5&VMtCCZNi+eXOs`hZ6sghxWt{9Lc>-0Yg{c1=!= z6$G*dY394fE)XHSbR-xl8pV(xm{JNHG}*+mN+TCyG;E5`Iw+2pIAr@^b+|0c>*toH z8t$F;b!};$oYr%Yk2kg>e?R#g5Qad`sz^1w=3=S^BF3x!BvCjzdKe|n;7SZp$1+PG zdm$#bVUk-jCk_s+?>5=oZ2@p@w#IV6MIUIvO;LE`rDXs-&*Rf)tDH*H1kCAU#o_o6 zAd`FeUed#0R5Fx0mQ|qiw28iLrM{t#)^HBUBGK zbtXX0XgGdhZZ8^OTo0czJsgn0^$aRrgInwGQ*v9WUGvke9UgCN0g`GV$2YED?(5Hk zasAiNE^vK$5sg~;C1+N z`y=L@&D1ya-knF(+ueRBsPc^)m(kSyuY3LK*+sr``DH(5{^a&UEXUwq#r$W?GiPyfg*-3NtG_IcRuBU`6N{@wH=2aesaez4!trz%eUURK_#Et zQ?FNr;O#3fbL;9WD1mi#t4ZfO6|0*`G)p0877ri|9P0_vrib9#d1~r9O6#y0$;uIGI?D~o$9@Mc9FmM${So?UK;uPJJ&99 z?d;;I?D={pk8bBD>%`c>AW3`yNJ^ytgi8N_K-JGrHzK2luBRhBK}Fk|ctH#H28L^K zr+5QfjGvahwcF;uKKhiezjlSCY8gccZk#*AtIJEQ?(FhpbDPa(o3?4uv1|;z_heA5 zSm69zjSKTLeyRu1HZ6Yg*#oMDJny`E(I){C?yjtl_K=g=@yU0KAl1=2P};SEgQego z6GWsGJp5by=%MZA(M1 zOT3XD9vJ~#qi>#a|Dl)ucjK0opJ`zsqfTHE5<@*-Ey zEHGCoP5ex=Yw~z~i%&ma8ANj^UK2FBK%m|4}zDi0k>` zU7|nLu;Y{{>YMw!DLgKA?vwZ0JkMQ7(UV<1d%D7BPgf`yI%jHCYNb3y!@$r~y0$~t zwrQIdt2?_iI^(_4-REloPau3x@Mz`9$*T951vIy?x6dYx)r%;y5`;(|74(UMO54p* z*eCcwTQXNL9Fr%-i+CvqzB{JPqqY5C!`)}=NDsbt{SpYlJJ&9uD_cBx{>cf5_X53J zMUj&qVe3hNw9f{{qu`O5Kv?+NtoTq^L#d)Y)0m@vYZkRFF69n6k_0`ZkE(1QEXmeF!(N zN|*Dk2dw7LQ8(s~wBheQUq=FeaN~TR1YZUOcUB%AS&FCFMRd2er_WCDlQH4ZI}x4m z;b#U4yH1I`zBN3*2ovQnNFbzSsq+lO?Xq52KGLN8-t#qdvB9nDUjPJeUcHGFg1gTj z9y<7%HG}FE_j8JrS%8dY_oMl~>uI#4PC?u513&z;5WRHpFi8MVHMdb+hqdB)JfR*L z3GS~vK@nZP`s!PN;H@h+fkPyLr=VMBP^5O?r;dFzEOx9v@ZRzaOixl!8pHkl>Y?Iq z2Xw?Cmd*=KljY_^boXdy>^xX~$j|P4$Y9j#tt(&R+VZ7?{~}k%FqaPo{J3=jlM6wE zP#{7+r0JH>lomO~2`v|cU{oUnLW1bmiw=Venrm}r_c0r#b9A-R5t85$AbOra2;RE# zrM@S4d@x^N7tx)?Lyd3=3q%BF1qT#=!>*81>ZrmQoiHLn85u5)-+w5|SbEGipHb>= zAF(HR#Lw=1H*C`h{JkJC9vQ7B}gIIu}hS+px6ud zc#$9i!=3=YYx*I2_7LOymj>^?|t?mZL_myVJbZZ#au-3 z3cWQr{Kg*s63Kyj&)69*fmK3msnfDG%G%BlHUD~gtQUiMl9By`Ng*=05ceP!aCqW9 zZ#7X|mwIUlPf?G^6Fg>RXPvg$Wv@TV?ztLDXSoMUP`3}-FYxQ1QxhR!HdFj)U>~@) zo5zz5MRnKr1i{MhNJ%)dzsG@iZy*qdI=f19y3`uaX_S|6RpUrVu%DjqIeBQGK?;Ro z&k3O)AcXMSeVT7ep9TVTyG&l}GMFPS6Tlw)9^E7n7~J1;0+K^@1ArnODvcG|#W}3p z(TT&Ug}zyV?m{q*1{zpK@E84uc7l#!?`;?x0-Cm9S@af1xOwaE0NCEV&x3pK4lgip zZIfNU4F81rG{4QTUvY?}(Aq+{4rZ}-vXGYor@e@@%l=fm-j6*>zpwTBmj9equj7Qm z!Kgm?+Uv`_wA33K{+A!!P1G)l9w4u}feQ+U20uL*Ttuwv7sGWSL^xISUH=h}sTnf^ao>==#&^f}!h= zA5%DNQ|&Is?iN;Q7OASo=ki?xt8otD4{gg)1Vr=+q&Py1dI5w;T!kh50ulX+o|Iv4 zEh66^idlk)>kkT+`0U|0Cf6#Cjb7ivF3%wKJr1SHy?@L#%UI3DUZyZCw)E3u8BLjn z`w|HeJWTYaaCYhnlpyZh+VL1MV@b|@qaf1EJR8-(;tsCsxDvDZUw?%)>6$b9~Y z7$C5lb6DNkzFrQx^@u165h4ipKpzBj!iXdcFJmyNh!jfzr9U4aw8VO3h&VbsObpf# zj&=PqA%#3n0VWZxCem?{rP85tQF6(R+%71QARjN2`a&Zrv`p9zFS;lkqe*I(U zs>7Hjc!5NH5SNCD{+x^8q(P^&g5pjSI zn>tp3c6}MoQ~ST(DkEw=*h`|hvzVX{#(4n(;h;Ann^*}<9t#M+XK!q>Dlh>H~vu+u=L^ z<9&}Tr&%X3=6R67L!a?rNB4(a|G4Vm398FTiPPxd6+HB!u_p~ErJ&tdqSLC4PK1a{ z+s1eSAJT|Em?Rj3Kg%;{XHQNY0{ZT~e_GRIq zV#x6Qa2Xbgc|DT($?4KSe+d{dlqZGCSuR{ZJK7P%XvcVb=Uuk9?#FnEgzDi5G~0A> z-7bY{E@qYP(S~&F9NQZg@LbhrdB`b8a&?(7xRaz07H5n6(al%FqbDLM;S?jk+Lk;k zI5BG-;4gia#mJ9PZZ87$_jtV!Q$0Mv=Gc{U<38 zoK&-y?C#hUDjK<*66UQG>^2u@Y|s1mIH_WnW{5Y=3#@H5`Qb;8hXFtY!4HH5zsdU( zZ~E6Hr_%sKuif0Z7YnwGkYK<{87%Yr*+6pj@HCi|&}_Ik1t^vJ_bC;&H_p*&m;3R4 zQVDnZA>N=qXm%``&+HL^hlB_cBccz6ig21VaNIcl=(vAxDmbaLgyDmvr^niD56c!T z=s7ml&S2TOiHba$t4}+`JJvI#OdFBO14QEWFWx+Ts~%-`V5Ui#;r}9=k~Kj_fXp%i zWR?*ivy1?lWdz79BS2;u0Wy1GOhbr&J(;HmJkIa`;DZlNb^D3a&ft$fyL+sq z%r|aa6Q|n)oO1I#kIY_V0%T(uFE#RBinBm*cS@nSYjG(QcPOw>+@U~AaVzfb#hr!X?hcC=TPV7)$m9OG zc`t90naP}FCSNj{OwKpoKdLL>U{YWL000~%MOm#^AN(IhM}4*Z;^t$o4%uB=NeBH^ z{L!tVUSkYbMMHN0fZ^yritv%)5cc{=?jdL3q3vSh;bji826%aSaoK-ybhk8jwdQgG z*=9n;C;$Mm1SMH19q-k%?3EqDe?&9v`JtkfION)@lXZV-9$5_ofqJq1IwOZ$5u*=IC zke*GFv}*=z`wr&nfm4R%q{psWqB`47&|d37Me}rwyWg}!3&Teje_C6U;Kx<+?^THE zRiTbYWZ)zA_w7CRF$F^MArdGkius=l%|%tyS+}-C7nn>RPf@ zA#ues^e0;>cH_~1|CxcvP=i*PXDop(F4KFipIlmvwokuOruS=k9WpYM83yqRx%nQ_2j@rTQu1@Z)HBhI+5~bp4tB5%yC3Nl8XWGCNczjp*p*h{_vX~A=$E0~Xvc7~ z3ygp08kv~rw6I7s-4c+NP5qxXkSG#pb}>;h;A)E4u%KN&Q~ErU_*3PWCTq=i7n zN-_OWz(w|ndSOG~OAN9^I>4(BRHAX!2J)PIN{HiyQ6J5D8u1|dsO?}#11ZG)T058x znO`2$M3a0_kIwiF=_0RIgA>PCNT;YC($Lg&j5Hy6CHBwRofw2b4KLR^OYix&r*(w^9HHewmKb8T8$x}{N6i%U*LVx-;yuWW2RrG*r zf8opfcFfrn z`0PHVd}3YUVqXTJPz`$h+SgtQCla2IzoZpTugt9);+0< zh$AZU*f28+%%Ps3@9c=G<;;TJns>X}CWwXBB9=-qnne>X=`fOL$^N z3AGVfJx6ddtuWWAB@wkzHLxbJz8&Z^=QAght-d&ERx6(=GI04=C2YX1zhci;2uyNK z&sZz5P}elk2Z(s4O)U}2-%5)2Pd6##u9M?-uj^klWK4eZ&q-RL8fq{c_cUZ)1uohQe0w6rSx;r zY}xP5=O{nNG2+VozumODbV2Jyzf-|Ydt@fv(7^giT5?+Lc|8`<=)KvANyG$}BvB;D z9hejTCg~@Uw)_u$n*$$nhYFKHoRVOZi@Rp%>654-fB(Mrl>7bsRTZgF+x>;RUh`rB zLZ09-cU!VBvuh>j50|OHh4nn~^e8J~b(``^I$gG|QBf3c+ipvaBx*Ht6nXR!cJ1W2 zyST8FZN}W8n9d4)uhih<9>-QrmSw5d-dvQ!D!ytp6Tce(S`M+c;=sZsccX!4r!!E3nNT*VZr%KsAu{2|%u1as5xygNS{UD;Z)hFG^TLr& zH!pu2p}83Qmv^w(WG7t*;^VK%O{fF%Nk(&hZYC7XVeE`Q@c*72d{mzM#e;Kc^P@A} zf+=%welU|&ha_cbva7egT%=~BTWaHX=#)Z};zVjarihiL|AnPbFcg#Ts2Gv(yJVgy z<$OgFnf>=renePeot~Ne9Bi^rNxh^&W}*W?tWeMz=48sPZ?M8zZ^Y4{S-#V{JJPSS zYQ>vt$?E6^@}hbtxTsJe)I8I|< zpdtbxUGZTA-}mH#Crf=29f#H%CN)b6lS*vCpf2TY?akuZ$B}yeUq1*bADgt&PuLyB z*176l=<2C6;f`bLrhbw_AHc;#Q*P$}I4UXz?Xa*u_go;p&O@Z>CXR+%IjS9@p=!um z`LLiY;%OI5uNL-qijGO`{3@emvdBRfl9Sd<-tOBBY@^c=F*}OMw^S3#`G4SJ>`raBC!iE z2CO{i&&l2}BWM+LWX?M#6^%2UWgejE6Yk=513u?u$&id*_`9ftW2ML^Z3Wy%!9R&A z3?2zrp#uHgvO6lr{LdcsMT9;8@4IXd-oTvp=|mS+@mo%4F9odz!rS?Bo{4_58DJX$ za&cx0NeE4d?H1k3A|y3W!6ll*t6{lSn}-^tQ+2;ak#jp1l+}cRwg14EyY*@tfpYam z2R*f>yYuT9pM#***lKtiTOg;vz}>f)3z*d?4*e<&368W)#K~bU4`dxA>j^J35|l`WQnY)#(EcK?UcW=z_Ozu?T_$Xxc|$ted_pluWfm1k@?Ew!xgtrtBII#_*;tl z{TLD5dEkIvri+YNXyj*Yi{H&sAHxN!FVzMw0}(!D*UG1cd^B;H&;(y*wf= zDakKqtDtvfT#l8Q^JEdEAa=y5;w25&p%e`Cd@}hgeuc=L(+T0flrSsQv6$a1<}uz& z-k=AgM^=1mJJmZ{$~^H?glN-16D7d|@uC70+GM^Wm-mJ>V?koii4vdQkMT~(x-sAh zVLe{nlD<{xE@8|1mGL@DyUSLwBdYH&_iKbW^`fm8e=yQ~Aua#;(MXx~{$gyZZ2rXm zsV^Fx&In=}lp6!$%{awlKPaVQDWCoc*V`hd6+Pzi$KI;DoOYzasSmSO%R5pFkCNA1 zbGu(*_JToI+=(5&)@+#AB%>MS{F-z(4@}kOPbwR=BNegyo0XY)u)Z#^g}kUj0cKoU~+U6Nj26@jpx{d86%J0dM|Gp(J) z{F6PguAF8+E1Q#&Z*{Cw>*Mvq*>d4$f>8G>eq)jPVJoJt!=s=;`mvoqDxIJMv0RDn zv1)}}?UusQVzkW4Dkm3HeSzX@qUjR)W{`b4*xKINL&M?ZN1-Z2^3lUloi2oFK0 zboseQ73wCaMa`mS)*^TiPF-S?O z?(P|Q&|3-6ooNLf@Rc5FfX)`-PAPHnrqX1HBGr;$sdoi;BGCyI!IDc7Mn0{T%(T3+ zAvTtT{blqFWRy79XEP7C?nH)xFS9-%gg7_RvSHiH$A9cw2ZNWK;)dM;0u+Q9!Q|AK zLIOK_2uKT`&(VR!VyB4!>|)7=bY^%N_QH(KJ1(xtf7Q{pxBd}Mdz#G1`jefK;z@_A zXLzpbZ(Ywb0V2ib`lkfw!Eu`84Pxs7rMncWt#Uc0J%h{#`eiKX28Unw=Lu`GMYhe4 zz0Fr}Gb*6pUsq>PXmZX@ns89`K2>0gR%>EoDs?_KHpRAOYGiYgb)qxz0Mi-f=j!e$ zwJW|)t4>8kS|s1J`L*As{{>ZpN7WOWEYhd4$Jl)2!;HDt$#7;CIkHy3jgfG9|P1vCE=@ z7Eni4=C1Ed)QW8(+LoPowso|F4=X91lG*a%?OIw@i!6GMV$17a?hvsjImbcwFvRp^ z0x318K({A~wf(;5_9r@ZT}QURsUp7QWCDE(?0Dd2Ri>t!UL^53SYo60_SLofFdgY6 zh^i(KB$|v|le}(ZxSNE=KF{fQ?fIo)BqlQM{(6RJl6(BJse!);hm-g*W)?THO+dFc zy-X`^8uL~Kf$)WZi%~0Tc|U*m7+s$y-xq}J5FL(RG;ggt79Z39?iL*J<5gaE`dbt5^Qnbb6tq zTV=q9Ey2U~^Q9*y6K8~nL|Kg|7oisIx-_MI6ZvQF9SDtJK z*9y+sP1>EE_u8UF2U;!V{8CnRPMgQ~_}ZyHQt-ueP&wq{O>5Wgj656ri; z2bo?IT1zO>7Fp#thi=asYH;Jo$)JNB57piFum2^?K3%C`jjQd!?r%Qn9LLC1o;Mv3 zS7eeOYREdH-09zi-E{9d=oG0Nt;yd$8Aa)dwB|AZhJ~9osWY3#F^uygVh;vJ9=&N8 zvgxZ&bQ~fj0s2xdTm{2jFF8{X>+Q33s4(liD-Op|yk4~PMF)!)P-)FZQ?#tzYK6k@AL0;+Iq3vuu3F|@waqkOi>cX~G;fZ1cU4Zz3 zhV;rv0V|2tj}5rBW~fvgB%9LCZ}jOS(nP%h$A;}&zbxNikw)jpO5n!cMA~aH5)&Mb z4wPr#fA}^xRoLwxdTImTHj;3_9bQ5OL&}}KtOKI1fmX`+v)0X>eX$}616jBpxB3zf z0(=;_IQ|#{r88OnYX%L>(Vq+af^jic9Y?~=hDH+?I&Esb{zLP zN|oq7oOkT4HdEIGdM62+`tT+rb6&Xo{>&nmIQyZ0Q}-jq70Z|Q6d_U44!9Zv9E#xC zg3+7!o6wS-36;hg^XVpZqor#IRWYvrI{62DmfJ2>lPXf$6G30|DTOv1IUjOf zy5ug9ysCxu=KR-2S746q^AJ;LGOrENTCXlOk^ZXHyp{q6>am%NaGi@^G}5yJEk69A z%50*B-_T!`wGjN1yUixnlTpSiav^=wxb20W;I9M{T(x4%`@BQ zkt=VIH%1NKxEF7yW18;hVnj>~NI{TH5F#32*5T(D&^81zy0$)S$KYb%V)>WyBHW0$ z(A*fCtwi?NZ8h$apw_pr<~d=c%I3qVeLl=?$%(gLk8Rd+MPF@T?`Fs_9F4x|tGBme z3YY^=i9aMuz(yIxJMSQ7;55XJ_i>G&oSWGK>x^RCJg@UP2b`=N5Bz?hEGLrM!Vy)x zT}!ARZdP;`I-Po+d2Ja&wuP7`_6@`YOjOM(+9)2@PmOB z+-OYd=uR`F>kj0#+Aa7QX%@Ia>QQV;r7U66bWB#vnXzJT_0#tLv~5ombz;X& z*4E(@EKNNOiyM3vtNYlsYle;A7mw3yd9NX`G zJ6^UEj%iepbZDo6uoQFfvm~m&ntm`EneiLwexK{nUD=;H=r#emyDx)F z)d_)xKEj%{?!)O%Yd+k{Et^-tToJG^Gm^ilF&izYo`_+Gh@o&;*eo~VqjWfQTN7gl z%%%4cWUnIwjQc2A$R}4Uutm@n61|1wn_GXf-J8dEni?(b8+_XbEyU)VbVr0Q1$rt3 zd6-VKKbddz7ZPbHpIYDupB32tOc2XU4s~z~F|tk!aVfeMv<0(Ghcr$BvH5=njzUJ4 zS@Ss0ku?skD+~opd6qwjSug;q9Eg_OctJKC`H8WElZ$RuHtm8jGzOLD7Mpyw0iJaz z9l3n4Q1L3K>qqfTB5CyORTAgsA$Ag^_DBVus)(@Nd8I>bbn8~+WxXF~F-fhlt8K8r zw$@LTaA6mV`>=OxJhL(dq3^w9iY4UlF3=;Lm6X@i_;oW)v0Ln?2gNbF4Xwv`y@q7g3d{4ZHyrmj(6->f*N zJTN)nR_ZO2LT9lOMI5rV2vrI0fx}(yEr+qAXP8FD1G)j8!4co`2jv5 zu0Z<4xl1R`Up@&$7p|NFqKj8g1JR|cXD(ekbNSlY%h%6dxqj}-jdNFToCl$s7p?)( zt&7)z==P->Ky>Hw%{y0s=+@n@wH6Xfk|2h!eeQ*Pa?mf79@8Qk+4{zOnbnC&R z+YcV!e)#wf2tB#`2#B8EdkjR+?mq#d=MSDfH$2p6?CKhvn7De&t`7lB-ascBmIV0) zGyEkH@S1caF)%Q)c)B=-RNQ(y!;!1mK)|&=>sqTGw{1g7=!?JW18o|9uj6hl2^Glh zZAxBLnLl;r&6;QFB_T{eTY&+8sc`!drW%dd1CG`28>WAevs|C#C}H{jep3Yd8P+{Y z&y92(82XcR4m9my^|-Wha)s3KS6Az-5@fZ%nZ-mq)CC93VZFmB&nD)2T7r9%v~ziy ze1iOh5=o6Md;eVeX#V_gi+ + + view-fullscreen.png + application-exit.png + krdc.png + kmag.png + camera-photo.png + preferences-desktop-keyboard.png + + diff --git a/plugins/remoteaccess/view-fullscreen.png b/plugins/remoteaccess/view-fullscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..6fc7cf047de20ecf422fc5c9b33d65712dbecdcc GIT binary patch literal 486 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?I3?vN&YJLDI=3*z$5DpHG+YkL80J)q69+AZi z40*dinDN@Zjp9H-$r9IylHmNblJdl&REF~Ma=pyF?Be9af>gcyqV(DCY@~pSgaUj* zT!HkbPoF-2{`~RdM<4?T?ibxL2a2+m1o;Isl-DGkg>+MAa) zgfS#HTsLK9+QKHmxTiXZiQyLG9`-%;k(*ezuvf7Ecvn4+Wp$P_p+P7(=_dB9U}h;b6j1>uJI4VO6` zJ~8Y&*UA| zFte|#`C%p!arBFvMnNby$6+yn)prtD*kZm49ecpUDp1gvC77Dg$i=b6VP}u=1_w@- zhy`&TQeg|&n6xkGr1ENAU|7+B*>X8NdYuVK~<)!4Xy TrmhPxL>W9?{an^LB{Ts5#7(;0 literal 0 HcmV?d00001 diff --git a/plugins/screenlock/CMakeLists.txt b/plugins/screenlock/CMakeLists.txt new file mode 100644 index 0000000..d0c7d8d --- /dev/null +++ b/plugins/screenlock/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildVeyonPlugin) + +build_veyon_plugin(screenlock ScreenLockFeaturePlugin.cpp ScreenLockFeaturePlugin.h screenlock.qrc) diff --git a/plugins/screenlock/ScreenLockFeaturePlugin.cpp b/plugins/screenlock/ScreenLockFeaturePlugin.cpp new file mode 100644 index 0000000..be776a3 --- /dev/null +++ b/plugins/screenlock/ScreenLockFeaturePlugin.cpp @@ -0,0 +1,166 @@ +/* + * ScreenLockFeaturePlugin.cpp - implementation of ScreenLockFeaturePlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "ScreenLockFeaturePlugin.h" +#include "FeatureWorkerManager.h" +#include "LockWidget.h" +#include "PlatformCoreFunctions.h" +#include "VeyonServerInterface.h" + + +ScreenLockFeaturePlugin::ScreenLockFeaturePlugin( QObject* parent ) : + QObject( parent ), + m_screenLockFeature( QStringLiteral( "ScreenLock" ), + Feature::Mode | Feature::AllComponents, + Feature::Uid( "ccb535a2-1d24-4cc1-a709-8b47d2b2ac79" ), + Feature::Uid(), + tr( "Lock" ), tr( "Unlock" ), + tr( "To reclaim all user's full attention you can lock " + "their computers using this button. " + "In this mode all input devices are locked and " + "the screens are blacked." ), + QStringLiteral(":/screenlock/system-lock-screen.png") ), + m_lockInputDevicesFeature( QStringLiteral( "InputDevicesLock" ), + Feature::Mode | Feature::AllComponents | Feature::Internal, + Feature::Uid( "e4a77879-e544-4fec-bc18-e534f33b934c" ), + {}, + tr( "Lock input devices" ), tr( "Unlock input devices" ), + tr( "To reclaim all user's full attention you can lock " + "their computers using this button. " + "In this mode all input devices are locked while the desktop is still visible." ), + QStringLiteral(":/screenlock/system-lock-screen.png") ), + m_features( { m_screenLockFeature, m_lockInputDevicesFeature } ), + m_lockWidget( nullptr ) +{ +} + + + +ScreenLockFeaturePlugin::~ScreenLockFeaturePlugin() +{ + delete m_lockWidget; +} + + + +bool ScreenLockFeaturePlugin::controlFeature( Feature::Uid featureUid, Operation operation, + const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + Q_UNUSED(arguments) + + if( hasFeature( featureUid ) == false ) + { + return false; + } + + if( operation == Operation::Start ) + { + sendFeatureMessage( FeatureMessage{ featureUid, StartLockCommand }, computerControlInterfaces ); + + return true; + } + + if( operation == Operation::Stop ) + { + sendFeatureMessage( FeatureMessage{ featureUid, StopLockCommand }, computerControlInterfaces ); + + return true; + } + + return false; +} + + + +bool ScreenLockFeaturePlugin::handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) +{ + Q_UNUSED(messageContext) + + if( message.featureUid() == m_screenLockFeature.uid() || + message.featureUid() == m_lockInputDevicesFeature.uid() ) + { + if( server.featureWorkerManager().isWorkerRunning( message.featureUid() ) == false && + message.command() == StopLockCommand ) + { + return true; + } + + // forward message to worker + server.featureWorkerManager().sendMessageToManagedSystemWorker( message ); + + return true; + } + + return false; +} + + + +bool ScreenLockFeaturePlugin::handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) +{ + Q_UNUSED(worker); + + if( message.featureUid() == m_screenLockFeature.uid() || + message.featureUid() == m_lockInputDevicesFeature.uid() ) + { + switch( message.command() ) + { + case StartLockCommand: + if( m_lockWidget == nullptr ) + { + VeyonCore::platform().coreFunctions().disableScreenSaver(); + + auto mode = LockWidget::BackgroundPixmap; + if( message.featureUid() == m_lockInputDevicesFeature.uid() ) + { + mode = LockWidget::DesktopVisible; + } + + m_lockWidget = new LockWidget( mode, + QPixmap( QStringLiteral(":/screenlock/locked-screen-background.png" ) ) ); + } + return true; + + case StopLockCommand: + delete m_lockWidget; + m_lockWidget = nullptr; + + VeyonCore::platform().coreFunctions().restoreScreenSaverSettings(); + + QCoreApplication::quit(); + + return true; + + default: + break; + } + } + + return false; +} diff --git a/plugins/screenlock/ScreenLockFeaturePlugin.h b/plugins/screenlock/ScreenLockFeaturePlugin.h new file mode 100644 index 0000000..2ac3b8f --- /dev/null +++ b/plugins/screenlock/ScreenLockFeaturePlugin.h @@ -0,0 +1,98 @@ +/* + * ScreenLockFeaturePlugin.h - declaration of ScreenLockFeaturePlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "FeatureProviderInterface.h" + +class LockWidget; + +class ScreenLockFeaturePlugin : public QObject, FeatureProviderInterface, PluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.ScreenLock") + Q_INTERFACES(PluginInterface FeatureProviderInterface) +public: + explicit ScreenLockFeaturePlugin( QObject* parent = nullptr ); + ~ScreenLockFeaturePlugin() override; + + Plugin::Uid uid() const override + { + return QStringLiteral("2ad98ccb-e9a5-43ef-8c4c-876ac5efbcb1"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral("ScreenLock"); + } + + QString description() const override + { + return tr( "Lock screen and input devices of a computer" ); + } + + QString vendor() const override + { + return QStringLiteral("Veyon Community"); + } + + QString copyright() const override + { + return QStringLiteral("Tobias Junghans"); + } + + const FeatureList& featureList() const override + { + return m_features; + } + + bool controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) override; + + bool handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) override; + +private: + enum Commands + { + StartLockCommand, + StopLockCommand, + CommandCount + }; + + const Feature m_screenLockFeature; + const Feature m_lockInputDevicesFeature; + const FeatureList m_features; + + LockWidget* m_lockWidget; + +}; diff --git a/plugins/screenlock/locked-screen-background.png b/plugins/screenlock/locked-screen-background.png new file mode 100644 index 0000000000000000000000000000000000000000..619c67ed867cc467b0d35fcddd38a98a2666ddcf GIT binary patch literal 2217 zcmb`IYgAJC8pbiRluAp}Zexr#YC_UzY8^qHyp?EZf~Mm@sbgjaC^%w*;GCwKO6}r> zF>h&Uc*$#;wFa3<(CnC+ii!w`hT5c-m%=+|pT#FxsHj#&4=SPbcOEH#824x&=2@JM15B`hR598MxfWKSV?fuB5ngdAyeo6ocj8aci$J9_g@Z*e1t4eCEvZ~$G5U_tEKE}K7H?JW1oc_$z>?;i9C$; zLt{W1Yc$ttt<%;4Z`im=S8wwceS@urM%#>cLrhHfd}C&AvDXp`+h+y;*4pNPo&CW> z4hW>9lk<0nU0mJVk9c|=Lm&6UU;~4KLw*cD9dYJt)Hwo?6dgmR#KuvRe!lR_mN068o7V``IO(>@~pM3y`%GGSGSr@g5q=oc!JB*DVm>hiViuK># zGM%OIki60MVYoJbZQFyYsvIND;LUqXy{%rbZKF?BU3|$iw_~|b{xss9(JV1(3T%h; zizGECF1w72t#t4T`;$fbWNa>l;WzL*a4GnAf>{@T&j#>qOot%o=N%r{|(lRF)F z2+K1pi9#e;hyC(=<9jZCm!$`FvJsPJ9(9aSr>HIrWu5a{4JB7pOCnHL*ET*@P}3q- zGW)mFspng&f_!lVu5;(RBTtA~S|^H`{-Ho8*q@`rlCyUGV0>u4 zW@^U>>P1n&S}?wTEHmbTxr5h*Jtq~pF?AJMa)0VuSwxmUS5`n?uFy*~;Z2%dk3|{fqvMQO$uB!h?xAdJr41K;F~} zr}Nm@E~;aBSC~i1I(qL@9cE|`R9@EMI2f_@4aPZ9s<%UsAa$NeMP71@b9l_jaYgS|uW7>cgtLmQ^a^0NO_> z5i!Ie_p5U&p>xyKv*-@xA6^R{_YWp8gJc>-I5bJ|emAiXjz}(R%1sWq-1@4N8eu?t zb^I9NC)mB0A^cZC-Nk-RxF;jSs97QNRb3bP`nYGzQ6-UjU~D1Mn%#1@z4lPc3+`XU zuBe&h8Meio!EjP)gRubi=-lr&Dsv53(G`_;Mklw3x}%VH^sc7SQkFuZc#*3OjYxAw zH-f;MG~!O`m37nZRVTx1ww$mxl_!>4?cYWj&QyF~=|2l1XIntN zb3qZX_Y@^QEt@WTbc^=W(@txSJ~ zZJV@C)SBW)&evZloQr@S7qxm7F@_GJYIAyuw%6q+Vt!r4j1?Zvm}F_yG4Fh|goO^2X_R?xW%yYpyqDB*hSxaM + + system-lock-screen.png + locked-screen-background.png + + diff --git a/plugins/screenlock/system-lock-screen.png b/plugins/screenlock/system-lock-screen.png new file mode 100644 index 0000000000000000000000000000000000000000..8c144b73474c978dc96abd8051ce2fd487291b54 GIT binary patch literal 4324 zcmZ8_c{tQ<)c+S_%owta?8{io7P7^|%n*ghT6PB65{<@|bxgKwBTL3k5hXiO_FY;S zBw30{WyvylG+Ezye!suo_r9)k&V65>>sF_zAVa$#>$_e-p>Fp) zfzZ&<3%&tgq3CiUkkClX(= zH5Y|EvtXSZX0r?Za-A{~!WL`#jf5QM)#lOezok9XxXJgeMyhMBQNdU7+Kx0IZ17*`VsY@ZBDQvCH2BwvMnxU|B=7I%lc6)$6Eum}6RTBiF^A0CIX!%R9+N9t zeYm+Ln)(#@Hmv1&n!;1EQ&uzx;4_UhL2vG>x1!wQtss|+A~gN2PXd|&z?VB02h-2BXl>&akocyp)R?(MeQL2^#)63T`@h3ggX zl+fazWvBM8)9&hpE75r^z!U(T?+SSFeY~ZA)mA=1UUB)A|DguAD0r8|YH%9<;>sBM zRk-YJV)Rw(Ac4MCTkltld_c7N_$w`3mp|^nGbkXxFZAK77gJ)Go-=j4OSz29exFuK z)GBaKJaqqH+b+W6?&CxkZM9(N(7B&e=4wC4H(ZOJ$1=Qr4UWsT%6m_SY z`gXQ9JUVT1q0^Ic=x1^yvftp^`>hu0H?9pP z!UaBqoExA+Wd0W+#;q+ERjZ(R=WgUB$yGj>eqoA+TU>yb7kmn?_BQWc@G{>Oyl9md zeNCF;{mO@MHtrwgiU2RiqZjYt<76fE2G*e(?2QA>_q7|Xvy+v8%hOWR!{6k@nZuf| z&-Tuk6;gnI(ejRzIaT$Vf>NBI;nNBU>$h>Vm0hZ0!Gxx|W%9@1Jv~8QtC(t;gp18L z%7wB!4>ndS%OrR6`I8&Q-OY8Y=dHG$?Um<)U#^d}|7|$%IC{7uee%1O__sRoE@m7V zerGW^Wf;mgU@<11@9;mv17D>0+##gg_79XD*Mu4j~6d0 zBnF#4@|On{{yb+3>ELZCOThU*=YM{FwlZRo-Mk)&SXt}5x1t^MV_TvL{+Y)0D?0kn zHQ~Owr;@AdbUbzTaNA?Wy9tiV{Op7T)5kTlVLhPPjwk4b^twHZCY1l=iGRO&jF zjbXOx9;san{A$^iRoB9`_EIFJ)T763eX;0=^N}6KYG*cOE2=NFL#GT3w_|lZ@;BM= zj6&?Un(gMgo#D%a2(yqw=dB|RifZ7hqJ`Pb4RxL-?AE=TeaUytb!Qt@)H$|a5^ASX z#3DysXHNyZyzZojW(RM#Oc#-~uMJDA@MG~@!ZVyj{E0Owp*erHRlnn!MW(#gQ;ds$ z;8=M3gs#TiY>O{RTW#eR1CYmCl9!HCq(sCN93vCY-eyAlxPSbN zequSY7LyRnWExVEsC8&&4+5{B^8PdIt48eEt2HwEe*yZR53;(84J@2&CVQ@&yEp2pw2LDOdwmn zM!E2g5GHEv)-E`VX-KR47mB-z_PB^NXo^VU(En_Y4l}I;v}#*Jlzi*HGEy8PVntKC z?<5TdUXrM$&24-~5w7q}Veq(iUC3k_UE2!Rkpwc4cZqT;xEg z1k-YJu6!4aEf$hJND@qZwqx@=bu~t+oR_Io5K$00aCL5ezw3HrZVg9&Ce%TrJZeV( z(?8;iw`FR2DrQ{O4<3;{s#+0YTyAq|gJCDyV>hKY6qA)O>w|5*KoaIJr=-WD!pvU} z#Mbw90p>;94CXL(<*e5-WgiPW!2qQ|?~W;p9*>ywG2VKHVv1RHnJ9)QqjQro80M$D z3>7epHt_Q(-d4}Qg!LizymM0$NO$dEGkqjQvH>^Oo=XexLw)6VvWi}6@Npc|eSFHu z$pbeVCpnNr^Rm#JjM{%4R8`O-5Mo1;b*%EA6m^EAt@@zvbGo+(^ZSMNly(&i7tn0sl%ab9(f`cYAa~y6vtebl&WVdrxn6H9)X^k*8{6)Ew?yt}YO(gz)4}|9 zIXXy=4v2WiCAQSSj+U_)0SR5%IfY61_~@H~FXgmJ{Qqo4+sL@jyh}_KgPMA~2g_~3 z);(@`TbOl&JolQZ2&l;p6ymlPugLw?4cz+yap`OFp>9~$m_J8Ky7E(hlx|3Ww1((9 zFyxm8VIx4@4MmAldRlDC{n zsc-U6wb6e^eklkn8Z2{9&R>fxQB0Q2gr3MJG6Xc;!hdJVCH;&Z`12(ptRxs>nsI)` zPJClW6K%hv(bLpNJxSsI8q2sP;=$tTr~i9kyl-skVIb)Z21a^2?Y1^V)LMzwR0N8u zX#IQK3VpoENUDkxz!-o#%{&QHP-eE1$gW68+HrxA00Ab*EG1r8Nr25ldq;~-bUMP4 zkV%oeci1FMjha3$<8zJYcE>V_G2xfp#U64ohxB<_?^u*r$a6|E-1GX%2zL1Ofx zgO6%q+}S*0kLF5pl8CWy5H@n{(4B7fJLj*7dc6g*iuk1Hsrg&Ekl@`k{rLL40E&=i+ay|2$Ac; z59{FHZV>rAfp?`q(p_+aTM1_cP4P0Q7sTMl25%nvn~|2~*Dy^Qg0bIGf6RvUIn;uhBojwAh{BWRoLuKyx*(-glM=X;?4|t18 zKxDQZ-AQ=q>m$4+eQx^GZUhX$GyRkCmy-*nWDVgvJt7vx9Qj^SQ-a#eOaG{(;810v zLWp-Dn)w)p2IZN|3<+i@oPVknQFX5s(?J0Qujs(QVM=O>^n!Jt z(aE~$MV9BdZYpI4)CZm;DWkCtbWuJ%o8l#3Fal zSe59`ndM9qFbG$^K6ACrf}9&aH)GBTL-j~5sv9aBg%JZYMCmi|PPT&6%4nBC?^OtP zGzL8rMtCEZuATd7S@G3b3jq7$Wij6yFA!S?AB{NWW1Nm3Z1Zy#kHdzosD`RZH&8Vv zdSPryij6)5$PhcFJ?+6Bf1Y?N>h-5?vAALUj6}*6BMbqdBhz}|D*I|+3$~-HH%@Dw z0MD%J{#PH@>tjo&$aSn+msIjgR+(=$^T6ihe-B3pQHf$r;g~dP#Ja+NHC2elYv$Qb z01-}=q$`%xntqr#t^@1JBWubOq0hUl<~iex8dQt6PQ|2KGiHNie11X*&}Bg(A3xjP zZuvHHX1%O0-Z@?NnWagj!Gk>2^<*w_)lhv|Cex{+TY`fFt!#Y1)+vE1K1*^r7Xi5r z;c%P1e(O zc4d+@vX{u7af9|S1KrfzUEGbd!FZeiH!MR8jl@AUPdxksCe^$IfV0IAPfVvulFl9PGGL}bk1!b%oZ*tik@MhwUT+X9J@ zj03|J8%(kX<1>(Ws9*-4=ZU}YTgLgx@!0!=X7wS1B^-HK=BwFkD+W!OV1!Ikts< zMBvO?cDk3}!6STLdszoGVLF@E6O4kL>Iix>SU+*_WlGA!q$P30f7_N3xyB#^VKE!m zil_1?^Z>^^xp+quUazlxk1PE5&>@HzoqyF5ffC(=jPyasng@}^bUqiTQ%jNI@42KGS&7sqIv+p=+50>e z)dPBg0nla8|7%oWDqX512s5(Ikv(CiE?9vjXvc8z3`_=1sga;=l{#FjOxP}fpSt$~ zPoOtrX%;qTy34GsL8*l$AJbUBf4X|#7HY~O&BPOG9ETvGUowp2RGvTw1Oa$IjXh95 zfZgIen^V?x|JZ1CXwjDY5XeW{qJs92VeoPI?nzXv?g8Q!A@HUtKF%hGHZ;I5c)2M5 z0SQ4K%Z5&gM++}Ftlzv`x(cvLYhIJV*hh9hggw{T_!eB$Gt$X=E-aB9K48aln*NRF z+cU=xOP_HC#|Z<9cw50bB-kF4M5!21Mn)B45Ik)t*8?`W*JERv9b@N;@$h2zUXT%{ zV(`x@yb0;?yY)>A&s=E_!!V1zO02tWSMhp)yjhfa>&<@eWjQxQZIc^;@{r@tpAzOh zX!~_o6>K_?1NH^uVhQI@mN;R2vnEtxeq?7I?HfyzVjCCl7OlAA$*GEtokg7y*s?cU zZT|$up00l$ZtcMo-I##vD6L z&g{9nf=tdp@lKAE@YhV5rSgDI4`1GW&J&2!MzD)VY**IRl*5#R%oUEnP7CfohbHdU zh>shLkn~v{=*HoAMEv_s)cBQRGTJbAU29WCjU%Fjcq~#oadU*{J7Q6MQ4K5bekUHW z=I?gexZQMl + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "ScreenshotFeaturePlugin.h" +#include "ComputerControlInterface.h" +#include "VeyonMasterInterface.h" +#include "Screenshot.h" + + +ScreenshotFeaturePlugin::ScreenshotFeaturePlugin( QObject* parent ) : + QObject( parent ), + m_screenshotFeature( Feature( QStringLiteral( "Screenshot" ), + Feature::Action | Feature::Master, + Feature::Uid( "d5ee3aac-2a87-4d05-b827-0c20344490bd" ), + Feature::Uid(), + tr( "Screenshot" ), {}, + tr( "Use this function to take a screenshot of selected computers." ), + QStringLiteral(":/screenshot/camera-photo.png") ) ), + m_features( { m_screenshotFeature } ) +{ +} + + + +const FeatureList &ScreenshotFeaturePlugin::featureList() const +{ + return m_features; +} + + + +bool ScreenshotFeaturePlugin::controlFeature( Feature::Uid featureUid, + Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + Q_UNUSED(arguments) + + if( hasFeature( featureUid ) && operation == Operation::Start ) + { + for( const auto& controlInterface : computerControlInterfaces ) + { + Screenshot().take( controlInterface ); + } + + return true; + } + + return false; +} + + + +bool ScreenshotFeaturePlugin::startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( controlFeature( feature.uid(), Operation::Start, {}, computerControlInterfaces ) ) + { + QMessageBox::information( master.mainWindow(), + tr( "Screenshots taken" ), + tr( "Screenshot of %1 computer have been taken successfully." ). + arg( computerControlInterfaces.count() ) ); + + return true; + } + + return false; +} diff --git a/plugins/screenshot/ScreenshotFeaturePlugin.h b/plugins/screenshot/ScreenshotFeaturePlugin.h new file mode 100644 index 0000000..123e697 --- /dev/null +++ b/plugins/screenshot/ScreenshotFeaturePlugin.h @@ -0,0 +1,82 @@ +/* + * ScreenshotFeaturePlugin.h - declaration of ScreenshotFeature class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "Feature.h" +#include "FeatureProviderInterface.h" + +class ScreenshotFeaturePlugin : public QObject, FeatureProviderInterface, PluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.Screenshot") + Q_INTERFACES(PluginInterface FeatureProviderInterface) +public: + explicit ScreenshotFeaturePlugin( QObject* parent = nullptr ); + ~ScreenshotFeaturePlugin() override = default; + + Plugin::Uid uid() const override + { + return QStringLiteral("ee322521-f4fb-482d-b082-82a79003afa7"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral("Screenshot"); + } + + QString description() const override + { + return tr( "Take screenshots of computers and save them locally." ); + } + + QString vendor() const override + { + return QStringLiteral("Veyon Community"); + } + + QString copyright() const override + { + return QStringLiteral("Tobias Junghans"); + } + + const FeatureList& featureList() const override; + + bool controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + +private: + const Feature m_screenshotFeature; + const FeatureList m_features; + +}; diff --git a/plugins/screenshot/camera-photo.png b/plugins/screenshot/camera-photo.png new file mode 100644 index 0000000000000000000000000000000000000000..964a472cf67e542f92e2353737fee7b49c8bc671 GIT binary patch literal 2986 zcmai$c{J1w7sr3oFh&gHF_uADLXveT_1O2dh>|5{$d-M}6caNO8I5%qvWp~pq7-3z z>=arQS!RrsWEo2t(Re+7y??#uz31NhJ@=mbIp_ZQO>sDH2|Fxt7ytm6wUwFUf#Uxy zDCD4b8+Z*L2v?Me^+o7G;GjOFgPAwn$~6iAjwJtEpp1oe--9JG+T11DDalNt> zV6j-WYoS3=SG~f0)xskE3s?0d0D$kfwVCn78=qH-cbqO>7VX~*du_kz?sM&Rv3`?d;TvI$ND~t22_Yuhm>HKFU6d;)lFH?tkhFvNX0~SQ zVA0J*FTR~!Df`F0Ahb=o$bP)~iOX;O+3np`-ES}B8XG$-;9SQ4J2iQB#t9}Yzsy5E z0FmD)69h#8#t@kSfD4H^&AE^8JT2g%jRHK*pa5fy;Az)ymf&G*Ct0-e?ME_>&GS&xbQO^_9)7&(pMqMyjDu~RZ-1S41_XuAs-4VxStwKQxXtOpn_vd~cx$*TUcxZ$%;zuHxO3+ zJr?4C$|~hkH{Kgd9EP|3Ih?r6<~G54+=&+y4E%hul&Xolc6Mr7FV9Ot{IY zP+Fst+Thyt@W7C+MRFITy+7Dx|GW8|2g1;&{}OcaCX9%s8coc)vn-rz7dNfySMRJx ztQFGq0+M^H!iKO0G>?wrldXxiD1Ec4oG#^;l0Aa6*N+f|!%SGKj-)fr>TPB!PTMzT z0JO(Vc{;Cbcd&S`*eRlzo;}ZnT^l{M_;5!gITC7L@74c-ujJE`oUr(7=SB&PXJ8f% zhEF@HiH-lz8Kk8cKUOjYZ`l^mGFH{?e9Zfdhe;2x^r zzsEY`aQ2&H4kdx2dh}9htZ`J7mUx;43J>R{>c13=nww0wF4U83zSJm=>3$)aaaa0A zndqUe`s903oC`9m&ya3zZe>rOip0jn4FXPtO_f|e`GmKYt9At|mVFDa(D@AA)d|-# zn5&^DRs0RxcP!M&1}?E|wZza70>kh)ktvvvTvi$M!r|pEk}G6_?^P4G8VSt3K$SqkDeyg>FL7ByIC8CMZP& zL8kNcmQ%C)nSy7Iaoq>Wlcd8RgNW&mxTL`g%Bsf=EG#W~&PWNMXv1H$r`Yr*-&3|V zoAa&$EInD3COcJ5BJ_8-u+p!KBvC_|1zs+xz(k#qnDvr4zqAx#biH%;`zrhUwY7YG zeSHIf@Z|pEj0CoQ`|;|*X6A(H#0aH3@tuLoFY_l#;P56{R|*@HTp%OYoV`80J=?4& zCT)oy89A?ZV#~)@Mi-28?c4vTuDnywB0zr_40ZeG&ad9u9`dNZz(j@ITus3GA3+_{ zffJk(T3%(YSWo{GhuU!yELI0e8?-dv>)0n1Cs97P8wa)83def~Pak)~?H{X$=Zus> zOCN`fVjyt0neOrJ zzYgB;mGsb|-R(*drDvq9l{N?kL)KF=yiOOsIDfxg89a$(m=fe2@?GSsTMdilH8QD0 z@7`_Q8UihQZv|r)p48LoqB5<7X`cN05EP!pO*c=SMl091cAXVNgf&n?fnU0yDO)r0 zT3$m^CVw#osbqmu;fsrnou(j$Mu5Rf9ecO%6_+UJWhH}@yioj4Yf8_y?I*$p4`fB$~Yp+!?nCXr>cOn4C>k?W%Jhq}7zK5B8OvmdnU$!Ifc*veM!+_#?l9z+dn zK45lt&w?3+rkAr1DBzRROD9@_X2rXjD+#4t3et!lF+^r@m!1~5?+Fb4JYI*Z2Tj#m zu3&GbOUi1Ky9h}M*&{L@I6KH&py4a{NfG?7xV!|D0b=tJJ2vyX{Z}?R&)%k5C;tE_ zlu^v1w@fM5sgW}@wY9%E8pzm!r^lf7ykxx%8cgfv(nD3w54*iFmAR*Umka9O|0Qh+ z6uGLLlFqCIEia#y`2csjS3$^p;MhoB!@lF!{Fc;8eOvhYy{Po@Oooz-f^_Ce3rqOt zKR-YfS{FS<_J0cdkp@IP8%j8jrb3wWA0kgw;Dce9BS&x3zw-Hi9Qq*9m?ACby@(#- z+&SIR8^Y9u$;2QsfBL<({9#sic;vn5#M}~63?1=VE-))VqJ6hRp>r9AkK0I@6!P(E zNY7`yeVtP3MM(OV)Z5Q3E0H}O#60%IQ|fL`v_Q(0?8ao)79@i#U34Nok20*P?$R1uaXRmze36wQhV6Itw+pvAk8z^2%}X_bcki_1o(Bil*m!Y} zFeRJV8wYGU2I333GS|7`U3o`|-;4{ajW<-`}~V0}&H>^`OjW0#fDWdJR_;6{<4IGMA=3JpVV`IB3h$w^rI z^l73^C9(gUYNk`|?F4H6+zRf~8+_O9S|lIhQC-=of|=hopYnS0IzaH369I?(8ISN8 zJeO=G5r*H1Tgi%~`p2{?g;7rX^Ay`L7M*=+&vWm{$)ZP1Cf5p)EwS@0RS|>6d#+vp z7q`(|F?0To-H858#nsnySIX;GKhT`goIga?PpWY0Gy!5cNvJf8x_U)8!5Ik{zp~mp z8+?GtuiqK~5F7j`0Ey=}`ma?y7-gWu|C1zFS^}ti<3YFkNWsE^uLi8m&zm)vV2J+# DBGZD1 literal 0 HcmV?d00001 diff --git a/plugins/screenshot/screenshot.qrc b/plugins/screenshot/screenshot.qrc new file mode 100644 index 0000000..7d26b75 --- /dev/null +++ b/plugins/screenshot/screenshot.qrc @@ -0,0 +1,5 @@ + + + camera-photo.png + + diff --git a/plugins/servicecontrol/CMakeLists.txt b/plugins/servicecontrol/CMakeLists.txt new file mode 100644 index 0000000..b0d2ce0 --- /dev/null +++ b/plugins/servicecontrol/CMakeLists.txt @@ -0,0 +1,6 @@ +INCLUDE(BuildVeyonPlugin) + +build_veyon_plugin(servicecontrol + ServiceControlPlugin.cpp + ServiceControlPlugin.h +) diff --git a/plugins/servicecontrol/ServiceControlPlugin.cpp b/plugins/servicecontrol/ServiceControlPlugin.cpp new file mode 100644 index 0000000..ca98424 --- /dev/null +++ b/plugins/servicecontrol/ServiceControlPlugin.cpp @@ -0,0 +1,113 @@ +/* + * ServiceControlPlugin.cpp - implementation of ServiceControlPlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "CommandLineIO.h" +#include "ServiceControlPlugin.h" +#include "VeyonServiceControl.h" + +ServiceControlPlugin::ServiceControlPlugin( QObject* parent ) : + QObject( parent ), + m_commands( { +{ QStringLiteral("register"), tr( "Register Veyon Service" ) }, +{ QStringLiteral("unregister"), tr( "Unregister Veyon Service" ) }, +{ QStringLiteral("start"), tr( "Start Veyon Service" ) }, +{ QStringLiteral("stop"), tr( "Stop Veyon Service" ) }, +{ QStringLiteral("restart"), tr( "Restart Veyon Service" ) }, +{ QStringLiteral("status"), tr( "Query status of Veyon Service" ) }, + } ) +{ +} + + + +CommandLinePluginInterface::RunResult ServiceControlPlugin::handle_register( const QStringList& arguments ) +{ + Q_UNUSED(arguments) + + VeyonServiceControl serviceControl; + serviceControl.registerService(); + + return serviceControl.isServiceRegistered() ? Successful : Failed; +} + + + +CommandLinePluginInterface::RunResult ServiceControlPlugin::handle_unregister( const QStringList& arguments ) +{ + Q_UNUSED(arguments) + VeyonServiceControl serviceControl; + serviceControl.unregisterService(); + + return serviceControl.isServiceRegistered() ? Failed : Successful; +} + + + +CommandLinePluginInterface::RunResult ServiceControlPlugin::handle_start( const QStringList& arguments ) +{ + Q_UNUSED(arguments) + + VeyonServiceControl serviceControl; + serviceControl.startService(); + + return serviceControl.isServiceRunning() ? Successful : Failed; +} + + + +CommandLinePluginInterface::RunResult ServiceControlPlugin::handle_stop( const QStringList& arguments ) +{ + Q_UNUSED(arguments) + + VeyonServiceControl serviceControl; + serviceControl.stopService(); + + return serviceControl.isServiceRunning() ? Failed : Successful; +} + + + +CommandLinePluginInterface::RunResult ServiceControlPlugin::handle_restart( const QStringList& arguments ) +{ + handle_stop( arguments ); + return handle_start( arguments ); +} + + + +CommandLinePluginInterface::RunResult ServiceControlPlugin::handle_status( const QStringList& arguments ) +{ + Q_UNUSED(arguments) + + if( VeyonServiceControl().isServiceRunning() ) + { + CommandLineIO::print( tr( "Service is running" ) ); + } + else + { + CommandLineIO::print( tr( "Service is not running" ) ); + } + + return NoResult; +} diff --git a/plugins/servicecontrol/ServiceControlPlugin.h b/plugins/servicecontrol/ServiceControlPlugin.h new file mode 100644 index 0000000..4c9a086 --- /dev/null +++ b/plugins/servicecontrol/ServiceControlPlugin.h @@ -0,0 +1,99 @@ +/* + * ServiceControlPlugin.h - declaration of ServiceControlPlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CommandLinePluginInterface.h" + +class ServiceControlPlugin : public QObject, CommandLinePluginInterface, PluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.ServiceControl") + Q_INTERFACES(PluginInterface CommandLinePluginInterface) +public: + explicit ServiceControlPlugin( QObject* parent = nullptr ); + ~ServiceControlPlugin() override = default; + + Plugin::Uid uid() const override + { + return QStringLiteral("b47bcae0-24ff-4bf5-869c-484d64af5c4c"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral( "ServiceControl" ); + } + + QString description() const override + { + return tr( "Configure and control Veyon service" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + QString commandLineModuleName() const override + { + return QStringLiteral( "service" ); + } + + QString commandLineModuleHelp() const override + { + return tr( "Commands for configuring and controlling Veyon Service" ); + } + + QStringList commands() const override + { + return m_commands.keys(); + } + + QString commandHelp( const QString& command ) const override + { + return m_commands.value( command ); + } + +public Q_SLOTS: + CommandLinePluginInterface::RunResult handle_register( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_unregister( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_start( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_stop( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_restart( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_status( const QStringList& arguments ); + +private: + QMap m_commands; + +}; diff --git a/plugins/shell/CMakeLists.txt b/plugins/shell/CMakeLists.txt new file mode 100644 index 0000000..2e2ff4f --- /dev/null +++ b/plugins/shell/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildVeyonPlugin) + +build_veyon_plugin(shell ShellCommandLinePlugin.cpp ShellCommandLinePlugin.h) diff --git a/plugins/shell/ShellCommandLinePlugin.cpp b/plugins/shell/ShellCommandLinePlugin.cpp new file mode 100644 index 0000000..ccb7a22 --- /dev/null +++ b/plugins/shell/ShellCommandLinePlugin.cpp @@ -0,0 +1,104 @@ +/* + * ShellCommandLinePlugin.cpp - implementation of ShellCommandLinePlugin class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include "CommandLineIO.h" +#include "ShellCommandLinePlugin.h" + + +ShellCommandLinePlugin::ShellCommandLinePlugin( QObject* parent ) : + QObject( parent ), + m_commands( { +{ QStringLiteral("run"), tr( "Run command file" ) }, + } ) +{ +} + + + +QStringList ShellCommandLinePlugin::commands() const +{ + return m_commands.keys(); +} + + + +QString ShellCommandLinePlugin::commandHelp( const QString& command ) const +{ + return m_commands.value( command ); +} + + + +CommandLinePluginInterface::RunResult ShellCommandLinePlugin::handle_main() +{ + QTextStream stream( stdin ); + + while( true ) + { + printf("VEYON> "); + + QString line; + if( stream.readLineInto( &line ) && line != QLatin1String("exit") ) + { + runCommand( line ); + } + else + { + break; + } + } + + return NoResult; +} + + + +CommandLinePluginInterface::RunResult ShellCommandLinePlugin::handle_run( const QStringList& arguments ) +{ + QFile scriptFile( arguments.value( 0 ) ); + if( scriptFile.exists() == false ) + { + CommandLineIO::error( tr( "File \"%1\" does not exist!" ).arg( scriptFile.fileName() ) ); + return Failed; + } + + while( scriptFile.canReadLine() ) + { + runCommand( QString::fromUtf8( scriptFile.readLine() ) ); + } + + return Successful; +} + + + +void ShellCommandLinePlugin::runCommand( const QString& command ) +{ + // TODO: properly split arguments containing spaces + QProcess::execute( QCoreApplication::applicationFilePath(), command.split( QLatin1Char(' ') ) ); +} diff --git a/plugins/shell/ShellCommandLinePlugin.h b/plugins/shell/ShellCommandLinePlugin.h new file mode 100644 index 0000000..045880a --- /dev/null +++ b/plugins/shell/ShellCommandLinePlugin.h @@ -0,0 +1,90 @@ +/* + * ShellCommandLinePlugin.h - declaration of ShellCommandLinePlugin class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CommandLinePluginInterface.h" + +class ShellCommandLinePlugin : public QObject, CommandLinePluginInterface, PluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.ShellCommandLineInterface") + Q_INTERFACES(PluginInterface CommandLinePluginInterface) +public: + explicit ShellCommandLinePlugin( QObject* parent = nullptr ); + ~ShellCommandLinePlugin() override = default; + + Plugin::Uid uid() const override + { + return QStringLiteral("85f6c631-e75a-4c78-8cb2-a7f3f502015a"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral( "Shell" ); + } + + QString description() const override + { + return tr( "Interactive shell and script execution for Veyon Control" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + QString commandLineModuleName() const override + { + return QStringLiteral( "shell" ); + } + + QString commandLineModuleHelp() const override + { + return tr( "Commands for shell functionalities" ); + } + + QStringList commands() const override; + QString commandHelp( const QString& command ) const override; + +public Q_SLOTS: + CommandLinePluginInterface::RunResult handle_main(); + CommandLinePluginInterface::RunResult handle_run( const QStringList& arguments ); + +private: + void runCommand( const QString& command ); + + QMap m_commands; + +}; diff --git a/plugins/systemusergroups/CMakeLists.txt b/plugins/systemusergroups/CMakeLists.txt new file mode 100644 index 0000000..7c1b890 --- /dev/null +++ b/plugins/systemusergroups/CMakeLists.txt @@ -0,0 +1,6 @@ +INCLUDE(BuildVeyonPlugin) + +build_veyon_plugin(systemusergroups + SystemUserGroupsPlugin.cpp + SystemUserGroupsPlugin.h +) diff --git a/plugins/systemusergroups/SystemUserGroupsPlugin.cpp b/plugins/systemusergroups/SystemUserGroupsPlugin.cpp new file mode 100644 index 0000000..dba93c0 --- /dev/null +++ b/plugins/systemusergroups/SystemUserGroupsPlugin.cpp @@ -0,0 +1,53 @@ +/* + * SystemUserGroupsPlugin.cpp - implementation of SystemUserGroupsPlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "SystemUserGroupsPlugin.h" +#include "PlatformPluginInterface.h" +#include "PlatformUserFunctions.h" + + +SystemUserGroupsPlugin::SystemUserGroupsPlugin( QObject* parent ) : + QObject( parent ) +{ +} + + + +void SystemUserGroupsPlugin::reloadConfiguration() +{ +} + + + +QStringList SystemUserGroupsPlugin::userGroups( bool queryDomainGroups ) +{ + return VeyonCore::platform().userFunctions().userGroups( queryDomainGroups ); +} + + + +QStringList SystemUserGroupsPlugin::groupsOfUser( const QString& username, bool queryDomainGroups ) +{ + return VeyonCore::platform().userFunctions().groupsOfUser( username, queryDomainGroups ); +} diff --git a/plugins/systemusergroups/SystemUserGroupsPlugin.h b/plugins/systemusergroups/SystemUserGroupsPlugin.h new file mode 100644 index 0000000..4b72d20 --- /dev/null +++ b/plugins/systemusergroups/SystemUserGroupsPlugin.h @@ -0,0 +1,83 @@ +/* + * SystemUserGroupsPlugin.h - declaration of SystemUserGroupsPlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "UserGroupsBackendInterface.h" + +class SystemUserGroupsPlugin : public QObject, PluginInterface, UserGroupsBackendInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.SystemUserGroups") + Q_INTERFACES(PluginInterface UserGroupsBackendInterface) +public: + explicit SystemUserGroupsPlugin( QObject* paren = nullptr ); + ~SystemUserGroupsPlugin() override = default; + + Plugin::Uid uid() const override + { + return QStringLiteral("2917cdeb-ac13-4099-8715-20368254a367"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral( "SystemUserGroups" ); + } + + QString description() const override + { + return tr( "User groups backend for system user groups" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + Plugin::Flags flags() const override + { + return Plugin::ProvidesDefaultImplementation; + } + + QString userGroupsBackendName() const override + { + return tr( "Default (system user groups)" ); + } + + void reloadConfiguration() override; + + QStringList userGroups( bool queryDomainGroups ) override; + QStringList groupsOfUser( const QString& username, bool queryDomainGroups ) override; + +}; diff --git a/plugins/testing/CMakeLists.txt b/plugins/testing/CMakeLists.txt new file mode 100644 index 0000000..bdf691e --- /dev/null +++ b/plugins/testing/CMakeLists.txt @@ -0,0 +1,5 @@ +INCLUDE(BuildVeyonPlugin) + +IF(VEYON_DEBUG) +build_veyon_plugin(testing TestingCommandLinePlugin.cpp TestingCommandLinePlugin.h) +ENDIF() diff --git a/plugins/testing/TestingCommandLinePlugin.cpp b/plugins/testing/TestingCommandLinePlugin.cpp new file mode 100644 index 0000000..d462555 --- /dev/null +++ b/plugins/testing/TestingCommandLinePlugin.cpp @@ -0,0 +1,120 @@ +/* + * TestingCommandLinePlugin.cpp - implementation of TestingCommandLinePlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "CommandLineIO.h" +#include "AccessControlProvider.h" +#include "TestingCommandLinePlugin.h" + + +TestingCommandLinePlugin::TestingCommandLinePlugin( QObject* parent ) : + QObject( parent ), + m_commands( { +{ QStringLiteral("checkaccess"), QStringLiteral( "check access with arguments [ACCESSING USER] [ACCESSING COMPUTER] [CONNECTED USER]" ) }, +{ QStringLiteral("authorizedgroups"), QStringLiteral( "check if specified user is in authorized groups [ACCESSING USER]" ) }, +{ QStringLiteral("accesscontrolrules"), QStringLiteral( "process access control rules with arguments [ACCESSING USER] [ACCESSING COMPUTER] [LOCAL USER] [LOCAL COMPUTER] [CONNECTED USER]" ) }, +{ QStringLiteral("isaccessdeniedbylocalstate"), QStringLiteral( "check if access would be denied by local state") }, + } ) +{ +} + + + +QStringList TestingCommandLinePlugin::commands() const +{ + return m_commands.keys(); +} + + + +QString TestingCommandLinePlugin::commandHelp( const QString& command ) const +{ + return m_commands.value( command ); +} + + + +CommandLinePluginInterface::RunResult TestingCommandLinePlugin::handle_checkaccess( const QStringList& arguments ) +{ + + switch( AccessControlProvider().checkAccess( arguments.value( 0 ), arguments.value( 1 ), { arguments.value( 2 ) } ) ) + { + case AccessControlProvider::Access::Allow: printf( "[TEST]: CheckAccess: ALLOW\n" ); return Successful; + case AccessControlProvider::Access::Deny: printf( "[TEST]: CheckAccess: DENY\n" ); return Successful; + case AccessControlProvider::Access::ToBeConfirmed: printf( "[TEST]: CheckAccess: TO BE CONFIRMED\n" ); return Successful; + } + + printf( "[TEST]: CheckAccess: FAIL\n" ); + return Failed; +} + + + +CommandLinePluginInterface::RunResult TestingCommandLinePlugin::handle_authorizedgroups( const QStringList& arguments ) +{ + if( AccessControlProvider().processAuthorizedGroups( arguments.value( 0 ) ) ) + { + printf( "[TEST]: AuthorizedGroups: ALLOW\n" ); + } + else + { + printf( "[TEST]: AuthorizedGroups: DENY\n" ); + } + return Successful; +} + + + +CommandLinePluginInterface::RunResult TestingCommandLinePlugin::handle_accesscontrolrules( const QStringList& arguments ) +{ + switch( AccessControlProvider().processAccessControlRules( arguments.value( 0 ), arguments.value( 1 ), + arguments.value( 2 ), arguments.value( 3 ), + QStringList( arguments.value( 4 ) ) ) ) + { + case AccessControlRule::Action::Allow: printf( "[TEST]: AccessControlRules: ALLOW\n" ); return Successful; + case AccessControlRule::Action::Deny: printf( "[TEST]: AccessControlRules: DENY\n" ); return Successful; + case AccessControlRule::Action::None: printf( "[TEST]: AccessControlRules: NONE\n" ); return Successful; + case AccessControlRule::Action::AskForPermission: printf( "[TEST]: AccessControlRules: ASK FOR PERMISSION\n" ); return Successful; + } + + printf( "[TEST]: AccessControlRules: FAIL\n" ); + return Failed; +} + + + +CommandLinePluginInterface::RunResult TestingCommandLinePlugin::handle_isaccessdeniedbylocalstate( const QStringList& arguments ) +{ + Q_UNUSED(arguments) + + if( AccessControlProvider().isAccessToLocalComputerDenied() ) + { + printf( "[TEST]: IsAccessDeniedByLocalState: YES (server will listen on localhost only)\n" ); + } + else + { + printf( "[TEST]: IsAccessDeniedByLocalState: NO (server will listen normally on all interfaces)\n" ); + } + + return Successful; +} diff --git a/plugins/testing/TestingCommandLinePlugin.h b/plugins/testing/TestingCommandLinePlugin.h new file mode 100644 index 0000000..4fedf0a --- /dev/null +++ b/plugins/testing/TestingCommandLinePlugin.h @@ -0,0 +1,91 @@ +/* + * TestingCommandLinePlugin.h - declaration of TestingCommandLinePlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CommandLinePluginInterface.h" +#include "VeyonConfiguration.h" + +class TestingCommandLinePlugin : public QObject, CommandLinePluginInterface, PluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.TestingCommandLineInterface") + Q_INTERFACES(PluginInterface CommandLinePluginInterface) +public: + explicit TestingCommandLinePlugin( QObject* parent = nullptr ); + ~TestingCommandLinePlugin() override = default; + + Plugin::Uid uid() const override + { + return QStringLiteral("a8a84654-40ca-4731-811e-7e05997ed081"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 0 ); + } + + QString name() const override + { + return QStringLiteral( "Testing" ); + } + + QString description() const override + { + return tr( "Test internal Veyon components and functions" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + QString commandLineModuleName() const override + { + return QStringLiteral( "testing" ); + } + + QString commandLineModuleHelp() const override + { + return tr( "Commands for testing internal components and functions of Veyon" ); + } + + QStringList commands() const override; + QString commandHelp( const QString& command ) const override; + +public Q_SLOTS: + CommandLinePluginInterface::RunResult handle_checkaccess( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_authorizedgroups( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_accesscontrolrules( const QStringList& arguments ); + CommandLinePluginInterface::RunResult handle_isaccessdeniedbylocalstate( const QStringList& arguments ); + +private: + QMap m_commands; + +}; diff --git a/plugins/textmessage/CMakeLists.txt b/plugins/textmessage/CMakeLists.txt new file mode 100644 index 0000000..c1b0246 --- /dev/null +++ b/plugins/textmessage/CMakeLists.txt @@ -0,0 +1,11 @@ +INCLUDE(BuildVeyonPlugin) + +build_veyon_plugin(textmessage + TextMessageFeaturePlugin.cpp + TextMessageDialog.cpp + TextMessageDialog.ui + TextMessageFeaturePlugin.h + TextMessageDialog.h + textmessage.qrc +) + diff --git a/plugins/textmessage/TextMessageDialog.cpp b/plugins/textmessage/TextMessageDialog.cpp new file mode 100644 index 0000000..ec75331 --- /dev/null +++ b/plugins/textmessage/TextMessageDialog.cpp @@ -0,0 +1,56 @@ +/* + * TextMessageDialog.cpp - implementation of text message dialog class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "TextMessageDialog.h" +#include "VeyonCore.h" + +#include "ui_TextMessageDialog.h" + + +TextMessageDialog::TextMessageDialog( QString &msgStr, QWidget *parent ) : + QDialog( parent ), + ui( new Ui::TextMessageDialog ), + m_msgStr( msgStr ) +{ + ui->setupUi( this ); + + VeyonCore::enforceBranding( this ); +} + + + +TextMessageDialog::~TextMessageDialog() +{ + delete ui; +} + + + +void TextMessageDialog::accept() +{ + m_msgStr = ui->textEdit->toPlainText(); + QDialog::accept(); +} diff --git a/plugins/textmessage/TextMessageDialog.h b/plugins/textmessage/TextMessageDialog.h new file mode 100644 index 0000000..2eec97a --- /dev/null +++ b/plugins/textmessage/TextMessageDialog.h @@ -0,0 +1,49 @@ +/* + * TextMessageDialog.h - declaration of text message dialog class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +namespace Ui +{ + class TextMessageDialog; +} + +class TextMessageDialog : public QDialog +{ + Q_OBJECT +public: + TextMessageDialog( QString &msgStr, QWidget *parent ); + ~TextMessageDialog() override; + +private Q_SLOTS: + void accept() override; + + +private: + Ui::TextMessageDialog *ui; + QString &m_msgStr; + +} ; diff --git a/plugins/textmessage/TextMessageDialog.ui b/plugins/textmessage/TextMessageDialog.ui new file mode 100644 index 0000000..3e59362 --- /dev/null +++ b/plugins/textmessage/TextMessageDialog.ui @@ -0,0 +1,73 @@ + + + Tobias Junghans + TextMessageDialog + + + Send text message + + + + :/textmessage/dialog-information.png:/textmessage/dialog-information.png + + + + + + Use the field below to type your message which will be sent to all selected users. + + + true + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + TextMessageDialog + accept() + + + 199 + 384 + + + 199 + 206 + + + + + buttonBox + rejected() + TextMessageDialog + reject() + + + 199 + 384 + + + 199 + 206 + + + + + diff --git a/plugins/textmessage/TextMessageFeaturePlugin.cpp b/plugins/textmessage/TextMessageFeaturePlugin.cpp new file mode 100644 index 0000000..bf71779 --- /dev/null +++ b/plugins/textmessage/TextMessageFeaturePlugin.cpp @@ -0,0 +1,149 @@ +/* + * TextMessageFeaturePlugin.cpp - implementation of TextMessageFeaturePlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "TextMessageFeaturePlugin.h" +#include "TextMessageDialog.h" +#include "FeatureWorkerManager.h" +#include "ComputerControlInterface.h" +#include "VeyonMasterInterface.h" +#include "VeyonServerInterface.h" + + +TextMessageFeaturePlugin::TextMessageFeaturePlugin( QObject* parent ) : + QObject( parent ), + m_textMessageFeature( Feature( QStringLiteral( "TextMessage" ), + Feature::Action | Feature::AllComponents, + Feature::Uid( "e75ae9c8-ac17-4d00-8f0d-019348346208" ), + Feature::Uid(), + tr( "Text message" ), {}, + tr( "Use this function to send a text message to all " + "users e.g. to assign them new tasks." ), + QStringLiteral(":/textmessage/dialog-information.png") ) ), + m_features( { m_textMessageFeature } ) +{ +} + + + +const FeatureList &TextMessageFeaturePlugin::featureList() const +{ + return m_features; +} + + + +bool TextMessageFeaturePlugin::controlFeature( Feature::Uid featureUid, + Operation operation, + const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( operation != Operation::Start ) + { + return false; + } + + if( featureUid == m_textMessageFeature.uid() ) + { + const auto text = arguments.value( argToString(Argument::Text) ).toString(); + const auto icon = arguments.value( argToString(Argument::Icon) ).toInt(); + + sendFeatureMessage( FeatureMessage{ featureUid, ShowTextMessage } + .addArgument( Argument::Text, text ) + .addArgument( Argument::Icon, icon ), computerControlInterfaces ); + + return true; + } + + return false; +} + + + +bool TextMessageFeaturePlugin::startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( feature.uid() != m_textMessageFeature.uid() ) + { + return false; + } + + QString textMessage; + + TextMessageDialog( textMessage, master.mainWindow() ).exec(); + + if( textMessage.isEmpty() == false ) + { + controlFeature( m_textMessageFeature.uid(), Operation::Start, + { + { argToString(Argument::Text), textMessage }, + { argToString(Argument::Icon), QMessageBox::Information } + }, + computerControlInterfaces ); + } + + return true; +} + + + + +bool TextMessageFeaturePlugin::handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) +{ + Q_UNUSED(messageContext) + + if( m_textMessageFeature.uid() == message.featureUid() ) + { + // forward message to worker + server.featureWorkerManager().sendMessageToUnmanagedSessionWorker( message ); + + return true; + } + + return false; +} + + + +bool TextMessageFeaturePlugin::handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) +{ + Q_UNUSED(worker); + + if( message.featureUid() == m_textMessageFeature.uid() ) + { + QMessageBox* messageBox = new QMessageBox( static_cast( message.argument( Argument::Icon ).toInt() ), + tr( "Message from teacher" ), + message.argument( Argument::Text ).toString() ); + messageBox->show(); + + connect( messageBox, &QMessageBox::accepted, messageBox, &QMessageBox::deleteLater ); + + return true; + } + + return true; +} diff --git a/plugins/textmessage/TextMessageFeaturePlugin.h b/plugins/textmessage/TextMessageFeaturePlugin.h new file mode 100644 index 0000000..29ff79a --- /dev/null +++ b/plugins/textmessage/TextMessageFeaturePlugin.h @@ -0,0 +1,97 @@ +/* + * TextMessageFeaturePlugin.h - declaration of TextMessageFeature class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "Feature.h" +#include "FeatureProviderInterface.h" + +class TextMessageFeaturePlugin : public QObject, FeatureProviderInterface, PluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.TextMessage") + Q_INTERFACES(PluginInterface FeatureProviderInterface) +public: + enum class Argument { + Text, + Icon + }; + Q_ENUM(Argument) + + explicit TextMessageFeaturePlugin( QObject* parent = nullptr ); + ~TextMessageFeaturePlugin() override = default; + + Plugin::Uid uid() const override + { + return QStringLiteral("8ae6668b-9c12-4b29-9bfc-ff89f6604164"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral("TextMessage"); + } + + QString description() const override + { + return tr( "Send a message to a user" ); + } + + QString vendor() const override + { + return QStringLiteral("Veyon Community"); + } + + QString copyright() const override + { + return QStringLiteral("Tobias Junghans"); + } + + const FeatureList& featureList() const override; + + bool controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) override; + + bool handleFeatureMessage( VeyonWorkerInterface& worker, const FeatureMessage& message ) override; + +private: + enum Commands { + ShowTextMessage + }; + + const Feature m_textMessageFeature; + const FeatureList m_features; + +}; diff --git a/plugins/textmessage/dialog-information.png b/plugins/textmessage/dialog-information.png new file mode 100644 index 0000000000000000000000000000000000000000..3c6cf5889a0e907fcc7eb618104347967d8927e1 GIT binary patch literal 1889 zcmV-n2cGzeP)XQY6^nwJFJUcu zI(~y6U@d4!LCr^yf=rT0fQ-iv`JsC#t~A!ulI^j_(%rl9<6dbj?|t?;_aP5J5ClOG z1VIo4K@bE%5ClOGM4KVo9`Nz+3+2~#x_I$be=APCt^We)Zxtv2$I;R zvmE^G^LN1HX1xzuWNrJI$iKVDsE^DhFe;F<%hn0NoteP%$H2vta`H2ge@A}QN9Ggg zY(xiW^jg5+k^TJB8(Zzi)qhds>zaIs*rxdm3V{qRDS$?c00K;J?4#BsKTZ*|8C204 z5xBV+G`O@Wp~ChdVq4-Afv9gVg$83fK$9W{BU)R2n&q);GM1PaLDf&yr^ z2n3jo*5pT#u4N;GWZ^iU+1LKirFnpM?Ff;<5DEhc#?iG2)T+UfE&{89L<*4gqb{aa zE)5ldGY;_cm%RAFDYfvp?*=Po!vGf_Q(l_~hLIX_V(A~}SNi^X|h zUo}hwTnk9Gm(K>Khm4u`O+o}JK>v&aXq*FR`JBKe`_CSui@>fTHbewW0ZCyO%n59? z|B_>LIkzK<-hd9!loVhI-97TN$LL~i2MQ-H0y;oeB6~!B?wHlWjwJho_6R^*yCLL$ zB0qP`YGGfVs1vXVOr?MgRsSyJ$5=*AaDpOW3P@?7#LBm^eUakxhD*DY-1D3601e$1 zUi;b-)4iH_gaB$1kzUa^VJuL!qk?b&g+e^P$pUEcFtEtV4c0`XWFn}jen3(mwoM_x zas^d^czvdTW5S@$tw9+p*8=jZQbq1`iS1{xIfP65^aMme2gptY)rtHvx5%6?75Ryb zVQYewtqHn4I>1TP0Q*FKLE%>z`PshV$~HZq2@N*;hmWV{FqA zL_h)Db*N9$LCM!dIub2jTX;wTDB8IepqcI7sqtGuaVXJULn;|{<@(k;uRUM3&DcM1 zKoQUZQbk}@ z0EftD5xAikY_sjt0V+~JF7l<^C7K%f&N;yMe~f`uP#k@mB41G2x5<(3oCApG+d?p0 z6ztyJ5cws2n;!Xl2%xz?5hI>&zwgxo=`CLxk+1889ns>wfo1mt#CD;YAitn*?)AgM zxu1y>&j;e|*g4ye=k2w=?H8nzQ@4F5Q$R`tUEc;@xJ>Wf%|(8N?H4TCG{N@w5;(O&CRjOT`_89;ln6R~XxHAj>5A<;T?7_8K@aHm zor-)qK+Xwz00bH?orru2pm>Z3=>Te0>nLt9`8x@~ESE>HG4WB)OH$2XdFNZ;Z@yow!Y#C}NwibYR-VrPS2GE25 zj>>8_!{p@^q|-6*BrNfVWazg}B`0ZCAN@4`1<-E}9{E=<(e?Tx|HHxO*FSvnh7NEl zIB=YWL4ffqlJDd^0LgEH102W&!XUu-HOW7H0hj}jKaV26*B|*qlJ8Us$dUge$vBKNWQ~GV7MUp&K7~;BEaMw$#*gZko?vbfi=nhhvYjcfN&9D^6wS8U6Suy3P_QE zN%Ea40>dTAch&(&zGL$MnjwGs-xWF}-<2Xz)8xN@g-*9O8jyUqQb3ygGm`I83J6yL zrZ*(tNdc@Dlc&?$Ym)D33J7NJKPCAN3!u~O{dwm5pOJimAP9mW2!bF8f*=TjAP9mW bi2d+Cn{5hPn+^n100000NkvXXu0mjf^|NLV literal 0 HcmV?d00001 diff --git a/plugins/textmessage/textmessage.qrc b/plugins/textmessage/textmessage.qrc new file mode 100644 index 0000000..93f64c5 --- /dev/null +++ b/plugins/textmessage/textmessage.qrc @@ -0,0 +1,5 @@ + + + dialog-information.png + + diff --git a/plugins/usersessioncontrol/CMakeLists.txt b/plugins/usersessioncontrol/CMakeLists.txt new file mode 100644 index 0000000..ac148a6 --- /dev/null +++ b/plugins/usersessioncontrol/CMakeLists.txt @@ -0,0 +1,11 @@ +INCLUDE(BuildVeyonPlugin) + +build_veyon_plugin(usersessioncontrol + UserSessionControlPlugin.cpp + UserSessionControlPlugin.h + UserLoginDialog.cpp + UserLoginDialog.h + UserLoginDialog.ui + usersessioncontrol.qrc +) + diff --git a/plugins/usersessioncontrol/UserLoginDialog.cpp b/plugins/usersessioncontrol/UserLoginDialog.cpp new file mode 100644 index 0000000..eca9984 --- /dev/null +++ b/plugins/usersessioncontrol/UserLoginDialog.cpp @@ -0,0 +1,75 @@ +/* + * UserLoginDialog.cpp - dialog for querying logon credentials + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "UserLoginDialog.h" + +#include "ui_UserLoginDialog.h" + + +UserLoginDialog::UserLoginDialog( QWidget *parent ) : + QDialog( parent ), + ui( new Ui::UserLoginDialog ) +{ + ui->setupUi( this ); + + if( ui->username->text().isEmpty() == false ) + { + ui->password->setFocus(); + } + + updateOkButton(); + + VeyonCore::enforceBranding( this ); +} + + + +UserLoginDialog::~UserLoginDialog() +{ + delete ui; +} + + + +QString UserLoginDialog::username() const +{ + return ui->username->text(); +} + + + +CryptoCore::PlaintextPassword UserLoginDialog::password() const +{ + return ui->password->text().toUtf8(); +} + + + +void UserLoginDialog::updateOkButton() +{ + ui->buttonBox->button( QDialogButtonBox::Ok )-> + setEnabled( !username().isEmpty() && !password().isEmpty() ); +} diff --git a/plugins/usersessioncontrol/UserLoginDialog.h b/plugins/usersessioncontrol/UserLoginDialog.h new file mode 100644 index 0000000..8ebb98f --- /dev/null +++ b/plugins/usersessioncontrol/UserLoginDialog.h @@ -0,0 +1,49 @@ +/* + * UserLoginDialog.h - dialog for querying logon credentials + * + * Copyright (c) 2019-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CryptoCore.h" + +#include + +namespace Ui { class UserLoginDialog; } + +class UserLoginDialog : public QDialog +{ + Q_OBJECT +public: + explicit UserLoginDialog( QWidget *parent ); + ~UserLoginDialog() override; + + QString username() const; + CryptoCore::PlaintextPassword password() const; + +private Q_SLOTS: + void updateOkButton(); + +private: + Ui::UserLoginDialog *ui; + +} ; diff --git a/plugins/usersessioncontrol/UserLoginDialog.ui b/plugins/usersessioncontrol/UserLoginDialog.ui new file mode 100644 index 0000000..636b7f1 --- /dev/null +++ b/plugins/usersessioncontrol/UserLoginDialog.ui @@ -0,0 +1,142 @@ + + + UserLoginDialog + + + User login + + + + :/usersessioncontrol/login-user.png:/usersessioncontrol/login-user.png + + + + + + Please enter a username and password for automatic login on all computers. + + + true + + + + + + + 16 + + + + + Username + + + + + + + + 350 + 0 + + + + + + + + Password + + + + + + + QLineEdit::Password + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + UserLoginDialog + accept() + + + 206 + 137 + + + 169 + 79 + + + + + buttonBox + rejected() + UserLoginDialog + reject() + + + 206 + 137 + + + 169 + 79 + + + + + password + textChanged(QString) + UserLoginDialog + updateOkButton() + + + 204 + 107 + + + 169 + 78 + + + + + username + textChanged(QString) + UserLoginDialog + updateOkButton() + + + 204 + 76 + + + 169 + 78 + + + + + + updateOkButton() + + diff --git a/plugins/usersessioncontrol/UserSessionControlPlugin.cpp b/plugins/usersessioncontrol/UserSessionControlPlugin.cpp new file mode 100644 index 0000000..45f97fd --- /dev/null +++ b/plugins/usersessioncontrol/UserSessionControlPlugin.cpp @@ -0,0 +1,166 @@ +/* + * UserSessionControlPlugin.cpp - implementation of UserSessionControlPlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "PlatformUserFunctions.h" +#include "UserLoginDialog.h" +#include "UserSessionControlPlugin.h" +#include "VeyonConfiguration.h" +#include "VeyonMasterInterface.h" + + +UserSessionControlPlugin::UserSessionControlPlugin( QObject* parent ) : + QObject( parent ), + m_userLoginFeature( QStringLiteral( "UserLogin" ), + Feature::Action | Feature::Master | Feature::Service, + Feature::Uid( "7310707d-3918-460d-a949-65bd152cb958" ), + Feature::Uid(), + tr( "Log in" ), {}, + tr( "Click this button to log in a specific user on all computers." ), + QStringLiteral( ":/usersessioncontrol/login-user.png" ) ), + m_userLogoffFeature( QStringLiteral( "UserLogoff" ), + Feature::Action | Feature::Master | Feature::Service, + Feature::Uid( "7311d43d-ab53-439e-a03a-8cb25f7ed526" ), + Feature::Uid(), + tr( "Log off" ), {}, + tr( "Click this button to log off users from all computers." ), + QStringLiteral( ":/usersessioncontrol/logout-user.png" ) ), + m_features( { m_userLoginFeature, m_userLogoffFeature } ) +{ +} + + + +bool UserSessionControlPlugin::controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + if( operation != Operation::Start ) + { + return false; + } + + if( featureUid == m_userLoginFeature.uid() ) + { + const auto username = arguments.value( argToString(Argument::Username) ).toString(); + const auto password = arguments.value( argToString(Argument::Password) ).toByteArray(); + if( username.isEmpty() ) + { + return false; + } + + sendFeatureMessage( FeatureMessage{ featureUid, FeatureMessage::DefaultCommand } + .addArgument( Argument::Username, username ) + .addArgument( Argument::Password, VeyonCore::cryptoCore().encryptPassword( password ) ), + computerControlInterfaces ); + + return true; + } + + if( featureUid == m_userLogoffFeature.uid() ) + { + sendFeatureMessage( FeatureMessage{ featureUid, FeatureMessage::DefaultCommand }, computerControlInterfaces ); + + return true; + } + + return false; +} + + + +bool UserSessionControlPlugin::startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) +{ + Q_UNUSED(master) + + if( confirmFeatureExecution( feature, master.mainWindow() ) == false ) + { + return false; + } + + if( feature == m_userLoginFeature ) + { + UserLoginDialog loginDialog( master.mainWindow() ); + if( loginDialog.exec() ) + { + return controlFeature( feature.uid(), Operation::Start, + { + { argToString(Argument::Username), loginDialog.username() }, + { argToString(Argument::Password), loginDialog.password().toByteArray() } + }, + computerControlInterfaces ); + } + } + else if( feature == m_userLogoffFeature ) + { + return controlFeature( feature.uid(), Operation::Start, {}, computerControlInterfaces ); + } + + return false; +} + + + +bool UserSessionControlPlugin::handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) +{ + Q_UNUSED(server) + Q_UNUSED(messageContext) + + if( message.featureUid() == m_userLoginFeature.uid() ) + { + VeyonCore::platform().userFunctions().prepareLogon( message.argument( Argument::Username ).toString(), + VeyonCore::cryptoCore().decryptPassword( + message.argument( Argument::Password ).toString() ) ); + return true; + } + else if( message.featureUid() == m_userLogoffFeature.uid() ) + { + VeyonCore::platform().userFunctions().logoff(); + return true; + } + + return false; +} + + + +bool UserSessionControlPlugin::confirmFeatureExecution( const Feature& feature, QWidget* parent ) +{ + if( VeyonCore::config().confirmUnsafeActions() == false ) + { + return true; + } + + if( feature == m_userLogoffFeature ) + { + return QMessageBox::question( parent, tr( "Confirm user logoff" ), + tr( "Do you really want to log off the selected users?" ) ) == + QMessageBox::Yes; + } + + return true; +} diff --git a/plugins/usersessioncontrol/UserSessionControlPlugin.h b/plugins/usersessioncontrol/UserSessionControlPlugin.h new file mode 100644 index 0000000..320f668 --- /dev/null +++ b/plugins/usersessioncontrol/UserSessionControlPlugin.h @@ -0,0 +1,99 @@ +/* + * UserSessionControlPlugin.h - declaration of UserSessionControlPlugin class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "FeatureProviderInterface.h" + +class UserSessionControlPlugin : public QObject, public FeatureProviderInterface, public PluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.UserSessionControl") + Q_INTERFACES(FeatureProviderInterface PluginInterface) +public: + enum class Argument + { + Username, + Password + }; + Q_ENUM(Argument) + + explicit UserSessionControlPlugin( QObject* parent = nullptr ); + ~UserSessionControlPlugin() override = default; + + Plugin::Uid uid() const override + { + return QStringLiteral("80580500-2e59-4297-9e35-e53959b028cd"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 2 ); + } + + QString name() const override + { + return QStringLiteral( "UserSessionControl" ); + } + + QString description() const override + { + return tr( "User session control" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + const FeatureList& featureList() const override + { + return m_features; + } + + bool controlFeature( Feature::Uid featureUid, Operation operation, const QVariantMap& arguments, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool startFeature( VeyonMasterInterface& master, const Feature& feature, + const ComputerControlInterfaceList& computerControlInterfaces ) override; + + bool handleFeatureMessage( VeyonServerInterface& server, + const MessageContext& messageContext, + const FeatureMessage& message ) override; + +private: + bool confirmFeatureExecution( const Feature& feature, QWidget* parent ); + + const Feature m_userLoginFeature; + const Feature m_userLogoffFeature; + const FeatureList m_features; + +}; diff --git a/plugins/usersessioncontrol/login-user.png b/plugins/usersessioncontrol/login-user.png new file mode 100644 index 0000000000000000000000000000000000000000..0bfa1da0718415c190b589906c724a05cae1d26b GIT binary patch literal 4617 zcmZu#c{tRM^MCJJtozD+tTTxmkt@ey3!g+n&RB`W(w3XWI&!6u+~i2CL@UYNE-Obw z$Q`=~iC7A65kKGmfAc&uuV?0&XRc@FpGm%EY0AMW!U_NYhq)Q*`U&Fy$8e?-NilHm zKLJ>PvAHe$gmCcNNhdlB*32;g04{R=k0Ar^OXE(A!hx3@0&VVj1%|jk@B~6aLX_|N z`v!QpV?CAcJ-Ab_p(g?WJPGC~BiqpFUqunmPVHp%koG2q@}A4{hIl6Ab)Y0WGT|vd zrt}@V>Um)}lz4hhZaRUR=L_LShTny=Ns6e&i?tv+L(2G+bZ!!1eHazDuzk9ESC8>_ zuDxM}ThLzG>PFSi)x6c!+S=N?KaWg?G)C) zTG*2%?ND0oe!T^kyb`?I8nflT>;t_D#YzCBTMA|<(B)QCn%uO5)>KOmH!`6HSJ^lq zrs>9pj)Jv3Usui5I$xgSE58!y2S=XssNjj-wem=k15SI*|Az>@jb4}L^(D0o{E_Fb zlfv5$^xf+EcvQvP#R^pxDwi2ExTUun)at4KN&y3xG^g9~#r{UNiIIE7%-^TsfACE7 zWZTcn_HiRsZZylNK1nKuE|!TE1xGU_3p6V?9*uG12VT9$vaEL;2)DuUEX{tr_??dQ zAji=_-LPr4k_U;=0=W~=KjMWqF3ZDl1->c2c1EuQcCC|~_ptnYD&4D?c>9YFcd{2V z`vRun=&s#C>!+v-Kopz;(9*jIwZ&dD#lN8h&<3p>Qw*W0HubNGW7V-&W;JGfbrkU_ zen+ivhr3@k_BEyVjn!4v+GUx*1g~5^6Tu$o+tq z$Zh7^&IGbUod9)mR zd3Nl5{%f|9TcSBnsnPK0g=cm5&N+=+MXa z40zga17A#xdjr;LKSx}>2>D8^x(;%kU$LMbp&Kbd%ovaB$-?R+V)hhv0jJ{U@?uyW@5<-5h^hyC5e;d#nWFsprLNO<3?1{b0Y=CV3QO@SH5fAMgbHpa6^hFMH$kSU`pD2 zU6e=Cm4Ol_X)|2t)OoL)Gche|zX$DZ0u;BsRC#mUEzth>w6@;(g;w;N7N`TYIiDON zv>^`bc5u{^zt{W8}BsIQNe4qd~o~X z${roHj6%lN5(+%2uTp@su3H8js74=Yym_+(#0kQk3pa}3friQDJtgP=ZQk{K1xJI` zb1EgnUYv-oC4hNvlhS=0#Ua_5>KH(eQ(NKmfmqCbV>+GEY?x~wk8gK~JS5MxH1B(i z3X+vIx!&xBl^)HtvixdCXemZTp+B~J`>jqtZ7eT8r6?ixEr_ss?{aW#Y3&)LB+Tz2 zeDcv(1KX0Q(B};QFxc5Qz$e!YF$GES;ZH`<`xAUA#fS&BNMB!DK(1+GpH?D3wsXG7 zPj5|Nkn~nWeW}>~r=j%0-jI6a=>#Q&epD~Y({TPbbAL-Kr8rH@ApUcKCKRY`_CJ03 ziG)6Z5&L=f8W(8IZsZGz4c=Lu0F;TaO@Qd6$n00}C3n;uy;I!GX29d{ zM3R?3R)E>~J+y(d@pV+xQT`ukvM!a4-L)huZmz9Fxsp-Ux3bC}ys6TP;(n+56%H91 zqvnPLg!9nkhAsj(E3)D=(++e`-1Z`!wGPd|97@^;!MEDn@0Kes3Wio5b+;9lD*&54 z_~3{=|G&e>OYZ>x$s(%Cp?5soahvCDxiXZ=z^6&=*lmQ59KFBG@Op}u`0E+lZl+uC zMd@tWOT6->f!F_BK$i59>h{VEP2Dno98G^^jA>rGVEsKrOs7Ig$AdRx=!Wsyk=W9G z?Oi84i{ukSYVQghs;zeTqd8$9>?G0k$H2kQ=`p!4E<$XFmuHj`f}(+@pHX54@OCjn25(BMaYp8RGB zT{*V0MhO*ie=sgT4_cR+pYBJZxV;@{W;TFIsD?Pyr+cXh2o?gIUhqljDl3_hBa`eRNao0Zr=LCaej<~n1ZTTq7$n&wBB(ub1=?S{ z=RM8h5)f580ZGYBR4LZ%uBaT2&yr+Omv!G*!ybr4t(pahfUj3P(t{tCkr?@p6aQW5W56e)HD658Z+|Fk|BqTZHN?N~>1_|@0O~W6SY_I5T0!>QJ z&`_VxKJOYEAV5k=N}c!A8@ot1--5>)aA$R};IKjs3Tjai+B7syU2l?W*ChvR%;c@sVOVJW(PAH;_}zoDTQo16 zE&SIWU>oUn09Qo?fnti`d6GPRPmxa3p*Ba;C;PLTdPfA}e1VVru(=2}I1_pKn7;U` zNXhxd5B?|%)v{LUzNR+!Qjdsy6Xc2xjc#oM_;5h(5IBoHvWC20+TtN_&B#Xo(tp+O zV1Rr<|InZCcdwYnEqeVR1OKqz13L;GIb1A}WM*z}q5NVh)ucMJRqgxTAy=Yi0dm6S z*&-A~6L>%1@*MR+fF4GXHYLq~vhw)KZzRT&K0X6KaVmLkj=MV4`p>CLftW|ntoaoK zw(n+PE~UdyIgXjAREMKJbz695Cj4l&eB-e7=y}nD;Y82sN3z8~Kf3LkQ%u*zGUK_t z^ya|o*cm$>GF@KZUTsUDrk1z*Kxkc@BV)-n>=q?OZdRF#W$N8oPJ^=FKf9{K^=bXt zfeLS5F{>&`OWE6Xh4Bf_l*w;B z=uK}8S|xx$PKvQ}2A#88UH(#ghBimRS2$Cq^m^O_q8YGaxZdg3Q+_V9H69kHR=kCA zQk&vtydRW!tu0hpXgwlE3pB<2>wjYq)qLeJ1aLHt^)4@xpIBjK0(!olH|yjBh8KhOAseh(ZlU zLVY{*U$LUW%;#*f%U1OnIx}t5&JF*c2`W|~zRBy0Hux5GbxLV^R`@HU>6z)iXGMC_ z%1$Te3%##GfARY;6iitpa3X*~Ul$kp!&guM|CZXa2kHIvt67uUNNi5q>)qx5hkxr& z{gL|yk0AXKd8l%GD(Wp=W-5vc+`1(3cwT)TIl2Jd(MnE%qjm6!8FKaER-GIA25=gi z=VE2By+JPa&geqy0EW;0sHrja;j^*F(lsA4iD6bSMkW2C*}Lt3 zbKV0$3FdJGmqsm%tU!l?!^MIux<~s?r63N75Y5p(Pr9iGJky`E=J;}1U#ZsN-#Ox6 z%OFacs<0|fswML+ik?wIr)*zHai#GogdeKI5GdSL%19?<^LzHRj7Oo;C)RJwMnW&y zcwkQMzZ-g4FKuHGpTeR#XBv3PIaDE$IIg8m&X=ri&B$jbttN_GWE~t^&rrK*O^#&9 zomlnE8x!%4Yp7oejOV(QKZ-Rq`p?S$>RR1>PsZnIB`W| zIzRsh16t1x-BZMXT&&r`))0*m0}6L?S8$L>eeDrm@5BpV^5sBdrW=<4;Pl$74FujA!ca@~vc23;w&C8L>k`g&JlQ~ zHE!VrG=4pVg=QMgRw@X_-^t%lav>x{+wvi^GJnPB?ezeIqs4GNB22#-f|!(7V&oIa z&akpS+2n20nZXR%I%Z_)5sLOu?)xet$|X!o0etJ@kucvXwFOp0q;OU*30|KWzEcx8 zBt=)u1J?sTi*0xEZhLV?;T9^DaZIY!s~g64#Z!emW}az*-}=e-N=(!fBZ=|2^Tcc9 zE36-Iiwb@;23l)DwqVWm8H8}_fBiRtU%M~M{XJzQKj2=>E-@0X0xVl}Mz`Dz+bXjF zr32ja^9G%!>YfPw4pe~laRv0|h#i{Q-^-?v=1!f-FY>9J_s@KhMI^ee4jtI+bZF8NP?1^>^%-$C*K za3-QDQZ_M62HB!0%EoDgHFE}2<9G>wCsR5ZoT90(A1<7gpJC{1XQh2S8HxiRhziZ3 zY!nm}Qrf1#T1UjAeu*ChuVs1QF!MPIDVC5-$%~IpMLrTeeu1^QL8_D}yt#x?U?JlS zlLU=Se|dJf!?V98sPGUa1eE;RDhWDkRBJ2yn|Y&=u0kWDFqhOqjcH1{fJA^mbXkG= zNKsm$(qP%{6xxZCcA#T?B$F#YkoD20P4(jE>nBj`e~| zy!ADA+TDt!@N=_f$LpA41#|Y0*c&Q_T=XbE!=meTOcdQAa$5A@uK; sPrjT$4RYh%699Jg|L51CBzzE^xVf~y$(`(a^0ooYFIl2$jnT3H2VaS?+W-In literal 0 HcmV?d00001 diff --git a/plugins/usersessioncontrol/logout-user.png b/plugins/usersessioncontrol/logout-user.png new file mode 100644 index 0000000000000000000000000000000000000000..982821f572218f6223714bcc55f2228341608ad3 GIT binary patch literal 10642 zcmbt)i9b~T_y6mT8T-B@TO(^i6e7w@B3pJMDqBTl$zGPZvbR{W6lF`uQe@9IgCeDf z%G*9l$dZvT7&G7N^C$d%^LWhMJM);=y|33f&+|Obb6z*e(%g`XU6dUFz-4TtZw&y1 zoI-$=897`GcySFmpab=cZCR051nZSVwSL^O zzbVEh8Edd1%lq)3_1vamI;LPCjO#RUDu>I!o9V}KFbX`G3q%s1Kp8xGx{m{(^I#@#>nIlPQzN`W zlW}Wv0^qu&E>v;`4ndzY1Yha30_A@t%(p_UJ6cMw{4)R#uSCt+r zxlM9P>jKN@x?{izPs5ySSDB9|KKl5DA2RAAwXbf>Qnp&4CLvM^2!e`|&Xg}`!OAY9 zXmTXF6>GV<$=z!u1R04k(Tl{Kr1W>8VnB8R7c<>5HD zMfCUS#lMTBuPkIe&^gkss0%04LFcowKN2NS8E(A$e$1MQC6bQGNdb?~ zznRqeVp%|4CSQ_5IR5cH%Xv(*Pi;hvB+8#OLdR-O++^N94Qxd+n`BrE1VulQUSeMz zyd!r;3RTTRtr#mVG@kSXMAU%QS((BS?txkg#sisv#Yg`{P|3vD^;L$+U7+N{xb8?y zN|&Xoz|yU(=E!P$W_@&O>!{O|23Ggi6gbH&dFJffK`PXD*67LE2~C1Z*Flt#I2Akj zmGksTc8Ucw^FHyv@`s8YkJ(cGW<>NFp?RGt!y&!;A)_iJssemNgwLrIMJAFn7{oG0 zmWc>AAAX6f#)=(RNA)qd3hM7C7S-^?%KLxpsd`_^ECqg2h=iXH1}H?yp!6^PLAuf1 zxWYgAttw{@q7uFGsxVXn>&esI@^LvQI+YA6wlte}@3|inmxALfX4=F!vZ=D*Aw!&eXK1CNfW|JM1>&z}HYSJW@2*`cEx+Ak}i05P-+&72n(Q90LO%R6Md zKwlN-gT*=sE#9xPPWSVzg}HWoyP$Vq>dL}KhZP^}L?$q{og8dtL6h}@GpgXJLpFW_ zHz|oSx=InbyRj*DK%PP%U20h|`X?aODbN(WmhL+mL()Kz&2S&4@e~bUV7>UTn*&pk zMEg1M_hz9YnzV9>apqd_{+go+GR-%nDgF!e-~0IBq3Nlx-Tlm;(Ee#(>)+xJC(U1O zj$Cu&gKeg#2G)Dd(S);COx=_ZUZa(k_LL6k19NS!#qjc+xtUsYo|J~tQ9r1){K{a4 z?fA>b);#pj7b>ayQ0kT@_WXv{->tYF6tq9FNCZpl44n4dO;RH>>4Ej@hv*rb!3?@> z^;R`2Da3;==2XKU!9l9GE`5le6=U~`EZdw5Hvl2gjFn4HfksXVl!*Y@hx&6m)?aml zc6xO5L00}2qgXt0n+^{h z1JD~!n9vZeE%ycf^pi0_yROa=l&W^fP=WvE5k=_z)i!>43zqYP@yZ73pYk*h)xQLKD?!<3 zW2cNU#^NiWlW5bCg-Zm_@-{=?Q~Rh9QYE%cj7X=5>5@Z3GhpW9#B_$BY#ghw8=K>O24m2I=f%!*ag zW?KH8U#E_L^C&JVZcF=hB6q3AG z*i=vfmxl|pyYp5^?;8?;?iW6i&#MI7L-fDy{-@~6f$3=TL!z&qL(URr*P+)TmLUYHe1|XEMCyPmBZlC% z1Zf$?IFX`OGpP68oJX#e$=_ApvaI^H?D|xIsw{@racqkL(FAlJgavo>@L?HIH#eyr z7Ku7fd*c~5w#OEy} z!U5X4ELnnbdN4WDRL0+tZEI|R%-_n+d=dmj>N1@7j zyOrIW4k(FFMr5uRI@UI*zWvW9S3h|fa^RhYTu(@EIYr8vfZ1tHI@ihY4vY0iMw}ev zw;%2Ves}OHW%?~q`Zxo>V8)VIeeO4--!&QW=fb%4TI>fXY*`UD(?%WQrHV!4l;BV< z_5%AD_d~h(F0MM1ml;$0)?^xDg7)o3$=idUr7vD8kcQu3R-(AHE(c9-o;53(j9;3;@~dNe2bY6!8`FSvt68#OOW(L@ni z(Uy$ed*~zkbC*BBexveUU8%8blGWP_m)XDQgkUEZ?hy1NXrzaeD1tOa4K6Vts7b=S z=9TkBJIKvejwwW+P>_WTiVmpHKY5VMkM}f7c+^3ImoWos0WAInO_Cu^?(mQ?C4tD| zVhI#?^w*JL-BNAtOTPpz3m9eV@V1fP{2VJw<^xqLWnOkuD@+69TM~E`kIt%{?b&G- zSniD5B6xO-(H`QScI`ZB%tjBDka6GC(W;~6v>~e`RPMzIHgRinpSIYu2e0@+m2Vb8%(~8&-uDXbm8j;(5%w1FN;w|$$vHoj+SqYX)v#? z2YMB+wlks^bEV<;k7^Gb`r{!&bA?(T*s^&;wi%OrYP9;Fv)-EBFZrpK5_Rj>#@J!| zU|$hE7wnCIvDJBWwUr^*RgTGLeUIHff0xarK!Mu-itX_-diI1BD#wuPlHP73wyqf5 zDYiF5uilV<;8`hwKSpMOX@$M>KlE^@|m1zIX^y`i+&R_S2p`c-cM2V_L{9Ib&$>TXKyu^d=X+&UPa-75&)RilgzA zi#?P9dCn<8HwO4f;3MK{7atQg@=SSWQ-`yBbVE^}aY00$y9|k>IjQqed&C^HHu&7L z#GfG7J*!k0;KQ%(4YGi~J39HU?jtR9+>B#fWE=OhMycp@LI+Cr**2|LzeMw19BE+H z|7dLok`jp1#57zGm$j@bB-kxMAJyAvkdRF;d~ym*W$jl%ysP*=np+G`4$9!X3q=h^W4YrQ*6JnLoO{CE9^to8VGS3i*un-C zYhN&;O-wPCwY0lO4h+os^5Kt7%cF9H-pPT+uBlY8z~FLj;iCkrN=L_KnUcCpJPwa; z)EA>zj}YBd@gH*);l^OJ>f2*fDETyqLlL=Yh@E^SjzHa#^`1RmrbxDrdMC|!`}-SI zn5iK!$m-w$pYGm|Z2Mu<>k^G)r!Ia+k0!%aUHa+#kFOgq^ShRNRKE!?$lix9nE4>j zN^5e*3^kvdt~adeJ)4%7pz>+ZJY-eJ$8$(Wyk+ojhfbae z(RE!GESR}F2c2BH2m&VWK~1gbZ{Oz&+cA(c%K)f1XM@g z*ud|_e8n9cuZYm572wyuGK)UmL(`d~J{t8~tZPG6*$9cnQEphwMzVzEIo1U}{e}t? z!{=uR;cup0rSzBr6yaY__gKhdV>Ts|67=_SgDI|RTTuA$IHQLYTy_{9HNY#9QxX6= zO1dY0F&MRpq}?B-Oz6=ih3!Ag-a7DAp9ZF;!8hI@P348Q!%6jx9}i5p#i!@kZW(lk zT+nemR+kp@5k+|0Lv1y8Y1%S(aeAf$O_p#^e4BKHlzeJgVzEtTpclsC`%t`8_XOgG zYrPele+Iw9Q%|;xiZh{?eS=y2dGUdB>|`?uh_jZHUJbHD1xc#WIBv=?-LF3PjRtge z&h15#lK=v*iIG7sNSq~gaa;rbrtJ;7lw+`*GeSaQ+mXr2*dr9f-R-n#!%On2oRi$I zoo`hvIdTT?RO)A}91WiTeDYo?c$CkaIvvlACpNv8p$UJ#Hff2(b&0)7Nx)RgFhl!em{7zc zG*EC^Lv(V(-3@mkOF6d2{m9@~%W*WhJ{pLh!VCzI3X7$cV)7w%H3bkorAuM?xDS8` z6V~gKO-AIp;ROYJK~sH^FvJuYgtk0+Y`fu2N6(4o4!v1W^%Gg z)KhipVjfPEI+hZ>S^hc zzlJjkyjOI*VF{$s^svsUkVCB!e~#ap#R=1!clL#yGbO{JVYf{0VFqsVkjGU}m|(Pj zH3Z3UL;=rhmcZ~)N}?i-i;ZMP**QnTCIW&_pAp>gt9`mfnJ#5{b8&fzT!mr@ulmYI z^88xn04S^Md&CsO6YzMZo1Dsw>!k+XJttGfstJwdz=o#hhR>OuexJN%4q; z5OdQ5DB@4DrVP~mb26=k;I}O3(tFh=Y3sz?D`=k8<@cr69aU>wM9=9D_ueo`BD5ny zZ<>5n0a4dqEyNfji=(Sm6*P#swJ)nUiQ7{b|2P%sX92ZSOJemq21q2lWY5kN`YeP` zN(S9XMsxscTi0 zCLf8$8IXt5;eCIDOvGvz#Y{1zk88Ct^q`(earOVgvpZPQ9!F$%z>XQn(%{xi!RF_BpdLExuy4DKZ6 zu}3x2etC*vERnZ8S&ppY^e~9(ZEA7K!!?yB%y7A$zCrdtQ`ktCauq->x6AJ)8PXJ0 zV81ZQLAcXGn^JUu_VZjPHgXJ0=U9y<9$%NK?$z;HQEuRc304(v(4vdy{>REHVt#wB z>eG;eAKpX}iYW3cefg}^#S$hj*79d<(_;0Zdl}P#7ULYVnxTfV6psUy(fO>N+EBbnOrlq7KI= zvncrtCpjUyx!m-WmkKor@TjT=(q3u$90Yk&3pE9I0vdzBHYL6HhwhF?kp!IE<;W{< zZt(VBI3e=&Zv4vjI^b9k2PT zoKzX=%KbBAemF!vzH>kEv(E>U#R}a31E(@EsW+$-av<6n8^v63sP$<0(w_q%0;GIE z>%4_>sFa4;_SlZJSSAg=`_}~N-I>M;vQQsgrW}7SM`3~!GH_QPjBP18u}L-+eB)&{ z5toPC(=^AmT7!PPVR_wAvHhMTDb{k`aOl~v^*JCQwjY~Lv5BO*PvuBe2bV1)#Y>%f zvs&cWS9T5jpx-+aYfE)GnXh&NHSil`nQL)`DLwj^1LKrHx&#OpwPT)GRDzTcVrtt{2WBk&=0~CNCxB{Jw|n)Y z8;lF^&OAhg@RZ47Iqhu+KGh>p`{9GxOmf%8L2WQ9_pdM&zTS5KUfH3S|nmU zL{<2E|85@%LF3~1xzC29)cY-)ef7m4%q1UwozP35}?J9 z7qv#wG*iCu<~}QmBD?p6b{A98dPb@;27u@-Wr^T?*kuZXD82q;)DHa6vkOMWPlezS zHKGXh(FyJ-DZDHFkauSu_T5_8TW7F#B#K|382nijP6k;MNO3dGO`1fhI^yL`SB_$c zn;&$}HSR_qn?sg-*>_yS>+ZR7!T>l-Gdk(|r;ivcLc1!P9E%0VB1V0%x9*i)A*C(@`K2RUlEj+5B%#}Z7Fp)6?ZQ{bl{mv%%n-&-i3_!Dt0EkZy}P*|LMXb zIcmf+2qNzdHa!acH@Tk73smmN^R|Xz;e8vzq>B>JNy)v2fCErNIFp|>2cjBg!-QKV zB#XB!HA5S8$;o9xCmI0y8)av7n8{&w$wJEFYLz%} zmKUHrQcfULtWC233{7w2ZJUjN6@yIxPkxYf>cW@rr8!`U3nvmDnNI1EgIR!bh6~k{ zJk@*>qNLOR3y47UaqEGXhTTmon%X9yuYlCL&k3rlZ3MFj1DuY=b&9=Lj(Ar94j{RF zt`j#nlZ|^r89qgLI*kk5Q2Cfsz2f!k?Zx5j0n<>XKovSy3?r2tm53%z;!6(GR{2RI zCkTYVK)N7O0Yx032X|8=3FmBgaVZ6WZTC}KprVnO76N&4lD*<=N z^68YW04gLoXO5`3GCLcRWA)_IDTEp@w5zY{c5m*VLU-~2T|2Q1U0(*$ghDF9$73`_ zAhE0f4;S&mtI_xV2~FAVb(9`)v6BzNG3#W_TeB)ou8LIegnlI&dzklZ=v3(Q>jUC@ zFhq)6k%C7{Coo~k9~9u|7}ih{PxZ6{{#SXKi!yK?YnPjL7I7c*BTyky%)m@vICtyQ z`E854Ang2;f3V{l0n$5jVVFetW6>=IR)kJr=!q%b2{kI#DhPR%Tk8Q}xptc_pdU8g zi&CWvjdgQK3XxL`0Nu~NGc?$BX=3JkCaXFt(p~NU4Cjj+ASd;OZ(5k1-IX^3wC#7w zD8W%S$*#+&$1kPf1jMY5PR2yQKzvN9vdpHD}24fU77(tS7K;9aQ=wN zUUIVhn@XBurGeByj{r39A=5~N=By||-{0;Uw{)Gi#*o$=f3e)5OMsf?AxOVHZ`Y`| z>u48&e%gFnxmkb{2faUXjyCE`?Q`}Lk7{e%NzJ%2>B$PRkS@>|7kt@;9|CDA2K@{(iPpFwONU}#n zg7kHs`Ewj4CVHtuSaGXO*jxh`OWMmXP2BPVhYnI`=Js2dVuDJ zns)xRk2<|h`6(j6L)RTy_e%m{Uy4Pd%gYHUPakK9A_ARU5T~w({5>5pjbzI>G>P~j zkCM(!OXP~ew92c!xNMfbnHv&^%==mhT}vv@y$xJLv`I=gCTgNKhH2U&7FhGq1E+A# z4w~Z4is6IPnDbwWSCJ+w=5l{H59wvdK79Gy+q-2)yRd#x6Ms}^No6=si0RjOY=aO0 zRFAn(%yn!>XEWmm)x`xWs}3%9reXSIx#5X?8vNl}GRl?^|je z2SRizJFqS4rAHKh|!=vl@YqjwrIN5gHSM@>rwSr99 zswqNz1;*d)T@f;sKOj#n6h#fupgGyM@J3DM7o)WY+j>%391;Af>G%mo3p+}|QN2rG zN%N}K>%VFH$vCIuBXRxR+>G;^9fb3skZ)uYtK6_nO~1r`RJGzdo6X-w?w?UdR={Tg zn2#!gH@}baSEI388PD#rB#cf|$IgOMkC{v5K|VoBV_xuqyH@{ z1PzIB?H|I6UCf{2;2=aOLK%Q;^~J*w-q2?`p_~Y3t7$ zeY6ciauKX~nty&C6FQcpImQN;YJOVZp$39S@;0barpu;ud@?5Na;arND(2dcbFp262+{JEwt#~P4Ee=I_HEioY%GETRgmU zwd|@3B9v$4m*ri&sa(zuG888?&NeRU$t^}<>C*db9}J1}Z@*U!npAzI-PaCpaFrxa z;h^1&o?@!(dm0p$Xg9SUS_uH`G3&9TK;eVAd$UNt;r7xu1x9P8O^~N zclo6ck6$A40I|bmj_8?ZH8s%BL1XUuu1oR0+g(&!Dt^f=v_bqJ057t$cN765b7}aLgyl``c_Au> z?m3;%bB6f7nL!ycCx1AKXwQZ0H~j*XY9am??6)yd(ayqAQhC{Hq%X$it{<#WWCIG- z;+GOr(ZCUY)u18XF3K5l=wNn!_3^-!FFMyn??}P=#xB>Q8uE7+p&+wn^qtGL-<$>f zkL_hmbkrG-D02EzK`C5vK>L?Wp13>!E!LaO3eZ`%_<}9sjan3wQxqOYH z7K!=0*_R#hw|dAS0O1_+%)JZB5xQ5IwBAhdy~xDB)qtX>G$@J%%irkbe?DPPwe=^7 zpEwVI|LwEg#RsgXmKckzjI6^Zro4TN$8OmS zasn52lQ-yXf*Whs&8R?^Ws^3=ivR`EuKWiS$#y-afRQ zmHvf5@taz0RY__CC*Dx8glQGV9g{>6PEOmyKF<$#)gI7lko`8|9|vGHVLpsSKgu(# zrj+U1=_=9+FZC4rOLnh_(DVcg@jxcwSA4+#TG5wBD%)Im3&|v>WcvhBdZHz{QHXXr zPF30aLsIEdb95=Ir1@(9`=)G-941Sr3Ry{t6Az9rv*N(H_hA$&bK`5W3GyrzF8z^B z`Ya)b#3L7|Z7J}Voz`#%`_54a_jsrFZjtW{sUG#h?9;!#*8s;;U>u+gsg%c*D^QCd zeR2vK7ac9&l9!`X@Am4+;?z1495($P4gYRaF97L>ve7xIhY=DWKBIUMaTLqRlw88m z6jsSRm-Zm5gOCbnQlHK>e1C5t)sSp)gvOG%B|nm4tCZ>`45?q7ugy^}E8D=C#qJ24 z($zR9^zEP>*+P}3|NL}EMWNUBt)ct_xFdE)Py-Jbarc_N*cmGD7(Y&e^7u|ZKPOHp zA>SNs1v0PR&l?ZPjS7eK zH6NE(wQ6tfw-sn>vYrH%r0-m}^l~iWg5z0$D4HZ@!It{jBQE0^Me!|U*7c;)_3Y43 zdHW%CPir43;57GjEnC?RZ5ZRp4|}`iaeJ;0qc1Y!+;!ls-`Dd*MAB0-P3@Av(_tai z7u#F^#(KMo3SG5Wg?k3szIv9w{*LparnyjES)(u!aP-x4kC6v_$~I@zBKYtoW_;yO zJc6V>rJ?(%fk7jAWM_30D(&jmDmg?e+4uaNnfX&Cvv(7r$YW?6S9m?A+wx9FJ}%>d z^MI{1zyDzx+7m>YvlE#N!gcIlUbIN(OD*SxnvM$%)vgWcY3%N|1zZ#O`Tqu1%h<{& z-g$|XMd$`Z<0LzwbS(v!{Yp=SFUzJZKT7$d4<4R97>T*1@$LG=@@}3GF24XQ?yBnQwJ+eu4evq z=(LXB-2nCH*Aq_aw`e_Ie(~zX_@tMp`N#QtK!!hw6)%;R`2TnER+E8sRsD1_UgzRZ R + + login-user.png + logout-user.png + + diff --git a/plugins/vncserver/CMakeLists.txt b/plugins/vncserver/CMakeLists.txt new file mode 100644 index 0000000..e36d91b --- /dev/null +++ b/plugins/vncserver/CMakeLists.txt @@ -0,0 +1,10 @@ +IF(VEYON_BUILD_WIN32) +ADD_SUBDIRECTORY(ultravnc-builtin) +ENDIF() + +IF(VEYON_BUILD_LINUX) +ADD_SUBDIRECTORY(x11vnc-builtin) +ENDIF() + +ADD_SUBDIRECTORY(external) +add_subdirectory(headless) diff --git a/plugins/vncserver/external/CMakeLists.txt b/plugins/vncserver/external/CMakeLists.txt new file mode 100644 index 0000000..8a2b73d --- /dev/null +++ b/plugins/vncserver/external/CMakeLists.txt @@ -0,0 +1,11 @@ +INCLUDE(BuildVeyonPlugin) + +build_veyon_plugin(external-vnc-server + ExternalVncServer.cpp + ExternalVncServerConfigurationWidget.cpp + ExternalVncServerConfigurationWidget.ui + ExternalVncServer.h + ExternalVncServerConfiguration.h + ExternalVncServerConfigurationWidget.h +) + diff --git a/plugins/vncserver/external/ExternalVncServer.cpp b/plugins/vncserver/external/ExternalVncServer.cpp new file mode 100644 index 0000000..ca057f9 --- /dev/null +++ b/plugins/vncserver/external/ExternalVncServer.cpp @@ -0,0 +1,96 @@ +/* + * ExternalVncServer.cpp - implementation of ExternalVncServer class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "AuthenticationCredentials.h" +#include "ExternalVncServer.h" +#include "ExternalVncServerConfigurationWidget.h" +#include "VeyonConfiguration.h" + + +ExternalVncServer::ExternalVncServer( QObject* parent ) : + QObject( parent ), + m_configuration( &VeyonCore::config() ) +{ +} + + + +void ExternalVncServer::upgrade( const QVersionNumber& oldVersion ) +{ + if( oldVersion < QVersionNumber( 1, 1 ) ) + { + // external VNC server password not encrypted yet? + const auto rawPassword = m_configuration.passwordProperty().variantValue().toString(); + if( rawPassword.size() < MaximumPlaintextPasswordLength ) + { + // setting it again will encrypt it + m_configuration.setPassword( Configuration::Password::fromPlainText( rawPassword.toUtf8() ) ); + } + } +} + + + +QWidget* ExternalVncServer::configurationWidget() +{ + return new ExternalVncServerConfigurationWidget( m_configuration ); +} + + + +void ExternalVncServer::prepareServer() +{ +} + + + +bool ExternalVncServer::runServer( int serverPort, const Password& password ) +{ + Q_UNUSED(serverPort); + Q_UNUSED(password); + + QEventLoop().exec(); + + return true; +} + + + +int ExternalVncServer::configuredServerPort() +{ + return m_configuration.serverPort(); +} + + + +ExternalVncServer::Password ExternalVncServer::configuredPassword() +{ + return m_configuration.password().plainText(); +} + + + +IMPLEMENT_CONFIG_PROXY(ExternalVncServerConfiguration) diff --git a/plugins/vncserver/external/ExternalVncServer.h b/plugins/vncserver/external/ExternalVncServer.h new file mode 100644 index 0000000..8d8d471 --- /dev/null +++ b/plugins/vncserver/external/ExternalVncServer.h @@ -0,0 +1,98 @@ +/* + * ExternalVncServer.h - declaration of ExternalVncServer class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PluginInterface.h" +#include "VncServerPluginInterface.h" +#include "ExternalVncServerConfiguration.h" + +class ExternalVncServer : public QObject, VncServerPluginInterface, PluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.ExternalVncServer") + Q_INTERFACES(PluginInterface VncServerPluginInterface) +public: + explicit ExternalVncServer( QObject* parent = nullptr ); + + Plugin::Uid uid() const override + { + return QStringLiteral("67dfc1c1-8f37-4539-a298-16e74e34fd8b"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral( "ExternalVncServer" ); + } + + QString description() const override + { + return tr( "External VNC server" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + Plugin::Flags flags() const override + { + return Plugin::NoFlags; + } + + QStringList supportedSessionTypes() const override + { + return {}; + } + + void upgrade( const QVersionNumber& oldVersion ) override; + + QWidget* configurationWidget() override; + + void prepareServer() override; + + bool runServer( int serverPort, const Password& password ) override; + + int configuredServerPort() override; + + Password configuredPassword() override; + +private: + enum { + MaximumPlaintextPasswordLength = 64 + }; + + ExternalVncServerConfiguration m_configuration; + +}; diff --git a/plugins/vncserver/external/ExternalVncServerConfiguration.h b/plugins/vncserver/external/ExternalVncServerConfiguration.h new file mode 100644 index 0000000..21228d6 --- /dev/null +++ b/plugins/vncserver/external/ExternalVncServerConfiguration.h @@ -0,0 +1,33 @@ +/* + * ExternalVncServerConfiguration.h - configuration values for external VNC server + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "Configuration/Proxy.h" + +#define FOREACH_EXTERNAL_VNC_SERVER_CONFIG_PROPERTY(OP) \ + OP( ExternalVncServerConfiguration, m_configuration, int, serverPort, setServerPort, "ServerPort", "ExternalVncServer", 5900, Configuration::Property::Flag::Standard ) \ + OP( ExternalVncServerConfiguration, m_configuration, Configuration::Password, password, setPassword, "Password", "ExternalVncServer", QString(), Configuration::Property::Flag::Standard ) + +DECLARE_CONFIG_PROXY(ExternalVncServerConfiguration, FOREACH_EXTERNAL_VNC_SERVER_CONFIG_PROPERTY) diff --git a/plugins/vncserver/external/ExternalVncServerConfigurationWidget.cpp b/plugins/vncserver/external/ExternalVncServerConfigurationWidget.cpp new file mode 100644 index 0000000..1b2672c --- /dev/null +++ b/plugins/vncserver/external/ExternalVncServerConfigurationWidget.cpp @@ -0,0 +1,48 @@ +/* + * ExternalVncServerConfigurationWidget.h - implementation of the ExternalVncServerConfigurationWidget class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "Configuration/UiMapping.h" +#include "ExternalVncServerConfiguration.h" +#include "ExternalVncServerConfigurationWidget.h" + +#include "ui_ExternalVncServerConfigurationWidget.h" + +ExternalVncServerConfigurationWidget::ExternalVncServerConfigurationWidget( ExternalVncServerConfiguration& configuration, + QWidget* parent ) : + QWidget( parent), + ui( new Ui::ExternalVncServerConfigurationWidget ), + m_configuration( configuration ) +{ + ui->setupUi( this ); + + FOREACH_EXTERNAL_VNC_SERVER_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); + FOREACH_EXTERNAL_VNC_SERVER_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY); +} + + + +ExternalVncServerConfigurationWidget::~ExternalVncServerConfigurationWidget() +{ + delete ui; +} diff --git a/plugins/vncserver/external/ExternalVncServerConfigurationWidget.h b/plugins/vncserver/external/ExternalVncServerConfigurationWidget.h new file mode 100644 index 0000000..d218ec5 --- /dev/null +++ b/plugins/vncserver/external/ExternalVncServerConfigurationWidget.h @@ -0,0 +1,47 @@ +/* + * ExternalVncServerConfigurationWidget.h - header for the ExternalVncServerConfigurationWidget class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +namespace Ui { +class ExternalVncServerConfigurationWidget; +} + +class ExternalVncServerConfiguration; + +class ExternalVncServerConfigurationWidget : public QWidget +{ + Q_OBJECT + +public: + explicit ExternalVncServerConfigurationWidget( ExternalVncServerConfiguration& configuration, QWidget* parent = nullptr ); + ~ExternalVncServerConfigurationWidget() override; + +private: + Ui::ExternalVncServerConfigurationWidget *ui; + ExternalVncServerConfiguration& m_configuration; + +}; diff --git a/plugins/vncserver/external/ExternalVncServerConfigurationWidget.ui b/plugins/vncserver/external/ExternalVncServerConfigurationWidget.ui new file mode 100644 index 0000000..fa18789 --- /dev/null +++ b/plugins/vncserver/external/ExternalVncServerConfigurationWidget.ui @@ -0,0 +1,59 @@ + + + ExternalVncServerConfigurationWidget + + + External VNC server configuration + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Port: + + + + + + + 1024 + + + 65535 + + + 5900 + + + + + + + Password: + + + + + + + QLineEdit::Password + + + + + + + + diff --git a/plugins/vncserver/headless/CMakeLists.txt b/plugins/vncserver/headless/CMakeLists.txt new file mode 100644 index 0000000..b049d88 --- /dev/null +++ b/plugins/vncserver/headless/CMakeLists.txt @@ -0,0 +1,16 @@ +include(BuildVeyonPlugin) + +find_package(LibVNCServer 0.9.8) + +if(LibVNCServer_FOUND) + +build_veyon_plugin(headless-vnc-server + HeadlessVncServer.cpp + HeadlessVncServer.h + HeadlessVncConfiguration.h +) + +target_link_libraries(headless-vnc-server LibVNC::LibVNCServer) + +endif() + diff --git a/plugins/vncserver/headless/HeadlessVncConfiguration.h b/plugins/vncserver/headless/HeadlessVncConfiguration.h new file mode 100644 index 0000000..e8e175c --- /dev/null +++ b/plugins/vncserver/headless/HeadlessVncConfiguration.h @@ -0,0 +1,34 @@ +/* + * HeadlessVncConfiguration.h - headless VNC server specific configuration values + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "Configuration/Proxy.h" + +#define FOREACH_HEADLESS_VNC_CONFIG_PROPERTY(OP) \ + OP( HeadlessVncConfiguration, m_configuration, QColor, backgroundColor, setBackgroundColor, "BackgroundColor", "HeadlessVncServer", QColor(QStringLiteral("#198cb3")), Configuration::Property::Flag::Advanced ) + +DECLARE_CONFIG_PROXY(HeadlessVncConfiguration, FOREACH_HEADLESS_VNC_CONFIG_PROPERTY) diff --git a/plugins/vncserver/headless/HeadlessVncServer.cpp b/plugins/vncserver/headless/HeadlessVncServer.cpp new file mode 100644 index 0000000..061b964 --- /dev/null +++ b/plugins/vncserver/headless/HeadlessVncServer.cpp @@ -0,0 +1,147 @@ +/* + * HeadlessVncServer.cpp - implementation of HeadlessVncServer class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +extern "C" { +#include "rfb/rfb.h" +} + +#include "HeadlessVncServer.h" +#include "VeyonConfiguration.h" + + +struct HeadlessVncScreen +{ + ~HeadlessVncScreen() + { + delete[] passwords[0]; + } + + rfbScreenInfoPtr rfbScreen{nullptr}; + std::array passwords{}; + QImage framebuffer; + +}; + + +HeadlessVncServer::HeadlessVncServer( QObject* parent ) : + QObject( parent ), + m_configuration( &VeyonCore::config() ) +{ +} + + + +void HeadlessVncServer::prepareServer() +{ +} + + + +bool HeadlessVncServer::runServer( int serverPort, const Password& password ) +{ + HeadlessVncScreen screen; + + if( initScreen( &screen ) == false || + initVncServer( serverPort, password, &screen ) == false ) + { + return false; + } + + while( true ) + { + QThread::msleep( DefaultSleepTime ); + + rfbProcessEvents( screen.rfbScreen, 0 ); + } + + rfbShutdownServer( screen.rfbScreen, true ); + rfbScreenCleanup( screen.rfbScreen ); + + return true; +} + + + +bool HeadlessVncServer::initScreen( HeadlessVncScreen* screen ) +{ + screen->framebuffer = QImage( DefaultFramebufferWidth, DefaultFramebufferHeight, QImage::Format_RGB32 ); + screen->framebuffer.fill( m_configuration.backgroundColor() ); + + return true; +} + + + +bool HeadlessVncServer::initVncServer( int serverPort, const VncServerPluginInterface::Password& password, + HeadlessVncScreen* screen ) +{ + auto rfbScreen = rfbGetScreen( nullptr, nullptr, + screen->framebuffer.width(), screen->framebuffer.height(), + 8, 3, 4 ); + + if( rfbScreen == nullptr ) + { + return false; + } + + screen->passwords[0] = qstrdup( password.toByteArray().constData() ); + + rfbScreen->desktopName = "VeyonVNC"; + rfbScreen->frameBuffer = reinterpret_cast( screen->framebuffer.bits() ); + rfbScreen->port = serverPort; + rfbScreen->ipv6port = serverPort; + + rfbScreen->authPasswdData = screen->passwords.data(); + rfbScreen->passwordCheck = rfbCheckPasswordByList; + + rfbScreen->serverFormat.redShift = 16; + rfbScreen->serverFormat.greenShift = 8; + rfbScreen->serverFormat.blueShift = 0; + + rfbScreen->serverFormat.redMax = 255; + rfbScreen->serverFormat.greenMax = 255; + rfbScreen->serverFormat.blueMax = 255; + + rfbScreen->serverFormat.trueColour = true; + rfbScreen->serverFormat.bitsPerPixel = 32; + + rfbScreen->alwaysShared = true; + rfbScreen->handleEventsEagerly = true; + rfbScreen->deferUpdateTime = 5; + + rfbScreen->screenData = screen; + + rfbScreen->cursor = nullptr; + + rfbInitServer( rfbScreen ); + + rfbMarkRectAsModified( rfbScreen, 0, 0, rfbScreen->width, rfbScreen->height ); + + screen->rfbScreen = rfbScreen; + + return true; +} + + +IMPLEMENT_CONFIG_PROXY(HeadlessVncConfiguration) diff --git a/plugins/vncserver/headless/HeadlessVncServer.h b/plugins/vncserver/headless/HeadlessVncServer.h new file mode 100644 index 0000000..ce372ff --- /dev/null +++ b/plugins/vncserver/headless/HeadlessVncServer.h @@ -0,0 +1,113 @@ +/* + * HeadlessVncServer.h - declaration of HeadlessVncServer class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PluginInterface.h" +#include "VncServerPluginInterface.h" +#include "HeadlessVncConfiguration.h" + +struct HeadlessVncScreen; + +class HeadlessVncServer : public QObject, VncServerPluginInterface, PluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.HeadlessVncServer") + Q_INTERFACES(PluginInterface VncServerPluginInterface) +public: + explicit HeadlessVncServer( QObject* parent = nullptr ); + + Plugin::Uid uid() const override + { + return QStringLiteral("f626f759-7691-45c0-bd4a-37171d98d219"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 0 ); + } + + QString name() const override + { + return QStringLiteral( "HeadlessVncServer" ); + } + + QString description() const override + { + return tr( "Headless VNC server" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + Plugin::Flags flags() const override + { + return Plugin::NoFlags; + } + + virtual QStringList supportedSessionTypes() const override + { + return {}; + } + + QWidget* configurationWidget() override + { + return nullptr; + } + + void prepareServer() override; + + bool runServer( int serverPort, const Password& password ) override; + + int configuredServerPort() override + { + return -1; + } + + Password configuredPassword() override + { + return {}; + } + +private: + static constexpr auto DefaultFramebufferWidth = 640; + static constexpr auto DefaultFramebufferHeight = 480; + static constexpr auto DefaultSleepTime = 25; + + bool initScreen( HeadlessVncScreen* screen ); + bool initVncServer( int serverPort, const VncServerPluginInterface::Password& password, + HeadlessVncScreen* screen ); + + bool handleScreenChanges( HeadlessVncScreen* screen ); + + HeadlessVncConfiguration m_configuration; + +}; diff --git a/plugins/vncserver/ultravnc-builtin/BuiltinUltraVncServer.cpp b/plugins/vncserver/ultravnc-builtin/BuiltinUltraVncServer.cpp new file mode 100644 index 0000000..122b96a --- /dev/null +++ b/plugins/vncserver/ultravnc-builtin/BuiltinUltraVncServer.cpp @@ -0,0 +1,246 @@ +/* + * BuiltinUltraVncServer.cpp - implementation of BuiltinUltraVncServer class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "BuiltinUltraVncServer.h" +#include "LogoffEventFilter.h" +#include "UltraVncConfiguration.h" +#include "UltraVncConfigurationWidget.h" +#include "VeyonConfiguration.h" + +extern int WinVNCAppMain(); + +static BuiltinUltraVncServer* vncServerInstance = nullptr; + +extern HINSTANCE hAppInstance; +extern DWORD mainthreadId; +extern HINSTANCE hInstResDLL; + + +void ultravnc_veyon_load_password( char* out, int size ) +{ + const auto password = vncServerInstance->password().toByteArray(); + + if( password.size() == size ) + { + memcpy( out, password.constData(), static_cast( size ) ); // Flawfinder: ignore + } + else + { + qFatal( "Requested password too short!"); + } +} + + + +BOOL ultravnc_veyon_load_int( LPCSTR valname, LONG *out ) +{ + if( strcmp( valname, "LoopbackOnly" ) == 0 ) + { + *out = 1; + return true; + } + if( strcmp( valname, "DisableTrayIcon" ) == 0 ) + { + *out = 1; + return true; + } + if( strcmp( valname, "AuthRequired" ) == 0 ) + { + *out = 1; + return true; + } + if( strcmp( valname, "CaptureAlphaBlending" ) == 0 ) + { + *out = vncServerInstance->configuration().ultraVncCaptureLayeredWindows() ? 1 : 0; + return true; + } + if( strcmp( valname, "PollFullScreen" ) == 0 ) + { + *out = vncServerInstance->configuration().ultraVncPollFullScreen() ? 1 : 0; + return true; + } + if( strcmp( valname, "TurboMode" ) == 0 ) + { + *out = vncServerInstance->configuration().ultraVncLowAccuracy() ? 1 : 0; + return true; + } + if( strcmp( valname, "DeskDupEngine" ) == 0 ) + { + *out = vncServerInstance->configuration().ultraVncDeskDupEngineEnabled() ? 1 : 0; + return true; + } + if( strcmp( valname, "MaxCpu2" ) == 0 ) + { + *out = vncServerInstance->configuration().ultraVncMaxCpu(); + return true; + } + if( strcmp( valname, "NewMSLogon" ) == 0 ) + { + *out = 0; + return true; + } + if( strcmp( valname, "MSLogonRequired" ) == 0 ) + { + *out = 0; + return true; + } + if( strcmp( valname, "RemoveWallpaper" ) == 0 ) + { + *out = 0; + return true; + } + if( strcmp( valname, "FileTransferEnabled" ) == 0 ) + { + *out = 0; + return true; + } + if( strcmp( valname, "AllowLoopback" ) == 0 ) + { + *out = 1; + return true; + } + if( strcmp( valname, "AutoPortSelect" ) == 0 ) + { + *out = 0; + return true; + } + + if( strcmp( valname, "HTTPConnect" ) == 0 ) + { + *out = 0; + return true; + } + + if( strcmp( valname, "PortNumber" ) == 0 ) + { + *out = vncServerInstance->serverPort(); + return true; + } + + if( strcmp( valname, "secondary" ) == 0 ) + { + *out = vncServerInstance->configuration().ultraVncMultiMonitorSupportEnabled(); + return true; + } + + if( strcmp( valname, "autocapt" ) == 0 ) + { + *out = 0; + return true; + } + + return false; +} + + + +BuiltinUltraVncServer::BuiltinUltraVncServer() : + m_configuration( &VeyonCore::config() ), + m_serverPort( DefaultServerPort ), + m_logoffEventFilter( nullptr ) +{ + vncServerInstance = this; +} + + + +BuiltinUltraVncServer::~BuiltinUltraVncServer() +{ + if( m_logoffEventFilter ) + { + delete m_logoffEventFilter; + } + + vncServerInstance = nullptr; +} + + + +QWidget* BuiltinUltraVncServer::configurationWidget() +{ + return new UltraVncConfigurationWidget( m_configuration ); +} + + + +void BuiltinUltraVncServer::prepareServer() +{ + // initialize global instance handler and main thread ID + hAppInstance = GetModuleHandle( nullptr ); + mainthreadId = GetCurrentThreadId(); + + hInstResDLL = hAppInstance; + + m_logoffEventFilter = new LogoffEventFilter; +} + + + +bool BuiltinUltraVncServer::runServer( int serverPort, const Password& password ) +{ + m_serverPort = serverPort; + m_password = password; + + // run UltraVNC server + auto hUser32 = LoadLibrary( "user32.dll" ); + auto hSHCore = LoadLibrary( "SHCore.dll" ); + + using SetProcessDpiAwarenessFunc = HRESULT (WINAPI *)( DWORD ); + const auto setProcessDpiAwareness = + hSHCore ? SetProcessDpiAwarenessFunc( GetProcAddress( hSHCore, "SetProcessDpiAwareness" ) ) : nullptr; + + if( setProcessDpiAwareness ) + { + static constexpr DWORD PROCESS_PER_MONITOR_DPI_AWARE = 2; + setProcessDpiAwareness( PROCESS_PER_MONITOR_DPI_AWARE ); + } + else if( hUser32 ) + { + using SetProcessDPIAwareFunc = BOOL (*)(); + const auto setDPIAware = SetProcessDPIAwareFunc( GetProcAddress( hUser32, "SetProcessDPIAware" ) ); + if( setDPIAware ) + { + setDPIAware(); + } + } + + if( hUser32 ) + { + FreeLibrary( hUser32 ); + } + + if( hSHCore ) + { + FreeLibrary( hSHCore ); + } + + return WinVNCAppMain() == 1; +} + + + +IMPLEMENT_CONFIG_PROXY(UltraVncConfiguration) + diff --git a/plugins/vncserver/ultravnc-builtin/BuiltinUltraVncServer.h b/plugins/vncserver/ultravnc-builtin/BuiltinUltraVncServer.h new file mode 100644 index 0000000..63e7693 --- /dev/null +++ b/plugins/vncserver/ultravnc-builtin/BuiltinUltraVncServer.h @@ -0,0 +1,122 @@ +/* + * BuiltinUltraVncServer.h - declaration of BuiltinUltraVncServer class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "UltraVncConfiguration.h" +#include "VncServerPluginInterface.h" + +class LogoffEventFilter; + +class BuiltinUltraVncServer : public QObject, VncServerPluginInterface, PluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.BuiltinUltraVncServer") + Q_INTERFACES(PluginInterface VncServerPluginInterface) +public: + BuiltinUltraVncServer(); + ~BuiltinUltraVncServer() override; + + Plugin::Uid uid() const override + { + return QStringLiteral("39d7a07f-94db-4912-aa1a-c4df8aee3879"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral( "BuiltinUltraVncServer" ); + } + + QString description() const override + { + return tr( "Builtin VNC server (UltraVNC)" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + Plugin::Flags flags() const override + { + return Plugin::ProvidesDefaultImplementation; + } + + QStringList supportedSessionTypes() const override + { + return { QStringLiteral("console"), QStringLiteral("rdp") }; + } + + QWidget* configurationWidget() override; + + void prepareServer() override; + + bool runServer( int serverPort, const Password& password ) override; + + int configuredServerPort() override + { + return -1; + } + + Password configuredPassword() override + { + return {}; + } + + const UltraVncConfiguration& configuration() const + { + return m_configuration; + } + + int serverPort() const + { + return m_serverPort; + } + + const Password& password() const + { + return m_password; + } + +private: + UltraVncConfiguration m_configuration; + + static constexpr auto DefaultServerPort = 5900; + + int m_serverPort; + Password m_password; + + LogoffEventFilter* m_logoffEventFilter; + +}; diff --git a/plugins/vncserver/ultravnc-builtin/CMakeLists.txt b/plugins/vncserver/ultravnc-builtin/CMakeLists.txt new file mode 100644 index 0000000..b3482ac --- /dev/null +++ b/plugins/vncserver/ultravnc-builtin/CMakeLists.txt @@ -0,0 +1,105 @@ +add_subdirectory(vnchooks) + +include(BuildVeyonPlugin) + +find_package(ZLIB REQUIRED) +find_package(JPEG REQUIRED) +find_package(LZO REQUIRED) + +add_definitions(-D_WIN32_WINNT=0x0602) + +set(ultravnc_CXX_SOURCES + ${ultravnc_DIR}/winvnc/winvnc/HideDesktop.cpp + ${ultravnc_DIR}/winvnc/winvnc/rfbRegion_win32.cpp + ${ultravnc_DIR}/winvnc/winvnc/vistahook.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncdesktopthread.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncdesktopsink.cpp + ${ultravnc_DIR}/winvnc/winvnc/IPC.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncencoderre.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncdesktop.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncserver.cpp + ${ultravnc_DIR}/winvnc/winvnc/rfbUpdateTracker.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncencodehext.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncproperties.cpp + ${ultravnc_DIR}/winvnc/winvnc/security.cpp + ${ultravnc_DIR}/winvnc/winvnc/buildtime.cpp + ${ultravnc_DIR}/winvnc/winvnc/Timer.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncencoderCursor.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncencoder.cpp + ${ultravnc_DIR}/winvnc/winvnc/vnclog.cpp + ${ultravnc_DIR}/winvnc/winvnc/translate.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncencodecorre.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncencodezrle.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncEncodeTight.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncservice.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncMultiMonitor.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncbuffer.cpp + ${ultravnc_DIR}/winvnc/winvnc/videodrivercheck.cpp + ${ultravnc_DIR}/winvnc/winvnc/videodriver.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncDesktopSW.cpp + ${ultravnc_DIR}/winvnc/winvnc/vnckeymap.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncOSVersion.cpp + ${ultravnc_DIR}/winvnc/winvnc/winvnc.cpp + ${ultravnc_DIR}/winvnc/winvnc/stdhdrs.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncEncodeUltra.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncEncodeUltra2.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncsockconnect.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncinsthandler.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncEncodeZlib.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncEncodeZlibHex.cpp + ${ultravnc_DIR}/winvnc/winvnc/vncpropertiesPoll.cpp + ${ultravnc_DIR}/winvnc/winvnc/helpers.cpp + ${ultravnc_DIR}/winvnc/winvnc/CpuUsage.cpp + ${ultravnc_DIR}/winvnc/winvnc/uvncUiAccess.cpp + ${ultravnc_DIR}/winvnc/winvnc/ScreenCapture.cpp + ${ultravnc_DIR}/winvnc/winvnc/DeskdupEngine.cpp + ${ultravnc_DIR}/winvnc/winvnc/vsocket.cpp + ${ultravnc_DIR}/winvnc/omnithread/nt.cpp + ${ultravnc_DIR}/common/Clipboard.cpp + ${ultravnc_DIR}/common/win32_helpers.cpp + ${ultravnc_DIR}/common/UltraVncZ.cpp + ${ultravnc_DIR}/rfb/dh.cpp + ${ultravnc_DIR}/rdr/ZlibOutStream.cxx + ${ultravnc_DIR}/rdr/ZlibInStream.cxx + ultravnc.cpp + vncntlm.cpp + ) + +set(ultravnc_C_SOURCES + ${ultravnc_DIR}/winvnc/winvnc/d3des.c + ${ultravnc_DIR}/winvnc/winvnc/vncauth.c + ) + +set(WITH_PCH OFF) + +build_veyon_plugin(builtin-ultravnc-server + BuiltinUltraVncServer.cpp + LogoffEventFilter.cpp + UltraVncConfigurationWidget.cpp + UltraVncConfigurationWidget.ui + ${ultravnc_C_SOURCES} + ${ultravnc_CXX_SOURCES} + BuiltinUltraVncServer.h + LogoffEventFilter.h + UltraVncConfigurationWidget.h + UltraVncConfiguration.h +) + +target_link_libraries(builtin-ultravnc-server -luserenv -lole32 -lversion -lgdi32 -limm32 -lwinmm ${ZLIB_LIBRARIES} ${JPEG_LIBRARIES} ${LZO_LIBRARIES}) +target_include_directories(builtin-ultravnc-server PRIVATE + ${ultravnc_DIR} + ${ultravnc_DIR}/winvnc + ${ultravnc_DIR}/winvnc/omnithread + ${ultravnc_DIR}/winvnc/winvnc + ) + +target_compile_definitions(builtin-ultravnc-server PRIVATE ULTRAVNC_VEYON_SUPPORT _INTERNALLIB) + +if(VEYON_BUILD_WIN64) + target_compile_definitions(builtin-ultravnc-server PRIVATE _X64) +endif() + +set(ULTRAVNC_COMPILER_FLAGS "-Wno-comments -Wno-attributes -Wno-write-strings -Wno-parentheses -Wno-misleading-indentation -Wno-unused-result -Wno-unused-label -Wno-unknown-pragmas -Wno-unused-variable -Wno-unused-but-set-variable -Wno-deprecated-declarations -Wno-format-zero-length -Wno-sign-compare -fexceptions") + +set_source_files_properties(${ultravnc_C_SOURCES} PROPERTIES COMPILE_FLAGS "${ULTRAVNC_COMPILER_FLAGS}") +set_source_files_properties(${ultravnc_CXX_SOURCES} PROPERTIES COMPILE_FLAGS "${ULTRAVNC_COMPILER_FLAGS} -Wno-terminate -Wno-conversion-null") diff --git a/plugins/vncserver/ultravnc-builtin/LogoffEventFilter.cpp b/plugins/vncserver/ultravnc-builtin/LogoffEventFilter.cpp new file mode 100644 index 0000000..0e1bacb --- /dev/null +++ b/plugins/vncserver/ultravnc-builtin/LogoffEventFilter.cpp @@ -0,0 +1,71 @@ +/* + * LogoffEventFilter.cpp - implementation of LogoffEventFilter class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "VeyonCore.h" +#include "LogoffEventFilter.h" + +LogoffEventFilter::LogoffEventFilter() : + m_shutdownEventHandle( OpenEvent( EVENT_ALL_ACCESS, false, "Global\\SessionEventUltra" ) ) +{ + if( m_shutdownEventHandle == nullptr ) + { + // no global event available already as we're not running under the + // control of the veyon service supervisor? + if( GetLastError() == ERROR_FILE_NOT_FOUND ) + { + vWarning() << "Creating session event"; + // then create our own event as otherwise the VNC server main loop + // will eat 100% CPU due to failing WaitForSingleObject() calls + m_shutdownEventHandle = CreateEvent( nullptr, false, false, "Global\\SessionEventUltra" ); + } + else + { + vWarning() << "Could not open or create session event"; + } + } + + QCoreApplication::instance()->installNativeEventFilter( this ); +} + + + +bool LogoffEventFilter::nativeEventFilter( const QByteArray& eventType, void* message, long* result ) +{ + Q_UNUSED(eventType); + Q_UNUSED(result); + + const auto winMsg = reinterpret_cast( message )->message; + + if( winMsg == WM_QUERYENDSESSION ) + { + vInfo() << "Got WM_QUERYENDSESSION - initiating server shutdown"; + + // tell UltraVNC server to quit + SetEvent( m_shutdownEventHandle ); + } + + return false; +} diff --git a/plugins/vncserver/ultravnc-builtin/LogoffEventFilter.h b/plugins/vncserver/ultravnc-builtin/LogoffEventFilter.h new file mode 100644 index 0000000..131ddee --- /dev/null +++ b/plugins/vncserver/ultravnc-builtin/LogoffEventFilter.h @@ -0,0 +1,42 @@ +/* + * LogoffEventFilter.h - declaration of LogoffEventFilter class + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include + +// event filter which makes ICA recognize logoff events etc. +class LogoffEventFilter : public QAbstractNativeEventFilter +{ +public: + LogoffEventFilter(); + + bool nativeEventFilter( const QByteArray& eventType, void* message, long* result) override; + +private: + HANDLE m_shutdownEventHandle; + +}; diff --git a/plugins/vncserver/ultravnc-builtin/UltraVncConfiguration.h b/plugins/vncserver/ultravnc-builtin/UltraVncConfiguration.h new file mode 100644 index 0000000..5fcec5e --- /dev/null +++ b/plugins/vncserver/ultravnc-builtin/UltraVncConfiguration.h @@ -0,0 +1,38 @@ +/* + * UltraVncConfiguration.h - UltraVNC-specific configuration values + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "Configuration/Proxy.h" + +#define FOREACH_ULTRAVNC_CONFIG_PROPERTY(OP) \ + OP( UltraVncConfiguration, m_configuration, bool, ultraVncCaptureLayeredWindows, setUltraVncCaptureLayeredWindows, "CaptureLayeredWindows", "UltraVNC", true, Configuration::Property::Flag::Advanced ) \ + OP( UltraVncConfiguration, m_configuration, bool, ultraVncMultiMonitorSupportEnabled, setUltraVncMultiMonitorSupportEnabled, "MultiMonitorSupport", "UltraVNC", true, Configuration::Property::Flag::Standard ) \ + OP( UltraVncConfiguration, m_configuration, bool, ultraVncPollFullScreen, setUltraVncPollFullScreen, "PollFullScreen", "UltraVNC", true, Configuration::Property::Flag::Advanced ) \ + OP( UltraVncConfiguration, m_configuration, bool, ultraVncLowAccuracy, setUltraVncLowAccuracy, "LowAccuracy", "UltraVNC", true, Configuration::Property::Flag::Advanced ) \ + OP( UltraVncConfiguration, m_configuration, bool, ultraVncDeskDupEngineEnabled, setUltraVncDeskDupEngineEnabled, "DeskDupEngine", "UltraVNC", true, Configuration::Property::Flag::Advanced ) \ + OP( UltraVncConfiguration, m_configuration, int, ultraVncMaxCpu, setUltraVncMaxCpu, "MaxCPU", "UltraVNC", 100, Configuration::Property::Flag::Advanced ) \ + +DECLARE_CONFIG_PROXY(UltraVncConfiguration, FOREACH_ULTRAVNC_CONFIG_PROPERTY) + diff --git a/plugins/vncserver/ultravnc-builtin/UltraVncConfigurationWidget.cpp b/plugins/vncserver/ultravnc-builtin/UltraVncConfigurationWidget.cpp new file mode 100644 index 0000000..ab826d2 --- /dev/null +++ b/plugins/vncserver/ultravnc-builtin/UltraVncConfigurationWidget.cpp @@ -0,0 +1,50 @@ +/* + * UltraVncConfigurationWidget.h - implementation of the UltraVncConfigurationWidget class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "Configuration/UiMapping.h" +#include "UltraVncConfiguration.h" +#include "UltraVncConfigurationWidget.h" + +#include "ui_UltraVncConfigurationWidget.h" + +UltraVncConfigurationWidget::UltraVncConfigurationWidget( UltraVncConfiguration& configuration ) : + QWidget(), + ui( new Ui::UltraVncConfigurationWidget ), + m_configuration( configuration ) +{ + ui->setupUi( this ); + + Configuration::UiMapping::setFlags( ui->ultraVncMaxCpu, Configuration::Property::Flag::Advanced ); + Configuration::UiMapping::setFlags( ui->ultraVncMaxCpuLabel, Configuration::Property::Flag::Advanced ); + + FOREACH_ULTRAVNC_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); + FOREACH_ULTRAVNC_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY); +} + + + +UltraVncConfigurationWidget::~UltraVncConfigurationWidget() +{ + delete ui; +} diff --git a/plugins/vncserver/ultravnc-builtin/UltraVncConfigurationWidget.h b/plugins/vncserver/ultravnc-builtin/UltraVncConfigurationWidget.h new file mode 100644 index 0000000..e71a2be --- /dev/null +++ b/plugins/vncserver/ultravnc-builtin/UltraVncConfigurationWidget.h @@ -0,0 +1,47 @@ +/* + * UltraVncConfigurationWidget.h - header for the UltraVncConfigurationWidget class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +namespace Ui { +class UltraVncConfigurationWidget; +} + +class UltraVncConfiguration; + +class UltraVncConfigurationWidget : public QWidget +{ + Q_OBJECT + +public: + UltraVncConfigurationWidget( UltraVncConfiguration& configuration ); + ~UltraVncConfigurationWidget() override; + +private: + Ui::UltraVncConfigurationWidget *ui; + UltraVncConfiguration& m_configuration; + +}; diff --git a/plugins/vncserver/ultravnc-builtin/UltraVncConfigurationWidget.ui b/plugins/vncserver/ultravnc-builtin/UltraVncConfigurationWidget.ui new file mode 100644 index 0000000..c862e5f --- /dev/null +++ b/plugins/vncserver/ultravnc-builtin/UltraVncConfigurationWidget.ui @@ -0,0 +1,87 @@ + + + UltraVncConfigurationWidget + + + Builtin UltraVNC server configuration + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Maximum CPU usage + + + + + + + % + + + 10 + + + 100 + + + + + + + Low accuracy (turbo mode) + + + + + + + Poll full screen (leave this enabled per default) + + + + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + + + + Enable multi monitor support + + + + + + + Enable capturing of layered (semi-transparent) windows + + + + + + + ultraVncCaptureLayeredWindows + ultraVncMultiMonitorSupportEnabled + ultraVncPollFullScreen + ultraVncDeskDupEngineEnabled + ultraVncLowAccuracy + + + + diff --git a/plugins/vncserver/ultravnc-builtin/ultravnc.cpp b/plugins/vncserver/ultravnc-builtin/ultravnc.cpp new file mode 100644 index 0000000..628c599 --- /dev/null +++ b/plugins/vncserver/ultravnc-builtin/ultravnc.cpp @@ -0,0 +1,19 @@ +#include "stdhdrs.h" + +#define rfbConnFailed 0 +#define rfbInvalidAuth 0 +#define rfbNoAuth 1 +#define rfbVncAuth 2 +#define rfbUltraVNC 17 + +#define rfbVncAuthOK 0 +#define rfbVncAuthFailed 1 + +// adzm 2010-09 - rfbUltraVNC or other auths may send this to restart authentication (perhaps over a now-secure channel) +#define rfbVncAuthContinue 0xFFFFFFFF + +#include "vncclient.cpp" + +extern bool G_USE_PIXEL; + +bool G_USE_PIXEL = false; diff --git a/plugins/vncserver/ultravnc-builtin/vnchooks/CMakeLists.txt b/plugins/vncserver/ultravnc-builtin/vnchooks/CMakeLists.txt new file mode 100644 index 0000000..c42ef30 --- /dev/null +++ b/plugins/vncserver/ultravnc-builtin/vnchooks/CMakeLists.txt @@ -0,0 +1,20 @@ +SET(VH_WINRC "${CMAKE_CURRENT_BINARY_DIR}/vnchooksrc.obj") +ADD_CUSTOM_COMMAND(OUTPUT ${VH_WINRC} + COMMAND ${WINDRES} + -I${CMAKE_CURRENT_SOURCE_DIR} + -o${VH_WINRC} + -i${ultravnc_DIR}/winvnc/vnchooks/vnchooks.rc) + +set(vnchooks_SOURCES + ${ultravnc_DIR}/winvnc/vnchooks/VNCHooks.cpp + ${ultravnc_DIR}/winvnc/vnchooks/SharedData.cpp +) + +add_library(vnchooks MODULE ${vnchooks_SOURCES} ${VH_WINRC}) +set_source_files_properties(${vnchooks_SOURCES} PROPERTIES SKIP_UNITY_BUILD_INCLUSION TRUE) +target_compile_options(vnchooks PRIVATE ${VEYON_COMPILE_OPTIONS}) +set_default_target_properties(vnchooks) +set_target_properties(vnchooks PROPERTIES PREFIX "") +set_target_properties(vnchooks PROPERTIES COMPILE_FLAGS "-Wno-write-strings -Wno-unused-variable -Wno-unknown-pragmas") +set_target_properties(vnchooks PROPERTIES LINK_FLAGS -Wl,-export-all-symbols) +target_link_libraries(vnchooks -ladvapi32) diff --git a/plugins/vncserver/ultravnc-builtin/vncntlm.cpp b/plugins/vncserver/ultravnc-builtin/vncntlm.cpp new file mode 100644 index 0000000..77b9add --- /dev/null +++ b/plugins/vncserver/ultravnc-builtin/vncntlm.cpp @@ -0,0 +1,31 @@ +/* + * vncntlm.cpp - dummy implementation of vncntlm module + * + * Copyright (c) 2016-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +int CheckUserGroupPasswordUni(char *,char *,const char *) +{ + // never perform logon authentication as we're only using + // simple VNC authentication for internal VNC server + return 0; +} + diff --git a/plugins/vncserver/x11vnc-builtin/BuiltinX11VncServer.cpp b/plugins/vncserver/x11vnc-builtin/BuiltinX11VncServer.cpp new file mode 100644 index 0000000..f7bb919 --- /dev/null +++ b/plugins/vncserver/x11vnc-builtin/BuiltinX11VncServer.cpp @@ -0,0 +1,158 @@ +/* + * BuiltinX11VncServer.cpp - implementation of BuiltinX11VncServer class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "BuiltinX11VncServer.h" +#include "VeyonConfiguration.h" +#include "X11VncConfigurationWidget.h" + +extern "C" int x11vnc_main( int argc, char * * argv ); + + +BuiltinX11VncServer::BuiltinX11VncServer( QObject* parent ) : + QObject( parent ), + m_configuration( &VeyonCore::config() ) +{ +} + + + +QWidget* BuiltinX11VncServer::configurationWidget() +{ + return new X11VncConfigurationWidget( m_configuration ); +} + + + +void BuiltinX11VncServer::prepareServer() +{ +} + + + +bool BuiltinX11VncServer::runServer( int serverPort, const Password& password ) +{ + QStringList cmdline = { QStringLiteral("-localhost"), + QStringLiteral("-nosel"), // do not exchange clipboard-contents + QStringLiteral("-nosetclipboard"), // do not exchange clipboard-contents + QStringLiteral("-rfbport"), QString::number( serverPort ), // set port at which the VNC server should listen + QStringLiteral("-rfbportv6"), QString::number( serverPort ), // set IPv6 port at which the VNC server should listen + QStringLiteral("-no6"), + } ; + + const auto extraArguments = m_configuration.extraArguments(); + + if( extraArguments.isEmpty() == false ) + { + cmdline.append( extraArguments.split( QLatin1Char(' ') ) ); + } + + if( m_configuration.isXDamageDisabled() ) + { + cmdline.append( QStringLiteral("-noxdamage") ); + } + else + { + // workaround for x11vnc when running in an NX session or a Thin client LTSP session + const auto systemEnv = QProcess::systemEnvironment(); + for( const auto& s : systemEnv ) + { + if( s.startsWith( QStringLiteral("NXSESSIONID=") ) || + s.startsWith( QStringLiteral("X2GO_SESSION=") ) || + s.startsWith( QStringLiteral("LTSP_CLIENT_MAC=") ) ) + { + cmdline.append( QStringLiteral("-noxdamage") ); + } + } + } + +#ifdef VEYON_X11VNC_EXTERNAL + QTemporaryFile tempFile; + if( tempFile.open() == false ) // Flawfinder: ignore + { + vCritical() << "Could not create temporary file!"; + return false; + } + tempFile.write( password.toByteArray() ); + tempFile.close(); + + cmdline.append( QStringLiteral("-passwdfile") ); + cmdline.append( QStringLiteral("rm:") + tempFile.fileName() ); + cmdline.append( QStringLiteral("-forever") ); + cmdline.append( QStringLiteral("-shared") ); + cmdline.append( QStringLiteral("-nocmds") ); + cmdline.append( QStringLiteral("-noremote") ); + + QProcess x11vnc; + x11vnc.setProcessChannelMode( QProcess::ForwardedChannels ); + x11vnc.start( QStringLiteral("x11vnc"), cmdline ); + if( x11vnc.waitForStarted() == false ) + { + vCritical() << "Could not start external x11vnc:" << x11vnc.errorString(); + vCritical() << "Please make sure x11vnc is installed and installation directory is in PATH!"; + return false; + } + else + { + x11vnc.waitForFinished( -1 ); + } + + return true; +#else + cmdline.append( { QStringLiteral("-passwd"), QString::fromUtf8( password.toByteArray() ) } ); + + // build new C-style command line array based on cmdline-QStringList + const auto appArguments = QCoreApplication::arguments(); + auto argv = new char *[cmdline.size()+1]; // Flawfinder: ignore + argv[0] = qstrdup( appArguments.first().toUtf8().constData() ); + int argc = 1; + + for( auto it = cmdline.begin(), end = cmdline.end(); it != end; ++it, ++argc ) + { + const auto len = static_cast( it->length() ); + argv[argc] = new char[len + 1]; + strncpy( argv[argc], it->toUtf8().constData(), len ); // Flawfinder: ignore + argv[argc][len] = 0; + } + + // run x11vnc-server + const auto result = x11vnc_main( argc, argv ); + + for( int i = 0; i < argc; ++i ) + { + delete[] argv[i]; + } + + delete[] argv; + + return result == 0; +#endif +} + + +IMPLEMENT_CONFIG_PROXY(X11VncConfiguration) diff --git a/plugins/vncserver/x11vnc-builtin/BuiltinX11VncServer.h b/plugins/vncserver/x11vnc-builtin/BuiltinX11VncServer.h new file mode 100644 index 0000000..570f356 --- /dev/null +++ b/plugins/vncserver/x11vnc-builtin/BuiltinX11VncServer.h @@ -0,0 +1,98 @@ +/* + * BuiltinX11VncServer.h - declaration of BuiltinX11VncServer class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "PluginInterface.h" +#include "VncServerPluginInterface.h" +#include "X11VncConfiguration.h" + +class BuiltinX11VncServer : public QObject, VncServerPluginInterface, PluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.BuiltinX11VncServer") + Q_INTERFACES(PluginInterface VncServerPluginInterface) +public: + explicit BuiltinX11VncServer( QObject* parent = nullptr ); + + Plugin::Uid uid() const override + { + return QStringLiteral("39d7a07f-94db-4912-aa1a-c4df8aee3879"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 1 ); + } + + QString name() const override + { + return QStringLiteral( "BuiltinX11VncServer" ); + } + + QString description() const override + { + return tr( "Builtin VNC server (x11vnc)" ); + } + + QString vendor() const override + { + return QStringLiteral( "Veyon Community" ); + } + + QString copyright() const override + { + return QStringLiteral( "Tobias Junghans" ); + } + + Plugin::Flags flags() const override + { + return Plugin::ProvidesDefaultImplementation; + } + + QStringList supportedSessionTypes() const override + { + return { QStringLiteral("x11") }; + } + + QWidget* configurationWidget() override; + + void prepareServer() override; + + bool runServer( int serverPort, const Password& password ) override; + + int configuredServerPort() override + { + return -1; + } + + Password configuredPassword() override + { + return {}; + } + +private: + X11VncConfiguration m_configuration; + +}; diff --git a/plugins/vncserver/x11vnc-builtin/CMakeLists.txt b/plugins/vncserver/x11vnc-builtin/CMakeLists.txt new file mode 100644 index 0000000..bd30936 --- /dev/null +++ b/plugins/vncserver/x11vnc-builtin/CMakeLists.txt @@ -0,0 +1,262 @@ +include(BuildVeyonPlugin) + +if(NOT VEYON_X11VNC_EXTERNAL) + +find_package(LibVNCServer 0.9.8) +if(NOT LibVNCServer_FOUND) + ### BEGIN: libvncserver configuration + include(LibVNCServerIntegration) + + set(_RFB_RFBCONFIG_H TRUE) + set(LIBVNCSERVER_HAVE_LIBJPEG TRUE) + set(LIBVNCSERVER_HAVE_LZO TRUE) + set(LIBVNCSERVER_HAVE_LIBPNG TRUE) + set(LIBVNCSERVER_HAVE_LIBPTHREAD TRUE) + set(LIBVNCSERVER_HAVE_LIBZ TRUE) + set(LIBVNCSERVER_HAVE_LIBSSL TRUE) + set(LIBVNCSERVER_ALLOW24BPP TRUE) + set(LIBVNCSERVER_IPv6 TRUE) + ### END: libvncserver configuration + + # write libvncserver configuration header + file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/${VEYON_CORE_INCLUDE_DIR}/rfb) + configure_file(${CMAKE_SOURCE_DIR}/3rdparty/libvncserver/rfb/rfbconfig.h.cmakein ${CMAKE_BINARY_DIR}/${VEYON_CORE_INCLUDE_DIR}/rfb/rfbconfig.h @ONLY) + +endif() + +set(FULL_PACKAGE_NAME "Veyon") +set(PACKAGE_VERSION "${VERSION_STRING}") +set(VERSION_PATCHLEVEL "${VERSION_PATCH}") + +# check for x11vnc requirements +set(FUNCS getpwnam getspnam getuid grantpt initgroups seteuid setegid setgid setsid setuid shmat waitpid) +foreach(_func ${FUNCS}) + string(TOUPPER "${_func}" fuc) + check_function_exists(${_func} HAVE_${fuc}) +endforeach() + +check_c_source_compiles("static __thread int p = 0; int main() {}" HAVE_TLS) + +# x11vnc header macros +check_include_files(linux/fb.h HAVE_LINUX_FB_H) +check_include_files(linux/input.h HAVE_LINUX_INPUT_H) +check_include_files(linux/uinput.h HAVE_LINUX_UINPUT_H) +check_include_files(linux/videodev.h HAVE_LINUX_VIDEODEV_H) +check_include_files(netdb.h HAVE_NETDB_H) +check_include_files(netinet/in.h HAVE_NETINET_IN_H) +check_include_files(pwd.h HAVE_PWD_H) +check_include_files(sys/ioctl.h HAVE_SYS_IOCTL_H) +check_include_files(sys/stropts.h HAVE_SYS_STROPTS_H) +check_include_files(sys/wait.h HAVE_SYS_WAIT_H) +check_include_files(termios.h HAVE_TERMIOS_H) +check_include_files(utmpx.h HAVE_UTMPX_H) + +find_package(X11 REQUIRED) + +if(NOT X11_XTest_FOUND) + message(FATAL_ERROR "XTest library or headers not found - please install libxtst-dev or libXtst-devel") +endif() + +if(NOT X11_Xrandr_FOUND) + message(FATAL_ERROR "Xrandr library or headers not found - please install libxrandr-dev or libXrandr-devel") +endif() + +if(NOT X11_Xinerama_FOUND) + message(FATAL_ERROR "Xinerama library or headers not found - please install libxinerama-dev or libXinerama-devel") +endif() + +if(NOT X11_Xdamage_FOUND) + message(FATAL_ERROR "Xdamage library or headers not found - please install libxdamage-dev or libXdamage-devel") +endif() + +if(NOT X11_Xfixes_FOUND) + message(FATAL_ERROR "Xfixes library or headers not found - please install libxfixes-dev or libXfixes-devel") +endif() + +set(HAVE_X11 TRUE) +set(HAVE_XTEST TRUE) +set(HAVE_LIBSSL TRUE) +set(HAVE_LIBXINERAMA TRUE) +set(HAVE_LIBXRANDR TRUE) +set(HAVE_LIBXDAMAGE TRUE) +set(HAVE_LIBXFIXES TRUE) + +if(X11_XShm_FOUND) + set(HAVE_XSHM TRUE) +else() + message("WARNING: XShm library or headers not found - building VNC server without XShm support") +endif() + +if(X11_Xinput_FOUND) + set(HAVE_XI2 TRUE) +else() + message("WARNING: Xinput library or headers not found - building VNC server without Xinput support") +endif() + +if(X11_Xcomposite_FOUND) + set(HAVE_LIBXCOMPOSITE TRUE) +else() + message("WARNING: Xcomposite library or headers not found - building VNC server without Xcomposite support") +endif() + +if(X11_Xcursor_FOUND) + set(HAVE_LIBXCURSOR TRUE) +else() + message("WARNING: Xcursor library or headers not found - building VNC server without Xcursor support") +endif() + +set(CMAKE_REQUIRED_LIBRARIES ${X11_LIBRARIES} ${X11_XTest_LIB}) + +check_function_exists(XReadScreen HAVE_SOLARIS_XREADSCREEN) +check_function_exists(FBPMForceLevel HAVE_FBPM) +check_function_exists(DPMSForceLevel HAVE_DPMS) +check_function_exists(XTestGrabControl HAVE_XTESTGRABCONTROL) +check_function_exists(XRecordEnableContextAsync HAVE_RECORD) +check_include_files(X11/extensions/readdisplay.h HAVE_IRIX_XREADDISPLAY) +check_include_files(X11/XKBlib.h HAVE_XKBLIB_H) +if(HAVE_XKBLIB_H) + check_function_exists(XkbSelectEvents HAVE_XKEYBOARD) +endif() + +set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) +check_function_exists(X509_print_ex_fp HAVE_X509_PRINT_EX_FP) + +unset(CMAKE_REQUIRED_LIBRARIES) + +set(X11VNC_CONFIG ${CMAKE_BINARY_DIR}/config.h) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) +if(NOT LibVNCServer_FOUND) +set(libvncserver_SOURCES + ${libvncserver_DIR}/libvncserver/auth.c + ${libvncserver_DIR}/libvncserver/cargs.c + ${libvncserver_DIR}/libvncserver/corre.c + ${libvncserver_DIR}/libvncserver/cursor.c + ${libvncserver_DIR}/libvncserver/cutpaste.c + ${libvncserver_DIR}/libvncserver/draw.c + ${libvncserver_DIR}/libvncserver/font.c + ${libvncserver_DIR}/libvncserver/hextile.c + ${libvncserver_DIR}/libvncserver/httpd.c + ${libvncserver_DIR}/libvncserver/main.c + ${libvncserver_DIR}/libvncserver/rfbregion.c + ${libvncserver_DIR}/libvncserver/rfbserver.c + ${libvncserver_DIR}/libvncserver/rre.c + ${libvncserver_DIR}/libvncserver/scale.c + ${libvncserver_DIR}/libvncserver/selbox.c + ${libvncserver_DIR}/libvncserver/sockets.c + ${libvncserver_DIR}/libvncserver/stats.c + ${libvncserver_DIR}/libvncserver/translate.c + ${libvncserver_DIR}/libvncserver/ultra.c + ${libvncserver_DIR}/libvncserver/zlib.c + ${libvncserver_DIR}/libvncserver/zrle.c + ${libvncserver_DIR}/libvncserver/zrleoutstream.c + ${libvncserver_DIR}/libvncserver/zrlepalettehelper.c + ${libvncserver_DIR}/libvncserver/tight.c + ${libvncserver_DIR}/common/d3des.c + ${libvncserver_DIR}/common/turbojpeg.c + ${libvncserver_DIR}/common/vncauth.c) +endif() + +set(x11vnc_SOURCES x11vnc-veyon.c + ${x11vnc_DIR}/src/appshare.c + ${x11vnc_DIR}/src/avahi.c + ${x11vnc_DIR}/src/rates.c + ${x11vnc_DIR}/src/cleanup.c + ${x11vnc_DIR}/src/remote.c + ${x11vnc_DIR}/src/pointer.c + ${x11vnc_DIR}/src/userinput.c + ${x11vnc_DIR}/src/unixpw.c + ${x11vnc_DIR}/src/gui.c + ${x11vnc_DIR}/src/xkb_bell.c + ${x11vnc_DIR}/src/xinerama.c + ${x11vnc_DIR}/src/solid.c + ${x11vnc_DIR}/src/selection.c + ${x11vnc_DIR}/src/xrandr.c + ${x11vnc_DIR}/src/win_utils.c + ${x11vnc_DIR}/src/cursor.c + ${x11vnc_DIR}/src/screen.c + ${x11vnc_DIR}/src/xevents.c + ${x11vnc_DIR}/src/help.c + ${x11vnc_DIR}/src/inet.c + ${x11vnc_DIR}/src/sslcmds.c + ${x11vnc_DIR}/src/xwrappers.c + ${x11vnc_DIR}/src/scan.c + ${x11vnc_DIR}/src/options.c + ${x11vnc_DIR}/src/user.c + ${x11vnc_DIR}/src/util.c + ${x11vnc_DIR}/src/x11vnc_defs.c + ${x11vnc_DIR}/src/xrecord.c + ${x11vnc_DIR}/src/8to24.c + ${x11vnc_DIR}/src/xdamage.c + ${x11vnc_DIR}/src/keyboard.c + ${x11vnc_DIR}/src/connections.c + ${x11vnc_DIR}/src/sslhelper.c + ${x11vnc_DIR}/src/linuxfb.c + ${x11vnc_DIR}/src/v4l.c + ${x11vnc_DIR}/src/macosx.c + ${x11vnc_DIR}/src/macosxCG.c + ${x11vnc_DIR}/src/macosxCGP.c + ${x11vnc_DIR}/src/macosxCGS.c + ${x11vnc_DIR}/src/xi2_devices.c + ${x11vnc_DIR}/src/uinput.c +) + +set_source_files_properties(${x11vnc_SOURCES} ${libvncserver_SOURCES} PROPERTIES COMPILE_FLAGS "-Wno-unused-result -Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable -Wno-misleading-indentation -Wno-deprecated-declarations -Wno-address -Wno-format -Wno-discarded-qualifiers -Wno-strict-aliasing -Wno-restrict -Wno-multistatement-macros") + +endif() + +set(WITH_PCH OFF) + +build_veyon_plugin(builtin-x11vnc-server + BuiltinX11VncServer.cpp + X11VncConfigurationWidget.cpp + X11VncConfigurationWidget.ui + ${libvncserver_SOURCES} + ${x11vnc_SOURCES} + BuiltinX11VncServer.h + X11VncConfigurationWidget.h + X11VncConfiguration.h +) + +if(VEYON_X11VNC_EXTERNAL) + +target_compile_definitions(builtin-x11vnc-server PRIVATE VEYON_X11VNC_EXTERNAL) + +else() + +target_compile_definitions(builtin-x11vnc-server PRIVATE VNCSHARED FOREVER NOREPEAT=0 NOPW=1 REMOTE_CONTROL=0 EXTERNAL_COMMANDS=0 FILEXFER=0 NOGUI SMALL_FOOTPRINT) + +target_include_directories(builtin-x11vnc-server PRIVATE ${x11vnc_DIR}/src) + +target_link_libraries(builtin-x11vnc-server + ${X11_LIBRARIES} + ${X11_XTest_LIB} + ${X11_Xfixes_LIB} + ${X11_Xinerama_LIB} + ${X11_Xdamage_LIB} + ${X11_Xrandr_LIB} +) + +if(LibVNCServer_FOUND) + #target_include_directories(builtin-x11vnc-server PRIVATE ${3rdparty_DIR} ${x11vnc_DIR}/src) + target_link_libraries(builtin-x11vnc-server LibVNC::LibVNCServer) +else() + target_include_directories(builtin-x11vnc-server PRIVATE ${libvncserver_DIR}/libvncserver ${libvncserver_DIR}/common) +endif() + +if(X11_XShm_FOUND) +target_link_libraries(builtin-x11vnc-server ${X11_XShm_LIB}) +endif() + +if(X11_Xcomposite_FOUND) +target_link_libraries(builtin-x11vnc-server ${X11_Xcomposite_LIB}) +endif() + +if(X11_Xcursor_FOUND) +target_link_libraries(builtin-x11vnc-server ${X11_Xcursor_LIB}) +endif() + +if(X11_Xinput_FOUND) +target_link_libraries(builtin-x11vnc-server ${X11_Xinput_LIB}) +endif() + +endif() diff --git a/plugins/vncserver/x11vnc-builtin/X11VncConfiguration.h b/plugins/vncserver/x11vnc-builtin/X11VncConfiguration.h new file mode 100644 index 0000000..2e09375 --- /dev/null +++ b/plugins/vncserver/x11vnc-builtin/X11VncConfiguration.h @@ -0,0 +1,33 @@ +/* + * X11VncConfiguration.h - x11vnc-specific configuration values + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "Configuration/Proxy.h" + +#define FOREACH_X11VNC_CONFIG_PROPERTY(OP) \ + OP( X11VncConfiguration, m_configuration, bool, isXDamageDisabled, setXDamageDisabled, "XDamageDisabled", "X11Vnc", false, Configuration::Property::Flag::Advanced ) \ + OP( X11VncConfiguration, m_configuration, QString, extraArguments, setExtraArguments, "ExtraArguments", "X11Vnc", QString(), Configuration::Property::Flag::Advanced ) + +DECLARE_CONFIG_PROXY(X11VncConfiguration, FOREACH_X11VNC_CONFIG_PROPERTY) diff --git a/plugins/vncserver/x11vnc-builtin/X11VncConfigurationWidget.cpp b/plugins/vncserver/x11vnc-builtin/X11VncConfigurationWidget.cpp new file mode 100644 index 0000000..40321d1 --- /dev/null +++ b/plugins/vncserver/x11vnc-builtin/X11VncConfigurationWidget.cpp @@ -0,0 +1,47 @@ +/* + * X11VncConfigurationWidget.h - implementation of the X11VncConfigurationWidget class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "Configuration/UiMapping.h" +#include "X11VncConfiguration.h" +#include "X11VncConfigurationWidget.h" + +#include "ui_X11VncConfigurationWidget.h" + +X11VncConfigurationWidget::X11VncConfigurationWidget( X11VncConfiguration& configuration, QWidget* parent ) : + QWidget( parent ), + ui( new Ui::X11VncConfigurationWidget ), + m_configuration( configuration ) +{ + ui->setupUi( this ); + + FOREACH_X11VNC_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); + FOREACH_X11VNC_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY); +} + + + +X11VncConfigurationWidget::~X11VncConfigurationWidget() +{ + delete ui; +} diff --git a/plugins/vncserver/x11vnc-builtin/X11VncConfigurationWidget.h b/plugins/vncserver/x11vnc-builtin/X11VncConfigurationWidget.h new file mode 100644 index 0000000..3aac281 --- /dev/null +++ b/plugins/vncserver/x11vnc-builtin/X11VncConfigurationWidget.h @@ -0,0 +1,47 @@ +/* + * X11VncConfigurationWidget.h - header for the X11VncConfigurationWidget class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +namespace Ui { +class X11VncConfigurationWidget; +} + +class X11VncConfiguration; + +class X11VncConfigurationWidget : public QWidget +{ + Q_OBJECT + +public: + explicit X11VncConfigurationWidget( X11VncConfiguration& configuration, QWidget* parent = nullptr ); + ~X11VncConfigurationWidget() override; + +private: + Ui::X11VncConfigurationWidget *ui; + X11VncConfiguration& m_configuration; + +}; diff --git a/plugins/vncserver/x11vnc-builtin/X11VncConfigurationWidget.ui b/plugins/vncserver/x11vnc-builtin/X11VncConfigurationWidget.ui new file mode 100644 index 0000000..187e9a4 --- /dev/null +++ b/plugins/vncserver/x11vnc-builtin/X11VncConfigurationWidget.ui @@ -0,0 +1,50 @@ + + + X11VncConfigurationWidget + + + + 0 + 0 + 510 + 84 + + + + Builtin x11vnc server configuration + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Custom x11vnc parameters: + + + + + + + + + + Do not use X Damage extension + + + + + + + + diff --git a/plugins/vncserver/x11vnc-builtin/config.h.in b/plugins/vncserver/x11vnc-builtin/config.h.in new file mode 100644 index 0000000..e977c64 --- /dev/null +++ b/plugins/vncserver/x11vnc-builtin/config.h.in @@ -0,0 +1,305 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_ARPA_INET_H 1 + +/* Avahi/mDNS client build environment present */ +#cmakedefine HAVE_AVAHI 1 + +/* Define to 1 if you have the `crypt' function. */ +#cmakedefine HAVE_CRYPT 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#cmakedefine HAVE_DOPRNT 1 + +/* DPMS extension build environment present */ +#cmakedefine HAVE_DPMS 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_ENDIAN_H 1 + +/* FBPM extension build environment present */ +#cmakedefine HAVE_FBPM 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `fork' function. */ +#cmakedefine HAVE_FORK 1 + +/* Define to 1 if you have the `ftime' function. */ +#cmakedefine HAVE_FTIME 1 + +/* Define to 1 if you have the `geteuid' function. */ +#cmakedefine HAVE_GETEUID 1 + +/* Define to 1 if you have the `gethostbyname' function. */ +#cmakedefine HAVE_GETHOSTBYNAME 1 + +/* Define to 1 if you have the `gethostname' function. */ +#cmakedefine HAVE_GETHOSTNAME 1 + +/* Define to 1 if you have the `getpwnam' function. */ +#cmakedefine HAVE_GETPWNAM 1 + +/* Define to 1 if you have the `getpwuid' function. */ +#cmakedefine HAVE_GETPWUID 1 + +/* Define to 1 if you have the `getspnam' function. */ +#cmakedefine HAVE_GETSPNAM 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#cmakedefine HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the `getuid' function. */ +#cmakedefine HAVE_GETUID 1 + +/* Define to 1 if you have the `grantpt' function. */ +#cmakedefine HAVE_GRANTPT 1 + +/* Define to 1 if you have the `inet_ntoa' function. */ +#cmakedefine HAVE_INET_NTOA 1 + +/* Define to 1 if you have the `initgroups' function. */ +#cmakedefine HAVE_INITGROUPS 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_INTTYPES_H 1 + +/* IRIX XReadDisplay available */ +#cmakedefine HAVE_IRIX_XREADDISPLAY 1 + +/* libcrypt library present */ +#cmakedefine HAVE_LIBCRYPT 1 + +/* openssl libcrypto library present */ +#cmakedefine HAVE_LIBCRYPTO 1 + +/* Define to 1 if you have the `cygipc' library (-lcygipc). */ +#cmakedefine HAVE_LIBCYGIPC 1 + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +#cmakedefine HAVE_LIBNSL 1 + +/* Define to 1 if you have the `socket' library (-lsocket). */ +#cmakedefine HAVE_LIBSOCKET 1 + +/* openssl libssl library present */ +#cmakedefine HAVE_LIBSSL 1 + +/* XCOMPOSITE extension build environment present */ +#cmakedefine HAVE_LIBXCOMPOSITE 1 + +/* Xcursor library build environment present */ +#cmakedefine HAVE_LIBXCURSOR 1 + +/* XDAMAGE extension build environment present */ +#cmakedefine HAVE_LIBXDAMAGE 1 + +/* XFIXES extension build environment present */ +#cmakedefine HAVE_LIBXFIXES 1 + +/* XINERAMA extension build environment present */ +#cmakedefine HAVE_LIBXINERAMA 1 + +/* XRANDR extension build environment present */ +#cmakedefine HAVE_LIBXRANDR 1 + +/* DEC-XTRAP extension build environment present */ +#cmakedefine HAVE_LIBXTRAP 1 + +/* linux fb device build environment present */ +#cmakedefine HAVE_LINUX_FB_H 1 + +/* linux/input.h present */ +#cmakedefine HAVE_LINUX_INPUT_H 1 + +/* linux uinput device build environment present */ +#cmakedefine HAVE_LINUX_UINPUT_H 1 + +/* video4linux build environment present */ +#cmakedefine HAVE_LINUX_VIDEODEV_H 1 + +/* build MacOS X native display support */ +#cmakedefine HAVE_MACOSX_NATIVE_DISPLAY 1 + +/* MacOS X OpenGL present */ +#cmakedefine HAVE_MACOSX_OPENGL_H 1 + +/* Define to 1 if you have the `memmove' function. */ +#cmakedefine HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#cmakedefine HAVE_MEMSET 1 + +/* Define to 1 if you have the `mkfifo' function. */ +#cmakedefine HAVE_MKFIFO 1 + +/* Define to 1 if you have the `mmap' function. */ +#cmakedefine HAVE_MMAP 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_PWD_H 1 + +/* RECORD extension build environment present */ +#cmakedefine HAVE_RECORD 1 + +/* Define to 1 if you have the `select' function. */ +#cmakedefine HAVE_SELECT 1 + +/* Define to 1 if you have the `setegid' function. */ +#cmakedefine HAVE_SETEGID 1 + +/* Define to 1 if you have the `seteuid' function. */ +#cmakedefine HAVE_SETEUID 1 + +/* Define to 1 if you have the `setgid' function. */ +#cmakedefine HAVE_SETGID 1 + +/* Define to 1 if you have the `setpgrp' function. */ +#cmakedefine HAVE_SETPGRP 1 + +/* Define to 1 if you have the `setsid' function. */ +#cmakedefine HAVE_SETSID 1 + +/* Define to 1 if you have the `setuid' function. */ +#cmakedefine HAVE_SETUID 1 + +/* Define to 1 if you have the `setutxent' function. */ +#cmakedefine HAVE_SETUTXENT 1 + +/* Define to 1 if you have the `shmat' function. */ +#cmakedefine HAVE_SHMAT 1 + +/* Define to 1 if you have the `socket' function. */ +#cmakedefine HAVE_SOCKET 1 + +/* Solaris XReadScreen available */ +#cmakedefine HAVE_SOLARIS_XREADSCREEN 1 + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +#cmakedefine HAVE_STAT_EMPTY_STRING_BUG 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strchr' function. */ +#cmakedefine HAVE_STRCHR 1 + +/* Define to 1 if you have the `strcspn' function. */ +#cmakedefine HAVE_STRCSPN 1 + +/* Define to 1 if you have the `strdup' function. */ +#cmakedefine HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#cmakedefine HAVE_STRERROR 1 + +/* Define to 1 if you have the `strftime' function. */ +#cmakedefine HAVE_STRFTIME 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRING_H 1 + +/* Define to 1 if you have the `strstr' function. */ +#cmakedefine HAVE_STRSTR 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_ENDIAN_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_STROPTS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TIMEB_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#cmakedefine HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_TERMIOS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UTMPX_H 1 + +/* Define to 1 if you have the `vfork' function. */ +#cmakedefine HAVE_VFORK 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_VFORK_H 1 + +/* Define to 1 if you have the `vprintf' function. */ +#cmakedefine HAVE_VPRINTF 1 + +/* Define to 1 if you have the `waitpid' function. */ +#cmakedefine HAVE_WAITPID 1 + +/* Define to 1 if `fork' works. */ +#cmakedefine HAVE_WORKING_FORK 1 + +/* Define to 1 if `vfork' works. */ +#cmakedefine HAVE_WORKING_VFORK 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_WS2TCPIP_H 1 + +/* X11 build environment present */ +#cmakedefine HAVE_X11 1 + +/* open ssl X509_print_ex_fp available */ +#cmakedefine HAVE_X509_PRINT_EX_FP 1 + +/* XI2 available */ +#cmakedefine HAVE_XI2 1 + +/* XKEYBOARD extension build environment present */ +#cmakedefine HAVE_XKEYBOARD 1 + +/* MIT-SHM extension build environment present */ +#cmakedefine HAVE_XSHM 1 + +/* XTEST extension build environment present */ +#cmakedefine HAVE_XTEST 1 + +/* XTEST extension has XTestGrabControl */ +#cmakedefine HAVE_XTESTGRABCONTROL 1 + +/* Version number of package */ +#define VERSION "@VERSION_STRING@" diff --git a/plugins/vncserver/x11vnc-builtin/x11vnc-veyon.c b/plugins/vncserver/x11vnc-builtin/x11vnc-veyon.c new file mode 100644 index 0000000..c5542cc --- /dev/null +++ b/plugins/vncserver/x11vnc-builtin/x11vnc-veyon.c @@ -0,0 +1,7 @@ +#define SHOW_NO_PASSWORD_WARNING 0 +#define main x11vnc_main + +#include + +#include "x11vnc.c" +#include "pm.c" diff --git a/plugins/webapi/CMakeLists.txt b/plugins/webapi/CMakeLists.txt new file mode 100644 index 0000000..60eb56f --- /dev/null +++ b/plugins/webapi/CMakeLists.txt @@ -0,0 +1,39 @@ +find_package(Qt5HttpServer) + +if(Qt5HttpServer_DIR OR (Qt5Core_VERSION VERSION_GREATER 5.12.0 AND TARGET Qt5::CorePrivate AND TARGET Qt5::NetworkPrivate)) + +include(BuildVeyonPlugin) + +build_veyon_plugin(webapi + WebApiPlugin.cpp + WebApiPlugin.h + WebApiAuthenticationProxy.cpp + WebApiAuthenticationProxy.h + WebApiConfiguration.h + WebApiConfigurationPage.cpp + WebApiConfigurationPage.h + WebApiConfigurationPage.ui + WebApiConnection.cpp + WebApiConnection.h + WebApiController.cpp + WebApiController.h + WebApiHttpServer.cpp + WebApiHttpServer.h + webapi.qrc + ) + +set_source_files_properties(WebApiHttpServer.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION TRUE) + +if(Qt5HttpServer_DIR) + target_link_libraries(webapi Qt5::HttpServer) +else() + add_subdirectory(qthttpserver) + + target_link_libraries(webapi qthttpserver) +endif() + +else() + +message(WARNING "Neither Qt5HttpServer nor Qt >= 5.12 with private headers found - omitting WebAPI plugin from build") + +endif() diff --git a/plugins/webapi/WebApiAuthenticationProxy.cpp b/plugins/webapi/WebApiAuthenticationProxy.cpp new file mode 100644 index 0000000..51639f7 --- /dev/null +++ b/plugins/webapi/WebApiAuthenticationProxy.cpp @@ -0,0 +1,146 @@ +/* + * WebApiAuthenticationProxy.cpp - implementation of WebApiAuthenticationProxy class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "WebApiAuthenticationProxy.h" +#include "WebApiConfiguration.h" + + +WebApiAuthenticationProxy::WebApiAuthenticationProxy( const WebApiConfiguration& configuration ) : + m_waitConditionWaitTime( configuration.connectionAuthenticationTimeout() * 1000 ) +{ +} + + + +#if VEYON_VERSION_MAJOR < 5 +#include "RfbVeyonAuth.h" + +QVector WebApiAuthenticationProxy::authenticationMethods() +{ + const auto types = authenticationTypes(); + QVector uuids; + for( auto authType : types ) + { + switch( authType ) + { + case RfbVeyonAuth::Logon: uuids.append( m_authLogonUuid ); break; + case RfbVeyonAuth::KeyFile: uuids.append( m_authKeysUuid ); break; + default: break; + } + } + + return uuids; +} +#endif + + + +WebApiAuthenticationProxy::AuthenticationMethod WebApiAuthenticationProxy::initCredentials() +{ + QMutexLocker l( mutex() ); + + if( m_selectedAuthenticationMethod.isNull() ) + { + l.unlock(); + + QMutex credentialsWaitMutex; + QMutexLocker credentialsWaitMutexLocker( &credentialsWaitMutex ); + + if( m_credentialsPopulated.wait( &credentialsWaitMutex, QDeadlineTimer(m_waitConditionWaitTime) ) == false ) + { + vWarning() << "waiting for credentials timed out"; +#if VEYON_VERSION_MAJOR >= 5 + return {}; +#else + return RfbVeyonAuth::None; +#endif + } + l.relock(); + } + +#if VEYON_VERSION_MAJOR >= 5 + return m_selectedAuthenticationMethod; +#else + if( m_selectedAuthenticationMethod == m_authKeysUuid ) + { + return RfbVeyonAuth::KeyFile; + } + + if( m_selectedAuthenticationMethod == m_authLogonUuid ) + { + return RfbVeyonAuth::Logon; + } + + if( m_selectedAuthenticationMethod == m_authDummyUuid ) + { + return RfbVeyonAuth::None; + } + + vCritical() << "invalid authentication method selected"; + return RfbVeyonAuth::None; +#endif +} + + + +bool WebApiAuthenticationProxy::populateCredentials( QUuid authMethod, const QVariantMap& data ) +{ + AuthenticationCredentials credentials; + + if( authMethod == m_authKeysUuid ) + { + const auto keyName = data[QStringLiteral("keyname")].toString(); + const auto privateKey = CryptoCore::PrivateKey::fromPEM( data[QStringLiteral("keydata")].toString() ); + + if( keyName.isEmpty() || credentials.setPrivateKey( privateKey ) == false ) + { + return false; + } + + credentials.setAuthenticationKeyName( keyName ); + } + else if( authMethod == m_authLogonUuid ) + { + const auto username = data[QStringLiteral("username")].toString(); + const auto password = data[QStringLiteral("password")].toString(); + + if( username.isEmpty() || password.isEmpty() ) + { + return false; + } + + credentials.setLogonUsername( username ); + credentials.setLogonPassword( password.toUtf8() ); + } + + setCredentials( credentials ); + + lock(); + m_selectedAuthenticationMethod = authMethod; + unlock(); + + m_credentialsPopulated.wakeAll(); + + return true; +} diff --git a/plugins/webapi/WebApiAuthenticationProxy.h b/plugins/webapi/WebApiAuthenticationProxy.h new file mode 100644 index 0000000..ff9dd88 --- /dev/null +++ b/plugins/webapi/WebApiAuthenticationProxy.h @@ -0,0 +1,66 @@ +/* + * WebApiAuthenticationProxy.h - declaration of WebApiAuthenticationProxy class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "AuthenticationProxy.h" +#include "veyonconfig.h" + +class WebApiConfiguration; + +class WebApiAuthenticationProxy : public AuthenticationProxy +{ +public: + WebApiAuthenticationProxy( const WebApiConfiguration& configuration ); + +#if VEYON_VERSION_MAJOR < 5 + QVector authenticationMethods(); +#endif + + void setSelectedAuthenticationMethod( QUuid authMethod ); + + AuthenticationMethod initCredentials() override; + + bool populateCredentials( QUuid authMethod, const QVariantMap& data ); + + QUuid dummyAuthenticationMethod() const + { + return m_authDummyUuid; + } + +private: + int m_waitConditionWaitTime{0}; + QUuid m_selectedAuthenticationMethod; + + QWaitCondition m_credentialsPopulated; + + const QUuid m_authDummyUuid{QStringLiteral("0f4e6525-56e9-4660-8e4e-959e6b53f737")}; +#if VEYON_VERSION_MAJOR < 5 + const QUuid m_authKeysUuid{QStringLiteral("0c69b301-81b4-42d6-8fae-128cdd113314")}; + const QUuid m_authLogonUuid{QStringLiteral("63611f7c-b457-42c7-832e-67d0f9281085")}; +#endif +}; diff --git a/plugins/webapi/WebApiConfiguration.h b/plugins/webapi/WebApiConfiguration.h new file mode 100644 index 0000000..74fc86d --- /dev/null +++ b/plugins/webapi/WebApiConfiguration.h @@ -0,0 +1,41 @@ +/* + * WebApiConfiguration.h - configuration values for the HTTP API plugin + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonConfiguration.h" +#include "Configuration/Proxy.h" + +#define FOREACH_HTTP_API_CONFIG_PROPERTY(OP) \ + OP( WebApiConfiguration, m_configuration, bool, httpServerEnabled, setHttpServerEnabled, "HttpServerEnabled", "WebAPI", false, Configuration::Property::Flag::Advanced ) \ + OP( WebApiConfiguration, m_configuration, int, httpServerPort, setHttpServerPort, "HttpServerPort", "WebAPI", 11080, Configuration::Property::Flag::Advanced ) \ + OP( WebApiConfiguration, m_configuration, int, connectionLifetime, setConnectionLifetime, "ConnectionLifetime", "WebAPI", 3, Configuration::Property::Flag::Advanced ) \ + OP( WebApiConfiguration, m_configuration, int, connectionIdleTimeout, setConnectionIdleTimeout, "ConnectionIdleTimeout", "WebAPI", 60, Configuration::Property::Flag::Advanced ) \ + OP( WebApiConfiguration, m_configuration, int, connectionAuthenticationTimeout, setConnectionAuthenticationTimeout, "ConnectionAuthenticationTimeout", "WebAPI", 15, Configuration::Property::Flag::Advanced ) \ + OP( WebApiConfiguration, m_configuration, int, connectionLimit, setConnectionLimit, "ConnectionLimit", "WebAPI", 32, Configuration::Property::Flag::Advanced ) \ + OP( WebApiConfiguration, m_configuration, bool, httpsEnabled, setHttpsEnabled, "HttpsEnabled", "WebAPI", false, Configuration::Property::Flag::Advanced ) \ + OP( WebApiConfiguration, m_configuration, QString, tlsCertificateFile, setTlsCertificateFile, "TlsCertificateFile", "WebAPI", QString(), Configuration::Property::Flag::Advanced ) \ + OP( WebApiConfiguration, m_configuration, QString, tlsPrivateKeyFile, setTlsPrivateKeyFile, "TlsPrivateKeyFile", "WebAPI", QString(), Configuration::Property::Flag::Advanced ) \ + +DECLARE_CONFIG_PROXY(WebApiConfiguration, FOREACH_HTTP_API_CONFIG_PROPERTY) diff --git a/plugins/webapi/WebApiConfigurationPage.cpp b/plugins/webapi/WebApiConfigurationPage.cpp new file mode 100644 index 0000000..bb34796 --- /dev/null +++ b/plugins/webapi/WebApiConfigurationPage.cpp @@ -0,0 +1,75 @@ +/* + * WebApiConfigurationPage.cpp - implementation of WebApiConfigurationPage + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "FileSystemBrowser.h" +#include "WebApiConfiguration.h" +#include "WebApiConfigurationPage.h" +#include "Configuration/UiMapping.h" + +#include "ui_WebApiConfigurationPage.h" + +WebApiConfigurationPage::WebApiConfigurationPage( WebApiConfiguration& configuration, QWidget* parent ) : + ConfigurationPage( parent ), + ui( new Ui::WebApiConfigurationPage ), + m_configuration( configuration ) +{ + ui->setupUi(this); + + connect( ui->browseTlsCertificateFile, &QAbstractButton::clicked, this, [this]() { + FileSystemBrowser( FileSystemBrowser::ExistingFile ).exec( ui->tlsCertificateFile ); + } ); + + connect( ui->browseTlsPrivateKeyFile, &QAbstractButton::clicked, this, [this]() { + FileSystemBrowser( FileSystemBrowser::ExistingFile ).exec( ui->tlsPrivateKeyFile ); + } ); + + Configuration::UiMapping::setFlags( this, Configuration::Property::Flag::Advanced ); +} + + + +WebApiConfigurationPage::~WebApiConfigurationPage() +{ + delete ui; +} + + + +void WebApiConfigurationPage::resetWidgets() +{ + FOREACH_HTTP_API_CONFIG_PROPERTY(INIT_WIDGET_FROM_PROPERTY); +} + + + +void WebApiConfigurationPage::connectWidgetsToProperties() +{ + FOREACH_HTTP_API_CONFIG_PROPERTY(CONNECT_WIDGET_TO_PROPERTY) +} + + + +void WebApiConfigurationPage::applyConfiguration() +{ +} diff --git a/plugins/webapi/WebApiConfigurationPage.h b/plugins/webapi/WebApiConfigurationPage.h new file mode 100644 index 0000000..ae719e3 --- /dev/null +++ b/plugins/webapi/WebApiConfigurationPage.h @@ -0,0 +1,51 @@ +/* + * WebApiConfigurationPage.h - header for the WebApiConfigurationPage class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "ConfigurationPage.h" + +namespace Ui { +class WebApiConfigurationPage; +} + +class WebApiConfiguration; + +class WebApiConfigurationPage : public ConfigurationPage +{ + Q_OBJECT +public: + explicit WebApiConfigurationPage( WebApiConfiguration& configuration, QWidget* parent = nullptr ); + ~WebApiConfigurationPage() override; + + void resetWidgets() override; + void connectWidgetsToProperties() override; + void applyConfiguration() override; + +private: + Ui::WebApiConfigurationPage *ui; + + WebApiConfiguration& m_configuration; + +}; diff --git a/plugins/webapi/WebApiConfigurationPage.ui b/plugins/webapi/WebApiConfigurationPage.ui new file mode 100644 index 0000000..48c6f45 --- /dev/null +++ b/plugins/webapi/WebApiConfigurationPage.ui @@ -0,0 +1,249 @@ + + + WebApiConfigurationPage + + + Web API + + + + :/webapi/webapi.png:/webapi/webapi.png + + + + 0 + + + 0 + + + + + General + + + + + + Network port + + + + + + + Enable WebAPI server + + + + + + + 1 + + + 65535 + + + 10 + + + 11080 + + + + + + + + + + Connection settings + + + + + + Lifetime + + + + + + + h + + + 1 + + + 24 + + + 1 + + + 3 + + + 10 + + + + + + + s + + + 10 + + + 3600 + + + 10 + + + 60 + + + + + + + Idle timeout + + + + + + + s + + + 10 + + + 3600 + + + 10 + + + 15 + + + + + + + Authentication timeout + + + + + + + Maximum number of open connections + + + + + + + 1 + + + 1000 + + + 10 + + + 10 + + + + + + + + + + Connection encryption + + + + + + + + + + + + TLS certificate file + + + + + + + TLS private key file + + + + + + + ... + + + + :/core/document-open.png:/core/document-open.png + + + + + + + ... + + + + :/core/document-open.png:/core/document-open.png + + + + + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + diff --git a/plugins/webapi/WebApiConnection.cpp b/plugins/webapi/WebApiConnection.cpp new file mode 100644 index 0000000..7958f0b --- /dev/null +++ b/plugins/webapi/WebApiConnection.cpp @@ -0,0 +1,179 @@ +/* + * WebApiConnection.cpp - implementation of WebApiConnection class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "ComputerControlInterface.h" +#include "WebApiConnection.h" + + +WebApiConnection::WebApiConnection( const QString& hostAddress ) : + m_controlInterface( ComputerControlInterface::Pointer::create( Computer( {}, hostAddress, hostAddress ) ) ), + m_idleTimer( new QTimer ), + m_lifetimeTimer( new QTimer ) +{ +} + + + +WebApiConnection::~WebApiConnection() +{ + m_framebufferEncoder.waitForFinished(); + + m_idleTimer->deleteLater(); + m_lifetimeTimer->deleteLater(); + + m_controlInterface->stop(); +} + + + +QSize WebApiConnection::scaledFramebufferSize( int width, int height ) const +{ + const auto screenSize = m_controlInterface->screen().size(); + QSize scaledSize{}; + + if( width > 0 && height > 0 ) + { + scaledSize = { width, height }; + } + else if( width > 0 ) + { + scaledSize = screenSize.scaled( width, screenSize.height(), Qt::KeepAspectRatio ); + } + else if( height > 0 ) + { + scaledSize = screenSize.scaled( screenSize.width(), height, Qt::KeepAspectRatio ); + } + + return scaledSize; +} + + + +QByteArray WebApiConnection::encodedFramebufferData( QSize size, const QByteArray& format, int compression, int quality ) +{ + if( m_lastFramebufferRequestTimer.isValid() ) + { + m_lastFramebufferRequestInterval = ( m_lastFramebufferRequestInterval + m_lastFramebufferRequestTimer.restart() ) / 2; + } + else + { + m_lastFramebufferRequestTimer.start(); + } + + m_framebufferEncoder.waitForFinished(); + + if( format != m_imageFormat || + compression != m_imageCompression || + quality != m_imageQuality || + size != m_imageSize || + m_framebufferEncoder.isCanceled() || + m_framebufferEncoder.result().expired() ) + { + m_imageFormat = format; + m_imageCompression = compression; + m_imageQuality = quality; + m_imageSize = size; + + runFramebufferEncoder(); + + m_framebufferEncoder.waitForFinished(); + } + + const auto result = m_framebufferEncoder.result(); + + m_encodingError = result.errorString; + + const auto preencodeInterval = m_lastFramebufferRequestInterval - + m_framebufferEncodingTime * 125 / 100; + + if( preencodeInterval >= MinimumPreencodeInterval ) + { + QTimer::singleShot( preencodeInterval, + m_controlInterface.data(), [this]() { + lock(); + runFramebufferEncoder(); + unlock(); + } ); + } + else + { + m_framebufferEncoder = {}; + } + + + return result.imageData; +} + + + +void WebApiConnection::runFramebufferEncoder() +{ + m_framebufferEncoder = QtConcurrent::run( [this]() { + + QElapsedTimer encodingTimer; + encodingTimer.start(); + + EncodingResult result; + QBuffer dataBuffer( &result.imageData ); + dataBuffer.open( QBuffer::WriteOnly ); + QImageWriter imageWriter( &dataBuffer, m_imageFormat ); + + if( m_imageCompression > 0 ) + { + static constexpr auto QtPngCompressionLevelFactor = 11; + + imageWriter.setCompression( m_imageCompression * QtPngCompressionLevelFactor ); + } + + if( m_imageQuality > 0 ) + { + imageWriter.setQuality( m_imageQuality ); + } + + if( m_imageSize != m_controlInterface->scaledScreenSize() ) + { + m_controlInterface->setScaledScreenSize( m_imageSize ); + } + + const auto writeResult = imageWriter.write( + m_imageSize.isEmpty() ? + controlInterface()->screen() : + controlInterface()->scaledScreen() ); + + dataBuffer.close(); + + if( writeResult == false ) + { + result.imageData = {}; + result.errorString = imageWriter.errorString(); + } + + m_framebufferEncodingTime = ( m_framebufferEncodingTime + encodingTimer.elapsed() ) / 2; + + return result; + } ); +} diff --git a/plugins/webapi/WebApiConnection.h b/plugins/webapi/WebApiConnection.h new file mode 100644 index 0000000..3d563a3 --- /dev/null +++ b/plugins/webapi/WebApiConnection.h @@ -0,0 +1,109 @@ +/* + * WebApiConnection.h - declaration of WebApiConnection class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "ComputerControlInterface.h" + +class ComputerControlInterface; +class QTimer; + +class WebApiConnection +{ +public: + WebApiConnection( const QString& hostAddress ); + ~WebApiConnection(); + + ComputerControlInterface::Pointer controlInterface() const + { + return m_controlInterface; + } + + void lock() + { + m_controlInterface->lock(); + } + + void unlock() + { + m_controlInterface->unlock(); + } + + QTimer* idleTimer() const + { + return m_idleTimer; + } + + QTimer* lifetimeTimer() const + { + return m_lifetimeTimer; + } + + QSize scaledFramebufferSize( int width, int height ) const; + + QByteArray encodedFramebufferData( QSize size, const QByteArray& format, int compression, int quality ); + + const QString& framebufferEncodingError() const + { + return m_encodingError; + } + +private: + void runFramebufferEncoder(); + + ComputerControlInterface::Pointer m_controlInterface; + QTimer* m_idleTimer{nullptr}; + QTimer* m_lifetimeTimer{nullptr}; + + QByteArray m_imageFormat{}; + int m_imageQuality{0}; + int m_imageCompression{0}; + QSize m_imageSize{}; + + struct EncodingResult { + QByteArray imageData{}; + QString errorString{}; + qint64 timestamp{QDateTime::currentMSecsSinceEpoch()}; + + bool expired() const + { + static constexpr auto MaxAge = 1000; + + return QDateTime::currentMSecsSinceEpoch() - timestamp > MaxAge; + } + }; + + static constexpr auto MinimumPreencodeInterval = 10; + + QFuture m_framebufferEncoder; + QString m_encodingError; + + QElapsedTimer m_lastFramebufferRequestTimer; + qint64 m_lastFramebufferRequestInterval{0}; + QAtomicInteger m_framebufferEncodingTime{0}; + +}; diff --git a/plugins/webapi/WebApiController.cpp b/plugins/webapi/WebApiController.cpp new file mode 100644 index 0000000..9e2d720 --- /dev/null +++ b/plugins/webapi/WebApiController.cpp @@ -0,0 +1,491 @@ +/* + * WebApiController.cpp - implementation of WebApiController class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include "ComputerControlInterface.h" +#include "PlatformSessionFunctions.h" +#include "WebApiAuthenticationProxy.h" +#include "WebApiConfiguration.h" +#include "WebApiController.h" + + +static void runInMainThread( const std::function& functor ) +{ + if( QThread::currentThread() != VeyonCore::instance()->thread() ) + { + QMetaObject::invokeMethod( VeyonCore::instance(), functor, Qt::BlockingQueuedConnection ); + } + else + { + functor(); + } +} + + +template +static T runInMainThread( const std::function& functor ) +{ + if( QThread::currentThread() != VeyonCore::instance()->thread() ) + { + T retval{}; + QMetaObject::invokeMethod( VeyonCore::instance(), functor, Qt::BlockingQueuedConnection, &retval ); + return retval; + } + + return functor(); +} + + +static QString uuidToString( QUuid uuid ) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + return uuid.toString( QUuid::WithoutBraces ); +#else + return uuid.toString().remove( QLatin1Char('{') ).remove( QLatin1Char('}') ); +#endif +} + + +WebApiController::WebApiController( const WebApiConfiguration& configuration, QObject* parent ) : + QObject( parent ), + m_configuration( configuration ), + m_connectionsLock( QReadWriteLock::Recursive ) +{ +} + + + +WebApiController::~WebApiController() +{ + QWriteLocker connectionsWriteLocker{ &m_connectionsLock }; + + m_connections.clear(); +} + + + + +WebApiController::Response WebApiController::getAuthenticationMethods( const Request& request, const QString& host ) +{ + Q_UNUSED(request) + + WebApiConnection connection( host.isEmpty() ? QStringLiteral("localhost") : host ); + + const auto proxy = new WebApiAuthenticationProxy( m_configuration ); + proxy->populateCredentials( proxy->dummyAuthenticationMethod(), {} ); + + connection.controlInterface()->start( {}, ComputerControlInterface::UpdateMode::Monitoring, proxy ); + + if( proxy->waitForAuthenticationMethods( + m_configuration.connectionAuthenticationTimeout() * MillisecondsPerSecond ) == false ) + { + vWarning() << "waiting for authentication methods timed out"; + return Error::ConnectionTimedOut; + } + + const auto authMethodUuids = proxy->authenticationMethods(); + + QVariantList methods; // clazy:exclude=inefficient-qlist + methods.reserve( authMethodUuids.size() ); + + for( const auto& authMethodUuid : authMethodUuids ) + { + methods.append( uuidToString( authMethodUuid ) ); + } + + return QVariantMap{ { k2s(Key::Methods), methods } }; +} + + + +WebApiController::Response WebApiController::performAuthentication( const Request& request, const QString& host ) +{ + QReadLocker connectionsReadLocker{&m_connectionsLock}; + + if( m_connections.size() >= m_configuration.connectionLimit() ) + { + return Error::ConnectionLimitReached; + } + + const auto methodUuid = QUuid( request.data[k2s(Key::Method)].toString() ); + if( methodUuid.isNull() ) + { + return Error::InvalidData; + } + + auto uuid = QUuid::createUuid(); + while( m_connections.contains( uuid ) ) + { + uuid = QUuid::createUuid(); + } + + connectionsReadLocker.unlock(); + + auto proxy = new WebApiAuthenticationProxy( m_configuration ); + + // create connection (including timer resources) in main thread + auto connection = runInMainThread( [host, proxy]() { + auto connection = new WebApiConnection{host.isEmpty() ? QStringLiteral("localhost") : host}; + connection->controlInterface()->start( {}, ComputerControlInterface::UpdateMode::Monitoring, proxy ); + + // make shared pointer destroy the connection in main thread again + return WebApiConnectionPointer{ connection, + []( WebApiConnection* c ) { runInMainThread( [c] { delete c; } ); } + }; + } ); + + const auto authTimeout = m_configuration.connectionAuthenticationTimeout() * MillisecondsPerSecond; + if( proxy->waitForAuthenticationMethods( authTimeout ) == false ) + { + vWarning() << "waiting for authentication methods timed out"; + return Error::ConnectionTimedOut; + } + + if( proxy->authenticationMethods().contains( methodUuid ) == false ) + { + return Error::AuthenticationMethodNotAvailable; + } + + if( proxy->populateCredentials( methodUuid, request.data[k2s(Key::Credentials)].toMap() ) == false ) + { + return Error::InvalidCredentials; + } + + QEventLoop eventLoop; + QTimer authenticationTimeoutTimer; + + static constexpr auto ResultAuthSucceeded = 0; + static constexpr auto ResultAuthFailed = 1; + static constexpr auto ResultAuthTimedOut = 2; + + connect( &authenticationTimeoutTimer, &QTimer::timeout, &eventLoop, + [&eventLoop]() { eventLoop.exit(ResultAuthTimedOut); } ); + connect( connection->controlInterface().data(), &ComputerControlInterface::stateChanged, &eventLoop, + [&connection, &eventLoop]() { // clazy:exclude=lambda-in-connect + switch( connection->controlInterface()->state() ) + { + case ComputerControlInterface::State::AuthenticationFailed: + eventLoop.exit( ResultAuthFailed ); + break; + case ComputerControlInterface::State::Connected: + eventLoop.exit( ResultAuthSucceeded ); + default: + break; + } + } ); + + authenticationTimeoutTimer.start( authTimeout ); + + const auto result = eventLoop.exec() == ResultAuthSucceeded; + + if( result ) + { + LockingConnectionPointer connectionLock{connection}; + + m_connectionsLock.lockForWrite(); + m_connections[uuid] = connection; + m_connectionsLock.unlock(); + + const auto idleTimer = connection->idleTimer(); + const auto lifetimeTimer = connection->lifetimeTimer(); + + connect( idleTimer, &QTimer::timeout, this, [this, uuid]() { removeConnection( uuid ); } ); + connect( lifetimeTimer, &QTimer::timeout, this, [this, uuid]() { removeConnection( uuid ); } ); + + const auto connectionIdleTimeout = m_configuration.connectionIdleTimeout() * MillisecondsPerSecond; + const auto connectionLifetime = m_configuration.connectionLifetime() * MillisecondsPerHour; + + runInMainThread( [=] { + idleTimer->start( connectionIdleTimeout ); + lifetimeTimer->start( connectionLifetime ); + } ); + + connect( connection->controlInterface().data(), &ComputerControlInterface::featureMessageReceived, this, + [this]( const FeatureMessage& featureMessage, + const ComputerControlInterface::Pointer& computerControlInterface ) { + computerControlInterface->lock(); + m_featureManager.handleFeatureMessage( computerControlInterface, featureMessage ); + computerControlInterface->unlock(); + } ); + + return QVariantMap{ + { connectionUidHeaderFieldName().toLower(), uuidToString( uuid ) }, + { k2s(Key::ValidUntil), QDateTime::currentSecsSinceEpoch() + connectionLifetime / MillisecondsPerSecond } + }; + } + + return Error::AuthenticationFailed; +} + + + +WebApiController::Response WebApiController::closeConnection( const Request& request, const QString& host ) +{ + Q_UNUSED(host) + + Response checkResponse{}; + if( ( checkResponse = checkConnection( request ) ).error != Error::NoError ) + { + return checkResponse; + } + + removeConnection( QUuid( request.headers[connectionUidHeaderFieldName()].toString() ) ); + + return {}; +} + + + +WebApiController::Response WebApiController::getFramebuffer( const Request& request ) +{ + Response checkResponse{}; + if( ( checkResponse = checkConnection( request ) ).error != Error::NoError ) + { + return checkResponse; + } + + const auto connection = lookupConnection( request ); + + if( connection->controlInterface()->hasValidFramebuffer() == false ) + { + return Error::FramebufferNotAvailable; + } + + const auto width = request.data[k2s(Key::Width)].toInt(); + const auto height = request.data[k2s(Key::Height)].toInt(); + const auto size = connection->scaledFramebufferSize( width, height ); + + const auto compression = request.data[k2s(Key::Compression)].toString().toInt(); + const auto quality = request.data[k2s(Key::Quality)].toString().toInt(); + + auto format = request.data[k2s(Key::Format)].toString().toUtf8(); + if( format.isEmpty() ) + { + format = QByteArrayLiteral("png"); + } + + if( QImageWriter::supportedImageFormats().contains( format ) == false ) + { + return Error::UnsupportedImageFormat; + } + + const auto imageData = connection->encodedFramebufferData( size, format, compression, quality ); + + if( imageData.isNull() ) + { + return { Error::FramebufferEncodingError, connection->framebufferEncodingError() }; + } + + return imageData; +} + + + +WebApiController::Response WebApiController::listFeatures( const Request& request ) +{ + Response checkResponse{}; + if( ( checkResponse = checkConnection( request ) ).error != Error::NoError ) + { + return checkResponse; + } + + const auto& features = m_featureManager.features(); // clazy:exclude=inefficient-qlist + const auto activeFeatures = lookupConnection( request )->controlInterface()->activeFeatures(); + + QVariantList featureList; // clazy:exclude=inefficient-qlist + featureList.reserve( features.size() ); + + for( const auto& feature : features ) + { + QVariantMap featureObject{ { k2s(Key::Name), feature.name() }, + { k2s(Key::Uid), uuidToString(feature.uid()) }, + { k2s(Key::ParentUid), uuidToString(feature.parentUid()) }, + { k2s(Key::Active), activeFeatures.contains(feature.uid()) } }; + featureList.append( featureObject ); + } + + return featureList; +} + + + +WebApiController::Response WebApiController::setFeatureStatus( const Request& request, const QString& feature ) +{ + Response checkResponse{}; + if( ( checkResponse = checkConnection( request ) ).error != Error::NoError || + ( checkResponse = checkFeature( feature ) ).error != Error::NoError ) + { + return checkResponse; + } + + if( request.data.contains( k2s(Key::Active) ) == false ) + { + return Error::InvalidData; + } + + const auto connection = lookupConnection( request ); + + const auto operation = request.data[k2s(Key::Active)].toBool() ? FeatureProviderInterface::Operation::Start + : FeatureProviderInterface::Operation::Stop; + const auto arguments = request.data[k2s(Key::Arguments)].toMap(); + + m_featureManager.controlFeature( feature, operation, arguments, { connection->controlInterface() } ); + + return {}; +} + + + +WebApiController::Response WebApiController::getFeatureStatus( const Request& request, const QString& feature ) +{ + Response checkResponse{}; + if( ( checkResponse = checkConnection( request ) ).error != Error::NoError ) + { + return checkResponse; + } + + const auto connection = lookupConnection( request ); + const auto controlInterface = connection->controlInterface(); + + const auto result = controlInterface->activeFeatures().contains( feature ); + + return QVariantMap{ { k2s(Key::Active), result } }; +} + + + +WebApiController::Response WebApiController::getUserInformation( const Request& request ) +{ + Response checkResponse{}; + if( ( checkResponse = checkConnection( request ) ).error != Error::NoError ) + { + return checkResponse; + } + + const auto connection = lookupConnection( request ); + const auto controlInterface = connection->controlInterface(); + + const auto& userLoginName = controlInterface->userLoginName(); + auto userFullName = controlInterface->userFullName(); + auto sessionId = controlInterface->userSessionId(); + if( userLoginName.isEmpty() ) + { + userFullName.clear(); + sessionId = PlatformSessionFunctions::InvalidSessionId; + } + + return QVariantMap{ { + { k2s(Key::Login), userLoginName }, + { k2s(Key::FullName), userFullName }, + { k2s(Key::Session), sessionId } + } }; +} + + + +QString WebApiController::errorString( WebApiController::Error error ) +{ + switch( error ) + { + case Error::NoError: return {}; + case Error::InvalidData: return QStringLiteral("Invalid data"); + case Error::InvalidConnection: return QStringLiteral("Invalid connection"); + case Error::InvalidFeature: return QStringLiteral("Invalid feature"); + case Error::AuthenticationMethodNotAvailable: return QStringLiteral("Authentication method not offered by server"); + case Error::InvalidCredentials: return QStringLiteral("Invalid or incomplete credentials"); + case Error::AuthenticationFailed: return QStringLiteral("Authentication failed"); + case Error::ConnectionLimitReached: return QStringLiteral("Limit for maximum number of connections reached"); + case Error::ConnectionTimedOut: return QStringLiteral("Connection timed out"); + case Error::UnsupportedImageFormat: return QStringLiteral("Unsupported image format"); + case Error::FramebufferNotAvailable: return QStringLiteral("Framebuffer not yet available"); + case Error::FramebufferEncodingError: return QStringLiteral("Framebuffer encoding error"); + } + + return {}; +} + + + +void WebApiController::removeConnection( QUuid connectionUuid ) +{ + // destroy connection in main thread + runInMainThread( [=] { + QWriteLocker connectionsWriteLocker{ &m_connectionsLock }; + + m_connections.remove( connectionUuid ); + } ); +} + + + +WebApiController::LockingConnectionPointer WebApiController::lookupConnection( const Request& request ) +{ + QReadLocker connectionsReadLocker{&m_connectionsLock}; + + return m_connections.value( request.headers[connectionUidHeaderFieldName()].toUuid() ); +} + + + +WebApiController::Response WebApiController::checkConnection( const Request& request ) +{ + const auto connectionUuid = QUuid( request.headers[connectionUidHeaderFieldName()].toString() ); + + return runInMainThread( [=]() -> WebApiController::Response { + m_connectionsLock.lockForRead(); + if( connectionUuid.isNull() || m_connections.contains( connectionUuid ) == false ) + { + m_connectionsLock.unlock(); + return Error::InvalidConnection; + } + + const auto connection = qAsConst(m_connections)[connectionUuid]; + m_connectionsLock.unlock(); + + connection->lock(); + + const auto idleTimer = connection->idleTimer(); + idleTimer->stop(); + idleTimer->start(); + + connection->unlock(); + + return {}; + } ); +} + + + +WebApiController::Response WebApiController::checkFeature( const QString& featureUid ) +{ + if( m_featureManager.feature( featureUid ).isValid() == false ) + { + return Error::InvalidFeature; + } + + return {}; +} diff --git a/plugins/webapi/WebApiController.h b/plugins/webapi/WebApiController.h new file mode 100644 index 0000000..b809687 --- /dev/null +++ b/plugins/webapi/WebApiController.h @@ -0,0 +1,148 @@ +/* + * WebApiController.h - declaration of WebApiController class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "EnumHelper.h" +#include "FeatureManager.h" +#include "LockingPointer.h" +#include "WebApiConnection.h" + +class WebApiConfiguration; + +class WebApiController : public QObject +{ + Q_OBJECT +public: + enum class Key + { + State, + Method, + Methods, + Credentials, + Format, + Compression, + Quality, + Width, + Height, + Feature, + Name, + Uid, + ParentUid, + Active, + Arguments, + Login, + FullName, + Session, + ValidUntil + }; + Q_ENUM(Key) + + enum class Error + { + NoError, + InvalidData, + InvalidConnection, + InvalidFeature, + InvalidCredentials, + AuthenticationMethodNotAvailable, + AuthenticationFailed, + ConnectionLimitReached, + ConnectionTimedOut, + UnsupportedImageFormat, + FramebufferNotAvailable, + FramebufferEncodingError, + }; + + struct Request + { + Request( const QVariantMap& h = {}, const QVariantMap& d = {} ) : headers(h), data(d) { } + QVariantMap headers{}; + QVariantMap data{}; + }; + + struct Response + { + Response( const QVariantMap& md = {} ) : mapData(md) { } + Response( const QVariantList& ad ) : arrayData(ad) { } + Response( const QByteArray& bd ) : binaryData(bd) { } + Response( Error e, const QString& ed = {} ) : error(e), errorDetails(ed) { } + QVariantList arrayData{}; + QVariantMap mapData{}; + QByteArray binaryData{}; + Error error{Error::NoError}; + QString errorDetails{}; + }; + + explicit WebApiController( const WebApiConfiguration& configuration, QObject* parent = nullptr ); + ~WebApiController() override; + + Response getAuthenticationMethods( const Request& request, const QString& host ); + Response performAuthentication( const Request& request, const QString& host ); + Response closeConnection( const Request& request, const QString& host ); + + Response getFramebuffer( const Request& request ); + + Response listFeatures( const Request& request ); + Response setFeatureStatus( const Request& request, const QString& feature ); + Response getFeatureStatus( const Request& request, const QString& feature ); + + Response getUserInformation( const Request& request ); + + static QString errorString( Error error ); + +private: + void removeConnection( QUuid connectionUuid ); + + static constexpr auto MillisecondsPerSecond = 1000; + static constexpr auto MillisecondsPerHour = MillisecondsPerSecond * 60 * 60; + + static QString k2s( Key key ) + { + return EnumHelper::toCamelCaseString( key ); + } + + static QString connectionUidHeaderFieldName() + { + return QStringLiteral("Connection-Uid"); + } + + using WebApiConnectionPointer = QSharedPointer; + using LockingConnectionPointer = LockingPointer; + + LockingConnectionPointer lookupConnection( const Request& request ); + + using CheckFunction = std::function; + + Response checkConnection( const Request& request ); + Response checkFeature( const QString& featureUid ); + + const WebApiConfiguration& m_configuration; + const FeatureManager m_featureManager; + QMap m_connections{}; + QReadWriteLock m_connectionsLock; + +}; diff --git a/plugins/webapi/WebApiHttpServer.cpp b/plugins/webapi/WebApiHttpServer.cpp new file mode 100644 index 0000000..9dac992 --- /dev/null +++ b/plugins/webapi/WebApiHttpServer.cpp @@ -0,0 +1,320 @@ +/* + * WebApiHttpServer.cpp - implementation of WebApiHttpServer class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include "Filesystem.h" +#include "WebApiHttpServer.h" +#include "WebApiConfiguration.h" +#include "WebApiController.h" + + +static QHttpServerResponse convertResponse( const WebApiController::Response& response ) +{ + if( response.error == WebApiController::Error::NoError ) + { + if( response.binaryData.isEmpty() == false ) + { + return QHttpServerResponse{ response.binaryData }; + } + + if( response.arrayData.isEmpty() == false ) + { + return { QJsonArray::fromVariantList(response.arrayData) }; + } + + return { QJsonObject::fromVariantMap(response.mapData) }; + } + + const auto statusCode = [&response]() { + switch( response.error ) + { + case WebApiController::Error::NoError: return QHttpServerResponse::StatusCode::Ok; + case WebApiController::Error::InvalidData: return QHttpServerResponse::StatusCode::BadRequest; + case WebApiController::Error::InvalidConnection: return QHttpServerResponse::StatusCode::Unauthorized; + case WebApiController::Error::InvalidFeature: return QHttpServerResponse::StatusCode::BadRequest; + case WebApiController::Error::InvalidCredentials: return QHttpServerResponse::StatusCode::BadRequest; + case WebApiController::Error::AuthenticationMethodNotAvailable: return QHttpServerResponse::StatusCode::BadRequest; + case WebApiController::Error::AuthenticationFailed: return QHttpServerResponse::StatusCode::Unauthorized; + case WebApiController::Error::ConnectionLimitReached: return QHttpServerResponse::StatusCode::TooManyRequests; + case WebApiController::Error::ConnectionTimedOut: return QHttpServerResponse::StatusCode::RequestTimeout; + case WebApiController::Error::UnsupportedImageFormat: return QHttpServerResponse::StatusCode::ServiceUnavailable; + case WebApiController::Error::FramebufferNotAvailable: return QHttpServerResponse::StatusCode::ServiceUnavailable; + case WebApiController::Error::FramebufferEncodingError: return QHttpServerResponse::StatusCode::InternalServerError; + } + return QHttpServerResponse::StatusCode::BadRequest; + }(); + + QJsonObject errorObject{ + { QStringLiteral("code"), int(response.error) }, + { QStringLiteral("message"), WebApiController::errorString( response.error ) } + }; + + if( response.errorDetails.isEmpty() == false ) + { + errorObject[QStringLiteral("details")] = response.errorDetails; + } + + return { + QByteArrayLiteral("application/json"), + QJsonDocument{ QJsonObject{ { QStringLiteral("error"), errorObject } } }.toJson( QJsonDocument::Compact ), + statusCode + }; +} + + + +WebApiHttpServer::WebApiHttpServer( const WebApiConfiguration& configuration, QObject* parent ) : + QObject( parent ), + m_configuration( configuration ), + m_controller( new WebApiController( configuration, this ) ), + m_server( new QHttpServer( this ) ) +{ + m_threadPool.setMaxThreadCount( m_configuration.connectionLimit() ); +} + + + +WebApiHttpServer::~WebApiHttpServer() +{ + delete m_server; + + delete m_controller; +} + + + +template<> +QVariantMap WebApiHttpServer::dataFromRequest( const QHttpServerRequest& request ) +{ + QVariantMap data; + + const auto bodyData = QJsonDocument::fromJson( request.body() ).object(); + for( auto it = bodyData.constBegin(), end = bodyData.constEnd(); it != end; ++it ) + { + data[it.key()] = it.value(); + } + + vDebug() << "POST" << request.url() << request.headers() << data; + + return data; +} + + + +template<> +QVariantMap WebApiHttpServer::dataFromRequest( const QHttpServerRequest& request ) +{ + QVariantMap data; + + const auto bodyData = QJsonDocument::fromJson( request.body() ).object(); + for( auto it = bodyData.constBegin(), end = bodyData.constEnd(); it != end; ++it ) + { + data[it.key()] = it.value(); + } + + vDebug() << "PUT" << request.url() << request.headers() << data; + + return data; +} + + + +template<> +QVariantMap WebApiHttpServer::dataFromRequest( const QHttpServerRequest& request ) +{ + QVariantMap data; + + const auto items = request.query().queryItems(); // clazy:exclude=inefficient-qlist + for( const auto& item : items ) + { + data[item.first] = item.second; + } + + vDebug() << "GET" << request.url() << request.headers(); + + return data; +} + + + +template<> +QVariantMap WebApiHttpServer::dataFromRequest( const QHttpServerRequest& request ) +{ + QVariantMap data; + + const auto items = request.query().queryItems(); // clazy:exclude=inefficient-qlist + for( const auto& item : items ) + { + data[item.first] = item.second; + } + + vDebug() << "DELETE" << request.url() << request.headers(); + + return data; +} + + + +template +bool WebApiHttpServer::addRoute( const QString& path, + WebApiController::Response(WebApiController::* controllerMethod)( const WebApiController::Request& request, + Args... args ) ) +{ + return m_server->route( QStringLiteral("/api/v1/%1").arg(path), []() + { + switch(M) + { + case Method::Get: return QHttpServerRequest::Method::Get; + case Method::Post: return QHttpServerRequest::Method::Post; + case Method::Put: return QHttpServerRequest::Method::Put; + case Method::Delete: return QHttpServerRequest::Method::Delete; + } + }(), + [=]( Args... args, const QHttpServerRequest& request ) -> QHttpServerFutureResponse + { + const auto headers = request.headers(); + const auto data = dataFromRequest( request ); + + return QtConcurrent::run( &m_threadPool, [=] { + return convertResponse( (m_controller->*controllerMethod)( + { headers, data }, + std::forward(args)... ) ); + } ); + } ); +} + + + +bool WebApiHttpServer::start() +{ + if( m_server == nullptr || m_controller == nullptr ) + { + return false; + } + + if( m_configuration.httpsEnabled() && + setupTls() == false ) + { + return false; + } + + if( m_server->listen( QHostAddress::Any, m_configuration.httpServerPort() ) != m_configuration.httpServerPort() ) + { + vCritical() << "can't listen at port" << m_configuration.httpServerPort(); + return false; + } + + auto success = true; + + success &= addRoute( QStringLiteral("authentication/"), &WebApiController::getAuthenticationMethods ); + success &= addRoute( QStringLiteral("authentication/"), &WebApiController::performAuthentication ); + success &= addRoute( QStringLiteral("authentication/"), &WebApiController::closeConnection ); + success &= addRoute( QStringLiteral("framebuffer"), &WebApiController::getFramebuffer ); + success &= addRoute( QStringLiteral("feature"), &WebApiController::listFeatures ); + success &= addRoute( QStringLiteral("feature/"), &WebApiController::getFeatureStatus ); + success &= addRoute( QStringLiteral("feature/"), &WebApiController::setFeatureStatus ); + success &= addRoute( QStringLiteral("user"), &WebApiController::getUserInformation ); + + success &= m_server->route( QStringLiteral(".*"), [] { + return QHttpServerResponse{ + QByteArrayLiteral("text/plain"), + QStringLiteral("Invalid command or non-matching HTTP method").toUtf8(), + QHttpServerResponse::StatusCode::NotFound + }; + } ); + + vInfo() << "listening at port" << m_configuration.httpServerPort(); + + return success; +} + + + +bool WebApiHttpServer::setupTls() +{ + QFile certFile( VeyonCore::filesystem().expandPath( m_configuration.tlsCertificateFile() ) ); + + if( certFile.exists() == false ) + { + vCritical() << "TLS certificate file" << certFile.fileName() << "does not exist"; + return false; + } + + if( certFile.open( QFile::ReadOnly ) == false ) + { + vCritical() << "TLS certificate file" << certFile.fileName() << "is not readable"; + return false; + } + + QSslCertificate certificate( certFile.readAll() ); + if( certificate.isNull() ) + { + vCritical() << m_configuration.tlsCertificateFile() << "does not contain a valid TLS certificate"; + return false; + } + + QFile privateKeyFile( VeyonCore::filesystem().expandPath( m_configuration.tlsPrivateKeyFile() ) ); + + if( privateKeyFile.exists() == false ) + { + vCritical() << "TLS private key file" << privateKeyFile.fileName() << "does not exist"; + return false; + } + + if( privateKeyFile.open( QFile::ReadOnly ) == false ) + { + vCritical() << "TLS private key file" << privateKeyFile.fileName() << "is not readable"; + return false; + } + + const auto privateKeyFileData = privateKeyFile.readAll(); + + QSslKey privateKey; + for( auto algorithm : { QSsl::KeyAlgorithm::Rsa, QSsl::KeyAlgorithm::Ec +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) + , QSsl::KeyAlgorithm::Dh +#endif + } ) + { + QSslKey currentPrivateKey( privateKeyFileData, algorithm ); + if( currentPrivateKey.isNull() == false ) + { + privateKey = currentPrivateKey; + break; + } + } + + if( privateKey.isNull() ) + { + vCritical() << m_configuration.tlsCertificateFile() << "does contains an invalid or unsupported TLS private key"; + return false; + } + + m_server->sslSetup( certificate, privateKey, QSsl::TlsV1_3OrLater ); + + return true; +} diff --git a/plugins/webapi/WebApiHttpServer.h b/plugins/webapi/WebApiHttpServer.h new file mode 100644 index 0000000..d70273f --- /dev/null +++ b/plugins/webapi/WebApiHttpServer.h @@ -0,0 +1,69 @@ +/* + * WebApiHttpServer.h - declaration of WebApiHttpServer class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "WebApiController.h" + +class QHttpServer; +class QHttpServerRequest; +class WebApiConfiguration; + +class WebApiHttpServer : public QObject +{ + Q_OBJECT +public: + explicit WebApiHttpServer( const WebApiConfiguration& configuration, QObject* parent = nullptr ); + ~WebApiHttpServer() override; + + bool start(); + +private: + enum class Method { + Get, + Post, + Put, + Delete + }; + + bool setupTls(); + + template + static QVariantMap dataFromRequest( const QHttpServerRequest& request ); + + template + bool addRoute( const QString& path, + WebApiController::Response(WebApiController::* controllerMethod)( const WebApiController::Request& request, + Args... args ) ); + + const WebApiConfiguration& m_configuration; + + QThreadPool m_threadPool{this}; + + WebApiController* m_controller{nullptr}; + QHttpServer* m_server{nullptr}; + +}; diff --git a/plugins/webapi/WebApiPlugin.cpp b/plugins/webapi/WebApiPlugin.cpp new file mode 100644 index 0000000..f3f2634 --- /dev/null +++ b/plugins/webapi/WebApiPlugin.cpp @@ -0,0 +1,98 @@ +/* + * WebApiPlugin.cpp - implementation of WebApiPlugin class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "CommandLineIO.h" +#include "WebApiConfigurationPage.h" +#include "WebApiHttpServer.h" +#include "WebApiPlugin.h" + + +WebApiPlugin::WebApiPlugin( QObject* parent ) : + QObject( parent ), + m_configuration( &VeyonCore::config() ), + m_httpServerThread( this ), + m_commands( { + { QStringLiteral("runserver"), tr( "Run WebAPI server" ) } + } ) +{ + if( VeyonCore::component() == VeyonCore::Component::Service && + m_configuration.httpServerEnabled() ) + { + connect( VeyonCore::instance(), &VeyonCore::initialized, + this, &WebApiPlugin::startHttpServerThread ); + } +} + + + +WebApiPlugin::~WebApiPlugin() +{ + if( m_httpServerThread.isRunning() ) + { + m_httpServerThread.quit(); + m_httpServerThread.wait( ServerThreadTerminationTimeout ); + } +} + + + +ConfigurationPage* WebApiPlugin::createConfigurationPage() +{ + return new WebApiConfigurationPage( m_configuration ); +} + + + +CommandLinePluginInterface::RunResult WebApiPlugin::handle_runserver( const QStringList& arguments ) +{ + Q_UNUSED(arguments) + + m_httpServer = new WebApiHttpServer{ m_configuration }; + + if( m_httpServer->start() == false ) + { + CommandLineIO::error( tr( "Failed to start WebAPI server at port %1" ).arg( m_configuration.httpServerPort() ) ); + + return Failed; + } + + CommandLineIO::info( tr("WebAPI server running at port %1").arg( m_configuration.httpServerPort() ) ); + + return VeyonCore::instance()->exec() == 0 ? Successful : Failed; +} + + + +void WebApiPlugin::startHttpServerThread() +{ + m_httpServer = new WebApiHttpServer{ m_configuration }; + m_httpServer->moveToThread( &m_httpServerThread ); + + connect( &m_httpServerThread, &QThread::started, m_httpServer, &WebApiHttpServer::start ); + + m_httpServerThread.start(); +} + + +IMPLEMENT_CONFIG_PROXY(WebApiConfiguration) diff --git a/plugins/webapi/WebApiPlugin.h b/plugins/webapi/WebApiPlugin.h new file mode 100644 index 0000000..c02e301 --- /dev/null +++ b/plugins/webapi/WebApiPlugin.h @@ -0,0 +1,113 @@ +/* + * WebApiPlugin.h - declaration of WebApiPlugin class + * + * Copyright (c) 2020-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "CommandLinePluginInterface.h" +#include "ConfigurationPagePluginInterface.h" +#include "WebApiConfiguration.h" + +class WebApiHttpServer; + +class WebApiPlugin : public QObject, PluginInterface, CommandLinePluginInterface, ConfigurationPagePluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.veyon.Veyon.Plugins.WebApi") + Q_INTERFACES(PluginInterface + CommandLinePluginInterface + ConfigurationPagePluginInterface) +public: + explicit WebApiPlugin( QObject* parent = nullptr ); + ~WebApiPlugin() override; + + Plugin::Uid uid() const override + { + return QStringLiteral("e11bee03-b99c-465c-bf90-7e5339b83f6b"); + } + + QVersionNumber version() const override + { + return QVersionNumber( 1, 0 ); + } + + QString name() const override + { + return QStringLiteral("WebAPI"); + } + + QString description() const override + { + return tr( "Provide access to a computer via HTTP" ); + } + + QString vendor() const override + { + return QStringLiteral("Veyon Community"); + } + + QString copyright() const override + { + return QStringLiteral("Tobias Junghans"); + } + + QString commandLineModuleName() const override + { + return QStringLiteral( "webapi" ); + } + + QString commandLineModuleHelp() const override + { + return tr( "Commands for running the WebAPI server" ); + } + + QStringList commands() const override + { + return m_commands.keys(); + } + + QString commandHelp( const QString& command ) const override + { + return m_commands.value( command ); + } + + ConfigurationPage* createConfigurationPage() override; + +public Q_SLOTS: + CommandLinePluginInterface::RunResult handle_runserver( const QStringList& arguments ); + +private: + void startHttpServerThread(); + + static constexpr auto ServerThreadTerminationTimeout = 1000; + + WebApiConfiguration m_configuration; + + QThread m_httpServerThread; + WebApiHttpServer* m_httpServer{nullptr}; + + const QMap m_commands; + +}; diff --git a/plugins/webapi/python/LICENSE b/plugins/webapi/python/LICENSE new file mode 100644 index 0000000..1168b48 --- /dev/null +++ b/plugins/webapi/python/LICENSE @@ -0,0 +1,352 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + +------------------------------------------------------------------------- + +In addition, as a special exception, Tobias Junghans gives permission to link the +code of its release of Veyon with the OpenSSL project's "OpenSSL" library (or +modified versions of the "OpenSSL" library that use the same license as the +original version), and distribute the linked executables. + +You must comply with the GNU General Public License version 2 in all respects +for all of the code used other than the "OpenSSL" code. If you modify this +file, you may extend this exception to your version of the file, but you are +not obligated to do so. If you do not wish to do so, delete this exception +statement from your version of this file. diff --git a/plugins/webapi/python/README.md b/plugins/webapi/python/README.md new file mode 100644 index 0000000..f99ce71 --- /dev/null +++ b/plugins/webapi/python/README.md @@ -0,0 +1,4 @@ +# veyon package + +The veyon Python package implements a Veyon WebAPI client. + diff --git a/plugins/webapi/python/setup.py b/plugins/webapi/python/setup.py new file mode 100644 index 0000000..e20c87d --- /dev/null +++ b/plugins/webapi/python/setup.py @@ -0,0 +1,22 @@ +import setuptools + +with open("README.md", "r") as fh: + long_description = fh.read() + +setuptools.setup( + name="veyon", # Replace with your own username + version="0.1.0", + author="Tobias Junghans", + author_email="tobydox@veyon.io", + description="Veyon WebAPI client", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/veyon/veyon", + packages=setuptools.find_packages(), + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", + "Operating System :: OS Independent", + ], + python_requires='>=3.2', +) diff --git a/plugins/webapi/python/tests/test_veyon_webapi.py b/plugins/webapi/python/tests/test_veyon_webapi.py new file mode 100644 index 0000000..a03a306 --- /dev/null +++ b/plugins/webapi/python/tests/test_veyon_webapi.py @@ -0,0 +1,75 @@ +#!/usr/bin/python3 + +import io +import json +import unittest +import time + +from PIL import Image +from veyon import WebAPIClient + +class Test_VeyonWebAPI(unittest.TestCase): + def setUp(self): + self.client = WebAPIClient('http://localhost:11080/api/v1/', 'localhost') + assert self.client.authenticate_with_key_file('teacher'), "Authentication failed!" + + def tearDown(self): + self.client.close_connection() + + def test_supported_auth_methods(self): + methods = self.client.supported_authentication_methods() + assert (len(methods) > 0), "Server did not report any supported authentication method" + + def test_framebuffer(self): + c = self.client + assert c.wait_for_framebuffer(), "Framebuffer not available!" + + img = Image.open(io.BytesIO(c.get_image(format='png', compression='3'))) + assert img.size == (640, 480), "Framebuffer image size mismatch" + assert img.mode == "RGB", "Framebuffer image format mismatch" + assert img.getpixel((0,0)) == (0x19, 0x8c, 0xb3), "Framebuffer image pixel mismatch" + + img = Image.open(io.BytesIO(c.get_image(format='jpg', quality=100))) + assert img.size == (640, 480), "Framebuffer image size mismatch" + assert img.mode == "RGB", "Framebuffer image format mismatch" + # exact matching not possible due to lossy JPEG encoding so just check color components of pixel for plausibility + p = img.getpixel((0,0)) + assert p[0] > 0x10 and p[0] < 0x29 and p[1] > 0x80 and p[1] < 0x99 and p[2] > 0xb0 and p[2] <= 0xb9, "Framebuffer image pixel mismatch" + + assert Image.open(io.BytesIO(c.get_image(format='png', width=160))).size == (160, 120) + assert Image.open(io.BytesIO(c.get_image(format='png', height=120))).size == (160, 120) + assert Image.open(io.BytesIO(c.get_image(format='png', width=100, height=100))).size == (100, 100) + + def test_user_information(self): + assert self.client.wait_for_framebuffer(), "Framebuffer not available!" + + user_information = self.client.user_information() + assert len(user_information['login']) > 0 + assert 'fullName' in user_information + assert 'session' in user_information + + def test_features(self): + available_features = self.client.available_features() + uids = [value['uid'] for value in available_features] + assert WebAPIClient.FEATURE_SCREEN_LOCK in uids + assert WebAPIClient.FEATURE_LOCK_INPUT_DEVICES in uids + assert WebAPIClient.FEATURE_LOGOFF_USER in uids + assert WebAPIClient.FEATURE_REBOOT in uids + assert WebAPIClient.FEATURE_POWER_DOWN in uids + assert WebAPIClient.FEATURE_DEMO_SERVER in uids + assert WebAPIClient.FEATURE_WINDOW_DEMO_CLIENT in uids + assert WebAPIClient.FEATURE_FULLSCREEN_DEMO_CLIENT in uids + + token = self.client.start_demo_server() + assert token, "Failed to start demo server" + + i = 0 + while i < 30 and not self.client.is_feature_active(WebAPIClient.FEATURE_DEMO_SERVER): + i += 1 + time.sleep(1) + assert self.client.is_feature_active(WebAPIClient.FEATURE_DEMO_SERVER), "Demo server feature not active" + + self.client.stop_demo_server() + +if "__main__" == __name__: + unittest.main() diff --git a/plugins/webapi/python/veyon/__init__.py b/plugins/webapi/python/veyon/__init__.py new file mode 100755 index 0000000..ad0800e --- /dev/null +++ b/plugins/webapi/python/veyon/__init__.py @@ -0,0 +1,194 @@ +#! /usr/bin/python3 + +import json +import requests +import time +import uuid + +class WebAPIClient(object): + + FEATURE_SCREEN_LOCK = 'ccb535a2-1d24-4cc1-a709-8b47d2b2ac79' + FEATURE_LOCK_INPUT_DEVICES = 'e4a77879-e544-4fec-bc18-e534f33b934c' + FEATURE_LOGOFF_USER = 'e4a77879-e544-4fec-bc18-e534f33b934c' + FEATURE_POWER_DOWN = '6f5a27a0-0e2f-496e-afcc-7aae62eede10' + FEATURE_REBOOT = '4f7d98f0-395a-4fff-b968-e49b8d0f748c' + + FEATURE_DEMO_SERVER = 'e4b6e743-1f5b-491d-9364-e091086200f4' + FEATURE_WINDOW_DEMO_CLIENT = 'ae45c3db-dc2e-4204-ae8b-374cdab8c62c' + FEATURE_FULLSCREEN_DEMO_CLIENT = '7b6231bd-eb89-45d3-af32-f70663b2f878' + + ERROR_NONE = 0 + ERROR_INVALID_DATA = 1 + ERROR_INVALID_CONNECTION = 2 + ERROR_INVALID_FEATURE = 3 + ERROR_INVALID_CREDENTIALS = 4 + ERROR_AUTH_METHOD_NOT_AVAILABLE = 5 + ERROR_AUTH_FAILED = 6 + ERROR_CONN_LIMIT_REACHED = 7 + ERROR_CONN_TIMED_OUT = 8 + ERROR_UNSUPPORTED_IMAGE_FORMAT = 9 + ERROR_FRAMEBUFFER_NOT_AVAILABLE = 10 + ERROR_FRAMEBUFFER_ENCODING_ERROR = 11 + + class Error(RuntimeError): + def __init__(self, error): + error_object = json.loads(error) + self.code = error_object['error']['code'] + self.message = error_object['error']['message'] + + def __init__(self, api_url, client): + self.api_url = api_url + self.client = client + self.headers = {} + + def __del__(self): + self.close_connection() + + def is_authenticated(self): + return self.headers and isinstance(self.headers['Connection-Uid'], str) + + def supported_authentication_methods(self): + return self.__get('authentication/%s' % (self.client), {}).get('methods') + + def authenticate(self, method, credentials): + auth_result = self.__post('authentication/%s' % (self.client), {}, {'method': method, 'credentials': credentials}) + if not auth_result: + return False + + self.headers = {'Connection-Uid': auth_result.get('connection-uid')} + return True + + def authenticate_with_key_file(self, keyname, keydata = None): + if not keydata: + with open('/etc/veyon/keys/private/%s/key' % (keyname), 'r') as keyfile: + keydata = keyfile.read() + + return self.authenticate( '0c69b301-81b4-42d6-8fae-128cdd113314', {'keyname': keyname, 'keydata': keydata} ) + + def authenticate_with_logon_credentials(self, username, password): + return self.authenticate( '63611f7c-b457-42c7-832e-67d0f9281085', { "username": username, "password": password } ) + + def close_connection(self): + if self.is_authenticated(): + self.__delete('authentication/%s' % (self.client), self.headers) + self.headers = {} + + def user_information(self): + return self.__get('user', self.headers) + + def wait_for_framebuffer(self, timeout = 30): + i = 0 + while(i < timeout): + try: + if self.get_image(): + return True + except self.Error as error: + if error.code != self.ERROR_FRAMEBUFFER_NOT_AVAILABLE: + raise error + time.sleep(1) + i += 1 + return False + + def get_image(self, width = 0, height = 0, format='png', compression = 5, quality = 75): + parameters = {'format': format, 'compression': compression, 'quality': quality} + if width > 0: + parameters['width'] = width + if height > 0: + parameters['height'] = height + + return self.__get_binary('framebuffer', self.headers, parameters) + + def available_features(self): + return self.__get('feature', self.headers) + + def is_feature_active(self, feature): + return self.__get('feature/%s' % (feature), self.headers).get('active') + + def start_feature(self, feature, arguments = {}): + return self.__put('feature/%s' % (feature), self.headers, {'active': True, 'arguments': arguments}) + + def stop_feature(self, feature, arguments = {}): + return self.__put('feature/%s' % (feature), self.headers, {'active': False, 'arguments': arguments}) + + def logoff_user(self): + return self.start_feature(self.FEATURE_LOGOFF_USER) + + def powerdown_computer(self): + return self.start_feature(self.FEATURE_POWER_DOWN) + + def reboot_computer(self): + return self.start_feature(self.FEATURE_REBOOT) + + def lock_screen(self): + return self.start_feature(self.FEATURE_SCREEN_LOCK) + + def unlock_screen(self): + return self.stop_feature(self.FEATURE_SCREEN_LOCK) + + def lock_input_devices(self): + return self.start_feature(self.FEATURE_LOCK_INPUT_DEVICES) + + def unlock_input_devices(self): + return self.stop_feature(self.FEATURE_LOCK_INPUT_DEVICES) + + def start_demo_server(self): + token = str(uuid.uuid4()) + self.start_feature(self.FEATURE_DEMO_SERVER, { 'demoAccessToken': token }) + return token + + def stop_demo_server(self): + return self.stop_feature(self.FEATURE_DEMO_SERVER) + + def start_demo_client(self, demo_server, demo_token, fullscreen = True): + if fullscreen: + feature = self.FEATURE_FULLSCREEN_DEMO_CLIENT + else: + feature = self.FEATURE_WINDOW_DEMO_CLIENT + + return self.start_feature(feature, { 'demoAccessToken': demo_token, 'demoServerHost': demo_server }) + + def stop_demo_client(self): + return self.stop_feature(self.FEATURE_FULLSCREEN_DEMO_CLIENT) and self.stop_feature(self.FEATURE_WINDOW_DEMO_CLIENT) + + # private helper methods + def __get(self, method, headers, data = {}): + response = requests.get(self.api_url + method, headers=headers, params=data) + + if response.status_code != 200: + print("error:", response.text) + return None + + return response.json() + + def __get_binary(self, method, headers, data = {}): + response = requests.get(self.api_url + method, headers=headers, params=data) + + if response.status_code != 200: + raise self.Error(response.text) + return None + + return response.content + + def __post(self, method, headers, data = {}): + response = requests.post(self.api_url + method, headers=headers, json=data) + + if response.status_code != 200: + raise self.Error(response.text) + + return response.json() + + def __put(self, method, headers, data = {}): + response = requests.put(self.api_url + method, headers=headers, json=data) + + if response.status_code != 200: + raise self.Error(response.text) + + return response.json() + + def __delete(self, method, headers = {}): + response = requests.delete(self.api_url + method, headers=headers) + + if response.status_code != 200: + raise self.Error(response.text) + + return response.json() diff --git a/plugins/webapi/qthttpserver/CMakeLists.txt b/plugins/webapi/qthttpserver/CMakeLists.txt new file mode 100644 index 0000000..e049ee3 --- /dev/null +++ b/plugins/webapi/qthttpserver/CMakeLists.txt @@ -0,0 +1,70 @@ +include(BuildVeyonPlugin) + +set(QTHTTPSERVER_SOURCES + ${qthttpserver_DIR}/src/3rdparty/http-parser/http_parser.c + ${qthttpserver_DIR}/src/httpserver/qhttpserverresponder.h + ${qthttpserver_DIR}/src/httpserver/qhttpserverrouter_p.h + ${qthttpserver_DIR}/src/httpserver/qabstracthttpserver.cpp + ${qthttpserver_DIR}/src/httpserver/qhttpserver.cpp + ${qthttpserver_DIR}/src/httpserver/qhttpserverresponse.h + ${qthttpserver_DIR}/src/httpserver/qhttpserverfutureresponse.h + ${qthttpserver_DIR}/src/httpserver/qhttpserverliterals_p.h + ${qthttpserver_DIR}/src/httpserver/qhttpserverrouterrule.cpp + ${qthttpserver_DIR}/src/httpserver/qhttpserverrouterrule.h + ${qthttpserver_DIR}/src/httpserver/qhttpserverrouterviewtraits.h + ${qthttpserver_DIR}/src/httpserver/qhttpserverresponse.cpp + ${qthttpserver_DIR}/src/httpserver/qhttpserverfutureresponse.cpp + ${qthttpserver_DIR}/src/httpserver/qhttpserverrouterrule_p.h + ${qthttpserver_DIR}/src/httpserver/qhttpserverrequest.cpp + ${qthttpserver_DIR}/src/httpserver/qhttpserverliterals.cpp + ${qthttpserver_DIR}/src/httpserver/qhttpserverresponder.cpp + ${qthttpserver_DIR}/src/httpserver/qthttpserverglobal.h + ${qthttpserver_DIR}/src/httpserver/qhttpserver_p.h + ${qthttpserver_DIR}/src/httpserver/qhttpserverrequest.h + ${qthttpserver_DIR}/src/httpserver/qhttpserver.h + ${qthttpserver_DIR}/src/httpserver/qabstracthttpserver.h + ${qthttpserver_DIR}/src/httpserver/qabstracthttpserver_p.h + ${qthttpserver_DIR}/src/httpserver/httpserver.pro + ${qthttpserver_DIR}/src/httpserver/qhttpserverrouter.cpp + ${qthttpserver_DIR}/src/httpserver/qhttpserverrouter.h + ${qthttpserver_DIR}/src/httpserver/qhttpserverresponse_p.h + ${qthttpserver_DIR}/src/httpserver/qhttpserverrequest_p.h + ${qthttpserver_DIR}/src/httpserver/qhttpserverresponder_p.h + ${qthttpserver_DIR}/src/sslserver/qsslserver.cpp + ${qthttpserver_DIR}/src/sslserver/qsslserver.h + ${qthttpserver_DIR}/src/sslserver/qtsslserverglobal.h + ${qthttpserver_DIR}/src/sslserver/qsslserver_p.h + ) + +add_library(qthttpserver SHARED ${QTHTTPSERVER_SOURCES}) +set_source_files_properties(${QTHTTPSERVER_SOURCES} PROPERTIES SKIP_UNITY_BUILD_INCLUSION TRUE) + +target_compile_definitions(qthttpserver PRIVATE QT_USE_QSTRINGBUILDER QT_BUILD_HTTPSERVER_LIB QT_BUILD_SSLSERVER_LIB) + +# qthttpserver does not build with QT_NO_CAST_FROM_ASCII +remove_definitions(-DQT_NO_CAST_FROM_ASCII) + +add_custom_target(qthttpserver_symlinks + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/httpserver + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/sslserver + COMMAND ${CMAKE_COMMAND} -E create_symlink ${qthttpserver_DIR}/src/httpserver ${CMAKE_CURRENT_BINARY_DIR}/httpserver/private + COMMAND ${CMAKE_COMMAND} -E create_symlink ${qthttpserver_DIR}/src/sslserver ${CMAKE_CURRENT_BINARY_DIR}/sslserver/private + COMMAND ${CMAKE_COMMAND} -E create_symlink ${qthttpserver_DIR}/src/httpserver ${CMAKE_CURRENT_BINARY_DIR}/QtHttpServer + COMMAND ${CMAKE_COMMAND} -E create_symlink ${qthttpserver_DIR}/src/sslserver ${CMAKE_CURRENT_BINARY_DIR}/QtSslServer +) + +add_dependencies(qthttpserver qthttpserver_symlinks) + +target_include_directories(qthttpserver PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}/httpserver + ${CMAKE_CURRENT_BINARY_DIR}/sslserver + ${qthttpserver_DIR}/src/httpserver + ${qthttpserver_DIR}/src/3rdparty/http-parser) + +target_include_directories(qthttpserver PUBLIC + ${CMAKE_CURRENT_BINARY_DIR} +) + +target_link_libraries(qthttpserver PRIVATE Qt5::CorePrivate Qt5::Concurrent Qt5::NetworkPrivate) + +install(TARGETS qthttpserver DESTINATION ${VEYON_LIB_DIR}) diff --git a/plugins/webapi/webapi.png b/plugins/webapi/webapi.png new file mode 100644 index 0000000000000000000000000000000000000000..f363b065f813707e8049b102646d1f2c37121bf8 GIT binary patch literal 3265 zcma)C>SX6^zHsz5D$3osZwHD^2ve5Lo-yBr7t-QfNEqA2eS{Q`(wK}ee*b3gZ>5a$3l zP)JCKjMufRfiBMeZZduW9{Fo(+#nF;pox(M5(ona4u`X}v_v2f?(XgY=}1Q=NR&H4xBxCOv*>zGfv z$0Nt$N#$hsH~;`}{8tOe08{^${eL(v9_c4DC#|FL-`u0hKUgOhj(_^7^;`FNR#~^H=>?;15j-GDDk#C@86@X=v%d^bjZm<0&R)7FITPjz2h0b8++V z@}1!q5EOz5pA``mmync}k(GnXpHn!msB}SDMOE#hx&}g1>ykE7M^{hZz|aVVHa0Oe zv#_+Xw!zri**iEoIbU&cb#wRd^1kZp=YKsQFevy&NW{%ZEG{}GHZI{-Vp4KS>g}|Q z%&hD?cXRIL=H1UPcu-h`FD@y4NGPwUd{kBaxaLW1UH#K%&l{UwG`GBLebv_9(fMar zchBqIH-Gi@za4ltIP`vaWORJu!{pTT#~I@6+^6}4&x=dTD__2@ep@50Z)|RTC;!;q z+1=a!d2qO@wkHPyfj#xLH7!CXzuE3OS|Zu8P$t(mXE~|E#NG?JCEvkkaZq~QC*%^W zatTdk(m4b|o~Mi(?gd^XX*MeXpTkpMmKY83{G+TwTS$O%+j040-nRQ%|8xY=9-&dB z?iUpvxY(~uj3D*rkqYuWG8r9KL#Six0>@)Gt_b-Tu| zgUKYsGBKgu&lg($=-D->9_QGoP&u@*#e09GfEliA{Kp^3@O5C54WCL#p5Pbx?I%tyI=vozXp*)5H7rI$J=!cEtT zi3-a0uRByG#A|wd<0`k{|yF;xt3b^qX#L_J(W`)0y89&J36ZlQGz+8!8 zw|q3*H>esFjl2~38RAA_X-@VxV|aB( zmd*C7hzSLZSOK-`yA4U^vP;-Zc-VRQ1~gn$yXBpZ5cSTz4rh4MAey$0GPQPt3D@}? zAyhV_Ycysrh|2^AvT6p;j#USXRN8ky&F4ss&N~uh4t^RJcFKv=a0V9IF*Pw;JqZn( zIMU?RK<$_iYWYT>Se1x{5^9#!JiF$HhXgoctCf@-3TlPm)W{znS8wPuGi6Je_SeUUwI)_JePG?K{ z;%kIp9_*gnIw`80tnjLIMG)Da9Tiu-7+_pioZqW-&?i4r=)b0999LSa$p*UcLbsh` zF+5{=`bB%=H%_nGPR}s~$cTXg1x|`2S6$6aCXE{~SUF#IF1movDyK&%+EEHrdOr|c zbd&IevUt@o6>RyN_6|`CP$_=Lk)DLbB<4SMI^gum1<|3CE*51}(%}U0lu-kj)kR+g z)ShmN?YFP^4pPk~maZhVX&yKTe_Y}cDC(TxX7I8(kBLb0ru3(MPQKOgvnbl4=!H|z z&Jdw6o~s2@VDe@h&L~)p%PZ$G&W>znwb!7z@O7u(P&kfE$*q`ubQ` z)GjOY%Ew42_aM6H-)y@!b%=2g-Uvk}a_8HB>)+sumeQNYFrW1)a!W_YaL`L-ZAXI7 zIsCZDSQnBs3U*R2uI(+XUcm{M@9e=hwO5!&_Tn<4@GR{PW`7PC$n)7%xv)9fYcoGn zFkQ^8qy~u?zP@Zg8+JMCf!$R1JOY)gtVlrI_7x6P7{`}h zEMdoUKiJ#Q-W|GmhAY*&EBR8!q1mJE15v?o3BfSfB%5mpzF8Vc8+*r1bZ~4Pg6fQ% zeKUH#Fb>mUtl3b)Hr-sfQT*+0aV#ocwbUXmSDAacf2sHh?Uu%TbWe8Y+jCea(|)-| zy^Y6&lAn5z;jz*?lDA|7==P1q4DGn`%?vdcudq|rTBwFAT+Zv6^;zx=jkVSzbo$?r z8x{odl~_oi@Ax8e+wP2TfhLJT*Lz?L+_@&&#CBVd-t?W%V(x+7=^s>`LT$p z*U}X4KnHhMKrLh8!0PNYwUWMly$UBbP7F>{JK7uIE&>s)+CtqQCg1gulMABA(FnNG zh4~gFC9YE2)O*IJOFYl|0i^b}>!_$mCoG>=tG2M>SFPRCq;h7rYE!BW?zV$IW9xFk(L(_2}o z@hq0UkwHHBdWP^(!}&%Po`;OpBf+84VK=d7mTFqBSU-rk6abg@Ec!~-a9~v*17(P^ zwI$m-Wwdr)_e0^bXYYkJM#^UT-xklz>bnoWVrahaSUT6VdDdBIb%4G3-^y^^mb>27)B=ey|oG|`<-O4quN8pnxIc1xIf2hVabJ2_g%77YTUeAMkK-? zD=E|&%tVlzJB)0`L+Wx*5f>*GAmXIEQ@XYj@krvR_t0mUn0g91N~*YM`F#$76tD|> wH7yg_zp@t_3&jCvdnxU#M9n9OU!|M;aRJ(_tYSRTf4@NJBhlJ5T23+l0 + + webapi.png + + diff --git a/plugins/webapi/webapi.svg b/plugins/webapi/webapi.svg new file mode 100644 index 0000000..9c105e2 --- /dev/null +++ b/plugins/webapi/webapi.svg @@ -0,0 +1,70 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/project.yml b/project.yml new file mode 100644 index 0000000..5066f69 --- /dev/null +++ b/project.yml @@ -0,0 +1,19 @@ +project: + name: Veyon + version: 4.5.3 + copyright: 2004-2021 + author: Tobias Junghans + contact: Tobias Junghans + contributors: + - Tobias Junghans + description: Virtual Eye On Networks - OpenSource classroom management + license: GPL-2.0 + category: Education + homepage: https://veyon.io + repository: + type: git + url: https://github.com/veyon/veyon.git + bugs: + url: https://github.com/veyon/veyon/issues + documentation: + url: https://docs.veyon.io diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt new file mode 100644 index 0000000..4e9237d --- /dev/null +++ b/server/CMakeLists.txt @@ -0,0 +1,19 @@ +INCLUDE(BuildVeyonApplication) +INCLUDE(WindowsBuildHelpers) + +FILE(GLOB server_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h) +FILE(GLOB server_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src) + +build_veyon_application(veyon-server ${server_SOURCES} ${server_INCLUDES}) +TARGET_LINK_LIBRARIES(veyon-server veyon-core) + +ADD_WINDOWS_RESOURCE(veyon-server) +MAKE_GRAPHICAL_APP(veyon-server) + +TARGET_LINK_LIBRARIES(veyon-server + Qt5::Gui + Qt5::Network + Qt5::Widgets + ) diff --git a/server/data/veyon-server.exe.manifest.in b/server/data/veyon-server.exe.manifest.in new file mode 100644 index 0000000..b3867d9 --- /dev/null +++ b/server/data/veyon-server.exe.manifest.in @@ -0,0 +1,14 @@ + + + + Veyon Server + + + + + + + + + + diff --git a/server/src/ComputerControlClient.cpp b/server/src/ComputerControlClient.cpp new file mode 100644 index 0000000..6f51a51 --- /dev/null +++ b/server/src/ComputerControlClient.cpp @@ -0,0 +1,73 @@ +/* + * ComputerControlClient.cpp - implementation of the ComputerControlClient class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "VeyonCore.h" +#include "ComputerControlClient.h" +#include "ComputerControlServer.h" + + +ComputerControlClient::ComputerControlClient( ComputerControlServer* server, + QTcpSocket* clientSocket, + int vncServerPort, + const Password& vncServerPassword, + QObject* parent ) : + VncProxyConnection( clientSocket, vncServerPort, parent ), + m_server( server ), + m_serverClient(), + m_serverProtocol( clientSocket, + &m_serverClient, + server->authenticationManager(), + server->accessControlManager() ), + m_clientProtocol( vncServerSocket(), vncServerPassword ) +{ +} + + + +ComputerControlClient::~ComputerControlClient() +{ + m_server->accessControlManager().removeClient( &m_serverClient ); +} + + + +bool ComputerControlClient::receiveClientMessage() +{ + auto socket = proxyClientSocket(); + + char messageType = 0; + if( socket->peek( &messageType, sizeof(messageType) ) != sizeof(messageType) ) + { + return false; + } + + if( messageType == FeatureMessage::RfbMessageType ) + { + return m_server->handleFeatureMessage( socket ); + } + + return VncProxyConnection::receiveClientMessage(); +} diff --git a/server/src/ComputerControlClient.h b/server/src/ComputerControlClient.h new file mode 100644 index 0000000..de1eb5f --- /dev/null +++ b/server/src/ComputerControlClient.h @@ -0,0 +1,73 @@ +/* + * ComputerControlClient.h - header file for the ComputerControlClient class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VncClientProtocol.h" +#include "VncProxyConnection.h" +#include "VncServerClient.h" +#include "VeyonServerProtocol.h" + +class ComputerControlServer; + +class ComputerControlClient : public VncProxyConnection +{ + Q_OBJECT +public: + using Password = CryptoCore::SecureArray; + + ComputerControlClient( ComputerControlServer* server, + QTcpSocket* clientSocket, + int vncServerPort, + const Password& vncServerPassword, + QObject* parent ); + ~ComputerControlClient() override; + + bool receiveClientMessage() override; + + VncServerClient* serverClient() + { + return &m_serverClient; + } + +protected: + VncClientProtocol& clientProtocol() override + { + return m_clientProtocol; + } + + VncServerProtocol& serverProtocol() override + { + return m_serverProtocol; + } + +private: + ComputerControlServer* m_server; + + VncServerClient m_serverClient; + + VeyonServerProtocol m_serverProtocol; + VncClientProtocol m_clientProtocol; + +} ; diff --git a/server/src/ComputerControlServer.cpp b/server/src/ComputerControlServer.cpp new file mode 100644 index 0000000..3b2f23d --- /dev/null +++ b/server/src/ComputerControlServer.cpp @@ -0,0 +1,253 @@ +/* + * ComputerControlServer.cpp - implementation of ComputerControlServer + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "AccessControlProvider.h" +#include "BuiltinFeatures.h" +#include "ComputerControlClient.h" +#include "ComputerControlServer.h" +#include "FeatureMessage.h" +#include "HostAddress.h" +#include "VeyonConfiguration.h" +#include "SystemTrayIcon.h" + + +ComputerControlServer::ComputerControlServer( QObject* parent ) : + QObject( parent ), + m_allowedIPs(), + m_failedAuthHosts(), + m_featureManager(), + m_featureWorkerManager( *this, m_featureManager ), + m_serverAuthenticationManager( this ), + m_serverAccessControlManager( m_featureWorkerManager, VeyonCore::builtinFeatures().desktopAccessDialog(), this ), + m_vncServer(), + m_vncProxyServer( VeyonCore::config().localConnectOnly() || AccessControlProvider().isAccessToLocalComputerDenied() ? + QHostAddress::LocalHost : QHostAddress::Any, + VeyonCore::config().veyonServerPort() + VeyonCore::sessionId(), + this, + this ) +{ + updateTrayIconToolTip(); + + // make app terminate once the VNC server thread has finished + connect( &m_vncServer, &VncServer::finished, QCoreApplication::instance(), &QCoreApplication::quit ); + + connect( &m_serverAuthenticationManager, &ServerAuthenticationManager::finished, + this, &ComputerControlServer::showAuthenticationMessage ); + + connect( &m_serverAccessControlManager, &ServerAccessControlManager::finished, + this, &ComputerControlServer::showAccessControlMessage ); + + connect( &m_vncProxyServer, &VncProxyServer::connectionClosed, this, &ComputerControlServer::updateTrayIconToolTip ); +} + + + +ComputerControlServer::~ComputerControlServer() +{ + vDebug(); + + m_vncProxyServer.stop(); +} + + + +bool ComputerControlServer::start() +{ + if( m_vncProxyServer.start( m_vncServer.serverPort(), m_vncServer.password() ) == false ) + { + return false; + } + + m_vncServer.prepare(); + m_vncServer.start(); + + return true; +} + + + +VncProxyConnection* ComputerControlServer::createVncProxyConnection( QTcpSocket* clientSocket, + int vncServerPort, + const Password& vncServerPassword, + QObject* parent ) +{ + auto client = new ComputerControlClient( this, clientSocket, vncServerPort, vncServerPassword, parent ); + + connect( client, &ComputerControlClient::serverConnectionClosed, this, + [=]() { checkForIncompleteAuthentication( client->serverClient() ); }, + Qt::DirectConnection ); + + return client; +} + + + +bool ComputerControlServer::handleFeatureMessage( QTcpSocket* socket ) +{ + char messageType; + if( socket->getChar( &messageType ) == false ) + { + vWarning() << "could not read feature message!"; + return false; + } + + // receive message + FeatureMessage featureMessage; + if( featureMessage.isReadyForReceive( socket ) == false ) + { + socket->ungetChar( messageType ); + return false; + } + + featureMessage.receive( socket ); + + return m_featureManager.handleFeatureMessage( *this, MessageContext( socket ), featureMessage ); +} + + + +bool ComputerControlServer::sendFeatureMessageReply( const MessageContext& context, const FeatureMessage& reply ) +{ + vDebug() << reply.featureUid() << reply.command() << reply.arguments(); + + char rfbMessageType = FeatureMessage::RfbMessageType; + context.ioDevice()->write( &rfbMessageType, sizeof(rfbMessageType) ); + + return reply.send( context.ioDevice() ); +} + + + +void ComputerControlServer::checkForIncompleteAuthentication( VncServerClient* client ) +{ + // connection to client closed during authentication? + if( client->protocolState() == VncServerProtocol::AuthenticationTypes || + client->protocolState() == VncServerProtocol::Authenticating ) + { + // then mark as failed authentication and report it + client->setAuthState( VncServerClient::AuthState::Failed ); + + showAuthenticationMessage( client ); + } +} + + + +void ComputerControlServer::showAuthenticationMessage( VncServerClient* client ) +{ + if( client->authState() == VncServerClient::AuthState::Failed ) + { + vWarning() << "Authentication failed for" << client->hostAddress() << client->username(); + + if( VeyonCore::config().failedAuthenticationNotificationsEnabled() ) + { + QMutexLocker l( &m_dataMutex ); + + if( m_failedAuthHosts.contains( client->hostAddress() ) == false ) + { + m_failedAuthHosts += client->hostAddress(); + + const auto fqdn = HostAddress( client->hostAddress() ).tryConvert( HostAddress::Type::FullyQualifiedDomainName ); + + VeyonCore::builtinFeatures().systemTrayIcon().showMessage( + tr( "Authentication error" ), + tr( "User \"%1\" at host \"%2\" attempted to access this computer " + "but could not authenticate successfully." ).arg( client->username(), fqdn ), + m_featureWorkerManager ); + } + } + } +} + + + +void ComputerControlServer::showAccessControlMessage( VncServerClient* client ) +{ + if( client->accessControlState() == VncServerClient::AccessControlState::Successful && + client->protocolState() == VncServerProtocol::State::AccessControl ) + { + vInfo() << "Access control successful for" << client->hostAddress() << client->username(); + + if( VeyonCore::config().remoteConnectionNotificationsEnabled() ) + { + const auto fqdn = HostAddress( client->hostAddress() ).tryConvert( HostAddress::Type::FullyQualifiedDomainName ); + + VeyonCore::builtinFeatures().systemTrayIcon().showMessage( + tr( "Remote access" ), + tr( "User \"%1\" at host \"%2\" is now accessing this computer." ). + arg( client->username(), fqdn ), + m_featureWorkerManager ); + } + + updateTrayIconToolTip(); + } + else if( client->accessControlState() == VncServerClient::AccessControlState::Failed ) + { + vWarning() << "Access control failed for" << client->hostAddress() << client->username(); + + if( VeyonCore::config().failedAuthenticationNotificationsEnabled() ) + { + QMutexLocker l( &m_dataMutex ); + + if( m_failedAccessControlHosts.contains( client->hostAddress() ) == false ) + { + m_failedAccessControlHosts += client->hostAddress(); + + const auto fqdn = HostAddress( client->hostAddress() ).tryConvert( HostAddress::Type::FullyQualifiedDomainName ); + + VeyonCore::builtinFeatures().systemTrayIcon().showMessage( + tr( "Access control error" ), + tr( "User \"%1\" at host \"%2\" attempted to access this computer " + "but has been blocked due to access control settings." ). + arg( client->username(), fqdn ), + m_featureWorkerManager ); + } + } + } +} + + + +void ComputerControlServer::updateTrayIconToolTip() +{ + auto toolTip = tr( "%1 Service %2 at %3:%4" ).arg( VeyonCore::applicationName(), VeyonCore::version(), + HostAddress::localFQDN(), + QString::number( VeyonCore::config().veyonServerPort() + VeyonCore::sessionId() ) ); + + QStringList clients; + for( const auto* client : m_vncProxyServer.clients() ) + { + const auto clientAddress = HostAddress( client->proxyClientSocket()->peerAddress().toString() ); + clients.append( clientAddress.tryConvert( HostAddress::Type::FullyQualifiedDomainName ) ); + } + + if( clients.isEmpty() == false ) + { + toolTip += QLatin1Char('\n') + tr( "Active connections:") + QLatin1Char('\n') + clients.join( QLatin1Char('\n') ); + } + + VeyonCore::builtinFeatures().systemTrayIcon().setToolTip( toolTip, m_featureWorkerManager ); +} diff --git a/server/src/ComputerControlServer.h b/server/src/ComputerControlServer.h new file mode 100644 index 0000000..094bab3 --- /dev/null +++ b/server/src/ComputerControlServer.h @@ -0,0 +1,100 @@ +/* + * ComputerControlServer.h - header file for ComputerControlServer + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "FeatureManager.h" +#include "FeatureWorkerManager.h" +#include "RfbVeyonAuth.h" +#include "ServerAuthenticationManager.h" +#include "ServerAccessControlManager.h" +#include "VeyonServerInterface.h" +#include "VncProxyServer.h" +#include "VncProxyConnectionFactory.h" +#include "VncServer.h" + +class ComputerControlServer : public QObject, VncProxyConnectionFactory, VeyonServerInterface +{ + Q_OBJECT +public: + explicit ComputerControlServer( QObject* parent = nullptr ); + ~ComputerControlServer() override; + + bool start(); + + VncProxyConnection* createVncProxyConnection( QTcpSocket* clientSocket, + int vncServerPort, + const Password& vncServerPassword, + QObject* parent ) override; + + ServerAuthenticationManager& authenticationManager() + { + return m_serverAuthenticationManager; + } + + ServerAccessControlManager& accessControlManager() + { + return m_serverAccessControlManager; + } + + bool handleFeatureMessage( QTcpSocket* socket ); + + bool sendFeatureMessageReply( const MessageContext& context, const FeatureMessage& reply ) override; + + FeatureWorkerManager& featureWorkerManager() override + { + return m_featureWorkerManager; + } + + int vncServerBasePort() const override + { + return m_vncServer.serverBasePort(); + } + +private: + void checkForIncompleteAuthentication( VncServerClient* client ); + void showAuthenticationMessage( VncServerClient* client ); + void showAccessControlMessage( VncServerClient* client ); + + void updateTrayIconToolTip(); + + QMutex m_dataMutex; + QStringList m_allowedIPs; + + QStringList m_failedAuthHosts; + QStringList m_failedAccessControlHosts; + + FeatureManager m_featureManager; + FeatureWorkerManager m_featureWorkerManager; + + ServerAuthenticationManager m_serverAuthenticationManager; + ServerAccessControlManager m_serverAccessControlManager; + + VncServer m_vncServer; + VncProxyServer m_vncProxyServer; + +} ; diff --git a/server/src/ServerAccessControlManager.cpp b/server/src/ServerAccessControlManager.cpp new file mode 100644 index 0000000..e78c50c --- /dev/null +++ b/server/src/ServerAccessControlManager.cpp @@ -0,0 +1,232 @@ +/* + * ServerAccessControlManager.cpp - implementation of ServerAccessControlManager + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "VeyonCore.h" + +#include "ServerAccessControlManager.h" +#include "AccessControlProvider.h" +#include "DesktopAccessDialog.h" +#include "VeyonConfiguration.h" +#include "VariantArrayMessage.h" + + +ServerAccessControlManager::ServerAccessControlManager( FeatureWorkerManager& featureWorkerManager, + DesktopAccessDialog& desktopAccessDialog, + QObject* parent ) : + QObject( parent ), + m_featureWorkerManager( featureWorkerManager ), + m_desktopAccessDialog( desktopAccessDialog ), + m_clients(), + m_desktopAccessChoices() +{ +} + + + +void ServerAccessControlManager::addClient( VncServerClient* client ) +{ + switch( client->authType() ) + { + case RfbVeyonAuth::KeyFile: + case RfbVeyonAuth::Logon: + performAccessControl( client ); + break; + + case RfbVeyonAuth::None: + case RfbVeyonAuth::Token: + client->setAccessControlState( VncServerClient::AccessControlState::Successful ); + break; + + default: + // reject unknown auth type + client->setAccessControlState( VncServerClient::AccessControlState::Failed ); + break; + } + + if( client->accessControlState() == VncServerClient::AccessControlState::Successful ) + { + m_clients.append( client ); + } +} + + + +void ServerAccessControlManager::removeClient( VncServerClient* client ) +{ + m_clients.removeAll( client ); + + // force all remaining clients to pass access control again as conditions might + // have changed (e.g. AccessControlRule::Condition::AccessFromAlreadyConnectedUser) + + const VncServerClientList previousClients = m_clients; + m_clients.clear(); + + for( auto prevClient : qAsConst( previousClients ) ) + { + prevClient->setAccessControlState( VncServerClient::AccessControlState::Init ); + addClient( prevClient ); + + if( prevClient->accessControlState() != VncServerClient::AccessControlState::Successful && + prevClient->accessControlState() != VncServerClient::AccessControlState::Pending ) + { + vDebug() << "closing connection as client does not pass access control any longer"; + prevClient->setProtocolState( VncServerProtocol::Close ); + } + } +} + + + +void ServerAccessControlManager::performAccessControl( VncServerClient* client ) +{ + // implement access control wait for connections other than the one an + // access dialog is currently active for + switch( client->accessControlState() ) + { + case VncServerClient::AccessControlState::Init: + client->accessControlTimer().restart(); + break; + case VncServerClient::AccessControlState::Waiting: + if( client->accessControlTimer().elapsed() < ClientWaitInterval ) + { + return; + } + client->accessControlTimer().restart(); + break; + default: + break; + } + + const auto accessResult = + AccessControlProvider().checkAccess( client->username(), + client->hostAddress(), + connectedUsers() ); + + switch( accessResult ) + { + case AccessControlProvider::Access::Allow: + client->setAccessControlState( VncServerClient::AccessControlState::Successful ); + break; + + case AccessControlProvider::Access::ToBeConfirmed: + client->setAccessControlState( confirmDesktopAccess( client ) ); + break; + + default: + client->setAccessControlState( VncServerClient::AccessControlState::Failed ); + client->setProtocolState( VncServerProtocol::Close ); + break; + } + + Q_EMIT finished( client ); +} + + + +VncServerClient::AccessControlState ServerAccessControlManager::confirmDesktopAccess( VncServerClient* client ) +{ + const HostUserPair hostUserPair( client->username(), client->hostAddress() ); + + // did we save a previous choice because user chose "always" or "never"? + if( m_desktopAccessChoices.contains( hostUserPair ) ) + { + if( qAsConst(m_desktopAccessChoices)[hostUserPair] == DesktopAccessDialog::ChoiceAlways ) + { + return VncServerClient::AccessControlState::Successful; + } + + return VncServerClient::AccessControlState::Failed; + } + + // already an access dialog running? + if( m_desktopAccessDialog.isBusy( &m_featureWorkerManager ) ) + { + // then close connection so that client has to try again later + return VncServerClient::AccessControlState::Waiting; + } + + // get notified whenever the dialog finishes - use signal indirection for + // automatically breaking connection if VncServerClient gets deleted while + // dialog is active (e.g. due another connection being closed and thus + // all other connections are closed as well in order to perform access + // control again) + connect( &m_desktopAccessDialog, &DesktopAccessDialog::finished, + client, &VncServerClient::finishAccessControl ); + + connect( client, &VncServerClient::accessControlFinished, + this, &ServerAccessControlManager::finishDesktopAccessConfirmation ); + + // start the dialog (non-blocking) + m_desktopAccessDialog.exec( &m_featureWorkerManager, client->username(), client->hostAddress() ); + + return VncServerClient::AccessControlState::Pending; +} + + + +void ServerAccessControlManager::finishDesktopAccessConfirmation( VncServerClient* client ) +{ + // break helper connections for asynchronous desktop access control operations + if( m_desktopAccessDialog.disconnect( client ) == false || + client->disconnect( this ) == false ) + { + vCritical() << "could not break object connections"; + } + + const auto choice = m_desktopAccessDialog.choice(); + + // remember choices "always" and "never" + if( choice == DesktopAccessDialog::ChoiceAlways || choice == DesktopAccessDialog::ChoiceNever ) + { + m_desktopAccessChoices[HostUserPair( client->username(), client->hostAddress() )] = choice; + } + + // evaluate choice and set according access control state + if( choice == DesktopAccessDialog::ChoiceYes || choice == DesktopAccessDialog::ChoiceAlways ) + { + client->setAccessControlState( VncServerClient::AccessControlState::Successful ); + m_clients.append( client ); + } + else + { + client->setAccessControlState( VncServerClient::AccessControlState::Failed ); + client->setProtocolState( VncServerProtocol::Close ); + } +} + + + +QStringList ServerAccessControlManager::connectedUsers() const +{ + QStringList users; + + users.reserve( m_clients.size() ); + + for( auto client : m_clients ) + { + users += client->username(); + } + + return users; +} diff --git a/server/src/ServerAccessControlManager.h b/server/src/ServerAccessControlManager.h new file mode 100644 index 0000000..cd91e27 --- /dev/null +++ b/server/src/ServerAccessControlManager.h @@ -0,0 +1,67 @@ +/* + * ServerAccessControlManager.h - header file for ServerAccessControlManager + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "DesktopAccessDialog.h" +#include "RfbVeyonAuth.h" +#include "VncServerClient.h" + +class VariantArrayMessage; + +class ServerAccessControlManager : public QObject +{ + Q_OBJECT +public: + ServerAccessControlManager( FeatureWorkerManager& featureWorkerManager, + DesktopAccessDialog& desktopAccessDialog, + QObject* parent ); + + void addClient( VncServerClient* client ); + void removeClient( VncServerClient* client ); + + +Q_SIGNALS: + void finished( VncServerClient* client ); + +private: + static constexpr int ClientWaitInterval = 1000; + + void performAccessControl( VncServerClient* client ); + VncServerClient::AccessControlState confirmDesktopAccess( VncServerClient* client ); + void finishDesktopAccessConfirmation( VncServerClient* client ); + + QStringList connectedUsers() const; + + FeatureWorkerManager& m_featureWorkerManager; + DesktopAccessDialog& m_desktopAccessDialog; + + VncServerClientList m_clients; + + using HostUserPair = QPair; + using DesktopAccessChoiceMap = QMap; + + DesktopAccessChoiceMap m_desktopAccessChoices; + +} ; diff --git a/server/src/ServerAuthenticationManager.cpp b/server/src/ServerAuthenticationManager.cpp new file mode 100644 index 0000000..04a1ae1 --- /dev/null +++ b/server/src/ServerAuthenticationManager.cpp @@ -0,0 +1,247 @@ +/* + * ServerAuthenticationManager.cpp - implementation of ServerAuthenticationManager + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "AuthenticationCredentials.h" +#include "ServerAuthenticationManager.h" +#include "CryptoCore.h" +#include "Filesystem.h" +#include "PlatformUserFunctions.h" +#include "VariantArrayMessage.h" +#include "VeyonConfiguration.h" + + +ServerAuthenticationManager::ServerAuthenticationManager( QObject* parent ) : + QObject( parent ) +{ +} + + + +QVector ServerAuthenticationManager::supportedAuthTypes() const +{ + QVector authTypes; + + if( VeyonCore::config().authenticationMethod() == VeyonCore::AuthenticationMethod::KeyFileAuthentication ) + { + authTypes.append( RfbVeyonAuth::KeyFile ); + } + + if( VeyonCore::config().authenticationMethod() == VeyonCore::AuthenticationMethod::LogonAuthentication ) + { + authTypes.append( RfbVeyonAuth::Logon ); + } + + if( VeyonCore::authenticationCredentials().hasCredentials( AuthenticationCredentials::Type::Token ) ) + { + authTypes.append( RfbVeyonAuth::Token ); + } + + return authTypes; +} + + + +void ServerAuthenticationManager::processAuthenticationMessage( VncServerClient* client, + VariantArrayMessage& message ) +{ + vDebug() << "state" << client->authState() + << "type" << client->authType() + << "host" << client->hostAddress() + << "user" << client->username(); + + switch( client->authType() ) + { + // no authentication + case RfbVeyonAuth::None: + client->setAuthState( VncServerClient::AuthState::Successful ); + break; + + // authentication via DSA-challenge/-response + case RfbVeyonAuth::KeyFile: + client->setAuthState( performKeyAuthentication( client, message ) ); + break; + + case RfbVeyonAuth::Logon: + client->setAuthState( performLogonAuthentication( client, message ) ); + break; + + case RfbVeyonAuth::Token: + client->setAuthState( performTokenAuthentication( client, message ) ); + break; + + default: + // unknown or unsupported auth type + client->setAuthState( VncServerClient::AuthState::Failed ); + break; + } + + switch( client->authState() ) + { + case VncServerClient::AuthState::Failed: + case VncServerClient::AuthState::Successful: + Q_EMIT finished( client ); + break; + default: + break; + } +} + + + +VncServerClient::AuthState ServerAuthenticationManager::performKeyAuthentication( VncServerClient* client, + VariantArrayMessage& message ) +{ + switch( client->authState() ) + { + case VncServerClient::AuthState::Init: + client->setChallenge( CryptoCore::generateChallenge() ); + if( VariantArrayMessage( message.ioDevice() ).write( client->challenge() ).send() == false ) + { + vWarning() << "failed to send challenge"; + return VncServerClient::AuthState::Failed; + } + return VncServerClient::AuthState::Challenge; + + case VncServerClient::AuthState::Challenge: + { + // get authentication key name + const auto authKeyName = message.read().toString(); // Flawfinder: ignore + + if( VeyonCore::isAuthenticationKeyNameValid( authKeyName ) == false ) + { + vDebug() << "invalid auth key name!"; + return VncServerClient::AuthState::Failed; + } + + // now try to verify received signed data using public key of the user + // under which the client claims to run + const auto signature = message.read().toByteArray(); // Flawfinder: ignore + + const auto publicKeyPath = VeyonCore::filesystem().publicKeyPath( authKeyName ); + + vDebug() << "loading public key" << publicKeyPath; + CryptoCore::PublicKey publicKey( publicKeyPath ); + + if( publicKey.isNull() || publicKey.isPublic() == false || + publicKey.verifyMessage( client->challenge(), signature, CryptoCore::DefaultSignatureAlgorithm ) == false ) + { + vWarning() << "FAIL"; + return VncServerClient::AuthState::Failed; + } + + vDebug() << "SUCCESS"; + return VncServerClient::AuthState::Successful; + } + + default: + break; + } + + return VncServerClient::AuthState::Failed; +} + + + +VncServerClient::AuthState ServerAuthenticationManager::performLogonAuthentication( VncServerClient* client, + VariantArrayMessage& message ) +{ + switch( client->authState() ) + { + case VncServerClient::AuthState::Init: + client->setPrivateKey( CryptoCore::KeyGenerator().createRSA( CryptoCore::RsaKeySize ) ); + + if( VariantArrayMessage( message.ioDevice() ).write( client->privateKey().toPublicKey().toPEM() ).send() ) + { + return VncServerClient::AuthState::Password; + } + + vDebug() << "failed to send public key"; + return VncServerClient::AuthState::Failed; + + case VncServerClient::AuthState::Password: + { + auto privateKey = client->privateKey(); + + CryptoCore::SecureArray encryptedPassword( message.read().toByteArray() ); // Flawfinder: ignore + + CryptoCore::SecureArray decryptedPassword; + + if( privateKey.decrypt( encryptedPassword, + &decryptedPassword, + CryptoCore::DefaultEncryptionAlgorithm ) == false ) + { + vWarning() << "failed to decrypt password"; + return VncServerClient::AuthState::Failed; + } + + vInfo() << "authenticating user" << client->username(); + + if( VeyonCore::platform().userFunctions().authenticate( client->username(), decryptedPassword ) ) + { + vDebug() << "SUCCESS"; + return VncServerClient::AuthState::Successful; + } + + vDebug() << "FAIL"; + return VncServerClient::AuthState::Failed; + } + + default: + break; + } + + return VncServerClient::AuthState::Failed; +} + + + +VncServerClient::AuthState ServerAuthenticationManager::performTokenAuthentication( VncServerClient* client, + VariantArrayMessage& message ) +{ + switch( client->authState() ) + { + case VncServerClient::AuthState::Init: + return VncServerClient::AuthState::Token; + + case VncServerClient::AuthState::Token: + { + const auto token = AuthenticationCredentials::Token( message.read().toByteArray() ); // Flawfinder: ignore + + if( VeyonCore::authenticationCredentials().hasCredentials( AuthenticationCredentials::Type::Token ) && + token == VeyonCore::authenticationCredentials().token() ) + { + vDebug() << "SUCCESS"; + return VncServerClient::AuthState::Successful; + } + + vDebug() << "FAIL"; + return VncServerClient::AuthState::Failed; + } + + default: + break; + } + + return VncServerClient::AuthState::Failed; +} diff --git a/server/src/ServerAuthenticationManager.h b/server/src/ServerAuthenticationManager.h new file mode 100644 index 0000000..8cc85e2 --- /dev/null +++ b/server/src/ServerAuthenticationManager.h @@ -0,0 +1,61 @@ +/* + * ServerAuthenticationManager.h - header file for ServerAuthenticationManager + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "RfbVeyonAuth.h" +#include "VncServerClient.h" + +class VariantArrayMessage; + +class ServerAuthenticationManager : public QObject +{ + Q_OBJECT +public: + enum class AuthResult { + Successful, + Failed + } ; + Q_ENUM(AuthResult) + + explicit ServerAuthenticationManager( QObject* parent ); + + QVector supportedAuthTypes() const; + + void processAuthenticationMessage( VncServerClient* client, + VariantArrayMessage& message ); + + +Q_SIGNALS: + void finished( VncServerClient* client ); + +private: + VncServerClient::AuthState performKeyAuthentication( VncServerClient* client, VariantArrayMessage& message ); + VncServerClient::AuthState performLogonAuthentication( VncServerClient* client, VariantArrayMessage& message ); + VncServerClient::AuthState performTokenAuthentication( VncServerClient* client, VariantArrayMessage& message ); + +} ; diff --git a/server/src/VeyonServerProtocol.cpp b/server/src/VeyonServerProtocol.cpp new file mode 100644 index 0000000..e9c9c5a --- /dev/null +++ b/server/src/VeyonServerProtocol.cpp @@ -0,0 +1,66 @@ +/* + * VeyonServerProtocol.cpp - implementation of the VeyonServerProtocol class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "ServerAuthenticationManager.h" +#include "ServerAccessControlManager.h" +#include "VeyonServerProtocol.h" + + +VeyonServerProtocol::VeyonServerProtocol( QTcpSocket* socket, + VncServerClient* client, + ServerAuthenticationManager& serverAuthenticationManager, + ServerAccessControlManager& serverAccessControlManager ) : + VncServerProtocol( socket, client ), + m_serverAuthenticationManager( serverAuthenticationManager ), + m_serverAccessControlManager( serverAccessControlManager ) +{ +} + + + +QVector VeyonServerProtocol::supportedAuthTypes() const +{ + return m_serverAuthenticationManager.supportedAuthTypes(); +} + + + +void VeyonServerProtocol::processAuthenticationMessage(VariantArrayMessage &message) +{ + m_serverAuthenticationManager.processAuthenticationMessage( client(), message ); +} + + + +void VeyonServerProtocol::performAccessControl() +{ + // perform access control via ServerAccessControl manager if either + // client just entered access control or is still waiting to be + // processed (e.g. desktop access dialog already active for a different connection) + if( client()->accessControlState() == VncServerClient::AccessControlState::Init || + client()->accessControlState() == VncServerClient::AccessControlState::Waiting ) + { + m_serverAccessControlManager.addClient( client() ); + } +} diff --git a/server/src/VeyonServerProtocol.h b/server/src/VeyonServerProtocol.h new file mode 100644 index 0000000..4438f8e --- /dev/null +++ b/server/src/VeyonServerProtocol.h @@ -0,0 +1,51 @@ +/* + * VeyonServerProtocol.h - header file for the VeyonServerProtocol class + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VncServerProtocol.h" + +class ServerAuthenticationManager; +class ServerAccessControlManager; + +// clazy:excludeall=copyable-polymorphic + +class VeyonServerProtocol : public VncServerProtocol +{ +public: + VeyonServerProtocol( QTcpSocket* socket, + VncServerClient* client, + ServerAuthenticationManager& serverAuthenticationManager, + ServerAccessControlManager& serverAccessControlManager ); + +protected: + QVector supportedAuthTypes() const override; + void processAuthenticationMessage( VariantArrayMessage& message ) override; + void performAccessControl() override; + +private: + ServerAuthenticationManager& m_serverAuthenticationManager; + ServerAccessControlManager& m_serverAccessControlManager; + +} ; diff --git a/server/src/VncProxyConnection.cpp b/server/src/VncProxyConnection.cpp new file mode 100644 index 0000000..c8d532c --- /dev/null +++ b/server/src/VncProxyConnection.cpp @@ -0,0 +1,250 @@ +/* + * VncProxyConnection.cpp - class representing a connection within VncProxyServer + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "VncClientProtocol.h" +#include "VncProxyConnection.h" +#include "VncServerProtocol.h" + +VncProxyConnection::VncProxyConnection( QTcpSocket* clientSocket, + int vncServerPort, + QObject* parent ) : + QObject( parent ), + m_vncServerPort( vncServerPort ), + m_proxyClientSocket( clientSocket ), + m_vncServerSocket( new QTcpSocket( this ) ), + m_rfbClientToServerMessageSizes( { + { rfbSetPixelFormat, sz_rfbSetPixelFormatMsg }, + { rfbFramebufferUpdateRequest, sz_rfbFramebufferUpdateRequestMsg }, + { rfbKeyEvent, sz_rfbKeyEventMsg }, + { rfbPointerEvent, sz_rfbPointerEventMsg }, + { rfbXvp, sz_rfbXvpMsg }, + } ) +{ + connect( m_proxyClientSocket, &QTcpSocket::readyRead, this, &VncProxyConnection::readFromClient ); + connect( m_vncServerSocket, &QTcpSocket::readyRead, this, &VncProxyConnection::readFromServer ); + + connect( m_vncServerSocket, &QTcpSocket::disconnected, this, &VncProxyConnection::clientConnectionClosed ); + connect( m_proxyClientSocket, &QTcpSocket::disconnected, this, &VncProxyConnection::serverConnectionClosed ); +} + + + +VncProxyConnection::~VncProxyConnection() +{ + // do not get notified about disconnects any longer + disconnect( m_vncServerSocket ); + disconnect( m_proxyClientSocket ); + + delete m_vncServerSocket; + delete m_proxyClientSocket; +} + + + +void VncProxyConnection::start() +{ + serverProtocol().start(); +} + + + +void VncProxyConnection::readFromClient() +{ + if( serverProtocol().state() != VncServerProtocol::Running ) + { + while( serverProtocol().read() ) // Flawfinder: ignore + { + } + + // try again later in case we could not proceed because of + // external protocol dependencies or in case we're finished + // and already have RFB messages in receive queue + readFromClientLater(); + } + else if( clientProtocol().state() == VncClientProtocol::Running ) + { + while( receiveClientMessage() ) + { + } + } + else + { + // try again as client connection is not yet ready and we can't forward data + readFromClientLater(); + } + + if( serverProtocol().state() == VncServerProtocol::FramebufferInit && + clientProtocol().state() == VncClientProtocol::Disconnected ) + { + m_vncServerSocket->connectToHost( QHostAddress::LocalHost, quint16(m_vncServerPort) ); + + clientProtocol().start(); + } +} + + + +void VncProxyConnection::readFromServer() +{ + if( clientProtocol().state() != VncClientProtocol::Running ) + { + while( clientProtocol().read() ) // Flawfinder: ignore + { + } + + // did we finish client protocol initialization? then we must not miss this + // read signaĺ from server but process it as the server is still waiting + // for our response + if( clientProtocol().state() == VncClientProtocol::Running ) + { + // if client protocol is running we have the server init message which + // we can forward to the real client + serverProtocol().setServerInitMessage( clientProtocol().serverInitMessage() ); + + readFromServerLater(); + } + } + else if( serverProtocol().state() == VncServerProtocol::Running ) + { + while( receiveServerMessage() ) + { + } + } + else + { + // try again as server connection is not yet ready and we can't forward data + readFromServerLater(); + } +} + + + +bool VncProxyConnection::forwardDataToClient( qint64 size ) +{ + if( m_vncServerSocket->bytesAvailable() >= size ) + { + const auto data = m_vncServerSocket->read( size ); // Flawfinder: ignore + if( data.size() == size ) + { + return m_proxyClientSocket->write( data ) == size; + } + } + + return false; +} + + + +bool VncProxyConnection::forwardDataToServer( qint64 size ) +{ + if( m_proxyClientSocket->bytesAvailable() >= size ) + { + const auto data = m_proxyClientSocket->read( size ); // Flawfinder: ignore + if( data.size() == size ) + { + return m_vncServerSocket->write( data ) == size; + } + } + + return false; +} + + + +void VncProxyConnection::readFromServerLater() +{ + QTimer::singleShot( ProtocolRetryTime, this, &VncProxyConnection::readFromServer ); +} + + + +void VncProxyConnection::readFromClientLater() +{ + QTimer::singleShot( ProtocolRetryTime, this, &VncProxyConnection::readFromClient ); +} + + + +bool VncProxyConnection::receiveClientMessage() +{ + auto socket = proxyClientSocket(); + + uint8_t messageType = 0; + if( socket->peek( reinterpret_cast( &messageType ), sizeof(messageType) ) != sizeof(messageType) ) + { + return false; + } + + switch( messageType ) + { + case rfbSetEncodings: + if( socket->bytesAvailable() >= sz_rfbSetEncodingsMsg ) + { + rfbSetEncodingsMsg setEncodingsMessage; + if( socket->peek( reinterpret_cast( &setEncodingsMessage ), sz_rfbSetEncodingsMsg ) == sz_rfbSetEncodingsMsg ) + { + const auto nEncodings = qFromBigEndian(setEncodingsMessage.nEncodings); + if( nEncodings > MAX_ENCODINGS ) + { + vCritical() << "received too many encodings from client"; + socket->close(); + return false; + } + return forwardDataToServer( sz_rfbSetEncodingsMsg + nEncodings * sizeof(uint32_t) ); + } + } + break; + + default: + if( m_rfbClientToServerMessageSizes.contains( messageType ) == false ) + { + vCritical() << "received unknown message type:" << static_cast( messageType ); + socket->close(); + return false; + } + + return forwardDataToServer( m_rfbClientToServerMessageSizes[messageType] ); + } + + return false; +} + + + +bool VncProxyConnection::receiveServerMessage() +{ + if( clientProtocol().receiveMessage() ) + { + m_proxyClientSocket->write( clientProtocol().lastMessage() ); + + return true; + } + + return false; +} diff --git a/server/src/VncProxyConnection.h b/server/src/VncProxyConnection.h new file mode 100644 index 0000000..8d30ff2 --- /dev/null +++ b/server/src/VncProxyConnection.h @@ -0,0 +1,87 @@ +/* + * VncProxyConnection.h - class representing a connection within VncProxyServer + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "VeyonCore.h" + +class QBuffer; +class QTcpSocket; + +class VncClientProtocol; +class VncServerProtocol; + +class VncProxyConnection : public QObject +{ + Q_OBJECT +public: + enum { + ProtocolRetryTime = 250 + }; + + VncProxyConnection( QTcpSocket* clientSocket, int vncServerPort, QObject* parent ); + ~VncProxyConnection() override; + + void start(); + + QTcpSocket* proxyClientSocket() const + { + return m_proxyClientSocket; + } + + QTcpSocket* vncServerSocket() const + { + return m_vncServerSocket; + } + +protected Q_SLOTS: + void readFromClient(); + void readFromServer(); + +protected: + bool forwardDataToClient( qint64 size ); + bool forwardDataToServer( qint64 size ); + + void readFromServerLater(); + void readFromClientLater(); + + virtual bool receiveClientMessage(); + virtual bool receiveServerMessage(); + + virtual VncClientProtocol& clientProtocol() = 0; + virtual VncServerProtocol& serverProtocol() = 0; + +private: + const int m_vncServerPort; + + QTcpSocket* m_proxyClientSocket; + QTcpSocket* m_vncServerSocket; + + const QMap m_rfbClientToServerMessageSizes; + +Q_SIGNALS: + void clientConnectionClosed(); + void serverConnectionClosed(); + +} ; diff --git a/server/src/VncProxyConnectionFactory.h b/server/src/VncProxyConnectionFactory.h new file mode 100644 index 0000000..e0138e3 --- /dev/null +++ b/server/src/VncProxyConnectionFactory.h @@ -0,0 +1,46 @@ +/* + * VncProxyConnectionFactory.h - abstract factory class for VncProxyConnectionFactory objects + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "CryptoCore.h" + +class QTcpSocket; +class VncProxyConnection; + +// clazy:excludeall=copyable-polymorphic + +class VncProxyConnectionFactory +{ +public: + using Password = CryptoCore::SecureArray; + + virtual ~VncProxyConnectionFactory() = default; + + virtual VncProxyConnection* createVncProxyConnection( QTcpSocket* clientSocket, + int vncServerPort, + const Password& vncServerPassword, + QObject* parent ) = 0; + +} ; diff --git a/server/src/VncProxyServer.cpp b/server/src/VncProxyServer.cpp new file mode 100644 index 0000000..b67d5d5 --- /dev/null +++ b/server/src/VncProxyServer.cpp @@ -0,0 +1,130 @@ +/* + * VncProxyServer.cpp - a VNC proxy implementation for intercepting VNC connections + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "VeyonCore.h" +#include "VncProxyServer.h" +#include "VncProxyConnection.h" +#include "VncProxyConnectionFactory.h" + + +VncProxyServer::VncProxyServer( const QHostAddress& listenAddress, + int listenPort, + VncProxyConnectionFactory* connectionFactory, + QObject* parent ) : + QObject( parent ), + m_vncServerPort( -1 ), + m_vncServerPassword(), + m_listenAddress( listenAddress ), + m_listenPort( listenPort ), + m_server( new QTcpServer( this ) ), + m_connectionFactory( connectionFactory ) +{ + connect( m_server, &QTcpServer::newConnection, this, &VncProxyServer::acceptConnection ); + connect( m_server, &QTcpServer::acceptError, this, &VncProxyServer::handleAcceptError ); +} + + + +VncProxyServer::~VncProxyServer() +{ + stop(); +} + + + +bool VncProxyServer::start( int vncServerPort, const Password& vncServerPassword ) +{ + m_vncServerPort = vncServerPort; + m_vncServerPassword = vncServerPassword; + + if( m_listenPort < 0 || + m_server->listen( m_listenAddress, static_cast( m_listenPort ) ) == false ) + { + vWarning() << "could not listen on port" << m_listenPort << m_server->errorString(); + return false; + } + + vDebug() << "started on port" << m_listenPort; + return true; +} + + + +void VncProxyServer::stop() +{ + for( auto connection : qAsConst( m_connections ) ) + { + delete connection; + } + + m_connections.clear(); + + delete m_server; + m_server = nullptr; +} + + + +void VncProxyServer::acceptConnection() +{ + auto clientSocket = m_server->nextPendingConnection(); + if( clientSocket == nullptr ) + { + vCritical() << "ignoring invalid client socket"; + return; + } + + auto connection = m_connectionFactory->createVncProxyConnection( clientSocket, + m_vncServerPort, + m_vncServerPassword, + this ); + + connect( connection, &VncProxyConnection::clientConnectionClosed, this, [=]() { closeConnection( connection ); } ); + connect( connection, &VncProxyConnection::serverConnectionClosed, this, [=]() { closeConnection( connection ); } ); + + connection->start(); + + m_connections += connection; +} + + + +void VncProxyServer::closeConnection( VncProxyConnection* connection ) +{ + m_connections.removeAll( connection ); + + Q_EMIT connectionClosed( connection ); + + connection->deleteLater(); +} + + + +void VncProxyServer::handleAcceptError( QAbstractSocket::SocketError socketError ) +{ + vCritical() << "error while accepting connection" << socketError; +} diff --git a/server/src/VncProxyServer.h b/server/src/VncProxyServer.h new file mode 100644 index 0000000..85c848a --- /dev/null +++ b/server/src/VncProxyServer.h @@ -0,0 +1,73 @@ +/* + * VncProxyServer.h - a VNC proxy implementation for intercepting VNC connections + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "CryptoCore.h" + +class QTcpServer; +class VncProxyConnection; +class VncProxyConnectionFactory; + +class VncProxyServer : public QObject +{ + Q_OBJECT +public: + using Password = CryptoCore::SecureArray; + using VncProxyConnectionList = QVector; + + VncProxyServer( const QHostAddress& listenAddress, + int listenPort, + VncProxyConnectionFactory* clientFactory, + QObject* parent = nullptr ); + ~VncProxyServer() override; + + bool start( int vncServerPort, const Password& vncServerPassword ); + void stop(); + + const VncProxyConnectionList& clients() const + { + return m_connections; + } + +Q_SIGNALS: + void connectionClosed( VncProxyConnection* connection ); + +private: + void acceptConnection(); + void closeConnection( VncProxyConnection* ); + void handleAcceptError( QAbstractSocket::SocketError socketError ); + + int m_vncServerPort; + Password m_vncServerPassword; + QHostAddress m_listenAddress; + int m_listenPort; + QTcpServer* m_server; + VncProxyConnectionFactory* m_connectionFactory; + VncProxyConnectionList m_connections; + +} ; diff --git a/server/src/VncServer.cpp b/server/src/VncServer.cpp new file mode 100644 index 0000000..ae78e0f --- /dev/null +++ b/server/src/VncServer.cpp @@ -0,0 +1,163 @@ +/* + * VncServer.cpp - implementation of VncServer, a VNC-server- + * abstraction for platform independent VNC-server-usage + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "rfb/rfbproto.h" + +#include "AuthenticationCredentials.h" +#include "CryptoCore.h" +#include "VeyonConfiguration.h" +#include "PlatformSessionFunctions.h" +#include "PluginManager.h" +#include "VncServer.h" +#include "VncServerPluginInterface.h" + + +VncServer::VncServer( QObject* parent ) : + QThread( parent ), + m_pluginInterface( nullptr ) +{ + const auto currentSessionType = VeyonCore::platform().sessionFunctions().currentSessionType(); + + VeyonCore::authenticationCredentials().setInternalVncServerPassword( + CryptoCore::generateChallenge().toBase64().left( MAXPWLEN ) ); + + VncServerPluginInterfaceList defaultVncServerPlugins; + + for( auto pluginObject : qAsConst( VeyonCore::pluginManager().pluginObjects() ) ) + { + auto pluginInterface = qobject_cast( pluginObject ); + auto vncServerPluginInterface = qobject_cast( pluginObject ); + + if( pluginInterface && vncServerPluginInterface ) + { + // skip VNC server plugins which support certain session types only and do not support + // the current session type + if( vncServerPluginInterface->supportedSessionTypes().isEmpty() == false && + vncServerPluginInterface->supportedSessionTypes().contains( currentSessionType, Qt::CaseInsensitive ) == false ) + { + continue; + } + + if( pluginInterface->uid() == VeyonCore::config().vncServerPlugin() ) + { + m_pluginInterface = vncServerPluginInterface; + } + else if( pluginInterface->flags().testFlag( Plugin::ProvidesDefaultImplementation ) ) + { + defaultVncServerPlugins.append( vncServerPluginInterface ); // clazy:exclude=reserve-candidates + } + } + } + + if( m_pluginInterface == nullptr ) + { + if( defaultVncServerPlugins.isEmpty() ) + { + vCritical() << "no VNC server plugins found!"; + } + else + { + m_pluginInterface = defaultVncServerPlugins.first(); + } + } +} + + + +VncServer::~VncServer() +{ + vDebug(); +} + + + +void VncServer::prepare() +{ + vDebug(); + + if( m_pluginInterface ) + { + m_pluginInterface->prepareServer(); + } +} + + + +int VncServer::serverBasePort() const +{ + + if( m_pluginInterface && m_pluginInterface->configuredServerPort() > 0 ) + { + return m_pluginInterface->configuredServerPort(); + } + + return VeyonCore::config().vncServerPort(); +} + + + +int VncServer::serverPort() const +{ + return serverBasePort() + VeyonCore::sessionId(); +} + + + +VncServer::Password VncServer::password() const +{ + if( m_pluginInterface && m_pluginInterface->configuredPassword().isEmpty() == false ) + { + return m_pluginInterface->configuredPassword(); + } + + return VeyonCore::authenticationCredentials().internalVncServerPassword(); +} + + + +void VncServer::run() +{ + if( m_pluginInterface ) + { + vDebug() << "running"; + + if( m_pluginInterface->configuredServerPort() > 0 ) + { + VeyonCore::config().setVncServerPort( m_pluginInterface->configuredServerPort() ); + } + + if( m_pluginInterface->configuredPassword().isEmpty() == false ) + { + VeyonCore::authenticationCredentials().setInternalVncServerPassword( m_pluginInterface->configuredPassword() ); + } + + if( m_pluginInterface->runServer( serverPort(), password() ) == false ) + { + vCritical() << "An error occurred while running the VNC server plugin"; + } + + vDebug() << "finished"; + } +} diff --git a/server/src/VncServer.h b/server/src/VncServer.h new file mode 100644 index 0000000..dc58f7a --- /dev/null +++ b/server/src/VncServer.h @@ -0,0 +1,55 @@ +/* + * VncServer.h - class VncServer, a VNC server abstraction for + * platform-independent VNC server usage + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include + +#include "CryptoCore.h" + +class VncServerPluginInterface; + +class VncServer : public QThread +{ + Q_OBJECT +public: + using Password = CryptoCore::SecureArray; + + explicit VncServer( QObject* parent = nullptr ); + ~VncServer() override; + + void prepare(); + + int serverBasePort() const; + int serverPort() const; + + Password password() const; + +private: + void run() override; + + VncServerPluginInterface* m_pluginInterface; + +} ; diff --git a/server/src/main.cpp b/server/src/main.cpp new file mode 100644 index 0000000..5a82b61 --- /dev/null +++ b/server/src/main.cpp @@ -0,0 +1,45 @@ +/* + * main.cpp - main file for Veyon Server + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "ComputerControlServer.h" +#include "VeyonConfiguration.h" + + +int main( int argc, char **argv ) +{ + QCoreApplication app( argc, argv ); + + VeyonCore core( &app, VeyonCore::Component::Server, QStringLiteral("Server") ); + + ComputerControlServer server( &core ); + if( server.start() == false ) + { + vCritical() << "Failed to start server"; + return -1; + } + + return core.exec(); +} diff --git a/server/veyon-server.1 b/server/veyon-server.1 new file mode 100644 index 0000000..4918a3a --- /dev/null +++ b/server/veyon-server.1 @@ -0,0 +1,45 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH VEYON-SERVER 1 2018-12-07 Veyon +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +veyon-server \- Veyon Server +.SH SYNOPSIS +.B veyon-server +.br +.SH DESCRIPTION +.PP +.\" TeX users may be more comfortable with the \fB\fP and +.\" \fI\fP escape sequences to invode bold face and italics, +.\" respectively. +\fBVEYON-SERVER\fR is a server application which provides access to a +computer as well as control and application functions. +.PP +Under normal conditions this program is started by the Veyon Service +automatically and with elevated privileges so it can’t be terminated by +users. +.PP +.SH SEE ALSO +veyon-service(1), veyon-master(1), veyon-configurator(8), veyon-auth-helper(1) +.PP +https://veyon.io/ + +.SH AUTHOR +Veyon has been written by Tobias Junghans. +.PP +This manual page has been written by Tobias Junghans and Mike Gabriel. It +was originally written for the Debian project (but may be used by +others). diff --git a/server/veyon-server.rc.in b/server/veyon-server.rc.in new file mode 100644 index 0000000..b362f4a --- /dev/null +++ b/server/veyon-server.rc.in @@ -0,0 +1,31 @@ +#include + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST data/veyon-server.exe.manifest + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@,@VERSION_BUILD@ + FILEFLAGSMASK 0 + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, charset = Windows, Multilingual + BEGIN + VALUE "Comments", "Virtual Eye On Networks (https://veyon.io)\0" + VALUE "CompanyName", "Veyon Solutions\0" + VALUE "ProductName", "Veyon\0" + VALUE "ProductVersion", "@VERSION_STRING@\0" + VALUE "FileDescription", "Veyon Server\0" + VALUE "FileVersion", "@VERSION_STRING@\0" + VALUE "LegalCopyright", "Copyright (c) 2017-2021 Veyon Solutions / Tobias Junghans\0" + VALUE "OriginalFilename", "veyon-server.exe\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04E4 + END +END diff --git a/service/CMakeLists.txt b/service/CMakeLists.txt new file mode 100644 index 0000000..fe937c2 --- /dev/null +++ b/service/CMakeLists.txt @@ -0,0 +1,19 @@ +INCLUDE(BuildVeyonApplication) +INCLUDE(WindowsBuildHelpers) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src) + +build_veyon_application(veyon-service ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp) +TARGET_LINK_LIBRARIES(veyon-service veyon-core) + +ADD_WINDOWS_RESOURCE(veyon-service) +MAKE_GRAPHICAL_APP(veyon-service) + +IF(VEYON_BUILD_LINUX) + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/veyon.service.in ${CMAKE_CURRENT_BINARY_DIR}/veyon.service @ONLY) + IF(NOT SYSTEMD_SERVICE_INSTALL_DIR) + SET(SYSTEMD_SERVICE_INSTALL_DIR /lib/systemd/system) + ENDIF() + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/veyon.service DESTINATION ${SYSTEMD_SERVICE_INSTALL_DIR}) +ENDIF() + diff --git a/service/data/veyon-service.exe.manifest.in b/service/data/veyon-service.exe.manifest.in new file mode 100644 index 0000000..54b8702 --- /dev/null +++ b/service/data/veyon-service.exe.manifest.in @@ -0,0 +1,14 @@ + + + + Veyon Service + + + + + + + + + + diff --git a/service/src/main.cpp b/service/src/main.cpp new file mode 100644 index 0000000..457f821 --- /dev/null +++ b/service/src/main.cpp @@ -0,0 +1,44 @@ +/* + * main.cpp - main file for Veyon Service + * + * Copyright (c) 2006-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "VeyonServiceControl.h" +#include "PlatformServiceFunctions.h" + +int main( int argc, char** argv ) +{ + QCoreApplication app( argc, argv ); + VeyonCore core( &app, VeyonCore::Component::Service, QStringLiteral("Service") ); + + auto& serviceFunctions = core.platform().serviceFunctions(); + + if( serviceFunctions.runAsService( VeyonServiceControl::name(), + [&]() { serviceFunctions.manageServerInstances(); } ) ) + { + return 0; + } + + return -1; +} diff --git a/service/veyon-service.1 b/service/veyon-service.1 new file mode 100644 index 0000000..2331ce3 --- /dev/null +++ b/service/veyon-service.1 @@ -0,0 +1,45 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH VEYON-SERVICE 1 2018-12-07 Veyon +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +veyon-service \- Veyon Service +.SH SYNOPSIS +.B veyon-service +.br +.SH DESCRIPTION +.PP +.\" TeX users may be more comfortable with the \fB\fP and +.\" \fI\fP escape sequences to invode bold face and italics, +.\" respectively. +\fBVEYON-SERVICE\fR is a non-graphical service application which monitors +user sessions on a computer and starts Veyon Server instances within +these sessions. +.PP +The service and its server subprocesses are required to run on all +computers including teacher computers. + +.SH SEE ALSO +veyon-server(1), veyon-configurator(1), veyon-auth-helper(8) +.PP +https://veyon.io/ + +.SH AUTHOR +Veyon has been written by Tobias Junghans. +.PP +This manual page has been written by Tobias Junghans and updated by Mike +Gabriel. It was originally written for the Debian project (but may be +used by others). diff --git a/service/veyon-service.rc.in b/service/veyon-service.rc.in new file mode 100644 index 0000000..3feb0e3 --- /dev/null +++ b/service/veyon-service.rc.in @@ -0,0 +1,31 @@ +#include + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST data/veyon-service.exe.manifest + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@,@VERSION_BUILD@ + FILEFLAGSMASK 0 + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, charset = Windows, Multilingual + BEGIN + VALUE "Comments", "Virtual Eye On Networks (https://veyon.io)\0" + VALUE "CompanyName", "Veyon Solutions\0" + VALUE "ProductName", "Veyon\0" + VALUE "ProductVersion", "@VERSION_STRING@\0" + VALUE "FileDescription", "Veyon Service\0" + VALUE "FileVersion", "@VERSION_STRING@\0" + VALUE "LegalCopyright", "Copyright (c) 2017-2021 Veyon Solutions / Tobias Junghans\0" + VALUE "OriginalFilename", "veyon-service.exe\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04E4 + END +END diff --git a/service/veyon.service.in b/service/veyon.service.in new file mode 100644 index 0000000..c010b45 --- /dev/null +++ b/service/veyon.service.in @@ -0,0 +1,17 @@ +[Unit] +Description=Veyon Service +After=network-online.target dbus.service systemd-logind.service +Wants=network-online.target +Requires=dbus.service systemd-logind.service +Documentation=man:veyon-service(1) + +[Service] +ExecStart=@CMAKE_INSTALL_PREFIX@/bin/veyon-service +Type=simple +Restart=always +StartLimitInterval=60 +StartLimitBurst=10 + +[Install] +WantedBy=multi-user.target + diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt new file mode 100644 index 0000000..5e79d3d --- /dev/null +++ b/translations/CMakeLists.txt @@ -0,0 +1,12 @@ +include(CreateTranslations) +include(FindQtTranslations) + +file(GLOB veyon_translations ${CMAKE_CURRENT_SOURCE_DIR}/*.ts) +set(ts_targets "") +set(qm_targets "") +file(GLOB_RECURSE veyon_sources ${CMAKE_SOURCE_DIR}/*.cpp ${CMAKE_SOURCE_DIR}/*.h ${CMAKE_SOURCE_DIR}/*.ui ${CMAKE_SOURCE_DIR}/*.qml) +string(REGEX REPLACE "${CMAKE_SOURCE_DIR}/3rdparty[^;]+;?" "" veyon_sources "${veyon_sources}") +string(REGEX REPLACE "${CMAKE_SOURCE_DIR}/addons[^;]+;?" "" veyon_sources "${veyon_sources}") + +create_translations(veyon "${veyon_translations}" "${veyon_sources}") +find_qt_translations() diff --git a/translations/veyon.ts b/translations/veyon.ts new file mode 100644 index 0000000..6faf54e --- /dev/null +++ b/translations/veyon.ts @@ -0,0 +1,4056 @@ + + + + + AboutDialog + + About + + + + Translation + + + + License + + + + About Veyon + + + + Contributors + + + + Version: + + + + Website: + + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + + + + About %1 %2 + + + + Support Veyon project with a donation + + + + + AccessControlPage + + Computer access control + + + + Grant access to every authenticated user (default) + + + + Test + + + + Process access control rules + + + + User groups authorized for computer access + + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + + + + Authorized user groups + + + + All groups + + + + Access control rules + + + + Add access control rule + + + + Remove access control rule + + + + Move selected rule down + + + + Move selected rule up + + + + Edit selected rule + + + + Enter username + + + + Please enter a user login name whose access permissions to test: + + + + Access allowed + + + + The specified user is allowed to access computers with this configuration. + + + + Access denied + + + + The specified user is not allowed to access computers with this configuration. + + + + Enable usage of domain groups + + + + User groups backend: + + + + Missing user groups backend + + + + No default user groups plugin was found. Please check your installation! + + + + Restrict access to members of specific user groups + + + + + AccessControlRuleEditDialog + + Edit access control rule + + + + General + + + + enter a short name for the rule here + + + + Rule name: + + + + enter a description for the rule here + + + + Rule description: + + + + Invert all conditions ("is/has" interpreted as "is/has not") + + + + Conditions + + + + is member of group + + + + Accessing computer is localhost + + + + Accessing user is logged on user + + + + Accessing user is already connected + + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + + + + Action + + + + Allow access + + + + Deny access + + + + Ask logged on user for permission + + + + None (rule disabled) + + + + Accessing user + + + + Accessing computer + + + + Local (logged on) user + + + + Local computer + + + + Always process rule and ignore conditions + + + + No user logged on + + + + Accessing user has one or more groups in common with local (logged on) user + + + + Accessing computer and local computer are at the same location + + + + is located at + + + + + AccessControlRulesTestDialog + + Access control rules test + + + + Accessing user: + + + + Local computer: + + + + Accessing computer: + + + + Please enter the following user and computer information in order to test the configured ruleset. + + + + Local user: + + + + Connected users: + + + + The access in the given scenario is allowed. + + + + The access in the given scenario is denied. + + + + The access in the given scenario needs permission of the logged on user. + + + + ERROR: Unknown action + + + + Test result + + + + + AuthKeysConfigurationPage + + Authentication keys + + + + Introduction + + + + Key file directories + + + + Public key file base directory + + + + Private key file base directory + + + + Available authentication keys + + + + Create key pair + + + + Delete key + + + + Import key + + + + Export key + + + + Set access group + + + + Key files (*.pem) + + + + Authentication key name + + + + Please enter the name of the user group or role for which to create an authentication key pair: + + + + Do you really want to delete authentication key "%1/%2"? + + + + Please select a key to delete! + + + + Please enter the name of the user group or role for which to import the authentication key: + + + + Please select a key to export! + + + + Please select a user group which to grant access to key "%1": + + + + Please select a key which to set the access group for! + + + + Please perform the following steps to set up key file authentication: + + + + 1) Create a key pair on the master computer. + + + + 2) Set an access group whose members should be allowed to access other computers. + + + + 3) Export the public key and import it on all client computers with the same name. + + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + + + + + AuthKeysManager + + Please check your permissions. + + + + Key name contains invalid characters! + + + + Invalid key type specified! Please specify "%1" or "%2". + + + + Specified key does not exist! Please use the "list" command to list all installed keys. + + + + One or more key files already exist! Please delete them using the "delete" command. + + + + Creating new key pair for "%1" + + + + Failed to create public or private key! + + + + Newly created key pair has been saved to "%1" and "%2". + + + + Could not remove key file "%1"! + + + + Could not remove key file directory "%1"! + + + + Failed to create directory for output file. + + + + File "%1" already exists. + + + + Failed to write output file. + + + + Key "%1/%2" has been exported to "%3" successfully. + + + + Failed read input file. + + + + File "%1" does not contain a valid private key! + + + + File "%1" does not contain a valid public key! + + + + Failed to create directory for key file. + + + + Failed to write key file "%1". + + + + Failed to set permissions for key file "%1"! + + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + + + + Failed to convert private key to public key + + + + Failed to create directory for private key file "%1". + + + + Failed to save private key in file "%1"! + + + + Failed to set permissions for private key file "%1"! + + + + Failed to create directory for public key file "%1". + + + + Failed to save public key in file "%1"! + + + + Failed to set permissions for public key file "%1"! + + + + Failed to set owner of key file "%1" to "%2". + + + + Failed to set permissions for key file "%1". + + + + Key "%1" is now accessible by user group "%2". + + + + <N/A> + + + + Failed to read key file. + + + + + AuthKeysPlugin + + Create new authentication key pair + + + + Delete authentication key + + + + List authentication keys + + + + Import public or private key + + + + Export public or private key + + + + Extract public key from existing private key + + + + Set user group allowed to access a key + + + + KEY + + + + ACCESS GROUP + + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + + NAME + + + + FILE + + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + + Please specify the command to display help for! + + + + TYPE + + + + PAIR ID + + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + + + + Type + + + + Access group + + + + Pair ID + + + + + BuiltinDirectoryConfigurationPage + + Computers + + + + Name + + + + Host address/IP + + + + MAC address + + + + Add new computer + + + + Remove selected computer + + + + New computer + + + + Builtin directory + + + + Locations & computers + + + + Locations + + + + Add new location + + + + Remove selected location + + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + + + + + BuiltinDirectoryPlugin + + Show help for specific command + + + + Import objects from given file + + + + Export objects to given file + + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + + + + Name + + + + Host address + + + + MAC address + + + + Specified object not found. + + + + File "%1" does not exist! + + + + Can't open file "%1" for reading! + + + + Unknown argument "%1". + + + + Computer "%1" (host address: "%2" MAC address: "%3") + + + + Unclassified object "%1" with ID "%2" + + + + None + + + + Computer + + + + Root + + + + Invalid + + + + Error while parsing line %1. + + + + Network object directory which stores objects in local configuration + + + + Commands for managing the builtin network object directory + + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + + + + Parent UUID + + + + Add a location or computer + + + + Clear all locations and computers + + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + + + + FILE + + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + + + + NAME + + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + + + + + ComputerControlListModel + + Host/IP address: %1 + + + + Active features: %1 + + + + Online and connected + + + + Establishing connection + + + + Computer offline or switched off + + + + Authentication failed or access denied + + + + Disconnected + + + + No user logged on + + + + Logged on user: %1 + + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + + + + Authentication error + + + + Remote access + + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + + + + Missing network object directory plugin + + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + + + + Add location + + + + Save computer/user list + + + + Select output filename + + + + CSV files (*.csv) + + + + File error + + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + + + + Please specify a valid filename for the configuration export. + + + + Please specify a valid key. + + + + Specified key does not exist in current configuration! + + + + Please specify a valid value. + + + + Configure Veyon at command line + + + + Output file is not writable! + + + + Output directory is not writable! + + + + Configuration file is not readable! + + + + Clear system-wide Veyon configuration + + + + List all configuration keys and values + + + + Import configuration from given file + + + + Export configuration to given file + + + + Read and output configuration value for given key + + + + Write given value to given configuration key + + + + Unset (remove) given configuration key + + + + Commands for managing the configuration of Veyon + + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + + + + + DemoConfigurationPage + + Demo server + + + + Tunables + + + + ms + + + + Key frame interval + + + + Memory limit + + + + MB + + + + Update interval + + + + s + + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + + + + Window demo + + + + Give a demonstration by screen broadcasting + + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + + + + Confirm desktop access + + + + Never for this session + + + + Always for this session + + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + + DesktopServicesConfigurationPage + + Programs & websites + + + + Predefined programs + + + + Name + + + + Path + + + + Add new program + + + + Remove selected program + + + + Predefined websites + + + + Remove selected website + + + + URL + + + + New program + + + + New website + + + + + DesktopServicesFeaturePlugin + + Run program + + + + Open website + + + + Click this button to open a website on all computers. + + + + Start programs and services in user desktop + + + + Click this button to run a program on all computers. + + + + Run program "%1" + + + + Custom program + + + + Open website "%1" + + + + Custom website + + + + + DocumentationFigureCreator + + Teacher + + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + + + + Port: + + + + Password: + + + + + FeatureControl + + Feature control + + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + + + + Destination directory + + + + Default source directory + + + + Options + + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + + + + Select one or more files to transfer + + + + Transfer files to remote computer + + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + + + + Language: + + + + Use system language setting + + + + Veyon + + + + Logging + + + + Log file directory + + + + Log level + + + + Nothing + + + + Only critical messages + + + + Errors and critical messages + + + + Warnings and errors + + + + Information, warnings and errors + + + + Debug messages and everything else + + + + Limit log file size + + + + Clear all log files + + + + Log to standard error output + + + + Network object directory + + + + Backend: + + + + Update interval: + + + + %1 service + + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + + + + All log files were cleared successfully. + + + + Error + + + + Could not remove all log files. + + + + MB + + + + Rotate log files + + + + x + + + + seconds + + + + Write to logging system of operating system + + + + Authentication + + + + Method: + + + + Logon authentication + + + + Key file authentication + + + + Test + + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + + + + + LdapConfigurationPage + + Basic settings + + + + General + + + + LDAP server and port + + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + + + + e.g. OU=Computers + + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + + LDAP base DN test failed + + + + LDAP base DN test successful + + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + + + + Please enter a group name whose members to query: + + + + group members + + + + Group not found + + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + + + + users + + + + user groups + + + + computer groups + + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + + + + LDAP %1 test failed + + + + LDAP %1 test successful + + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + + + + TLS certificate verification + + + + System defaults + + + + Never (insecure!) + + + + Custom CA certificate file + + + + None + + + + TLS + + + + SSL + + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + + + + enter search filter... + + + + + MainToolBar + + Configuration + + + + Disable balloon tooltips + + + + Show icons only + + + + + MainWindow + + MainWindow + + + + toolBar + + + + General + + + + &File + + + + &Help + + + + &Quit + + + + Ctrl+Q + + + + Ctrl+S + + + + L&oad settings from file + + + + Ctrl+O + + + + About Qt + + + + Authentication impossible + + + + Configuration not writable + + + + Load settings from file + + + + Save settings to file + + + + Unsaved settings + + + + There are unsaved settings. Quit anyway? + + + + Veyon Configurator + + + + Service + + + + Master + + + + Access control + + + + About Veyon + + + + Auto + + + + About + + + + %1 Configurator %2 + + + + JSON files (*.json) + + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + + + + Align computers to grid + + + + %1 Configurator + + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + + + + User configuration + + + + Feature on computer double click: + + + + Features + + + + All features + + + + Disabled features + + + + Screenshots + + + + <no feature> + + + + Basic settings + + + + Behaviour + + + + Enforce selected mode for client computers + + + + Hide local computer + + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + + + + User interface + + + + Background color + + + + Thumbnail update interval + + + + ms + + + + Program start + + + + Modes and features + + + + User and computer name + + + + Only user name + + + + Only computer name + + + + Computer thumbnail caption + + + + Text color + + + + Sort order + + + + Computer and user name + + + + Computer locations + + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + + + + Name: + + + + + PasswordDialog + + Username + + + + Password + + + + Veyon Logon + + + + Authentication error + + + + Logon failed with given username and password. Please try again! + + + + Please enter your username and password in order to access computers. + + + + + PowerControlFeaturePlugin + + Power on + + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + + Reboot + + + + Click this button to reboot all computers. + + + + Power down + + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + + + + Confirm reboot + + + + Confirm power down + + + + Do you really want to reboot the selected computers? + + + + Do you really want to power down the selected computer? + + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + + + + Open a remote view for a computer without interaction. + + + + Remote control + + + + Open a remote control window for a computer. + + + + Remote access + + + + Remote view or control a computer + + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + + + + Remote control + + + + Send shortcut + + + + Fullscreen + + + + Window + + + + Ctrl+Alt+Del + + + + Ctrl+Esc + + + + Alt+Tab + + + + Alt+F4 + + + + Win+Tab + + + + Win + + + + Menu + + + + Alt+Ctrl+F1 + + + + Connecting %1 + + + + Connected. + + + + Screenshot + + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + + + + Unlock + + + + Lock screen and input devices of a computer + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + + + + Use this function to take a screenshot of selected computers. + + + + Screenshots taken + + + + Screenshot of %1 computer have been taken successfully. + + + + Take screenshots of computers and save them locally. + + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + + + + Computer: + + + + Date: + + + + Time: + + + + Show + + + + Delete + + + + Screenshot + + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + + + + Autostart + + + + Hide tray icon + + + + Start service + + + + Stopped + + + + Stop service + + + + State: + + + + Enable firewall exception + + + + Allow connections from localhost only + + + + VNC server + + + + Plugin: + + + + Restart %1 Service + + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + + Running + + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + + + + Stopping service %1 + + + + Registering service %1 + + + + Unregistering service %1 + + + + Service control + + + + + ServiceControlPlugin + + Service is running + + + + Service is not running + + + + Configure and control Veyon service + + + + Register Veyon Service + + + + Unregister Veyon Service + + + + Start Veyon Service + + + + Stop Veyon Service + + + + Restart Veyon Service + + + + Query status of Veyon Service + + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + + + + Use the field below to type your message which will be sent to all selected users. + + + + + TextMessageFeaturePlugin + + Text message + + + + Use this function to send a text message to all users e.g. to assign them new tasks. + + + + Message from teacher + + + + Send a message to a user + + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + + + + Poll full screen (leave this enabled per default) + + + + Low accuracy (turbo mode) + + + + Builtin UltraVNC server configuration + + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + + + + Password + + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + + + + + VeyonCore + + [OK] + + + + [FAIL] + + + + Invalid command! + + + + Available commands: + + + + Invalid arguments given + + + + Not enough arguments given - use "%1 help" for more information + + + + Unknown result! + + + + Available modules: + + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + + + + INFO + + + + ERROR + + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + + + + + VncViewWidget + + Establishing connection to %1 ... + + + + + WebApiConfigurationPage + + Web API + + + + General + + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + + + + Custom x11vnc parameters: + + + + Do not use X Damage extension + + + + diff --git a/translations/veyon_ar.ts b/translations/veyon_ar.ts new file mode 100644 index 0000000..2b7df20 --- /dev/null +++ b/translations/veyon_ar.ts @@ -0,0 +1,4056 @@ + + + + + AboutDialog + + About + حول + + + Translation + ترجمة + + + License + ترخيص + + + About Veyon + حول فيون + + + Contributors + المساهمون + + + Version: + نسخة: + + + Website: + الموقع الإلكتروني: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + + + + About %1 %2 + حول %1 %2 + + + Support Veyon project with a donation + دعم مشروع فيون بالتبرع + + + + AccessControlPage + + Computer access control + التحكم في الوصول إلى الكمبيوتر + + + Grant access to every authenticated user (default) + منح حق الوصول إلى كل مستخدم تمت مصادقته (افتراضي) + + + Test + إختبار + + + Process access control rules + قواعد التحكم في الوصول إلى العمليات + + + User groups authorized for computer access + + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + + + + Authorized user groups + + + + All groups + جميع المجموعات + + + Access control rules + قواعد التحكم في الوصول + + + Add access control rule + إضافة قاعدة للتحكم في الوصول + + + Remove access control rule + إزالة قاعدة للتحكم في الوصول + + + Move selected rule down + نقل القاعدة المحددة لأسفل + + + Move selected rule up + نقل القاعدة المحددة لأعلى + + + Edit selected rule + تعديل القاعدة المحددة + + + Enter username + أدخل اسم المستخدم + + + Please enter a user login name whose access permissions to test: + + + + Access allowed + حق الوصول مسموح + + + The specified user is allowed to access computers with this configuration. + + + + Access denied + حق الوصول ممنوع + + + The specified user is not allowed to access computers with this configuration. + + + + Enable usage of domain groups + + + + User groups backend: + + + + Missing user groups backend + + + + No default user groups plugin was found. Please check your installation! + + + + Restrict access to members of specific user groups + + + + + AccessControlRuleEditDialog + + Edit access control rule + + + + General + عام + + + enter a short name for the rule here + + + + Rule name: + + + + enter a description for the rule here + + + + Rule description: + + + + Invert all conditions ("is/has" interpreted as "is/has not") + + + + Conditions + حالات + + + is member of group + هو عضو في المجموعة + + + Accessing computer is localhost + + + + Accessing user is logged on user + + + + Accessing user is already connected + + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + + + + Action + + + + Allow access + + + + Deny access + + + + Ask logged on user for permission + + + + None (rule disabled) + + + + Accessing user + + + + Accessing computer + + + + Local (logged on) user + + + + Local computer + + + + Always process rule and ignore conditions + + + + No user logged on + + + + Accessing user has one or more groups in common with local (logged on) user + + + + Accessing computer and local computer are at the same location + + + + is located at + + + + + AccessControlRulesTestDialog + + Access control rules test + + + + Accessing user: + + + + Local computer: + + + + Accessing computer: + + + + Please enter the following user and computer information in order to test the configured ruleset. + + + + Local user: + + + + Connected users: + + + + The access in the given scenario is allowed. + + + + The access in the given scenario is denied. + + + + The access in the given scenario needs permission of the logged on user. + + + + ERROR: Unknown action + + + + Test result + + + + + AuthKeysConfigurationPage + + Authentication keys + + + + Introduction + + + + Key file directories + + + + Public key file base directory + دليل ملفات المفاتيح العامة الأساسي + + + Private key file base directory + دليل ملفات المفاتيح الخاصة الأساسي + + + Available authentication keys + + + + Create key pair + + + + Delete key + + + + Import key + + + + Export key + + + + Set access group + + + + Key files (*.pem) + + + + Authentication key name + + + + Please enter the name of the user group or role for which to create an authentication key pair: + + + + Do you really want to delete authentication key "%1/%2"? + + + + Please select a key to delete! + + + + Please enter the name of the user group or role for which to import the authentication key: + + + + Please select a key to export! + + + + Please select a user group which to grant access to key "%1": + + + + Please select a key which to set the access group for! + + + + Please perform the following steps to set up key file authentication: + + + + 1) Create a key pair on the master computer. + + + + 2) Set an access group whose members should be allowed to access other computers. + + + + 3) Export the public key and import it on all client computers with the same name. + + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + + + + + AuthKeysManager + + Please check your permissions. + + + + Key name contains invalid characters! + + + + Invalid key type specified! Please specify "%1" or "%2". + + + + Specified key does not exist! Please use the "list" command to list all installed keys. + + + + One or more key files already exist! Please delete them using the "delete" command. + + + + Creating new key pair for "%1" + + + + Failed to create public or private key! + + + + Newly created key pair has been saved to "%1" and "%2". + + + + Could not remove key file "%1"! + + + + Could not remove key file directory "%1"! + + + + Failed to create directory for output file. + + + + File "%1" already exists. + + + + Failed to write output file. + + + + Key "%1/%2" has been exported to "%3" successfully. + + + + Failed read input file. + + + + File "%1" does not contain a valid private key! + + + + File "%1" does not contain a valid public key! + + + + Failed to create directory for key file. + + + + Failed to write key file "%1". + + + + Failed to set permissions for key file "%1"! + + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + + + + Failed to convert private key to public key + + + + Failed to create directory for private key file "%1". + + + + Failed to save private key in file "%1"! + + + + Failed to set permissions for private key file "%1"! + + + + Failed to create directory for public key file "%1". + + + + Failed to save public key in file "%1"! + + + + Failed to set permissions for public key file "%1"! + + + + Failed to set owner of key file "%1" to "%2". + + + + Failed to set permissions for key file "%1". + + + + Key "%1" is now accessible by user group "%2". + + + + <N/A> + + + + Failed to read key file. + + + + + AuthKeysPlugin + + Create new authentication key pair + + + + Delete authentication key + + + + List authentication keys + + + + Import public or private key + + + + Export public or private key + + + + Extract public key from existing private key + + + + Set user group allowed to access a key + + + + KEY + + + + ACCESS GROUP + + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + + NAME + + + + FILE + + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + + Please specify the command to display help for! + + + + TYPE + + + + PAIR ID + + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + الاسم + + + Type + النوع + + + Access group + + + + Pair ID + + + + + BuiltinDirectoryConfigurationPage + + Computers + + + + Name + الاسم + + + Host address/IP + + + + MAC address + IPعنوان + + + Add new computer + + + + Remove selected computer + + + + New computer + + + + Builtin directory + + + + Locations & computers + + + + Locations + + + + Add new location + + + + Remove selected location + + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + + + + + BuiltinDirectoryPlugin + + Show help for specific command + + + + Import objects from given file + + + + Export objects to given file + + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + النوع + + + Name + الاسم + + + Host address + + + + MAC address + IPعنوان + + + Specified object not found. + + + + File "%1" does not exist! + + + + Can't open file "%1" for reading! + + + + Unknown argument "%1". + + + + Computer "%1" (host address: "%2" MAC address: "%3") + + + + Unclassified object "%1" with ID "%2" + + + + None + + + + Computer + + + + Root + + + + Invalid + + + + Error while parsing line %1. + + + + Network object directory which stores objects in local configuration + + + + Commands for managing the builtin network object directory + + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + + + + Parent UUID + + + + Add a location or computer + + + + Clear all locations and computers + + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + + + + FILE + + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + + + + NAME + + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + + + + + ComputerControlListModel + + Host/IP address: %1 + + + + Active features: %1 + + + + Online and connected + + + + Establishing connection + + + + Computer offline or switched off + + + + Authentication failed or access denied + + + + Disconnected + + + + No user logged on + + + + Logged on user: %1 + + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + + + + Authentication error + خطأ في عملية التوثيق + + + Remote access + التحكم عن بعد + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + + + + Missing network object directory plugin + + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + + + + Add location + + + + Save computer/user list + + + + Select output filename + + + + CSV files (*.csv) + + + + File error + + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + + + + Please specify a valid filename for the configuration export. + + + + Please specify a valid key. + + + + Specified key does not exist in current configuration! + + + + Please specify a valid value. + + + + Configure Veyon at command line + + + + Output file is not writable! + + + + Output directory is not writable! + + + + Configuration file is not readable! + + + + Clear system-wide Veyon configuration + + + + List all configuration keys and values + + + + Import configuration from given file + + + + Export configuration to given file + + + + Read and output configuration value for given key + + + + Write given value to given configuration key + + + + Unset (remove) given configuration key + + + + Commands for managing the configuration of Veyon + + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + + + + + DemoConfigurationPage + + Demo server + خادم العرض + + + Tunables + + + + ms + + + + Key frame interval + + + + Memory limit + + + + MB + + + + Update interval + + + + s + + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + + + + Window demo + عرض على نافذة + + + Give a demonstration by screen broadcasting + + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + + + + Confirm desktop access + تأكيد أمكانية الوصول لسطح المكتب + + + Never for this session + ليس لهذه الحلقة + + + Always for this session + دائما لهذه الحلقة + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + + DesktopServicesConfigurationPage + + Programs & websites + + + + Predefined programs + + + + Name + الاسم + + + Path + + + + Add new program + + + + Remove selected program + + + + Predefined websites + + + + Remove selected website + + + + URL + + + + New program + + + + New website + + + + + DesktopServicesFeaturePlugin + + Run program + + + + Open website + + + + Click this button to open a website on all computers. + + + + Start programs and services in user desktop + + + + Click this button to run a program on all computers. + + + + Run program "%1" + + + + Custom program + + + + Open website "%1" + + + + Custom website + + + + + DocumentationFigureCreator + + Teacher + مدرِّس + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + + + + Port: + + + + Password: + + + + + FeatureControl + + Feature control + + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + أدلة + + + Destination directory + + + + Default source directory + + + + Options + + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + + + + Select one or more files to transfer + + + + Transfer files to remote computer + + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + واجهة المستخدم + + + Language: + اللغة : + + + Use system language setting + + + + Veyon + + + + Logging + دخول + + + Log file directory + دليل ملف الدخول + + + Log level + مستوى الدخول + + + Nothing + لا شيء + + + Only critical messages + فقط الرسائل الحرجة + + + Errors and critical messages + أخطاء ورسائل حرجة + + + Warnings and errors + تحذيرات وأخطاء + + + Information, warnings and errors + المعلومات، والتحذيرات والأخطاء + + + Debug messages and everything else + رسائل الأخطاء وما عداها + + + Limit log file size + تحديد حجم ملف الدخول + + + Clear all log files + مسح كافة ملفات الدخول + + + Log to standard error output + الدخول إلى خرج الاخطاء القياسية + + + Network object directory + + + + Backend: + + + + Update interval: + + + + %1 service + + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + مسح ملفات الدخول + + + All log files were cleared successfully. + تمت إزالة جميع ملفات الدخول بنجاح + + + Error + خطأ + + + Could not remove all log files. + تعذرت إزالة كافة ملفات الدخول + + + MB + + + + Rotate log files + + + + x + + + + seconds + ثواني + + + Write to logging system of operating system + + + + Authentication + توثيق + + + Method: + + + + Logon authentication + توثيق الدخول + + + Key file authentication + توثيق ملف المفاتيح + + + Test + إختبار + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + + + + + LdapConfigurationPage + + Basic settings + + + + General + عام + + + LDAP server and port + + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + + + + e.g. OU=Computers + + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + + LDAP base DN test failed + + + + LDAP base DN test successful + + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + أدخل اسم المستخدم + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + + + + Please enter a group name whose members to query: + + + + group members + + + + Group not found + + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + + + + users + + + + user groups + + + + computer groups + + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + + + + LDAP %1 test failed + + + + LDAP %1 test successful + + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + + + + TLS certificate verification + + + + System defaults + + + + Never (insecure!) + + + + Custom CA certificate file + + + + None + + + + TLS + + + + SSL + + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + إختبار + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + عرض المساعدة حول الأمر + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + + + + enter search filter... + أدخل مرشح البحث ... + + + + MainToolBar + + Configuration + + + + Disable balloon tooltips + + + + Show icons only + + + + + MainWindow + + MainWindow + الشاشة الرئيسية + + + toolBar + شريط الأدوات + + + General + عام + + + &File + ‎‏&‏ملف‏‎ + + + &Help + ‏&مساعدة + + + &Quit + ‏&خروج + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + تحميل الاعدادات من الملف + + + Ctrl+O + Ctrl+O + + + About Qt + حول"كيو تي" + + + Authentication impossible + يتعذر التوثيق + + + Configuration not writable + تعيئة غير قابلة للكتابة + + + Load settings from file + تحميل الاعدادات من الملف + + + Save settings to file + حفظ الاعدادات إلى الملف + + + Unsaved settings + إعدادات لم يتم حفظها + + + There are unsaved settings. Quit anyway? + هنالك إعدادات لم يتم حفظها. هل ترغب في الخروج على أي حال؟ + + + Veyon Configurator + + + + Service + + + + Master + + + + Access control + + + + About Veyon + حول فيون + + + Auto + + + + About + حول + + + %1 Configurator %2 + + + + JSON files (*.json) + + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + حق الوصول ممنوع + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + + + + Align computers to grid + + + + %1 Configurator + + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + أدلة + + + User configuration + + + + Feature on computer double click: + + + + Features + + + + All features + + + + Disabled features + + + + Screenshots + + + + <no feature> + + + + Basic settings + + + + Behaviour + + + + Enforce selected mode for client computers + + + + Hide local computer + + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + + + + User interface + واجهة المستخدم + + + Background color + + + + Thumbnail update interval + + + + ms + + + + Program start + + + + Modes and features + + + + User and computer name + + + + Only user name + + + + Only computer name + + + + Computer thumbnail caption + + + + Text color + + + + Sort order + + + + Computer and user name + + + + Computer locations + + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + + + + Name: + + + + + PasswordDialog + + Username + اسم المستخدم + + + Password + كلمة السر + + + Veyon Logon + + + + Authentication error + خطأ في عملية التوثيق + + + Logon failed with given username and password. Please try again! + + + + Please enter your username and password in order to access computers. + يرجى إدخال اسم المستخدم وكلمة المرور من أجل الوصول إلى الحواسيب. + + + + PowerControlFeaturePlugin + + Power on + تشغيل + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + انقر فوق هذا الزر لتشغيل كل الحواسيب. بهذه الطريقة لا تحتاج إلى تشغيل كل حاسوب يدويا. + + + Reboot + إعادة تشغيل الحاسب + + + Click this button to reboot all computers. + انقر على هذا الزر لإعادة تشغيل كافة الحواسيب. + + + Power down + إطفاء + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + إيقاف التشغيل / إعادة تشغيل حاسوب + + + Confirm reboot + تاكيد إعادة التشغيل + + + Confirm power down + تاكيد إيقاف التشغيل + + + Do you really want to reboot the selected computers? + هل تريد حقًا إعادة تشغيل الحاسوب المحدد؟ + + + Do you really want to power down the selected computer? + هل تريد حقًا إيقاف تشغيل الحاسوب المحدد؟ + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + إطفاء + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + عرض عن بعد + + + Open a remote view for a computer without interaction. + + + + Remote control + تحكم من بُعد + + + Open a remote control window for a computer. + افتح نافذة التحكم عن بعد لحاسوب + + + Remote access + التحكم عن بعد + + + Remote view or control a computer + عرض عن بعد أو التحكم في الحاسوب + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + عرض المساعدة حول الأمر + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + معاينة فقط + + + Remote control + تحكم من بُعد + + + Send shortcut + إرسال اختصار + + + Fullscreen + ملء الشاشة + + + Window + نافذة + + + Ctrl+Alt+Del + + + + Ctrl+Esc + + + + Alt+Tab + + + + Alt+F4 + + + + Win+Tab + + + + Win + + + + Menu + قائمة + + + Alt+Ctrl+F1 + + + + Connecting %1 + توصيل 1%‏ + + + Connected. + متصل + + + Screenshot + لقطة شاشة + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + الرجاء إدخال برامج أو أوامر لتشغيلها على الحاسوب (الحواسيب) المحددة. يمكنك فصل العديد من البرامج / الأوامر بواسطة سطر. + + + Run programs + + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + + + + Unlock + + + + Lock screen and input devices of a computer + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + غير معروف + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + لقطة شاشة + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + لقطة شاشة + + + Use this function to take a screenshot of selected computers. + + + + Screenshots taken + + + + Screenshot of %1 computer have been taken successfully. + + + + Take screenshots of computers and save them locally. + + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + المستخدم + + + Computer: + + + + Date: + التاريخ + + + Time: + الزمن + + + Show + عرض + + + Delete + حذف + + + Screenshot + لقطة شاشة + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + عام + + + Autostart + التشغيل آليا + + + Hide tray icon + إخفاء أيقونة الصينية + + + Start service + بدء الخدمة + + + Stopped + توقف + + + Stop service + إيقاف الخدمة + + + State: + بيان:‏ + + + Enable firewall exception + تمكين إستثناء الجدار الناري + + + Allow connections from localhost only + السماح بالتوصيل من المضيف المحلي فقط + + + VNC server + + + + Plugin: + + + + Restart %1 Service + + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + + Running + تشغيل + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + خادم العرض + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + + + + Stopping service %1 + + + + Registering service %1 + + + + Unregistering service %1 + + + + Service control + + + + + ServiceControlPlugin + + Service is running + + + + Service is not running + + + + Configure and control Veyon service + + + + Register Veyon Service + + + + Unregister Veyon Service + + + + Start Veyon Service + + + + Stop Veyon Service + + + + Restart Veyon Service + + + + Query status of Veyon Service + + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + إرسال رسالة نصية + + + Use the field below to type your message which will be sent to all selected users. + استخدم الحقل أدناه لطباعة رسالتك التي سيتم ارسالها الى كافة المستخدمين المختارين + + + + TextMessageFeaturePlugin + + Text message + رسالة نصيِّة + + + Use this function to send a text message to all users e.g. to assign them new tasks. + + + + Message from teacher + رسالة من المدرّس + + + Send a message to a user + + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + تمكين إلتقاط صورة نوافذ طبقية (شبه شفافة)‏ + + + Poll full screen (leave this enabled per default) + + + + Low accuracy (turbo mode) + دقة منخفضة (وضعية تيربو)‏ + + + Builtin UltraVNC server configuration + + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + لا يوجد وصول للكتابة + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + تعذر حفظ إعداداتك الشخصية! الرجاء التحقق من مسار ملف ضبط المستخدم باستخدام٪ 1 مكون. + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + اسم المستخدم + + + Password + كلمة السر + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + التحكم في جلسة عمل المستخدم + + + + VeyonCore + + [OK] + [حسنا] + + + [FAIL] + [فشل] + + + Invalid command! + أمر خاطئ! + + + Available commands: + الأوامر المتاحة: + + + Invalid arguments given + + + + Not enough arguments given - use "%1 help" for more information + + + + Unknown result! + نتيجة غير معروفة! + + + Available modules: + + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + + + + INFO + + + + ERROR + + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + + + + + VncViewWidget + + Establishing connection to %1 ... + انشاء اتصال بـ 1% + + + + WebApiConfigurationPage + + Web API + + + + General + عام + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + عام + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + + + + Custom x11vnc parameters: + + + + Do not use X Damage extension + + + + diff --git a/translations/veyon_bg.ts b/translations/veyon_bg.ts new file mode 100644 index 0000000..2a8bd59 --- /dev/null +++ b/translations/veyon_bg.ts @@ -0,0 +1,4083 @@ + + + + + AboutDialog + + About + За програмата + + + Translation + Превод + + + License + Лиценз + + + About Veyon + За Veyron + + + Contributors + Автори + + + Version: + Версия: + + + Website: + Уебсайт: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Настоящият език все още не е преведен (или местен английски). + + Ако се интересувате от превода на Veyon на вашия местен или друг език или искате да подобрите съществуващ превод, моля, свържете се с разработчик на Veyon! + + + About %1 %2 + Относно %1 %2 + + + Support Veyon project with a donation + Подкрепете проект Veyon с дарение + + + + AccessControlPage + + Computer access control + Контрол на достъпа на компютъра + + + Grant access to every authenticated user (default) + Дайте достъп на всеки удостоверен потребител (по подразбиране) + + + Test + Тест + + + Process access control rules + Обработвайте правила за контрол на достъпа + + + User groups authorized for computer access + Потребителски групи, оторизирани за достъп до компютър + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Моля, добавете групите, чиито членове трябва да бъдат упълномощени за достъп до компютри във вашата Veyon мрежа. + + + Authorized user groups + Оторизирани потребителски групи + + + All groups + Всички групи + + + Access control rules + Правила за контрол на достъпа + + + Add access control rule + Добави правило за контрол на достъпа + + + Remove access control rule + Премахни правило за контрол на достъпа + + + Move selected rule down + Премести надолу + + + Move selected rule up + Премести нагоре + + + Edit selected rule + Промени + + + Enter username + Въведи потребителско име + + + Please enter a user login name whose access permissions to test: + Моля, въведете потребителско име за вход, чиито разрешения за достъп да тествате: + + + Access allowed + Достъпът е разрешен + + + The specified user is allowed to access computers with this configuration. + На посочения потребител е разрешен достъпа до компютри с тази конфигурация. + + + Access denied + Достъпът е отказан + + + The specified user is not allowed to access computers with this configuration. + Посоченият потребител няма право да получава достъп до компютри с тази конфигурация. + + + Enable usage of domain groups + Активиране на използването на групи от домейни + + + User groups backend: + Потребителски групи: + + + Missing user groups backend + Липсват потребителски групи + + + No default user groups plugin was found. Please check your installation! + Не е намерен плъгин за групи потребители по подразбиране. Моля, проверете вашата инсталация! + + + Restrict access to members of specific user groups + Ограничете достъпа до членове на определени потребителски групи + + + + AccessControlRuleEditDialog + + Edit access control rule + Промени + + + General + Общи настройки + + + enter a short name for the rule here + въведете кратко име за правилото + + + Rule name: + Име на правило: + + + enter a description for the rule here + въведете описание на правилото + + + Rule description: + Описание на правилото: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Invert all conditions ("is/has" interpreted as "is/has not") + + + Conditions + Условия + + + is member of group + е член на групата + + + Accessing computer is localhost + Достъпът до компютъра е localhost + + + Accessing user is logged on user + Достъп като влезлия потребител + + + Accessing user is already connected + Вече сте свързан + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + + + Action + Действие + + + Allow access + Позволи достъп + + + Deny access + Забрани достъп + + + Ask logged on user for permission + Попитайте влезлия потребител за разрешение + + + None (rule disabled) + Няма (правилото е деактивирано) + + + Accessing user + Достъп до потребител + + + Accessing computer + Достъп до компютър + + + Local (logged on) user + Локален (влязъл) потребител + + + Local computer + Локален компютър + + + Always process rule and ignore conditions + Винаги използвай правилото и игнорирай условията + + + No user logged on + Няма потребител + + + Accessing user has one or more groups in common with local (logged on) user + Accessing user has one or more groups in common with local (logged on) user + + + Accessing computer and local computer are at the same location + Accessing computer and local computer are at the same location + + + is located at + is located at + + + + AccessControlRulesTestDialog + + Access control rules test + Access control rules test + + + Accessing user: + Accessing user: + + + Local computer: + Local computer: + + + Accessing computer: + Accessing computer: + + + Please enter the following user and computer information in order to test the configured ruleset. + Please enter the following user and computer information in order to test the configured ruleset. + + + Local user: + Local user: + + + Connected users: + Connected users: + + + The access in the given scenario is allowed. + The access in the given scenario is allowed. + + + The access in the given scenario is denied. + The access in the given scenario is denied. + + + The access in the given scenario needs permission of the logged on user. + The access in the given scenario needs permission of the logged on user. + + + ERROR: Unknown action + ERROR: Unknown action + + + Test result + Test result + + + + AuthKeysConfigurationPage + + Authentication keys + Authentication keys + + + Introduction + Introduction + + + Key file directories + Key file directories + + + Public key file base directory + Public key file base directory + + + Private key file base directory + Private key file base directory + + + Available authentication keys + Available authentication keys + + + Create key pair + Create key pair + + + Delete key + Delete key + + + Import key + Import key + + + Export key + Export key + + + Set access group + Set access group + + + Key files (*.pem) + Key files (*.pem) + + + Authentication key name + Authentication key name + + + Please enter the name of the user group or role for which to create an authentication key pair: + Please enter the name of the user group or role for which to create an authentication key pair: + + + Do you really want to delete authentication key "%1/%2"? + Do you really want to delete authentication key "%1/%2"? + + + Please select a key to delete! + Please select a key to delete! + + + Please enter the name of the user group or role for which to import the authentication key: + Please enter the name of the user group or role for which to import the authentication key: + + + Please select a key to export! + Please select a key to export! + + + Please select a user group which to grant access to key "%1": + Please select a user group which to grant access to key "%1": + + + Please select a key which to set the access group for! + Please select a key which to set the access group for! + + + Please perform the following steps to set up key file authentication: + Please perform the following steps to set up key file authentication: + + + 1) Create a key pair on the master computer. + 1) Create a key pair on the master computer. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Set an access group whose members should be allowed to access other computers. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Export the public key and import it on all client computers with the same name. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + + + + AuthKeysManager + + Please check your permissions. + Please check your permissions. + + + Key name contains invalid characters! + Key name contains invalid characters! + + + Invalid key type specified! Please specify "%1" or "%2". + Invalid key type specified! Please specify "%1" or "%2". + + + Specified key does not exist! Please use the "list" command to list all installed keys. + Specified key does not exist! Please use the "list" command to list all installed keys. + + + One or more key files already exist! Please delete them using the "delete" command. + One or more key files already exist! Please delete them using the "delete" command. + + + Creating new key pair for "%1" + Creating new key pair for "%1" + + + Failed to create public or private key! + Failed to create public or private key! + + + Newly created key pair has been saved to "%1" and "%2". + Newly created key pair has been saved to "%1" and "%2". + + + Could not remove key file "%1"! + Could not remove key file "%1"! + + + Could not remove key file directory "%1"! + Could not remove key file directory "%1"! + + + Failed to create directory for output file. + Failed to create directory for output file. + + + File "%1" already exists. + File "%1" already exists. + + + Failed to write output file. + Failed to write output file. + + + Key "%1/%2" has been exported to "%3" successfully. + Key "%1/%2" has been exported to "%3" successfully. + + + Failed read input file. + Failed read input file. + + + File "%1" does not contain a valid private key! + File "%1" does not contain a valid private key! + + + File "%1" does not contain a valid public key! + File "%1" does not contain a valid public key! + + + Failed to create directory for key file. + Failed to create directory for key file. + + + Failed to write key file "%1". + Failed to write key file "%1". + + + Failed to set permissions for key file "%1"! + Failed to set permissions for key file "%1"! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + + + Failed to convert private key to public key + Failed to convert private key to public key + + + Failed to create directory for private key file "%1". + Failed to create directory for private key file "%1". + + + Failed to save private key in file "%1"! + Failed to save private key in file "%1"! + + + Failed to set permissions for private key file "%1"! + Failed to set permissions for private key file "%1"! + + + Failed to create directory for public key file "%1". + Failed to create directory for public key file "%1". + + + Failed to save public key in file "%1"! + Failed to save public key in file "%1"! + + + Failed to set permissions for public key file "%1"! + Failed to set permissions for public key file "%1"! + + + Failed to set owner of key file "%1" to "%2". + Failed to set owner of key file "%1" to "%2". + + + Failed to set permissions for key file "%1". + Failed to set permissions for key file "%1". + + + Key "%1" is now accessible by user group "%2". + Key "%1" is now accessible by user group "%2". + + + <N/A> + <N/A> + + + Failed to read key file. + Failed to read key file. + + + + AuthKeysPlugin + + Create new authentication key pair + Create new authentication key pair + + + Delete authentication key + Delete authentication key + + + List authentication keys + List authentication keys + + + Import public or private key + Import public or private key + + + Export public or private key + Export public or private key + + + Extract public key from existing private key + Extract public key from existing private key + + + Set user group allowed to access a key + Set user group allowed to access a key + + + KEY + KEY + + + ACCESS GROUP + ACCESS GROUP + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + NAME + NAME + + + FILE + FILE + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + Please specify the command to display help for! + Please specify the command to display help for! + + + TYPE + TYPE + + + PAIR ID + PAIR ID + + + Command line support for managing authentication keys + Command line support for managing authentication keys + + + Commands for managing authentication keys + Commands for managing authentication keys + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + AuthKeysTableModel + + Name + Name + + + Type + Type + + + Access group + Access group + + + Pair ID + Pair ID + + + + BuiltinDirectoryConfigurationPage + + Computers + Computers + + + Name + Name + + + Host address/IP + Host address/IP + + + MAC address + MAC address + + + Add new computer + Add new computer + + + Remove selected computer + Remove selected computer + + + New computer + New computer + + + Builtin directory + Builtin directory + + + Locations & computers + Locations & computers + + + Locations + Locations + + + Add new location + Add new location + + + Remove selected location + Remove selected location + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + New location + New location + + + + BuiltinDirectoryPlugin + + Show help for specific command + Show help for specific command + + + Import objects from given file + Import objects from given file + + + Export objects to given file + Export objects to given file + + + Invalid type specified. Valid values are "%1" or "%2". + Invalid type specified. Valid values are "%1" or "%2". + + + Type + Type + + + Name + Name + + + Host address + Host address + + + MAC address + MAC address + + + Specified object not found. + Specified object not found. + + + File "%1" does not exist! + File "%1" does not exist! + + + Can't open file "%1" for reading! + Can't open file "%1" for reading! + + + Unknown argument "%1". + Unknown argument "%1". + + + Computer "%1" (host address: "%2" MAC address: "%3") + Computer "%1" (host address: "%2" MAC address: "%3") + + + Unclassified object "%1" with ID "%2" + Unclassified object "%1" with ID "%2" + + + None + None + + + Computer + Computer + + + Root + Root + + + Invalid + Invalid + + + Error while parsing line %1. + Error while parsing line %1. + + + Network object directory which stores objects in local configuration + Network object directory which stores objects in local configuration + + + Commands for managing the builtin network object directory + Commands for managing the builtin network object directory + + + No format string or regular expression specified! + No format string or regular expression specified! + + + Can't open file "%1" for writing! + Can't open file "%1" for writing! + + + No format string specified! + No format string specified! + + + Object UUID + Object UUID + + + Parent UUID + Parent UUID + + + Add a location or computer + Add a location or computer + + + Clear all locations and computers + Clear all locations and computers + + + Dump all or individual locations and computers + Dump all or individual locations and computers + + + List all locations and computers + List all locations and computers + + + Remove a location or computer + Remove a location or computer + + + Location "%1" + Location "%1" + + + Builtin (computers and locations in local configuration) + Builtin (computers and locations in local configuration) + + + Location + Location + + + FILE + FILE + + + LOCATION + LOCATION + + + FORMAT-STRING-WITH-PLACEHOLDERS + FORMAT-STRING-WITH-PLACEHOLDERS + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + Import simple CSV file to a single room + Import simple CSV file to a single room + + + Import CSV file with location name in first column + Import CSV file with location name in first column + + + Import text file with with key/value pairs using regular expressions + Import text file with with key/value pairs using regular expressions + + + Import arbitrarily formatted data + Import arbitrarily formatted data + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + Export all objects to a CSV file + Export all objects to a CSV file + + + Export all computers in a specific location to a CSV file + Export all computers in a specific location to a CSV file + + + TYPE + TYPE + + + NAME + NAME + + + PARENT + PARENT + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + Add a room + Add a room + + + Add a computer to room %1 + Add a computer to room %1 + + + OBJECT + OBJECT + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + Remove a computer by name + Remove a computer by name + + + Remove an object by UUID + Remove an object by UUID + + + "Room 01" + "Room 01" + + + "Computer 01" + "Computer 01" + + + HOST ADDRESS + HOST ADDRESS + + + MAC ADDRESS + MAC ADDRESS + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Builtin VNC server (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Builtin VNC server (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Host/IP address: %1 + + + Active features: %1 + Active features: %1 + + + Online and connected + Online and connected + + + Establishing connection + Establishing connection + + + Computer offline or switched off + Computer offline or switched off + + + Authentication failed or access denied + Authentication failed or access denied + + + Disconnected + Disconnected + + + No user logged on + Няма потребител + + + Logged on user: %1 + Logged on user: %1 + + + Location: %1 + Location: %1 + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 Service %2 at %3:%4 + + + Authentication error + Authentication error + + + Remote access + Remote access + + + User "%1" at host "%2" is now accessing this computer. + User "%1" at host "%2" is now accessing this computer. + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + Access control error + Access control error + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + Active connections: + + + + + ComputerManager + + User + User + + + Missing network object directory plugin + Missing network object directory plugin + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + Location detection failed + Location detection failed + + + Computer name;Hostname;User + Computer name;Hostname;User + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + ComputerSelectPanel + + Computer search + Computer search + + + Add location + Add location + + + Save computer/user list + Save computer/user list + + + Select output filename + Select output filename + + + CSV files (*.csv) + CSV files (*.csv) + + + File error + File error + + + Could not write the computer and users list to %1! Please check the file access permissions. + Could not write the computer and users list to %1! Please check the file access permissions. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Please specify an existing configuration file to import. + + + Please specify a valid filename for the configuration export. + Please specify a valid filename for the configuration export. + + + Please specify a valid key. + Please specify a valid key. + + + Specified key does not exist in current configuration! + Specified key does not exist in current configuration! + + + Please specify a valid value. + Please specify a valid value. + + + Configure Veyon at command line + Configure Veyon at command line + + + Output file is not writable! + Output file is not writable! + + + Output directory is not writable! + Output directory is not writable! + + + Configuration file is not readable! + Configuration file is not readable! + + + Clear system-wide Veyon configuration + Clear system-wide Veyon configuration + + + List all configuration keys and values + List all configuration keys and values + + + Import configuration from given file + Import configuration from given file + + + Export configuration to given file + Export configuration to given file + + + Read and output configuration value for given key + Read and output configuration value for given key + + + Write given value to given configuration key + Write given value to given configuration key + + + Unset (remove) given configuration key + Unset (remove) given configuration key + + + Commands for managing the configuration of Veyon + Commands for managing the configuration of Veyon + + + Upgrade and save configuration of program and plugins + Upgrade and save configuration of program and plugins + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + Could not modify the autostart property for the %1 Service. + + + Could not configure the firewall configuration for the %1 Server. + Could not configure the firewall configuration for the %1 Server. + + + Could not configure the firewall configuration for the %1 Worker. + Could not configure the firewall configuration for the %1 Worker. + + + Configuration is not writable. Please check your permissions! + Configuration is not writable. Please check your permissions! + + + Could not apply platform-specific configuration settings. + Could not apply platform-specific configuration settings. + + + + DemoClient + + %1 Demo + %1 Demo + + + + DemoConfigurationPage + + Demo server + Demo server + + + Tunables + Tunables + + + ms + ms + + + Key frame interval + Key frame interval + + + Memory limit + Memory limit + + + MB + MB + + + Update interval + Update interval + + + s + s + + + Slow down thumbnail updates while demo is running + Slow down thumbnail updates while demo is running + + + + DemoFeaturePlugin + + Stop demo + Stop demo + + + Window demo + Window demo + + + Give a demonstration by screen broadcasting + Give a demonstration by screen broadcasting + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + Desktop access dialog + + + Confirm desktop access + Confirm desktop access + + + Never for this session + Never for this session + + + Always for this session + Always for this session + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programs & websites + + + Predefined programs + Predefined programs + + + Name + Name + + + Path + Path + + + Add new program + Add new program + + + Remove selected program + Remove selected program + + + Predefined websites + Predefined websites + + + Remove selected website + Remove selected website + + + URL + URL + + + New program + New program + + + New website + New website + + + + DesktopServicesFeaturePlugin + + Run program + Run program + + + Open website + Open website + + + Click this button to open a website on all computers. + Click this button to open a website on all computers. + + + Start programs and services in user desktop + Start programs and services in user desktop + + + Click this button to run a program on all computers. + Click this button to run a program on all computers. + + + Run program "%1" + Run program "%1" + + + Custom program + Custom program + + + Open website "%1" + Open website "%1" + + + Custom website + Custom website + + + + DocumentationFigureCreator + + Teacher + Teacher + + + Room %1 + Room %1 + + + Please complete all tasks within the next 5 minutes. + Please complete all tasks within the next 5 minutes. + + + Custom website + Custom website + + + Open file manager + Open file manager + + + Start learning tool + Start learning tool + + + Play tutorial video + Play tutorial video + + + Custom program + Custom program + + + Handout + Handout + + + Texts to read + Texts to read + + + generic-student-user + generic-student-user + + + + ExternalVncServer + + External VNC server + External VNC server + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + External VNC server configuration + + + Port: + Port: + + + Password: + Password: + + + + FeatureControl + + Feature control + Feature control + + + + FileTransferConfigurationPage + + File transfer + File transfer + + + Directories + Directories + + + Destination directory + + + + Default source directory + + + + Options + Options + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + Could not open file "%1" for reading! Please check your permissions! + + + + FileTransferDialog + + File transfer + File transfer + + + Options + Options + + + Transfer only + Transfer only + + + Transfer and open file(s) with associated program + Transfer and open file(s) with associated program + + + Transfer and open destination folder + Transfer and open destination folder + + + Files + Files + + + Start + Start + + + Overwrite existing files + Overwrite existing files + + + + FileTransferPlugin + + File transfer + File transfer + + + Click this button to transfer files from your computer to all computers. + Click this button to transfer files from your computer to all computers. + + + Select one or more files to transfer + Select one or more files to transfer + + + Transfer files to remote computer + Transfer files to remote computer + + + Received file "%1". + Received file "%1". + + + Could not receive file "%1" as it already exists. + Could not receive file "%1" as it already exists. + + + Could not receive file "%1" as it could not be opened for writing! + Could not receive file "%1" as it could not be opened for writing! + + + + GeneralConfigurationPage + + User interface + User interface + + + Language: + Language: + + + Use system language setting + Use system language setting + + + Veyon + Veyon + + + Logging + Logging + + + Log file directory + Log file directory + + + Log level + Log level + + + Nothing + Nothing + + + Only critical messages + Only critical messages + + + Errors and critical messages + Errors and critical messages + + + Warnings and errors + Warnings and errors + + + Information, warnings and errors + Information, warnings and errors + + + Debug messages and everything else + Debug messages and everything else + + + Limit log file size + Limit log file size + + + Clear all log files + Clear all log files + + + Log to standard error output + Log to standard error output + + + Network object directory + Network object directory + + + Backend: + Backend: + + + Update interval: + Update interval: + + + %1 service + %1 service + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + Log files cleared + Log files cleared + + + All log files were cleared successfully. + All log files were cleared successfully. + + + Error + Error + + + Could not remove all log files. + Could not remove all log files. + + + MB + MB + + + Rotate log files + Rotate log files + + + x + x + + + seconds + seconds + + + Write to logging system of operating system + Write to logging system of operating system + + + Authentication + Authentication + + + Method: + Method: + + + Logon authentication + Logon authentication + + + Key file authentication + Key file authentication + + + Test + Тест + + + Authentication is set up properly on this computer. + Authentication is set up properly on this computer. + + + Authentication keys are not set up properly on this computer. + Authentication keys are not set up properly on this computer. + + + Authentication test + Authentication test + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + Browse LDAP + + + + LdapClient + + LDAP error description: %1 + LDAP error description: %1 + + + + LdapConfigurationPage + + Basic settings + Basic settings + + + General + Общи настройки + + + LDAP server and port + LDAP server and port + + + Bind DN + Bind DN + + + Bind password + Bind password + + + Anonymous bind + Anonymous bind + + + Use bind credentials + Use bind credentials + + + Base DN + Base DN + + + Fixed base DN + Fixed base DN + + + e.g. dc=example,dc=org + e.g. dc=example,dc=org + + + Discover base DN by naming context + Discover base DN by naming context + + + e.g. namingContexts or defaultNamingContext + e.g. namingContexts or defaultNamingContext + + + Environment settings + Environment settings + + + Object trees + Object trees + + + Computer tree + Computer tree + + + e.g. OU=Groups + e.g. OU=Groups + + + User tree + User tree + + + e.g. OU=Users + e.g. OU=Users + + + e.g. OU=Computers + e.g. OU=Computers + + + Group tree + Group tree + + + Perform recursive search operations in object trees + Perform recursive search operations in object trees + + + Object attributes + Object attributes + + + e.g. hwAddress + e.g. hwAddress + + + e.g. member or memberUid + e.g. member or memberUid + + + e.g. dNSHostName + e.g. dNSHostName + + + Computer MAC address attribute + Computer MAC address attribute + + + Group member attribute + Group member attribute + + + e.g. uid or sAMAccountName + e.g. uid or sAMAccountName + + + Advanced settings + Advanced settings + + + Optional object filters + Optional object filters + + + Filter for user groups + Filter for user groups + + + Filter for users + Filter for users + + + Filter for computer groups + Filter for computer groups + + + Group member identification + Group member identification + + + Distinguished name (Samba/AD) + Distinguished name (Samba/AD) + + + List all groups of a user + List all groups of a user + + + List all groups of a computer + List all groups of a computer + + + Get computer object by IP address + Get computer object by IP address + + + LDAP connection failed + LDAP connection failed + + + LDAP bind failed + LDAP bind failed + + + LDAP bind successful + LDAP bind successful + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + LDAP base DN test failed + LDAP base DN test failed + + + LDAP base DN test successful + LDAP base DN test successful + + + LDAP naming context test failed + LDAP naming context test failed + + + LDAP naming context test successful + LDAP naming context test successful + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + user tree + user tree + + + group tree + group tree + + + computer tree + computer tree + + + Enter username + Въведи потребителско име + + + Please enter a user login name (wildcards allowed) which to query: + Please enter a user login name (wildcards allowed) which to query: + + + user objects + user objects + + + Enter group name + Enter group name + + + Please enter a group name whose members to query: + Please enter a group name whose members to query: + + + group members + group members + + + Group not found + Group not found + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + Enter computer name + Enter computer name + + + computer objects + computer objects + + + Enter computer DN + Enter computer DN + + + Please enter the DN of a computer whose MAC address to query: + Please enter the DN of a computer whose MAC address to query: + + + computer MAC addresses + computer MAC addresses + + + users + users + + + user groups + user groups + + + computer groups + computer groups + + + Please enter a user login name whose group memberships to query: + Please enter a user login name whose group memberships to query: + + + groups of user + groups of user + + + User not found + User not found + + + groups of computer + groups of computer + + + Computer not found + Computer not found + + + Enter computer IP address + Enter computer IP address + + + Please enter a computer IP address which to resolve to an computer object: + Please enter a computer IP address which to resolve to an computer object: + + + computers + computers + + + LDAP %1 test failed + LDAP %1 test failed + + + LDAP %1 test successful + LDAP %1 test successful + + + The %1 has been queried successfully and %2 entries were found. + The %1 has been queried successfully and %2 entries were found. + + + %1 %2 have been queried successfully: + +%3 + %1 %2 have been queried successfully: + +%3 + + + LDAP filter test failed + LDAP filter test failed + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + LDAP filter test successful + LDAP filter test successful + + + %1 %2 have been queried successfully using the configured filter. + %1 %2 have been queried successfully using the configured filter. + + + (only if different from group tree) + (only if different from group tree) + + + Computer group tree + Computer group tree + + + computer group tree + computer group tree + + + Filter for computers + Filter for computers + + + e.g. room or computerLab + e.g. room or computerLab + + + Integration tests + Integration tests + + + Computer groups + Computer groups + + + e.g. name or description + e.g. name or description + + + Filter for computer containers + Filter for computer containers + + + Computer containers or OUs + Computer containers or OUs + + + Connection security + Connection security + + + TLS certificate verification + TLS certificate verification + + + System defaults + System defaults + + + Never (insecure!) + Never (insecure!) + + + Custom CA certificate file + Custom CA certificate file + + + None + None + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + e.g. (objectClass=computer) + + + e.g. (objectClass=group) + e.g. (objectClass=group) + + + e.g. (objectClass=person) + e.g. (objectClass=person) + + + e.g. (objectClass=room) or (objectClass=computerLab) + e.g. (objectClass=room) or (objectClass=computerLab) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + Certificate files (*.pem) + Certificate files (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + Encryption protocol + Encryption protocol + + + Computer location attribute + Computer location attribute + + + Computer display name attribute + Computer display name attribute + + + Location name attribute + Location name attribute + + + e.g. cn or displayName + e.g. cn or displayName + + + Computer locations identification + Computer locations identification + + + Identify computer locations (e.g. rooms) via: + Identify computer locations (e.g. rooms) via: + + + Location attribute in computer objects + Location attribute in computer objects + + + List all entries of a location + List all entries of a location + + + List all locations + List all locations + + + Enter computer display name + Enter computer display name + + + Please enter a computer display name to query: + Please enter a computer display name to query: + + + Enter computer location name + Enter computer location name + + + Please enter the name of a computer location (wildcards allowed): + Please enter the name of a computer location (wildcards allowed): + + + computer locations + computer locations + + + Enter location name + Enter location name + + + Please enter the name of a location whose entries to query: + Please enter the name of a location whose entries to query: + + + location entries + location entries + + + LDAP test failed + LDAP test failed + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + and + and + + + LDAP test successful + LDAP test successful + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + Browse + Browse + + + Test + Тест + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + Computer hostname attribute + Computer hostname attribute + + + Please enter a computer hostname to query: + Please enter a computer hostname to query: + + + Invalid hostname + Invalid hostname + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + Enter hostname + Enter hostname + + + Please enter a computer hostname whose group memberships to query: + Please enter a computer hostname whose group memberships to query: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + Hostname lookup failed + Hostname lookup failed + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + User login name attribute + User login name attribute + + + Configured attribute for user login name or computer hostname (OpenLDAP) + Configured attribute for user login name or computer hostname (OpenLDAP) + + + computer containers + computer containers + + + + LdapPlugin + + Auto-configure the base DN via naming context + Auto-configure the base DN via naming context + + + Query objects from LDAP directory + Query objects from LDAP directory + + + Show help about command + Show help about command + + + Commands for configuring and testing LDAP/AD integration + Commands for configuring and testing LDAP/AD integration + + + Basic LDAP/AD support for Veyon + Basic LDAP/AD support for Veyon + + + %1 (load computers and locations from LDAP/AD) + %1 (load computers and locations from LDAP/AD) + + + %1 (load users and groups from LDAP/AD) + %1 (load users and groups from LDAP/AD) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + No naming context attribute name given - falling back to configured value. + No naming context attribute name given - falling back to configured value. + + + Could not query base DN. Please check your LDAP configuration. + Could not query base DN. Please check your LDAP configuration. + + + Configuring %1 as base DN and disabling naming context queries. + Configuring %1 as base DN and disabling naming context queries. + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + Custom PAM service for user authentication + + + User authentication + User authentication + + + Session management + Session management + + + Display manager users + Display manager users + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Plugin implementing abstract functions for the Linux platform + + + + LocationDialog + + Select location + Select location + + + enter search filter... + enter search filter... + + + + MainToolBar + + Configuration + Configuration + + + Disable balloon tooltips + Disable balloon tooltips + + + Show icons only + Show icons only + + + + MainWindow + + MainWindow + MainWindow + + + toolBar + toolBar + + + General + Общи настройки + + + &File + &File + + + &Help + &Help + + + &Quit + &Quit + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + L&oad settings from file + + + Ctrl+O + Ctrl+O + + + About Qt + About Qt + + + Authentication impossible + Authentication impossible + + + Configuration not writable + Configuration not writable + + + Load settings from file + Load settings from file + + + Save settings to file + Save settings to file + + + Unsaved settings + Unsaved settings + + + There are unsaved settings. Quit anyway? + There are unsaved settings. Quit anyway? + + + Veyon Configurator + Veyon Configurator + + + Service + Service + + + Master + Master + + + Access control + Access control + + + About Veyon + За Veyron + + + Auto + Auto + + + About + За програмата + + + %1 Configurator %2 + %1 Configurator %2 + + + JSON files (*.json) + JSON files (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + Access denied + Достъпът е отказан + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + Screenshots + Screenshots + + + Feature active + Feature active + + + The feature "%1" is still active. Please stop it before closing %2. + The feature "%1" is still active. Please stop it before closing %2. + + + Reset configuration + Reset configuration + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Do you really want to reset the local configuration and revert all settings to their defaults? + + + Search users and computers + Search users and computers + + + Align computers to grid + Align computers to grid + + + %1 Configurator + %1 Configurator + + + Insufficient privileges + Insufficient privileges + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + Only show powered on computers + Only show powered on computers + + + &Save settings to file + &Save settings to file + + + &View + &View + + + &Standard + &Standard + + + &Advanced + &Advanced + + + Use custom computer arrangement + Use custom computer arrangement + + + Locations && computers + Locations && computers + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Directories + + + User configuration + User configuration + + + Feature on computer double click: + Feature on computer double click: + + + Features + Features + + + All features + All features + + + Disabled features + Disabled features + + + Screenshots + Screenshots + + + <no feature> + <no feature> + + + Basic settings + Basic settings + + + Behaviour + Behaviour + + + Enforce selected mode for client computers + Enforce selected mode for client computers + + + Hide local computer + Hide local computer + + + Hide computer filter field + Hide computer filter field + + + Actions such as rebooting or powering down computers + Actions such as rebooting or powering down computers + + + User interface + User interface + + + Background color + Background color + + + Thumbnail update interval + Thumbnail update interval + + + ms + ms + + + Program start + Program start + + + Modes and features + Modes and features + + + User and computer name + User and computer name + + + Only user name + Only user name + + + Only computer name + Only computer name + + + Computer thumbnail caption + Computer thumbnail caption + + + Text color + Text color + + + Sort order + Sort order + + + Computer and user name + Computer and user name + + + Computer locations + Computer locations + + + Show current location only + Show current location only + + + Allow adding hidden locations manually + Allow adding hidden locations manually + + + Hide empty locations + Hide empty locations + + + Show confirmation dialog for potentially unsafe actions + Show confirmation dialog for potentially unsafe actions + + + Perform access control + Perform access control + + + Automatically select current location + Automatically select current location + + + Automatically open computer select panel + Automatically open computer select panel + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + Auto + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + Monitoring + + + Builtin monitoring mode + Builtin monitoring mode + + + This mode allows you to monitor all computers at one or more locations. + This mode allows you to monitor all computers at one or more locations. + + + + NetworkObjectTreeModel + + Locations/Computers + Locations/Computers + + + + OpenWebsiteDialog + + Open website + Open website + + + e.g. Veyon + e.g. Veyon + + + Remember and add to website menu + Remember and add to website menu + + + e.g. www.veyon.io + e.g. www.veyon.io + + + Please enter the URL of the website to open: + Please enter the URL of the website to open: + + + Name: + Name: + + + + PasswordDialog + + Username + Username + + + Password + Password + + + Veyon Logon + Veyon Logon + + + Authentication error + Authentication error + + + Logon failed with given username and password. Please try again! + Logon failed with given username and password. Please try again! + + + Please enter your username and password in order to access computers. + Please enter your username and password in order to access computers. + + + + PowerControlFeaturePlugin + + Power on + Power on + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + Reboot + Reboot + + + Click this button to reboot all computers. + Click this button to reboot all computers. + + + Power down + Power down + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + Power on/down or reboot a computer + Power on/down or reboot a computer + + + Confirm reboot + Confirm reboot + + + Confirm power down + Confirm power down + + + Do you really want to reboot the selected computers? + Do you really want to reboot the selected computers? + + + Do you really want to power down the selected computer? + Do you really want to power down the selected computer? + + + Power on a computer via Wake-on-LAN (WOL) + Power on a computer via Wake-on-LAN (WOL) + + + MAC ADDRESS + MAC ADDRESS + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + Please specify the command to display help for! + Please specify the command to display help for! + + + Invalid MAC address specified! + Invalid MAC address specified! + + + Commands for controlling power status of computers + Commands for controlling power status of computers + + + Power down now + Power down now + + + Install updates and power down + Install updates and power down + + + Power down after user confirmation + Power down after user confirmation + + + Power down after timeout + Power down after timeout + + + The computer was remotely requested to power down. Do you want to power down the computer now? + The computer was remotely requested to power down. Do you want to power down the computer now? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + PowerDownTimeInputDialog + + Power down + Power down + + + Please specify a timeout for powering down the selected computers: + Please specify a timeout for powering down the selected computers: + + + minutes + minutes + + + seconds + seconds + + + + RemoteAccessFeaturePlugin + + Remote view + Remote view + + + Open a remote view for a computer without interaction. + Open a remote view for a computer without interaction. + + + Remote control + Remote control + + + Open a remote control window for a computer. + Open a remote control window for a computer. + + + Remote access + Remote access + + + Remote view or control a computer + Remote view or control a computer + + + Please enter the hostname or IP address of the computer to access: + Please enter the hostname or IP address of the computer to access: + + + Show help about command + Show help about command + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 - %2 Remote Access + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + View only + + + Remote control + Remote control + + + Send shortcut + Send shortcut + + + Fullscreen + Fullscreen + + + Window + Window + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Menu + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Connecting %1 + + + Connected. + Connected. + + + Screenshot + Screenshot + + + Exit + Exit + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + Run programs + Run programs + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + Name: + + + Remember and add to program menu + Remember and add to program menu + + + e.g. VLC + e.g. VLC + + + + ScreenLockFeaturePlugin + + Lock + Lock + + + Unlock + Unlock + + + Lock screen and input devices of a computer + Lock screen and input devices of a computer + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + unknown + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + Screenshot + Screenshot + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + Screenshot + + + Use this function to take a screenshot of selected computers. + Use this function to take a screenshot of selected computers. + + + Screenshots taken + Screenshots taken + + + Screenshot of %1 computer have been taken successfully. + Screenshot of %1 computer have been taken successfully. + + + Take screenshots of computers and save them locally. + Take screenshots of computers and save them locally. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + User: + User: + + + Computer: + Computer: + + + Date: + Date: + + + Time: + Time: + + + Show + Show + + + Delete + Delete + + + Screenshot + Screenshot + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Общи настройки + + + Autostart + Autostart + + + Hide tray icon + Hide tray icon + + + Start service + Start service + + + Stopped + Stopped + + + Stop service + Stop service + + + State: + State: + + + Enable firewall exception + Enable firewall exception + + + Allow connections from localhost only + Allow connections from localhost only + + + VNC server + VNC server + + + Plugin: + Plugin: + + + Restart %1 Service + Restart %1 Service + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + Running + Running + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + Show notification on remote connection + Show notification on remote connection + + + Show notification when an unauthorized access is blocked + Show notification when an unauthorized access is blocked + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + Demo server + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + Starting service %1 + + + Stopping service %1 + Stopping service %1 + + + Registering service %1 + Registering service %1 + + + Unregistering service %1 + Unregistering service %1 + + + Service control + Service control + + + + ServiceControlPlugin + + Service is running + Service is running + + + Service is not running + Service is not running + + + Configure and control Veyon service + Configure and control Veyon service + + + Register Veyon Service + Register Veyon Service + + + Unregister Veyon Service + Unregister Veyon Service + + + Start Veyon Service + Start Veyon Service + + + Stop Veyon Service + Stop Veyon Service + + + Restart Veyon Service + Restart Veyon Service + + + Query status of Veyon Service + Query status of Veyon Service + + + Commands for configuring and controlling Veyon Service + Commands for configuring and controlling Veyon Service + + + + ShellCommandLinePlugin + + Run command file + Run command file + + + File "%1" does not exist! + File "%1" does not exist! + + + Interactive shell and script execution for Veyon Control + Interactive shell and script execution for Veyon Control + + + Commands for shell functionalities + Commands for shell functionalities + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + System tray icon + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + User groups backend for system user groups + + + Default (system user groups) + Default (system user groups) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + Test internal Veyon components and functions + + + Commands for testing internal components and functions of Veyon + Commands for testing internal components and functions of Veyon + + + + TextMessageDialog + + Send text message + Send text message + + + Use the field below to type your message which will be sent to all selected users. + Use the field below to type your message which will be sent to all selected users. + + + + TextMessageFeaturePlugin + + Text message + Text message + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Use this function to send a text message to all users e.g. to assign them new tasks. + + + Message from teacher + Message from teacher + + + Send a message to a user + Send a message to a user + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Enable capturing of layered (semi-transparent) windows + + + Poll full screen (leave this enabled per default) + Poll full screen (leave this enabled per default) + + + Low accuracy (turbo mode) + Low accuracy (turbo mode) + + + Builtin UltraVNC server configuration + Builtin UltraVNC server configuration + + + Enable multi monitor support + Enable multi monitor support + + + Enable Desktop Duplication Engine on Windows 8 and newer + Enable Desktop Duplication Engine on Windows 8 and newer + + + Maximum CPU usage + + + + + UserConfig + + No write access + No write access + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + + + + UserLoginDialog + + User login + User login + + + Please enter a username and password for automatic login on all computers. + Please enter a username and password for automatic login on all computers. + + + Username + Username + + + Password + Password + + + + UserSessionControlPlugin + + Log in + Log in + + + Click this button to log in a specific user on all computers. + Click this button to log in a specific user on all computers. + + + Log off + Log off + + + Click this button to log off users from all computers. + Click this button to log off users from all computers. + + + Confirm user logoff + Confirm user logoff + + + Do you really want to log off the selected users? + Do you really want to log off the selected users? + + + User session control + User session control + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [FAIL] + + + Invalid command! + Invalid command! + + + Available commands: + Available commands: + + + Invalid arguments given + Invalid arguments given + + + Not enough arguments given - use "%1 help" for more information + Not enough arguments given - use "%1 help" for more information + + + Unknown result! + Unknown result! + + + Available modules: + Available modules: + + + No module specified or module not found - available modules are: + No module specified or module not found - available modules are: + + + Plugin not licensed + Plugin not licensed + + + INFO + INFO + + + ERROR + ERROR + + + USAGE + USAGE + + + DESCRIPTION + DESCRIPTION + + + EXAMPLES + EXAMPLES + + + WARNING + WARNING + + + + VeyonServiceControl + + Veyon Service + Veyon Service + + + + VncViewWidget + + Establishing connection to %1 ... + Establishing connection to %1 ... + + + + WebApiConfigurationPage + + Web API + + + + General + Общи настройки + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + s + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + Общи настройки + + + Enable SAS generation by software (Ctrl+Alt+Del) + Enable SAS generation by software (Ctrl+Alt+Del) + + + Screen lock + Screen lock + + + Hide taskbar + Hide taskbar + + + Hide start menu + Hide start menu + + + Hide desktop + Hide desktop + + + User authentication + User authentication + + + Use alternative user authentication mechanism + Use alternative user authentication mechanism + + + User login + User login + + + Input start delay + Input start delay + + + Simulated key presses interval + Simulated key presses interval + + + Confirm legal notice (message displayed before user logs in) + Confirm legal notice (message displayed before user logs in) + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Plugin implementing abstract functions for the Windows platform + + + + WindowsServiceControl + + The service "%1" is already installed. + The service "%1" is already installed. + + + The service "%1" could not be installed. + The service "%1" could not be installed. + + + The service "%1" has been installed successfully. + The service "%1" has been installed successfully. + + + The service "%1" could not be uninstalled. + The service "%1" could not be uninstalled. + + + The service "%1" has been uninstalled successfully. + The service "%1" has been uninstalled successfully. + + + The start type of service "%1" could not be changed. + The start type of service "%1" could not be changed. + + + Service "%1" could not be found. + Service "%1" could not be found. + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Builtin x11vnc server configuration + + + Custom x11vnc parameters: + Custom x11vnc parameters: + + + Do not use X Damage extension + Do not use X Damage extension + + + diff --git a/translations/veyon_ca_ES.ts b/translations/veyon_ca_ES.ts new file mode 100644 index 0000000..c1f15ed --- /dev/null +++ b/translations/veyon_ca_ES.ts @@ -0,0 +1,4056 @@ + + + + + AboutDialog + + About + Quant a + + + Translation + Traducció + + + License + Llicència + + + About Veyon + + + + Contributors + + + + Version: + + + + Website: + + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + + + + About %1 %2 + + + + Support Veyon project with a donation + + + + + AccessControlPage + + Computer access control + + + + Grant access to every authenticated user (default) + + + + Test + Comprova + + + Process access control rules + + + + User groups authorized for computer access + + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + + + + Authorized user groups + + + + All groups + Tots els grups + + + Access control rules + + + + Add access control rule + + + + Remove access control rule + + + + Move selected rule down + + + + Move selected rule up + + + + Edit selected rule + + + + Enter username + + + + Please enter a user login name whose access permissions to test: + + + + Access allowed + + + + The specified user is allowed to access computers with this configuration. + + + + Access denied + + + + The specified user is not allowed to access computers with this configuration. + + + + Enable usage of domain groups + + + + User groups backend: + + + + Missing user groups backend + + + + No default user groups plugin was found. Please check your installation! + + + + Restrict access to members of specific user groups + + + + + AccessControlRuleEditDialog + + Edit access control rule + + + + General + General + + + enter a short name for the rule here + + + + Rule name: + + + + enter a description for the rule here + + + + Rule description: + + + + Invert all conditions ("is/has" interpreted as "is/has not") + + + + Conditions + + + + is member of group + + + + Accessing computer is localhost + + + + Accessing user is logged on user + + + + Accessing user is already connected + + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + + + + Action + + + + Allow access + + + + Deny access + + + + Ask logged on user for permission + + + + None (rule disabled) + + + + Accessing user + + + + Accessing computer + + + + Local (logged on) user + + + + Local computer + + + + Always process rule and ignore conditions + + + + No user logged on + + + + Accessing user has one or more groups in common with local (logged on) user + + + + Accessing computer and local computer are at the same location + + + + is located at + + + + + AccessControlRulesTestDialog + + Access control rules test + + + + Accessing user: + + + + Local computer: + + + + Accessing computer: + + + + Please enter the following user and computer information in order to test the configured ruleset. + + + + Local user: + + + + Connected users: + + + + The access in the given scenario is allowed. + + + + The access in the given scenario is denied. + + + + The access in the given scenario needs permission of the logged on user. + + + + ERROR: Unknown action + + + + Test result + + + + + AuthKeysConfigurationPage + + Authentication keys + + + + Introduction + + + + Key file directories + + + + Public key file base directory + Carpeta del fitxer de la clau pública + + + Private key file base directory + Carpeta del fitxer de la clau privada + + + Available authentication keys + + + + Create key pair + + + + Delete key + + + + Import key + + + + Export key + + + + Set access group + + + + Key files (*.pem) + + + + Authentication key name + + + + Please enter the name of the user group or role for which to create an authentication key pair: + + + + Do you really want to delete authentication key "%1/%2"? + + + + Please select a key to delete! + + + + Please enter the name of the user group or role for which to import the authentication key: + + + + Please select a key to export! + + + + Please select a user group which to grant access to key "%1": + + + + Please select a key which to set the access group for! + + + + Please perform the following steps to set up key file authentication: + + + + 1) Create a key pair on the master computer. + + + + 2) Set an access group whose members should be allowed to access other computers. + + + + 3) Export the public key and import it on all client computers with the same name. + + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + + + + + AuthKeysManager + + Please check your permissions. + + + + Key name contains invalid characters! + + + + Invalid key type specified! Please specify "%1" or "%2". + + + + Specified key does not exist! Please use the "list" command to list all installed keys. + + + + One or more key files already exist! Please delete them using the "delete" command. + + + + Creating new key pair for "%1" + + + + Failed to create public or private key! + + + + Newly created key pair has been saved to "%1" and "%2". + + + + Could not remove key file "%1"! + + + + Could not remove key file directory "%1"! + + + + Failed to create directory for output file. + + + + File "%1" already exists. + + + + Failed to write output file. + + + + Key "%1/%2" has been exported to "%3" successfully. + + + + Failed read input file. + + + + File "%1" does not contain a valid private key! + + + + File "%1" does not contain a valid public key! + + + + Failed to create directory for key file. + + + + Failed to write key file "%1". + + + + Failed to set permissions for key file "%1"! + + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + + + + Failed to convert private key to public key + + + + Failed to create directory for private key file "%1". + + + + Failed to save private key in file "%1"! + + + + Failed to set permissions for private key file "%1"! + + + + Failed to create directory for public key file "%1". + + + + Failed to save public key in file "%1"! + + + + Failed to set permissions for public key file "%1"! + + + + Failed to set owner of key file "%1" to "%2". + + + + Failed to set permissions for key file "%1". + + + + Key "%1" is now accessible by user group "%2". + + + + <N/A> + + + + Failed to read key file. + + + + + AuthKeysPlugin + + Create new authentication key pair + + + + Delete authentication key + + + + List authentication keys + + + + Import public or private key + + + + Export public or private key + + + + Extract public key from existing private key + + + + Set user group allowed to access a key + + + + KEY + + + + ACCESS GROUP + + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + + NAME + + + + FILE + + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + + Please specify the command to display help for! + + + + TYPE + + + + PAIR ID + + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + Nom + + + Type + Tipus + + + Access group + + + + Pair ID + + + + + BuiltinDirectoryConfigurationPage + + Computers + + + + Name + Nom + + + Host address/IP + + + + MAC address + Adreça MAC + + + Add new computer + + + + Remove selected computer + + + + New computer + + + + Builtin directory + + + + Locations & computers + + + + Locations + + + + Add new location + + + + Remove selected location + + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + + + + + BuiltinDirectoryPlugin + + Show help for specific command + + + + Import objects from given file + + + + Export objects to given file + + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + Tipus + + + Name + Nom + + + Host address + + + + MAC address + Adreça MAC + + + Specified object not found. + + + + File "%1" does not exist! + + + + Can't open file "%1" for reading! + + + + Unknown argument "%1". + + + + Computer "%1" (host address: "%2" MAC address: "%3") + + + + Unclassified object "%1" with ID "%2" + + + + None + + + + Computer + + + + Root + + + + Invalid + + + + Error while parsing line %1. + + + + Network object directory which stores objects in local configuration + + + + Commands for managing the builtin network object directory + + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + + + + Parent UUID + + + + Add a location or computer + + + + Clear all locations and computers + + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + + + + FILE + + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + + + + NAME + + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + + + + + ComputerControlListModel + + Host/IP address: %1 + + + + Active features: %1 + + + + Online and connected + + + + Establishing connection + + + + Computer offline or switched off + + + + Authentication failed or access denied + + + + Disconnected + + + + No user logged on + + + + Logged on user: %1 + + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + + + + Authentication error + Error d'autentificació + + + Remote access + + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + + + + Missing network object directory plugin + + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + + + + Add location + + + + Save computer/user list + + + + Select output filename + + + + CSV files (*.csv) + + + + File error + + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + + + + Please specify a valid filename for the configuration export. + + + + Please specify a valid key. + + + + Specified key does not exist in current configuration! + + + + Please specify a valid value. + + + + Configure Veyon at command line + + + + Output file is not writable! + + + + Output directory is not writable! + + + + Configuration file is not readable! + + + + Clear system-wide Veyon configuration + + + + List all configuration keys and values + + + + Import configuration from given file + + + + Export configuration to given file + + + + Read and output configuration value for given key + + + + Write given value to given configuration key + + + + Unset (remove) given configuration key + + + + Commands for managing the configuration of Veyon + + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + + + + + DemoConfigurationPage + + Demo server + Servidor demo + + + Tunables + + + + ms + + + + Key frame interval + + + + Memory limit + + + + MB + + + + Update interval + + + + s + + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + + + + Window demo + Demo en finestra + + + Give a demonstration by screen broadcasting + + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + + + + Confirm desktop access + Confirmeu l'accés a l'escriptori + + + Never for this session + Mai per a aquesta sessió + + + Always for this session + Sempre per a aquesta sessió + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + + DesktopServicesConfigurationPage + + Programs & websites + + + + Predefined programs + + + + Name + Nom + + + Path + + + + Add new program + + + + Remove selected program + + + + Predefined websites + + + + Remove selected website + + + + URL + + + + New program + + + + New website + + + + + DesktopServicesFeaturePlugin + + Run program + + + + Open website + + + + Click this button to open a website on all computers. + + + + Start programs and services in user desktop + + + + Click this button to run a program on all computers. + + + + Run program "%1" + + + + Custom program + + + + Open website "%1" + + + + Custom website + + + + + DocumentationFigureCreator + + Teacher + Professor + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + + + + Port: + + + + Password: + + + + + FeatureControl + + Feature control + + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + Carpetes + + + Destination directory + + + + Default source directory + + + + Options + + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + + + + Select one or more files to transfer + + + + Transfer files to remote computer + + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + Interfície d'usuari + + + Language: + Llengua: + + + Use system language setting + + + + Veyon + + + + Logging + Registre + + + Log file directory + Carpeta dels fitxers de registre + + + Log level + Nivell de registre + + + Nothing + Res + + + Only critical messages + Només missatges crítics + + + Errors and critical messages + Errors i missatges crítics + + + Warnings and errors + Avisos i errors + + + Information, warnings and errors + Informació, avisos i errors + + + Debug messages and everything else + Missatges de depuració i tota la resta + + + Limit log file size + Límita la mida del fitxer de registre + + + Clear all log files + Buida tots els fitxers de registre + + + Log to standard error output + Registra en la sortida d'error estàndard + + + Network object directory + + + + Backend: + + + + Update interval: + + + + %1 service + + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + Fitxers de registre buidats + + + All log files were cleared successfully. + Tots els fitxers de registre s'han buidat amb èxit. + + + Error + Error + + + Could not remove all log files. + No s'han pogut buidar tots els fitxers de registre. + + + MB + + + + Rotate log files + + + + x + + + + seconds + segons + + + Write to logging system of operating system + + + + Authentication + Autentificació + + + Method: + + + + Logon authentication + Autentificació d'inici de sessió + + + Key file authentication + Autentificació amb fitxers de claus + + + Test + Comprova + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + + + + + LdapConfigurationPage + + Basic settings + + + + General + General + + + LDAP server and port + + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + + + + e.g. OU=Computers + + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + + LDAP base DN test failed + + + + LDAP base DN test successful + + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + + + + Please enter a group name whose members to query: + + + + group members + + + + Group not found + + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + + + + users + + + + user groups + + + + computer groups + + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + + + + LDAP %1 test failed + + + + LDAP %1 test successful + + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + + + + TLS certificate verification + + + + System defaults + + + + Never (insecure!) + + + + Custom CA certificate file + + + + None + + + + TLS + + + + SSL + + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + Comprova + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + + + + enter search filter... + + + + + MainToolBar + + Configuration + + + + Disable balloon tooltips + + + + Show icons only + + + + + MainWindow + + MainWindow + Finestra principal + + + toolBar + Barra d'eines + + + General + General + + + &File + &Fitxer + + + &Help + &Ajuda + + + &Quit + &Surt + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + &Carrega els ajustaments des d'un fitxer + + + Ctrl+O + Ctrl+O + + + About Qt + Quant a Qt + + + Authentication impossible + Autentificació impossible + + + Configuration not writable + La configuració no es pot escriure + + + Load settings from file + Carregar ajustaments des d'un fitxer + + + Save settings to file + Desa els ajustaments en un fitxer + + + Unsaved settings + Ajustaments no desats + + + There are unsaved settings. Quit anyway? + Hi ha ajustaments no desats. Voleu sortir de totes formes? + + + Veyon Configurator + + + + Service + + + + Master + + + + Access control + + + + About Veyon + + + + Auto + + + + About + Quant a + + + %1 Configurator %2 + + + + JSON files (*.json) + + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + + + + Align computers to grid + + + + %1 Configurator + + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Carpetes + + + User configuration + + + + Feature on computer double click: + + + + Features + + + + All features + + + + Disabled features + + + + Screenshots + + + + <no feature> + + + + Basic settings + + + + Behaviour + + + + Enforce selected mode for client computers + + + + Hide local computer + + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + + + + User interface + Interfície d'usuari + + + Background color + + + + Thumbnail update interval + + + + ms + + + + Program start + + + + Modes and features + + + + User and computer name + + + + Only user name + + + + Only computer name + + + + Computer thumbnail caption + + + + Text color + + + + Sort order + + + + Computer and user name + + + + Computer locations + + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + + + + Name: + + + + + PasswordDialog + + Username + Nom d'usuari + + + Password + Clau d'accés + + + Veyon Logon + + + + Authentication error + Error d'autentificació + + + Logon failed with given username and password. Please try again! + + + + Please enter your username and password in order to access computers. + + + + + PowerControlFeaturePlugin + + Power on + Engega + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + + Reboot + Reinicia + + + Click this button to reboot all computers. + + + + Power down + Apaga + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + + + + Confirm reboot + + + + Confirm power down + + + + Do you really want to reboot the selected computers? + + + + Do you really want to power down the selected computer? + + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + Apaga + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + + + + Open a remote view for a computer without interaction. + + + + Remote control + Control remot + + + Open a remote control window for a computer. + + + + Remote access + + + + Remote view or control a computer + + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + Només veure + + + Remote control + Control remot + + + Send shortcut + + + + Fullscreen + Pantalla completa + + + Window + Finestra + + + Ctrl+Alt+Del + + + + Ctrl+Esc + + + + Alt+Tab + + + + Alt+F4 + + + + Win+Tab + + + + Win + + + + Menu + + + + Alt+Ctrl+F1 + + + + Connecting %1 + S'està connectant a %1 + + + Connected. + Connectat. + + + Screenshot + + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + + + + Unlock + + + + Lock screen and input devices of a computer + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + desconegut + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + + + + Use this function to take a screenshot of selected computers. + + + + Screenshots taken + + + + Screenshot of %1 computer have been taken successfully. + + + + Take screenshots of computers and save them locally. + + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + Usuari: + + + Computer: + + + + Date: + Data: + + + Time: + Hora: + + + Show + Mostra + + + Delete + Suprimeix + + + Screenshot + + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + General + + + Autostart + Arranc automàtic + + + Hide tray icon + Ocultar icona de la barra del sistema + + + Start service + Inicia el servei + + + Stopped + Aturat + + + Stop service + Atura el servei + + + State: + Estat: + + + Enable firewall exception + Activar excepció en el tallafocs + + + Allow connections from localhost only + Permet connexions només des d'aquest equip + + + VNC server + + + + Plugin: + + + + Restart %1 Service + + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + + Running + Funcionant + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + Servidor demo + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + + + + Stopping service %1 + + + + Registering service %1 + + + + Unregistering service %1 + + + + Service control + + + + + ServiceControlPlugin + + Service is running + + + + Service is not running + + + + Configure and control Veyon service + + + + Register Veyon Service + + + + Unregister Veyon Service + + + + Start Veyon Service + + + + Stop Veyon Service + + + + Restart Veyon Service + + + + Query status of Veyon Service + + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + Envia un missatge de text + + + Use the field below to type your message which will be sent to all selected users. + Utilitzeu el camp de sota per escriure el missatge que serà enviat als usuaris seleccionats. + + + + TextMessageFeaturePlugin + + Text message + Missatge de text + + + Use this function to send a text message to all users e.g. to assign them new tasks. + + + + Message from teacher + Missatge del professor + + + Send a message to a user + + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Habilita la captura de finestres apilades (semitransparents) + + + Poll full screen (leave this enabled per default) + Monitora tota la pantalla (deixa activat per defecte) + + + Low accuracy (turbo mode) + Baixa precissió (mode turbo) + + + Builtin UltraVNC server configuration + + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + Sense permís d'escriptura + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + Nom d'usuari + + + Password + Clau d'accés + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + + + + + VeyonCore + + [OK] + + + + [FAIL] + + + + Invalid command! + + + + Available commands: + + + + Invalid arguments given + + + + Not enough arguments given - use "%1 help" for more information + + + + Unknown result! + + + + Available modules: + + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + + + + INFO + + + + ERROR + + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + + + + + VncViewWidget + + Establishing connection to %1 ... + S'està establint connexió amb %1 ... + + + + WebApiConfigurationPage + + Web API + + + + General + General + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + General + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + + + + Custom x11vnc parameters: + + + + Do not use X Damage extension + + + + diff --git a/translations/veyon_cs.ts b/translations/veyon_cs.ts new file mode 100644 index 0000000..277c739 --- /dev/null +++ b/translations/veyon_cs.ts @@ -0,0 +1,4079 @@ + + + + + AboutDialog + + About + O aplikaci + + + Translation + Překlad + + + License + Licence + + + About Veyon + O aplikaci Veyon + + + Contributors + Na vývoji se podíleli + + + Version: + Verze: + + + Website: + Webové stránky: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Texty rozhraní aplikace už jsou přeložené do tohoto jazyka. + +Pokud ale překlad není kompletní a nebo by potřeboval vylepšit, případně máte zájem vytvořit/doplnit/vylepšit překlad jiného jazyka, kontaktujte vývojáře aplikace Veyon! + + + About %1 %2 + O %1 %2 + + + Support Veyon project with a donation + Podpořit projekt Veyon darem + + + + AccessControlPage + + Computer access control + Řízení přístupu k počítači + + + Grant access to every authenticated user (default) + Zpřístupnit všem ověřeným uživatelům (výchozí) + + + Test + Vyzkoušet funkčnost + + + Process access control rules + Zpracovat pravidla řízení přístupu + + + User groups authorized for computer access + Skupiny uživatelů pověřených pro přístup k počítači + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Přidejte skupiny jejichž členové budou moci přistupovat k počítačům v rámci sítě s Veyon. + + + Authorized user groups + Skupiny pověřených uživatelů + + + All groups + Všechny skupiny + + + Access control rules + Pravidla řízení přístupu + + + Add access control rule + Přidat pravidlo řízení přístupu + + + Remove access control rule + Odebrat pravidlo řízení přístupu + + + Move selected rule down + Posunout označené pravidlo níž + + + Move selected rule up + Posunout označené pravidlo výš + + + Edit selected rule + Upravit označené pravidlo + + + Enter username + Zadejte uživatelské jméno + + + Please enter a user login name whose access permissions to test: + Zadejte uživatelské jméno jehož přístupová práva chcete vyzkoušet: + + + Access allowed + Přístup umožněn + + + The specified user is allowed to access computers with this configuration. + Zadanému uživateli je umožněn přístup k počítačům s tímto nastavením. + + + Access denied + Přístup odepřen + + + The specified user is not allowed to access computers with this configuration. + Zadanému uživateli není umožněn přístup k počítačům s tímto nastavením. + + + Enable usage of domain groups + Používat doménové skupiny + + + User groups backend: + Podpůrná vrstva uživatelských skupin: + + + Missing user groups backend + Chybí podpůrná vrstva uživatelských skupin + + + No default user groups plugin was found. Please check your installation! + Nebyl nalezen žádný výchozí zásuvný modul uživatelských skupin. Zkontrolujte svou instalaci! + + + Restrict access to members of specific user groups + Omezit přístup na členy konkrétních skupin uživatelů + + + + AccessControlRuleEditDialog + + Edit access control rule + Upravit pravidlo řízení přístupu + + + General + Obecné + + + enter a short name for the rule here + sem zadejte stručný název tohoto pravidla + + + Rule name: + Název pravidla: + + + enter a description for the rule here + sem zadejte popis pravidla + + + Rule description: + Popis pravidla: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Převrátit všechny podmínky („je/má“ si vykládat jako není/nemá) + + + Conditions + Podmínky + + + is member of group + je členem skupiny + + + Accessing computer is localhost + Přistupující počítač přistupuje sám na sebe + + + Accessing user is logged on user + Přistupující uživatel přistupuje sám na sebe + + + Accessing user is already connected + Přistupující uživatel je už připojený + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Pokud je zapnutá více než jedna, pak aby se pravidlo uplatnilo je třeba, aby byly splněné všechny podmínky (logické A). Pokud postačí splnit pouze jednu z daných podmínek (logické NEBO) vytvořte pravidlo řízení přístupu pro každou zvlášť. + + + Action + Akce + + + Allow access + Umožnit přístup + + + Deny access + Odepřít přístup + + + Ask logged on user for permission + Požádat přihlášeného uživatele o svolení + + + None (rule disabled) + Žádné (pravidlo vypnuto) + + + Accessing user + Přistupující uživatel + + + Accessing computer + Přistupující počítač + + + Local (logged on) user + Uživatel na tomto počítači (právě přihlášený) + + + Local computer + Tento počítač + + + Always process rule and ignore conditions + Ignorovat podmínky a pravidlo zpracovat vždy + + + No user logged on + Není přihlášený žádný uživatel + + + Accessing user has one or more groups in common with local (logged on) user + Přistupující uživatel má jednu nebo více skupin společných s uživatelem, který je právě přihlášený k počítači + + + Accessing computer and local computer are at the same location + Přistupující počítač a tento počítač se nacházejí ve stejném umístění + + + is located at + se nachází v + + + + AccessControlRulesTestDialog + + Access control rules test + Test pravidel řízení přístupu + + + Accessing user: + Přistupující uživatel: + + + Local computer: + Počítač ke kterému se přistupuje: + + + Accessing computer: + Přistupující počítač: + + + Please enter the following user and computer information in order to test the configured ruleset. + Pro vyzkoušení nastavené sady pravidel zadejte následující údaje o uživateli a počítači. + + + Local user: + Místní uživatel: + + + Connected users: + Připojení uživatelé: + + + The access in the given scenario is allowed. + Přístup je v daném scénáři umožněn. + + + The access in the given scenario is denied. + Přístup je v daném scénáři odepřen. + + + The access in the given scenario needs permission of the logged on user. + Přístup v zadaném scénáři vyžaduje svolení od přihlášeného uživatele. + + + ERROR: Unknown action + CHYBA: neznámá akce + + + Test result + Výsledek zkoušky fungování + + + + AuthKeysConfigurationPage + + Authentication keys + Ověřovací klíče + + + Introduction + Úvod + + + Key file directories + Složky souboru s klíčem + + + Public key file base directory + Základní složka pro veřejnou část klíče + + + Private key file base directory + Základní složka pro soukromou část klíče + + + Available authentication keys + Ověřovací klíče k dispozici + + + Create key pair + Vytvořit dvojici klíčů + + + Delete key + Smazat klíč + + + Import key + Importovat klíč + + + Export key + Exportovat klíč + + + Set access group + Nastavit přístupovou skupinu + + + Key files (*.pem) + Soubory s klíči (*.pem) + + + Authentication key name + Název ověřovacího klíče + + + Please enter the name of the user group or role for which to create an authentication key pair: + Zadejte název uživatelské skupiny nebo role pro kterou chcete vytvořit ověřovací klíč: + + + Do you really want to delete authentication key "%1/%2"? + Opravdu chcete smazat ověřovací klíč „%1/%2“? + + + Please select a key to delete! + Vyberte klíč, který chcete smazat! + + + Please enter the name of the user group or role for which to import the authentication key: + Zadejte název uživatelské skupiny nebo role pro kterou chcete importovat ověřovací klíč: + + + Please select a key to export! + Vyberte klíč který chcete exportovat! + + + Please select a user group which to grant access to key "%1": + Vyberte uživatelskou skupinu které udělit přístup ke klíči „%1“: + + + Please select a key which to set the access group for! + Vyberte klíč pro který nastavit přístupovou skupinu! + + + Please perform the following steps to set up key file authentication: + Pro nastavení ověřování souborem s klíčem proveďte následující kroky: + + + 1) Create a key pair on the master computer. + 1) Vytvořte dvojici klíčů na řídícím počítači. + + + 2) Set an access group whose members should be allowed to access other computers. + 2( Nastavte přístupovou skupinu jejíž členům by mělo být umožněno přistupovat k ostatním počítačům. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Exportujte veřejnou část klíče a importujte ji na všechny klientské počítače pod stejným názvem. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Nahlédněte do <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Příručky správy Veyon</a> pro další informace. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Dvojice ověřovacích klíčů je tvořena propojovacími šifrovacími klíči, soukromým a veřejným. +Soukromá část umožňuje uživatelům na hlavním počítači přistupovat ke klientským počítačům. +Je důležité aby pouze pověření uživatelé mohli číst soubor se soukromou částí klíče. +Veřejná část je použita na klientských počítačích pro ověření příchozího požadavku na připojení. + + + + AuthKeysManager + + Please check your permissions. + Zkontrolujte svá oprávnění. + + + Key name contains invalid characters! + Název klíče obsahuje neplatné znaky! + + + Invalid key type specified! Please specify "%1" or "%2". + Zadán neplatný typ klíče! Zadejte „%1“ nebo „%2“. + + + Specified key does not exist! Please use the "list" command to list all installed keys. + Zadaný klíč neexistuje! Použijte příkaz „list“ pro zobrazení všech nainstalovaných klíčů. + + + One or more key files already exist! Please delete them using the "delete" command. + Jeden nebo více souborů s klíči už existuje! Smažte je příkazem „delete“. + + + Creating new key pair for "%1" + Vytváření nové dvojice klíčů pro „%1“ + + + Failed to create public or private key! + Nepodařilo se vytvořit veřejnou či soukromou část klíče! + + + Newly created key pair has been saved to "%1" and "%2". + Nově vytvořená dvojice klíčů byla uložena do „%1“ a „%2“. + + + Could not remove key file "%1"! + Nedaří se odebrat soubor s klíčem „%1“! + + + Could not remove key file directory "%1"! + Nedaří se odebrat složku pro soubor s klíčem „%1“! + + + Failed to create directory for output file. + Nepodařilo se vytvořit složku pro výstupní soubor. + + + File "%1" already exists. + Soubor „%1“ už existuje. + + + Failed to write output file. + Nepodařilo se zapsat do výstupního souboru. + + + Key "%1/%2" has been exported to "%3" successfully. + Klíč „%1/%2“ byl úspěšně exportován do „%3“. + + + Failed read input file. + Nepodařilo se načíst vstupní soubor. + + + File "%1" does not contain a valid private key! + Soubor „%1“ neobsahuje platnou soukromou část klíče! + + + File "%1" does not contain a valid public key! + Soubor „%1“ neobsahuje platnou veřejnou část klíče! + + + Failed to create directory for key file. + Nepodařilo se vytvořit složku pro soubor s klíčem. + + + Failed to write key file "%1". + Nepodařilo se zapsat soubor s klíčem „%1“. + + + Failed to set permissions for key file "%1"! + Nepodařilo se nastavit oprávnění pro soubor s klíčem „%1“! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + Klíč „%1/%2“ byl úspěšně importován. Pro zabránění neoprávněnému přístupu zkontrolujte práva na souboru pro „%3“. + + + Failed to convert private key to public key + Nepodařilo se vytvořit veřejnou část klíče z té soukromé + + + Failed to create directory for private key file "%1". + Nepodařilo se vytvořit složku pro soubor se soukromou částí klíče „%1“. + + + Failed to save private key in file "%1"! + Nepodařilo se uložit soukromou část klíče do souboru „%1“! + + + Failed to set permissions for private key file "%1"! + Nepodařilo se nastavit práva na souboru se soukromou částí klíče „%1“! + + + Failed to create directory for public key file "%1". + Nepodařilo se vytvořit složku pro veřejnou část klíče „%1“. + + + Failed to save public key in file "%1"! + Nepodařilo se uložit veřejnou část klíče do souboru „%1“! + + + Failed to set permissions for public key file "%1"! + Nepodařilo se nastavit práva na souboru s veřejnou částí klíče „%1“! + + + Failed to set owner of key file "%1" to "%2". + Nepodařilo se nastavit vlastníka souboru s klíčem „%1“ na „%2“. + + + Failed to set permissions for key file "%1". + Nepodařilo se nastavit práva na souboru s klíčem „%1“. + + + Key "%1" is now accessible by user group "%2". + Klíč „%1“ je nyní přístupný skupině uživatelů „%2“. + + + <N/A> + <N/A> + + + Failed to read key file. + Soubor s klíčem se nepodařilo načíst. + + + + AuthKeysPlugin + + Create new authentication key pair + Vytvořit novou dvojici ověřovacích klíčů + + + Delete authentication key + Smazat ověřovací klíč + + + List authentication keys + Vypsat ověřovací klíče + + + Import public or private key + Importovat veřejnou nebo soukromou část klíče + + + Export public or private key + Exportovat veřejnou nebo soukromou část klíče + + + Extract public key from existing private key + Vytvořit veřejnou část klíče z existující soukromé + + + Set user group allowed to access a key + Nastavit skupinu uživatelů která může přistupovat ke klíči + + + KEY + KLÍČ + + + ACCESS GROUP + PŘÍSTUPOVÁ SKUPINA + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + Tento příkaz přizpůsobí práva na souboru na <KEY> tak, aby pouze uživatelská skupina <ACCESS GROUP> měla přístup pro čtení. + + + NAME + NÁZEV + + + FILE + SOUBOR + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Tento příkaz exportuje ověřovací klíč <KEY> do <FILE>. Pokud <FILE> není určeno, název bude vytvořen z názvu a typu <KEY>. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Tento příkaz importuje ověřovací klíč <KEY> z <FILE>. Pokud <FILE> není určeno, název bude vytvořen z názvu a typu <KEY>. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + Tento příkaz vypíše všechny dostupné ověřovací klíče v nastavené složce s klíči. Pokud je zadaná volba „%1“, bude namísto toho zobrazena tabulka s podrobnostmi o klíči. Některé podrobnosti mohou chybět pokud klíč není dostupný například kvůli chybějícím právům na čtení. + + + Please specify the command to display help for! + Zadejte příkaz pro který chcete zobrazit nápovědu! + + + TYPE + TYP + + + PAIR ID + IDENTIFIKÁTOR DVOJICE + + + Command line support for managing authentication keys + Podpora pro správu ověřovacích klíčů z příkazového řádku + + + Commands for managing authentication keys + Příkazy pro zprávu ověřovacích klíčů + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + Tento příkaz vytvoří novou dvojici ověřovacího klíče s názvem <NAME> a uloží jeho soukromou a veřejnou část do nastavených složek s klíči. Je třeba, aby parametr byl název pro klíč a může obsahovat pouze písmena. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + Tento příkaz maže ověřovací klíč <KEY> z nastavené složky s klíči. Uvědomte si, že klíč nemůže být po smazání obnoven. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + Tento příkaz vytáhne veřejnou část klíče ze soukromého klíče <KEY> a uloží jí do odpovídajícího veřejného klíče. Při nastavování jiného řídícího počítače proto stačí přenést pouze soukromou část klíče. Tu veřejnou z něj lze vytáhnout. + + + + AuthKeysTableModel + + Name + Název + + + Type + Typ + + + Access group + Přístupová skupina + + + Pair ID + Identifikátor dvojice + + + + BuiltinDirectoryConfigurationPage + + Computers + Počítače + + + Name + Název + + + Host address/IP + Adresa IP/stroje + + + MAC address + Fyzická (MAC) adresa + + + Add new computer + Přidat nový počítač + + + Remove selected computer + Odebrat označený počítač + + + New computer + Nový počítač + + + Builtin directory + Vestavěný adresář + + + Locations & computers + Umístění a počítače + + + Locations + Umístění + + + Add new location + Přidat nové umístění + + + Remove selected location + Odebrat označené umístění + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + Import CSV souborů je možný prostřednictvím rozhraní pro příkazový řádek. Další informace jsou k dispozici v <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">dokumentaci na webu</a>. + + + New location + Nové umístění + + + + BuiltinDirectoryPlugin + + Show help for specific command + Zobrazit nápovědu pro konkrétní příkaz + + + Import objects from given file + Importovat objekty ze zadaného souboru + + + Export objects to given file + Exportovat objekty do zadaného souboru + + + Invalid type specified. Valid values are "%1" or "%2". + Zadán neplatný typ. Platné hodnoty jsou „%1“ nebo „%1“. + + + Type + Typ + + + Name + Název + + + Host address + Adresa stroje + + + MAC address + Fyzická (MAC) adresa + + + Specified object not found. + Zadaný objekt nenalezen. + + + File "%1" does not exist! + Soubor „%1“ neexistuje! + + + Can't open file "%1" for reading! + Nedaří se zapisovat do souboru „%1“ + + + Unknown argument "%1". + Neznámý argument „%1“. + + + Computer "%1" (host address: "%2" MAC address: "%3") + Počítač „%1“ (adresa stroje: „%2“ MAC adresa: „%3“) + + + Unclassified object "%1" with ID "%2" + Nezařazený objekt „%1“ s identifikátorem „%2“ + + + None + Žádné + + + Computer + Počítač + + + Root + Root + + + Invalid + Neplatné + + + Error while parsing line %1. + Chyba při zpracovávání řádku %1. + + + Network object directory which stores objects in local configuration + Adresář síťových objektů který uchovává objekty v místním nastavení + + + Commands for managing the builtin network object directory + Příkazy pro správu vestavěného adresáře síťových objektů + + + No format string or regular expression specified! + Nebyl zadán žádný řetězec formátu nebo regulární výraz! + + + Can't open file "%1" for writing! + Nedaří se zapisovat do souboru „%1“! + + + No format string specified! + Nebyl určený žádný řetězec formátu! + + + Object UUID + Nikde se neopakující identifikátor objektu + + + Parent UUID + Nikde se neopakující identifikátor nadřazeného + + + Add a location or computer + Přidat umístění nebo počítač + + + Clear all locations and computers + Vyčistit všechna umístění a počítače + + + Dump all or individual locations and computers + Vypsat všechna nebo jednotlivá umístění a počítače + + + List all locations and computers + Vypsat všechna umístění a počítače + + + Remove a location or computer + Odebrat umístění nebo počítač + + + Location "%1" + Umístění „%1“ + + + Builtin (computers and locations in local configuration) + Vestavěné (počítače a umístění v místním nastavení) + + + Location + Umístění + + + FILE + SOUBOR + + + LOCATION + UMÍSTĚNÍ + + + FORMAT-STRING-WITH-PLACEHOLDERS + FORMÁTOVACÍ-ŘETĚZEC-SE-ZÁSTUPNÝMI-VYJÁDŘENÍMI + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + REGULÁRNÍ-VÝRAZ-SE-ZÁSTUPNÝM-VYJÁDŘENÍM + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Importuje objekty ze zadaného textového souboru pomocí daného formátovacího řetězce nebo regulárního výrazu obsahujícího jeden nebo více zástupných vyjádření. Platná zástupná vyjádření jsou: %1 + + + Import simple CSV file to a single room + Importovat jednoduchý CSV soubor do jedné místnosti + + + Import CSV file with location name in first column + Importovat CSV soubor s názvem umístění v prvním sloupci + + + Import text file with with key/value pairs using regular expressions + Importovat textový soubor s dvojicemi klíč-hodnota pomocí regulárních výrazů + + + Import arbitrarily formatted data + Importovat libovolně formátovaná data + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Exportuje objekty do zadaného textového souboru pomocí daného formátovacího řetězce, obsahujícího jeden nebo více zástupných vyjádření. Platná zástupná vyjádření jsou: %1 + + + Export all objects to a CSV file + Exportovat veškeré objekty do CSV souboru + + + Export all computers in a specific location to a CSV file + Exportovat všechny počítače v daném umístění do CSV souboru + + + TYPE + TYP + + + NAME + NÁZEV + + + PARENT + NADŘAZENÉ + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + Přidá objekt kde %1 může být jedno z „%2“ nebo „%3“. %4 je možné určit pomocí názvu nebo nikde se neopakujícího identifikátoru (UUID). + + + Add a room + Přidat místnost + + + Add a computer to room %1 + Přidat počítač do místnosti %1 + + + OBJECT + OBJEKT + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Odebere daný objekt z adresáře. %1 může být určen názvem nebo nikde se neopakujícím identifikátorem. Odebrání umístění odebere také všechny počítače v něm. + + + Remove a computer by name + Odebrat počítač podle názvu + + + Remove an object by UUID + Odebrat objekt podle UUID + + + "Room 01" + „Místnost 01“ + + + "Computer 01" + „Počítač 01“ + + + HOST ADDRESS + ADRESA STROJE + + + MAC ADDRESS + MAC ADRESA + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Vestavěný VNC server (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Vestavěný VNC server (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Stroj / IP adresa: %1 + + + Active features: %1 + Aktivní funkce: %1 + + + Online and connected + Dostupné a připojeno + + + Establishing connection + Navazování spojení + + + Computer offline or switched off + Počítač není dostupný na síti nebo je vypnutý + + + Authentication failed or access denied + Ověření se nezdařilo nebo odepřen přístup + + + Disconnected + Odpojeno + + + No user logged on + Není přihlášený žádný uživatel + + + Logged on user: %1 + Přihlášený uživatel: %1 + + + Location: %1 + Umístění: %1 + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 služba %2 na %3:%4 + + + Authentication error + Chyba ověření + + + Remote access + Vzdálený přístup + + + User "%1" at host "%2" is now accessing this computer. + Uživatel „%1“ ze stroje „%2“ nyní přistupuje k tomuto počítači. + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + Uživatel „%1“ na stroji „%2“ se pokusil přistoupit k tomuto počítači, ale nepodařilo se úspěšně ověřit. + + + Access control error + Chyba řízení přístupu + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + Uživatel „%1“ na stroji „%2“ se pokusil přistoupit k tomuto počítači, ale byl zablokován dle nastavení řízení přístupu. + + + Active connections: + Aktivní spojení: + + + + ComputerManager + + User + Uživatel + + + Missing network object directory plugin + Chybí zásuvný modul pro adresář se síťovými objekty + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Nebyl nalezen žádný výchozí zásuvný modul pro adresář se síťovými objekty. Zkontrolujte svou instalaci nebo v nastavení %1 určete jinou podpůrnou vrstvu (backend) pro takový adresář. + + + Location detection failed + Zjištění umístění se nezdařilo + + + Computer name;Hostname;User + Název počítače;Název stroje;Uživatel + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + Umístění počítače se nedaří zjistit. To značí problém s nastavením systému. Náhradně budou v panelu výběru počítačů zobrazena všechna umístění. + + + + ComputerSelectPanel + + Computer search + Vyhledání počítače + + + Add location + Přidat umístění + + + Save computer/user list + Uložit seznam počítačů/uživatelů + + + Select output filename + Vyberte výstupní soubor + + + CSV files (*.csv) + CSV soubory (*.csv) + + + File error + Chyba souboru + + + Could not write the computer and users list to %1! Please check the file access permissions. + Nedaří se zapsat seznam počítačů a uživatelů do %1! Zkontrolujte přístupová práva souboru. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Zadejte existující soubor s nastaveními, který importovat. + + + Please specify a valid filename for the configuration export. + Zadejte platný název souboru pro export nastavení. + + + Please specify a valid key. + Zadejte platný klíč. + + + Specified key does not exist in current configuration! + Zadaný klíč ve stávajícím nastavení neexistuje! + + + Please specify a valid value. + Zadejte platnou hodnotu. + + + Configure Veyon at command line + Nastavit Veyon na příkazovém řádku + + + Output file is not writable! + Výstupní soubor není přístupný pro zápis! + + + Output directory is not writable! + Výstupní složka není přístupná pro zápis! + + + Configuration file is not readable! + Soubor s nastaveními není přístupný pro čtení! + + + Clear system-wide Veyon configuration + Vyčistit nastavení Veyon týkající se celého systému + + + List all configuration keys and values + Vypsat veškeré položky nastavení a jejich hodnoty + + + Import configuration from given file + Importovat nastavení ze zadaného souboru + + + Export configuration to given file + Exportovat nastavení do zadaného souboru + + + Read and output configuration value for given key + Načíst a zobrazit hodnotu nastavení pro danou položku + + + Write given value to given configuration key + Zapsat zadanou hodnotu do zadané položky nastavení + + + Unset (remove) given configuration key + Zrušit nastavení (odebrat ze) zadané položky nastavení + + + Commands for managing the configuration of Veyon + Příkazy pro správu nastavení Veyon + + + Upgrade and save configuration of program and plugins + Přejít na novější verzi a uložit nastavení aplikace a zásuvných modulů + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + Nedaří se nastavit automatické spouštění služby %1. + + + Could not configure the firewall configuration for the %1 Server. + Nedaří se nastavit nastavení brány firewall pro server %1. + + + Could not configure the firewall configuration for the %1 Worker. + Nedaří se nastavit bránu firewall pro worker %1. + + + Configuration is not writable. Please check your permissions! + Nastavení nejsou přístupná pro zápis. Zkontrolujte svá oprávnění! + + + Could not apply platform-specific configuration settings. + Nedaří se uplatnit změny nastavení pro konkrétní platformu. + + + + DemoClient + + %1 Demo + Ukázka %1 + + + + DemoConfigurationPage + + Demo server + Ukázkový server + + + Tunables + Vyladitelné + + + ms + ms + + + Key frame interval + Interval mezi úplnými snímky + + + Memory limit + Paměťový limit + + + MB + MB + + + Update interval + Interval aktualizace + + + s + s + + + Slow down thumbnail updates while demo is running + Zpomalit aktualizace náhledů při běhu ukázky + + + + DemoFeaturePlugin + + Stop demo + Zastavit ukázku + + + Window demo + Ukázka v okně + + + Give a demonstration by screen broadcasting + Předvést ostatním vysíláním obsahu obrazovky + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + V tomto režimu bude vaše obrazovka zobrazována v okně na všech počítačích. Uživatelé přitom budou moci v případě potřeby přepínat na další okna. + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + Dialog přístupu k pracovní ploše + + + Confirm desktop access + Schválit přístup k pracovní ploše + + + Never for this session + Ne po celou dobu této relace + + + Always for this session + Ano kdykoli po dobu této relace + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + Uživatel %1 chce z počítače %2 přistoupit k Vaší pracovní ploše. Umožníte mu to? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programy a webové stránky + + + Predefined programs + Přednastavené aplikace + + + Name + Název + + + Path + Umístění + + + Add new program + Přidat novou aplikaci + + + Remove selected program + Odebrat označenou aplikaci + + + Predefined websites + Přednastavené webové stránky + + + Remove selected website + Odebrat označenou webovou stránku + + + URL + URL + + + New program + Nová aplikace + + + New website + Nová webová stránka + + + + DesktopServicesFeaturePlugin + + Run program + Spustit aplikaci + + + Open website + Otevřít webovou stránku + + + Click this button to open a website on all computers. + Kliknutím na toto tlačítko otevřete webovou stránku na všech počítačích. + + + Start programs and services in user desktop + Spustit aplikace a služby na počítači uživatele + + + Click this button to run a program on all computers. + Kliknutím na toto tlačítko spustíte aplikaci na všech počítačích. + + + Run program "%1" + Spustit aplikaci „%1“ + + + Custom program + Uživatelem určená aplikace + + + Open website "%1" + Otevřít webovou stránku „%1“ + + + Custom website + Uživatelsky určené webová stránka + + + + DocumentationFigureCreator + + Teacher + Vyučující + + + Room %1 + Místnost %1 + + + Please complete all tasks within the next 5 minutes. + Dokončete všechny úkoly během příštích 5 minut. + + + Custom website + Uživatelsky určené webová stránka + + + Open file manager + Otevřít správu souborů + + + Start learning tool + Spustit nástroj pro učení + + + Play tutorial video + Přehrát výukové video + + + Custom program + Uživatelem určená aplikace + + + Handout + Předání + + + Texts to read + Text ke čtení + + + generic-student-user + obecný student-uživatel + + + + ExternalVncServer + + External VNC server + Externí VNC server + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Nastavení externího VNC serveru + + + Port: + Port: + + + Password: + Heslo: + + + + FeatureControl + + Feature control + Ovládání funkce + + + + FileTransferConfigurationPage + + File transfer + Přenos souboru + + + Directories + Složky + + + Destination directory + + + + Default source directory + + + + Options + Volby + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + Soubor „%1“ se nedaří otevřít pro čtení! Zkontrolujte svá oprávnění! + + + + FileTransferDialog + + File transfer + Přenos souboru + + + Options + Volby + + + Transfer only + Pouze přenos + + + Transfer and open file(s) with associated program + Přenést a otevřít soubory pomocí přiřazeného programu + + + Transfer and open destination folder + Přenést a otevřít cílovou složku + + + Files + Soubory + + + Start + Spustit + + + Overwrite existing files + Přepsat existující soubory + + + + FileTransferPlugin + + File transfer + Přenos souboru + + + Click this button to transfer files from your computer to all computers. + Kliknutím na toto tlačítko zkopírujete soubory ze svého počítače na všechny ostatní. + + + Select one or more files to transfer + Vyberte jeden nebo více souborů k přenosu + + + Transfer files to remote computer + Přenést soubory na vzdálený počítač + + + Received file "%1". + Obdržen soubor „%1“. + + + Could not receive file "%1" as it already exists. + Není možné přijmout soubor „%1“, protože už existuje. + + + Could not receive file "%1" as it could not be opened for writing! + Není možné přijmout soubor „%1“, protože by ho nebylo možné otevřít pro zápis! + + + + GeneralConfigurationPage + + User interface + Uživatelské rozhraní + + + Language: + Jazyk: + + + Use system language setting + Použit jazyková nastavení systému + + + Veyon + Veyon + + + Logging + Pořizování záznamů událostí + + + Log file directory + Složka pro soubor se záznamy událostí + + + Log level + Úroveň podrobností záznamu událostí + + + Nothing + Nic + + + Only critical messages + Pouze kritické zprávy + + + Errors and critical messages + Chyby a kritické zprávy + + + Warnings and errors + Varování a chyby + + + Information, warnings and errors + Informace, varování a chyby + + + Debug messages and everything else + Ladící zprávy a vše ostatní + + + Limit log file size + Omezit velikost souboru se záznamy událostí + + + Clear all log files + Vymazat všechny soubory se záznamy událostí + + + Log to standard error output + Zaznamenávat na standardní chybový výstup + + + Network object directory + Adresář síťových objektů + + + Backend: + Podpůrná vrstva (backend): + + + Update interval: + Interval aktualizace: + + + %1 service + služba %1 + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + Pro smazání souborů se záznamy událostí je třeba dočasně zastavit službu %1 – pokračovat? + + + Log files cleared + Soubory se záznamy událostí byly smazány + + + All log files were cleared successfully. + Všechny soubory se záznamy událostí byly úspěšně smazány. + + + Error + Chyba + + + Could not remove all log files. + Nedaří se smazat soubory se záznamy událostí. + + + MB + MB + + + Rotate log files + Rotovat soubory se záznamem událostí + + + x + x + + + seconds + sekund + + + Write to logging system of operating system + Zapisovat do systému záznamu událostí operačního systému + + + Authentication + Ověření + + + Method: + Metoda: + + + Logon authentication + Ověření přihlášením se + + + Key file authentication + Ověření pomocí souboru s klíčem + + + Test + Vyzkoušet funkčnost + + + Authentication is set up properly on this computer. + Ověřování je na tomto počítači nastaveno správně + + + Authentication keys are not set up properly on this computer. + Ověřování není na tomto počítači nastaveno správně + + + Authentication test + Vyzkoušení ověřování + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + Procházet LDAP + + + + LdapClient + + LDAP error description: %1 + Popis LDAP chyby: %1 + + + + LdapConfigurationPage + + Basic settings + Základní nastavení + + + General + Obecné + + + LDAP server and port + LDAP server a port + + + Bind DN + Spojovací rozlišený název (DN) + + + Bind password + Spojovací heslo + + + Anonymous bind + Anonymní spojení + + + Use bind credentials + Použít spojovací přihlašovací údaje + + + Base DN + Základ rozlišeného názvu (DN) + + + Fixed base DN + Neměnný základ rozlišeného názvu (DN) + + + e.g. dc=example,dc=org + např. dc=example,dc=org + + + Discover base DN by naming context + Zjistit základ rozlišeného názvu (DN) pomocí kontextu pojmenování + + + e.g. namingContexts or defaultNamingContext + např. namingContexts nebo defaultNamingContext + + + Environment settings + Nastavení prostředí + + + Object trees + Stromy objektů + + + Computer tree + Strom počítačů + + + e.g. OU=Groups + např. OU=Groups + + + User tree + Strom uživatelů + + + e.g. OU=Users + např. OU=Users + + + e.g. OU=Computers + např. OU=Computers + + + Group tree + Strom skupin + + + Perform recursive search operations in object trees + Hledat i ve vnořených úrovních stromů objektu + + + Object attributes + Atributy objektu + + + e.g. hwAddress + např. hwAdress + + + e.g. member or memberUid + Např. member nebo memberUid + + + e.g. dNSHostName + Např. dNSHostName + + + Computer MAC address attribute + Atribut fyzická (MAC) adresa počítače + + + Group member attribute + Atribut členství ve skupinách + + + e.g. uid or sAMAccountName + např. uid nebo sAMAccountName + + + Advanced settings + Pokročilá nastavení + + + Optional object filters + Volitelné filtry objektů + + + Filter for user groups + Filtrovat dle skupin uživatele + + + Filter for users + Filtrovat dle uživatelů + + + Filter for computer groups + Filtrovat dle skupin počítačů + + + Group member identification + Identifikace člena skupiny + + + Distinguished name (Samba/AD) + Rozlišený název (Samba/AD) + + + List all groups of a user + Vypsat veškeré skupiny kterých je uživatel členem + + + List all groups of a computer + Vypsat veškeré skupiny počítačů + + + Get computer object by IP address + Získat objekt počítače pomocí IP adresy + + + LDAP connection failed + Nepodařilo se připojit do LDAP + + + LDAP bind failed + Spojení s LDAP se nezdařilo + + + LDAP bind successful + Spojení do LDAP úspěšně navázáno + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Úspěšně připojeno k LDAP serveru a provedeno LDAP přihlášení. Základní LDAP nastavení jsou v pořádku. + + + LDAP base DN test failed + Test základu LDAP rozliš. (DN) názvu se nezdařil + + + LDAP base DN test successful + Test základu LDAP rozliš. názvu úspěšný + + + LDAP naming context test failed + Test LDAP kontextu názvů se nezdařil + + + LDAP naming context test successful + Test LDAP kontextu názvů úspěšný + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + LDAP kontext názvů by úspěšně dotázán. Následující základní DN byly nalezeny: +%1 + + + user tree + strom uživatelů + + + group tree + strom skupin + + + computer tree + strom počítačů + + + Enter username + Zadejte uživatelské jméno + + + Please enter a user login name (wildcards allowed) which to query: + Zadejte přihlašovací jméno uživatele (je možné použít i zástupné znaky) na které se dotázat: + + + user objects + objekty uživatele + + + Enter group name + Zadejte název skupiny + + + Please enter a group name whose members to query: + Zadejte název skupiny na jejíž členy se dotázat: + + + group members + členové skupiny + + + Group not found + Skupina nenalezena + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + Nedaří se nalézt skupinu s názvem „%1“. Zkontrolujte název skupiny a parametr „strom skupin“. + + + Enter computer name + Zadejte název počítače + + + computer objects + objekty počítačů + + + Enter computer DN + Zadejte rozlišený název (DN) počítače + + + Please enter the DN of a computer whose MAC address to query: + Zadejte rozlišený název (DN) počítače na jehož MAC adresu se dotázat: + + + computer MAC addresses + Fyzické (MAC) adresy sítových rozhraní počítačů + + + users + uživatelé + + + user groups + skupiny uživatelů + + + computer groups + skupiny počítačů + + + Please enter a user login name whose group memberships to query: + Zadejte přihlašovací jméno uživatele na jehož členství ve skupinách se dotázat: + + + groups of user + skupiny uživatelů + + + User not found + Uživatel nebyl nalezen + + + groups of computer + skupiny počítačů + + + Computer not found + Počítač nenalezen + + + Enter computer IP address + Zadejte IP adresu počítače + + + Please enter a computer IP address which to resolve to an computer object: + Zadejte IP adresu počítače kterou přeložit na objekt počítače: + + + computers + počítače + + + LDAP %1 test failed + Test LDAP %1 se nezdařil + + + LDAP %1 test successful + Test LDAP %1 úspěšný + + + The %1 has been queried successfully and %2 entries were found. + Úspěšně dotázáno na %1 a nalezeno %2 položek. + + + %1 %2 have been queried successfully: + +%3 + Úspěšně dotázáno na %1 %2: + +%3 + + + LDAP filter test failed + Test LDAP filtru se nezdařil + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + S takto nastaveným filtrem se nedaří dotázat na žádné %1. Zkontrolujte LDAP filtr pro %1. + +%2 + + + LDAP filter test successful + Test LDAP filtru úspěšný + + + %1 %2 have been queried successfully using the configured filter. + S nastaveným filtrem úspěšně dotázáno na %1 %2. + + + (only if different from group tree) + (pouze pokud se liší od stromu skupin) + + + Computer group tree + Strom skupin počítačů + + + computer group tree + Strom skupin uživatelů + + + Filter for computers + Filtrovat dle počítačů + + + e.g. room or computerLab + např. room (místnost) nebo computerLab (počítačová laboratoř) + + + Integration tests + Integrační testy + + + Computer groups + Skupiny počítačů + + + e.g. name or description + např. název nebo popis + + + Filter for computer containers + Filtrovat dle kontejnerů s počítači + + + Computer containers or OUs + Kontejnery s počítači nebo organizační jednotky + + + Connection security + Zabezpečení připojení + + + TLS certificate verification + Ověření TLS certifikátu + + + System defaults + Výchozí systémové + + + Never (insecure!) + Nikdy (nezabezpečené!) + + + Custom CA certificate file + Uživatelsky určený soubor s certifikátem cert. autority + + + None + Žádné + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + např. (objectClass=computer) + + + e.g. (objectClass=group) + např. (objectClass=group) + + + e.g. (objectClass=person) + např. (objectClass=person) + + + e.g. (objectClass=room) or (objectClass=computerLab) + např. (objectClass=room) nebo (objectClass=computerLab) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + např. (objectClass=container) nebo (objectClass=organizationalUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + Nedaří se dotázat na nastavený základ rozlišeného názvu. Zkontrolujte parametr základu DN. + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + Základ LDAP DN byl úspěšně dotázán. Byly nalezeny následující položky: + +%1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + Nedaří se dotázat na základ rozlišeného názvu prostřednictvím názvového kontextu. Zkontrolujte parametr atributu kontextu názvů. + + + Certificate files (*.pem) + Soubory certifikátů (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + Nedaří se připojit na LDAP server. Zkontrolujte parametry serveru. + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + Nedaří se přihlásit k LDAP serveru. Zkontrolujte parametry serveru a přihlašovací údaje. + + + Encryption protocol + Šifrovací protokol + + + Computer location attribute + Atribut umístění počítače + + + Computer display name attribute + Atribut zobrazovaného názvu počítače + + + Location name attribute + Atribut názvu umístění + + + e.g. cn or displayName + např. cn nebo displayName + + + Computer locations identification + Identifikace umístění počítače + + + Identify computer locations (e.g. rooms) via: + Identifikuje umístění počítačů (např. místnosti) prostřednictvím: + + + Location attribute in computer objects + Atribut umístění v objektu počítače + + + List all entries of a location + Vypsat všechny položky v daném umístění + + + List all locations + Vypsat všechna umístění + + + Enter computer display name + Zadejte zobrazovaný název počítače + + + Please enter a computer display name to query: + Zadejte zobrazovaný název počítače na se který dotázat: + + + Enter computer location name + Zadejte název umístění počítače + + + Please enter the name of a computer location (wildcards allowed): + Zadejte název umístění počítače (je možné použít i zástupné znaky): + + + computer locations + umístění počítačů + + + Enter location name + Zadejte název umístění + + + Please enter the name of a location whose entries to query: + Zadejte název umístění na jejíž členy se chcete dotázat: + + + location entries + položky umístění + + + LDAP test failed + Zkouška LDAP neúspěšná + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + Nedaří se dotázat na žádné %1. Zkontrolujte parametr(y) %2 a zadejte název existujícího objektu. + +%3 + + + and + a + + + LDAP test successful + Zkouška LDAP úspěšná + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + Nedaří se dotázat na žádné položky v nastaveném %1. Zkontrolujte parametr „%2“. + +%3 + + + Browse + Procházet + + + Test + Vyzkoušet funkčnost + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + Názvy strojů ukládané jako úplné doménové názvy (FQDN), např. mujstroj.example.org + + + Computer hostname attribute + Atribut název počítače + + + Please enter a computer hostname to query: + Zadejte název počítače na se který dotázat: + + + Invalid hostname + Neplatný název stroje + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + Nastavili jste, že názvy strojů mají být ukládány v podobě úplných doménových názvů (FQDN), ale nyní jste zadali pouze název stroje bez domény. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + Nastavili jste, že názvy strojů mají být ukládány v podobě krátkých názvů bez doménové části, ale nyní jste zadali název stroje včetně domény. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + Nedaří se nalézt uživatele se jménem „%1“. Zkontrolujte uživatelské jméno nebo parametr strom uživatelů. + + + Enter hostname + Zadejte název stroje + + + Please enter a computer hostname whose group memberships to query: + Zadejte název počítače na jehož členství ve skupinách se dotázat: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + Nedaří se nalézt počítač nazvaný „%1“. Zkontrolujte název stroje nebo parametr strom počítačů. + + + Hostname lookup failed + Vyhledání názvu stroje se nezdařilo + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + Nedaří se zjistit název stroje pro IP adresu %1. Zkontrolujte nastavení pro DNS server. + + + User login name attribute + Atribut obsahující přihlašovací jméno uživatele + + + Configured attribute for user login name or computer hostname (OpenLDAP) + Nastavený atribut pro uživatelské jméno nebo název počítače (OpenLDAP) + + + computer containers + kontejnery s počítači + + + + LdapPlugin + + Auto-configure the base DN via naming context + Automaticky nastavit základ rozliš. názvu (DN) prostřednictvím kontextu pojmenování + + + Query objects from LDAP directory + Dotazovat se na objekty z LDAP adresáře + + + Show help about command + Zobrazit nápovědu k příkazu + + + Commands for configuring and testing LDAP/AD integration + Příkazy pro nastavování a testování napojení na LDAP/AD + + + Basic LDAP/AD support for Veyon + Základní podpora pro LDAP/AD ve Veyon + + + %1 (load computers and locations from LDAP/AD) + %1 (načíst počítače a umístění z LDAP/AD) + + + %1 (load users and groups from LDAP/AD) + %1 (načíst uživatele a skupiny z LDAP/AD) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + Zadejte platnou LDAP url následující toto schéma „ldap[s]://[uzivatel[:heslo]@]stroj[:port]" + + + No naming context attribute name given - falling back to configured value. + Nebyl zadán žádný název pro atribut jmenného kontextu – náhradně bude použita nastavená hodnota. + + + Could not query base DN. Please check your LDAP configuration. + Nedaří se dotázat na nastavený základ rozlišeného názvu. Zkontrolujte nastavení svého LDAP. + + + Configuring %1 as base DN and disabling naming context queries. + %1 je nastavováno jako základ rozlišeného názvu a vypínají se dotazy na jmenný kontext. + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + Uživatelsky určená PAM služba pro ověřování uživatelů + + + User authentication + Ověření uživatele + + + Session management + Správa relace + + + Display manager users + Zobrazit uživatele-správce + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Zásuvný modul implementující abstrahující funkce pro Linuxovou platformu + + + + LocationDialog + + Select location + Vybrat umístění + + + enter search filter... + zadejte vyhledávací filtr… + + + + MainToolBar + + Configuration + Nastavení + + + Disable balloon tooltips + Nezobrazovat popisky nástrojů + + + Show icons only + Zobrazovat pouze ikony + + + + MainWindow + + MainWindow + Hlavní Okno + + + toolBar + Lišta nástrojů + + + General + Obecné + + + &File + S&oubor + + + &Help + &Nápověda + + + &Quit + &Ukončit + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + Načíst nastavení ze soub&oru + + + Ctrl+O + Ctrl+O + + + About Qt + O aplikačním rámci (framework) Qt + + + Authentication impossible + Ověření není možné + + + Configuration not writable + Nastavení není přístupné pro zápis + + + Load settings from file + Načíst nastavení ze souboru + + + Save settings to file + Uložit nastavení do souboru + + + Unsaved settings + Neuložená nastavení + + + There are unsaved settings. Quit anyway? + Některá nastavení doposud nebyla uložena. Ukončit i tak? + + + Veyon Configurator + Nastavení pro Veyon + + + Service + Služba + + + Master + Řídící + + + Access control + Řízení přístupu + + + About Veyon + O aplikaci Veyon + + + Auto + Automaticky + + + About + O aplikaci + + + %1 Configurator %2 + %1 nastavení %2 + + + JSON files (*.json) + Soubory JSON (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + Podpůrná vrstva (backend) nastavení na tomto počítači hlásí, že do nastavení nelze zapisovat! Spusťte konzolu pro správu %1 s vyššími oprávněními. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + Nebyly nalezeny žádné ověřovací klíče nebo jsou ty vámi právě používané zastaralé. Vytvořte nové soubory s klíči pomocí konzole pro správu %1. Případně můžete v konzoli pro správu %1 nastavit ověřování přihlášením. Pokud tak neučiníte, nebudete moci přistupovat k počítačům pomocí %1. + + + Access denied + Přístup odepřen + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + Podle místního nastavení vám není umožněn přístup k počítačům na síti. Přihlaste se jiným účtem nebo se obraťte na správce systémů ohledně kontroly místního nastavení. + + + Screenshots + Snímky obrazovky + + + Feature active + Funkce aktivní + + + The feature "%1" is still active. Please stop it before closing %2. + Funkce „%1“ je pořád aktivní. Ukončete ji a až teprve potom ukončete %2. + + + Reset configuration + Resetovat nastavení + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Opravdu chcete resetovat místní nastavení a vrátit veškerá nastavení do výchozích hodnot? + + + Search users and computers + Hledání uživatelů a počítačů + + + Align computers to grid + Zarovnat počítače do mřížky + + + %1 Configurator + Nastavení %1 + + + Insufficient privileges + Nedostatečná oprávnění + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + Nedaří se spustit s oprávněními správce. Ověřte že je v desktopovém prostředí nainstalovaný program typu sudo ! Program bude spuštěný s běžnými uživatelskými oprávněními. + + + Only show powered on computers + Zobrazit pouze zapnuté počítače + + + &Save settings to file + Uložit na&stavení do souboru + + + &View + &Zobrazení + + + &Standard + &Standardní + + + &Advanced + &Pokročilé + + + Use custom computer arrangement + Použít uživatelsky určené uspořádání počítačů + + + Locations && computers + Umístění && počítače + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Složky + + + User configuration + Nastavení uživatele + + + Feature on computer double click: + Funkce při dvojkliku na počítač: + + + Features + Funkce + + + All features + Veškeré funkce + + + Disabled features + Vypnuté funkce + + + Screenshots + Snímky obrazovky + + + <no feature> + <žádná funkce> + + + Basic settings + Základní nastavení + + + Behaviour + Chování + + + Enforce selected mode for client computers + Vynutit vybraný režim pro klientské počítače + + + Hide local computer + Skrýt tento počítač + + + Hide computer filter field + Skrýt kolonku filtr počítačů + + + Actions such as rebooting or powering down computers + Akce jako například restart nebo vypnutí počítačů + + + User interface + Uživatelské rozhraní + + + Background color + Barva pozadí + + + Thumbnail update interval + Interval aktualizace náhledu + + + ms + ms + + + Program start + Spuštění aplikace + + + Modes and features + Režimy a funkce + + + User and computer name + Uživatel a název počítače + + + Only user name + Pouze uživatelské jméno + + + Only computer name + Pouze název počítače + + + Computer thumbnail caption + Titulek náhledu počítače + + + Text color + Barva textu + + + Sort order + Pořadí řazení + + + Computer and user name + Počítač a uživatelské jméno + + + Computer locations + Umístění počítačů + + + Show current location only + Zobrazit pouze stávající umístění + + + Allow adding hidden locations manually + Umožnit ruční přidávání skrytých umístění + + + Hide empty locations + Skrýt prázdná umístění + + + Show confirmation dialog for potentially unsafe actions + U potenciálně nebezpečných akcí zobrazovat potvrzovací dialog + + + Perform access control + Provádět řízení přístupu + + + Automatically select current location + Automaticky vybrat stávající umístění + + + Automatically open computer select panel + Automaticky otevřít panel výběru počítače + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + Automaticky + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + Dohledování + + + Builtin monitoring mode + Režim vestavěného dohledování + + + This mode allows you to monitor all computers at one or more locations. + Tento režim umožňuje monitorovat veškeré počítače v jednom a více umístěních. + + + + NetworkObjectTreeModel + + Locations/Computers + Umístění/počítače + + + + OpenWebsiteDialog + + Open website + Otevřít webovou stránku + + + e.g. Veyon + např. Veyon + + + Remember and add to website menu + Zapamatovat a přidat do nabídky webu + + + e.g. www.veyon.io + např. www.veyon.io + + + Please enter the URL of the website to open: + Zadejte URL adresu webové stránky, kterou otevřít: + + + Name: + Název: + + + + PasswordDialog + + Username + Uživatelské jméno + + + Password + Heslo + + + Veyon Logon + Veyon přihlášení + + + Authentication error + Chyba ověření + + + Logon failed with given username and password. Please try again! + Přihlášení daným uživatelským jménem a heslem se nezdařilo. Zkuste to znovu! + + + Please enter your username and password in order to access computers. + Zadejte své uživatelské jméno a heslo pro přístup k počítačům. + + + + PowerControlFeaturePlugin + + Power on + Zapnout + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Kliknutím na toto tlačítko zapnete všechny počítače. Takto není třeba jednotlivě zapínat každý z počítačů ručně. + + + Reboot + Restartovat + + + Click this button to reboot all computers. + Kliknutím na toto tlačítko restartujete všechny počítače. + + + Power down + Vypnout + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Kliknutím na toto tlačítko vypnete všechny počítače. Takto není třeba jednotlivě vypínat každý z počítačů ručně. + + + Power on/down or reboot a computer + Zap./vyp. nebo restartovat počítač + + + Confirm reboot + Potvrdit restart + + + Confirm power down + Potvrdit vypnutí + + + Do you really want to reboot the selected computers? + Opravdu chcete vybrané počítače restartovat? + + + Do you really want to power down the selected computer? + Opravdu chcete vybrané počítače vypnout? + + + Power on a computer via Wake-on-LAN (WOL) + Zapnout počítač prostřednictvím probouzení po síti (WoL) + + + MAC ADDRESS + MAC ADRESA + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + Tento příkaz vyšle na celou síť probouzecí (WoL) paket, kterými probudí počítač s danou MAC adresou. + + + Please specify the command to display help for! + Zadejte příkaz pro který chcete zobrazit nápovědu! + + + Invalid MAC address specified! + Zadána neplatná MAC adresa! + + + Commands for controlling power status of computers + Příkazy pro řízení stavu napájení počítačů + + + Power down now + Vypnout nyní + + + Install updates and power down + Nainstalovat aktualizace a vypnout + + + Power down after user confirmation + Vypnout po po potvrzení uživatelem + + + Power down after timeout + Vypnout po uplynutí časového limitu + + + The computer was remotely requested to power down. Do you want to power down the computer now? + Na dálku bylo vyžádáno vypnutí počítače. Chcete ho vypnout nyní? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + Počítač bude z %1 minut, %2 sekund vypnut. + +Uložte si rozdělanou práci a ukončete všechny aplikace. + + + + PowerDownTimeInputDialog + + Power down + Vypnout + + + Please specify a timeout for powering down the selected computers: + Zadejte časový limit pro vypnutí označených počítačů: + + + minutes + minut + + + seconds + sekund + + + + RemoteAccessFeaturePlugin + + Remote view + Vzdálený pohled + + + Open a remote view for a computer without interaction. + Otevřít vzdálený pohled na počítač bez interakce. + + + Remote control + Ovládání na dálku + + + Open a remote control window for a computer. + Otevřít okno ovládání počítače na dálku. + + + Remote access + Vzdálený přístup + + + Remote view or control a computer + Pohled na nebo ovládání počítače na dálku + + + Please enter the hostname or IP address of the computer to access: + Zadejte název nebo IP adresu počítače ke kterému přistoupit: + + + Show help about command + Zobrazit nápovědu k příkazu + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 – %2 vzdálený přístup + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + Pouze zobrazovat + + + Remote control + Ovládání na dálku + + + Send shortcut + Odeslat klávesovou zkratku + + + Fullscreen + Celá obrazovka + + + Window + Okno + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Menu + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Připojování k %1… + + + Connected. + Připojeno. + + + Screenshot + Snímek obrazovky + + + Exit + Ukončit + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Zadejte aplikace nebo příkazy které mají být spuštěné na označených počítačích. Každou z aplikací/příkazů uvádějte na samostatný řádek + + + Run programs + Spustit aplikace + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + např.: "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + Název: + + + Remember and add to program menu + Zapamatovat a přidat do nabídky programů + + + e.g. VLC + např. VLC + + + + ScreenLockFeaturePlugin + + Lock + Uzamknout + + + Unlock + Odemknout + + + Lock screen and input devices of a computer + Uzamknout obrazovku a vstupní zařízení počítače + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Abyste upoutali plnou pozornost všech uživatelů, můžete jim pomocí tohoto tlačítka uzamknout jejich počítače. Ty v tomto režimu nebudou reagovat na klávesnici a myš a jejich obrazovky potemní. + + + Lock input devices + Uzamknout vstupní zařízení + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + Neznámé + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + Nepodařilo se pořídit snímek obrazovky protože složka %1 neexistuje a nepodařilo se ji vytvořit. + + + Screenshot + Snímek obrazovky + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + Snímek obrazovky + + + Use this function to take a screenshot of selected computers. + Pomocí této funkce je možné pořizovat snímky obrazovky označených počítačů. + + + Screenshots taken + Snímek obrazovky zachycen + + + Screenshot of %1 computer have been taken successfully. + Snímek obrazovky počítače %1 úspěšně pořízen. + + + Take screenshots of computers and save them locally. + Pořizovat snímky obrazovek počítačů a ukládat je místně. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Zde jsou vypsány veškeré snímky obrazovky, které jste pořídili. Snímky je možné pořídit kliknutím na položku „Snímek obrazovky“ v kontextové nabídce počítače. Snímky obrazovky je možné spravovat pomocí níže se nacházejícího tlačítka. + + + User: + Uživatel: + + + Computer: + Počítač: + + + Date: + Datum: + + + Time: + Čas: + + + Show + Zobrazit + + + Delete + Smazat + + + Screenshot + Snímek obrazovky + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Obecné + + + Autostart + Spouštět automaticky + + + Hide tray icon + Skrýt ikonu v oznamovací oblasti + + + Start service + Spustit službu + + + Stopped + Zastaveno + + + Stop service + Zastavit službu + + + State: + Stav: + + + Enable firewall exception + Vytvořit výjimku na bráně firewall + + + Allow connections from localhost only + Umožnit připojení pouze v rámci tohoto počítače (localhost) + + + VNC server + VNC server + + + Plugin: + Zásuvný modul: + + + Restart %1 Service + Restartovat službu %1 + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Všechna nastavení byla úspěšně uložena. Aby se změny projevily, je třeba restartovat službu %1 – provést nyní? + + + Running + Spuštěné + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + Zapnutím této volby bude serverový proces spouštěn pro každé z interaktivních sezení na počítači. +Typicky je toto třeba na terminálových serverech. + + + Show notification on remote connection + Zobrazovat upozornění na připojení na dálku + + + Show notification when an unauthorized access is blocked + Zobrazit oznámení když je zablokován nepověřený přístup + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + Ukázkový server + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + Spouštění služby %1 + + + Stopping service %1 + Zastavování služby %1 + + + Registering service %1 + Registrace služby %1 + + + Unregistering service %1 + Rušení registrace služby %1 + + + Service control + Řízení služby + + + + ServiceControlPlugin + + Service is running + Služba je spuštěná + + + Service is not running + Služba není spuštěná + + + Configure and control Veyon service + Nastavit a ovládat službu Veyon + + + Register Veyon Service + Zaregistrovat službu Veyon + + + Unregister Veyon Service + Zrušit registraci služby Veyon + + + Start Veyon Service + Spustit službu Veyon + + + Stop Veyon Service + Zastavit službu Veyon + + + Restart Veyon Service + Restartovat službu Veyon + + + Query status of Veyon Service + Dotázat se na stav služby Veyon + + + Commands for configuring and controlling Veyon Service + Přikazy pro nastavování a ovládání služby Veyon + + + + ShellCommandLinePlugin + + Run command file + Spustit příkazový soubor + + + File "%1" does not exist! + Soubor „%1“ neexistuje! + + + Interactive shell and script execution for Veyon Control + Interaktivní vykonávání shellu a skriptu pro ovládání Veyon + + + Commands for shell functionalities + Příkazy pro shellové funkce + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + Ikona v oznamovací oblasti systémového panelu + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + Podpůrná vrstva uživatelských skupin pro systémové uživatelské skupiny + + + Default (system user groups) + Výchozí (systémové uživatelské skupiny) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + Vyzkoušet vnitřní součásti a funkce Veyon + + + Commands for testing internal components and functions of Veyon + Příkazy pro testování vnitřních součástí a funkcí Veyon + + + + TextMessageDialog + + Send text message + Poslat textovou zprávu + + + Use the field below to type your message which will be sent to all selected users. + Do kolonky níže zadejte svou zprávu, určenou všem označeným uživatelům. + + + + TextMessageFeaturePlugin + + Text message + Textová zpráva + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Pomocí této funkce je možné poslat textovou zprávu všem uživatelům – např. jim zadat nové úkoly. + + + Message from teacher + Zpráva od vyučujícího + + + Send a message to a user + Poslat uživateli zprávu + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Zapnout zachytávání vrstvených (jako by průhledných) oken + + + Poll full screen (leave this enabled per default) + Zjišťovat z celé obrazovky (ponechte zapnuté jako výchozí) + + + Low accuracy (turbo mode) + Nízká přesnost (rychlé) + + + Builtin UltraVNC server configuration + Nastavení vestavěného UltraVNC serveru + + + Enable multi monitor support + Zapnout podporu vícero monitorů + + + Enable Desktop Duplication Engine on Windows 8 and newer + Zapnout Desktop Duplication Engine na Windows 8 a novějších + + + Maximum CPU usage + + + + + UserConfig + + No write access + Do daného umístění nelze zapisovat + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Nedaří se uložit vaše osobní nastavení! Zkontrolujte popis umístění souboru s uživatelskými nastaveními v nastavení %1. + + + + UserLoginDialog + + User login + Přihlášení uživatele + + + Please enter a username and password for automatic login on all computers. + Zadejte uživatelské jméno a heslo pro automatické přihlášení na všechny počítače. + + + Username + Uživatelské jméno + + + Password + Heslo + + + + UserSessionControlPlugin + + Log in + Přihlásit + + + Click this button to log in a specific user on all computers. + Kliknutím na toto tlačítko se přihlásíte jako konkrétní uživatel na všechny počítače. + + + Log off + Odhlásit + + + Click this button to log off users from all computers. + Kliknutím na toto tlačítko ze všech počítačů odhlásíte uživatele. + + + Confirm user logoff + Potvrdit odhlášení uživatele + + + Do you really want to log off the selected users? + Opravdu chcete označené uživatele odhlásit? + + + User session control + Ovládání relace uživatele + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [NEZDAR] + + + Invalid command! + Neplatný příkaz! + + + Available commands: + Příkazy k dispozici: + + + Invalid arguments given + Zadány neplatné argumenty + + + Not enough arguments given - use "%1 help" for more information + Nebyl zadán dostatek parametrů – další informace poskytne „%1 help“ + + + Unknown result! + Neznámý výsledek! + + + Available modules: + Moduly k dispozici: + + + No module specified or module not found - available modules are: + Modul nebyl určen nebo nalezen – k dispozici jsou moduly: + + + Plugin not licensed + Zásuvný modul není licencován + + + INFO + INFORMACE + + + ERROR + CHYBA + + + USAGE + POUŽITÍ + + + DESCRIPTION + POPIS + + + EXAMPLES + PŘÍKLADY + + + WARNING + VAROVÁNÍ + + + + VeyonServiceControl + + Veyon Service + Služba Veyon + + + + VncViewWidget + + Establishing connection to %1 ... + Připojování k %1… + + + + WebApiConfigurationPage + + Web API + + + + General + Obecné + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + s + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + … + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + Nedaří se změnit nastavení vytváření SAS v software. Odesílání Ctrl+Alt+Del přes ovládání na dálku nebude fungovat! + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + Obecné + + + Enable SAS generation by software (Ctrl+Alt+Del) + Zapnout vytváření SAS pomocí software (Ctrl+Alt+Del) + + + Screen lock + Uzamčení obrazovky + + + Hide taskbar + Skrýt lištu úloh + + + Hide start menu + Skrýt nabídku start + + + Hide desktop + Skrýt plochu + + + User authentication + Ověření uživatele + + + Use alternative user authentication mechanism + Použít alternativní mechanizmus ověřování uživatelů + + + User login + Přihlášení uživatele + + + Input start delay + Prodleva začátku vstupu + + + Simulated key presses interval + Interval simulovaných stisků kláves + + + Confirm legal notice (message displayed before user logs in) + Potvrďte právní upozornění (zprávu zobrazovanou před tím, než se uživatel přihlásí) + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Zásuvný modul implementující abstrahující funkce pro platformu Windows + + + + WindowsServiceControl + + The service "%1" is already installed. + Služba „%1“ je už nainstalovaná. + + + The service "%1" could not be installed. + Službu „%1“ se nepodařilo nainstalovat + + + The service "%1" has been installed successfully. + Služba „%1“ byla úspěšně nainstalována. + + + The service "%1" could not be uninstalled. + Službu „%1“ se nepodařilo odinstalovat. + + + The service "%1" has been uninstalled successfully. + Služba „%1“ byla úspěšně odinstalována. + + + The start type of service "%1" could not be changed. + Typ spouštění služby „%1“ nemůže být změněn. + + + Service "%1" could not be found. + Službu „%1“ se nedaří najít. + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Nastavení vestavěného x11vnc serveru + + + Custom x11vnc parameters: + Vlastní parametry pro x11vnc: + + + Do not use X Damage extension + Nezjišťovat změny ve zobrazení pomocí rozšíření Damage pro X zobrazovací server + + + diff --git a/translations/veyon_de.ts b/translations/veyon_de.ts new file mode 100644 index 0000000..798e7da --- /dev/null +++ b/translations/veyon_de.ts @@ -0,0 +1,4080 @@ + + + + + AboutDialog + + About + Über + + + Translation + Übersetzung + + + License + Lizenz + + + About Veyon + Über Veyon + + + Contributors + Mitwirkende + + + Version: + Version: + + + Website: + Webseite: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Deutsche Übersetzung von Tobias Junghans. + + + About %1 %2 + Über %1 %2 + + + Support Veyon project with a donation + Unterstützen Sie das Veyon-Projekt mit einer Spende + + + + AccessControlPage + + Computer access control + Computerzugriffskontrolle + + + Grant access to every authenticated user (default) + Jedem authentifizierten Benutzer Zugriff erlauben (Standard) + + + Test + Testen + + + Process access control rules + Zugriffskontrollregeln abarbeiten + + + User groups authorized for computer access + Autorisierte Benutzergruppen für Computerzugriff + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Bitte geben Sie die Gruppen an, deren Mitgliedern es erlaubt sein soll, auf Computer in Ihrem Veyon-Netzwerk zuzugreifen. + + + Authorized user groups + Autorisierte Benutzergruppen + + + All groups + Alle Gruppen + + + Access control rules + Zugriffskontrollregeln + + + Add access control rule + Zugriffskontrollregel hinzufügen + + + Remove access control rule + Zugriffskontrollregel entfernen + + + Move selected rule down + Gewählte Regel nach unten schieben + + + Move selected rule up + Gewählte Regel nach oben schieben + + + Edit selected rule + Gewählte Regel bearbeiten + + + Enter username + Benutzername eingeben + + + Please enter a user login name whose access permissions to test: + Bitte geben Sie einen Benutzername ein, dessen Zugriffsberechtigungen getestet werden sollen: + + + Access allowed + Zugriff erlaubt + + + The specified user is allowed to access computers with this configuration. + Der angegebene Benutzer darf mit dieser Konfiguration auf Computer zugreifen. + + + Access denied + Zugriff verweigert + + + The specified user is not allowed to access computers with this configuration. + Der angegebene Benutzer darf mit dieser Konfiguration nicht auf Computer zugreifen. + + + Enable usage of domain groups + Verwendung von Domaingruppen aktivieren + + + User groups backend: + Benutzergruppen-Backend: + + + Missing user groups backend + Fehlendes Benutzergruppen-Backend + + + No default user groups plugin was found. Please check your installation! + Es wurde kein Benutzergruppen-Plugin gefunden. Bitte überprüfen Sie Ihre Installation! + + + Restrict access to members of specific user groups + Zugriff auf Mitglieder bestimmter Benutzergruppen einschränken + + + + AccessControlRuleEditDialog + + Edit access control rule + Zugriffskontrollregel bearbeiten + + + General + Allgemein + + + enter a short name for the rule here + Kurznamen für Regel eingeben + + + Rule name: + Regelname: + + + enter a description for the rule here + Beschreibung für Regel eingeben + + + Rule description: + Regelbeschreibung: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Alle Bedingungen umkehren ("ist/hat" wird als "ist/hat nicht" interpretiert) + + + Conditions + Bedingungen + + + is member of group + ist Mitglied von Gruppe + + + Accessing computer is localhost + Zugreifender Computer ist localhost + + + Accessing user is logged on user + Zugreifender Benutzer ist angemeldeter Benutzer + + + Accessing user is already connected + Zugreifender Benutzer ist bereits verbunden + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Wenn mehr als eine Bedingung aktiviert wird muss jede Bedingung zutreffen, damit die Regel angewendet wird (logisches UND). Wenn nur eine von mehreren Regeln zutreffen soll (logisches ODER) erstellen Sie bitte mehrere Zugriffskontrollregeln. + + + Action + Aktion + + + Allow access + Zugriff erlauben + + + Deny access + Zugriff verweigern + + + Ask logged on user for permission + Angemeldeten Benutzer um Erlaubnis fragen + + + None (rule disabled) + Keine (Regel deaktiviert) + + + Accessing user + Zugreifender Benutzer + + + Accessing computer + Zugreifender Computer + + + Local (logged on) user + Lokaler (angemeldeter) Benutzer + + + Local computer + Lokaler Computer + + + Always process rule and ignore conditions + Regel immer verarbeiten und Bedingungen ignorieren + + + No user logged on + Kein Benutzer angemeldet + + + Accessing user has one or more groups in common with local (logged on) user + Zugreifender Benutzer hat eine oder mehrere gemeinsame Gruppen mit lokalem (angemeldeten) Benutzer + + + Accessing computer and local computer are at the same location + Zugreifender Computer und lokaler Computer befinden sich am selben Standort + + + is located at + befindet sich in + + + + AccessControlRulesTestDialog + + Access control rules test + Test Zugriffskontrollregeln + + + Accessing user: + Zugreifender Benutzer: + + + Local computer: + Lokaler Computer: + + + Accessing computer: + Zugreifender Computer: + + + Please enter the following user and computer information in order to test the configured ruleset. + Bitte geben Sie die folgenden Benutzer- und Computerinformationen ein, um das konfigurierte Regelwerk zu testen. + + + Local user: + Lokaler Benutzer: + + + Connected users: + Verbundene Benutzer: + + + The access in the given scenario is allowed. + Der Zugriff wird im angegebenen Szenario erlaubt. + + + The access in the given scenario is denied. + Der Zugriff wird im angegebenen Szenario verweigert. + + + The access in the given scenario needs permission of the logged on user. + Der Zugriff benötigt im angegebenen Szenario die Erlaubnis des angemeldeten Benutzers. + + + ERROR: Unknown action + FEHLER: Unbekannte Aktion + + + Test result + Testergebnis + + + + AuthKeysConfigurationPage + + Authentication keys + Authentifizierungsschlüssel + + + Introduction + Einführung + + + Key file directories + Schlüsseldateiverzeichnis + + + Public key file base directory + Basisverzeichnis der öffentlichen Schlüsseldatei + + + Private key file base directory + Basisverzeichnis der privaten Schlüsseldatei + + + Available authentication keys + Verfügbare Authentifizierungsschlüssel + + + Create key pair + Schlüsselpaar erzeugen + + + Delete key + Schlüssel löschen + + + Import key + Schlüssel importieren + + + Export key + Schlüssel exportieren + + + Set access group + Zugriffsgruppe setzen + + + Key files (*.pem) + Schlüsseldateien (*.pem) + + + Authentication key name + Authentifizierungsschlüsselname + + + Please enter the name of the user group or role for which to create an authentication key pair: + Bitte geben Sie den Namen der Benutzergruppe oder -rolle ein, für die ein Authentifizierungsschlüsselpaar erzeugt werden soll: + + + Do you really want to delete authentication key "%1/%2"? + Möchten Sie den Authentifizierungsschlüssel "%1/%2" wirklich löschen? + + + Please select a key to delete! + Bitte wählen Sie den zu löschenden Schlüssel! + + + Please enter the name of the user group or role for which to import the authentication key: + Bitte geben Sie den Namen der Benutzergruppe oder -rolle ein, für die ein Authentifizierungsschlüssel importiert werden soll: + + + Please select a key to export! + Bitte wählen Sie den zu exportierenden Schlüssel! + + + Please select a user group which to grant access to key "%1": + Bitte wählen Sie eine Benutzergruppe, der der Zugriff auf den Schlüssel "%1" gewährt werden soll: + + + Please select a key which to set the access group for! + Bitte wählen Sie den Schlüssel, für den die Zugriffsgruppe gesetzt werden soll! + + + Please perform the following steps to set up key file authentication: + Bitte führen Sie die folgenden Schritte durch, um die Schlüsseldatei-Authentifizierung einzurichten: + + + 1) Create a key pair on the master computer. + 1) Schlüsselpaar auf dem Master-Computer erzeugen. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Zugriffsgruppe festlegen, deren Mitgliedern der Zugriff auf andere Computer erlaubt werden soll. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Öffentlichen Schlüssel exportieren und auf allen Client-Computern mit dem selben Namen importieren. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Weitere Informationen entnehmen Sie bitte dem <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon-Administrationshandbuch</a>. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Ein Authentifizierungsschlüsselpaar besteht aus zwei zueinander gehörigen Teilen, einem privaten und einem öffentlichen Schlüsselteil. +Mit Hilfe des privaten Schlüssels können Nutzer auf dem Mastercomputer auf Clientcomputer zugreifen. Es ist wichtig, dass nur autorisierte Nutzer Lesezugriff auf die private Schlüsseldatei besitzen. +Der öffentliche Schlüssel wird auf den Clientcomputern genutzt, um für jede eingehende Verbindungsanfrage zu prüfen, ob diese autorisiert ist. + + + + AuthKeysManager + + Please check your permissions. + Bitte überprüfen Sie Ihre Berechtigungen. + + + Key name contains invalid characters! + Schlüsselname enthält ungültige Zeichen! + + + Invalid key type specified! Please specify "%1" or "%2". + Ungültiger Schlüsseltyp angegeben. Bitte geben Sie "%1" oder "%2" an. + + + Specified key does not exist! Please use the "list" command to list all installed keys. + Der angegebene Schlüssel existiert nicht! Bitte benutzen Sie den "list"-Befehl, um alle installierten Schlüssel anzuzeigen. + + + One or more key files already exist! Please delete them using the "delete" command. + Ein oder mehrere Schlüsseldateien existieren bereits! Bitte löschen Sie diese mit Hilfe des "delete"-Befehls. + + + Creating new key pair for "%1" + Erzeuge neues Schlüsselpaar für "%1" + + + Failed to create public or private key! + Erzeugung des öffentlichen oder privaten Schlüssels fehlgeschlagen! + + + Newly created key pair has been saved to "%1" and "%2". + Das neu erzeugte Schlüsselpaar wurde nach "%1" und "%2" gespeichert. + + + Could not remove key file "%1"! + Die Schlüsseldatei "%1" konnte nicht gelöscht werden! + + + Could not remove key file directory "%1"! + Das Schlüsseldateiverzeichnis "%1" konnte nicht gelöscht werden! + + + Failed to create directory for output file. + Erzeugung des Verzeichnisses für die Ausgabedatei fehlgeschlagen. + + + File "%1" already exists. + Datei "%1" existiert bereits. + + + Failed to write output file. + Schreiben der Ausgabedatei fehlgeschlagen. + + + Key "%1/%2" has been exported to "%3" successfully. + Der Schlüssel "%1/%2" wurde erfolgreich nach "%3" exportiert. + + + Failed read input file. + Lesen der Eingabedatei fehlgeschlagen. + + + File "%1" does not contain a valid private key! + Die Datei "%1" enthält keinen gültigen privaten Schlüssel! + + + File "%1" does not contain a valid public key! + Die Datei "%1" enthält keinen gültigen öffentlichen Schlüssel! + + + Failed to create directory for key file. + Erzeugung des Verzeichnisses für Schlüsseldatei fehlgeschlagen. + + + Failed to write key file "%1". + Schreiben der Schlüsseldatei "%1" fehlgeschlagen. + + + Failed to set permissions for key file "%1"! + Setzen der Berechtigungen für Schlüsseldatei "%1" fehlgeschlagen! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + Der Schlüssel "%1/%2" wurde erfolgreich importiert. Bitte überprüfen Sie die Dateiberechtigungen von "%3", um unerlaubten Zugriff zu verhindern. + + + Failed to convert private key to public key + Konvertierung des privaten Schlüssels in einen öffentlichen Schlüssel fehlgeschlagen + + + Failed to create directory for private key file "%1". + Erzeugung des Verzeichnisses für die private Schlüsseldatei "%1" fehlgeschlagen. + + + Failed to save private key in file "%1"! + Speichern des privaten Schlüssels in Datei "%1" fehlgeschlagen! + + + Failed to set permissions for private key file "%1"! + Setzen der Berechtigungen für private Schlüsseldatei "%1" fehlgeschlagen! + + + Failed to create directory for public key file "%1". + Erzeugung des Verzeichnisses für die öffentliche Schlüsseldatei "%1" fehlgeschlagen. + + + Failed to save public key in file "%1"! + Speichern des öffentlichen Schlüssels in Datei "%1" fehlgeschlagen! + + + Failed to set permissions for public key file "%1"! + Setzen der Berechtigungen für öffentliche Schlüsseldatei "%1" fehlgeschlagen! + + + Failed to set owner of key file "%1" to "%2". + Setzen des Besitzers der Schlüsseldatei "%1" auf "%2" fehlgeschlagen. + + + Failed to set permissions for key file "%1". + Setzen der Berechtigungen für Schlüsseldatei "%1" fehlgeschlagen. + + + Key "%1" is now accessible by user group "%2". + Die Benutzergruppe "%2" kann nun auf den Schlüssel "%1" zugreifen. + + + <N/A> + <N/V> + + + Failed to read key file. + Lesen der Schlüsseldatei fehlgeschlagen. + + + + AuthKeysPlugin + + Create new authentication key pair + Neues Authentifizierungsschlüsselpaar erzeugen + + + Delete authentication key + Authentifizierungsschlüssel löschen + + + List authentication keys + Authentifizierungsschlüssel auflisten + + + Import public or private key + Öffentlichen oder privaten Schlüssel importieren + + + Export public or private key + Öffentlichen oder privaten Schlüssel exportieren + + + Extract public key from existing private key + Öffentlichen Schlüssel aus bestehendem privaten Schlüssel extrahieren + + + Set user group allowed to access a key + Benutzergruppe setzen, die auf einen Schlüssel zugreifen darf + + + KEY + SCHLÜSSEL + + + ACCESS GROUP + ZUGRIFFSGRUPPE + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + Dieser Befehl passt die Dateizugriffsberechtigungen auf den Schlüssel <SCHLÜSSEL> so an, dass nur die Benutzergruppe <ZUGRIFFSGRUPPE> Lesezugriff darauf hat. + + + NAME + NAME + + + FILE + DATEI + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Dieser Befehl exportiert den Authentifizierungsschlüssel <SCHLÜSEL> nach <DATEI>. Wenn <DATEI> nicht angegeben wird, wird der Dateiname aus Name und Typ von <SCHLÜSSEL> abgeleitet. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Dieser Befehl importiert den Authentifizierungsschlüssel <SCHLÜSSEL> aus <DATEI>. Wenn <DATEI> nicht angeben wird, wird der Dateiname aus Name und Typ von <SCHLÜSSEL> abgeleitet. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + Dieser Befehl listet alle verfügbaren Authentifizierungsschlüssel im konfigurierten Schlüsselverzeichnis auf. Wenn die Option "%1" angegeben wird, wird stattdessen eine Tabelle mit Schlüsseldetails ausgegeben. Einige Details können fehlen, wenn auf einen Schlüssel nicht zugegriffen werden kann, z.B. aufgrund fehlender Leserechte. + + + Please specify the command to display help for! + Bitte geben Sie den Befehl an, für den Hilfe angezeigt werden soll! + + + TYPE + TYP + + + PAIR ID + PAAR-ID + + + Command line support for managing authentication keys + Kommandozeilenunterstützung zur Verwaltung von Authentifizierungsschlüsseln + + + Commands for managing authentication keys + Befehle zur Verwaltung von Authentifizierungsschlüsseln + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + Dieser Befehl erzeugt ein neues Authentifizierungsschlüsselpaar mit dem Namen <NAME> und speichert den privaten und öffentlichen Schlüssel im konfigurierten Schlüsselverzeichnis. Als Argument muss ein Name für den Schlüssel angegeben werden, der nur auch Buchstaben bestehen darf. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + Dieser Befehl löscht den Authentifizierungsschlüssel ``<SCHLÜSSEL>`` aus dem konfigurierten Schlüsselverzeichnis. Bitte beachten Sie, dass ein Schlüssel nicht wiederhergestellt werden kann, sobald er gelöscht wurde. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + Dieser Befehl extrahiert den öffentlichen Schlüsselteil aus dem privaten Schlüssel ``<SCHLÜSSEL>`` und speichert ihn als den zugehörigen öffentlichen Schlüssel. Bei der Einrichtung eines weiteren Mastercomputers genügt somit die Übertragung des privaten Schlüssels. Anschließend kann der öffentliche Schlüssel extrahiert werden. + + + + AuthKeysTableModel + + Name + Name + + + Type + Typ + + + Access group + Zugriffsgruppe + + + Pair ID + Paar-ID + + + + BuiltinDirectoryConfigurationPage + + Computers + Computer + + + Name + Name + + + Host address/IP + Hostadresse/IP + + + MAC address + MAC-Adresse + + + Add new computer + Neuen Computer hinzufügen + + + Remove selected computer + Gewählten Computer entfernen + + + New computer + Neuer Computer + + + Builtin directory + Integriertes Verzeichnis + + + Locations & computers + Standorte & Computer + + + Locations + Standorte + + + Add new location + Neuen Standort hinzufügen + + + Remove selected location + Gewählten Standort entfernen + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + Der Import von CSV-Dateien ist über die Kommandozeilenschnittstelle möglich. Weitere Informationen finden Sie in der <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">Online-Dokumentation</a>. + + + New location + Neuer Standort + + + + BuiltinDirectoryPlugin + + Show help for specific command + Hilfe für bestimmten Befehl anzeigen + + + Import objects from given file + Objekte aus angegebener Datei importieren + + + Export objects to given file + Objekte in angegebene Datei exportieren + + + Invalid type specified. Valid values are "%1" or "%2". + Ungültiger Typ angegeben. Gültige Werte sind "%1" oder "%2". + + + Type + Typ + + + Name + Name + + + Host address + Hostadresse + + + MAC address + MAC-Adresse + + + Specified object not found. + Angegebenes Objekt nicht gefunden. + + + File "%1" does not exist! + Datei "%1 existiert nicht! + + + Can't open file "%1" for reading! + Datei "%1" kann nicht zum Lesen geöffnet werden! + + + Unknown argument "%1". + Unbekanntes Argument "%1". + + + Computer "%1" (host address: "%2" MAC address: "%3") + Computer "%1" (Hostadresse: %2 MAC-Adresse: "%3") + + + Unclassified object "%1" with ID "%2" + Unbestimmtes Object "%1" mit ID "%2" + + + None + Keine + + + Computer + Computer + + + Root + Wurzel + + + Invalid + Ungültig + + + Error while parsing line %1. + Fehler beim Parsen von Zeile %1. + + + Network object directory which stores objects in local configuration + Netzwerkobjektverzeichnis, das Objekte in lokaler Konfiguration speichert + + + Commands for managing the builtin network object directory + Befehle zur Verwaltung des eingebauten Netzwerkobjektverzeichnisses + + + No format string or regular expression specified! + Kein Format-String oder regulärer Ausdruck angegeben! + + + Can't open file "%1" for writing! + Datei "%1" kann nicht zum Schreiben geöffnet werden! + + + No format string specified! + Kein Format-String angegeben! + + + Object UUID + Objekt-UUID + + + Parent UUID + Eltern-UUID + + + Add a location or computer + Standort oder Computer hinzufügen + + + Clear all locations and computers + Alle Standorte und Computer löschen + + + Dump all or individual locations and computers + Alle oder einzelne Standorte und Computer anzeigen + + + List all locations and computers + Alle Standorte und Computer auflisten + + + Remove a location or computer + Einen Standort oder Computer löschen + + + Location "%1" + Standort "%1" + + + Builtin (computers and locations in local configuration) + Eingebaut (Computer und Standorte in lokaler Konfiguration) + + + Location + Standort + + + FILE + DATEI + + + LOCATION + STANDORT + + + FORMAT-STRING-WITH-PLACEHOLDERS + FORMATSTRING-MIT-PLATZHALTERN + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + REGULÄRER-AUSRUCK-MIT-PLATZHALTERN + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Importiert Objekte aus der angegebenen Textdatei mit Hilfe des angegebenen Formatstrings oder regulären Ausdrucks, die eine oder mehrere Platzhalter enthalten. Gültige Platzhalter sind: %1 + + + Import simple CSV file to a single room + Einfache CSV-Datei in einen einzelnen Raum importieren + + + Import CSV file with location name in first column + CSV-Datei mit Standortname in erster Spalte importieren + + + Import text file with with key/value pairs using regular expressions + Textdatei mit Schlüssel-/Wert-Paaren mit Hilfe von regulären Ausdrücken importieren + + + Import arbitrarily formatted data + Beliebig formatierte Daten importieren + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Exportiert Objekte in die angegebene Textdatei mit Hilfe des angegebenen Formatstrings, der einen oder mehrere Platzhalter enthält. Gültige Platzhalter sind: %1 + + + Export all objects to a CSV file + Alle Objekte in eine CSV-Datei exportieren + + + Export all computers in a specific location to a CSV file + Alle Computer eines bestimmten Standortes in eine CSV-Datei exportieren + + + TYPE + TYP + + + NAME + NAME + + + PARENT + PARENT + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + Fügt ein Objekt hinzu, wobei %1 "%2" oder "%3" sein kann. %4 kann als Name oder UUID angegeben werden. + + + Add a room + Einen Raum hinzufügen + + + Add a computer to room %1 + Einen Computer zu Raum %1 hinzufügen + + + OBJECT + OBJEKT + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Entfernt das angegebene Objekt aus dem Verzeichnis. %1 kann als Name oder UUID angegeben werden. Wenn ein Standort entfernt wird, werden alle darin befindlichen Computer ebenfalls entfernt. + + + Remove a computer by name + Einen Computer über seinen Namen entfernen + + + Remove an object by UUID + Ein Objekt über seine UUID entfernen + + + "Room 01" + "Raum 01" + + + "Computer 01" + "Computer 01" + + + HOST ADDRESS + HOST-ADRESSE + + + MAC ADDRESS + MAC-ADRESSE + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Eingebauter VNC-Server (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Eingebauter VNC-Server (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Host-/IP-Adresse: %1 + + + Active features: %1 + Aktive Funktionen: %1 + + + Online and connected + Online und verbunden + + + Establishing connection + Verbindung wird hergestellt + + + Computer offline or switched off + Computer offline oder ausgeschalten + + + Authentication failed or access denied + Authentifizierung fehgeschlagen oder Zugriff verweigert + + + Disconnected + Nicht verbunden + + + No user logged on + Kein Benutzer angemeldet + + + Logged on user: %1 + Angemeldeter Benutzer: %1 + + + Location: %1 + Standort: %1 + + + Veyon Server unreachable or not running + Veyon-Server nicht erreichbar oder läuft nicht + + + [no user] + [Kein Benutzer] + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 Dienst %2 auf %3:%4 + + + Authentication error + Authentifizierungsfehler + + + Remote access + Fernzugriff + + + User "%1" at host "%2" is now accessing this computer. + Der Benutzer "%1" am Computer "%2" greift jetzt auf diesen Computer zu. + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + Der Benutzer "%1" am Computer "%2" hat versucht, auf diesen Computer zuzugreifen, konnte sich aber nicht erfolgreich authentifizieren. + + + Access control error + Fehler Zugriffskontrolle + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + Der Benutzer "%1" am Computer "%2" hat versucht, auf diesen Computer zuzugreifen, wurde aber aufgrund von Zugriffskontrolleneinstellungen blockiert. + + + Active connections: + Aktive Verbindungen: + + + + ComputerManager + + User + Benutzer + + + Missing network object directory plugin + Fehlendes Netzwerkobjektverzeichnis-Plugin + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Es wurde kein Standard-Netzwerkobjektverzeichnis-Plugin gefunden. Bitte überprüfen Sie Ihre Installation oder stellen ein anderes Netzwerkobjektverzeichnis-Backend mit Hilfe des %1 Configurators ein. + + + Location detection failed + Standorterkennung fehlgeschlagen + + + Computer name;Hostname;User + Computername;Hostname;Benutzer + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + Der Standort dieses Computers konnte nicht ermittelt werden. Das deutet auf ein Problem mit der Systemkonfiguration hin. Stattdessen werden alle Standorte im Computerauswahlbedienfeld angezeigt. + + + + ComputerSelectPanel + + Computer search + Computersuche + + + Add location + Standort hinzufügen + + + Save computer/user list + Computer-/Benutzerliste speichern + + + Select output filename + Ausgabedateiname wählen + + + CSV files (*.csv) + CSV-Dateien (*.csv) + + + File error + Dateifehler + + + Could not write the computer and users list to %1! Please check the file access permissions. + Die Computer- und Benutzerliste konnte nicht in die Datei %1 geschrieben werden. Bitte überprüfen Sie die Dateizugriffsrechte. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Bitte geben Sie eine existierende Konfigurationsdatei für den Import an. + + + Please specify a valid filename for the configuration export. + Bitte geben Sie einen gültigen Dateinamen für den Konfigurationsexport an. + + + Please specify a valid key. + Bitte geben Sie einen gültigen Schlüssel ein. + + + Specified key does not exist in current configuration! + Der angegebene Schlüssel existiert in der derzeitigen Konfiguration nicht! + + + Please specify a valid value. + Bitte geben Sie einen gültigen Wert ein. + + + Configure Veyon at command line + Veyon auf der Kommandozeile konfigurieren + + + Output file is not writable! + Ausgabedatei ist nicht schreibbar! + + + Output directory is not writable! + Ausgabeverzeichnis ist nicht schreibbar! + + + Configuration file is not readable! + Konfigurationsdatei ist nicht lesbar! + + + Clear system-wide Veyon configuration + Systemweite Veyon-Konfiguration löschen + + + List all configuration keys and values + Alle Konfigurationsschlüssel und -werte auflisten + + + Import configuration from given file + Konfiguration aus angegebener Datei importieren + + + Export configuration to given file + Konfiguration in angegebene Datei exportieren + + + Read and output configuration value for given key + Konfigurationswert für gegebenen Schlüssel lesen und ausgeben + + + Write given value to given configuration key + Angegebenen Wert in angegebenen Konfigurationsschlüssel schreiben + + + Unset (remove) given configuration key + Angegebenen Konfigurationsschlüssel zurücksetzen (löschen) + + + Commands for managing the configuration of Veyon + Befehle zur Verwaltung der Veyon-Konfiguration + + + Upgrade and save configuration of program and plugins + Konfiguration von Programm und Plugins aktualisieren und speichern + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + Die Autostart-Einstellung für den %1-Dienst konnte nicht geändert werden. + + + Could not configure the firewall configuration for the %1 Server. + Die Firewall-Einstellungen für den %1 Server konnten nicht geändert werden. + + + Could not configure the firewall configuration for the %1 Worker. + Die Firewall-Einstellungen für den %1 Worker konnten nicht geändert werden. + + + Configuration is not writable. Please check your permissions! + Konfiguration ist nicht schreibbar. Bitte überprüfen Sie Ihre Berechtigungen! + + + Could not apply platform-specific configuration settings. + Die plattformspezifischen Konfigurationseinstellungen konnten nicht angewendet werden. + + + + DemoClient + + %1 Demo + %1 Demo + + + + DemoConfigurationPage + + Demo server + Demo-Server + + + Tunables + Feineinstellungen + + + ms + ms + + + Key frame interval + Key-Frame-Intervall + + + Memory limit + Speicherlimit + + + MB + MB + + + Update interval + Update-Intervall + + + s + s + + + Slow down thumbnail updates while demo is running + Miniaturbildaktualisierungen während laufender Demo verlangsamen + + + + DemoFeaturePlugin + + Stop demo + Demo beenden + + + Window demo + Fenster-Demo + + + Give a demonstration by screen broadcasting + Eine Präsentation durch Bildschirmübertragung + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + In diesem Modus wird Ihr Bildschirm in einem Fenster auf allen Computern angezeigt. Die Benutzer können bei Bedarf zu anderen Fenstern wechseln. + + + Demo + Demo + + + Share your screen or allow a user to share his screen with other users. + Eigenen Bildschirm teilen oder einem Benutzer erlauben, seinen Bildschirm mit anderen Benutzern zu teilen. + + + Full screen demo + Vollbild-Demo + + + Share your own screen in fullscreen mode + Eigenen Bildschirm im Vollbildmodus teilen + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + In diesem Modus wird Ihr Bildschirm im Vollbild auf allen Computern angezeigt, während die Eingabegeräte der Benutzer gesperrt werden. + + + Share your own screen in a window + Eigenen Bildschirm in einem Fenster teilen + + + Share selected user's screen in fullscreen mode + Bildschirm des ausgewählten Benutzers im Vollbild teilen + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + In diesem Modus wird der Bildschirm des gewählten Benutzers im Vollbild auf allen Computern angezeigt, während die Eingabegeräte der Benutzer gesperrt werden. + + + Share selected user's screen in a window + Bildschirm des ausgewählten Benutzers in einem Fenster teilen + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + In diesem Modus wird der Bildschirm des gewählten Benutzers in einem Fenster auf allen Computern angezeigt. Die Benutzer können bei Bedarf zu anderen Fenstern wechseln. + + + Please select a user screen to share. + Bitte wählen Sie einen Benutzer, dessen Bildschirm geteilt werden soll. + + + Please select only one user screen to share. + Bitte wählen Sie nur einen Benutzer-Bildschirm zum Teilen. + + + All screens + Alle Bildschirme + + + Screen %1 [%2] + Bildschirm %1 [%2] + + + + DesktopAccessDialog + + Desktop access dialog + Desktopzugriffsdialog + + + Confirm desktop access + Arbeitsflächenzugriff bestätigen + + + Never for this session + Nie für diese Sitzung + + + Always for this session + Immer für diese Sitzung + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + Der Benutzer %1 am Computer %2 möchte auf Ihre Arbeitsfläche zugreifen. Möchten Sie den Zugriff erlauben? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programme & Webseiten + + + Predefined programs + Vordefinierte Programme + + + Name + Name + + + Path + Pfad + + + Add new program + Neues Programm hinzufügen + + + Remove selected program + Ausgewähltes Programm entfernen + + + Predefined websites + Vordefinierte Webseiten + + + Remove selected website + Ausgewählte Webseiten entfernen + + + URL + URL + + + New program + Neues Programm + + + New website + Neue Webseite + + + + DesktopServicesFeaturePlugin + + Run program + Programm starten + + + Open website + Webseite öffnen + + + Click this button to open a website on all computers. + Klicken Sie auf diesen Button, um eine Webseite auf allen Computern zu öffnen. + + + Start programs and services in user desktop + Programme und Dienste im Benutzerdesktop starten + + + Click this button to run a program on all computers. + Klicken Sie auf diesen Button, um Programme auf allen Computern zu starten. + + + Run program "%1" + Programm "%1" ausführen + + + Custom program + Benutzerdefiniertes Programm + + + Open website "%1" + Webseite "%1" öffnen + + + Custom website + Benutzerdefinierte Webseite + + + + DocumentationFigureCreator + + Teacher + Lehrer + + + Room %1 + Raum %1 + + + Please complete all tasks within the next 5 minutes. + Bitte alle Aufgaben in den nächsten 5 Minuten fertigstellen. + + + Custom website + Benutzerdefinierte Webseite + + + Open file manager + Dateimanager öffnen + + + Start learning tool + Lernwerkzeug starten + + + Play tutorial video + Tutorial-Video abspielen + + + Custom program + Benutzerdefiniertes Programm + + + Handout + Arbeitsblatt + + + Texts to read + Texte zum Lesen + + + generic-student-user + allgemeiner-schueler-benutzer + + + + ExternalVncServer + + External VNC server + Externer VNC-Server + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Konfiguration des externen VNC-Servers + + + Port: + Port: + + + Password: + Passwort: + + + + FeatureControl + + Feature control + Funktionssteuerung + + + + FileTransferConfigurationPage + + File transfer + Dateiübertragung + + + Directories + Verzeichnisse + + + Destination directory + Zielverzeichnis + + + Default source directory + Standardquellverzeichnis + + + Options + Optionen + + + Remember last source directory + Letztes Quellverzeichnis merken + + + Create destination directory if it does not exist + Zielverzeichnis erstellen, wenn es nicht existiert + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + Datei "%1" konnte nicht zum Lesen geöffnet werden! Bitte Berechtigungen überprüfen! + + + + FileTransferDialog + + File transfer + Dateiübertragung + + + Options + Optionen + + + Transfer only + Nur übertragen + + + Transfer and open file(s) with associated program + Übertragen und Dateie(n) mit verknüpftem Programm öffnen + + + Transfer and open destination folder + Übertragen und Zielordner öffnen + + + Files + Dateien + + + Start + Start + + + Overwrite existing files + Bestehende Dateien überschreiben + + + + FileTransferPlugin + + File transfer + Dateiübertragung + + + Click this button to transfer files from your computer to all computers. + Klicken Sie auf diesen Button, um Dateien von Ihrem Computer auf alle Computer zu übertragen. + + + Select one or more files to transfer + Eine oder mehrere Dateien für die Übertragung auswählen + + + Transfer files to remote computer + Dateien auf entfernte Computer übertragen + + + Received file "%1". + Datei "%1" empfangen. + + + Could not receive file "%1" as it already exists. + Datei "%1" konnte nicht empfangen werden, da sie bereits existiert. + + + Could not receive file "%1" as it could not be opened for writing! + Datei "%1" konnte nicht empfangen werden, da sie nicht zum Schreiben geöffnet werden konnte! + + + + GeneralConfigurationPage + + User interface + Benutzeroberfläche + + + Language: + Sprache: + + + Use system language setting + Spracheinstellung des Systems verwenden + + + Veyon + Veyon + + + Logging + Logaufzeichnung + + + Log file directory + Logdateiverzeichnis + + + Log level + Loglevel + + + Nothing + Nichts + + + Only critical messages + Nur kritische Nachrichten + + + Errors and critical messages + Fehler und kritische Nachrichten + + + Warnings and errors + Warnungen und Fehler + + + Information, warnings and errors + Informationen, Warnungen und Fehler + + + Debug messages and everything else + Debugmeldungen und alles andere + + + Limit log file size + Logdateigröße begrenzen + + + Clear all log files + Alle Logdateien leeren + + + Log to standard error output + Nach Standardfehlerausgabe loggen + + + Network object directory + Netzwerkobjektverzeichnis + + + Backend: + Backend: + + + Update interval: + Aktualisierungsintervall: + + + %1 service + %1-Dienst + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + Der %1-Dienst muss temporär beendet werden, um die Logdateien zu löschen. Fortfahren? + + + Log files cleared + Logdateien gelöscht + + + All log files were cleared successfully. + Alle Logdateien wurden erfolgreich gelöscht. + + + Error + Fehler + + + Could not remove all log files. + Konnte nicht alle Logdateien entfernen. + + + MB + MB + + + Rotate log files + Logdateien rotieren + + + x + x + + + seconds + Sekunden + + + Write to logging system of operating system + In Ereignisprotokollierung des Betriebssystems schreiben + + + Authentication + Authentifizierung + + + Method: + Methode: + + + Logon authentication + Anmelde-Authentifizierung + + + Key file authentication + Schlüsseldatei-Authentifizierung + + + Test + Testen + + + Authentication is set up properly on this computer. + Authentifizierung ist auf diesem Computer ordnungsgemäß eingerichtet. + + + Authentication keys are not set up properly on this computer. + Authentifizierungsschlüssel sind auf diesem Computer nicht ordnungsgemäß eingerichtet. + + + Authentication test + Authentifizierungstest + + + + HeadlessVncServer + + Headless VNC server + Headless VNC-Server + + + + LdapBrowseDialog + + Browse LDAP + LDAP durchsuchen + + + + LdapClient + + LDAP error description: %1 + LDAP-Fehlerbeschreibung: %1 + + + + LdapConfigurationPage + + Basic settings + Grundeinstellungen + + + General + Allgemein + + + LDAP server and port + LDAP-Server und Port + + + Bind DN + Bind-DN + + + Bind password + Bind-Passwort + + + Anonymous bind + Anonymer Bind + + + Use bind credentials + Bind-Zugangsdaten verwenden + + + Base DN + Base-DN + + + Fixed base DN + Fester Base-DN + + + e.g. dc=example,dc=org + z.B. dc=example,dc=org + + + Discover base DN by naming context + Base-DN über Naming-Context ermitteln + + + e.g. namingContexts or defaultNamingContext + z.B. namingContexts oder defaultNamingContext + + + Environment settings + Umgebungseinstellungen + + + Object trees + Objektbäume + + + Computer tree + Computerbaum + + + e.g. OU=Groups + z.B. OU=Groups + + + User tree + Benutzerbaum + + + e.g. OU=Users + z.B. OU=Users + + + e.g. OU=Computers + z.B. OU=Computers + + + Group tree + Gruppenbaum + + + Perform recursive search operations in object trees + Rekursive Suchoperationen in Objektbäumen durchführen + + + Object attributes + Objektattribute + + + e.g. hwAddress + z.B. hwAddress + + + e.g. member or memberUid + z.B. member oder memberUid + + + e.g. dNSHostName + z.B. dNSHostName + + + Computer MAC address attribute + Attribut Computer-MAC-Adresse + + + Group member attribute + Attribut Gruppenmitglied + + + e.g. uid or sAMAccountName + z.B. uid oder sAMAccountName + + + Advanced settings + Erweiterte Einstellungen + + + Optional object filters + Optionale Objektfilter + + + Filter for user groups + Filter für Benutzergruppen + + + Filter for users + Filter für Benutzer + + + Filter for computer groups + Filter für Computergruppen + + + Group member identification + Identifizierung von Gruppenmitgliedern + + + Distinguished name (Samba/AD) + Distinguished name (Samba/AD) + + + List all groups of a user + Alle Gruppen eines Benutzers auflisten + + + List all groups of a computer + Alle Gruppen eines Computers auflisten + + + Get computer object by IP address + Computerobjekt über IP-Adresse ermitteln + + + LDAP connection failed + LDAP-Verbindung fehlgeschlagen + + + LDAP bind failed + LDAP-Bind fehlgeschlagen + + + LDAP bind successful + LDAP-Bind erfolgreich + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Der Verbindungsaufbau zum LDAP-Server und LDAP-Bind waren erfolgreich. Die grundlegenden LDAP-Einstellungen sind korrekt eingerichtet. + + + LDAP base DN test failed + LDAP-Base-DN-Test fehlgeschlagen + + + LDAP base DN test successful + LDAP-Base-DN-erfolgreich getestet + + + LDAP naming context test failed + LDAP-Naming-Context-Test fehlgeschlagen + + + LDAP naming context test successful + LDAP-Naming-Context-erfolgreich getestet + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + Der LDAP-Naming-Context wurde erfolgreich abgefragt. Die folgende Base-DN wurde gefunden: +%1 + + + user tree + Benutzerbaum + + + group tree + Gruppenbaum + + + computer tree + Computerbaum + + + Enter username + Benutzername eingeben + + + Please enter a user login name (wildcards allowed) which to query: + Bitte geben Sie den abzufragenden Benutzername ein (Platzhalter erlaubt): + + + user objects + Benutzerobjekte + + + Enter group name + Grupenname eingeben + + + Please enter a group name whose members to query: + Bitte geben Sie den Namen der Gruppe ein, deren Mitglieder abgefragt werden sollen: + + + group members + Gruppenmitglieder + + + Group not found + Gruppe nicht gefunden + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + Es wurde keine Gruppe mit dem Name "%1" gefunden. Bitte überprüfen Sie den Gruppenname oder den Gruppenbaum-Parameter. + + + Enter computer name + Computername eingeben + + + computer objects + Computerobjekte + + + Enter computer DN + Computer-DN eingeben + + + Please enter the DN of a computer whose MAC address to query: + Bitte geben Sie den DN von einem Computer ein, dessen MAC-Adresse abgefragt werden soll: + + + computer MAC addresses + Computer-MAC-Adressen + + + users + Benutzer + + + user groups + Benutzergruppen + + + computer groups + Computergruppen + + + Please enter a user login name whose group memberships to query: + Bitte geben Sie den Benutzerlogin-Name ein, dessen Gruppenmitgliedschaften abgefragt werden sollen: + + + groups of user + Gruppen des Benutzers + + + User not found + Benutzer nicht gefunden + + + groups of computer + Gruppen des Computers + + + Computer not found + Computer nicht gefunden + + + Enter computer IP address + Computer-IP-Adresse eingeben + + + Please enter a computer IP address which to resolve to an computer object: + Bitte geben Sie eine Computer-IP-Adresse ein, die in ein Computerobjekt aufgelöst werden soll: + + + computers + Computer + + + LDAP %1 test failed + LDAP %1 Test fehlgeschlagen + + + LDAP %1 test successful + LDAP %1 Test erfolgreich + + + The %1 has been queried successfully and %2 entries were found. + Der %1 wurde erfolgreich abgefragt und %2 Einträge wurden gefunden. + + + %1 %2 have been queried successfully: + +%3 + %1 %2 wurden erfolgreich abgefragt: + +%3 + + + LDAP filter test failed + LDAP-Filter-Test fehlgeschlagen + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + Es konnten keine %1 mit dem eingestellten Filter abfragen. Bitte überprüfen Sie den LDAP-Filter für %1. + +%2 + + + LDAP filter test successful + LDAP-Filter-Test erfolgreich + + + %1 %2 have been queried successfully using the configured filter. + %1 %2 wurden mit dem konfigurierten Filter erfolgreich abgefragt. + + + (only if different from group tree) + (nur wenn anders als Gruppenbaum) + + + Computer group tree + Computergruppenbaum + + + computer group tree + Computergruppenbaum + + + Filter for computers + Filter für Computer + + + e.g. room or computerLab + z.B. room oder computerLab + + + Integration tests + Integrationstests + + + Computer groups + Computergruppen + + + e.g. name or description + z.B. name oder description + + + Filter for computer containers + Filter für Computercontainer + + + Computer containers or OUs + Computercontainer oder OUs + + + Connection security + Verbindungssicherheit + + + TLS certificate verification + TLS-Zertifikatsüberprüfung + + + System defaults + Systemstandard + + + Never (insecure!) + Nie (unsicher!) + + + Custom CA certificate file + Benutzerdefinierte CA-Zertifikatsdatei + + + None + Keine + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + z.B. (objectClass=computer) + + + e.g. (objectClass=group) + z.B. (objectClass=group) + + + e.g. (objectClass=person) + z.B. (objectClass=person) + + + e.g. (objectClass=room) or (objectClass=computerLab) + z.B. (objectClass=room) oder (objectClass=computerLab) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + z.B. (objectClass=container) oder (objectClass=organizationalUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + Die konfigurierte Base-DN konnte nicht abgefragt werden. Bitte überprüfen Sie den Base-DN-Parameter. + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + Die LDAP-Base-DN wurde erfolgreich abgefragt. Die folgenden Einträge wurden gefunden: + +%1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + Die Base-DN konnte nicht mittels Naming-Contexts abgefragt werden. Bitte überprüfen Sie den Naming-Context-Attribut-Parameter. + +%1 + + + Certificate files (*.pem) + Zertifikatsdateien (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + Verbindung zum LDAP-Server konnte nicht hergestellt werden. Bitte überprüfen Sie die Servereinstellungen. + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + Bind zum LDAP-Server fehlgeschlagen. Bitte überprüfen Sie die Serverparameter und Bind-Zugangsdaten. + +%1 + + + Encryption protocol + Verschlüsselungsprotokoll + + + Computer location attribute + Attribut Computerstandort + + + Computer display name attribute + Attribut Computeranzeigename + + + Location name attribute + Attribut Standortname + + + e.g. cn or displayName + z.B. cn oder displayName + + + Computer locations identification + Identifizierung von Computerstandorten + + + Identify computer locations (e.g. rooms) via: + Computerstandorte (z.B. Räume) identifizieren über: + + + Location attribute in computer objects + Attribut Standort in Computerobjekten + + + List all entries of a location + Alle Einträge eines Standortes auflisten + + + List all locations + Alle Standorte auflisten + + + Enter computer display name + Computeranzeigename eingeben + + + Please enter a computer display name to query: + Bitte geben Sie den abzufragenden Computeranzeigename ein: + + + Enter computer location name + Computerstandortname eingeben + + + Please enter the name of a computer location (wildcards allowed): + Bitte geben Sie den Namen eines Computerstandortes ein (Platzhalter möglich): + + + computer locations + Computerstandorte + + + Enter location name + Standortname eingeben + + + Please enter the name of a location whose entries to query: + Bitte geben Sie den Namen eines Standortes ein, dessen Einträge abgefragt werden sollen: + + + location entries + Standorteinträge + + + LDAP test failed + LDAP-Test fehlgeschlagen + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + Es konnte keine %1 abgefragt werden. Bitte überprüfen Sie den/die Parameter %2 und geben Sie den Namen eines existierenden Objekts ein. + +%3 + + + and + und + + + LDAP test successful + LDAP-Test erfolgreich + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + Es konnten keine Enträge im konfigurierten %1 abgefragt werden. Bitte überprüfen Sie den Parameter "%2". + +%3 + + + Browse + Durchsuchen + + + Test + Testen + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + Hostnamen sind als vollqualifizierte Domainnamen gespeichert (FQDN, z.B. myhost.example.org) + + + Computer hostname attribute + Attribut Computerhostname + + + Please enter a computer hostname to query: + Bitte geben Sie den abzufragenden Computerhostnamen ein: + + + Invalid hostname + Ungültiger Hostname + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + Sie haben eingestellt, dass Computerhostnamen als vollqualifizierte Domainnamen (FQDN) gespeichert sind, haben aber einen Hostnamen ohne Domain eingegeben. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + Sie haben eingestellt, dass Computerhostnamen als einfache Hostnamen ohne Domainnamen gespeichert sind, haben aber einen Hostnamen mit Domain eingegeben. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + Es wurde kein Benutzer mit dem Name "%1" gefunden! Bitte überprüfen Sie den Benutzername oder den Benutzergruppenbaum-Parameter. + + + Enter hostname + Hostname eingeben + + + Please enter a computer hostname whose group memberships to query: + Bitte geben Sie den Computerhostnamen ein, dessen Gruppenmitgliedschaften abgefragt werden sollen: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + Es wurde kein Computer mit dem Hostname "%1" gefunden. Bitte überprüfen Sie den Hostnamen oder den Computerbaum-Parameter. + + + Hostname lookup failed + Hostname-Lookup fehlgeschlagen + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + Der Hostname für die IP-Adresse %1 konnte nicht ermittelt werden. Bitte überprüfen Sie Ihre DNS-Server-Einstellungen. + + + User login name attribute + Attribut Benutzeranmeldename + + + Configured attribute for user login name or computer hostname (OpenLDAP) + Konfiguriertes Attribut für Benutzeranmeldename oder Computerhostname (OpenLDAP) + + + computer containers + Computercontainer + + + + LdapPlugin + + Auto-configure the base DN via naming context + Basis-DN mittels Naming-Context automatisch konfigurieren + + + Query objects from LDAP directory + Objekte aus LDAP-Verzeichnis abfragen + + + Show help about command + Hilfe über Befehl anzeigen + + + Commands for configuring and testing LDAP/AD integration + Befehle zum Konfigurieren und Testen der LDAP-/AD-Integration + + + Basic LDAP/AD support for Veyon + Einfache LDAP/AD-Unterstützung für Veyon + + + %1 (load computers and locations from LDAP/AD) + %1 (Computer und Standorte aus LDAP/AD laden) + + + %1 (load users and groups from LDAP/AD) + %1 (Benutzer und Gruppen aus LDAP/AD laden) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + Bitte geben Sie eine gültige LDAP-URL an, die dem Schema "ldap[s]://[Benutzer[:Passwort]@]Hostname[:Port]" folgt + + + No naming context attribute name given - falling back to configured value. + Es wurde kein Naming-Context-Attribut angegeben - falle auf konfigurierten Wert zurück. + + + Could not query base DN. Please check your LDAP configuration. + Base-DN konnte nicht abgefragt werden. Bitte überprüfen Sie Ihre LDAP-Konfiguration. + + + Configuring %1 as base DN and disabling naming context queries. + Konfiguriere %1 als Base-DN und deaktiviere Naming-Context-Abfragen. + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + Benutzerdefinierter PAM-Dienst für Benutzerauthentifizierung + + + User authentication + Benutzerauthentifizierung + + + Session management + Sitzungsverwaltung + + + Display manager users + Displaymanager-Benutzer + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Plugin zur Implementierung abstrakter Funktionen für die Linux-Plattform + + + + LocationDialog + + Select location + Standort auswählen + + + enter search filter... + Suchfilter eingeben... + + + + MainToolBar + + Configuration + Konfiguration + + + Disable balloon tooltips + Balloon-Tooltips deaktivieren + + + Show icons only + Nur Icons anzeigen + + + + MainWindow + + MainWindow + Hauptfenster + + + toolBar + Werkzeugleiste + + + General + Allgemein + + + &File + &Datei + + + &Help + &Hilfe + + + &Quit + &Beenden + + + Ctrl+Q + Strg+Q + + + Ctrl+S + Strg+S + + + L&oad settings from file + Einstellungen aus Datei &laden + + + Ctrl+O + Strg+O + + + About Qt + Über Qt + + + Authentication impossible + Authentifizierung nicht möglich + + + Configuration not writable + Konfiguration nicht schreibbar + + + Load settings from file + Einstellungen aus Datei laden + + + Save settings to file + Einstellungen in Datei speichern + + + Unsaved settings + Ungespeicherte Einstellungen + + + There are unsaved settings. Quit anyway? + Einige Einstellungen sind nicht gespeichert. Trotzdem beenden? + + + Veyon Configurator + Veyon Configurator + + + Service + Dienst + + + Master + Master + + + Access control + Zugriffskontrolle + + + About Veyon + Über Veyon + + + Auto + Auto + + + About + Über + + + %1 Configurator %2 + %1 Configurator %2 + + + JSON files (*.json) + JSON-Dateien (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + Das Backend für die lokale Konfiguration hat gemeldet, dass die Konfiguration nicht beschreibbar ist. Bitte führen Sie den %1 Configurator mit höheren Privilegien aus. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + Es wurden keine Authentifizierungsschlüsseldateien gefunden oder sie sind nicht mehr aktuell. Bitte erzeugen Sie neue Schlüsseldateien mit Hilfe des %1 Configurators. Alternativ können Sie die Anmelde-Authentifizierung mit Hilfe des %1 Configurators einrichten. Andernfalls werden Sie nicht in der Lage sein, mit %1 auf Computer zuzugreifen. + + + Access denied + Zugriff verweigert + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + Gemäß der lokalen Konfiguration sind Sie nicht berechtigt, auf Computer im Netzwerk zuzugreifen. Bitte melden Sie sich mit einem anderen Konto an oder lassen Ihren Systemadministrator die lokale Konfiguration überprüfen. + + + Screenshots + Bildschirmfotos + + + Feature active + Funktion aktiv + + + The feature "%1" is still active. Please stop it before closing %2. + Die Funktion "%1" ist noch aktiv. Bitte beenden Sie diese bevor Sie %2 schließen. + + + Reset configuration + Konfiguration zurücksetzen + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Möchten Sie wirklich die lokale Konfiguration zurücksetzen und alle Einstellungen auf ihre Vorgabewerte setzen? + + + Search users and computers + Benutzer und Computer suchen + + + Align computers to grid + Computer an Gitter ausrichten + + + %1 Configurator + %1 Configurator + + + Insufficient privileges + Ungenügende Rechte + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + Programmstart mit administrativen Rechten nicht möglich. Bitte stellen Sie sicher, dass ein sudo-ähnliches Programm für Ihre Desktop-Umgebung installiert ist! Das Programm wird nun mit normalen Benutzerrechten ausgeführt. + + + Only show powered on computers + Nur eingeschaltete Computer anzeigen + + + &Save settings to file + Einstellungen in Datei &speichern + + + &View + &Ansicht + + + &Standard + &Standard + + + &Advanced + &Erweitert + + + Use custom computer arrangement + Benutzerdefinierte Computeranordnung verwenden + + + Locations && computers + Standorte && Computer + + + Slideshow + Diashow + + + Spotlight + Scheinwerfer + + + Adjust size of computer icons automatically + Größe der Computer-Icons automatisch anpassen + + + + MasterConfigurationPage + + Directories + Verzeichnisse + + + User configuration + Benutzerkonfiguration + + + Feature on computer double click: + Funktion bei Doppelklick: + + + Features + Funktionen + + + All features + Alle Funktionen + + + Disabled features + Deaktivierte Funktionen + + + Screenshots + Bildschirmfotos + + + <no feature> + <Keine Funktion> + + + Basic settings + Grundeinstellungen + + + Behaviour + Verhalten + + + Enforce selected mode for client computers + Gewählten Modus für Client-Computer durchsetzen + + + Hide local computer + Lokalen Computer ausblenden + + + Hide computer filter field + Filterfeld für Computer ausblenden + + + Actions such as rebooting or powering down computers + Aktionen wie Computer neustarten oder ausschalten + + + User interface + Benutzeroberfläche + + + Background color + Hintergrundfarbe + + + Thumbnail update interval + Aktualisierungsintervall Vorschaubilder + + + ms + ms + + + Program start + Programmstart + + + Modes and features + Modi und Funktionen + + + User and computer name + Benutzer- und Computername + + + Only user name + Nur Benutzername + + + Only computer name + Nur Computername + + + Computer thumbnail caption + Computerminiaturbild-Beschriftung + + + Text color + Textfarbe + + + Sort order + Sortierreihenfolge + + + Computer and user name + Computer- und Benutzername + + + Computer locations + Computerstandorte + + + Show current location only + Nur aktuellen Standort anzeigen + + + Allow adding hidden locations manually + Manuelles Hinzufügen von ausgeblendeten Standorten erlauben + + + Hide empty locations + Leere Standorte ausblenden + + + Show confirmation dialog for potentially unsafe actions + Bestätigungsdialog für möglicherweise unsichere Aktionen anzeigen + + + Perform access control + Zugriffskontrolle durchführen + + + Automatically select current location + Automatisch aktuellen Standort auswählen + + + Automatically open computer select panel + Automatisch Computerauswahlbedienfeld öffnen + + + Hide local session + Lokale Sitzung ausblenden + + + px + px + + + Thumbnail spacing + Abstand Miniaturbilder + + + Auto + Auto + + + Thumbnail aspect ratio + Seitenverhältnis Miniaturbilder + + + Automatically adjust computer icon size + Automatisch die Größe der Computer-Icons anpassen + + + Open feature windows on the same screen as the main window + Funktionsfenster auf dem gleichen Bildschirm wie der des Hauptfensters öffnen + + + + MonitoringMode + + Monitoring + Beobachten + + + Builtin monitoring mode + Eingebauter Beobachtungsmodus + + + This mode allows you to monitor all computers at one or more locations. + Dieser Modus erlaubt es Ihnen, alle Computer an einem oder mehreren Standorten zu beobachten. + + + + NetworkObjectTreeModel + + Locations/Computers + Standorte/Computer + + + + OpenWebsiteDialog + + Open website + Webseite öffnen + + + e.g. Veyon + z.B. Veyon + + + Remember and add to website menu + Merken und zum Webseitenmenü hinzufügen + + + e.g. www.veyon.io + z.B. www.veyon.io + + + Please enter the URL of the website to open: + Bitte geben Sie die URL der zu öffnenden Webseite ein: + + + Name: + Name: + + + + PasswordDialog + + Username + Benutzername + + + Password + Passwort + + + Veyon Logon + Veyon-Anmeldung + + + Authentication error + Authentifizierungsfehler + + + Logon failed with given username and password. Please try again! + Die Anmeldung mit dem angegebenen Benutzername und Passwort ist fehlgeschlagen. Bitte versuchen Sie es erneut! + + + Please enter your username and password in order to access computers. + Bitte geben Sie Ihren Benutzernamen und Passwort ein, um auf Computer zuzugreifen. + + + + PowerControlFeaturePlugin + + Power on + Einschalten + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Klicken Sie diesen Button an, um alle Computer einzuschalten. Auf diesem Wege müssen Sie nicht jeden Computer per Hand einschalten. + + + Reboot + Neustarten + + + Click this button to reboot all computers. + Klicken Sie auf diesen Button, um alle Computer neuzustarten. + + + Power down + Herunterfahren + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Klicken Sie diesen Button an, um alle Computer herunterzufahren. Auf diesem Wege müssen Sie nicht jeden Computer per Hand herunterfahren. + + + Power on/down or reboot a computer + Computer ein-/ausschalten oder neustarten + + + Confirm reboot + Neustart bestätigen + + + Confirm power down + Herunterfahren bestätigen + + + Do you really want to reboot the selected computers? + Möchten Sie wirklich die gewählten Computer neustarten? + + + Do you really want to power down the selected computer? + Möchten Sie wirklich die gewählten Computer herunterfahren? + + + Power on a computer via Wake-on-LAN (WOL) + Computer via Wake-on-LAN (WOL) einschalten + + + MAC ADDRESS + MAC-ADRESSE + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + Dieser Befehl sendet ein Wake-on-LAN (WOL) Paket an das Netzwerk, um den Computer mit der angegebenen MAC-Adresse einzuschalten + + + Please specify the command to display help for! + Bitte geben Sie den Befehl an, für den Hilfe angezeigt werden soll! + + + Invalid MAC address specified! + Ungültige MAC-Adresse angegeben! + + + Commands for controlling power status of computers + Befehle zur Steuerung des Einschaltzustands von Computern + + + Power down now + Jetzt herunterfahren + + + Install updates and power down + Updates installieren und herunterfahren + + + Power down after user confirmation + Nach Benutzerbestätigung herunterfahren + + + Power down after timeout + Nach Zeitablauf herunterfahren + + + The computer was remotely requested to power down. Do you want to power down the computer now? + Der Computer wurde aus der Ferne aufgefordert, herunterzufahren. Möchten Sie den Computer jetzt herunterfahren? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + Der Computer wird in %1 Minuten, %2 Sekunden heruntergefahren. + +Bitte speichern Sie Ihre Arbeiten und schließen alle Programme. + + + + PowerDownTimeInputDialog + + Power down + Herunterfahren + + + Please specify a timeout for powering down the selected computers: + Bitte geben Sie eine Zeit an, nach deren Ablauf die gewählten Computer heruntergefahren werden sollen: + + + minutes + Minuten + + + seconds + Sekunden + + + + RemoteAccessFeaturePlugin + + Remote view + Fernansicht + + + Open a remote view for a computer without interaction. + Eine Fernansicht ohne Interaktion/Bedienung für einen Computer öffnen. + + + Remote control + Fernsteuern + + + Open a remote control window for a computer. + Eine Fernsteuerung für einen Computer öffnen. + + + Remote access + Fernzugriff + + + Remote view or control a computer + Fernansicht oder -Steuerung eines Computers + + + Please enter the hostname or IP address of the computer to access: + Bitte geben Sie den Hostnamen oder IP-Adresse des Computers ein, auf den Sie zugreifen möchten: + + + Show help about command + Hilfe über Befehl anzeigen + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 - %2 Fernzugriff + + + %1 - %2 - %3 Remote Access + %1 - %2 - %3 Fernzugriff + + + + RemoteAccessWidgetToolBar + + View only + Nur beobachten + + + Remote control + Fernsteuern + + + Send shortcut + Tastaturkürzel senden + + + Fullscreen + Vollbild + + + Window + Fenster + + + Ctrl+Alt+Del + Strg+Alt+Entf + + + Ctrl+Esc + Strg+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Menü + + + Alt+Ctrl+F1 + Alt+Strg+F1 + + + Connecting %1 + Verbindung wird hergestellt %1 + + + Connected. + Verbindung hergestellt. + + + Screenshot + Bildschirmfoto + + + Exit + Beenden + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Bitte geben Sie die Programme oder die Befehle ein, die auf den gewählten Computern gestartet werden sollen. Sie können mehrere Programme/Befehle über einzelne Zeilen angeben. + + + Run programs + Programme starten + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + z.B. "C:\Programme\VideoLAN\VLC\vlc.exe" + + + Name: + Name: + + + Remember and add to program menu + Merken und zum Programmmenü hinzufügen + + + e.g. VLC + z.B. VLC + + + + ScreenLockFeaturePlugin + + Lock + Sperren + + + Unlock + Entsperren + + + Lock screen and input devices of a computer + Bildschirm und Eingabegeräte eines Computers sperren + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Mit diesem Button können Sie alle Computer sperren und die volle Aufmerksamkeit der Benutzer zurückerhalten. In diesem Modus werden alle Eingabegeräte gesperrt und die Bildschirme der Benutzer schwarz eingefärbt. + + + Lock input devices + Eingabegeräte sperren + + + Unlock input devices + Eingabegeräte entsperren + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + Mit diesem Button können Sie alle Computer sperren und die volle Aufmerksamkeit der Benutzer zurückerhalten. In diesem Modus werden alle Eingabegeräte gesperrt, während der Desktop sichtbar bleibt. + + + + Screenshot + + unknown + unbekannt + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + Es konnte kein Bildschirmfoto erstellt werden, da das Verzeichnis %1 nicht existiert und nicht erstellt werden konnte. + + + Screenshot + Bildschirmfoto + + + Could not open screenshot file %1 for writing. + Die Bildschirmfotodatei %1 konnte nicht zum Schreiben geöffnet werden. + + + + ScreenshotFeaturePlugin + + Screenshot + Bildschirmfoto + + + Use this function to take a screenshot of selected computers. + Nutzen Sie diese Funktion, um ein Bildschirmfoto der gewählten Computer aufzunehmen. + + + Screenshots taken + Bildschirmfotos aufgenommen + + + Screenshot of %1 computer have been taken successfully. + Bildschirmfotos von %1 Computern wurden erfolgreich aufgenommen. + + + Take screenshots of computers and save them locally. + Bildschirmfotos von Computern aufnehmen und lokal speichern. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Hier sind alle Bildschirmfotos aufgelistet, die Sie erstellt haben. Sie können Bildschirmfotos erstellen, indem Sie den Eintrag "Bildschirmfoto" im Kontextmenü eines Computers anklicken. Die Bildschirmfotos können mit Hilfe der unterhalb befindlichen Schaltflächen verwaltet werden. + + + User: + Benutzer: + + + Computer: + Computer: + + + Date: + Datum: + + + Time: + Zeit: + + + Show + Anzeigen + + + Delete + Löschen + + + Screenshot + Bildschirmfoto + + + Do you really want to delete all selected screenshots? + Möchten Sie wirklich alle ausgewählten Screenshots löschen? + + + + ServiceConfigurationPage + + General + Allgemein + + + Autostart + Autostart + + + Hide tray icon + Icon im Infobereich verstecken + + + Start service + Dienst starten + + + Stopped + Beendet + + + Stop service + Dienst stoppen + + + State: + Status: + + + Enable firewall exception + Firewall-Ausnahme aktivieren + + + Allow connections from localhost only + Nur Verbindungen vom lokalen Computer erlauben + + + VNC server + VNC-Server + + + Plugin: + Plugin: + + + Restart %1 Service + %1 Dienst neustarten + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Alle Einstellungen wurden erfolgreich gespeichert. Damit die Einstellungen wirksam werden, muss der %1-Dienst neugestartet werden. Jetzt neustarten? + + + Running + Läuft + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + Wenn diese Option aktiviert wird, startet der Dienst einen Serverprozess für jede interaktive Sitzung auf einem Computer. +Normalerweise ist dies erforderlich, um Terminalserver zu unterstützen. + + + Show notification on remote connection + Benachrichtigung bei Fernzugriff anzeigen + + + Show notification when an unauthorized access is blocked + Benachrichtigung bei unerlaubtem Zugriff anzeigen + + + Sessions + Sitzungen + + + Single session mode (create server instance for local/physical session only) + Einzelsitzungsmodus (Serverinstanz nur für lokale/physische Sitzung erstellen) + + + Multi session mode (create server instance for each local and remote desktop session) + Mehrfachsitzungsmodus (Serverinstanz für jede lokale und Remote-Desktop-Sitzung erstellen) + + + Maximum session count + Maximale Sitzungsanzahl + + + Network port numbers + Netzwerk-Portnummern + + + Veyon server + Veyon-Server + + + Internal VNC server + Interner VNC-Server + + + Feature manager + Funktionsverwalter + + + Demo server + Demo-Server + + + Miscellaneous network settings + Diverse Netzwerkeinstellungen + + + + ServiceControl + + Starting service %1 + Starte Dienst %1 + + + Stopping service %1 + Beende Dienst %1 + + + Registering service %1 + Registriere Dienst %1 + + + Unregistering service %1 + Deregistriere Dienst %1 + + + Service control + Dienststeuerung + + + + ServiceControlPlugin + + Service is running + Dienst läuft + + + Service is not running + Dienst läuft nicht + + + Configure and control Veyon service + Veyon-Dienst konfigurieren und steuern + + + Register Veyon Service + Veyon-Dienst registrieren + + + Unregister Veyon Service + Veyon-Dienst deregistrieren + + + Start Veyon Service + Veyon-Dienst starten + + + Stop Veyon Service + Veyon-Dienst beenden + + + Restart Veyon Service + Veyon-Dienst neustarten + + + Query status of Veyon Service + Status des Veyon-Diensts abfragen + + + Commands for configuring and controlling Veyon Service + Befehle zur Konfiguration und Steuerung des Veyon-Diensts + + + + ShellCommandLinePlugin + + Run command file + Befehlsdatei ausführen + + + File "%1" does not exist! + Datei "%1 existiert nicht! + + + Interactive shell and script execution for Veyon Control + Interaktive Shell und Scriptausführung für Veyon Control + + + Commands for shell functionalities + Befehle für Shellfunktionalitäten + + + + SlideshowPanel + + Previous + Zurück + + + Start/pause + Starten/pausieren + + + Next + Weiter + + + Duration: + Dauer: + + + + SpotlightPanel + + Add selected computers + Gewählte Computer hinzufügen + + + Remove selected computers + Gewählte Computer entfernen + + + Update computers in realtime + Computer in Echtzeit aktualisieren + + + Spotlight + Scheinwerfer + + + Please select at least one computer to add. + Bitte wählen Sie mindestens einen hinzuzufügenden Computer aus. + + + Please select at least one computer to remove. + Bitte wählen Sie mindestens einen zu entfernenden Computer aus. + + + Add computers by clicking with the middle mouse button or clicking the first button below. + Computer durch Klick mit der mittleren Maustaste oder mit Hilfe des ersten Buttons hinzufügen. + + + + SystemTrayIcon + + System tray icon + Icon im Infobereich + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + Benutzergruppenbackend für Systembenutzergruppen + + + Default (system user groups) + Standard (Systembenutzergruppen) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + Interne Veyon-Komponenten und -Funktionen testen + + + Commands for testing internal components and functions of Veyon + Befehle zum Testen interner Komponenten und Funktionen von Veyon + + + + TextMessageDialog + + Send text message + Textnachricht übermitteln + + + Use the field below to type your message which will be sent to all selected users. + Nutzen Sie das Feld unterhalb, um Ihre Nachricht zu tippen, die an alle Benutzer übermittelt wird. + + + + TextMessageFeaturePlugin + + Text message + Textnachricht + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Nutzen Sie diese Funktion, um eine Textnachricht an alle Benutzer zu übermitteln, z. B. um ihnen neue Aufgaben zuzuweisen. + + + Message from teacher + Nachricht vom Lehrer + + + Send a message to a user + Eine Nachricht an einen Benutzer senden + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Halbdurchsichtige Fenster (layered windows) aufzeichnen + + + Poll full screen (leave this enabled per default) + Vollen Bildschirm abfragen (standardmäßig aktiviert lassen) + + + Low accuracy (turbo mode) + Niedrige Genauigkeit (Turbomodus) + + + Builtin UltraVNC server configuration + Konfiguration des eingebauten UltraVNC-Servers + + + Enable multi monitor support + Multi-Monitor-Unterstützung aktivieren + + + Enable Desktop Duplication Engine on Windows 8 and newer + Desktop-Duplication-Engine unter Windows 8 und neuer aktivieren + + + Maximum CPU usage + Maximale CPU-Auslastung + + + + UserConfig + + No write access + Kein Schreibzugriff + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Ihre persönlichen Einstellungen konnten nicht gespeichert werden! Bitte überprüfen Sie den Pfad für die Benutzerkonfiguration mit Hilfe des %1 Configurators. + + + + UserLoginDialog + + User login + Benutzeranmeldung + + + Please enter a username and password for automatic login on all computers. + Bitte geben Sie einen Benutzernamen und ein Passwort zur automatischen Anmeldung auf allen Computern ein. + + + Username + Benutzername + + + Password + Passwort + + + + UserSessionControlPlugin + + Log in + Anmelden + + + Click this button to log in a specific user on all computers. + Klicken Sie auf diesen Button, um einen bestimmten Benutzer auf allen Computern anzumelden. + + + Log off + Abmelden + + + Click this button to log off users from all computers. + Klicken Sie auf diesen Button, um die Benutzer von allen Computern abzumelden. + + + Confirm user logoff + Benutzerabmeldung bestätigen + + + Do you really want to log off the selected users? + Möchten Sie wirklich die gewählten Benutzer abmelden? + + + User session control + Benutzersitzungssteuerung + + + + VeyonCore + + [OK] + [IN ORDNUNG] + + + [FAIL] + [FEHLGESCHLAGEN] + + + Invalid command! + Ungültiger Befehl! + + + Available commands: + Verfügbare Befehle: + + + Invalid arguments given + Ungültige Argumente angegeben + + + Not enough arguments given - use "%1 help" for more information + Nicht genügend Argumente angegeben - benutzen Sie "%1 help" für mehr Informationen + + + Unknown result! + Unbekanntes Ergebnis! + + + Available modules: + Verfügbare Module: + + + No module specified or module not found - available modules are: + Kein Modul angegeben oder Modul nicht gefunden - verfügbare Module sind: + + + Plugin not licensed + Plugin nicht lizenziert + + + INFO + INFO + + + ERROR + FEHLER + + + USAGE + VERWENDUNG + + + DESCRIPTION + BESCHREIBUNG + + + EXAMPLES + BEISPIELE + + + WARNING + WARNUNG + + + + VeyonServiceControl + + Veyon Service + Veyon-Dienst + + + + VncViewWidget + + Establishing connection to %1 ... + Verbindung zu %1 wird hergestellt ... + + + + WebApiConfigurationPage + + Web API + Web API + + + General + Allgemein + + + Network port + Netzwerkport + + + Enable WebAPI server + WebAPI-Server aktivieren + + + Connection settings + Verbindungseinstellungen + + + Lifetime + Gültigkeitsdauer + + + h + h + + + s + s + + + Idle timeout + Inaktivitätstimeout + + + Authentication timeout + Authentifizierungstimeout + + + Maximum number of open connections + Maximale Anzahl offener Verbindungen + + + Connection encryption + Verbindungsverschlüsselung + + + TLS certificate file + TLS-Zertifikatsdatei + + + TLS private key file + Private TLS-Schlüsseldatei + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + HTTPS mit TLS 1.3 anstatt HTTP verwenden + + + + WebApiPlugin + + Run WebAPI server + WebAPI-Server ausführen + + + Failed to start WebAPI server at port %1 + Starten des WebAPI-Servers auf Port %1 fehlgeschlagen + + + WebAPI server running at port %1 + WebAPI-Server wird auf Port %1 ausgeführt + + + Provide access to a computer via HTTP + Zugriff auf einen Computer über HTTP ermöglichen + + + Commands for running the WebAPI server + Befehle zum Ausführen des WebAPI-Servers + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + Die Einstellung für die SAS-Generation in Software konnte nicht geändert werden. Das Senden von Strg+Alt-Entf über Fernzugriff wird nicht funktionieren! + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + Allgemein + + + Enable SAS generation by software (Ctrl+Alt+Del) + SAS-Generierung in Software aktivieren (Strg+Alt+Entf) + + + Screen lock + Bildschirmsperre + + + Hide taskbar + Taskleiste verbergen + + + Hide start menu + Startmenü verbergen + + + Hide desktop + Desktop verbergen + + + User authentication + Benutzerauthentifizierung + + + Use alternative user authentication mechanism + Alternativen Mechanismus zur Benutzerauthentifizierung verwenden + + + User login + Benutzeranmeldung + + + Input start delay + Verzögerung vor Eingabebeginn + + + Simulated key presses interval + Intervall zwischen simulierten Tastendrücken + + + Confirm legal notice (message displayed before user logs in) + Rechtshinweis bestätigen (Nachricht, die vor Benutzeranmeldung angezeigt wird) + + + Use input device interception driver + Eingabegeräte-Interception-Treiber verwenden + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Plugin zur Implementierung abstrakter Funktionen für die Windows-Plattform + + + + WindowsServiceControl + + The service "%1" is already installed. + Der Dienst "%1" ist bereits installiert. + + + The service "%1" could not be installed. + Der Dienst "%1" konnte nicht installiert werden. + + + The service "%1" has been installed successfully. + Der Dienst "%1" wurde erfolgreich installiert. + + + The service "%1" could not be uninstalled. + Der Dienst "%1" konnte nicht deinstalliert werden. + + + The service "%1" has been uninstalled successfully. + Der Dienst "%1" wurde erfolgreich deinstalliert. + + + The start type of service "%1" could not be changed. + Der Starttyp des Diensts "%1" konnte nicht geändert werden. + + + Service "%1" could not be found. + Der Dienst "%1" wurde nicht gefunden. + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Konfiguration des eingebauten x11vnc-Servers + + + Custom x11vnc parameters: + Benutzerdefinierte x11vnc-Parameter: + + + Do not use X Damage extension + X-Damage-Erweiterung nicht nutzen + + + diff --git a/translations/veyon_el.ts b/translations/veyon_el.ts new file mode 100644 index 0000000..39a19dc --- /dev/null +++ b/translations/veyon_el.ts @@ -0,0 +1,4056 @@ + + + + + AboutDialog + + About + Σχετικά + + + Translation + Μετάφραση + + + License + Άδεια + + + About Veyon + Σχετικά με το Veyon + + + Contributors + Συνεισφέροντες + + + Version: + Έκδοση: + + + Website: + Ιστοσελίδα: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + + + + About %1 %2 + Σχετικά %1 %2 + + + Support Veyon project with a donation + Υποστήριξε το Veyon με μία δωρεά + + + + AccessControlPage + + Computer access control + + + + Grant access to every authenticated user (default) + + + + Test + Δοκιμή + + + Process access control rules + + + + User groups authorized for computer access + Εξουσιοδοτημένα μέλη ομάδων με πρόσβαση στον υπολογιστή + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Παρακαλώ επιλέξτε τις ομάδες των οποίων τα μέλη θα έχουν εξουσιοδότηση πρόσβασης σε υπολογιστές του δικού σας δικτύου Veyon. + + + Authorized user groups + Εξουσιοδοτημένα μέλη ομάδων + + + All groups + Όλες οι ομάδες + + + Access control rules + Έλεγχος πρόσβασης + + + Add access control rule + + + + Remove access control rule + + + + Move selected rule down + Μετακίνηση του κανόνα προς τα κάτω + + + Move selected rule up + Μετακίνηση του κανόνα προς τα πάνω + + + Edit selected rule + Επεξεργασία επιλεγμένου κανόνα + + + Enter username + Εισάγετε όνομα χρήστη + + + Please enter a user login name whose access permissions to test: + Παρακαλώ εισαγάγετε ένα όνομα σύνδεσης χρήστη, του οποίου τα δικαιώματα πρόσβασης θα ελεγχθούν: + + + Access allowed + Επιτρέπεται η πρόσβαση + + + The specified user is allowed to access computers with this configuration. + Ο συγκεκριμένος χρήστης έχει δικαίωμα πρόσβασης σε υπολογιστή, με αυτή την διαμόρφωση. + + + Access denied + Δεν επιτρέπεται η πρόσβαση + + + The specified user is not allowed to access computers with this configuration. + Ο συγκεκριμένος χρήστης δεν έχει δικαίωμα πρόσβασης σε υπολογιστή, με αυτή την διαμόρφωση. + + + Enable usage of domain groups + + + + User groups backend: + + + + Missing user groups backend + + + + No default user groups plugin was found. Please check your installation! + + + + Restrict access to members of specific user groups + + + + + AccessControlRuleEditDialog + + Edit access control rule + + + + General + Γενικά + + + enter a short name for the rule here + γράψτε ένα σύντομο όνομα για τον κανόνα + + + Rule name: + Όνομα κανόνα: + + + enter a description for the rule here + γράψτε μια περιγραφή για τον κανόνα εδώ + + + Rule description: + Περιγραφή κανόνα: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Αντιστροφή όλων των συνθηκών ("είναι/έχει" θα είναι "δεν είναι/δεν έχει") + + + Conditions + Συνθήκες + + + is member of group + είναι μέλος της ομάδας + + + Accessing computer is localhost + + + + Accessing user is logged on user + + + + Accessing user is already connected + + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + + + + Action + Ενέργεια + + + Allow access + Επιτρέπεται η πρόσβαση + + + Deny access + Άρνηση πρόσβασης + + + Ask logged on user for permission + + + + None (rule disabled) + Καμία (κανόνας απενεργοποιημένος) + + + Accessing user + + + + Accessing computer + + + + Local (logged on) user + + + + Local computer + Τοπικός υπολογιστής + + + Always process rule and ignore conditions + + + + No user logged on + + + + Accessing user has one or more groups in common with local (logged on) user + + + + Accessing computer and local computer are at the same location + + + + is located at + + + + + AccessControlRulesTestDialog + + Access control rules test + + + + Accessing user: + + + + Local computer: + Τοπικός υπολογιστής: + + + Accessing computer: + + + + Please enter the following user and computer information in order to test the configured ruleset. + + + + Local user: + Τοπικός χρήστης: + + + Connected users: + Συνδεδεμένοι χρήστες: + + + The access in the given scenario is allowed. + + + + The access in the given scenario is denied. + + + + The access in the given scenario needs permission of the logged on user. + + + + ERROR: Unknown action + + + + Test result + Αποτέλεσμα δοκιμής + + + + AuthKeysConfigurationPage + + Authentication keys + + + + Introduction + Εισαγωγή + + + Key file directories + + + + Public key file base directory + + + + Private key file base directory + + + + Available authentication keys + + + + Create key pair + + + + Delete key + Διαγραφή κλειδιού + + + Import key + Εισαγωγή κλειδιού + + + Export key + Εξαγωγή κλειδιού + + + Set access group + + + + Key files (*.pem) + Αρχεία κλειδιού (*.pem) + + + Authentication key name + + + + Please enter the name of the user group or role for which to create an authentication key pair: + + + + Do you really want to delete authentication key "%1/%2"? + + + + Please select a key to delete! + Επιλέξτε ένα κλειδί για διαγραφή! + + + Please enter the name of the user group or role for which to import the authentication key: + + + + Please select a key to export! + Επιλέξτε ένα κλειδί για εξαγωγή! + + + Please select a user group which to grant access to key "%1": + + + + Please select a key which to set the access group for! + + + + Please perform the following steps to set up key file authentication: + + + + 1) Create a key pair on the master computer. + + + + 2) Set an access group whose members should be allowed to access other computers. + + + + 3) Export the public key and import it on all client computers with the same name. + + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + + + + + AuthKeysManager + + Please check your permissions. + Ελέγξτε τα δικαιώματά σας. + + + Key name contains invalid characters! + + + + Invalid key type specified! Please specify "%1" or "%2". + + + + Specified key does not exist! Please use the "list" command to list all installed keys. + + + + One or more key files already exist! Please delete them using the "delete" command. + + + + Creating new key pair for "%1" + + + + Failed to create public or private key! + + + + Newly created key pair has been saved to "%1" and "%2". + + + + Could not remove key file "%1"! + + + + Could not remove key file directory "%1"! + + + + Failed to create directory for output file. + + + + File "%1" already exists. + + + + Failed to write output file. + + + + Key "%1/%2" has been exported to "%3" successfully. + + + + Failed read input file. + + + + File "%1" does not contain a valid private key! + + + + File "%1" does not contain a valid public key! + + + + Failed to create directory for key file. + + + + Failed to write key file "%1". + + + + Failed to set permissions for key file "%1"! + + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + + + + Failed to convert private key to public key + + + + Failed to create directory for private key file "%1". + + + + Failed to save private key in file "%1"! + + + + Failed to set permissions for private key file "%1"! + + + + Failed to create directory for public key file "%1". + + + + Failed to save public key in file "%1"! + + + + Failed to set permissions for public key file "%1"! + + + + Failed to set owner of key file "%1" to "%2". + + + + Failed to set permissions for key file "%1". + + + + Key "%1" is now accessible by user group "%2". + + + + <N/A> + + + + Failed to read key file. + + + + + AuthKeysPlugin + + Create new authentication key pair + + + + Delete authentication key + + + + List authentication keys + + + + Import public or private key + + + + Export public or private key + + + + Extract public key from existing private key + + + + Set user group allowed to access a key + + + + KEY + + + + ACCESS GROUP + + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + + NAME + + + + FILE + + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + + Please specify the command to display help for! + + + + TYPE + + + + PAIR ID + + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + Όνομα + + + Type + + + + Access group + + + + Pair ID + + + + + BuiltinDirectoryConfigurationPage + + Computers + Υπολογιστές + + + Name + Όνομα + + + Host address/IP + Διεύθυνση ΙΡ + + + MAC address + Διεύθυνση MAC + + + Add new computer + Προσθήκη υπολογιστή + + + Remove selected computer + Διαγραφή του επιλεγμένου υπολογιστή + + + New computer + Καινούριος υπολογιστής + + + Builtin directory + + + + Locations & computers + + + + Locations + + + + Add new location + + + + Remove selected location + + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + + + + + BuiltinDirectoryPlugin + + Show help for specific command + + + + Import objects from given file + + + + Export objects to given file + + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + + + + Name + Όνομα + + + Host address + + + + MAC address + Διεύθυνση MAC + + + Specified object not found. + + + + File "%1" does not exist! + + + + Can't open file "%1" for reading! + + + + Unknown argument "%1". + + + + Computer "%1" (host address: "%2" MAC address: "%3") + + + + Unclassified object "%1" with ID "%2" + + + + None + + + + Computer + + + + Root + + + + Invalid + + + + Error while parsing line %1. + + + + Network object directory which stores objects in local configuration + + + + Commands for managing the builtin network object directory + + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + + + + Parent UUID + + + + Add a location or computer + + + + Clear all locations and computers + + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + + + + FILE + + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + + + + NAME + + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + + + + + ComputerControlListModel + + Host/IP address: %1 + + + + Active features: %1 + + + + Online and connected + + + + Establishing connection + + + + Computer offline or switched off + + + + Authentication failed or access denied + + + + Disconnected + + + + No user logged on + + + + Logged on user: %1 + + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + + + + Authentication error + + + + Remote access + Απομακρυσμένη πρόσβαση + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + Χρήστης + + + Missing network object directory plugin + + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + Αναζήτηση υπολογιστή + + + Add location + + + + Save computer/user list + + + + Select output filename + + + + CSV files (*.csv) + + + + File error + + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + + + + Please specify a valid filename for the configuration export. + + + + Please specify a valid key. + + + + Specified key does not exist in current configuration! + + + + Please specify a valid value. + + + + Configure Veyon at command line + + + + Output file is not writable! + + + + Output directory is not writable! + + + + Configuration file is not readable! + + + + Clear system-wide Veyon configuration + + + + List all configuration keys and values + + + + Import configuration from given file + + + + Export configuration to given file + + + + Read and output configuration value for given key + + + + Write given value to given configuration key + + + + Unset (remove) given configuration key + + + + Commands for managing the configuration of Veyon + + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + + + + + DemoConfigurationPage + + Demo server + + + + Tunables + + + + ms + ms + + + Key frame interval + + + + Memory limit + + + + MB + ΜΒ + + + Update interval + Διάστημα ανανέωσης + + + s + s + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + Διακοπή παρουσίασης + + + Window demo + Παρουσίαση σε παράθυρο + + + Give a demonstration by screen broadcasting + Κάντε μια παρουσίαση μεταδίδοντας το περιεχόμενο της οθόνης σας + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + + + + Confirm desktop access + + + + Never for this session + Ποτέ για αυτήν την συνεδρία + + + Always for this session + Πάντα για αυτήν την συνεδρία + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + + DesktopServicesConfigurationPage + + Programs & websites + + + + Predefined programs + + + + Name + Όνομα + + + Path + + + + Add new program + + + + Remove selected program + + + + Predefined websites + + + + Remove selected website + + + + URL + + + + New program + + + + New website + + + + + DesktopServicesFeaturePlugin + + Run program + Εκτέλεση προγράμματος + + + Open website + Άνοιγμα ιστοχώρου + + + Click this button to open a website on all computers. + Πατήστε το κουμπί για να ανοίξετε έναν ιστοχώρο σε όλους τους υπολογιστές. + + + Start programs and services in user desktop + + + + Click this button to run a program on all computers. + + + + Run program "%1" + + + + Custom program + + + + Open website "%1" + + + + Custom website + + + + + DocumentationFigureCreator + + Teacher + Εκπαιδευτικός + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + Εξωτερικός διακομιστής VNC + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Ρυθμίσεις εξωτερικού διακομιστή VNC + + + Port: + Πόρτα: + + + Password: + Κωδικός πρόσβασης: + + + + FeatureControl + + Feature control + + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + Φάκελοι + + + Destination directory + + + + Default source directory + + + + Options + + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + + + + Select one or more files to transfer + + + + Transfer files to remote computer + + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + Διεπαφή χρήστη + + + Language: + Γλώσσα: + + + Use system language setting + Χρήση της γλώσσας του συστήματος + + + Veyon + Veyon + + + Logging + Καταγραφή συμβάντων + + + Log file directory + Φάκελος αρχείου καταγραφής + + + Log level + Λεπτομέρεια αρχείου καταγραφής + + + Nothing + Τίποτα + + + Only critical messages + Μόνο τα κρίσιμα μηνύματα + + + Errors and critical messages + Σφάλματα και κρίσιμα μηνύματα + + + Warnings and errors + Προειδοποιήσεις και σφάλματα + + + Information, warnings and errors + Πληροφορίες, προειδοποιήσεις και σφάλματα + + + Debug messages and everything else + + + + Limit log file size + Μέγεθος αρχείου καταγραφής + + + Clear all log files + + + + Log to standard error output + + + + Network object directory + + + + Backend: + + + + Update interval: + Ρυθμός ανανέωσης: + + + %1 service + υπηρεσία %1 + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + + + + All log files were cleared successfully. + Η εκκαθάριση των αρχείων καταγραφής έγινε με επιτυχία. + + + Error + Σφάλμα + + + Could not remove all log files. + + + + MB + ΜΒ + + + Rotate log files + + + + x + x + + + seconds + δευτερόλεπτα + + + Write to logging system of operating system + + + + Authentication + + + + Method: + + + + Logon authentication + + + + Key file authentication + + + + Test + Δοκιμή + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + + + + + LdapConfigurationPage + + Basic settings + Βασικές ρυθμίσεις + + + General + Γενικά + + + LDAP server and port + Διακομιστής και πόρτα LDAP + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + + + + e.g. OU=Computers + + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + + LDAP base DN test failed + + + + LDAP base DN test successful + + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + Εισάγετε όνομα χρήστη + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + + + + Please enter a group name whose members to query: + + + + group members + μέλη ομάδας + + + Group not found + Δεν βρέθηκε η ομάδα + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + + + + users + χρήστες + + + user groups + ομάδες χρηστών + + + computer groups + ομάδες υπολογιστών + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + υπολογιστές + + + LDAP %1 test failed + + + + LDAP %1 test successful + + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + + + + TLS certificate verification + + + + System defaults + + + + Never (insecure!) + + + + Custom CA certificate file + + + + None + + + + TLS + + + + SSL + + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + Δοκιμή + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + Εμφάνιση βοήθειας για την εντολή + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + + + + enter search filter... + + + + + MainToolBar + + Configuration + Ρυθμίσεις + + + Disable balloon tooltips + + + + Show icons only + Προβολή μόνο των εικονιδίων + + + + MainWindow + + MainWindow + + + + toolBar + + + + General + Γενικά + + + &File + &Αρχείο + + + &Help + &Βοήθεια + + + &Quit + &Έξοδος + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + + + + Ctrl+O + Ctrl+O + + + About Qt + Σχετικά με το Qt + + + Authentication impossible + + + + Configuration not writable + + + + Load settings from file + + + + Save settings to file + Αποθήκευση ρυθμίσεων σε αρχείο + + + Unsaved settings + Μη αποθηκευμένες ρυθμίσεις + + + There are unsaved settings. Quit anyway? + Υπάρχουν ρυθμίσεις που δεν έχουν αποθηκευτεί. Να γίνει έξοδος; + + + Veyon Configurator + + + + Service + Υπηρεσία + + + Master + + + + Access control + Έλεγχος πρόσβασης + + + About Veyon + Σχετικά με το Veyon + + + Auto + Αυτόματα + + + About + Σχετικά + + + %1 Configurator %2 + + + + JSON files (*.json) + + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + Δεν επιτρέπεται η πρόσβαση + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + Στιγμιότυπα + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + Επαναφορά ρυθμίσεων + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + + + + Align computers to grid + + + + %1 Configurator + + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Φάκελοι + + + User configuration + + + + Feature on computer double click: + + + + Features + + + + All features + + + + Disabled features + + + + Screenshots + Στιγμιότυπα + + + <no feature> + + + + Basic settings + Βασικές ρυθμίσεις + + + Behaviour + Συμπεριφορά + + + Enforce selected mode for client computers + + + + Hide local computer + Απόκρυψη τοπικού υπολογιστή + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + + + + User interface + Διεπαφή χρήστη + + + Background color + + + + Thumbnail update interval + + + + ms + ms + + + Program start + + + + Modes and features + + + + User and computer name + + + + Only user name + + + + Only computer name + + + + Computer thumbnail caption + + + + Text color + + + + Sort order + + + + Computer and user name + + + + Computer locations + + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + Αυτόματα + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + Άνοιγμα ιστοχώρου + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + Πληκτρολογήστε την διεύθυνση του ιστοχώρου που θέλετε να ανοίξετε: + + + Name: + + + + + PasswordDialog + + Username + Όνομα χρήστη + + + Password + Κωδικός πρόσβασης + + + Veyon Logon + + + + Authentication error + + + + Logon failed with given username and password. Please try again! + + + + Please enter your username and password in order to access computers. + + + + + PowerControlFeaturePlugin + + Power on + + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + + Reboot + Επανεκκίνηση + + + Click this button to reboot all computers. + Πατήστε το κουμπί για την επανεκκίνηση όλων των υπολογιστών. + + + Power down + + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + + + + Confirm reboot + Επιβεβαίωση επανεκκίνησης + + + Confirm power down + + + + Do you really want to reboot the selected computers? + + + + Do you really want to power down the selected computer? + + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + + + + Open a remote view for a computer without interaction. + + + + Remote control + Απομακρυσμένος έλεγχος + + + Open a remote control window for a computer. + Άνοιγμα ενός παράθυρου για τον απομακρυσμένο έλεγχο ενός υπολογιστή. + + + Remote access + Απομακρυσμένη πρόσβαση + + + Remote view or control a computer + Απομακρυσμένος έλεγχος/προβολή ενός υπολογιστή + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + Εμφάνιση βοήθειας για την εντολή + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + Προβολή μόνο + + + Remote control + Απομακρυσμένος έλεγχος + + + Send shortcut + Αποστολή συντόμευσης + + + Fullscreen + Πλήρης οθόνη + + + Window + Παράθυρο + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Μενού + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Σύνδεση σε %1 + + + Connected. + Συνδεδεμένος + + + Screenshot + Στιγμιότυπο + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + Εκτέλεση προγραμμάτων + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + Κλείδωμα + + + Unlock + Ξεκλείδωμα + + + Lock screen and input devices of a computer + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + άγνωστο + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + Στιγμιότυπο + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + Στιγμιότυπο + + + Use this function to take a screenshot of selected computers. + + + + Screenshots taken + + + + Screenshot of %1 computer have been taken successfully. + + + + Take screenshots of computers and save them locally. + + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + Χρήστης: + + + Computer: + Υπολογιστής: + + + Date: + Ημερομηνία: + + + Time: + Ώρα: + + + Show + Προβολή + + + Delete + Διαγραφή + + + Screenshot + Στιγμιότυπο + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Γενικά + + + Autostart + Αυτόματη έναρξη + + + Hide tray icon + Απόκρυψη εικονιδίου γραμμής κατάστασης + + + Start service + Εκκίνηση υπηρεσίας + + + Stopped + Τερματισμένη + + + Stop service + Τερματισμός υπηρεσίας + + + State: + Κατάσταση: + + + Enable firewall exception + + + + Allow connections from localhost only + + + + VNC server + + + + Plugin: + + + + Restart %1 Service + + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + + Running + Εκτελείται + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + + + + Stopping service %1 + + + + Registering service %1 + + + + Unregistering service %1 + + + + Service control + + + + + ServiceControlPlugin + + Service is running + Η υπηρεσία εκτελείται + + + Service is not running + Η υπηρεσία δεν εκτελείται + + + Configure and control Veyon service + + + + Register Veyon Service + + + + Unregister Veyon Service + + + + Start Veyon Service + Εκκίνηση της υπηρεσίας Veyon + + + Stop Veyon Service + Τερματισμός της υπηρεσίας Veyon + + + Restart Veyon Service + Επανεκκίνηση της υπηρεσίας Veyon + + + Query status of Veyon Service + + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + Εικονίδιο γραμμής κατάστασης + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + Αποστολή μηνύματος + + + Use the field below to type your message which will be sent to all selected users. + + + + + TextMessageFeaturePlugin + + Text message + Μήνυμα + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Μπορείτε να στείλετε ένα μήνυμα σε όλους του χρήστες. Για παράδειγμα για να τους αναθέσετε μια εργασία. + + + Message from teacher + Μήνυμα από τον εκπαιδευτικό + + + Send a message to a user + Αποστολή ενός μηνύματος σε ένα χρήστη + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + + + + Poll full screen (leave this enabled per default) + + + + Low accuracy (turbo mode) + + + + Builtin UltraVNC server configuration + + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + Όνομα χρήστη + + + Password + Κωδικός πρόσβασης + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + + + + + VeyonCore + + [OK] + + + + [FAIL] + + + + Invalid command! + + + + Available commands: + Διαθέσιμες εντολές: + + + Invalid arguments given + + + + Not enough arguments given - use "%1 help" for more information + + + + Unknown result! + + + + Available modules: + + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + + + + INFO + + + + ERROR + + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + + + + + VncViewWidget + + Establishing connection to %1 ... + + + + + WebApiConfigurationPage + + Web API + + + + General + Γενικά + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + s + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + Γενικά + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + + + + Custom x11vnc parameters: + + + + Do not use X Damage extension + + + + diff --git a/translations/veyon_es_ES.ts b/translations/veyon_es_ES.ts new file mode 100644 index 0000000..354d2f7 --- /dev/null +++ b/translations/veyon_es_ES.ts @@ -0,0 +1,4085 @@ + + + + + AboutDialog + + About + Acerca de + + + Translation + Traducción + + + License + Licencia + + + About Veyon + Acerca de Veyon + + + Contributors + Colaboradores + + + Version: + Versión: + + + Website: + Sitio web: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Traducción realizada por + +José Antonio Muñoz Jiménez <jamj2000 at gmail dot com> + +Si deseas mejorar la traducción actual, por favor, ¡contacta con un desarrollador de Veyon! + + + About %1 %2 + Acerca de %1 %2 + + + Support Veyon project with a donation + Apoyar el proyecto Veyon con una donación + + + + AccessControlPage + + Computer access control + Control de acceso al equipo + + + Grant access to every authenticated user (default) + Conceder acceso a cada usuario autenticado (predeterminado) + + + Test + Comprobar + + + Process access control rules + Reglas de control de acceso al proceso + + + User groups authorized for computer access + Grupos de usuarios autorizados para acceso al equipo + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Por favor, agregue los grupos cuyos miembros deben estar autorizados a acceder a los equipos de su red Veyon. + + + Authorized user groups + Grupos de usuarios autorizados + + + All groups + Todos los grupos + + + Access control rules + Reglas de control de acceso + + + Add access control rule + Añadir regla de control de acceso + + + Remove access control rule + Eliminar regla de control de acceso + + + Move selected rule down + Mover la regla seleccionada hacia abajo + + + Move selected rule up + Mover la regla seleccionada hacia arriba + + + Edit selected rule + Editar la regla seleccionada + + + Enter username + Introduzca nombre de usuario + + + Please enter a user login name whose access permissions to test: + Por favor, introduzca un nombre de usuario para comprobar los permisos de acceso: + + + Access allowed + Acceso permitido + + + The specified user is allowed to access computers with this configuration. + El usuario especificado puede acceder a los equipos con esta configuración. + + + Access denied + Acceso denegado + + + The specified user is not allowed to access computers with this configuration. + El usuario especificado no puede acceder a los equipos con esta configuración. + + + Enable usage of domain groups + Habilitar el uso de grupos de dominio + + + User groups backend: + Backend para grupos de usuarios: + + + Missing user groups backend + Falta backend para grupos de usuarios + + + No default user groups plugin was found. Please check your installation! + No se encontró el plugin por defecto para grupos de usuarios. ¡Por favor, compruebe la instalación! + + + Restrict access to members of specific user groups + Restringir el acceso a miembros de grupos de usuarios específicos + + + + AccessControlRuleEditDialog + + Edit access control rule + Editar regla de control de acceso + + + General + General + + + enter a short name for the rule here + introduzca aquí un nombre corto para la regla + + + Rule name: + Nombre de regla: + + + enter a description for the rule here + introduzca aquí una descripción para la regla + + + Rule description: + Descripción de regla: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Invertir todas las condiciones ("está/tiene" interpretado como "no está/no tiene") + + + Conditions + Condiciones + + + is member of group + es miembro del grupo + + + Accessing computer is localhost + El computador de acceso es localhost + + + Accessing user is logged on user + El usuario de acceso ha iniciado sesión + + + Accessing user is already connected + El usuario de acceso ya está conectado + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Si se activa más de una condición, cada condición se tiene que cumplir para hacer que la regla se aplique (AND lógico). Si sólo una de las condiciones múltiples se tiene que cumplir (OR lógico) cree varias reglas de control de acceso. + + + Action + Acción + + + Allow access + Permitir el acceso + + + Deny access + Denegar el acceso + + + Ask logged on user for permission + Solicitar permiso al usuario conectado + + + None (rule disabled) + Ninguno (regla deshabilitada) + + + Accessing user + Acceso al usuario + + + Accessing computer + Acceso al equipo + + + Local (logged on) user + Usuario local (conectado) + + + Local computer + Equipo local + + + Always process rule and ignore conditions + Siempre procesar reglas e ignorar condiciones + + + No user logged on + Ningún usuario ha iniciado sesión + + + Accessing user has one or more groups in common with local (logged on) user + El acceso al usuario tiene uno o más grupos en común con el usuario local (conectado) + + + Accessing computer and local computer are at the same location + El acceso al equipo y el equipo local están en la misma ubicación + + + is located at + se encuentra en + + + + AccessControlRulesTestDialog + + Access control rules test + Prueba de reglas de control de acceso + + + Accessing user: + Acceso al usuario: + + + Local computer: + Equipo local: + + + Accessing computer: + Acceso al equipo: + + + Please enter the following user and computer information in order to test the configured ruleset. + Introduzca la siguiente información de usuario y de equipo para probar el conjunto de reglas configurado. + + + Local user: + Usuario local: + + + Connected users: + Usuarios conectados: + + + The access in the given scenario is allowed. + Se permite el acceso en el escenario dado. + + + The access in the given scenario is denied. + El acceso en el escenario dado se deniega. + + + The access in the given scenario needs permission of the logged on user. + El acceso en el escenario dado necesita permiso del usuario conectado. + + + ERROR: Unknown action + ERROR: Acción desconocida + + + Test result + Resultado de la prueba + + + + AuthKeysConfigurationPage + + Authentication keys + Claves de autenticación + + + Introduction + Introducción + + + Key file directories + Directorios de claves + + + Public key file base directory + Directorio base para archivo de clave pública + + + Private key file base directory + Directorio base para archivo de clave privada + + + Available authentication keys + Claves de autenticación disponibles + + + Create key pair + Crear par de claves + + + Delete key + Borrar clave + + + Import key + Importar clave + + + Export key + Exportar clave + + + Set access group + Establecer el grupo de acceso + + + Key files (*.pem) + Archivos de clave (*.pem) + + + Authentication key name + Nombre de la clave de autenticación + + + Please enter the name of the user group or role for which to create an authentication key pair: + Introduzca el nombre del grupo de usuarios o rol para el cual crear un par de claves de autenticación: + + + Do you really want to delete authentication key "%1/%2"? + ¿Realmente desea eliminar la clave de autenticación "%1/%2"? + + + Please select a key to delete! + Por favor, ¡seleccione clave a eliminar! + + + Please enter the name of the user group or role for which to import the authentication key: + Introduzca el nombre del grupo de usuarios o rol para el cual importar la clave de autenticación: + + + Please select a key to export! + Por favor, ¡seleccione una clave a exportar! + + + Please select a user group which to grant access to key "%1": + Seleccione un grupo de usuarios al que otorgar acceso a la clave "%1": + + + Please select a key which to set the access group for! + ¡Seleccione una clave para configurar el grupo que tenga acceso! + + + Please perform the following steps to set up key file authentication: + Realice los siguientes pasos para configurar la autenticación de archivos de claves: + + + 1) Create a key pair on the master computer. + 1) Crea un par de llaves en la computadora maestra. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Establezca un grupo con acceso cuyos miembros deberían tener acceso a otras computadoras. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Exporte la clave pública e impórtela en todas las computadoras cliente con el mismo nombre. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Consulte el <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Manual de Administrador de Veyon</a> para obtener más información. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Un par de claves de autenticación consta de dos claves criptográficas acopladas, una privada y otra pública. +Una clave privada permite a los usuarios de la computadora maestra acceder a las computadoras cliente. +Es importante que solo los usuarios autorizados tengan acceso de lectura al archivo de clave privada. +La clave pública se usa en las computadoras cliente para autenticar la solicitud de conexión entrante. + + + + AuthKeysManager + + Please check your permissions. + Por favor compruebe sus permisos. + + + Key name contains invalid characters! + ¡El nombre de la clave contiene caracteres inválidos! + + + Invalid key type specified! Please specify "%1" or "%2". + ¡Tipo de clave especificada no válida! Especifique "%1" o "%2". + + + Specified key does not exist! Please use the "list" command to list all installed keys. + ¡La clave especificada no existe! Utilice el comando "list" para enumerar todas las claves instaladas. + + + One or more key files already exist! Please delete them using the "delete" command. + ¡Uno o más archivos de claves ya existen! Por favor, elimínelos utilizando el comando "delete". + + + Creating new key pair for "%1" + Creando un nuevo par de claves para "%1" + + + Failed to create public or private key! + ¡No se ha podido crear la clave pública o privada! + + + Newly created key pair has been saved to "%1" and "%2". + El par de claves recién creadas se ha guardado en "%1" y "%2". + + + Could not remove key file "%1"! + ¡No se pudo eliminar el archivo de claves "%1"! + + + Could not remove key file directory "%1"! + ¡No se pudo eliminar el directorio de archivos de claves "%1"! + + + Failed to create directory for output file. + No se pudo crear el directorio para el archivo de salida. + + + File "%1" already exists. + El archivo "%1" ya existe. + + + Failed to write output file. + No se pudo escribir el archivo de salida. + + + Key "%1/%2" has been exported to "%3" successfully. + Clave "%1/%2" se ha exportado a "%3" exitosamente. + + + Failed read input file. + No se pudo leer el archivo de entrada. + + + File "%1" does not contain a valid private key! + ¡Archivo "%1" no contiene una clave privada válida! + + + File "%1" does not contain a valid public key! + ¡Archivo "%1" no contiene una clave pública válida! + + + Failed to create directory for key file. + No se pudo crear el directorio para el archivo de clave. + + + Failed to write key file "%1". + No se pudo escribir el archivo de clave "%1". + + + Failed to set permissions for key file "%1"! + ¡No se pudieron establecer los permisos para el archivo de clave "%1"! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + La clave "%1/%2" se ha importado correctamente. Verifique los permisos de archivo de "%3" para evitar accesos no autorizados. + + + Failed to convert private key to public key + No se pudo convertir la clave privada en clave pública + + + Failed to create directory for private key file "%1". + No se pudo crear el directorio para el archivo de clave privada "%1". + + + Failed to save private key in file "%1"! + ¡No se pudo guardar la clave privada en el archivo "%1"! + + + Failed to set permissions for private key file "%1"! + ¡No se pudieron establecer los permisos para el archivo de clave privada "%1"! + + + Failed to create directory for public key file "%1". + No se pudo crear el directorio para el archivo de clave pública "%1". + + + Failed to save public key in file "%1"! + ¡No se pudo guardar la clave pública en el archivo "%1"! + + + Failed to set permissions for public key file "%1"! + ¡No se pudieron establecer los permisos para el archivo de clave pública "%1"! + + + Failed to set owner of key file "%1" to "%2". + No se pudo establecer el propietario del archivo de clave "%1" a "%2". + + + Failed to set permissions for key file "%1". + Error al establecer los permisos para el archivo de clave "%1". + + + Key "%1" is now accessible by user group "%2". + La clave "%1" ahora es accesible por el grupo de usuarios "%2". + + + <N/A> + <N/A> + + + Failed to read key file. + Error al leer el archivo de clave. + + + + AuthKeysPlugin + + Create new authentication key pair + Crear un nuevo par de claves de autenticación + + + Delete authentication key + Eliminar clave de autenticación + + + List authentication keys + Listas de claves de autenticación + + + Import public or private key + Importar clave pública o privada + + + Export public or private key + Exportar clave pública o privada + + + Extract public key from existing private key + Extraer la clave pública de la clave privada existente + + + Set user group allowed to access a key + Establecer el grupo de usuarios al que se permite acceder a una clave + + + KEY + CLAVE + + + ACCESS GROUP + GRUPO DE ACCESO + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + Este comando ajusta los permisos de acceso a <KEY> de manera que solo el grupo de usuarios <ACCESS GROUP> tenga acceso de lectura a él. + + + NAME + NOMBRE + + + FILE + ARCHIVO + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Este comando exporta la clave de autenticación <KEY> a <FILE>. Si no se especifica <FILE>, se construirá un nombre a partir del nombre y tipo de <KEY>. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Este comando importa la clave de autenticación <KEY> desde <FILE>. Si no se especifica <FILE>, se construirá un nombre a partir del nombre y tipo de <KEY>. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + Este comando enumera todas las claves de autenticación disponibles en el directorio de claves configurado. Si se especifica la opción "%1", se mostrará una tabla con los detalles de las claves. Es posible que falten algunos detalles si no se puede acceder a una clave, p. ej. debido a la falta de permisos de lectura. + + + Please specify the command to display help for! + Por favor, ¡especifique el comando sobre el cual mostrar ayuda! + + + TYPE + TIPO + + + PAIR ID + ID PAR + + + Command line support for managing authentication keys + Soporte de línea de comando para administrar claves de autenticación + + + Commands for managing authentication keys + Comandos para administrar claves de autenticación + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + Este comando crea un nuevo par de claves de autenticación con el nombre <NAME> y guarda las claves privada y pública en los directorios de claves configurados. El parámetro debe ser un nombre para la clave, que solo puede contener letras. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + Este comando elimina la clave de autenticación <KEY> del directorio de claves configuradas. Tenga en cuenta que una clave no se puede recuperar una vez que se ha eliminado. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + Este comando extrae la parte de la clave pública de la clave privada <KEY> y la guarda como la clave pública correspondiente. Al configurar otra computadora maestra, por lo tanto, es suficiente transferir la clave privada solamente. La clave pública puede entonces ser extraída. + + + + AuthKeysTableModel + + Name + Nombre + + + Type + Tipo + + + Access group + Grupo de acceso + + + Pair ID + ID del par + + + + BuiltinDirectoryConfigurationPage + + Computers + Equipos + + + Name + Nombre + + + Host address/IP + Dirección de equipo/IP + + + MAC address + Dirección MAC + + + Add new computer + Añadir nuevo equipo + + + Remove selected computer + Eliminar equipo seleccionado + + + New computer + Nuevo equipo + + + Builtin directory + Directorio incorporado + + + Locations & computers + Ubicaciones y equipos + + + Locations + Ubicaciones + + + Add new location + Añadir nueva ubicación + + + Remove selected location + Eliminar ubicación seleccionada + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + La importación de archivos CSV es posible a través de la interfaz de línea de comandos. Para más información, consulte la <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">documentación en línea</a>. + + + New location + Nueva ubicación + + + + BuiltinDirectoryPlugin + + Show help for specific command + Mostrar ayuda para un comando específico + + + Import objects from given file + Importar objetos desde un archivo + + + Export objects to given file + Exportar objetos a un archivo + + + Invalid type specified. Valid values are "%1" or "%2". + Tipo inválido especificado. Los valores válidos son "%1" o "%2". + + + Type + Tipo + + + Name + Nombre + + + Host address + Dirección del equipo + + + MAC address + Dirección MAC + + + Specified object not found. + Objeto especificado no encontrado. + + + File "%1" does not exist! + ¡El archivo "%1" no existe! + + + Can't open file "%1" for reading! + ¡No se puede abrir el archivo "%1" para leer! + + + Unknown argument "%1". + Argumento "%1" desconocido. + + + Computer "%1" (host address: "%2" MAC address: "%3") + Computador "%1" (dirección del equipo: "%2" Dirección MAC: "%3") + + + Unclassified object "%1" with ID "%2" + Objeto no clasificado "%1" con ID "%2" + + + None + Ninguno + + + Computer + Equipo + + + Root + Raíz + + + Invalid + Inválido + + + Error while parsing line %1. + Error al analizar la línea %1. + + + Network object directory which stores objects in local configuration + Directorio de objetos de red que almacena objetos en configuración local + + + Commands for managing the builtin network object directory + Comandos para administrar el directorio de objetos de red incorporado + + + No format string or regular expression specified! + ¡No se especificó ninguna cadena de formato o expresión regular! + + + Can't open file "%1" for writing! + ¡No se puede abrir el archivo "%1" para escribir! + + + No format string specified! + ¡No se especificó una cadena de formato! + + + Object UUID + UUID de objeto + + + Parent UUID + UUID del principal + + + Add a location or computer + Añadir una ubicación o equipo + + + Clear all locations and computers + Borrar todas las ubicaciones y equipos + + + Dump all or individual locations and computers + Volcar ubicaciones y equipos de forma completa o individual + + + List all locations and computers + Listar todas las ubicaciones y equipos + + + Remove a location or computer + Eliminar una ubicación o equipo + + + Location "%1" + Ubicación "%1" + + + Builtin (computers and locations in local configuration) + Incorporado (equipos y ubicaciones en la configuración local) + + + Location + Ubicación + + + FILE + ARCHIVO + + + LOCATION + UBICACIÓN + + + FORMAT-STRING-WITH-PLACEHOLDERS + CADENA-TEXTO-CON-MARCADORES + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + EXPRESIÓN-REGULAR-CON-MARCADORES + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Importa objetos del archivo de texto especificado utilizando la cadena de formato dada o expresión regular que contiene uno o varios marcadores de posición. Los marcadores válidos son: %1 + + + Import simple CSV file to a single room + Importar archivo CSV simple a un aula individual. + + + Import CSV file with location name in first column + Importar archivo CSV con nombre de ubicación en la primera columna + + + Import text file with with key/value pairs using regular expressions + Importe archivo de texto con pares clave/valor usando expresiones regulares + + + Import arbitrarily formatted data + Importar datos formateados arbitrariamente + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Exporta objetos al archivo de texto especificado utilizando la cadena de formato dada que contiene uno o varios marcadores de posición. Los marcadores válidos son: %1 + + + Export all objects to a CSV file + Exportar todos los objetos a un archivo CSV + + + Export all computers in a specific location to a CSV file + Exportar todos los equipos en una ubicación específica a un archivo CSV + + + TYPE + TIPO + + + NAME + NOMBRE + + + PARENT + PADRE + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + Añade un objeto donde %1 puede ser uno de "%2" o "%3". %4 se puede especificar por nombre o UUID. + + + Add a room + Añadir un aula + + + Add a computer to room %1 + Añadir un equipo al aula %1 + + + OBJECT + OBJETO + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Elimina el objeto especificado del directorio. %1 se puede especificar por nombre o UUID. Eliminar una ubicación también eliminará todos los equipos relacionados. + + + Remove a computer by name + Eliminar un equipo por su nombre + + + Remove an object by UUID + Eliminar un objeto por UUID + + + "Room 01" + "Aula 01" + + + "Computer 01" + "Equipo 01" + + + HOST ADDRESS + DIRECCIÓN DE EQUIPO + + + MAC ADDRESS + DIRECCIÓN MAC + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Servidor VNC (UltraVNC) incorporado + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Servidor VNC (x11vnc) incorporado + + + + ComputerControlListModel + + Host/IP address: %1 + Equipo/Dirección IP: %1 + + + Active features: %1 + Características activas: %1 + + + Online and connected + En línea y conectado + + + Establishing connection + Estableciendo conexión + + + Computer offline or switched off + El equipo está desconectado o apagado + + + Authentication failed or access denied + Error de autenticación o acceso denegado + + + Disconnected + Desconectado + + + No user logged on + Ningún usuario ha iniciado sesión + + + Logged on user: %1 + Usuario conectado: %1 + + + Location: %1 + Ubicación: %1 + + + Veyon Server unreachable or not running + Veyon Server es inaccesible o no se está ejecutando + + + [no user] + [ningún usuario] + + + + ComputerControlServer + + %1 Service %2 at %3:%4 +  Servicio %1 %2 en %3:%4 + + + Authentication error + Error de autenticación + + + Remote access + Acceso remoto + + + User "%1" at host "%2" is now accessing this computer. + Usuario "%1" en el anfitrión "%2" ahora está accediendo a esta equipo. + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + ¡El usuario "%1" en "%2" intentó acceder a este equipo pero no pudo autenticarse correctamente! + + + Access control error + Error de control de acceso + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + El usuario "%1" en "%2" intentó acceder a este equipo pero se ha bloqueado debido a la configuración de control de acceso. + + + Active connections: + Conexiones activas: + + + + ComputerManager + + User + Usuario + + + Missing network object directory plugin + Falta el plugin para el directorio de objetos de red + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + No se encontró ningún plugin predeterminado para el directorio de objetos de red. Compruebe su instalación o configure un backend diferente para el directorio de objetos de red a través del Configurador de %1. + + + Location detection failed + Falló la detección de ubicación + + + Computer name;Hostname;User + Nombre de equipo;Nombre de host;Usuario + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + No se pudo determinar la ubicación de este equipo. Esto indica un problema con la configuración del sistema. En su lugar, se mostrarán todas las ubicaciones en el panel de selección del equipo. + + + + ComputerSelectPanel + + Computer search + Búsqueda de equipos + + + Add location + Añadir ubicación + + + Save computer/user list + Guardar lista de equipos/usuarios + + + Select output filename + Seleccione nombre de archivo de salida + + + CSV files (*.csv) + Archivos CSV (*.csv) + + + File error + Error de archivo + + + Could not write the computer and users list to %1! Please check the file access permissions. + No se pudo escribir la lista de equipos y usuarios en %1. Por favor, compruebe los permisos de acceso al archivo. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Especifique un archivo de configuración existente para importar. + + + Please specify a valid filename for the configuration export. + Especifique un nombre de archivo válido para la exportación de configuración. + + + Please specify a valid key. + Especifique una clave válida. + + + Specified key does not exist in current configuration! + ¡La clave especificada no existe en la configuración actual! + + + Please specify a valid value. + Especifique un valor válido. + + + Configure Veyon at command line + Configurar Veyon en la línea de comandos + + + Output file is not writable! + ¡El archivo de salida no puede escribirse! + + + Output directory is not writable! + ¡El directorio de salida no puede escribirse! + + + Configuration file is not readable! + ¡El archivo de configuración no puede leerse! + + + Clear system-wide Veyon configuration + Borrar la configuración de Veyon del sistema + + + List all configuration keys and values + Listar todas las claves y valores de la configuración + + + Import configuration from given file + Importar configuración de archivo + + + Export configuration to given file + Exportar la configuración a archivo + + + Read and output configuration value for given key + Leer y mostrar valor de configuración para una clave + + + Write given value to given configuration key + Escribir un valor a una clave de configuración + + + Unset (remove) given configuration key + Desactivar (quitar) la clave de configuración + + + Commands for managing the configuration of Veyon + Comandos para gestionar la configuración de Veyon + + + Upgrade and save configuration of program and plugins + Actualizar y guardar la configuración del programa y los complementos + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + No se pudo modificar la propiedad autoinicio del servicio %1. + + + Could not configure the firewall configuration for the %1 Server. + No se pudo establecer la configuración del cortafuegos para el Servidor %1. + + + Could not configure the firewall configuration for the %1 Worker. + No se pudo establecer la configuración del cortafuegos para el Worker %1. + + + Configuration is not writable. Please check your permissions! + La configuración no es escribible. ¡Por favor revise sus permisos! + + + Could not apply platform-specific configuration settings. + No se pudieron aplicar los ajustes de configuración específicos de la plataforma. + + + + DemoClient + + %1 Demo + Demo %1 + + + + DemoConfigurationPage + + Demo server + Servidor demo + + + Tunables + Ajustes + + + ms + ms + + + Key frame interval + Intervalo entre frames + + + Memory limit + Límite de memoria + + + MB + MB + + + Update interval + Intervalo de actualización + + + s + s + + + Slow down thumbnail updates while demo is running + Ralentizar las actualizaciones de miniaturas mientras se ejecuta la demostración + + + + DemoFeaturePlugin + + Stop demo + Detener demo + + + Window demo + Demo en ventana + + + Give a demonstration by screen broadcasting + Dar una demostración por difusión de pantalla + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + En este modo, la pantalla se mostrará en una ventana de todos los ordenadores. Los usuarios pueden cambiar a otras ventanas según sea necesario. + + + Demo + Demo + + + Share your screen or allow a user to share his screen with other users. + Comparta su pantalla o permita que un usuario comparta su pantalla con otros usuarios. + + + Full screen demo + Demo a pantalla completa + + + Share your own screen in fullscreen mode + Compartir tu propia pantalla en modo de pantalla completa + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + En este modo, su pantalla se muestra en modo de pantalla completa en todas las computadoras mientras los dispositivos de entrada de los usuarios están bloqueados. + + + Share your own screen in a window + Compartir tu propia pantalla en una ventana + + + Share selected user's screen in fullscreen mode + Compartir la pantalla del usuario seleccionado en modo de pantalla completa + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + En este modo, la pantalla del usuario seleccionado se muestra en modo de pantalla completa en todas las computadoras mientras los dispositivos de entrada de los usuarios están bloqueados. + + + Share selected user's screen in a window + Compartir la pantalla del usuario seleccionado en una ventana + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + En este modo, la pantalla del usuario seleccionado se muestra en una ventana en todas las computadoras. Los usuarios pueden cambiar a otras ventanas según sea necesario. + + + Please select a user screen to share. + Seleccione una pantalla de usuario para compartir. + + + Please select only one user screen to share. + Seleccione solo una pantalla de usuario para compartir. + + + All screens + Todas las pantallas + + + Screen %1 [%2] + Pantalla %1 [%2] + + + + DesktopAccessDialog + + Desktop access dialog + Diálogo de acceso al escritorio + + + Confirm desktop access + Confirmar acceso a escritorio + + + Never for this session + Nunca para esta sesión + + + Always for this session + Siempre para esta sesión + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + El usuario %1 del equipo %2 quiere acceder al escritorio. ¿Deseas conceder acceso? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programas y sitios web + + + Predefined programs + Programas predefinidos + + + Name + Nombre + + + Path + Ruta + + + Add new program + Añadir nuevo programa + + + Remove selected program + Eliminar el programa seleccionado + + + Predefined websites + Sitios web predefinidos + + + Remove selected website + Eliminar el sitio web seleccionado + + + URL + URL + + + New program + Nuevo programa + + + New website + Nuevo sitio web + + + + DesktopServicesFeaturePlugin + + Run program + Ejecutar programa + + + Open website + Abrir sitio web + + + Click this button to open a website on all computers. + Haga clic en este botón para abrir un sitio web en todos los equipos. + + + Start programs and services in user desktop + Iniciar programas y servicios en el escritorio del usuario + + + Click this button to run a program on all computers. + Haga clic en este botón para ejecutar un programa en todos los equipos. + + + Run program "%1" + Ejecutar el programa "%1" + + + Custom program + Programa personalizado + + + Open website "%1" + Abrir sitio web "%1" + + + Custom website + Sitio web personalizado + + + + DocumentationFigureCreator + + Teacher + Profesor + + + Room %1 + Aula %1 + + + Please complete all tasks within the next 5 minutes. + Por favor complete todas las tareas en los próximos 5 minutos. + + + Custom website + Sitio web personalizado + + + Open file manager + Abrir gestor de archivos + + + Start learning tool + Iniciar herramienta de aprendizaje + + + Play tutorial video + Reproducir video tutorial + + + Custom program + Programa personalizado + + + Handout + Folleto + + + Texts to read + Textos para leer + + + generic-student-user + usuario-estudiante-genérico + + + + ExternalVncServer + + External VNC server + Servidor VNC externo + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Configuración de servidor VNC externo + + + Port: + Puerto: + + + Password: + Contraseña: + + + + FeatureControl + + Feature control + Control de funciones + + + + FileTransferConfigurationPage + + File transfer + Transferir archivo + + + Directories + Directorios + + + Destination directory + Directorio de destino + + + Default source directory + Directorio de origen predeterminado + + + Options + Opciones + + + Remember last source directory + Recordar el último directorio de origen + + + Create destination directory if it does not exist + Crear directorio de destino si no existe + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + ¡No se pudo abrir el archivo "%1" para su lectura! ¡Por favor compruebe sus permisos! + + + + FileTransferDialog + + File transfer + Transferir archivo + + + Options + Opciones + + + Transfer only + Solo transferencia + + + Transfer and open file(s) with associated program + Transferir y abrir archivo/s con programa asociado + + + Transfer and open destination folder + Transferir y abrir carpeta de destino + + + Files + Archivos + + + Start + Iniciar + + + Overwrite existing files + Sobrescribir archivos existentes + + + + FileTransferPlugin + + File transfer + Transferir archivo + + + Click this button to transfer files from your computer to all computers. + Clic en este botón para transferir archivos desde su equipo a todos los equipos. + + + Select one or more files to transfer + Seleccione uno o más archivos para transferir + + + Transfer files to remote computer + Transferir archivos al equipo remoto + + + Received file "%1". + Archivo recibido "%1". + + + Could not receive file "%1" as it already exists. + No se pudo recibir el archivo "%1" porque ya existe. + + + Could not receive file "%1" as it could not be opened for writing! + ¡No se pudo recibir el archivo "%1" ya que no se pudo abrir para su escritura! + + + + GeneralConfigurationPage + + User interface + Interfaz de usuario + + + Language: + Idioma: + + + Use system language setting + Usar configuración de idioma del sistema + + + Veyon + Veyon + + + Logging + Registro + + + Log file directory + Directorio de registro + + + Log level + Nivel de registro + + + Nothing + Nada + + + Only critical messages + Solo mensajes críticos + + + Errors and critical messages + Errores y mensajes críticos + + + Warnings and errors + Avisos y errores + + + Information, warnings and errors + Información, avisos y errores + + + Debug messages and everything else + Mensajes de depuración y todo lo demás + + + Limit log file size + Límite archivo de registro + + + Clear all log files + Borrar todos los archivos de registro + + + Log to standard error output + Registro a salida de errores estándar + + + Network object directory + Directorio de objetos de red + + + Backend: + Backend: + + + Update interval: + Intervalo de actualización: + + + %1 service + servicio %1 + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + El servicio %1 debe detenerse temporalmente para eliminar los archivos de registro. ¿Continuar? + + + Log files cleared + Archivos de registro limpiados + + + All log files were cleared successfully. + Todos los archivos de registro se limpiaron correctamente. + + + Error + Error + + + Could not remove all log files. + No se pudieron eliminar todos los archivos de registro. + + + MB + MB + + + Rotate log files + Rotar archivos de registro + + + x + x + + + seconds + segundos + + + Write to logging system of operating system + Escribir en el sistema de registro del sistema operativo + + + Authentication + Autenticación + + + Method: + Método: + + + Logon authentication + Autenticación de inicio de sesión + + + Key file authentication + Autenticación mediante archivo de clave + + + Test + Comprobar + + + Authentication is set up properly on this computer. + La autenticación está configurada correctamente en esta computadora. + + + Authentication keys are not set up properly on this computer. + Las claves de autenticación no están configuradas correctamente en esta computadora. + + + Authentication test + Prueba de autenticacion + + + + HeadlessVncServer + + Headless VNC server + Servidor VNC headless + + + + LdapBrowseDialog + + Browse LDAP + Buscar LDAP + + + + LdapClient + + LDAP error description: %1 + Descripción del error LDAP: %1 + + + + LdapConfigurationPage + + Basic settings + Configuración básica + + + General + General + + + LDAP server and port + Servidor y puerto LDAP + + + Bind DN + Enlace DN + + + Bind password + Contraseña de enlace + + + Anonymous bind + Enlace anónimo + + + Use bind credentials + Utilizar credenciales de enlace + + + Base DN + DN base + + + Fixed base DN + DN base fijo + + + e.g. dc=example,dc=org + v.g. dc=example,dc=org + + + Discover base DN by naming context + Descubrir el DN base mediante el contexto de nombres + + + e.g. namingContexts or defaultNamingContext + v.g. namingContexts o defaultNamingContext + + + Environment settings + Configuración del entorno + + + Object trees + Árboles de objetos + + + Computer tree + Árbol de equipos + + + e.g. OU=Groups + v.g. OU=Groups + + + User tree + Árbol de usuarios + + + e.g. OU=Users + v.g. OU=Users + + + e.g. OU=Computers + v.g. OU=Computers + + + Group tree + Árbol de grupos + + + Perform recursive search operations in object trees + Realizar operaciones de búsqueda recursiva en árboles de objetos + + + Object attributes + Atributos de objeto + + + e.g. hwAddress + p. ej. hwAddress + + + e.g. member or memberUid + v.g. member o memberUid + + + e.g. dNSHostName + v.g. dNSHostName + + + Computer MAC address attribute + Atributo de dirección MAC del equipo + + + Group member attribute + Atributo de miembro del grupo + + + e.g. uid or sAMAccountName + v.g. uid o sAMAccountName + + + Advanced settings + Configuración avanzada + + + Optional object filters + Filtros de objeto opcionales + + + Filter for user groups + Filtro para grupos de usuarios + + + Filter for users + Filtro para usuarios + + + Filter for computer groups + Filtro para grupos de computadoras + + + Group member identification + Identificación del miembro del grupo + + + Distinguished name (Samba/AD) + Nombre distinguido (Samba/AD) + + + List all groups of a user + Listar todos los grupos de un usuario + + + List all groups of a computer + Listar todos los grupos de un equipo + + + Get computer object by IP address + Obtener objeto de equipo por dirección IP + + + LDAP connection failed + Conexión LDAP fallida + + + LDAP bind failed + Enlace LDAP fallido + + + LDAP bind successful + Enlace LDAP exitoso + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Se conectó correctamente al servidor LDAP y se realizó un enlace LDAP. La configuración básica de LDAP está configurada correctamente. + + + LDAP base DN test failed + Comprobación fallida de DN base de LDAP + + + LDAP base DN test successful + Comprobación correcta de DN base de LDAP + + + LDAP naming context test failed + Comprobación fallida de contexto de nombres LDAP + + + LDAP naming context test successful + Comprobación correcta de contexto de nombres LDAP + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + Consulta correcta al contexto de nombres LDAP. Se han encontrado los siguientes DN base: +%1 + + + user tree + árbol de usuarios + + + group tree + árbol de grupos + + + computer tree + árbol de equipos + + + Enter username + Introduzca nombre de usuario + + + Please enter a user login name (wildcards allowed) which to query: + Por favor, introduzca un nombre de inicio de sesión (se permiten comodines) a consultar: + + + user objects + objetos de usuario + + + Enter group name + Introduzca nombre del grupo + + + Please enter a group name whose members to query: + Por favor, introduzca un nombre de grupo para consultar sus miembros: + + + group members + miembros del grupo + + + Group not found + Grupo no encontrado + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + No se pudo encontrar un grupo con el nombre "%1". Compruebe el nombre del grupo o el parámetro de árbol de grupo. + + + Enter computer name + Introduzca nombre del equipo + + + computer objects + Objetos de equipo + + + Enter computer DN + Introduzca DN de equipo + + + Please enter the DN of a computer whose MAC address to query: + Por favor, introduzca el DN de un equipo para consultar su dirección MAC: + + + computer MAC addresses + direcciones MAC de equipo + + + users + usuarios + + + user groups + grupos de usuarios + + + computer groups + grupos de equipos + + + Please enter a user login name whose group memberships to query: + Por favor, introduzca nombre de inicio de sesión de usuario para consultar miembros del grupo: + + + groups of user + grupos de usuarios + + + User not found + Usuario no encontrado + + + groups of computer + grupos de equipos + + + Computer not found + Equipo no encontrado + + + Enter computer IP address + Introduzca la dirección IP del equipo + + + Please enter a computer IP address which to resolve to an computer object: + Introduzca una dirección IP de equipo a resolver a un objeto de equipo: + + + computers + equipos + + + LDAP %1 test failed + Comprobación fallida de LDAP %1 + + + LDAP %1 test successful + Comprobación correcta de LDAP %1 + + + The %1 has been queried successfully and %2 entries were found. + El %1 se ha consultado correctamente y se han encontrado %2 entradas. + + + %1 %2 have been queried successfully: + +%3 + %1 %2 ha sido consultado correctamente: + +%3 + + + LDAP filter test failed + Comprobación fallida de filtro LDAP + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + No se pudo consultar ningún %1 con el filtro configurado. Compruebe el filtro LDAP para %1. + +%2 + + + LDAP filter test successful + Comprobación correcta de filtro LDAP + + + %1 %2 have been queried successfully using the configured filter. + %1 %2 se han consultado correctamente con el filtro configurado. + + + (only if different from group tree) + (sólo si es diferente del árbol de grupos) + + + Computer group tree + Árbol de grupo de equipos + + + computer group tree + árbol de grupo de equipos + + + Filter for computers + Filtro para equipos + + + e.g. room or computerLab + v.g. sala o aula + + + Integration tests + Pruebas de integración + + + Computer groups + Grupos de equipos + + + e.g. name or description + v.g. nombre o descripción + + + Filter for computer containers + Filtro para contenedores de ordenadores + + + Computer containers or OUs + Contenedores de computadoras u OUs + + + Connection security + Seguridad de conexión + + + TLS certificate verification + Verificación del certificado TLS + + + System defaults + Valores por defecto del sistema + + + Never (insecure!) + Nunca (¡inseguro!) + + + Custom CA certificate file + Archivo de certificado CA personalizado + + + None + Ninguno + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + ej. (objectClass=computer) + + + e.g. (objectClass=group) + ej. (objectClass=group) + + + e.g. (objectClass=person) + ej. (objectClass=person) + + + e.g. (objectClass=room) or (objectClass=computerLab) + ej. (objectClass=room) o (objectClass=computerLab) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + ej. (objectClass=container) o (objectClass=organizationalUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + No se pudo consultar el DN base configurado. Verifique el parámetro base DN. + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + El DN base de LDAP se ha consultado con éxito. Se encontraron las siguientes entradas: + +%1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + No se pudo consultar el DN base a través de los contextos de nombres. Verifique el parámetro de atributo de contexto de nombres. + +%1 + + + Certificate files (*.pem) + Archivos de certificado (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + No se pudo conectar al servidor LDAP. Por favor, revise los parámetros del servidor. + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + No se pudo vincular al servidor LDAP. Verifique los parámetros del servidor y las credenciales de enlace. + +%1 + + + Encryption protocol + Protocolo de encriptación + + + Computer location attribute + Atributo de ubicación del equipo + + + Computer display name attribute + Atributo de nombre mostrado de equipo + + + Location name attribute + Atributo de nombre de ubicación + + + e.g. cn or displayName + v.g. cn o displayName + + + Computer locations identification + Identificación de ubicaciones de equipos + + + Identify computer locations (e.g. rooms) via: + Identifique las ubicaciones de equipos (p. ej., aulas) a través de: + + + Location attribute in computer objects + Atributo de ubicación en objetos de equipo + + + List all entries of a location + Listar todas las entradas de una ubicación + + + List all locations + Listar todas las ubicaciones + + + Enter computer display name + Introduzca el nombre mostrado del equipo + + + Please enter a computer display name to query: + Por favor, introduzca un nombre mostrado de equipo a consultar: + + + Enter computer location name + Introduzca el nombre de ubicación del equipo + + + Please enter the name of a computer location (wildcards allowed): + Por favor ingrese el nombre de una ubicación de equipo (comodines permitidos): + + + computer locations + ubicaciones de equipos + + + Enter location name + Introduzca el nombre de ubicación + + + Please enter the name of a location whose entries to query: + Por favor introduzca el nombre de ubicación cuyas entradas consultar: + + + location entries + entradas de ubicación + + + LDAP test failed + La prueba LDAP falló + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + No se pudo consultar ningún %1. Verifique el parámetro(s) %2 e ingrese el nombre de un objeto existente. + +%3 + + + and + y + + + LDAP test successful + La prueba LDAP fue exitosa + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + No se pudo consultar ninguna entrada en %1 configurado. Por favor, compruebe el parámetro "%2". + +%3 + + + Browse + Buscar + + + Test + Comprobar + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + Nombres de host almacenados como nombres de dominio completos (FQDN, p. ej. myhost.example.org) + + + Computer hostname attribute + Atributo de nombre de host del equipo + + + Please enter a computer hostname to query: + Por favor introduzca un nombre de host de equipo para consultar: + + + Invalid hostname + Nombre de host no válido + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + Configuró los nombres de host de los equipos para que se almacenaran como nombres de dominio completos (FQDN) pero ingresó un nombre de host sin dominio. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + Configuró los nombres de host de los equipos para ser almacenados como nombres de host simples sin un nombre de dominio, pero ingresó un nombre de host con una parte de nombre de dominio. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + No se pudo encontrar un usuario con el nombre "%1". Por favor, compruebe el nombre de usuario o el parámetro del árbol de usuarios. + + + Enter hostname + Introduzca el nombre de host + + + Please enter a computer hostname whose group memberships to query: + Por favor introduzca un nombre de host de equipo cuyos miembros de grupo desee consultar: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + No se pudo encontrar un equipo con el nombre de host "%1". Por favor, compruebe el nombre de host o el parámetro del árbol de equipos. + + + Hostname lookup failed + Error al buscar el nombre de host + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + No se pudo buscar el nombre de host para la dirección IP %1. Por favor, compruebe la configuración de su servidor DNS. + + + User login name attribute + Atributo de nombre de usuario + + + Configured attribute for user login name or computer hostname (OpenLDAP) + Atributo configurado para el nombre de inicio de sesión del usuario o el nombre de host de la computadora (OpenLDAP) + + + computer containers + contenedores de computadoras + + + + LdapPlugin + + Auto-configure the base DN via naming context + Configuración automática del DN base a través del contexto de nombres + + + Query objects from LDAP directory + Consultar objetos desde el directorio LDAP + + + Show help about command + Mostrar ayuda sobre el comando + + + Commands for configuring and testing LDAP/AD integration + Comandos para configurar y probar la integración LDAP/AD + + + Basic LDAP/AD support for Veyon + Soporte básico LDAP/AD para Veyon + + + %1 (load computers and locations from LDAP/AD) + %1 (cargar equipos y ubicaciones desde LDAP/AD) + + + %1 (load users and groups from LDAP/AD) + %1 (cargar usuarios y grupos desde LDAP/AD) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + Por favor, especifique una url LDAP válida siguiendo el esquema "ldap[s]://[usuario[:contraseña]@]hostname[:puerto]" + + + No naming context attribute name given - falling back to configured value. + No se ha dado un nombre de atributo de contexto de nombres - volvemos al valor configurado. + + + Could not query base DN. Please check your LDAP configuration. + No se pudo consultar el DN base. Por favor, compruebe su configuración LDAP. + + + Configuring %1 as base DN and disabling naming context queries. + Configurando %1 como DN base y deshabilitando las consultas de contexto de nombres. + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + Servicio PAM personalizado para autenticación de usuario + + + User authentication + Autenticacion de usuario + + + Session management + Gestión de sesiones + + + Display manager users + Usuarios del gestor de pantalla + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Plugin implementando funciones abstractas para la plataforma Linux + + + + LocationDialog + + Select location + Seleccionar ubicación + + + enter search filter... + introduzca el filtro de búsqueda ... + + + + MainToolBar + + Configuration + Configuración + + + Disable balloon tooltips + Desactivar ayudas en globos + + + Show icons only + Mostrar solo iconos + + + + MainWindow + + MainWindow + MainWindow + + + toolBar + toolBar + + + General + General + + + &File + &Archivo + + + &Help + A&yuda + + + &Quit + &Salir + + + Ctrl+Q + Ctrl+S + + + Ctrl+S + Ctrl+G + + + L&oad settings from file + &Leer configuración de archivo + + + Ctrl+O + Ctrl+L + + + About Qt + Acerca de Qt + + + Authentication impossible + Autenticación imposible + + + Configuration not writable + La configuración no se puede escribir + + + Load settings from file + Leer configuración de archivo + + + Save settings to file + Guardar configuración en archivo + + + Unsaved settings + Configuración no guardada + + + There are unsaved settings. Quit anyway? + Hay configuraciones no guardadas. ¿Salir de todas maneras? + + + Veyon Configurator + Veyon Configurator + + + Service + Servicio + + + Master + Maestro + + + Access control + Control de acceso + + + About Veyon + Acerca de Veyon + + + Auto + Auto + + + About + Acerca de + + + %1 Configurator %2 + Configurador de %1 %2 + + + JSON files (*.json) + Archivos JSON (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + ¡El backend de configuración local informó que la configuración no es escribible! Ejecute el Configurador de %1 con privilegios más altos. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + No se encontraron archivos de clave de autenticación o los actuales no están actualizados. Por favor, cree nuevos archivos de claves usando el Configurador de %1. También puede configurar la autenticación de inicio de sesión utilizando el Configurador de %1. De lo contrario, no podrá acceder a los equipos que utilizan %1. + + + Access denied + Acceso denegado + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + De acuerdo con la configuración local, no se le permite acceder a los equipos de la red. Inicie sesión con una cuenta diferente o permita que el administrador del sistema compruebe la configuración local. + + + Screenshots + Capturas + + + Feature active + Característica activa + + + The feature "%1" is still active. Please stop it before closing %2. + La característica "%1" está aún activa. Por favor, detengala antes de cerrar %2. + + + Reset configuration + Restablecer configuración + + + Do you really want to reset the local configuration and revert all settings to their defaults? + ¿Realmente desea restablecer la configuración local y revertir todas las configuraciones a sus valores predeterminados? + + + Search users and computers + Buscar usuarios y equipos + + + Align computers to grid + Alinear equipos a la rejilla + + + %1 Configurator + Configurador %1 + + + Insufficient privileges + Privilegios insuficientes + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + No se pudo iniciar con privilegios administrativos. ¡Asegúrese de que esté instalado un programa similar a sudo para su entorno de escritorio! El programa se ejecutará con privilegios de usuario normales. + + + Only show powered on computers + Mostrar sólo los equipos encendidos + + + &Save settings to file + &Guardar configuración a un archivo + + + &View + &Ver + + + &Standard + &Estándar + + + &Advanced + &Avanzado + + + Use custom computer arrangement + Usar disposición de equipos personalizada + + + Locations && computers + Ubicaciones && equipos + + + Slideshow + Diapositivas + + + Spotlight + Destacar + + + Adjust size of computer icons automatically + Ajustar el tamaño de los iconos de la computadora automáticamente + + + + MasterConfigurationPage + + Directories + Directorios + + + User configuration + Configuración de usuario + + + Feature on computer double click: + Función al hacer doble click en equipo: + + + Features + Características + + + All features + Todas las funciones + + + Disabled features + Funciones deshabilitadas + + + Screenshots + Capturas + + + <no feature> + <ninguna característica> + + + Basic settings + Configuración básica + + + Behaviour + Comportamiento + + + Enforce selected mode for client computers + Aplicar el modo seleccionado para los equipos cliente + + + Hide local computer + Ocultar equipo local + + + Hide computer filter field + Ocultar campo de filtro de equipo + + + Actions such as rebooting or powering down computers + Acciones como reiniciar o apagar equipos + + + User interface + Interfaz de usuario + + + Background color + Color de fondo + + + Thumbnail update interval + Intervalo de actualización de miniaturas + + + ms + ms + + + Program start + Iniciar el programa + + + Modes and features + Modos y funciones + + + User and computer name + Nombre del usuario y equipo + + + Only user name + Sólo nombre del usuario + + + Only computer name + Sólo nombre del equipo + + + Computer thumbnail caption + Nombre de miniatura + + + Text color + Color del texto + + + Sort order + Orden de clasificación + + + Computer and user name + Equipo y nombre del usuario + + + Computer locations + Ubicaciones de equipos + + + Show current location only + Mostrar solo la ubicación actual + + + Allow adding hidden locations manually + Permitir agregar ubicaciones ocultas manualmente + + + Hide empty locations + Ocultar ubicaciones vacías + + + Show confirmation dialog for potentially unsafe actions + Mostrar diálogo de confirmación para acciones potencialmente inseguras + + + Perform access control + Realizar control de acceso + + + Automatically select current location + Seleccionar automáticamente la ubicación actual + + + Automatically open computer select panel + Abrir automáticamente el panel de selección de equipo + + + Hide local session + Ocultar sesión local + + + px + px + + + Thumbnail spacing + Espaciado de miniaturas + + + Auto + Auto + + + Thumbnail aspect ratio + Relación de aspecto de la miniatura + + + Automatically adjust computer icon size + Ajustar automáticamente el tamaño del icono de la computadora + + + Open feature windows on the same screen as the main window + Abrir ventanas de funciones en la misma pantalla que la ventana principal + + + + MonitoringMode + + Monitoring + Monitorización + + + Builtin monitoring mode + Modo de monitorización incorporado + + + This mode allows you to monitor all computers at one or more locations. + Este modo le permite monitorear todos los equipos en una o más ubicaciones. + + + + NetworkObjectTreeModel + + Locations/Computers + Ubicaciones/Equipos + + + + OpenWebsiteDialog + + Open website + Abrir sitio web + + + e.g. Veyon + p.ej. Veyon + + + Remember and add to website menu + Recordar y añadir al menú del sitio web + + + e.g. www.veyon.io + p.ej. www.veyon.io + + + Please enter the URL of the website to open: + Por favor, introduzca la URL del sitio web a abrir: + + + Name: + Nombre: + + + + PasswordDialog + + Username + Nombre de usuario + + + Password + Contraseña + + + Veyon Logon + Inicio de sesión de Veyon + + + Authentication error + Error de autenticación + + + Logon failed with given username and password. Please try again! + Error de inicio de sesión con el nombre de usuario y contraseña dados. Por favor, ¡inténtelo de nuevo! + + + Please enter your username and password in order to access computers. + Introduzca su nombre de usuario y contraseña para acceder a los equipos. + + + + PowerControlFeaturePlugin + + Power on + Encender + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Haga clic en este botón para encender todos los equipos. De esta manera usted no tiene que encender cada equipo a mano. + + + Reboot + Reiniciar + + + Click this button to reboot all computers. + Haga clic en este botón para reiniciar todos los equipos. + + + Power down + Apagar + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Haga clic en este botón para apagar todos los equipos. De esta manera usted no tiene que apagar cada equipo a mano. + + + Power on/down or reboot a computer + Encender/apagar o reiniciar un equipo + + + Confirm reboot + Confirmar reinicio + + + Confirm power down + Confirmar apagado + + + Do you really want to reboot the selected computers? + ¿Realmente desea reiniciar los equipos seleccionados? + + + Do you really want to power down the selected computer? + ¿Realmente desea apagar el equipo seleccionado? + + + Power on a computer via Wake-on-LAN (WOL) + Encienda un equipo a través de Wake-on-LAN (WOL) + + + MAC ADDRESS + DIRECCIÓN MAC + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + Este comando transmite un paquete Wake-on-LAN (WOL) a la red para encender el equipo con la dirección MAC proporcionada. + + + Please specify the command to display help for! + Por favor, ¡especifique el comando sobre el cual mostrar ayuda! + + + Invalid MAC address specified! + ¡Dirección MAC especificada inválida! + + + Commands for controlling power status of computers + Comandos para controlar el estado de energía de los equipos + + + Power down now + Apagar ahora + + + Install updates and power down + Instalar actualizaciones y apagar + + + Power down after user confirmation + Apagar después de la confirmación del usuario + + + Power down after timeout + Apagar después de tiempo de espera + + + The computer was remotely requested to power down. Do you want to power down the computer now? + Se solicitó de forma remota apagar el equipo. ¿Quieres apagar el equipo ahora? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + El equipo se apagará en %1 minutos, %2 segundos. + +Por favor guarde su trabajo y cierre todos los programas. + + + + PowerDownTimeInputDialog + + Power down + Apagar + + + Please specify a timeout for powering down the selected computers: + Especifique un tiempo de espera para apagar los equipos seleccionados: + + + minutes + minutos + + + seconds + segundos + + + + RemoteAccessFeaturePlugin + + Remote view + Vista remota + + + Open a remote view for a computer without interaction. + Abrir una vista remota para un equipo sin interacción. + + + Remote control + Control remoto + + + Open a remote control window for a computer. + Abrir una ventana de control remoto para un equipo. + + + Remote access + Acceso remoto + + + Remote view or control a computer + Vista remota o control de un equipo + + + Please enter the hostname or IP address of the computer to access: + Por favor, introduzca el nombre o la dirección IP del equipo a acceder: + + + Show help about command + Mostrar ayuda sobre el comando + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 - %2 Acceso remoto + + + %1 - %2 - %3 Remote Access + Acceso Remoto %1 - %2 - %3 + + + + RemoteAccessWidgetToolBar + + View only + Solo mirar + + + Remote control + Control remoto + + + Send shortcut + Enviar combinación de teclas + + + Fullscreen + Pantalla completa + + + Window + Ventana + + + Ctrl+Alt+Del + Ctrl+Alt+Supr + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Menu + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Conectando %1 + + + Connected. + Conectado. + + + Screenshot + Captura + + + Exit + Salir + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Introduzca los programas o comandos a ejecutar en el equipo/s seleccionado/s. Puede separar varios programas/comandos por línea. + + + Run programs + Ejecutar programas + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + v.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + Nombre: + + + Remember and add to program menu + Recordar y añadir al menú del programa + + + e.g. VLC + p.ej. VLC + + + + ScreenLockFeaturePlugin + + Lock + Bloquear + + + Unlock + Desbloquear + + + Lock screen and input devices of a computer + Bloquear la pantalla y los dispositivos de entrada de un equipo + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Para recuperar toda la atención del usuario puede bloquear sus equipos con este botón. En este modo, todos los dispositivos de entrada están bloqueados y las pantallas son inhabilitadas . + + + Lock input devices + Bloquear dispositivos de entrada + + + Unlock input devices + Desbloquear dispositivos de entrada + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + Para reclamar la atención de todos los usuarios, puede bloquear sus computadoras usando este botón. En este modo, todos los dispositivos de entrada están bloqueados mientras el escritorio aún está visible. + + + + Screenshot + + unknown + desconocido + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + No se pudo tomar una captura de pantalla, ya que el directorio %1 no existe y no se pudo crear. + + + Screenshot + Captura + + + Could not open screenshot file %1 for writing. + No se pudo abrir el archivo de captura de pantalla %1 para escribir. + + + + ScreenshotFeaturePlugin + + Screenshot + Captura + + + Use this function to take a screenshot of selected computers. + Utilice esta función para tomar una captura de pantalla de los equipos seleccionados. + + + Screenshots taken + Capturas de pantalla tomadas + + + Screenshot of %1 computer have been taken successfully. + La captura de pantalla del equipo %1 se ha realizado correctamente. + + + Take screenshots of computers and save them locally. + Tome capturas de pantalla de los equipos y guárdelas localmente. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Todas las capturas de pantalla tomadas se muestran aquí. Puede tomar capturas de pantalla haciendo clic en "Captura" en el menú contextual de un equipo. Las capturas de pantalla se pueden gestionar mediante los botones de abajo. + + + User: + Usuario: + + + Computer: + Equipo: + + + Date: + Fecha: + + + Time: + Hora: + + + Show + Mostrar + + + Delete + Borrar + + + Screenshot + Captura + + + Do you really want to delete all selected screenshots? + ¿Realmente desea eliminar todas las capturas de pantalla seleccionadas? + + + + ServiceConfigurationPage + + General + General + + + Autostart + Inicio automático + + + Hide tray icon + Ocultar icono + + + Start service + Iniciar servicio + + + Stopped + Parado + + + Stop service + Parar servicio + + + State: + Estado: + + + Enable firewall exception + Habilitar excepción en el cortafuegos + + + Allow connections from localhost only + Solo permitir conexiones desde equipo local + + + VNC server + Servidor VNC + + + Plugin: + Plugin: + + + Restart %1 Service + Reiniciar Servicio %1 + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Todos las configuraciones se guardaron correctamente. Para tener efecto es necesario reiniciar el servicio %1. ¿Reiniciar ahora? + + + Running + Funcionando + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + Al habilitar esta opción, el servicio iniciará un proceso de servidor para cada sesión interactiva en un equipo. +Por lo general, esto es necesario para admitir servidores de terminales. + + + Show notification on remote connection + Mostrar notificación en conexión remota + + + Show notification when an unauthorized access is blocked + Mostrar notificación cuando un acceso no autorizado está bloqueado + + + Sessions + Sesiones + + + Single session mode (create server instance for local/physical session only) + Modo de sesión única (crear una instancia de servidor para sesión local/física únicamente) + + + Multi session mode (create server instance for each local and remote desktop session) + Modo de sesión múltiple (crear una instancia de servidor para cada sesión de escritorio local y remota) + + + Maximum session count + Número máximo de sesiones + + + Network port numbers + Números de puerto de red + + + Veyon server + Servidor Veyon + + + Internal VNC server + Servidor VNC interno + + + Feature manager + Gerente de funciones + + + Demo server + Servidor demo + + + Miscellaneous network settings + Configuraciones de red diversas + + + + ServiceControl + + Starting service %1 + Iniciando servicio %1 + + + Stopping service %1 + Deteniendo servicio %1 + + + Registering service %1 + Registrando servicio %1 + + + Unregistering service %1 + Eliminando registro de servicio %1 + + + Service control + Control de servicio + + + + ServiceControlPlugin + + Service is running + El servicio esta ejecutándose + + + Service is not running + El servicio no está ejecutándose + + + Configure and control Veyon service + Configurar y controlar el Servicio de Veyon + + + Register Veyon Service + Registrar Servicio Veyon + + + Unregister Veyon Service + Anular el registro del Servicio Veyon + + + Start Veyon Service + Iniciar Servicio Veyon + + + Stop Veyon Service + Detener Servicio Veyon + + + Restart Veyon Service + Reiniciar Servicio Veyon + + + Query status of Veyon Service + Consultar estado de Servicio Veyon + + + Commands for configuring and controlling Veyon Service + Comandos para configurar y controlar el Servicio Veyon + + + + ShellCommandLinePlugin + + Run command file + Ejecutar archivo de comandos + + + File "%1" does not exist! + ¡El archivo "%1" no existe! + + + Interactive shell and script execution for Veyon Control + Ejecución interactiva de shell y script para el Control de Veyon + + + Commands for shell functionalities + Comandos para funcionalidades de shell + + + + SlideshowPanel + + Previous + Previo + + + Start/pause + Inicio/pausa + + + Next + Siguiente + + + Duration: + Duración: + + + + SpotlightPanel + + Add selected computers + Añadir las computadoras seleccionadas + + + Remove selected computers + Eliminar las computadoras seleccionadas + + + Update computers in realtime + Actualizar computadoras en tiempo real + + + Spotlight + Destacar + + + Please select at least one computer to add. + Seleccione al menos una computadora para añadir. + + + Please select at least one computer to remove. + Seleccione al menos una computadora para eliminar. + + + Add computers by clicking with the middle mouse button or clicking the first button below. + Añada computadoras haciendo clic con el botón central del ratón o haciendo clic en el primer botón a continuación. + + + + SystemTrayIcon + + System tray icon + Icono de bandeja de sistema + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + Backend de grupos de usuarios para grupos de usuarios del sistema + + + Default (system user groups) + Predeterminado (grupos de usuarios del sistema) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + Comprobar componentes y funciones internas de Veyon + + + Commands for testing internal components and functions of Veyon + Comandos para comprobar componentes internos y funciones de Veyon + + + + TextMessageDialog + + Send text message + Enviar mensaje de texto + + + Use the field below to type your message which will be sent to all selected users. + Use el campo de más abajo para escribir su mensaje que será enviado a todos los usuarios seleccionados. + + + + TextMessageFeaturePlugin + + Text message + Mensaje de texto + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Utilice esta función para enviar un mensaje de texto a todos los usuarios, v.g., para asignarles nuevas tareas. + + + Message from teacher + Mensaje del profesor + + + Send a message to a user + Enviar un mensaje a un usuario + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Habilitar captura de ventanas apiladas (semitransparentes) + + + Poll full screen (leave this enabled per default) + Sondeo de pantalla completa (dejar habilitado por defecto) + + + Low accuracy (turbo mode) + Baja precisión (modo turbo) + + + Builtin UltraVNC server configuration + Configuración de servidor UltraVNC incorporado + + + Enable multi monitor support + Habilitar soporte multi-monitor + + + Enable Desktop Duplication Engine on Windows 8 and newer + Habilitar duplicación de escritorio en Windows 8 y versiones posteriores + + + Maximum CPU usage + Uso máximo de CPU + + + + UserConfig + + No write access + No hay acceso de escritura + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + No se pudo guardar su configuración personal. Compruebe la ruta del archivo de configuración del usuario utilizando el Configurador %1. + + + + UserLoginDialog + + User login + Inicio de sesión de usuario + + + Please enter a username and password for automatic login on all computers. + Introduzca un nombre de usuario y contraseña para iniciar sesión automáticamente en todas las computadoras. + + + Username + Nombre de usuario + + + Password + Contraseña + + + + UserSessionControlPlugin + + Log in + Iniciar sesión + + + Click this button to log in a specific user on all computers. + Haga clic en este botón para iniciar sesión en un usuario específico en todas las computadoras. + + + Log off + Desconectar + + + Click this button to log off users from all computers. + Haga clic en este botón para desconectar los usuarios de todas las computadoras. + + + Confirm user logoff + Confirmar desconectar usuario + + + Do you really want to log off the selected users? + ¿Realmente quieres desconectar a los usuarios seleccionados? + + + User session control + Control de sesión de usuario + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [FALLO] + + + Invalid command! + ¡Comando inválido! + + + Available commands: + Comandos disponibles: + + + Invalid arguments given + Argumentos inválidos + + + Not enough arguments given - use "%1 help" for more information + No hay suficientes argumentos - utilice "%1 help" para obtener más información + + + Unknown result! + ¡Resultado desconocido! + + + Available modules: + Módulos disponibles: + + + No module specified or module not found - available modules are: + No se especificó ningún módulo o no se encontró el módulo; los módulos disponibles son: + + + Plugin not licensed + Complemento no licenciado + + + INFO + INFO + + + ERROR + ERROR + + + USAGE + USO + + + DESCRIPTION + DESCRIPCIÓN + + + EXAMPLES + EJEMPLOS + + + WARNING + ADVERTENCIA + + + + VeyonServiceControl + + Veyon Service + Veyon Service + + + + VncViewWidget + + Establishing connection to %1 ... + Estableciendo conexión con %1 ... + + + + WebApiConfigurationPage + + Web API + API web + + + General + General + + + Network port + Puerto de red + + + Enable WebAPI server + Habilitar servidor WebAPI + + + Connection settings + Configuraciones de conexión + + + Lifetime + Tiempo de vida + + + h + h + + + s + s + + + Idle timeout + Tiempo de inactividad + + + Authentication timeout + Tiempo de espera de autenticación + + + Maximum number of open connections + Número máximo de conexiones abiertas + + + Connection encryption + Cifrado de conexión + + + TLS certificate file + Archivo de certificado TLS + + + TLS private key file + Archivo de clave privada TLS + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + Usar HTTPS con TLS 1.3 en lugar de HTTP + + + + WebApiPlugin + + Run WebAPI server + Ejecutar servidor WebAPI + + + Failed to start WebAPI server at port %1 + No se pudo iniciar el servidor WebAPI en el puerto %1 + + + WebAPI server running at port %1 + Servidor WebAPI ejecutándose en el puerto %1 + + + Provide access to a computer via HTTP + Proporcionar acceso a una computadora a través de HTTP + + + Commands for running the WebAPI server + Comandos para ejecutar el servidor WebAPI + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + No se pudo cambiar la configuración para la generación de SAS por software. ¡El envío de Ctrl+Alt+Del a través del control remoto no funcionará! + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + General + + + Enable SAS generation by software (Ctrl+Alt+Del) + Habilitar la generación de SAS por software (Ctrl+Alt+Supr) + + + Screen lock + Bloqueo de pantalla + + + Hide taskbar + Ocultar barra de tareas + + + Hide start menu + Ocultar menú de inicio + + + Hide desktop + Ocultar escritorio + + + User authentication + Autenticacion de usuario + + + Use alternative user authentication mechanism + Utilizar mecanismo de autenticación de usuario alternativo + + + User login + Inicio de sesión de usuario + + + Input start delay + Retardo de inicio de entrada + + + Simulated key presses interval + Intervalo de pulsaciones de teclas simuladas + + + Confirm legal notice (message displayed before user logs in) + Confirmar aviso legal (mensaje que se muestra antes de que el usuario inicie sesión) + + + Use input device interception driver + Utilice el controlador de interceptación del dispositivo de entrada + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Plugin implementando funciones abstractas para la plataforma Windows + + + + WindowsServiceControl + + The service "%1" is already installed. + El servicio "%1" ya está instalado. + + + The service "%1" could not be installed. + No se pudo instalar el servicio "%1". + + + The service "%1" has been installed successfully. + El servicio "%1" se ha instalado correctamente. + + + The service "%1" could not be uninstalled. + El servicio "%1" no se pudo desinstalar. + + + The service "%1" has been uninstalled successfully. + El servicio "%1" se ha desinstalado correctamente. + + + The start type of service "%1" could not be changed. + El tipo de inicio de servicio "%1" no se pudo cambiar. + + + Service "%1" could not be found. + No se pudo encontrar el servicio "%1". + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Configuración de servidor x11vnc incorporado + + + Custom x11vnc parameters: + Parámetros personalizados de x11vnc: + + + Do not use X Damage extension + No usar extensión X Damage + + + diff --git a/translations/veyon_et.ts b/translations/veyon_et.ts new file mode 100644 index 0000000..b58022e --- /dev/null +++ b/translations/veyon_et.ts @@ -0,0 +1,4083 @@ + + + + + AboutDialog + + About + Programmist + + + Translation + Tõlkimine + + + License + Litsents + + + About Veyon + Programmist Veyon + + + Contributors + Kaastöötajad + + + Version: + Versioon: + + + Website: + Koduleht: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Praegune keel pole veel tõlgitud (või emakeel). + +Kui olete huvitatud Veyoni tõlkimisest oma kohalikku või mõnda muusse keelde või soovite olemasolevat tõlget paremaks muuta, võtke ühendust Veyoni arendajaga! + + + About %1 %2 + Programmist %1 %2 + + + Support Veyon project with a donation + Toetage Veyon projekti annetusega + + + + AccessControlPage + + Computer access control + Arvuti juurdepääsu juhtimine + + + Grant access to every authenticated user (default) + Andke juurdepääs igale autentitud kasutajale (vaikimisi) + + + Test + Test + + + Process access control rules + Protsessi juurdepääsu juhtimise reeglid + + + User groups authorized for computer access + Kasutajarühmad, kellel on juurdepääs arvutile + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Palun lisage rühmad, mille liikmetel peaks olema õigus pääseda juurde teie Veyoni võrgu arvutitele. + + + Authorized user groups + Volitatud kasutajagrupid + + + All groups + Kõik grupid + + + Access control rules + Juurdepääsu reeglid + + + Add access control rule + Lisa juurdepääsu reegel + + + Remove access control rule + Eemalda juurdepääsu reegel + + + Move selected rule down + Teisalda valitud reegel alla + + + Move selected rule up + Teisalda valitud reegel üles + + + Edit selected rule + Muuda valitud reeglit + + + Enter username + Sisestage kasutajanimi + + + Please enter a user login name whose access permissions to test: + Sisestage kasutaja sisselogimisnimi, mille juurdepääsuluba testida: + + + Access allowed + Juurdepääs lubatud + + + The specified user is allowed to access computers with this configuration. + Valitud kasutajal on juurdepääs selle konfiguratsiooniga arvutitele. + + + Access denied + Juurdepääs keelatud + + + The specified user is not allowed to access computers with this configuration. + Määratud kasutajal pole lubatud selle konfiguratsiooniga arvutitele juurde pääseda. + + + Enable usage of domain groups + Domeeni gruppide juurdepääsu lubamine + + + User groups backend: + Kasutajarühmade taustaprogramm: + + + Missing user groups backend + Puuduvad kasutajagruppide taustaprogrammid + + + No default user groups plugin was found. Please check your installation! + Kasutajate rühmade vaikepistikprogrammi ei leitud. Palun kontrollige oma installimist! + + + Restrict access to members of specific user groups + Määratud gruppide liikmete juurdepääsu keelamine + + + + AccessControlRuleEditDialog + + Edit access control rule + Redigeeri juurdepääsu reeglit + + + General + Üldine + + + enter a short name for the rule here + sisestage siia reegli lühinimi + + + Rule name: + Reegli nimi: + + + enter a description for the rule here + sisestage siia reegli kirjeldus + + + Rule description: + Reegli kirjeldus: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Pöörake kõik tingimused ümber ("on/on" tõlgendatud kui "on/ei ole") + + + Conditions + Tingimused + + + is member of group + on rühma liige + + + Accessing computer is localhost + Arvutile juurdepääs on kohalik host + + + Accessing user is logged on user + Juurdepääs kasutajale on sisse logitud kasutaja + + + Accessing user is already connected + Juurdepääs kasutajale on juba ühendatud + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Kui aktiveeritakse rohkem kui üks tingimus, peavad reegli rakendamiseks kõik tingimused vastama (loogiline JA). Kui peab vastama ainult üks mitmest tingimusest (loogiline VÕI), siis looge mitu juurdepääsu kontrollreeglit. + + + Action + Tegevus + + + Allow access + Luba juurdepääs + + + Deny access + Keela juurdepääs + + + Ask logged on user for permission + Küsige sisselogitud kasutajalt luba + + + None (rule disabled) + Puudub (reegel on keelatud) + + + Accessing user + Kasutajale juurdepääs + + + Accessing computer + Arvutile juurdepääs + + + Local (logged on) user + Kohalik (sisse logitud) kasutaja + + + Local computer + Kohalik arvuti + + + Always process rule and ignore conditions + Kasutage reeglit alati ja eirake tingimusi + + + No user logged on + Ükski kasutaja pole sisse loginud + + + Accessing user has one or more groups in common with local (logged on) user + Kasutajal, kellel on juurdepääs, on üks või mitu kohaliku (sisse logitud) kasutajaga ühist rühma + + + Accessing computer and local computer are at the same location + Juurdepääs arvutile ja kohalikule arvutile asuvad samas asukohas + + + is located at + on asukohas + + + + AccessControlRulesTestDialog + + Access control rules test + Juurdepääsu reeglite test + + + Accessing user: + Kasutajale juurdepääs: + + + Local computer: + Kohalik arvuti: + + + Accessing computer: + Arvutile juurdepääs: + + + Please enter the following user and computer information in order to test the configured ruleset. + Konfigureeritud reeglistiku testimiseks sisestage järgmine kasutaja ja arvuti teave. + + + Local user: + Kohalik kasutaja: + + + Connected users: + ühenduses kasutajad + + + The access in the given scenario is allowed. + Antud stsenaariumi korral on juurdepääs lubatud. + + + The access in the given scenario is denied. + Antud stsenaariumi korral on juurdepääs keelatud. + + + The access in the given scenario needs permission of the logged on user. + Antud stsenaariumi korral vajab juurdepääs sisseloginud kasutaja luba. + + + ERROR: Unknown action + Viga: Tundmatu tegevus + + + Test result + Testi tulemus + + + + AuthKeysConfigurationPage + + Authentication keys + Autentimisvõtmed + + + Introduction + Juhend + + + Key file directories + Võtmefailide kataloogid + + + Public key file base directory + Avaliku võtme failibaasi kataloog + + + Private key file base directory + Privaatvõtme failibaasi kataloog + + + Available authentication keys + Saadaval olevad autentimisvõtmed + + + Create key pair + Loo võtmepaar + + + Delete key + Kustuta võti + + + Import key + Impordi võti + + + Export key + Ekspordi võti + + + Set access group + Määra juurdepääsugrupp + + + Key files (*.pem) + Võtmefailid (*.pem) + + + Authentication key name + Autentimisvõtme nimi + + + Please enter the name of the user group or role for which to create an authentication key pair: + Sisestage kasutajagrupi või rolli nimi, mille jaoks autentimisvõtmepaari luua: + + + Do you really want to delete authentication key "%1/%2"? + Kas soovite tõesti autentimisvõtme "%1/%2" kustutada? + + + Please select a key to delete! + Valige kustutamiseks võti! + + + Please enter the name of the user group or role for which to import the authentication key: + Sisestage kasutajagrupi või rolli nimi, mille jaoks autentimisvõtit importida: + + + Please select a key to export! + Valige eksportimiseks võti! + + + Please select a user group which to grant access to key "%1": + Valige võtmerühmale "%1" juurdepääsuks kasutajagrupp: + + + Please select a key which to set the access group for! + Valige võti, millele juurdepääsugrupp määrata! + + + Please perform the following steps to set up key file authentication: + Võtmefaili autentimise seadistamiseks toimige järgmiselt: + + + 1) Create a key pair on the master computer. + 1) Looge Masterarvutis võtmepaar. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Määrake juurdepääsugrupp, mille liikmetel peaks olema juurdepääs teistele arvutitele. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Eksportige avalik võti ja importige see kõigisse samanimelistesse klientarvutitesse. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Kasutage <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon administraatori käsiraamatut</a> lisateabe saamiseks. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Autentimisvõtmete paar koosneb kahest ühendatud krüptovõtmest, privaatsest ja avalikust võtmest. +Privaatvõti võimaldab peaarvuti kasutajatel pääseda juurde klientarvutitele. +On oluline, et ainult volitatud kasutajatel oleks juurdepääs privaatvõtme failile. +Avalikku võtit kasutatakse klientarvutites sissetuleva ühenduse päringu autentimiseks. + + + + AuthKeysManager + + Please check your permissions. + Kontrollige oma õigusi. + + + Key name contains invalid characters! + Võtme nimi sisaldab valesid tähemärke! + + + Invalid key type specified! Please specify "%1" or "%2". + Määratud on vale võtmetüüp! Palun määrake "%1" või "%2". + + + Specified key does not exist! Please use the "list" command to list all installed keys. + Määratud võtit pole olemas! Kõigi installitud võtmete loetlemiseks kasutage käsku "nimekiri". + + + One or more key files already exist! Please delete them using the "delete" command. + Üks või mitu võtmefaili on juba olemas! Kustutage need käsuga "kustuta". + + + Creating new key pair for "%1" + "%1" jaoks uue võtmepaari loomine + + + Failed to create public or private key! + Avaliku või privaatse võtme loomine ebaõnnestus! + + + Newly created key pair has been saved to "%1" and "%2". + Äsja loodud võtmepaar on salvestatud kaustadesse "%1" ja "%2". + + + Could not remove key file "%1"! + Võtmefaili "%1" ei saanud eemaldada! + + + Could not remove key file directory "%1"! + Võtmefailide kataloogi "%1" ei saanud eemaldada! + + + Failed to create directory for output file. + Väljundfaili kataloogi loomine nurjus. + + + File "%1" already exists. + Fail "%1" on juba olemas + + + Failed to write output file. + Faili kirjutamine ebaõnnestus + + + Key "%1/%2" has been exported to "%3" successfully. + Võti "%1/%2" on edukalt eksporditud kausta "%3". + + + Failed read input file. + Sisendfaili lugemine ei õnnestu. + + + File "%1" does not contain a valid private key! + Fail "%1" ei sisalda kehtivat privaatvõtit! + + + File "%1" does not contain a valid public key! + Fail "%1" ei sisalda kehtivat avalikku võtit! + + + Failed to create directory for key file. + Võtmefaili kataloogi loomine nurjus. + + + Failed to write key file "%1". + Võtmefaili "%1" kirjutamine nurjus. + + + Failed to set permissions for key file "%1"! + Võtmefaili "%1" õiguste seadistamine nurjus! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + Võtme "%1/%2" importimine õnnestus. Volitamata juurdepääsu vältimiseks kontrollige faili "%3" õigusi. + + + Failed to convert private key to public key + Privaatvõtme avalikuks võtmeks teisendamine nurjus + + + Failed to create directory for private key file "%1". + Kataloogi loomine privaatvõtme failile "%1" nurjus. + + + Failed to save private key in file "%1"! + Privaatvõtme salvestamine faili "%1" nurjus! + + + Failed to set permissions for private key file "%1"! + Privaatvõtme faili "%1" õiguste seadistamine nurjus! + + + Failed to create directory for public key file "%1". + Avaliku võtme faili "%1" kataloogi loomine nurjus. + + + Failed to save public key in file "%1"! + Faili "%1" avaliku võtme salvestamine ebaõnnestus! + + + Failed to set permissions for public key file "%1"! + Avaliku võtme faili "%1" lubade seadistamine nurjus! + + + Failed to set owner of key file "%1" to "%2". + Võtmefaili "%1" omaniku määramine väärtuseks "%2" nurjus. + + + Failed to set permissions for key file "%1". + Võtmefaili "%1" õiguste seadistamine nurjus. + + + Key "%1" is now accessible by user group "%2". + Võtmele "%1" pääseb nüüd kasutajarühm "%2". + + + <N/A> + <N/A> + + + Failed to read key file. + Võtmefaili lugemine nurjus. + + + + AuthKeysPlugin + + Create new authentication key pair + Looge uus autentimisvõtmepaar + + + Delete authentication key + Kustutage autentimisvõti + + + List authentication keys + Loendage autentimisvõtmed + + + Import public or private key + Importige avalik või privaatne võti + + + Export public or private key + Eksportige avalik või privaatne võti + + + Extract public key from existing private key + Eemaldage avalik võti olemasolevast privaatvõtmest + + + Set user group allowed to access a key + Määra kasutajagrupp, kellel on juurdepääs võtmele + + + KEY + VÕTI + + + ACCESS GROUP + JUURDEPÄÄSU GRUPP + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + See käsk reguleerib failile juurdepääsuõigused <KEY> selliseks, et ainult kasutajagrupil <ACCESS GROUP> on sellele lugemisõigus. + + + NAME + NIMI + + + FILE + FAIL + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + See käsk ekspordib autentimisvõtme <KEY> võtmeks <FILE>. Kui <FILE> pole täpsustatud, koostatakse nimi nime ja tüübi <KEY> järgi. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + See käsk impordib autentimisvõtme <KEY> kohast <FILE>. Kui <FILE>pole täpsustatud, koostatakse nimi nime ja tüübi <KEY> järgi. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + See käsk loetleb kõik saadaolevad autentimisvõtmed konfigureeritud võtmete kataloogis. Kui määratakse suvand "%1", kuvatakse selle asemel võtme üksikasjadega tabel. Mõni detail võib puududa, kui võtmele pole juurdepääsu, nt. lugemisõiguste puudumise tõttu. + + + Please specify the command to display help for! + Palun määrake käsk, mille jaoks abi kuvatakse! + + + TYPE + TÜÜP + + + PAIR ID + PAARI ID + + + Command line support for managing authentication keys + Käsurea tugi autentimisvõtmete haldamiseks + + + Commands for managing authentication keys + Käsud autentimisvõtmete haldamiseks + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + See käsk loob uue autentimisvõtmepaari nimega <NAME> ja salvestab privaatse ja avaliku võtme konfigureeritud võtmete kataloogidesse. Parameeter peab olema võtme nimi, mis võib sisaldada ainult tähti. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + See käsk kustutab autentimisvõtme <KEY> konfigureeritud võtmete kataloogist. Pange tähele, et võtit ei saa pärast selle kustutamist taastada. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + See käsk eraldab avaliku võtme osa privaatvõtmest <KEY> ja salvestab selle vastava avaliku võtmena. Teise põhiarvuti seadistamisel piisab seetõttu ainult privaatvõtme edastamisest. Seejärel saab avaliku võtme välja tõmmata. + + + + AuthKeysTableModel + + Name + Nimi + + + Type + Tüüp + + + Access group + Juurdepääsugrupp + + + Pair ID + Paari ID + + + + BuiltinDirectoryConfigurationPage + + Computers + Arvutid + + + Name + Nimi + + + Host address/IP + Seadme aadress/IP + + + MAC address + MAC aadress + + + Add new computer + Lisa arvuti + + + Remove selected computer + Eemalda valitud arvutid + + + New computer + Uus arvuti + + + Builtin directory + Sisseehitatud kataloog + + + Locations & computers + Asukohad&arvutid + + + Locations + Asukohad + + + Add new location + Uus asukoht + + + Remove selected location + Eemalda valitud asukohad + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + CSV-failide importimine on võimalik käsurea liidese kaudu. Lisateavet leiate <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">veebidokumentatsioonist</a>. + + + New location + Uus asukoht + + + + BuiltinDirectoryPlugin + + Show help for specific command + Näita valitud käsu abi + + + Import objects from given file + Objektide importimine antud failist + + + Export objects to given file + Objektide eksportimine antud faili + + + Invalid type specified. Valid values are "%1" or "%2". + Määratud kehtetu tüüp. Kehtivad väärtused on "%1" või "%2". + + + Type + Tüüp + + + Name + Nimi + + + Host address + Seadme aadress + + + MAC address + MAC aadress + + + Specified object not found. + Määratud objekti ei leia. + + + File "%1" does not exist! + Faili "%1" ei ole! + + + Can't open file "%1" for reading! + Ei saa avada faili "%1" lugemiseks! + + + Unknown argument "%1". + Tundmatu argument "%1". + + + Computer "%1" (host address: "%2" MAC address: "%3") + Arvuti "%1" (IP aadress: "%2" MAC aadress: "%3") + + + Unclassified object "%1" with ID "%2" + Klassifitseerimata objekt "%1" ID-ga "%2" + + + None + Puudub + + + Computer + Arvuti + + + Root + Juur + + + Invalid + Kehtetu + + + Error while parsing line %1. + Viga rea %1 lahendamisel. + + + Network object directory which stores objects in local configuration + Võrguobjektide kataloog, mis salvestab objekte kohalikus konfiguratsioonis + + + Commands for managing the builtin network object directory + Käsud sisseehitatud võrguobjektide kataloogi haldamiseks + + + No format string or regular expression specified! + Vormingustringi ega regulaaravaldist pole määratud! + + + Can't open file "%1" for writing! + Faili "%1" ei saa kirjutamiseks avada! + + + No format string specified! + Vormingustringi pole määratud! + + + Object UUID + Objekti UUID + + + Parent UUID + Vanema UUID + + + Add a location or computer + Lisage asukoht või arvuti + + + Clear all locations and computers + Kustutage kõik asukohad ja arvutid + + + Dump all or individual locations and computers + Kõigi või üksikute asukohtade ja arvutite ladustamine + + + List all locations and computers + Vaata kõiki asukohti ja arvuteid + + + Remove a location or computer + Eemaldage asukoht või arvuti + + + Location "%1" + Asukoht "%1" + + + Builtin (computers and locations in local configuration) + Sissemised (arvutid ja asukohad kohalikus konfiguratsioonis) + + + Location + Asukoht + + + FILE + FAIL + + + LOCATION + ASUKOHT + + + FORMAT-STRING-WITH-PLACEHOLDERS + VORMING-STRING-KOHALIKEGA + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + REGULAARAVALDISE-KOHATÄIDE + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Impordib objekte määratud tekstifailist, kasutades antud vormingu stringi või regulaaravaldist, mis sisaldab ühte või mitut kohahoidjat. Kehtivad kohatäited on: %1 + + + Import simple CSV file to a single room + Importige lihtne CSV-fail ühte ruumi + + + Import CSV file with location name in first column + Importige CSV-fail, mille esimeses veerus on asukoha nimi + + + Import text file with with key/value pairs using regular expressions + Tekstifaili import koos võtme/väärtuse paaridega, kasutades regulaaravaldisi + + + Import arbitrarily formatted data + Importige suvaliselt vormindatud andmeid + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Ekspordib objektid määratud tekstifaili, kasutades antud vormingustringi, mis sisaldab ühte või mitut kohahoidjat. Kehtivad kohatäited on: %1 + + + Export all objects to a CSV file + Eksportige kõik objektid CSV-faili + + + Export all computers in a specific location to a CSV file + Eksportige kõik kindlas asukohas olevad arvutid CSV-faili + + + TYPE + TÜÜP + + + NAME + NIMI + + + PARENT + VANEM + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + Lisab objekti, kus %1 võib olla üks "%2" või "%3". %4 saab määrata nime või UUID-i järgi. + + + Add a room + Lisa ruum + + + Add a computer to room %1 + Lisa arvuti ruumi %1 + + + OBJECT + OBJEKT + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Eemaldab määratud objekti kataloogist. %1 saab määrata nime või UUID-i järgi. Asukoha eemaldamine eemaldab ka kõik seotud arvutid. + + + Remove a computer by name + Eemaldage arvuti nime järgi + + + Remove an object by UUID + Eemaldage objekt UUID järgi + + + "Room 01" + "Ruum 01" + + + "Computer 01" + "Arvuti 01" + + + HOST ADDRESS + IP AADRESS + + + MAC ADDRESS + MAC AADRESS + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Sisemine VNC server (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Sisemine VNC server (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Host/IP aadress: %1 + + + Active features: %1 + Aktiivsed funktsioonid: %1 + + + Online and connected + Veebis ja ühendatud + + + Establishing connection + Ühenduse loomine + + + Computer offline or switched off + Arvuti on võrguühenduseta või välja lülitatud + + + Authentication failed or access denied + Autentimine nurjus või juurdepääs keelati + + + Disconnected + Ühendus katkestatud + + + No user logged on + Ükski kasutaja pole sisse loginud + + + Logged on user: %1 + Sisseloginud kasutaja: %1 + + + Location: %1 + Asukoht: %1 + + + Veyon Server unreachable or not running + Veyon Server pole kättesaadav või ei tööta + + + [no user] + [ükski kasutaja] + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 Teenus %2 kohas %3:%4 + + + Authentication error + Autentimise viga + + + Remote access + Kaugjuurdepääs + + + User "%1" at host "%2" is now accessing this computer. + Kasutaja "%1" hostis "%2" pääseb nüüd sellele arvutile ligi. + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + Kasutaja "%1" hostis "%2" üritas sellele arvutile juurde pääseda, kuid ei suutnud edukalt autentida. + + + Access control error + Juurdepääsu viga + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + Kasutaja "%1" hostis "%2" üritas sellele arvutile juurde pääseda, kuid on juurdepääsu seadete tõttu blokeeritud. + + + Active connections: + Aktiivsed ühendused: + + + + ComputerManager + + User + Kasutaja + + + Missing network object directory plugin + Võrguobjektide kataloogi pistikprogramm puudub + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Vaikevõrguobjektide kataloogi pistikprogrammi ei leitud. Kontrollige installimist või konfigureerige %1 konfiguraatori kaudu mõni muu võrguobjektide kataloogi taustaprogramm. + + + Location detection failed + Asukoha tuvastamine nurjus + + + Computer name;Hostname;User + Arvuti nimi;Hostinimi;Kasutaja + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + Selle arvuti asukohta ei õnnestunud kindlaks teha. See viitab probleemile süsteemi konfiguratsioonis. Kõik asukohad kuvatakse selle asemel arvuti valimise paneelil. + + + + ComputerSelectPanel + + Computer search + Arvuti otsing + + + Add location + Lisa asukoht + + + Save computer/user list + Salvesta arvuti/kasutajate loend + + + Select output filename + Vali väljundfaili nimi + + + CSV files (*.csv) + CSV failid (*.csv) + + + File error + Vigane fail + + + Could not write the computer and users list to %1! Please check the file access permissions. + Arvutit ja kasutajate loendi %1 kirjutamine nurjus! Kontrollige failidele juurdepääsu õigusi. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Palun määrake imporditav olemasolev konfiguratsioonifail. + + + Please specify a valid filename for the configuration export. + Palun määrake konfiguratsiooni eksportimiseks kehtiv failinimi. + + + Please specify a valid key. + Palun määrake kehtiv võti. + + + Specified key does not exist in current configuration! + Määratud võtit pole praeguses konfiguratsioonis olemas! + + + Please specify a valid value. + Palun määrake kehtiv väärtus. + + + Configure Veyon at command line + Seadistage Veyon käsureal + + + Output file is not writable! + Väljundfaili ei saa kirjutada! + + + Output directory is not writable! + Väljundkausta ei saa kirjutada! + + + Configuration file is not readable! + Konfiguratsioonifail pole loetav! + + + Clear system-wide Veyon configuration + Tühi kogu süsteemi hõlmav Veyoni konfiguratsioon + + + List all configuration keys and values + Loetlege kõik konfiguratsioonivõtmed ja väärtused + + + Import configuration from given file + Impordi konfiguratsioon antud failist + + + Export configuration to given file + Ekspordi konfiguratsioon antud faili + + + Read and output configuration value for given key + Loe ja väljasta antud võtme konfiguratsiooni väärtus + + + Write given value to given configuration key + Kirjutage etteantud väärtus antud konfiguratsioonivõtmele + + + Unset (remove) given configuration key + Tühjendage (eemaldage) antud konfiguratsioonivõti + + + Commands for managing the configuration of Veyon + Käsud Veyoni konfiguratsiooni haldamiseks + + + Upgrade and save configuration of program and plugins + Uuendage ja salvestage programmi ja pistikprogrammide konfiguratsioon + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + %1 teenuse automaatse käivitamise atribuuti ei saanud muuta. + + + Could not configure the firewall configuration for the %1 Server. + %1 serveri tulemüüri konfiguratsiooni ei õnnestunud seadistada. + + + Could not configure the firewall configuration for the %1 Worker. + %1 töötaja tulemüüri konfiguratsiooni ei õnnestunud konfigureerida. + + + Configuration is not writable. Please check your permissions! + Konfiguratsiooni ei saa kirjutada. Palun kontrollige oma õigusi! + + + Could not apply platform-specific configuration settings. + Platvormispetsiifilisi konfiguratsiooniseadeid ei saanud rakendada. + + + + DemoClient + + %1 Demo + %1 Demo + + + + DemoConfigurationPage + + Demo server + Demo server + + + Tunables + Häälestatavad + + + ms + ms + + + Key frame interval + Võtmekaadri intervall + + + Memory limit + Mälupiirang + + + MB + MB + + + Update interval + Värskendamise intervall + + + s + s + + + Slow down thumbnail updates while demo is running + Demo töötamise ajal vähendage pisipiltide värskendussagedustt + + + + DemoFeaturePlugin + + Stop demo + Peata demo + + + Window demo + Akna demo + + + Give a demonstration by screen broadcasting + Andke juhiseid ekraanil edastamise kaudu + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + Selles režiimis kuvatakse teie ekraan aknas kõigis arvutites. Kasutajad saavad vajadusel üle minna teistele akendele. + + + Demo + Demo + + + Share your screen or allow a user to share his screen with other users. + Jagage oma ekraani või lubage kasutajal oma ekraani teiste kasutajatega jagada. + + + Full screen demo + Täisekraaniga demo + + + Share your own screen in fullscreen mode + Jagage oma ekraani täisekraanirežiimis + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + Selles režiimis kuvatakse teie ekraani täisekraanirežiimis kõikides arvutites, kui kasutajate sisendseadmed on lukus. + + + Share your own screen in a window + Jagage oma ekraani aknas + + + Share selected user's screen in fullscreen mode + Jagage valitud kasutaja ekraani täisekraanirežiimis + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + Selles režiimis kuvatakse valitud kasutaja ekraan täisekraani režiimis kõigis arvutites, kui kasutajate sisendseadmed on lukus. + + + Share selected user's screen in a window + Jaga valitud kasutaja ekraani aknas + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + Selles režiimis kuvatakse valitud arvuti ekraan kõigi arvutite aknas. Kasutajad saavad vajadusel üle minna teistele akendele. + + + Please select a user screen to share. + Valige jagamiseks kasutajaekraan. + + + Please select only one user screen to share. + Valige jagamiseks ainult üks kasutajaekraan. + + + All screens + Kõik ekraanid + + + Screen %1 [%2] + Ekraan %1 [%2] + + + + DesktopAccessDialog + + Desktop access dialog + Töölaua juurdepääsu dialoog + + + Confirm desktop access + Kinnitage töölauale juurdepääs + + + Never for this session + Mitte kunagi selle seansi jaoks + + + Always for this session + Alati selle sessiooni jaoks + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + Kasutaja %1 arvutis %2 soovib teie töölauale juurde pääseda. Kas soovite lubada juurdepääsu? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programmid ja veebisaidid + + + Predefined programs + Eelmääratud programmid + + + Name + Nimi + + + Path + Rada + + + Add new program + Lisa uus programm + + + Remove selected program + Eemalda valitud programm + + + Predefined websites + Eelmääratud veebilehed + + + Remove selected website + Eemalda valitud veebileht + + + URL + URL aadress + + + New program + Uus programm + + + New website + Uus veebileht + + + + DesktopServicesFeaturePlugin + + Run program + Programmi käivitamine + + + Open website + Weebilehe avamine + + + Click this button to open a website on all computers. + Kõigil arvutitel veebisaidi avamiseks klõpsake seda nuppu. + + + Start programs and services in user desktop + Käivitage programmid ja teenused kasutaja töölaual + + + Click this button to run a program on all computers. + Programmi käivitamiseks kõigis arvutites klõpsake seda nuppu. + + + Run program "%1" + Käivitage programm "%1" + + + Custom program + Kohandatud programm + + + Open website "%1" + Avage veebileht "%1" + + + Custom website + Kohandatud veebileht + + + + DocumentationFigureCreator + + Teacher + Õpetaja + + + Room %1 + Ruum %1 + + + Please complete all tasks within the next 5 minutes. + Palun lõpetage kõik ülesanded järgmise 5 minuti jooksul. + + + Custom website + Kohandatud veebileht + + + Open file manager + Avage failihaldur + + + Start learning tool + Käivitage õppevahend + + + Play tutorial video + Esitage õppevideot + + + Custom program + Kohandatud programm + + + Handout + Jaotusmaterjal + + + Texts to read + Loetavad tekstid + + + generic-student-user + üldine-õpilane-kasutaja + + + + ExternalVncServer + + External VNC server + Väline VNC-server + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Välise VNC-serveri konfiguratsioon + + + Port: + Port: + + + Password: + Parool: + + + + FeatureControl + + Feature control + Funktsiooni juhtimine + + + + FileTransferConfigurationPage + + File transfer + Faili edastamine + + + Directories + Kataloogid + + + Destination directory + Sihtkataloog + + + Default source directory + Vaikeallika kataloog + + + Options + Valikud + + + Remember last source directory + Pidage meeles viimane lähtekataloog + + + Create destination directory if it does not exist + Looge sihtkataloog, kui seda pole olemas + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + Faili "%1" ei õnnestunud lugemiseks avada! Palun kontrollige oma õigusi! + + + + FileTransferDialog + + File transfer + Faili edastamine + + + Options + Valikud + + + Transfer only + Edasta vaid + + + Transfer and open file(s) with associated program + Faili(de) edastamine ja avamine seotud programmiga + + + Transfer and open destination folder + Sihtkausta teisaldamine ja avamine + + + Files + Failid + + + Start + Start + + + Overwrite existing files + Kirjutage olemasolevad failid üle + + + + FileTransferPlugin + + File transfer + Faili edastamine + + + Click this button to transfer files from your computer to all computers. + Klõpsake seda nuppu, et faile arvutist kõikidesse arvutitesse edastada. + + + Select one or more files to transfer + Valige üks või mitu edastatavat faili + + + Transfer files to remote computer + Failide edastamine kaugarvutisse + + + Received file "%1". + Vastuvõetud fail "%1". + + + Could not receive file "%1" as it already exists. + Faili "%1" ei saanud vastu võtta, kuna see on juba olemas. + + + Could not receive file "%1" as it could not be opened for writing! + Faili "%1" ei saanud vastu võtta, kuna seda ei saanud kirjutamiseks avada! + + + + GeneralConfigurationPage + + User interface + Kasutajaliides + + + Language: + Keel: + + + Use system language setting + Kasutage süsteemi keele seadistust + + + Veyon + Veyon + + + Logging + Logimine + + + Log file directory + Logifailide kataloog + + + Log level + Logi tase + + + Nothing + Ei midagi + + + Only critical messages + Ainult kriitilised sõnumid + + + Errors and critical messages + Vead ja kriitilised teated + + + Warnings and errors + Hoiatused ja vead + + + Information, warnings and errors + Teave, hoiatused ja vead + + + Debug messages and everything else + Siluge sõnumeid ja kõike muud + + + Limit log file size + Logi faili suuruse piirang + + + Clear all log files + Kustutage kõik logifailid + + + Log to standard error output + Logige standardvea väljundisse + + + Network object directory + Võrguobjektide kataloog + + + Backend: + Taustaprogramm: + + + Update interval: + Värskenduse sagedus: + + + %1 service + %1 teenus + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + Logifailide eemaldamiseks tuleb teenus %1 ajutiselt peatada. Kas jätkata? + + + Log files cleared + Logifailid on kustutatud + + + All log files were cleared successfully. + Kõik logifailid kustutati edukalt. + + + Error + Viga + + + Could not remove all log files. + Kõiki logifaile ei saanud eemaldada. + + + MB + MB + + + Rotate log files + Korraldage logifaile + + + x + x + + + seconds + sekundit + + + Write to logging system of operating system + Kirjutage operatsioonisüsteemi logimissüsteemi + + + Authentication + Autentimine + + + Method: + Meetod: + + + Logon authentication + Sisselogimise autentimine + + + Key file authentication + Võtmefaili autentimine + + + Test + Test + + + Authentication is set up properly on this computer. + Autentimine on selles arvutis õigesti seadistatud. + + + Authentication keys are not set up properly on this computer. + Autentimisvõtmed pole selles arvutis õigesti seadistatud. + + + Authentication test + Autentimistest + + + + HeadlessVncServer + + Headless VNC server + Peata VNC-server + + + + LdapBrowseDialog + + Browse LDAP + Sirvige LDAP-d + + + + LdapClient + + LDAP error description: %1 + LDAP-tõrke kirjeldus: %1 + + + + LdapConfigurationPage + + Basic settings + Üldised seaded + + + General + Üldine + + + LDAP server and port + LDAP server ja port + + + Bind DN + Siduv DN + + + Bind password + Siduge parool + + + Anonymous bind + Anonüümne side + + + Use bind credentials + Kasutage sidumismandaate + + + Base DN + Baas DN + + + Fixed base DN + Fikseeritud baas DN + + + e.g. dc=example,dc=org + näit. dc=example,dc=org + + + Discover base DN by naming context + Avastage baasi DN, nimetades konteksti + + + e.g. namingContexts or defaultNamingContext + näit. namingContexts või defaultNamingContext + + + Environment settings + Keskkonna seaded + + + Object trees + Objektipuud + + + Computer tree + Arvutipuu + + + e.g. OU=Groups + näit. OU=Grupid + + + User tree + Kasutajapuu + + + e.g. OU=Users + näit. OU=kasutajad + + + e.g. OU=Computers + näit. OU=arvutid + + + Group tree + Grupipuu + + + Perform recursive search operations in object trees + Tehke rekursiivsed otsinguoperatsioonid objektipuudes + + + Object attributes + Objekti atribuudid + + + e.g. hwAddress + näit. riistvara aadress + + + e.g. member or memberUid + näit. liige või liikmeUiD + + + e.g. dNSHostName + näit. dNSHostName + + + Computer MAC address attribute + Arvuti MAC-aadressi atribuut + + + Group member attribute + Grupi liikme atribuut + + + e.g. uid or sAMAccountName + näit. uid või sAMAccountName + + + Advanced settings + Täpsemad seaded + + + Optional object filters + Valikulised objektifiltrid + + + Filter for user groups + Filter kasutajagruppide jaoks + + + Filter for users + Kasutajatele mõeldud filter + + + Filter for computer groups + Filter arvutigruppide jaoks + + + Group member identification + Grupi liikme identifitseerimine + + + Distinguished name (Samba/AD) + Eraldatud nimi (Samba/AD) + + + List all groups of a user + Loetlege kõik kasutaja grupid + + + List all groups of a computer + Loetlege kõik arvuti grupid + + + Get computer object by IP address + Hankige arvutiobjekt IP-aadressi järgi + + + LDAP connection failed + LDAP-ühendus ebaõnnestus + + + LDAP bind failed + LDAP-sidumine nurjus + + + LDAP bind successful + LDAP-sidumine õnnestus + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + LDAP-serveriga ühenduse loomine ja LDAP-sidumine. LDAP põhiseaded on õigesti konfigureeritud. + + + LDAP base DN test failed + LDAP baasi DN test ebaõnnestus + + + LDAP base DN test successful + LDAP baasi DN test õnnestus + + + LDAP naming context test failed + LDAP-i nimetamise kontekstitest ebaõnnestus + + + LDAP naming context test successful + LDAP-i nimetamise kontekstitest õnnestus + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + LDAP-i nimetamise kontekst on edukalt päritud. Leiti järgmine alus DN: +%1 + + + user tree + kasutajapuu + + + group tree + grupipuu + + + computer tree + arvutipuu + + + Enter username + Sisestage kasutajanimi + + + Please enter a user login name (wildcards allowed) which to query: + Sisestage päringuks kasutajanimi (lubatud on metamärgid): + + + user objects + kasutaja objektid + + + Enter group name + Sisestage grupi nimi + + + Please enter a group name whose members to query: + Sisestage grupi nimi, mille liikmed päringuid teevad: + + + group members + grupi liikmed + + + Group not found + Gruppi ei leidnud + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + Gruppi nimega "%1" ei leitud. Kontrollige grupi nime või grupipuu parameetrit. + + + Enter computer name + Sisestage arvuti nimi + + + computer objects + arvuti objektid + + + Enter computer DN + Sisestage arvuti DN + + + Please enter the DN of a computer whose MAC address to query: + Sisestage selle arvuti DN, mille MAC-aadress päringuks: + + + computer MAC addresses + arvuti MAC-aadressid + + + users + kasutajad + + + user groups + kasutaja grupid + + + computer groups + arvuti grupid + + + Please enter a user login name whose group memberships to query: + Sisestage sisselogimiseks kasutajanimi, kelle grupi liikmesused päringu esitamiseks: + + + groups of user + kasutaja grupid + + + User not found + Kasutajat ei leidnud + + + groups of computer + arvuti grupid + + + Computer not found + Arvutit ei leidnud + + + Enter computer IP address + Sisestage arvuti IP aadress + + + Please enter a computer IP address which to resolve to an computer object: + Sisestage arvuti IP-aadress arvutiobjekti lahendamiseks: + + + computers + arvutid + + + LDAP %1 test failed + LDAP %1 test ebaõnnestus + + + LDAP %1 test successful + LDAP %1 test õnnestus + + + The %1 has been queried successfully and %2 entries were found. + %1 on edukalt päritud ja leitud %2 kirjet. + + + %1 %2 have been queried successfully: + +%3 + %1 %2 on edukalt päritud: + +%3 + + + LDAP filter test failed + LDAP-filtri test ebaõnnestus + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + Konfigureeritud filtrit kasutades ei saa üheltki %1 päringut teha. Palun kontrollige, kas LDAP-filtris pole %1. + +%2 + + + LDAP filter test successful + LDAP-filtri test õnnestus + + + %1 %2 have been queried successfully using the configured filter. + %1 %2 on konfigureeritud filtri abil edukalt päritud. + + + (only if different from group tree) + (ainult siis, kui see erineb grupipuust) + + + Computer group tree + Arvuti grupipuu + + + computer group tree + arvuti grupipuu + + + Filter for computers + Filter arvutite jaoks + + + e.g. room or computerLab + näit. ruum või arvutiLab + + + Integration tests + Integratsioonitestid + + + Computer groups + Arvutigrupid + + + e.g. name or description + näit. nimi või kirjeldus + + + Filter for computer containers + Filter arvutikonteinerite jaoks + + + Computer containers or OUs + Arvutikonteinerid või OU-d + + + Connection security + Ühenduse turvalisus + + + TLS certificate verification + TLS-sertifikaadi kontroll + + + System defaults + Süsteemi vaikesätted + + + Never (insecure!) + Mitte kunagi (ebakindel!) + + + Custom CA certificate file + Kohandatud CA-sertifikaadi fail + + + None + Puudub + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + näit. (objectClass=arvuti) + + + e.g. (objectClass=group) + näit. (objectClass=grupp) + + + e.g. (objectClass=person) + näit. (objectClass=person) + + + e.g. (objectClass=room) or (objectClass=computerLab) + näit. (objectClass=ruum) või (objectClass=arvutiLab) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + näit. (objectClass=konteiner) või (objectClass=organizationalUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + Konfigureeritud baasi DN-i ei saanud pärida. Kontrollige põhiparameetrit DN. + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + LDAP-i baasi DN-i kohta on edukalt päringuid tehtud. Leiti järgmised kirjed: + +%1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + Põhi-DN-i ei saanud päringukontekstide kaudu pärida. Palun kontrollige nimetamise konteksti atribuudi parameetrit. + +%1 + + + Certificate files (*.pem) + Sertifikaadifailid (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + LDAP-serveriga ei saanud ühendust luua. Palun kontrollige serveri parameetreid. + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + LDAP-serveriga ei saanud siduda. Kontrollige serveri parameetreid ja siduge mandaadid. + +%1 + + + Encryption protocol + Krüptimisprotokoll + + + Computer location attribute + Arvuti asukoha atribuut + + + Computer display name attribute + Arvuti kuvanime atribuut + + + Location name attribute + Asukoha nime atribuut + + + e.g. cn or displayName + näit. cn või displayName + + + Computer locations identification + Asukoha nime atribuut + + + Identify computer locations (e.g. rooms) via: + Tehke kindlaks arvuti asukohad (nt ruumid): + + + Location attribute in computer objects + Asukoha atribuut arvutiobjektides + + + List all entries of a location + Loetlege kõik asukoha kirjed + + + List all locations + Loetlege kõik asukohad + + + Enter computer display name + Sisestage arvuti kuvatav nimi + + + Please enter a computer display name to query: + Sisestage arvuti kuvatav nimi: + + + Enter computer location name + Sisestage arvuti asukoha nimi + + + Please enter the name of a computer location (wildcards allowed): + Sisestage arvuti asukoha nimi (metamärgid on lubatud): + + + computer locations + arvuti asukohad + + + Enter location name + Sisestage asukoha nimi + + + Please enter the name of a location whose entries to query: + Sisestage asukoha nimi, mille sisestuste kohta päringu teha: + + + location entries + asukohtade kirjed + + + LDAP test failed + LDAP-test ebaõnnestus + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + %1. päringu esitamine nurjus. Kontrollige parameetr(eid) %2 ja sisestage olemasoleva objekti nimi. + +%3 + + + and + ning + + + LDAP test successful + LDAP-test õnnestus + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + Konfigureeritud %1. kirjeid ei saa pärida. Kontrollige parameetrit "%2". + +%3 + + + Browse + Sirvi + + + Test + Test + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + Hosti nimed, mis on salvestatud täielikult kvalifitseeritud domeeninimedena (FQDN, nt myhost.example.org) + + + Computer hostname attribute + Arvuti hosti nime atribuut + + + Please enter a computer hostname to query: + Sisestage päringuks arvuti hostinimi: + + + Invalid hostname + Tundmatu hosti nimi + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + Konfigureerisite arvuti hostinimed salvestamiseks täielikult kvalifitseeritud domeeninimedena (FQDN), kuid sisestasite hostinime ilma domeenita. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + Konfigureerisite arvuti hostinimed salvestamiseks lihtsate hostinimedena ilma domeeninimeta, kuid sisestasite hostinime koos domeeninime osaga. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + Kasutajat nimega "%1" ei leitud. Kontrollige kasutajanime või kasutajapuu parameetrit. + + + Enter hostname + Sisestage hosti nimi + + + Please enter a computer hostname whose group memberships to query: + Sisestage arvuti hostinimi, mille rühma liikmesused päringu esitamiseks: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + Ei leidnud arvutit hostinimega "%1". Palun kontrolli hostinime või arvutipuu parameetrit. + + + Hostname lookup failed + Hostinime otsimine nurjus + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + IP-aadressi %1 hostinime otsimine nurjus. Palun kontrollige oma DNS-serveri seadeid. + + + User login name attribute + Kasutaja sisselogimise nime atribuut + + + Configured attribute for user login name or computer hostname (OpenLDAP) + Konfigureeritud atribuut kasutaja sisselogimisnimele või arvuti hostinimele (OpenLDAP) + + + computer containers + arvutikonteinerid + + + + LdapPlugin + + Auto-configure the base DN via naming context + Alam-DN automaatne konfigureerimine nimekonteksti kaudu + + + Query objects from LDAP directory + Objektide pärimine LDAP-kataloogist + + + Show help about command + Näita abi käsu kohta + + + Commands for configuring and testing LDAP/AD integration + Käsud LDAP/AD integreerimise konfigureerimiseks ja testimiseks + + + Basic LDAP/AD support for Veyon + Põhiline LDAP/AD tugi Veyonile + + + %1 (load computers and locations from LDAP/AD) + %1 (laadige arvutid ja asukohad LDAP/AD-st) + + + %1 (load users and groups from LDAP/AD) + %1 (laadige kasutajad ja grupid LDAP/AD-st) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + Palun määrake kehtiv LDAP-URL järgides skeemi "ldap[s]://[user[:password]@]hostname[:port]" + + + No naming context attribute name given - falling back to configured value. + Nimetamise konteksti atribuudi nime pole antud - langeb tagasi konfigureeritud väärtuse juurde. + + + Could not query base DN. Please check your LDAP configuration. + DN-i päringu esitamine nurjus. Palun kontrollige oma LDAP konfiguratsiooni. + + + Configuring %1 as base DN and disabling naming context queries. + %1 konfigureerimine baas-DN-ks ja kontekstipäringute nimetamise keelamine. + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + Kasutaja autentimiseks kohandatud PAM-teenus + + + User authentication + Kasutaja autentimine + + + Session management + Sessiooni haldus + + + Display manager users + Kuvahalduri kasutajad + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Plugin, mis rakendab abstraktseid funktsioone Linuxi platvormile + + + + LocationDialog + + Select location + Vali asukoht + + + enter search filter... + sisesta otsingufilter ... + + + + MainToolBar + + Configuration + Konfiguratsioon + + + Disable balloon tooltips + Keelake tööriistavihjed + + + Show icons only + Kuva ainult ikoone + + + + MainWindow + + MainWindow + PeaAken + + + toolBar + Tööriistariba + + + General + Üldine + + + &File + &Fail + + + &Help + &Abi + + + &Quit + &Välju + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + L&oe seaded failist + + + Ctrl+O + Ctrl+O + + + About Qt + Qt kohta + + + Authentication impossible + Autentimine on võimatu + + + Configuration not writable + Konfiguratsiooni ei saa kirjutada + + + Load settings from file + Loe seaded failist + + + Save settings to file + Salvesta seaded faili + + + Unsaved settings + Salvestamata seaded + + + There are unsaved settings. Quit anyway? + Seal on salvestamata seadeid. Kas ikkagi lõpetada? + + + Veyon Configurator + Veyoni konfiguraator + + + Service + Teenus + + + Master + Master + + + Access control + Juurdepääsu juhtimine + + + About Veyon + Programmist Veyon + + + Auto + Auto + + + About + Programmist + + + %1 Configurator %2 + %1 Konfiguraator %2 + + + JSON files (*.json) + JSON failid (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + Kohaliku konfiguratsiooni taustaprogramm teatas, et konfiguratsiooni ei saa kirjutada! Palun käivitage %1 konfiguraator suuremate õigustega. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + Autentimisvõtme faile ei leitud või teie praegused on aegunud. Looge %1 Konfiguraatori abil uued võtmefailid. Teise võimalusena seadistage sisselogimise autentimine %1 Konfiguraatori abil. Vastasel juhul ei pääse te arvutitele juurde, kasutades %1. + + + Access denied + Juurdepääs keelatud + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + Kohaliku konfiguratsiooni kohaselt pole teil lubatud võrgus olevatele arvutitele juurde pääseda. Logige sisse teise kontoga või laske oma süsteemiadministraatoril kohalikku konfiguratsiooni kontrollida. + + + Screenshots + Ekraanipildid + + + Feature active + Funktsioon on aktiivne + + + The feature "%1" is still active. Please stop it before closing %2. + Funktsioon "%1" on endiselt aktiivne. Enne %2 sulgemist peatage see. + + + Reset configuration + Lähtesta konfiguratsioon + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Kas soovite tõesti lähtestada kohaliku konfiguratsiooni ja taastada kõigi seadete vaikeväärtused? + + + Search users and computers + Otsige kasutajaid ja arvuteid + + + Align computers to grid + Joondage arvutid + + + %1 Configurator + %1 Konfiguraator + + + Insufficient privileges + Ebapiisavad privileegid + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + Ei saanud alustada administraatoriõigustega. Veenduge, et teie töölaua keskkonda oleks installitud sudo-laadne programm! Programmi käivitatakse tavaliste kasutajaõigustega. + + + Only show powered on computers + Näita ainult töötavaid arvuteid + + + &Save settings to file + &Salvesta seaded faili + + + &View + &Vaade + + + &Standard + &Standard + + + &Advanced + &Edasijõudnud + + + Use custom computer arrangement + Kasutage kohandatud arvutikorraldust + + + Locations && computers + Asukohad && arvutid + + + Slideshow + Slaidiseanss + + + Spotlight + Tähelepanu keskpunktis + + + Adjust size of computer icons automatically + Kohandage arvuti ikoonide suurust automaatselt + + + + MasterConfigurationPage + + Directories + Kataloogid + + + User configuration + Kasutaja konfiguratsioon + + + Feature on computer double click: + Funktsioon arvutis topeltklõpsake: + + + Features + Funktsioonid + + + All features + Kõik funktsioonid + + + Disabled features + Keelatud funktsioonid + + + Screenshots + Ekraanipildid + + + <no feature> + <no feature> + + + Basic settings + Üldised seaded + + + Behaviour + Käitumine + + + Enforce selected mode for client computers + Valitud režiimi rakendamine klientarvutite jaoks + + + Hide local computer + Peida kohalik arvuti + + + Hide computer filter field + Peida arvuti filtri väli + + + Actions such as rebooting or powering down computers + Sellised toimingud nagu arvutite taaskäivitamine või väljalülitamine + + + User interface + Kasutajaliides + + + Background color + Taustavärv + + + Thumbnail update interval + Pisipiltide uuendamise sagedus + + + ms + ms + + + Program start + Käivita programm + + + Modes and features + Režiimid ja funktsioonid + + + User and computer name + Kasutaja ja arvuti nimi + + + Only user name + Vaid kasutaja nimi + + + Only computer name + Vaid arvuti nimi + + + Computer thumbnail caption + Arvuti pisipiltide pealdis + + + Text color + Teksti värv + + + Sort order + Järjestamine + + + Computer and user name + Arvuti ja kasutajanimi + + + Computer locations + Arvuti asukoht + + + Show current location only + Kuva ainult praegune asukoht + + + Allow adding hidden locations manually + Luba peidetud asukohtade käsitsi lisamine + + + Hide empty locations + Peida tühjad asukohad + + + Show confirmation dialog for potentially unsafe actions + Kuva potentsiaalselt ohtlike toimingute kinnitusdialoog + + + Perform access control + Tehke juurdepääsu kontroll + + + Automatically select current location + Valige praegune asukoht automaatselt + + + Automatically open computer select panel + Avage arvuti valimispaneel automaatselt + + + Hide local session + Peida kohalik seanss + + + px + px + + + Thumbnail spacing + Pisipiltide vahe + + + Auto + Auto + + + Thumbnail aspect ratio + Pisipildi kuvasuhe + + + Automatically adjust computer icon size + Reguleerige arvuti ikooni suurust automaatselt + + + Open feature windows on the same screen as the main window + Avage funktsiooniaknad peaaknaga samal ekraanil + + + + MonitoringMode + + Monitoring + Jälgimine + + + Builtin monitoring mode + Sisemine jälgimisrežiim + + + This mode allows you to monitor all computers at one or more locations. + See režiim võimaldab teil jälgida kõiki arvuteid ühes või mitmes kohas. + + + + NetworkObjectTreeModel + + Locations/Computers + Asukohad/Arvutid + + + + OpenWebsiteDialog + + Open website + Weebilehe avamine + + + e.g. Veyon + näit. Veyon + + + Remember and add to website menu + Pidage meeles ja lisage veebisaidi menüüsse + + + e.g. www.veyon.io + näit. www.veyon.io + + + Please enter the URL of the website to open: + Avamiseks sisestage palun veebisaidi URL: + + + Name: + Nimi: + + + + PasswordDialog + + Username + Kasutajanimi + + + Password + Parool + + + Veyon Logon + Veyoni sisselogimine + + + Authentication error + Autentimise viga + + + Logon failed with given username and password. Please try again! + Sisselogimine nurjus antud kasutajanime ja parooliga. Palun proovi uuesti! + + + Please enter your username and password in order to access computers. + Arvutitele juurdepääsu saamiseks sisestage oma kasutajanimi ja parool. + + + + PowerControlFeaturePlugin + + Power on + Lülita sisse + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Kõigi arvutite sisselülitamiseks klõpsake seda nuppu. Nii ei pea te igat arvutit käsitsi sisse lülitama. + + + Reboot + Taaskäivitamine + + + Click this button to reboot all computers. + Kõigi arvutite taaskäivitamiseks klõpsake seda nuppu. + + + Power down + Lülita välja + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Kõigi arvutite väljalülitamiseks klõpsake seda nuppu. Nii ei pea te iga arvutit käsitsi välja lülitama. + + + Power on/down or reboot a computer + Lülitage arvuti sisse/välja või taaskäivitage + + + Confirm reboot + Kinnitage taaskäivitamine + + + Confirm power down + Kinnitage väljalülitamine + + + Do you really want to reboot the selected computers? + Kas soovite tõesti valitud arvutid taaskäivitada? + + + Do you really want to power down the selected computer? + Kas soovite tõesti valitud arvutid välja lülitada? + + + Power on a computer via Wake-on-LAN (WOL) + Lülitage arvuti sisse Wake-on-LAN (WOL) kaudu + + + MAC ADDRESS + MAC AADRESS + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + See käsk edastab võrgus Wake-on-LAN (WOL) paketi, et antud MAC-aadressiga arvuti sisse lülitada. + + + Please specify the command to display help for! + Palun määrake käsk, mille jaoks abi kuvatakse! + + + Invalid MAC address specified! + Määratud on kehtetu MAC-aadress! + + + Commands for controlling power status of computers + Käsud arvutite toiteoleku kontrollimiseks + + + Power down now + Lülita kohe välja + + + Install updates and power down + Uuenda ja lülita välja + + + Power down after user confirmation + Lülita välja kasutaja nõustumisel + + + Power down after timeout + Lülita välja aja möödumisel + + + The computer was remotely requested to power down. Do you want to power down the computer now? + Arvuti paluti eemalt välja lülitada. Kas soovite arvuti kohe välja lülitada? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + Arvuti lülitatakse välja %1 minuti, %2 sekundi pärast. + +Salvestage oma töö ja sulgege kõik programmid. + + + + PowerDownTimeInputDialog + + Power down + Lülita välja + + + Please specify a timeout for powering down the selected computers: + Määrake valitud arvutite väljalülitamise aeg: + + + minutes + minutit + + + seconds + sekundit + + + + RemoteAccessFeaturePlugin + + Remote view + Kaugvaade + + + Open a remote view for a computer without interaction. + Avage arvuti kaugvaade ilma suhtlemiseta. + + + Remote control + Kaugjuurdepääs + + + Open a remote control window for a computer. + Avage arvuti kaughalduse aken. + + + Remote access + Kaugjuurdepääs + + + Remote view or control a computer + Kaugvaatamine või arvuti kaughaldus + + + Please enter the hostname or IP address of the computer to access: + Palun sisestage juurdepääsuks arvuti hostinimi või IP-aadress: + + + Show help about command + Näita abi käsu kohta + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 - %2 Kaugjuurdepääs + + + %1 - %2 - %3 Remote Access + %1 - %2 - %3 Kaugjuurdepääs + + + + RemoteAccessWidgetToolBar + + View only + Ainult vaade + + + Remote control + Kaugjuurdepääs + + + Send shortcut + Saada otsetee + + + Fullscreen + Täisekraan + + + Window + Aken + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Menüü + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Ühendamine %1 + + + Connected. + Ühendatud + + + Screenshot + Ekraanipilt + + + Exit + Välju + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Sisestage palun valitud arvutis (arvutites) käivitamiseks programmid või käsud. Eraldage programmid/ käskud uutel ridadel. + + + Run programs + Käivita programmid + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + näit. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + Nimi: + + + Remember and add to program menu + Pea meeles ja lisa programmi menüüsse + + + e.g. VLC + näit. VLC + + + + ScreenLockFeaturePlugin + + Lock + Lukusta + + + Unlock + Lukusta lahti + + + Lock screen and input devices of a computer + Lukusta arvuti ekraan ja sisendseadmed + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Kasutaja tähelepanu äratamiseks võite selle nupu abil tema arvutid lukustada. Selles režiimis on kõik sisendseadmed lukustatud ja ekraanid mustad. + + + Lock input devices + Lukusta sisendseadmed + + + Unlock input devices + Sisendseadmete avamine + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + Kasutaja tähelepanu äratamiseks võite selle nupu abil tema arvutid lukustada. Selles režiimis on kõik sisendseadmed lukustatud, kuid töölaud on endiselt nähtav. + + + + Screenshot + + unknown + tundmatu + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + Ekraanipilti ei õnnestunud teha, kuna kataloogi %1 pole olemas ja seda ei saa luua. + + + Screenshot + Ekraanipilt + + + Could not open screenshot file %1 for writing. + Ekraanipilti faili %1 ei saanud kirjutamiseks avada. + + + + ScreenshotFeaturePlugin + + Screenshot + Ekraanipilt + + + Use this function to take a screenshot of selected computers. + Selle funktsiooni abil saate valitud arvutitest ekraanipildi teha. + + + Screenshots taken + Tehtud ekraanipildid + + + Screenshot of %1 computer have been taken successfully. + %1 arvuti ekraanipilt on edukalt tehtud. + + + Take screenshots of computers and save them locally. + Tehke arvutitest ekraanipilte ja salvestage need lokaalselt. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Kõik teie tehtud ekraanipildid on loetletud siin. Ekraanipilte saate teha, klõpsates arvuti kontekstimenüüs üksusel „Ekraanipilt”. Ekraanipilte saab hallata allolevate nuppude abil. + + + User: + Kasutaja: + + + Computer: + Arvuti: + + + Date: + Kuupäev: + + + Time: + Kellaaeg: + + + Show + Näita + + + Delete + Kustuta + + + Screenshot + Ekraanipilt + + + Do you really want to delete all selected screenshots? + Kas soovite tõesti kõik valitud ekraanipildid kustutada? + + + + ServiceConfigurationPage + + General + Üldine + + + Autostart + Autostart + + + Hide tray icon + Peida salveikoon + + + Start service + Käivita teenus + + + Stopped + Peatatud + + + Stop service + Peata teenus + + + State: + Seisund: + + + Enable firewall exception + Luba tulemüüri erisused + + + Allow connections from localhost only + Luba ühendused ainult localhostilt + + + VNC server + VNC server + + + Plugin: + Pistikprogramm: + + + Restart %1 Service + Taaskäivita %1 teenus + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Kõik seaded salvestati edukalt. Jõustumiseks tuleb %1 teenus taaskäivitada. Kas taaskäivitada see kohe? + + + Running + Käib + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + Selle suvandi lubamine käivitab teenuse serveriprotsessi iga arvuti interaktiivse seansi jaoks. +Tavaliselt on see vajalik terminaliserverite toetamiseks. + + + Show notification on remote connection + Kuva teated kaugühenduse korral + + + Show notification when an unauthorized access is blocked + Kuva teade, kui volitamata juurdepääs on blokeeritud + + + Sessions + Sessioonid + + + Single session mode (create server instance for local/physical session only) + Ühe seansi režiim (loo serveri eksemplar ainult kohaliku/füüsilise seansi jaoks) + + + Multi session mode (create server instance for each local and remote desktop session) + Mitme seansi režiim (looge iga kohaliku ja kaugtöölaua seansi jaoks serveri eksemplar) + + + Maximum session count + Maksimaalne seansside arv + + + Network port numbers + Võrgu pordi numbrid + + + Veyon server + Veyoni server + + + Internal VNC server + Sisemine VNC server + + + Feature manager + Funktsioonihaldur + + + Demo server + Demo server + + + Miscellaneous network settings + Mitmesugused võrguseaded + + + + ServiceControl + + Starting service %1 + Käivita teenus %1 + + + Stopping service %1 + Teenuse peatamine %1 + + + Registering service %1 + Teenuse registreerimine %1 + + + Unregistering service %1 + Teenuse registreerimise tühistamine %1 + + + Service control + Teenuse juhtimine + + + + ServiceControlPlugin + + Service is running + Teenus töötab + + + Service is not running + Teenus on peatatud + + + Configure and control Veyon service + Veyoni teenuse konfigureerimine ja juhtimine + + + Register Veyon Service + Registreerige Veyoni teenus + + + Unregister Veyon Service + Tühistage Veyoni teenuse registreerimine + + + Start Veyon Service + Käivitage Veyoni teenus + + + Stop Veyon Service + Peatan Veyoni teenuse + + + Restart Veyon Service + Taaskäivitage Veyoni teenus + + + Query status of Veyon Service + Veyoni teenuse päringu olek + + + Commands for configuring and controlling Veyon Service + Käsud Veyon Service'i konfigureerimiseks ja juhtimiseks + + + + ShellCommandLinePlugin + + Run command file + Käivitage käsufail + + + File "%1" does not exist! + Faili "%1" ei ole! + + + Interactive shell and script execution for Veyon Control + Interaktiivne kesta ja skripti käivitamine Veyon-i juhtimisele + + + Commands for shell functionalities + Käsud kestade funktsionaalsuste jaoks + + + + SlideshowPanel + + Previous + Eelmine + + + Start/pause + Start/paus + + + Next + Järgmine + + + Duration: + Kestus: + + + + SpotlightPanel + + Add selected computers + Lisage valitud arvutid + + + Remove selected computers + Eemaldage valitud arvutid + + + Update computers in realtime + Värskendage arvuteid reaalajas + + + Spotlight + Tähelepanu keskpunktis + + + Please select at least one computer to add. + Valige lisamiseks vähemalt üks arvuti. + + + Please select at least one computer to remove. + Valige eemaldamiseks vähemalt üks arvuti. + + + Add computers by clicking with the middle mouse button or clicking the first button below. + Arvutite lisamiseks klõpsake hiire keskmise nupuga või klõpsake allolevat esimest nuppu. + + + + SystemTrayIcon + + System tray icon + Süsteemse salve ikoon + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + Kasutajarühmade taustaprogramm süsteemi kasutajagruppide jaoks + + + Default (system user groups) + Vaikimisi (süsteemi kasutajagrupid) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + Testige Veyoni sisemisi komponente ja funktsioone + + + Commands for testing internal components and functions of Veyon + Käsud Veyoni sisemiste komponentide ja funktsioonide testimiseks + + + + TextMessageDialog + + Send text message + Saada tekstisõnum + + + Use the field below to type your message which will be sent to all selected users. + Kasutage allolevat välja, et sisestada sõnum, mis saadetakse kõigile valitud kasutajatele. + + + + TextMessageFeaturePlugin + + Text message + Tekstisõnum + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Selle funktsiooni abil saate tekstisõnumi saata kõigile kasutajatele, näit. neile uusi ülesandeid määrata. + + + Message from teacher + Sõnum õpetajalt + + + Send a message to a user + Saada kasutajale sõnum + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Luba kihiliste (poolläbipaistvate) akende hõivamine + + + Poll full screen (leave this enabled per default) + Küsitlus täisekraanil (jätke see vaikimisi lubatuks) + + + Low accuracy (turbo mode) + Madal täpsus (turborežiim) + + + Builtin UltraVNC server configuration + Sissemise UltraVNC serveri konfiguratsioon + + + Enable multi monitor support + Luba mitme kuvari tugi + + + Enable Desktop Duplication Engine on Windows 8 and newer + Luba töölaua laiendamismootor Windows 8 ja uuemates versioonides + + + Maximum CPU usage + Maksimaalne protsessori kasutamine + + + + UserConfig + + No write access + Kirjutusjuurdepääs puudub + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Teie isiklikke seadeid ei õnnestunud salvestada! Kontrollige kasutaja konfiguratsioonifaili teed %1 Konfiguraatori abil + + + + UserLoginDialog + + User login + Kasutaja sisselogimine + + + Please enter a username and password for automatic login on all computers. + Kõigi arvutite automaatseks sisselogimiseks sisestage kasutajanimi ja parool. + + + Username + Kasutajanimi + + + Password + Parool + + + + UserSessionControlPlugin + + Log in + Logi sisse + + + Click this button to log in a specific user on all computers. + Kõigil arvutitel konkreetse kasutaja sisselogimiseks klõpsake seda nuppu. + + + Log off + Logi välja + + + Click this button to log off users from all computers. + Kasutajate kõigist arvutitest väljalogimiseks klõpsake seda nuppu. + + + Confirm user logoff + Kinnitage kasutaja väljalogimine + + + Do you really want to log off the selected users? + Kas soovite tõesti valitud kasutajad välja logida? + + + User session control + Kasutaja seansi juhtimine + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [FAIL] + + + Invalid command! + Tundmatu käsk! + + + Available commands: + Saadaolevad käsud: + + + Invalid arguments given + Esitatud valed argumendid + + + Not enough arguments given - use "%1 help" for more information + Ei ole piisavalt argumente esitatud - lisateabe saamiseks kasutage "%1 abi" + + + Unknown result! + Tundmatu tulemus! + + + Available modules: + Saadaval olevad moodulid: + + + No module specified or module not found - available modules are: + Moodulit pole määratud või moodulit ei leitud - saadaval on: + + + Plugin not licensed + Pistikprogramm pole litsentsitud + + + INFO + INFO + + + ERROR + VIGA + + + USAGE + KASUTA + + + DESCRIPTION + KIRJELDUS + + + EXAMPLES + NÄITED + + + WARNING + HOIATUS + + + + VeyonServiceControl + + Veyon Service + Veyoni teenus + + + + VncViewWidget + + Establishing connection to %1 ... + Ühenduse loomine kasutajaga %1 ... + + + + WebApiConfigurationPage + + Web API + Veebi API + + + General + Üldine + + + Network port + Võrgu port + + + Enable WebAPI server + Luba veebi API server + + + Connection settings + Ühenduse seaded + + + Lifetime + Eluaegne + + + h + h + + + s + s + + + Idle timeout + Tühikäigu aegumine + + + Authentication timeout + Autentimise aegumine + + + Maximum number of open connections + Maksimaalne avatud ühenduste arv + + + Connection encryption + Ühenduse krüptimine + + + TLS certificate file + TLS-i sertifikaadifail + + + TLS private key file + TLS-i privaatvõtme fail + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + Kasutage HTTP asemel TLT 1.3-ga HTTPS-i + + + + WebApiPlugin + + Run WebAPI server + Käivitage veebi API server + + + Failed to start WebAPI server at port %1 + Veebi API serveri käivitamine pordis%1 nurjus + + + WebAPI server running at port %1 + Veebi API server töötab pordis %1 + + + Provide access to a computer via HTTP + Andke arvutile juurdepääs HTTP kaudu + + + Commands for running the WebAPI server + Käsklused veebi API serveri käitamiseks + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + Tarkvara järgi ei saanud SAS-i genereerimise seadet muuta. Ctrl+Alt+Del saatmine kaughalduse kaudu ei toimi! + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + Üldine + + + Enable SAS generation by software (Ctrl+Alt+Del) + Luba tarkvara SAS genereerimine (Ctrl+Alt+Del) + + + Screen lock + Ekraanilukk + + + Hide taskbar + Peida tegumiriba + + + Hide start menu + Peida Start menüü + + + Hide desktop + Peida töölaud + + + User authentication + Kasutaja autentimine + + + Use alternative user authentication mechanism + Kasutage alternatiivset kasutaja autentimismehhanismi + + + User login + Kasutaja sisselogimine + + + Input start delay + Sisendi käivitamise viivitus + + + Simulated key presses interval + Simuleeritud klahvivajutuste intervall + + + Confirm legal notice (message displayed before user logs in) + Kinnitage juriidiline teade (teade kuvatakse enne kasutaja sisselogimist) + + + Use input device interception driver + Kasutage sisendseadme pealtkuulamise draiverit + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Pistikprogramm, mis rakendab Windowsi platvormi jaoks abstraktseid funktsioone + + + + WindowsServiceControl + + The service "%1" is already installed. + Teenus "%1" on juba installitud. + + + The service "%1" could not be installed. + Teenust "%1" ei saanud installida. + + + The service "%1" has been installed successfully. + Teenus "%1" installiti edukalt. + + + The service "%1" could not be uninstalled. + Teenust "%1" ei saanud desinstallida. + + + The service "%1" has been uninstalled successfully. + Teenuse "%1" desinstallimine õnnestus. + + + The start type of service "%1" could not be changed. + Teenuse algustüüpi "%1" ei saanud muuta. + + + Service "%1" could not be found. + Teenust "%1" ei leitud. + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Sisemise x11vnc serveri konfiguratsioon + + + Custom x11vnc parameters: + Kohandatud x11vnc parameetrid: + + + Do not use X Damage extension + Ärge kasutage laiendust X Damage + + + diff --git a/translations/veyon_fa.ts b/translations/veyon_fa.ts new file mode 100644 index 0000000..320ced0 --- /dev/null +++ b/translations/veyon_fa.ts @@ -0,0 +1,4057 @@ + + + + + AboutDialog + + About + درباره + + + Translation + ترجمه + + + License + مجوز + + + About Veyon + درباره ویون + + + Contributors + همکاران + + + Version: + نسخه: + + + Website: + وب سایت: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + زبان فعلی هنوز ترجمه نشده است (یا زبان مادری انگلیسی). +اگر شما علاقه مند به ترجمه Veyon به زبان محلی خود یا زبان دیگری هستید یا مایل به بهبود ترجمه های موجود هستید، لطفا با یک توسعه دهنده Veyon تماس بگیرید! + + + About %1 %2 + درباره %1 %2 + + + Support Veyon project with a donation + پشتیبانی پروژه ویون با کمک مالی + + + + AccessControlPage + + Computer access control + کنترل دسترسی کامپیوتر + + + Grant access to every authenticated user (default) + دسترسی به هر کاربر معتبر (به طور پیش فرض) را تأیید کنید + + + Test + تست + + + Process access control rules + قوانین کنترل دسترسی کنترل فرآیند + + + User groups authorized for computer access + گروه های کاربری مجاز برای دسترسی به کامپیوتر + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + لطفا گروه هایی را که اعضای شما مجاز به دسترسی به کامپیوتر در شبکه ویون شما هستند، اضافه کنید. + + + Authorized user groups + گروه های کاربری مجاز + + + All groups + تمام گروه ها + + + Access control rules + قوانین کنترل دسترسی + + + Add access control rule + اضافه کردن قانون کنترل دسترسی + + + Remove access control rule + حذف قانون کنترل دسترسی + + + Move selected rule down + پایین بردن قانون انتخاب شده + + + Move selected rule up + بالا بردن قانون انتخاب شده + + + Edit selected rule + ویرایش قانون انتخاب شده + + + Enter username + نام کاربری را وارد کنید + + + Please enter a user login name whose access permissions to test: + لطفا یک نام کاربری ورود به سیستم که مجوزهای دسترسی برای تست را دارد وارد کنید: + + + Access allowed + دسترسی مجاز است + + + The specified user is allowed to access computers with this configuration. + کاربر مورد نظر مجاز است به این پیکربندی دسترسی پیدا کند. + + + Access denied + دسترسی غیرمجاز است + + + The specified user is not allowed to access computers with this configuration. + کاربر مورد نظر مجاز نیست به این پیکربندی دسترسی پیدا کند. + + + Enable usage of domain groups + استفاده از گروههای دامنه را فعال کنید + + + User groups backend: + گروه های کاربر: + + + Missing user groups backend + گروه های کاربری فاقد بخش مدیریت + + + No default user groups plugin was found. Please check your installation! + هیچ پلاگین گروه کاربر پیش فرض یافت نشد لطفا نصب خود را بررسی کنید! + + + Restrict access to members of specific user groups + + + + + AccessControlRuleEditDialog + + Edit access control rule + ویرایش قانون کنترل دسترسی + + + General + عمومی + + + enter a short name for the rule here + یک نام کوتاه برای این قانون بنویسید + + + Rule name: + نام قانون: + + + enter a description for the rule here + اینجا توضیحی برای این قانون وارد کنید + + + Rule description: + شرح قانون: + + + Invert all conditions ("is/has" interpreted as "is/has not") + همه شرایط را غیرفعال کنید ("is / has" به عنوان "آیا / ندارد" تفسیر شده است) + + + Conditions + شرایط + + + is member of group + عضو گروه است + + + Accessing computer is localhost + دسترسی به کامپیوتر میزبان محلی است + + + Accessing user is logged on user + دسترسی به کاربر به کاربر وارد شده است + + + Accessing user is already connected + دسترسی به کاربر در حال حاضر اتصال است + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + اگر بیشتر از یک شرایط فعال شود، هر شرایط باید برای تطابق قوانین (منطقی AND) مطابقت داشته باشد. اگر فقط یکی از شرایط چندگانه برای برآورده شدن باشد (منطقی OR)، لطفا قوانین کنترل دسترسی چندگانه ایجاد کنید. + + + Action + اقدام + + + Allow access + اجازه دسترسی + + + Deny access + دسترسی غیرمجاز + + + Ask logged on user for permission + از کاربر خواسته شده برای مجوز وارد شوید + + + None (rule disabled) + هیچکدام (قانون غیرفعال شده است) + + + Accessing user + دسترسی به کاربر + + + Accessing computer + دسترسی به کامپیوتر + + + Local (logged on) user + محلی (وارد سیستم) کاربر + + + Local computer + کامپیوتر محلی + + + Always process rule and ignore conditions + همیشه قوانین را فراموش کنید و شرایط را نادیده بگیرید + + + No user logged on + هیچ کاربری وارد نشده است + + + Accessing user has one or more groups in common with local (logged on) user + دسترسی به کاربر دارای یک یا چند گروه مشترک با کاربر محلی (وارد شده) است + + + Accessing computer and local computer are at the same location + + + + is located at + + + + + AccessControlRulesTestDialog + + Access control rules test + تست قوانین کنترل دسترسی + + + Accessing user: + دسترسی کاربر: + + + Local computer: + کامپیوتر محلی: + + + Accessing computer: + دسترسی کامپیوتر + + + Please enter the following user and computer information in order to test the configured ruleset. + برای تست تنظیمات پیکربندی شده، لطفا اطلاعات کاربر و رایانه زیر را وارد کنید. + + + Local user: + کاربر محلی: + + + Connected users: + کاربران متصل : + + + The access in the given scenario is allowed. + دسترسی به سناریو داده شده مجاز است. + + + The access in the given scenario is denied. + دسترسی به سناریو داده شده ممنوع است. + + + The access in the given scenario needs permission of the logged on user. + دسترسی به سناریو داده شده نیاز به اجازه ورود کاربر دارد. + + + ERROR: Unknown action + خطا: اقدام ناشناخته + + + Test result + نتیجه تست + + + + AuthKeysConfigurationPage + + Authentication keys + کلیدهای احراز هویت + + + Introduction + معرفی + + + Key file directories + دایرکتوری فایل های کلیدها + + + Public key file base directory + دایرکتوری پایگاه داده کلید عمومی + + + Private key file base directory + فایل پایگاه داده فایل خصوصی + + + Available authentication keys + کلیدهای تأیید موجود + + + Create key pair + ایجاد جفت کلید + + + Delete key + حذف کلید + + + Import key + وارد کردن کلید + + + Export key + صادرکردن کلید + + + Set access group + تعیین گروه دسترسی + + + Key files (*.pem) + فایل های کلیدی (* .pem) + + + Authentication key name + نام کلید تایید + + + Please enter the name of the user group or role for which to create an authentication key pair: + لطفا نام گروه کاربری یا نقش مورد نظر برای ایجاد یک جفت کلید تأیید اعتبار را وارد کنید: + + + Do you really want to delete authentication key "%1/%2"? + آیا واقعا میخواهید کلید تأیید اعتبار «٪ 1 /٪ 2» را حذف کنید؟ + + + Please select a key to delete! + لطفا یک کلید برای حذف انتخاب کنید! + + + Please enter the name of the user group or role for which to import the authentication key: + لطفا نام گروه کاربری یا نقش را برای وارد کردن کلید تأیید اعتبار وارد کنید. + + + Please select a key to export! + لطفا یک کلید برای صدورانتخاب کنید! + + + Please select a user group which to grant access to key "%1": + لطفا یک گروه کاربری که برای اعطای دسترسی به کلید "٪ 1" را انتخاب کنید: + + + Please select a key which to set the access group for! + لطفا یک کلید را برای تنظیم گروه دسترسی برای انتخاب کنید! + + + Please perform the following steps to set up key file authentication: + لطفا مراحل زیر را برای راه اندازی احراز هویت فایل کلید انجام دهید: + + + 1) Create a key pair on the master computer. + + + + 2) Set an access group whose members should be allowed to access other computers. + + + + 3) Export the public key and import it on all client computers with the same name. + + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + + + + + AuthKeysManager + + Please check your permissions. + + + + Key name contains invalid characters! + + + + Invalid key type specified! Please specify "%1" or "%2". + + + + Specified key does not exist! Please use the "list" command to list all installed keys. + + + + One or more key files already exist! Please delete them using the "delete" command. + + + + Creating new key pair for "%1" + + + + Failed to create public or private key! + + + + Newly created key pair has been saved to "%1" and "%2". + + + + Could not remove key file "%1"! + + + + Could not remove key file directory "%1"! + + + + Failed to create directory for output file. + + + + File "%1" already exists. + + + + Failed to write output file. + + + + Key "%1/%2" has been exported to "%3" successfully. + + + + Failed read input file. + + + + File "%1" does not contain a valid private key! + + + + File "%1" does not contain a valid public key! + + + + Failed to create directory for key file. + + + + Failed to write key file "%1". + + + + Failed to set permissions for key file "%1"! + + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + + + + Failed to convert private key to public key + + + + Failed to create directory for private key file "%1". + + + + Failed to save private key in file "%1"! + + + + Failed to set permissions for private key file "%1"! + + + + Failed to create directory for public key file "%1". + + + + Failed to save public key in file "%1"! + + + + Failed to set permissions for public key file "%1"! + + + + Failed to set owner of key file "%1" to "%2". + + + + Failed to set permissions for key file "%1". + + + + Key "%1" is now accessible by user group "%2". + + + + <N/A> + + + + Failed to read key file. + + + + + AuthKeysPlugin + + Create new authentication key pair + + + + Delete authentication key + + + + List authentication keys + + + + Import public or private key + + + + Export public or private key + + + + Extract public key from existing private key + + + + Set user group allowed to access a key + + + + KEY + + + + ACCESS GROUP + + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + + NAME + + + + FILE + + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + + Please specify the command to display help for! + + + + TYPE + + + + PAIR ID + + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + + + + Type + + + + Access group + + + + Pair ID + + + + + BuiltinDirectoryConfigurationPage + + Computers + + + + Name + + + + Host address/IP + + + + MAC address + + + + Add new computer + + + + Remove selected computer + + + + New computer + + + + Builtin directory + + + + Locations & computers + + + + Locations + + + + Add new location + + + + Remove selected location + + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + + + + + BuiltinDirectoryPlugin + + Show help for specific command + + + + Import objects from given file + + + + Export objects to given file + + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + + + + Name + + + + Host address + + + + MAC address + + + + Specified object not found. + + + + File "%1" does not exist! + + + + Can't open file "%1" for reading! + + + + Unknown argument "%1". + + + + Computer "%1" (host address: "%2" MAC address: "%3") + + + + Unclassified object "%1" with ID "%2" + + + + None + + + + Computer + + + + Root + + + + Invalid + + + + Error while parsing line %1. + + + + Network object directory which stores objects in local configuration + + + + Commands for managing the builtin network object directory + + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + + + + Parent UUID + + + + Add a location or computer + + + + Clear all locations and computers + + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + + + + FILE + + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + + + + NAME + + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + سرور داخلی vnc (ultravnc) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + سرور داخلی vnc (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + میزبان / آدرس آی پی :٪ 1 + + + Active features: %1 + ویژگی های فعال:٪ 1 + + + Online and connected + آنلاین و متصل + + + Establishing connection + برقراری ارتباط + + + Computer offline or switched off + کامپیوتر آفلاین یا خاموش است + + + Authentication failed or access denied + تأیید اعتبار ناموفق بود یا دسترسی به آن ممنوع شد + + + Disconnected + قطع شده + + + No user logged on + هیچ کاربری وارد نشده است + + + Logged on user: %1 + کاربر وارد شده:٪ 1 + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + ٪ 1 سرویس٪ 2 در٪ 3:٪ 4 + + + Authentication error + خطا احراز هویت + + + Remote access + + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + کاربر + + + Missing network object directory plugin + گم شده پلاگین دایرکتوری شیء شبکه + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + هیچ پلاگین دایرکتوری شیء پیش فرض شبکه یافت نشد. لطفا نصب خود را بررسی کنید یا یک پایگاه داده دایرکتوری شیء شبکه دیگر را از طریق٪ 1 Configurator پیکربندی کنید. + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + جستجوی رایانه + + + Add location + + + + Save computer/user list + ذخیره لیست کامپیوتر / کاربران + + + Select output filename + انتخاب نام فایل خروجی + + + CSV files (*.csv) + فایل های csv (*.csv) + + + File error + فایل خطا + + + Could not write the computer and users list to %1! Please check the file access permissions. + لیست کامپیوتر و کاربران را به٪ 1 نمی توان نوشت! لطفا مجوز دسترسی به فایل را بررسی کنید. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + لطفا یک فایل پیکربندی موجود برای وارد کردن را مشخص کنید + + + Please specify a valid filename for the configuration export. + لطفا نام فایل معتبر را برای تنظیم پیکربندی مشخص کنید. + + + Please specify a valid key. + لطفا یک کلید معتبر را مشخص کنید + + + Specified key does not exist in current configuration! + کلید مشخص در پیکربندی فعلی وجود ندارد! + + + Please specify a valid value. + لطفا یک مقدار معتبر را مشخص کنید. + + + Configure Veyon at command line + پیکربندی Veyon در خط فرمان + + + Output file is not writable! + فایل خروجی قابل نوشتن نیست! + + + Output directory is not writable! + دایرکتوری خروجی قابل نوشتن نیست! + + + Configuration file is not readable! + فایل پیکربندی قابل خواندن نیست! + + + Clear system-wide Veyon configuration + پاک کردن پیکربندی Veyon در سراسر سیستم + + + List all configuration keys and values + لیست تمام کلید های پیکربندی و مقادیر + + + Import configuration from given file + وارد نمودن پیکربندی از فایل داده شده + + + Export configuration to given file + ذخیره پیکربندی به فایل داده شده + + + Read and output configuration value for given key + خواندن و خروجی مقدار پیکربندی برای کلید داده شده + + + Write given value to given configuration key + مقدار داده شده به کلید پیکربندی داده شده را بنویسید + + + Unset (remove) given configuration key + رها کردن (حذف) کلید تنظیمات داده شده + + + Commands for managing the configuration of Veyon + دستورات برای مدیریت پیکربندی Veyon + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + ویژگی Property Autostart برای سرویس٪ 1 قابل تغییر نیست. + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + نمایش%1 + + + + DemoConfigurationPage + + Demo server + نمایش سرور + + + Tunables + + + + ms + ام اس + + + Key frame interval + فاصله فریم کلیدی + + + Memory limit + محدودیت حافظه + + + MB + مگابایت + + + Update interval + به روز رسانی فاصله + + + s + اس + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + توقف نمایش تمام صفحه + + + Window demo + نمایش پنجره + + + Give a demonstration by screen broadcasting + نمایش توسط پخش روی صفحه نمایش + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + در این حالت صفحه شما در یک پنجره در همه رایانه ها نمایش داده می شود. کاربران می توانند در صورت نیاز به پنجره های دیگر تغییر دهند. + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + پنجره دسترسی به دسک تاپ + + + Confirm desktop access + تأیید دسترسی به دسکتاپ + + + Never for this session + این جلسه برگزار نشود + + + Always for this session + این جلسه برگزار شود + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + کاربر٪ 1 در کامپیوتر٪ 2 میخواهد به دسکتاپ شما دسترسی پیدا کند. آیا می خواهید دسترسی را اعطا کنید؟ + + + + DesktopServicesConfigurationPage + + Programs & websites + + + + Predefined programs + + + + Name + + + + Path + + + + Add new program + + + + Remove selected program + + + + Predefined websites + + + + Remove selected website + + + + URL + + + + New program + + + + New website + + + + + DesktopServicesFeaturePlugin + + Run program + اجرای برنامه + + + Open website + وبسایت را باز کنید + + + Click this button to open a website on all computers. + برای باز کردن یک وب سایت روی تمام رایانه ها ، روی این دکمه کلیک کنید. + + + Start programs and services in user desktop + شروع برنامه ها و خدمات در دسکتاپ کاربر + + + Click this button to run a program on all computers. + برای اجرای یک برنامه روی تمام رایانه ها روی این دکمه کلیک کنید. + + + Run program "%1" + + + + Custom program + + + + Open website "%1" + + + + Custom website + + + + + DocumentationFigureCreator + + Teacher + + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + سرور خارجی VNC + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + پیکربندی سرور خارجی VNC + + + Port: + پورت + + + Password: + رمز + + + + FeatureControl + + Feature control + کنترل ویژگی + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + فهرست راهنما + + + Destination directory + + + + Default source directory + + + + Options + + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + + + + Select one or more files to transfer + + + + Transfer files to remote computer + + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + رابط کاربری + + + Language: + زبان + + + Use system language setting + از تنظیمات زبان سیستم استفاده کنید + + + Veyon + ویون + + + Logging + ورود به سیستم + + + Log file directory + دایرکتوری فایل ثبت فعالیت + + + Log level + سطح ورود + + + Nothing + هیچ چی + + + Only critical messages + فقط پیام های بحرانی + + + Errors and critical messages + خطاها و پیام های بحرانی + + + Warnings and errors + هشدارها و خطاها + + + Information, warnings and errors + اطلاعات، هشدارها و خطاها + + + Debug messages and everything else + پیام های اشکال زدایی و هر چیز دیگری + + + Limit log file size + محدود کردن اندازه فایل ورودی + + + Clear all log files + پاک کردن تمام ثبت فعالیت + + + Log to standard error output + ثبت گزارش به خروجی استاندارد خطا + + + Network object directory + + + + Backend: + + + + Update interval: + + + + %1 service + + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + + + + All log files were cleared successfully. + + + + Error + + + + Could not remove all log files. + + + + MB + مگابایت + + + Rotate log files + + + + x + + + + seconds + + + + Write to logging system of operating system + + + + Authentication + + + + Method: + + + + Logon authentication + ورود احراز هویت شده ها + + + Key file authentication + فایل کلید احراز هویت + + + Test + تست + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + + + + + LdapConfigurationPage + + Basic settings + + + + General + عمومی + + + LDAP server and port + + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + + + + e.g. OU=Computers + + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + + LDAP base DN test failed + + + + LDAP base DN test successful + + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + نام کاربری را وارد کنید + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + + + + Please enter a group name whose members to query: + + + + group members + + + + Group not found + + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + + + + users + + + + user groups + + + + computer groups + + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + + + + LDAP %1 test failed + + + + LDAP %1 test successful + + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + + + + TLS certificate verification + + + + System defaults + + + + Never (insecure!) + + + + Custom CA certificate file + + + + None + + + + TLS + + + + SSL + + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + تست + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + نمایش راهنما در مورد این فرمان + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + + + + enter search filter... + + + + + MainToolBar + + Configuration + + + + Disable balloon tooltips + + + + Show icons only + + + + + MainWindow + + MainWindow + + + + toolBar + + + + General + عمومی + + + &File + + + + &Help + + + + &Quit + + + + Ctrl+Q + + + + Ctrl+S + + + + L&oad settings from file + + + + Ctrl+O + + + + About Qt + + + + Authentication impossible + + + + Configuration not writable + + + + Load settings from file + + + + Save settings to file + + + + Unsaved settings + + + + There are unsaved settings. Quit anyway? + + + + Veyon Configurator + + + + Service + + + + Master + + + + Access control + + + + About Veyon + درباره ویون + + + Auto + + + + About + درباره + + + %1 Configurator %2 + + + + JSON files (*.json) + + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + دسترسی غیرمجاز است + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + عکس های گرفته شده + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + تنظیم مجدد پیکربندی + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + + + + Align computers to grid + + + + %1 Configurator + %1تنظیم کننده + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + فهرست راهنما + + + User configuration + پیکربندی کاربران + + + Feature on computer double click: + + + + Features + + + + All features + + + + Disabled features + + + + Screenshots + عکس های گرفته شده + + + <no feature> + + + + Basic settings + + + + Behaviour + + + + Enforce selected mode for client computers + + + + Hide local computer + مخفی کردن کامپیوتر محلی + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + + + + User interface + رابط کاربری + + + Background color + + + + Thumbnail update interval + + + + ms + ام اس + + + Program start + + + + Modes and features + + + + User and computer name + + + + Only user name + + + + Only computer name + + + + Computer thumbnail caption + + + + Text color + + + + Sort order + + + + Computer and user name + + + + Computer locations + + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + ساخته شده در حالت نظارت + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + وبسایت را باز کنید + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + لطفا نشانی اینترنتی وبسایت را باز کنید: + + + Name: + + + + + PasswordDialog + + Username + نام کاربری + + + Password + رمز + + + Veyon Logon + + + + Authentication error + خطا احراز هویت + + + Logon failed with given username and password. Please try again! + + + + Please enter your username and password in order to access computers. + + + + + PowerControlFeaturePlugin + + Power on + + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + + Reboot + + + + Click this button to reboot all computers. + + + + Power down + خاموش + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + + + + Confirm reboot + + + + Confirm power down + + + + Do you really want to reboot the selected computers? + + + + Do you really want to power down the selected computer? + + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + خاموش + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + + + + Open a remote view for a computer without interaction. + + + + Remote control + + + + Open a remote control window for a computer. + + + + Remote access + + + + Remote view or control a computer + + + + Please enter the hostname or IP address of the computer to access: + لطفا نام میزبان یا آدرس آی پی کامپیوتر را وارد کنید: + + + Show help about command + نمایش راهنما در مورد این فرمان + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + فقط مشاهده + + + Remote control + + + + Send shortcut + + + + Fullscreen + + + + Window + + + + Ctrl+Alt+Del + + + + Ctrl+Esc + + + + Alt+Tab + + + + Alt+F4 + + + + Win+Tab + + + + Win + ویندوز + + + Menu + فهرست + + + Alt+Ctrl+F1 + + + + Connecting %1 + اتصال٪ 1 + + + Connected. + متصل شد. + + + Screenshot + + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + قفل + + + Unlock + گشودن + + + Lock screen and input devices of a computer + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + + + + Use this function to take a screenshot of selected computers. + + + + Screenshots taken + عکسبرداری از صفحه مانیتور + + + Screenshot of %1 computer have been taken successfully. + تصاویری از رایانه٪ 1 با موفقیت انجام شده است. + + + Take screenshots of computers and save them locally. + عکسبرداری از رایانه های شبکه و ذخیره آنها در محلی مناسب. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + تمام عکسهای گرفته شده توسط شما در اینجا فهرست شده است. شما می توانید عکس ها را با کلیک روی آیتم "Screenshot" در منوی بالایی یک کامپیوتر بگیرید. تصاویر را می توان با استفاده از دکمه های زیر مدیریت کرد. + + + User: + کاربر + + + Computer: + کامپیوتر + + + Date: + تاریخ + + + Time: + زمان + + + Show + نمایش + + + Delete + حذف + + + Screenshot + + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + عمومی + + + Autostart + + + + Hide tray icon + + + + Start service + سرویس فعال شد + + + Stopped + متوقف شده + + + Stop service + سرویس متوقف شد + + + State: + + + + Enable firewall exception + اضافه کردن استثنا به دیوار آتش + + + Allow connections from localhost only + اجازه ارتباط فقط برای کامپیوتر محلی + + + VNC server + سرور vnc + + + Plugin: + پلاگین : + + + Restart %1 Service + راه اندازی مجدد٪ 1 سرویس + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + تمام تنظیمات با موفقیت ذخیره شدند. برای به تأخیر انداختن سرویس٪ 1 باید مجددا راه اندازی شود. دوباره راه اندازی کنید؟ + + + Running + در حال اجرا + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + نمایش سرور + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + + + + Stopping service %1 + + + + Registering service %1 + + + + Unregistering service %1 + + + + Service control + + + + + ServiceControlPlugin + + Service is running + سرویس در حال اجرا است + + + Service is not running + سرویس اجرا نمی شود + + + Configure and control Veyon service + پیکربندی و کنترل سرویس ویون + + + Register Veyon Service + ثبت سرویس های ویون + + + Unregister Veyon Service + عدم ثبت سرویس های ویون + + + Start Veyon Service + شروع سرویس ویون + + + Stop Veyon Service + توقف سرویس ویون + + + Restart Veyon Service + راه اندازی مجدد سرویس ویون + + + Query status of Veyon Service + بررسی وضعیت سرویس ویون + + + Commands for configuring and controlling Veyon Service + شامل فرمان هایی برای پیکربندی و کنترل سرویس ویون + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + آیکن کنار ساعت + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + ارسال پیام متنی + + + Use the field below to type your message which will be sent to all selected users. + از فیلد زیر برای تایپ پیام خود استفاده کنید که به تمامی کاربران انتخاب شده ارسال می شود. + + + + TextMessageFeaturePlugin + + Text message + پیام متنی + + + Use this function to send a text message to all users e.g. to assign them new tasks. + برای ارسال یک پیام متنی به همه کاربران از این تابع استفاده کنید، برای مثال آنها را به وظایف جدید اختصاص دهید. + + + Message from teacher + پیام از معلم + + + Send a message to a user + ارسال یک پیام به یک کاربر + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + ضبط کردن پنجره های لایه ای (نیمه شفاف) را فعال کنید + + + Poll full screen (leave this enabled per default) + نظرسنجی تمام صفحه (این را در پیشفرض فعال کنید) + + + Low accuracy (turbo mode) + دقت پایین (حالت توربو) + + + Builtin UltraVNC server configuration + + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + عدم دسترسی نوشتن + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + تنظیمات شخصی شما ذخیره نشد لطفا مسیر پرونده پیکربندی کاربر را با استفاده از پیکربندی ٪ 1 بررسی کنید. + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + نام کاربری + + + Password + رمز + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + کنترل جلسه کاربر + + + + VeyonCore + + [OK] + [تایید] + + + [FAIL] + [عدم موفقیت] + + + Invalid command! + دستور نا معتبر! + + + Available commands: + دستورات موجود: + + + Invalid arguments given + استدلال های نامعتبر داده شده است. + + + Not enough arguments given - use "%1 help" for more information + استدلال های کافی نیست - برای اطلاعات بیشتر از "٪ 1 کمک" استفاده کنید + + + Unknown result! + نتیجه ناشناخته! + + + Available modules: + ماژول های موجود: + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + + + + INFO + + + + ERROR + + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + + + + + VncViewWidget + + Establishing connection to %1 ... + ایجاد اتصال به٪ 1 ... + + + + WebApiConfigurationPage + + Web API + + + + General + عمومی + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + اس + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + تنظیمات نسل SAS توسط نرم افزار امکان پذیر نیست. ارسال Ctrl + Alt + Del از طریق کنترل از راه دور کار نخواهد کرد! + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + عمومی + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + پلاگین اجرای توابع انتزاعی برای پلت فرم ویندوز + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + x11vnc پیکربندی داخلی سرور + + + Custom x11vnc parameters: + پارامترهای x11vnc سفارشی: + + + Do not use X Damage extension + از X Damage extension استفاده نکنید + + + diff --git a/translations/veyon_fr.ts b/translations/veyon_fr.ts new file mode 100644 index 0000000..2bd23d5 --- /dev/null +++ b/translations/veyon_fr.ts @@ -0,0 +1,4083 @@ + + + + + AboutDialog + + About + À propos + + + Translation + Traduction + + + License + Licence + + + About Veyon + À propos de Veyon + + + Contributors + Contributeurs + + + Version: + Version: + + + Website: + Site Internet: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Veyon est actuellement traduit dans votre langue natale . + +Cependant, si vous êtes intéressé pour traduire Veyon dans une autre langue, ou si vous souhaitez améliorer la traduction existante, merci de contacter l'équipe des développeurs de Veyon ! + + + About %1 %2 + A propos %1 %2 + + + Support Veyon project with a donation + Soutenir le projet Veyon avec un don + + + + AccessControlPage + + Computer access control + Contrôle d'accès ordinateur + + + Grant access to every authenticated user (default) + Accorder l'accès à tous les utilisateurs authentifiés (par défaut) + + + Test + Test + + + Process access control rules + Procéder avec des règles de contrôle d'accès + + + User groups authorized for computer access + Groupes des utilisateurs autorisés à se connecter aux ordinateurs + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Veuillez ajouter les groupes dont les membres sont autorisés à accéder aux ordinateurs du réseau Veyon. + + + Authorized user groups + Groupe d'utilisateurs autorisés + + + All groups + Tous les groupes + + + Access control rules + Règles du contrôle d'accès + + + Add access control rule + Ajouter une règle de contrôle d'accès + + + Remove access control rule + Retirer une règle de contrôle d'accès + + + Move selected rule down + Déplacer la règle sélectionnée vers le bas + + + Move selected rule up + Déplacer la règle sélectionnée vers le haut + + + Edit selected rule + Éditer la règle sélectionnée + + + Enter username + Entrer votre nom d'utilisateur + + + Please enter a user login name whose access permissions to test: + Pour tester, veuillez entrer un identifiant qui possède des droits d'accès: + + + Access allowed + Accès autorisé + + + The specified user is allowed to access computers with this configuration. + Avec cette configuration, l'utilisateur spécifié est autorisé à accéder aux ordinateurs. + + + Access denied + Accès refusé + + + The specified user is not allowed to access computers with this configuration. + Avec cette configuration, l'utilisateur spécifié n'est pas autorisé à accéder aux ordinateurs. + + + Enable usage of domain groups + Activer l'utilisation des groupes de domaine + + + User groups backend: + Groupes d'utilisateurs de processus d'arrière plan: + + + Missing user groups backend + Groupes d'utilisateurs de processus d'arrière plan manquants + + + No default user groups plugin was found. Please check your installation! + Aucun greffon par défaut pour les groupes d'utilisateurs n'a été trouvé. Veuillez vérifier votre installation! + + + Restrict access to members of specific user groups + Restreindre l'accès aux membres de groupes d'utilisateurs spécifiques + + + + AccessControlRuleEditDialog + + Edit access control rule + Éditer une règle de contrôle d'accès + + + General + Général + + + enter a short name for the rule here + Entrer ici un titre ou un mot clé pour identifier la règle + + + Rule name: + Nom de la règle: + + + enter a description for the rule here + Entrer ici une description sommaire de l'action effectuée par la règle + + + Rule description: + Description de la règle: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Inverser toutes les conditions ("est" sera interprété par "n'est pas") + + + Conditions + Conditions + + + is member of group + est membre du groupe + + + Accessing computer is localhost + L'ordinateur accédant est l'hôte local + + + Accessing user is logged on user + L'utilisateur accédant est identifié en utilisateur + + + Accessing user is already connected + L'utilisateur accédant est déjà connecté + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Si plus d'une condition est validée, chaque condition doit être en adéquation avec les autres afin que la règle puisse s'appliquer (ET Logique). Si seulement une condition sur plusieurs est validée (OU Logique) optez plutôt pour la création de plusieurs règles de contrôle d'accès. + + + Action + Action + + + Allow access + Autoriser l'accès + + + Deny access + Refuser l'accès + + + Ask logged on user for permission + Demander la permission à l'utilisateur connecté + + + None (rule disabled) + Aucune (Règle désactivée) + + + Accessing user + L'utilisateur accédant + + + Accessing computer + L'ordinateur accédant + + + Local (logged on) user + L'utilisateur local (connecté) + + + Local computer + L'ordinateur local + + + Always process rule and ignore conditions + Toujours appliquer la règle et ignorer les conditions + + + No user logged on + Aucun utilisateur connecté + + + Accessing user has one or more groups in common with local (logged on) user + L'utilisateur accédant est membre d'un ou plusieurs groupes en commun avec l'utilisateur local (connecté) + + + Accessing computer and local computer are at the same location + L'accès à l'ordinateur et l'ordinateur local se trouvent au même endroit + + + is located at + est situé à + + + + AccessControlRulesTestDialog + + Access control rules test + Test des règles de contrôle d'accès + + + Accessing user: + Utilisateur accédant: + + + Local computer: + Ordinateur local: + + + Accessing computer: + Ordinateur accédant: + + + Please enter the following user and computer information in order to test the configured ruleset. + Veuillez entrer l'utilisateur et l'ordinateur afin de tester le groupe de règles configurées + + + Local user: + Utilisateur local: + + + Connected users: + Utilisateurs connectés + + + The access in the given scenario is allowed. + L'accès est autorisée avec le scénario donné. + + + The access in the given scenario is denied. + L'accès est refusée avec le scénario donné. + + + The access in the given scenario needs permission of the logged on user. + L'accès dans le scénario donné demande la permission à l'utilisateur connecté. + + + ERROR: Unknown action + ERREUR: Action inconnue + + + Test result + Résultat du test + + + + AuthKeysConfigurationPage + + Authentication keys + Clés d'authentification + + + Introduction + Introduction + + + Key file directories + Répertoires de fichier clé + + + Public key file base directory + Répertoire de base des clés d'accès publiques + + + Private key file base directory + Répertoire de base des clés d'accès privées + + + Available authentication keys + Clés d'authentification disponibles + + + Create key pair + Créer paire de clé + + + Delete key + Supprimer clé + + + Import key + Importer clé + + + Export key + Exporter clé + + + Set access group + Établir groupe d'accès + + + Key files (*.pem) + Fichiers clé (*.pem) + + + Authentication key name + Nom de la clé d'authentification + + + Please enter the name of the user group or role for which to create an authentication key pair: + Entrer le nom du groupe d'utilisateurs ou du rôle pour lequel se fait la création de la paire de clé d'authentification: + + + Do you really want to delete authentication key "%1/%2"? + Souhaitez-vous réellement supprimer la clé d'authentification "%1%2" ? + + + Please select a key to delete! + Veuillez sélectionner une clé à supprimer ! + + + Please enter the name of the user group or role for which to import the authentication key: + Entrez le nom du groupe d'utilisateurs ou du rôle pour lequel se fait l'import de la clé d'authentification: + + + Please select a key to export! + Veuillez sélectionner une clé à exporter ! + + + Please select a user group which to grant access to key "%1": + Veuillez sélectionner un groupe d'utilisateur pour lequel vous accordez l'accès à la clé "%1": + + + Please select a key which to set the access group for! + Veuillez sélectionner une clé pour accorder l'accès groupe ! + + + Please perform the following steps to set up key file authentication: + Veuillez suivre les indications suivantes pour configurer la clé d'authentification: + + + 1) Create a key pair on the master computer. + 1) Créer une paire de clé sur l'ordinateur maître. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Définissez un groupe d'accès dont les membres sont autorisés pour accéder aux autres ordinateurs. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Exporter la clé publique et importer la avec le même nom sur tous les ordinateurs clients . + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Veuillez vous référer au <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Manuel Administrateur Veyon</a> pour plus d'informations. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Une paire de clé d'authentification, consiste en deux clés cryptographiques couplées, une clé privée et une autre publique. +La clé privée permet aux utilisateurs de l'ordinateur maitre d'accéder aux ordinateurs clients. +Il est important que seuls les utilisateurs autorisés aient l'accès en lecture sur la clé privée. +La clé publique est utilisée sur les ordinateurs clients pour l'authentification des demandes de connexions. + + + + AuthKeysManager + + Please check your permissions. + Veuillez vérifier vos permissions. + + + Key name contains invalid characters! + Le nom de la clé contient des caractères non valides ! + + + Invalid key type specified! Please specify "%1" or "%2". + Le type de clé spécifiée est non valide! Veuillez spécifier "%1" ou "%2". + + + Specified key does not exist! Please use the "list" command to list all installed keys. + La clé spécifiée n'existe pas! Veuillez utiliser la commande "liste" pour examiner l'ensemble des clés installées. + + + One or more key files already exist! Please delete them using the "delete" command. + Une ou plusieurs clés existent déjà! Veuillez les supprimer en utilisant la commande "supprimer". + + + Creating new key pair for "%1" + Création d'une nouvelle paire de clé pour "%1" + + + Failed to create public or private key! + Impossible de créer une clé publique ou privée ! + + + Newly created key pair has been saved to "%1" and "%2". + La paire de clé nouvellement créée a été sauvé en "%1" et "%2". + + + Could not remove key file "%1"! + Impossible de retirer le fichier clé "%1" ! + + + Could not remove key file directory "%1"! + Impossible de retirer le répertoire clé "%1" ! + + + Failed to create directory for output file. + Impossible de créer le répertoire pour le fichier de sortie. + + + File "%1" already exists. + Le fichier "%1" existe déjà. + + + Failed to write output file. + L'écriture du fichier de sortie à échoué. + + + Key "%1/%2" has been exported to "%3" successfully. + La clé "%1%2" a été exportée avec succès en "%3". + + + Failed read input file. + L'écriture du fichier d'entrée à échoué. + + + File "%1" does not contain a valid private key! + Le fichier "%1" ne contient pas de clé privée valide ! + + + File "%1" does not contain a valid public key! + Le fichier "%1" ne contient pas de clé publique valide ! + + + Failed to create directory for key file. + Impossible de créer le répertoire pour le fichier clé. + + + Failed to write key file "%1". + L'écriture du fichier clé "%1" à échoué. + + + Failed to set permissions for key file "%1"! + La mise en place des permissions du fichier clé "%1" a échoué ! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + La clé "%1%2" a été importée avec succès. Veuillez vérifier les permissions de "%3" afin de prévenir un accès non autorisé. + + + Failed to convert private key to public key + La conversion de la clé privée en clé publique a échoué + + + Failed to create directory for private key file "%1". + Impossible de créer le répertoire pour la clé privée "%1" ! + + + Failed to save private key in file "%1"! + Impossible de sauver la clé privée dans le fichier "%1" ! + + + Failed to set permissions for private key file "%1"! + La mise en place des permissions de la clé privée "%1" a échoué ! + + + Failed to create directory for public key file "%1". + Impossible de créer le répertoire pour la clé publique "%1" ! + + + Failed to save public key in file "%1"! + Impossible de sauver la clé publique dans le fichier "%1" ! + + + Failed to set permissions for public key file "%1"! + La mise en place des permissions de la clé publique "%1" a échoué ! + + + Failed to set owner of key file "%1" to "%2". + Impossible de définir le propriétaire de la clé "%1" vers "%2". + + + Failed to set permissions for key file "%1". + La mise en place des permissions de la clé "%1" a échoué ! + + + Key "%1" is now accessible by user group "%2". + La clé "%1" est maintenant accessible par le groupe utilisateur "%2". + + + <N/A> + < Sans Objet > + + + Failed to read key file. + La lecture du fichier clé à échoué. + + + + AuthKeysPlugin + + Create new authentication key pair + Créer une nouvelle paire de clé d'authentification + + + Delete authentication key + Supprimer clés d'authentification + + + List authentication keys + Lister les clés d'authentification + + + Import public or private key + Importer une clé publique ou privée + + + Export public or private key + Exporter une clé publique ou privée + + + Extract public key from existing private key + Extraire une clé publique à partir d'une clé privée existante + + + Set user group allowed to access a key + Configure l'autorisation d'un groupe utilisateur pour accéder à une clé. + + + KEY + CLÉ + + + ACCESS GROUP + GROUPE D'ACCÈS + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + Cette commande ajuste les permissions du fichier d'accès vers <KEY> de sorte que seul le groupe d'utilisateurs <ACCESS GROUP> dispose d'un accès en lecture. + + + NAME + NOM + + + FILE + FICHIER + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Cette commande exporte la clé d'authentification <KEY> vers <FILE>. Si <FILE> n'est pas spécifié un nom sera construit à partir du nom et du type de <KEY>. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Cette commande importe la clé d'authentification <KEY> à partir de <FILE>. Si <FILE> n'est pas spécifié un nom sera construit à partir du nom et du type de <KEY>. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + Cette commande liste toutes les clés d'authentification disponibles dans le répertoire des clés configuré. Si l'option "%1" est spécifiée, un tableau avec les détails de la clé sera affiché à la place. Certaines informations peuvent être manquantes si une clé n'est pas accessible ex: en cas d'un manque de permission en lecture. + + + Please specify the command to display help for! + Veuillez spécifier la commande pour afficher l'aide correspondante ! + + + TYPE + TYPE + + + PAIR ID + ID PAIRE + + + Command line support for managing authentication keys + Prise en charge de la ligne de commande pour la gestion des clés d'authentification + + + Commands for managing authentication keys + Commandes pour gérer les clés d'authentification + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + Cette commande crée une nouvelle paire de clés d'authentification avec comme nom <NAME> et sauve les clés privée et publique dans le répertoire des clés configuré. Le paramètre doit être un nom pour la clé, qui ne peut contenir que des lettres. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + Cette commande supprime la clé d’authentification <KEY> depuis le répertoire de clé configuré. Veuillez noter qu'une clé ne peut pas être récupérée une fois qu'elle a été supprimée. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + Cette commande extrait la partie clé publique à partir de la clé privée <KEY> et l'enregistre comme la clé publique correspondante. Lors de la configuration d'un autre ordinateur maître, il suffit simplement de ne transférer que la clé privée. La clé publique pourra par la suite être extraite. + + + + AuthKeysTableModel + + Name + Nom + + + Type + Type + + + Access group + Groupe d'accès + + + Pair ID + Paire d'identification (ID) + + + + BuiltinDirectoryConfigurationPage + + Computers + Ordinateurs + + + Name + Nom + + + Host address/IP + Adresse hôte / IP + + + MAC address + Adresse MAC + + + Add new computer + Ajouter un nouvel ordinateur + + + Remove selected computer + Retirer l'ordinateur selectionné + + + New computer + Nouvel ordinateur + + + Builtin directory + Répertoire intégré + + + Locations & computers + Emplacements & ordinateurs + + + Locations + Emplacements + + + Add new location + Ajouter un nouvel emplacement + + + Remove selected location + Retirer l'emplacement sélectionné + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + L'import de fichiers CSV est possible à travers l'interface en ligne de commande. Pour plus d'informations, voir la <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">documentation en ligne</a>. + + + New location + Nouvel emplacement + + + + BuiltinDirectoryPlugin + + Show help for specific command + Affiche l'aide pour une commande spécifique + + + Import objects from given file + Importer des objets d'un fichier donné + + + Export objects to given file + Exporter des objets vers un fichier donné + + + Invalid type specified. Valid values are "%1" or "%2". + Le type spécifié est non valide! Les valeurs convenables sont "%1" ou "%2". + + + Type + Type + + + Name + Nom + + + Host address + Adresse hôte + + + MAC address + Adresse MAC + + + Specified object not found. + Objet spécifié n'a pas été trouvé. + + + File "%1" does not exist! + Le fichier "%1" n'existe pas. + + + Can't open file "%1" for reading! + Impossible d'ouvrir le fichier "%1" pour une lecture ! + + + Unknown argument "%1". + Argument inconnu "%1". + + + Computer "%1" (host address: "%2" MAC address: "%3") + Ordinateur "%1" (adresse hôte: "%2" adresse MAC: "%3") + + + Unclassified object "%1" with ID "%2" + Objet inclassable "%1" avec comme numéro d'identification "%2" + + + None + Aucun + + + Computer + Ordinateur + + + Root + Racine + + + Invalid + Invalide + + + Error while parsing line %1. + Erreur pendant l'analyse de la ligne %1. + + + Network object directory which stores objects in local configuration + Répertoire d'objets réseau qui stocke des objets dans la configuration locale + + + Commands for managing the builtin network object directory + Commandes de gestion du répertoire d'objets réseau intégré + + + No format string or regular expression specified! + Aucune chaîne de format ou d'expression régulière spécifiée! + + + Can't open file "%1" for writing! + Impossible d'ouvrir le fichier "%1" pour l'écriture ! + + + No format string specified! + Aucune chaîne de format spécifiée! + + + Object UUID + UUID Objet + + + Parent UUID + UUID Parent + + + Add a location or computer + Ajouter un emplacement ou un ordinateur + + + Clear all locations and computers + Vider tous les emplacements et les ordinateurs + + + Dump all or individual locations and computers + Décharger tout ou partie des emplacements et ordinateurs + + + List all locations and computers + Lister tous les emplacements et les ordinateurs + + + Remove a location or computer + Retirer un emplacement ou un ordinateur + + + Location "%1" + Emplacement "%1" + + + Builtin (computers and locations in local configuration) + Intégré (ordinateurs et emplacements dans la configuration locale) + + + Location + Emplacement + + + FILE + FICHIER + + + LOCATION + EMPLACEMENT + + + FORMAT-STRING-WITH-PLACEHOLDERS + FORMAT-CHAINE-AVEC-MÉTACARACTÈRES + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + EXPRESSION-RÉGULIÈRE-AVEC-MÉTACARACTÈRE + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Importe des objets à partir d'un fichier texte spécifique utilisant la chaîne de caractères donnée ou une expression régulière contenant un ou plusieurs jokers. Les jokers valides sont: %1 + + + Import simple CSV file to a single room + Importer un fichier CSV simple vers une salle + + + Import CSV file with location name in first column + Importer un fichier CSV avec le nom de l'emplacement dans la première colonne + + + Import text file with with key/value pairs using regular expressions + Importer un fichier texte comportant des paires clé/valeur utilisant une expression régulière + + + Import arbitrarily formatted data + Importer des données formatées de manière arbitraire + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Exporte les objets vers un fichier texte spécifique utilisant la chaîne de caractères donnée contenant un ou plusieurs jokers. Les jokers valides sont: %1 + + + Export all objects to a CSV file + Exporter tous les objets vers un fichier CSV + + + Export all computers in a specific location to a CSV file + Exporter tous les ordinateurs d'un emplacement spécifique vers un fichier CSV + + + TYPE + TYPE + + + NAME + NOM + + + PARENT + PARENT + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + Ajouter un objet où %1 peut être un de "%2" ou de "%3". %4 peut être spécifié par le nom ou l'UUIDD. + + + Add a room + Ajouter une salle + + + Add a computer to room %1 + Ajouter un ordinateur vers l'emplacement %1 + + + OBJECT + OBJET + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Retire l'objet spécifié du répertoire. %1 peut être spécifié par un nom ou un UUID. Retirer un emplacement supprimera également tous les ordinateurs qui y sont inclus. + + + Remove a computer by name + Retirer un ordinateur à partir de son nom + + + Remove an object by UUID + Retirer un objet à partir de son UUID + + + "Room 01" + "Salle 01" + + + "Computer 01" + "Ordinateur 01" + + + HOST ADDRESS + ADRESSE HÔTE + + + MAC ADDRESS + ADRESSE MAC + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Serveur VNC intégré (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Serveur VNC intégré (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Adresse de l'hôte / IP: %1 + + + Active features: %1 + Fonctionnalités activées: %1 + + + Online and connected + En ligne et connecté + + + Establishing connection + Établissement de la connexion + + + Computer offline or switched off + Ordinateur hors ligne ou éteint + + + Authentication failed or access denied + L'authentification a échouée ou l'accès est refusé + + + Disconnected + Déconnecté + + + No user logged on + Aucun utilisateur connecté + + + Logged on user: %1 + Connecté sur l'utilisateur: %1 + + + Location: %1 + Emplacement: %1 + + + Veyon Server unreachable or not running + Serveur Veyon inaccessible ou ne fonctionne pas + + + [no user] + [aucun utilisateur] + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + Service %1 %2 sur %3:%4 + + + Authentication error + Erreur d'authentification + + + Remote access + Accès à distance + + + User "%1" at host "%2" is now accessing this computer. + L'utilisateur "%1" sur l'hôte "%2" est en train d’accéder à cet ordinateur. + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + L'utilisateur "%1" sur l'hôte "%2" a tenté d'accéder à cet ordinateur mais n'a pas réussi à s'authentifier avec succès. + + + Access control error + Erreur de contrôle d'accès + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + L'utilisateur "%1" sur l'hôte "%2" a tenté d'accéder à cet ordinateur, mais a été bloqué en raison des paramétrages du contrôle d'accès. + + + Active connections: + Connexions actives: + + + + ComputerManager + + User + Utilisateur + + + Missing network object directory plugin + Contenu du répertoire d'objet réseau manquant + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Aucune extension de répertoire d'objet réseau par défaut n'a été trouvée. Veuillez vérifier votre installation ou configurer un processus d'annuaire d'objets réseau différent via la console de gestion %1. + + + Location detection failed + La détection de l'emplacement a échoué + + + Computer name;Hostname;User + Nom de l'ordinateur;Nom de l’hôte;Utilisateur + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + Impossible de déterminer l'emplacement de cet ordinateur. Cela indique un problème de configuration du système. A la place, tous les emplacements seront affichés dans le panneau de sélection de l'ordinateur. + + + + ComputerSelectPanel + + Computer search + Recherche d'ordinateur + + + Add location + Ajouter un emplacement + + + Save computer/user list + Sauver la liste ordinateur / utilisateur + + + Select output filename + Sélectionner le nom du fichier de sortie + + + CSV files (*.csv) + Fichiers CSV (*.csv) + + + File error + Erreur de fichier + + + Could not write the computer and users list to %1! Please check the file access permissions. + Impossible d'écrire la liste des ordinateurs et des utilisateurs dans %1! Vérifiez le fichier des permissions d'accès. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Veuillez spécifier un fichier de configuration existant à importer. + + + Please specify a valid filename for the configuration export. + Veuillez spécifier un nom de fichier valide pour l'export de la configuration. + + + Please specify a valid key. + Veuillez spécifier une clé valide. + + + Specified key does not exist in current configuration! + La clé spécifiée n'existe pas dans la configuration courante! + + + Please specify a valid value. + Veuillez spécifier une valeur valide. + + + Configure Veyon at command line + Configurer Veyon en ligne de commande + + + Output file is not writable! + Le fichier de sortie ne peut être écrit! + + + Output directory is not writable! + Le répertoire de sortie ne peut être écrit + + + Configuration file is not readable! + Le fichier de configuration n'est pas lisible! + + + Clear system-wide Veyon configuration + Purger la configuration Veyon de tout le système + + + List all configuration keys and values + Lister toutes les clés et paramètres de configuration + + + Import configuration from given file + Importer la configuration à partir d'un fichier + + + Export configuration to given file + Exporter la configuration dans un fichier + + + Read and output configuration value for given key + Lire et extraire la configuration pour une clé donnée + + + Write given value to given configuration key + Écrire un paramètre dans une clé de configuration donnée + + + Unset (remove) given configuration key + Désactiver (retirer) une clé de configuration donnée + + + Commands for managing the configuration of Veyon + Commandes pour la gestion de la configuration de Veyon + + + Upgrade and save configuration of program and plugins + Mettre à niveau et enregistrer la configuration du programme et des greffons + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + Impossible de modifier la propriété de démarrage automatique du service %1 + + + Could not configure the firewall configuration for the %1 Server. + Impossible de configurer les paramètres du pare-feu pour le Serveur %1. + + + Could not configure the firewall configuration for the %1 Worker. + Impossible de configurer les paramètres du pare-feu pour la personne %1. + + + Configuration is not writable. Please check your permissions! + La configuration n'est pas accessible en écriture. Veuillez vérifier vos autorisations ! + + + Could not apply platform-specific configuration settings. + Impossible d'appliquer les paramètres de configuration spécifiques à la plateforme. + + + + DemoClient + + %1 Demo + Démo %1 + + + + DemoConfigurationPage + + Demo server + Serveur de démo + + + Tunables + Réglables + + + ms + ms + + + Key frame interval + Intervalle entre les images clés + + + Memory limit + Limite de mémoire + + + MB + MB + + + Update interval + Intervalle d'actualisation + + + s + s + + + Slow down thumbnail updates while demo is running + Ralentir la mise à jour des vignettes lorsque qu'une démo est en cours d'exécution + + + + DemoFeaturePlugin + + Stop demo + Arrêter la démo + + + Window demo + Démo dans une fenêtre + + + Give a demonstration by screen broadcasting + Faire une démonstration par diffusion de l'écran + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + Dans ce mode votre écran est diffusé dans une fenêtre sur tous les autres ordinateurs. Les utilisateurs sont librement en mesure de changer de fenêtre comme ils le souhaitent. + + + Demo + Démo + + + Share your screen or allow a user to share his screen with other users. + Partagez votre écran ou autorisez un utilisateur à partager son écran avec d'autres utilisateurs. + + + Full screen demo + Démo en plein écran + + + Share your own screen in fullscreen mode + Partagez votre propre écran en mode plein écran + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + Dans ce mode, votre écran est affiché en mode plein écran sur tous les ordinateurs tandis que les périphériques d'entrée des utilisateurs sont verrouillés. + + + Share your own screen in a window + Partagez votre propre écran dans une fenêtre + + + Share selected user's screen in fullscreen mode + Partager l'écran de l'utilisateur sélectionné en mode plein écran + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + Dans ce mode, l'écran de l'utilisateur sélectionné est affiché en mode plein écran sur tous les ordinateurs tandis que les périphériques d'entrée des utilisateurs sont verrouillés. + + + Share selected user's screen in a window + Partager l'écran de l'utilisateur sélectionné dans une fenêtre + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + Dans ce mode, l'écran de l'utilisateur sélectionné est affiché dans une fenêtre sur tous les ordinateurs. Les utilisateurs peuvent consulter d'autres fenêtres selon leurs besoins. + + + Please select a user screen to share. + Veuillez sélectionner un écran d'utilisateur à partager. + + + Please select only one user screen to share. + Veuillez sélectionner un seul écran utilisateur à partager. + + + All screens + Tous les écrans + + + Screen %1 [%2] + Ecran %1 [%2] + + + + DesktopAccessDialog + + Desktop access dialog + Boîte de dialogue d'accès au bureau + + + Confirm desktop access + Confirmer l'accès au bureau + + + Never for this session + Jamais pour cette session + + + Always for this session + Toujours pour cette session + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + L'utilisateur %1 sur l'ordinateur %2 souhaite accéder à votre bureau. Souhaitez-vous autoriser cet accès ? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programmes et sites internet + + + Predefined programs + Programmes prédéfinis + + + Name + Nom + + + Path + Chemin + + + Add new program + Ajouter un nouveau programme + + + Remove selected program + Retirer le programme sélectionné + + + Predefined websites + Sites internet prédéfinis + + + Remove selected website + Retirer le site internet sélectionné + + + URL + URL + + + New program + Nouveau programme + + + New website + Nouveau site internet + + + + DesktopServicesFeaturePlugin + + Run program + Exécuter un programme + + + Open website + Ouvrir une page internet + + + Click this button to open a website on all computers. + Cliquez sur ce bouton pour ouvrir un site internet sur tous les ordinateurs. + + + Start programs and services in user desktop + Démarrer les programmes et les services dans le bureau utilisateur + + + Click this button to run a program on all computers. + Cliquez sur ce bouton pour exécuter un programme sur tous les ordinateurs. + + + Run program "%1" + Lance le programme "%1" + + + Custom program + Programme personnalisé + + + Open website "%1" + Ouvre le site internet "%1" + + + Custom website + Site internet personnalisé + + + + DocumentationFigureCreator + + Teacher + Enseignant + + + Room %1 + Salle %1 + + + Please complete all tasks within the next 5 minutes. + S'il vous plaît, veuillez terminer toutes vos tâches d'ici les 5 prochaines minutes. + + + Custom website + Site internet personnalisé + + + Open file manager + Ouvrir le gestionnaire de fichiers + + + Start learning tool + Démarrer l'outil d'apprentissage + + + Play tutorial video + Démarrer le didacticiel vidéo + + + Custom program + Programme personnalisé + + + Handout + don + + + Texts to read + Textes à lire + + + generic-student-user + utilisateur-élève-générique + + + + ExternalVncServer + + External VNC server + Serveur VNC Externe + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Configuration du serveur externe VNC + + + Port: + Port: + + + Password: + Mot de passe: + + + + FeatureControl + + Feature control + Contrôle des fonctionnalités + + + + FileTransferConfigurationPage + + File transfer + Transfert de fichier + + + Directories + Répertoires + + + Destination directory + Répertoire de destination + + + Default source directory + Répertoire source par défaut + + + Options + Options + + + Remember last source directory + Se souvenir du dernier répertoire source + + + Create destination directory if it does not exist + Créer un répertoire de destination s'il n'existe pas + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + impossible d'ouvrir le fichier "%1" en lecture! Veuillez vérifier les permissions! + + + + FileTransferDialog + + File transfer + Transfert de fichier + + + Options + Options + + + Transfer only + Transférer uniquement + + + Transfer and open file(s) with associated program + Transférer et ouvrir le(s) fichier(s) avec les programmes associés + + + Transfer and open destination folder + Transférer et ouvrir le répertoire de destination + + + Files + Fichiers + + + Start + Démarrer + + + Overwrite existing files + Écraser les fichiers existants + + + + FileTransferPlugin + + File transfer + Transfert de fichier + + + Click this button to transfer files from your computer to all computers. + Cliquez sur ce bouton pour transférer des fichiers de votre ordinateur vers tous les ordinateurs. + + + Select one or more files to transfer + Sélectionner un ou plusieurs fichiers à transférer + + + Transfer files to remote computer + Transférer des fichiers sur un ordinateur distant + + + Received file "%1". + Réception du fichier "%1" + + + Could not receive file "%1" as it already exists. + Impossible de recevoir le fichier "%1" puisqu'il existe déjà. + + + Could not receive file "%1" as it could not be opened for writing! + Impossible de recevoir le fichier "%1" puisqu'il ne peut pas être ouvert en écriture! + + + + GeneralConfigurationPage + + User interface + Interface de l'utilisateur + + + Language: + Langue: + + + Use system language setting + Utiliser les paramètres de langage du système + + + Veyon + Veyon + + + Logging + Journalisation + + + Log file directory + Répertoire du fichier de journalisation + + + Log level + Niveau de journalisation + + + Nothing + Ne rien consigner + + + Only critical messages + Seulement les messages critiques + + + Errors and critical messages + Les erreurs et les messages critiques + + + Warnings and errors + Les alertes et les erreurs + + + Information, warnings and errors + Les informations, les alertes et les erreurs + + + Debug messages and everything else + Les messages de débogage et tout le reste... + + + Limit log file size + Taille maximum du fichier de journalisation + + + Clear all log files + Purger tous les fichiers de journalisation + + + Log to standard error output + Journaliser via la sortie standard + + + Network object directory + Répertoire objets réseau + + + Backend: + Méthode de fonctionnement: + + + Update interval: + Intervalle de rafraichissement: + + + %1 service + Service %1 + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + Le service %1 a besoin d'être temporairement arrêté afin de supprimer les fichiers de journalisation. Souhaitez vous pour continuer ? + + + Log files cleared + Fichiers de journalisation vidés + + + All log files were cleared successfully. + Tous les fichiers de journalisation ont été vidés avec succès. + + + Error + Erreur + + + Could not remove all log files. + Impossible de supprimer tous les fichiers de journalisation. + + + MB + MB + + + Rotate log files + Rotation des fichiers de journalisation + + + x + x + + + seconds + secondes + + + Write to logging system of operating system + Écrire dans le système de journalisation du système d'exploitation + + + Authentication + Authentification + + + Method: + Méthode: + + + Logon authentication + Authentification par identifiant + + + Key file authentication + Authentification par un fichier clé + + + Test + Test + + + Authentication is set up properly on this computer. + L'authentification est correctement configurée sur cet ordinateur. + + + Authentication keys are not set up properly on this computer. + Les clés d'authentification ne sont pas configurées correctement sur cet ordinateur. + + + Authentication test + Test d'authentification + + + + HeadlessVncServer + + Headless VNC server + Serveur VNC sans périphériques + + + + LdapBrowseDialog + + Browse LDAP + Parcourir LDAP + + + + LdapClient + + LDAP error description: %1 + Description de l'erreur LDAP: %1 + + + + LdapConfigurationPage + + Basic settings + Paramètres généraux + + + General + Général + + + LDAP server and port + Port et serveur LDAP + + + Bind DN + Liaison DN + + + Bind password + Mot de passe lié + + + Anonymous bind + Liaison anonyme + + + Use bind credentials + Utiliser des informations d'identification par liaison + + + Base DN + Base DN + + + Fixed base DN + Base DN Fixe + + + e.g. dc=example,dc=org + ex: dc=exemple, dc=org + + + Discover base DN by naming context + Explorer la base DN à partir d'un contexte de nommage + + + e.g. namingContexts or defaultNamingContext + ex: ContextesNommés ou ContexteNomméParDéfaut + + + Environment settings + Paramètres d'environnement + + + Object trees + Arborescence objet + + + Computer tree + Arborescence ordinateur + + + e.g. OU=Groups + ex: OU=Groupes + + + User tree + Arborescence utilisateur + + + e.g. OU=Users + ex: OU=Utilisateurs + + + e.g. OU=Computers + ex: OU=Ordinateurs + + + Group tree + Arborescence groupe + + + Perform recursive search operations in object trees + Effectuer des recherches récursives dans les arborescences objet + + + Object attributes + Attributs d'objet + + + e.g. hwAddress + ex: Adressehw + + + e.g. member or memberUid + ex: membre ou surnom + + + e.g. dNSHostName + ex: NomHotedNS + + + Computer MAC address attribute + Attribut de l'adresse MAC de l'ordinateur + + + Group member attribute + Attribut du groupe membre + + + e.g. uid or sAMAccountName + ex: uid ou sAMAccountName + + + Advanced settings + Paramétrages avancés + + + Optional object filters + Filtres d'objet optionnel + + + Filter for user groups + Filtre pour les groupes utilisateur + + + Filter for users + Filtre pour les utilisateurs + + + Filter for computer groups + Filtre pour les groupes d'ordinateurs + + + Group member identification + Identification du groupe membre + + + Distinguished name (Samba/AD) + Nom distinct (Samba/AD) + + + List all groups of a user + Lister tous les groupes d'un utilisateur + + + List all groups of a computer + Lister tous les groupes d'un ordinateur + + + Get computer object by IP address + Trouver un ordinateur par son adresse IP + + + LDAP connection failed + La connexion LDAP a échoué + + + LDAP bind failed + Échec de la liaison LDAP + + + LDAP bind successful + Liaison LDAP réussie + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + La connexion au serveur LDAP ainsi que la liaison d'authentification LDAP ont été réussis. Les paramètres LDAP de base sont configurés convenablement. + + + LDAP base DN test failed + Échec du test base LDAP DN + + + LDAP base DN test successful + Le test base LDAP DN à réussi + + + LDAP naming context test failed + Échec du test LDAP de nommage par contexte + + + LDAP naming context test successful + Le test LDAP de nommage par contexte à réussi. + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + Le contexte de nommage LDAP à été interrogé avec succès. La base DN suivante à été trouvée: +%1 + + + user tree + arborescence utilisateur + + + group tree + arborescence groupe + + + computer tree + arborescence ordinateur + + + Enter username + Entrer votre nom d'utilisateur + + + Please enter a user login name (wildcards allowed) which to query: + Veuillez entrer un identifiant utilisateur (jokers autorisé ? *) à rechercher: + + + user objects + objets utilisateur + + + Enter group name + Entrer un nom de groupe + + + Please enter a group name whose members to query: + Veuillez entrer le nom d'un groupe dont vous recherchez les membres: + + + group members + membres du groupe + + + Group not found + Groupe introuvable + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + Impossible de trouver un groupe avec le nom "%1". Veuillez vérifier le nom du groupe ou le paramètre de l'arborescence du groupe. + + + Enter computer name + Entrer un nom d'ordinateur + + + computer objects + objets ordinateur + + + Enter computer DN + Entrer le DN de l'ordinateur + + + Please enter the DN of a computer whose MAC address to query: + Veuillez entrer le DN d'un ordinateur dont vous recherchez l'adresse MAC: + + + computer MAC addresses + Adresse MAC de l'ordinateur + + + users + utilisateurs + + + user groups + groupes d'utilisateurs + + + computer groups + groupes d'ordinateurs + + + Please enter a user login name whose group memberships to query: + Veuillez entrer l'identifiant dont vous recherchez les groupes d'appartenance: + + + groups of user + groupes d'utilisateur + + + User not found + Utilisateur introuvable + + + groups of computer + groupes d'ordinateur + + + Computer not found + Ordinateur introuvable + + + Enter computer IP address + Entrer l'adresse IP de l'ordinateur + + + Please enter a computer IP address which to resolve to an computer object: + Veuillez entrer une adresse IP pour trouver un ordinateur: + + + computers + ordinateurs + + + LDAP %1 test failed + Échec du test LDAP %1 + + + LDAP %1 test successful + Le test LDAP %1 à réussi + + + The %1 has been queried successfully and %2 entries were found. + %1 a été interrogé avec succès et %2 entrée(s) ont été trouvée(s). + + + %1 %2 have been queried successfully: + +%3 + %2 %1 a été interrogé avec succès: + +%3 + + + LDAP filter test failed + Échec du test du filtre LDAP + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + Impossible d'interroger %1 en utilisant le filtre configuré. Veuillez vérifier le filtre LDAP pour %1 + +%2 + + + LDAP filter test successful + Le test du filtre LDAP à réussi + + + %1 %2 have been queried successfully using the configured filter. + %2 %1 a été interrogé avec succès en appliquant le filtre. + + + (only if different from group tree) + (seulement si différent de l'arborescence du groupe) + + + Computer group tree + Arborescence groupe ordinateur + + + computer group tree + arborescence groupe ordinateur + + + Filter for computers + Filtre pour les ordinateurs + + + e.g. room or computerLab + ex: salle ou labo + + + Integration tests + Tests d'intégration + + + Computer groups + Groupe d'ordinateurs + + + e.g. name or description + Ex: nom ou description + + + Filter for computer containers + Filtre pour les conteneurs d'ordinateur + + + Computer containers or OUs + Conteneurs d'ordinateur ou bien OU + + + Connection security + Sécurité de connexion + + + TLS certificate verification + Certificat de vérification TLS + + + System defaults + Paramètres par défaut du système + + + Never (insecure!) + Jamais (non sécurisé!) + + + Custom CA certificate file + Fichier personnel de certificat CA + + + None + Aucun + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + ex: (ClasseObjet=ordinateur) + + + e.g. (objectClass=group) + ex: (ClasseObjet=groupe) + + + e.g. (objectClass=person) + ex: (ClasseObjet=personne) + + + e.g. (objectClass=room) or (objectClass=computerLab) + ex: (ClasseObjet=salle) ou (ClasseObjet=Labo) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + ex: (ClasseObjet=container) ou (ClasseObjet=Unité d'Organisation) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + Impossible d'interroger la base DN ainsi configurée. Veuillez vérifier les paramètres de la base DN. + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + La base LDAP DN a été interrogée avec succès. Les entrées suivantes ont été trouvées: + +%1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + Impossible d'interroger la base DN via des contextes de nommage. Veuillez vérifier l'attribut du paramètre de contexte de nommage. + +%1 + + + Certificate files (*.pem) + Fichiers de certificats (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + Impossible de se connecter au serveur LDAP. Veuillez vérifier les paramètres du serveur. + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + Impossible de créer une liaison au serveur LDAP. Veuillez vérifier les paramètres du serveur et les informations d'identification. + +%1 + + + Encryption protocol + Protocole de cryptage + + + Computer location attribute + Attribut d'emplacement d'ordinateur + + + Computer display name attribute + Attribut de nom d'affichage d'ordinateur + + + Location name attribute + Attribut de nom d'emplacement + + + e.g. cn or displayName + ex cn ou Nom d'affichage + + + Computer locations identification + Identification des emplacements d'ordinateur + + + Identify computer locations (e.g. rooms) via: + Identifier des emplacements ordinateur (ex salles) via: + + + Location attribute in computer objects + Attribut d'emplacement dans les objets ordinateur + + + List all entries of a location + Lister toutes les entrées d'un emplacement + + + List all locations + Lister tous les emplacements + + + Enter computer display name + Entrer un nom d'affichage ordinateur + + + Please enter a computer display name to query: + Veuillez entrer un nom d'affichage d'ordinateur à interroger: + + + Enter computer location name + Entrez le nom de l'emplacement de l'ordinateur + + + Please enter the name of a computer location (wildcards allowed): + Veuillez entrer le nom d'un emplacement d'ordinateur (jokers autorisés): + + + computer locations + Emplacements d'ordinateur + + + Enter location name + Entrer le nom de l'emplacement + + + Please enter the name of a location whose entries to query: + Veuillez entrer le nom d'un emplacement dont les entrées sont à interroger. + + + location entries + Entrées d'emplacement + + + LDAP test failed + Le test LDAP a échoué + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + Impossible d'interroger les %1. Veuillez vérifier le(s) paramètre(s) %2 et entrez le nom d'un objet existant. + +%3 + + + and + et + + + LDAP test successful + Le test LDAP a réussi + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + Impossible d'interroger les entrées dans le "%1" configuré. Veuillez vérifier le paramètre "%2". + +%3 + + + Browse + Parcourir + + + Test + Test + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + Noms d'hôtes enregistrés comme des noms de domaine pleinement nommé (FQDN, ex: monhote.exemple.org) + + + Computer hostname attribute + Attribut du nom d'hôte de l'ordinateur + + + Please enter a computer hostname to query: + Veuillez entrer un nom d'ordinateur hôte à rechercher: + + + Invalid hostname + Nom d'hôte invalide + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + Vous avez choisi de stocker les noms d'ordinateurs hôtes comme des noms de domaine pleinement nommé (FQDN) mais vous avez saisi un nom d'hôte sans domaine. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + Vous avez choisi de stocker les noms d'ordinateurs hôtes comme de simple noms d'hôte sans nom de domaine, mais vous avez saisi un nom d'hôte dont une partie est un nom de domaine. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + Impossible de trouver un utilisateur dont le nom est "%1". Vérifiez le nom de l'utilisateur ou du paramètre d'arborescence utilisateur. + + + Enter hostname + Entrer nom d'hôte + + + Please enter a computer hostname whose group memberships to query: + Veuillez entrer un nom d’hôte pour l’ordinateur appartenant au groupe à interroger + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + Impossible de trouver un ordinateur dont le nom d'hôte est "%1". Vérifiez le nom d'hôte de l'ordinateur ou du paramètre d'arborescence ordinateur. + + + Hostname lookup failed + Échec de la recherche du nom d'hôte + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + Impossible de rechercher le nom d'hôte pour l'adresse IP %1. Veuillez vérifier le paramétrage de votre serveur DNS. + + + User login name attribute + Attribut de l'identifiant utilisateur + + + Configured attribute for user login name or computer hostname (OpenLDAP) + Attribut configuré pour l'identifiant utilisateur ou le nom d'hôte de l'ordinateur (OpenLDAP) + + + computer containers + Conteneurs d'ordinateur + + + + LdapPlugin + + Auto-configure the base DN via naming context + Auto configurer la base DN à partir d'un contexte de nommage + + + Query objects from LDAP directory + Requête d'objets depuis l'annuaire LDAP + + + Show help about command + Afficher la commande aide et à propos + + + Commands for configuring and testing LDAP/AD integration + Commandes pour configurer et tester l'intégration LDAP/AD + + + Basic LDAP/AD support for Veyon + Support LDAP/AD basique pour Veyon + + + %1 (load computers and locations from LDAP/AD) + %1 (charger les ordinateurs et les emplacements depuis LDAP/AD) + + + %1 (load users and groups from LDAP/AD) + %1 (charger les utilisateurs et les groupes depuis LDAP/AD) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + Veuillez spécifier une url LDAP valide en suivant ce schéma "ldap[s]://[user[:password]@]hostname[:port]" + + + No naming context attribute name given - falling back to configured value. + Aucun attribut de nom de contexte de nommage n'a été donné - retour à la valeur configurée. + + + Could not query base DN. Please check your LDAP configuration. + Impossible d'interroger la base DN (Nom Domaine). Veuillez vérifier votre configuration LDAP. + + + Configuring %1 as base DN and disabling naming context queries. + Configuration de %1 en tant que base DN et désactivation des requêtes de contexte de dénomination. + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + Service PAM personnalisé pour l'authentification de l'utilisateur + + + User authentication + Authentification utilisateur + + + Session management + Gestion de session + + + Display manager users + Gestionnaire d'affichage utilisateurs + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Extension implémentant les fonctions abstraites pour le système Linux + + + + LocationDialog + + Select location + Sélectionner un emplacement + + + enter search filter... + entrer le filtre de recherche + + + + MainToolBar + + Configuration + Configuration + + + Disable balloon tooltips + Désactiver les info-bulles + + + Show icons only + Afficher seulement les icônes + + + + MainWindow + + MainWindow + Fenêtre principale + + + toolBar + Barre d'outils + + + General + Général + + + &File + &Fichier + + + &Help + &Aide + + + &Quit + &Quitter + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + &Charger la configuration à partir d'un fichier + + + Ctrl+O + Ctrl+O + + + About Qt + À propos de QT + + + Authentication impossible + Authentification impossible + + + Configuration not writable + Impossible d'enregistrer la configuration + + + Load settings from file + Charge la configuration à partir d'un fichier + + + Save settings to file + Enregistre la configuration dans un fichier + + + Unsaved settings + Paramètres non enregistrés + + + There are unsaved settings. Quit anyway? + Certains paramètres n'ont pas été enregistrés. Souhaitez-vous tout de même quitter? + + + Veyon Configurator + Console de gestion Veyon + + + Service + Service + + + Master + Maître + + + Access control + Contrôle d'accès + + + About Veyon + À propos de Veyon + + + Auto + Auto + + + About + À propos + + + %1 Configurator %2 + Console de gestion %1 %2 + + + JSON files (*.json) + Fichiers JSON (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + Le processus de configuration locale rapporte que le fichier de configuration ne peut pas s'enregistrer! Veuillez exécuter la console de gestion %1 avec des droits plus importants. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + Aucune clés d'authentification n'ont été trouvées ou bien les clés actuelles sont obsolètes. Veuillez créer de nouvelles clés en utilisant la Console de gestion %1. Sinon, vous pouvez paramétrer l'authentification par l'identifiant en utilisant également la Console de gestion %1. Sans cela, vous ne serez pas en mesure d'accéder aux ordinateurs en utilisant %1. + + + Access denied + Accès refusé + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + Selon la configuration locale, vous n'êtes pas autorisé à accéder aux ordinateurs du réseau. Veuillez vous identifier avec un compte différent ou bien demandez à votre administrateur système de vérifier la configuration locale. + + + Screenshots + Captures d'écrans + + + Feature active + Fonctionnalité active + + + The feature "%1" is still active. Please stop it before closing %2. + La fonctionnalité "%1" est toujours active. Veuillez l'interrompre avant de fermer %2. + + + Reset configuration + Réinitialisation de la configuration + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Voulez-vous vraiment reset la configuration locale et restaurer tous les paramètres à leurs valeurs par défaut ? + + + Search users and computers + Chercher des utilisateurs et des ordinateurs + + + Align computers to grid + Aligner les ordinateurs sur la grille + + + %1 Configurator + Console de gestion %1 + + + Insufficient privileges + Privilèges insuffisants + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + Impossible de démarrer avec des privilèges administrateurs. Veuillez vous assurez qu'un programme de type "sudo" (permettant les droits admin), est installé pour votre environnement! Le programme sera exécuté avec des privilèges d'utilisateur classique. + + + Only show powered on computers + Montrer seulement les ordinateurs allumés + + + &Save settings to file + &Sauver les paramètres vers un fichier + + + &View + &Vue + + + &Standard + &Standard + + + &Advanced + &Confirmé + + + Use custom computer arrangement + Utiliser une disposition personnalisé pour les ordinateurs + + + Locations && computers + Emplacements && ordinateurs + + + Slideshow + Diaporama + + + Spotlight + Focalisation + + + Adjust size of computer icons automatically + Ajuster automatiquement la taille des icônes d'ordinateur + + + + MasterConfigurationPage + + Directories + Répertoires + + + User configuration + Configuration utilisateur + + + Feature on computer double click: + Action au double clique sur un ordinateur: + + + Features + Fonctionnalités + + + All features + Fonctionnalités disponibles + + + Disabled features + Fonctionnalités désactivées + + + Screenshots + Captures d'écrans + + + <no feature> + Aucune action + + + Basic settings + Paramètres généraux + + + Behaviour + Comportement + + + Enforce selected mode for client computers + Forcer le mode sélectionné pour les ordinateurs + + + Hide local computer + Masquer l'ordinateur local + + + Hide computer filter field + Masquer le champ filtre ordinateur + + + Actions such as rebooting or powering down computers + Actions comme le redémarrage ou l'extinction d'ordinateurs + + + User interface + Interface de l'utilisateur + + + Background color + Couleur d'arrière plan + + + Thumbnail update interval + Intervalle de rafraichissement des miniatures d'écran + + + ms + ms + + + Program start + Démarrage du programme + + + Modes and features + Modes et fonctionnalités + + + User and computer name + Utilisateur et nom d'ordinateur + + + Only user name + Uniquement le nom d'utilisateur + + + Only computer name + Nom d'ordinateur seulement + + + Computer thumbnail caption + Légende de la vignette ordinateur + + + Text color + Couleur du texte + + + Sort order + Ordre de tri + + + Computer and user name + Ordinateur et nom d'utilisateur + + + Computer locations + Emplacements d'ordinateur + + + Show current location only + Afficher seulement l'emplacement actuel + + + Allow adding hidden locations manually + Autoriser l'ajout manuel d'emplacements cachés + + + Hide empty locations + Cacher les emplacements vides + + + Show confirmation dialog for potentially unsafe actions + Afficher un message de confirmation pour les actions potentiellement risquées + + + Perform access control + Effectuer un contrôle d'accès + + + Automatically select current location + Sélectionner automatiquement la localisation courante + + + Automatically open computer select panel + Ouvrir automatiquement le panneau de sélection ordinateur + + + Hide local session + Masquer la session locale + + + px + px + + + Thumbnail spacing + Espacement des miniatures + + + Auto + Auto + + + Thumbnail aspect ratio + Rapport hauteur / largeur de la miniature + + + Automatically adjust computer icon size + Ajuster automatiquement la taille de l'icône de l'ordinateur + + + Open feature windows on the same screen as the main window + Ouvrir les fenêtres de fonctionnalités sur le même écran que la fenêtre principale + + + + MonitoringMode + + Monitoring + Surveiller + + + Builtin monitoring mode + mode de surveillance intégré + + + This mode allows you to monitor all computers at one or more locations. + Ce mode vous permet de surveiller tous les ordinateurs d'un ou de plusieurs emplacements. + + + + NetworkObjectTreeModel + + Locations/Computers + Emplacements/Ordinateurs + + + + OpenWebsiteDialog + + Open website + Ouvrir une page internet + + + e.g. Veyon + ex : Veyon + + + Remember and add to website menu + Mémoriser et ajouter au menu des sites internet + + + e.g. www.veyon.io + ex: www.veyon.io + + + Please enter the URL of the website to open: + Veuillez entrer l'URL du site internet à ouvrir: + + + Name: + Nom: + + + + PasswordDialog + + Username + Identifiant + + + Password + Mot de passe + + + Veyon Logon + Connexion Veyon + + + Authentication error + Erreur d'authentification + + + Logon failed with given username and password. Please try again! + La connexion a échoué avec le nom d'utilisateur et le mot de passe donné. Veuillez réessayer! + + + Please enter your username and password in order to access computers. + Veuillez entrer votre identifiant et votre mot de passe afin d'accéder aux ordinateurs. + + + + PowerControlFeaturePlugin + + Power on + Allumer + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Cliquez sur ce bouton pour démarrer tous les ordinateurs. De cette façon, vous n'avez pas à les allumer tous individuellement. + + + Reboot + Redémarrer + + + Click this button to reboot all computers. + Cliquez sur ce bouton pour redémarrer tous les ordinateurs. + + + Power down + Éteindre + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Cliquez sur ce bouton pour éteindre tous les ordinateurs. De cette façon, vous n'avez pas à le faire à la main pour chacun. + + + Power on/down or reboot a computer + Allumer / Éteindre ou redémarrer un ordinateur + + + Confirm reboot + Confirmer le redémarrage + + + Confirm power down + Confirmer l'extinction + + + Do you really want to reboot the selected computers? + Souhaitez-vous réellement redémarrer les ordinateurs sélectionnés ? + + + Do you really want to power down the selected computer? + Souhaitez-vous réellement éteindre les ordinateurs sélectionnés ? + + + Power on a computer via Wake-on-LAN (WOL) + Allumer un ordinateur via Wake-on-LAN (WOL) + + + MAC ADDRESS + ADRESSE MAC + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + Cette commande diffuse un paquet Wake-on-LAN (WOL) sur le réseau afin d'allumer l'ordinateur correspondant à l'adresse MAC donnée. + + + Please specify the command to display help for! + Veuillez spécifier la commande pour afficher l'aide correspondante ! + + + Invalid MAC address specified! + L' adresse MAC spécifiée est invalide! + + + Commands for controlling power status of computers + Commandes pour contrôler l'état d'allumage des ordinateurs + + + Power down now + Éteindre maintenant + + + Install updates and power down + Installer les mises à jour et éteindre + + + Power down after user confirmation + Éteindre après la confirmation de l'utilisateur + + + Power down after timeout + Éteindre après l'expiration d'un délai + + + The computer was remotely requested to power down. Do you want to power down the computer now? + L'ordinateur a été invité à s'éteindre à distance. Voulez-vous éteindre l'ordinateur maintenant? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + L'ordinateur s'éteindra dans %1 minutes, %2 secondes. + +Veuillez sauvegarder votre travail et fermer tous les programmes. + + + + PowerDownTimeInputDialog + + Power down + Éteindre + + + Please specify a timeout for powering down the selected computers: + Veuillez spécifier un délai d'attente pour éteindre les ordinateurs sélectionnés: + + + minutes + minutes + + + seconds + seconds + + + + RemoteAccessFeaturePlugin + + Remote view + Vue à distance + + + Open a remote view for a computer without interaction. + Ouvre une vue à distance de l'ordinateur sans interaction. + + + Remote control + Contrôle à distance + + + Open a remote control window for a computer. + Ouvre une fenêtre de contrôle à distance pour un ordinateur. + + + Remote access + Accès à distance + + + Remote view or control a computer + Vue à distance ou contrôle de l'ordinateur + + + Please enter the hostname or IP address of the computer to access: + Pour accéder, veuillez entrer le nom de l'hôte ou l'adresse IP de l'ordinateur: + + + Show help about command + Afficher la commande aide et à propos + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 - %2 Accès distant + + + %1 - %2 - %3 Remote Access + %1 - %2 - %3 Accès à distance + + + + RemoteAccessWidgetToolBar + + View only + Voir seulement + + + Remote control + Contrôle à distance + + + Send shortcut + Envoyer un raccourci + + + Fullscreen + Plein écran + + + Window + Fenêtre + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Menu + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Connexion à %1... + + + Connected. + Connecté. + + + Screenshot + Capture d'écran + + + Exit + Quitter + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Veuillez entrer les programmes ou les commandes à exécuter sur les ordinateurs sélectionnés. Vous pouvez séparer vos différents programmes/commandes ligne par ligne. + + + Run programs + Exécuter des programmes + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + ex: "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + Nom: + + + Remember and add to program menu + Mémoriser et ajouter au menu du programme + + + e.g. VLC + ex: VLC + + + + ScreenLockFeaturePlugin + + Lock + Verrouiller + + + Unlock + Déverrouiller + + + Lock screen and input devices of a computer + Verrouiller l'écran et les périphériques d'entrée d'un ordinateur + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Pour attirer toute l'attention de tous les utilisateurs, vous pouvez verrouiller les ordinateurs en utilisant ce bouton. Dans ce mode, tous les périphériques d'entrée sont verrouillés et l'écran est noir. + + + Lock input devices + Verrouiller les périphériques d'entrée + + + Unlock input devices + Déverrouiller les périphériques d'entrée + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + Pour conserver toute l'attention de tous les utilisateurs, vous pouvez verrouiller leurs ordinateurs à l'aide de ce bouton. Dans ce mode, tous les périphériques d'entrée sont verrouillés tandis que le bureau est toujours visible. + + + + Screenshot + + unknown + inconnu + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + Impossible de prendre une capture d'écran car le répertoire %1 n'existe pas et ne peut pas être créé. + + + Screenshot + Capture d'écran + + + Could not open screenshot file %1 for writing. + Impossible d'ouvrir le fichier de capture d'écran "%1" pour écrire! + + + + ScreenshotFeaturePlugin + + Screenshot + Capture d'écran + + + Use this function to take a screenshot of selected computers. + Utiliser cette fonction pour prendre une capture d'écran des ordinateurs sélectionnés. + + + Screenshots taken + Captures d'écrans réalisées + + + Screenshot of %1 computer have been taken successfully. + La capture d'écran de l'ordinateur %1 a été réalisée avec succès. + + + Take screenshots of computers and save them locally. + Prend des captures d'écran des ordinateurs et les enregistre localement. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Toutes les captures d'écrans que vous avez effectuées sont listé ici. Vous pouvez prendre des captures d'écran en cliquant sur "capture d'écran" dans le menu contextuel d'un ordinateur. Les captures d'écran peuvent être gérées en utilisant les boutons ci-dessous. + + + User: + Utilisateur: + + + Computer: + Ordinateur: + + + Date: + Date: + + + Time: + Heure: + + + Show + Affiche + + + Delete + Supprimer + + + Screenshot + Capture d'écran + + + Do you really want to delete all selected screenshots? + Voulez-vous vraiment supprimer toutes les captures d'écran sélectionnées? + + + + ServiceConfigurationPage + + General + Général + + + Autostart + Démarrage automatique + + + Hide tray icon + Cacher l’icône de la barre + + + Start service + Démarrer le service + + + Stopped + Arrêté + + + Stop service + Arrêter le service + + + State: + État : + + + Enable firewall exception + Autoriser une exception pour le pare-feu + + + Allow connections from localhost only + Autoriser les connexions seulement à partir de l'hôte local + + + VNC server + Serveur VNC + + + Plugin: + Extension: + + + Restart %1 Service + Redémarrage du service %1 + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Tous les paramètres ont été sauvegardés avec succès. Afin de prendre effet, le service %1 doit être redémarré. Souhaitez vous le faire maintenant ? + + + Running + En cours d’exécution + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + L'activation de cette option, commandera au service de lancer un processus serveur pour chaque session interactive sur un ordinateur. +Généralement, ceci est nécessaire pour prendre en charge les serveurs de terminaux. + + + Show notification on remote connection + Afficher la notification sur la connexion à distance + + + Show notification when an unauthorized access is blocked + Afficher une notification quand un accès non autorisé est bloqué + + + Sessions + Sessions + + + Single session mode (create server instance for local/physical session only) + Mode de session unique (créer une instance de serveur pour une session locale / physique uniquement) + + + Multi session mode (create server instance for each local and remote desktop session) + Mode multi-session (créer une instance de serveur pour chaque session de bureau local et distant) + + + Maximum session count + Nombre maximum de sessions + + + Network port numbers + Numéros de port réseau + + + Veyon server + serveur Veyon + + + Internal VNC server + Serveur VNC interne + + + Feature manager + Gestionnaire de fonctionnalités + + + Demo server + Serveur de démo + + + Miscellaneous network settings + Divers paramètres réseau + + + + ServiceControl + + Starting service %1 + Démarrage du service %1 + + + Stopping service %1 + Arrêt du service %1 + + + Registering service %1 + Inscription du service %1 + + + Unregistering service %1 + Déinscription du service %1 + + + Service control + Contrôle du service + + + + ServiceControlPlugin + + Service is running + Le service est en cours d’exécution + + + Service is not running + Le service n'est pas en fonctionnement + + + Configure and control Veyon service + Configure et contrôle le service Veyon + + + Register Veyon Service + Inscription du service Veyon + + + Unregister Veyon Service + Désinscription du service Veyon + + + Start Veyon Service + Démarrer le service Veyon + + + Stop Veyon Service + Arrêter le service Veyon + + + Restart Veyon Service + Redémarrer le service Veyon + + + Query status of Veyon Service + Interrogation du statut du service Veyon + + + Commands for configuring and controlling Veyon Service + Commandes pour configurer et contrôler le Service Veyon + + + + ShellCommandLinePlugin + + Run command file + Lance un fichier de commande + + + File "%1" does not exist! + Le fichier "%1" n'existe pas. + + + Interactive shell and script execution for Veyon Control + Terminal interactif et script d'exécution pour le Contrôle de Veyon + + + Commands for shell functionalities + Commandes pour les fonctionnalités du terminal + + + + SlideshowPanel + + Previous + Précédent + + + Start/pause + Démarrage/pause + + + Next + Suivant + + + Duration: + Durée: + + + + SpotlightPanel + + Add selected computers + Ajouter les ordinateurs sélectionnés + + + Remove selected computers + Supprimer les ordinateurs sélectionnés + + + Update computers in realtime + Mettre à jour les ordinateurs en temps réel + + + Spotlight + Focalisation + + + Please select at least one computer to add. + Veuillez sélectionner au moins un ordinateur à ajouter. + + + Please select at least one computer to remove. + Veuillez sélectionner au moins un ordinateur à supprimer. + + + Add computers by clicking with the middle mouse button or clicking the first button below. + Ajoutez des ordinateurs en cliquant avec le bouton central de la souris ou en cliquant sur le premier bouton ci-dessous. + + + + SystemTrayIcon + + System tray icon + Icône de la zone de notification + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + Groupes d'utilisateurs de processus pour les groupes d'utilisateurs du système + + + Default (system user groups) + Défaut (Groupes utilisateur système) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + Teste les composants internes et fonctions de Veyon + + + Commands for testing internal components and functions of Veyon + Commandes pour tester les composants internes et les fonctions de Veyon + + + + TextMessageDialog + + Send text message + Envoyer un message écrit + + + Use the field below to type your message which will be sent to all selected users. + Utilisez le champ ci-dessous pour saisir votre message, il sera envoyé à tous les utilisateurs sélectionnés. + + + + TextMessageFeaturePlugin + + Text message + Message + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Utiliser cette fonction pour envoyer un message à tous les utilisateurs (ex: pour leur donner de nouvelles tâches à effectuer). + + + Message from teacher + Message de la part de l'enseignant + + + Send a message to a user + Envoyer un message à un utilisateur + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Activer la capture des fenêtres semi-transparentes + + + Poll full screen (leave this enabled per default) + Sondage en plein écran (laissez activé par défaut) + + + Low accuracy (turbo mode) + Résolution faible (mode turbo) + + + Builtin UltraVNC server configuration + Configuration du serveur intégré UltraVNC + + + Enable multi monitor support + Activer le support multi moniteur + + + Enable Desktop Duplication Engine on Windows 8 and newer + Activer le processus de duplication du bureau sur Windows 8 ou supérieur + + + Maximum CPU usage + Utilisation maximale du processeur + + + + UserConfig + + No write access + Pas d'accès en écriture + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Impossible de sauvegarder vos paramétrages! Veuillez vérifier le chemin du fichier de configuration utilisateur en utilisant la console de gestion %1 + + + + UserLoginDialog + + User login + Identifiant utilisateur + + + Please enter a username and password for automatic login on all computers. + Saisissez ici votre identifiant utilisateur et votre mot de passe pour la connexion automatique à toutes les stations. + + + Username + Identifiant + + + Password + Mot de passe + + + + UserSessionControlPlugin + + Log in + Connexion + + + Click this button to log in a specific user on all computers. + Cliquez sur ce bouton pour connecter un utilisateur spécifique sur toutes les stations. + + + Log off + Déconnexion + + + Click this button to log off users from all computers. + Cliquez sur ce bouton pour déconnecter les utilisateurs de tous les ordinateurs. + + + Confirm user logoff + Confirmer la déconnexion utilisateur + + + Do you really want to log off the selected users? + Souhaitez-vous vraiment déconnecter l'utilisateur sélectionné ? + + + User session control + Contrôle de session utilisateur + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [ERREUR] + + + Invalid command! + Commande non valide! + + + Available commands: + Commandes disponibles: + + + Invalid arguments given + Les arguments donnés ne sont pas valide + + + Not enough arguments given - use "%1 help" for more information + Pas assez d'arguments donnés - Utiliser "l'aide %1" pour plus d'information + + + Unknown result! + Résultat inconnu! + + + Available modules: + Modules disponibles: + + + No module specified or module not found - available modules are: + Aucun module spécifié ou module non trouvé - les modules disponibles sont: + + + Plugin not licensed + Greffon sans licence + + + INFO + INFO + + + ERROR + ERREUR + + + USAGE + USAGE + + + DESCRIPTION + DESCRIPTION + + + EXAMPLES + EXEMPLES + + + WARNING + ATTENTION + + + + VeyonServiceControl + + Veyon Service + Service Veyon + + + + VncViewWidget + + Establishing connection to %1 ... + Établissement de la connexion à %1 ... + + + + WebApiConfigurationPage + + Web API + API Web + + + General + Général + + + Network port + Port réseau + + + Enable WebAPI server + Activer le serveur API Web + + + Connection settings + Paramètres de connexion + + + Lifetime + Durée de vie + + + h + h + + + s + s + + + Idle timeout + Délai d'inactivité + + + Authentication timeout + Délai d'authentification + + + Maximum number of open connections + Nombre maximum de connexions ouvertes + + + Connection encryption + Cryptage de la connexion + + + TLS certificate file + Fichier de certificat TLS + + + TLS private key file + Fichier de clé privée TLS + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + Utilisez HTTPS avec TLS 1.3 au lieu de HTTP + + + + WebApiPlugin + + Run WebAPI server + Exécuter le serveur API Web + + + Failed to start WebAPI server at port %1 + Échec du démarrage du serveur API Web sur le port %1 + + + WebAPI server running at port %1 + Serveur WebAPI exécuté sur le port %1 + + + Provide access to a computer via HTTP + Fournir l'accès à un ordinateur via HTTP + + + Commands for running the WebAPI server + Commandes pour exécuter le serveur API Web + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + Impossible de changer le paramétrage de manière logicielle pour la génération SAS. L'exécution de Ctrl+Alt+Suppr via le contrôle distant ne fonctionnera pas! + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + Général + + + Enable SAS generation by software (Ctrl+Alt+Del) + Activer la génération de touche SAS de manière logicielle (Ctrl+Alt+Suppr) + + + Screen lock + Verrouillage d'écran + + + Hide taskbar + Masquer la barre des tâches + + + Hide start menu + Masquer le menu de démarrage + + + Hide desktop + Masquer le bureau + + + User authentication + Authentification utilisateur + + + Use alternative user authentication mechanism + Utiliser une autre méthode pour l'authentification de l'utilisateur + + + User login + Identifiant utilisateur + + + Input start delay + Délai de démarrage de l'entrée + + + Simulated key presses interval + intervalle entre simulations de saisie de touche + + + Confirm legal notice (message displayed before user logs in) + Confirmer les mentions légales (message affiché avant la connexion de l'utilisateur) + + + Use input device interception driver + Utiliser le pilote d'interception du périphérique d'entrée + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Extension implémentant les fonctions abstraites pour le système Windows + + + + WindowsServiceControl + + The service "%1" is already installed. + Le service "%1" est déjà installé. + + + The service "%1" could not be installed. + Le service "%1" ne peut pas être installé. + + + The service "%1" has been installed successfully. + Le service "%1" a été installé avec succès. + + + The service "%1" could not be uninstalled. + Le service "%1" ne peut pas être désinstallé. + + + The service "%1" has been uninstalled successfully. + Le service "%1" a été désinstallé avec succès. + + + The start type of service "%1" could not be changed. + Le type de démarrage du service "%1" ne peut pas être changé. + + + Service "%1" could not be found. + Service "%1" impossible à trouvé. + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Configuration du serveur intégré x11vnc + + + Custom x11vnc parameters: + Paramètres personnalisés x11vnc: + + + Do not use X Damage extension + N'utilise pas l'extension X Damage + + + diff --git a/translations/veyon_he.ts b/translations/veyon_he.ts new file mode 100644 index 0000000..9b3ba4c --- /dev/null +++ b/translations/veyon_he.ts @@ -0,0 +1,4059 @@ + + + + + AboutDialog + + About + אודות + + + Translation + תרגום + + + License + רישיון + + + About Veyon + אודות Veyon + + + Contributors + תורמים + + + Version: + גרסה: + + + Website: + אתר אינטרנט: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + השפה הנוכחית אינה תורגמה עדיין. אם את/ה מעוניין לתרגם את Veyon לשפה שלך או שפה אחרת או רוצה לשפר תרגום הנוכחי נא תפנה למיישם Veyon. + + + About %1 %2 + אודות %1 %2 + + + Support Veyon project with a donation + לתמיכה כספית + + + + AccessControlPage + + Computer access control + בקרת גישה מחשב + + + Grant access to every authenticated user (default) + תן גישה לכל משתמש רשום (ברירת מחדל) + + + Test + בדיקה + + + Process access control rules + הרצת בקרה כללי גישה + + + User groups authorized for computer access + קבוצת משתמשים רשאים לבצע גישה למחשבים + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + נא להוסיף קבוצות חברים בהם יהיו רשאים לגשת למחשבים ברשת Veyon שלך + + + Authorized user groups + קבוצות משתמשים רשומים + + + All groups + כל הקבוצות + + + Access control rules + כללי בקרת גישה + + + Add access control rule + הוסף כלל בקרת גישה + + + Remove access control rule + הסר כלל בקרת גישה + + + Move selected rule down + הזזת כלל נבחר מטה + + + Move selected rule up + הזזת כלל נבחר מעלה + + + Edit selected rule + שינוי כלל נבחר + + + Enter username + הכנס שם משתמש + + + Please enter a user login name whose access permissions to test: + נא הכנס שם משתמש עם הרשאות לבדיקה: + + + Access allowed + גישה מאופשרת + + + The specified user is allowed to access computers with this configuration. + המשתמש הזה רשאי לבצע גישה למחשבים עם הגדרה נוכחית + + + Access denied + גישה נדחתה + + + The specified user is not allowed to access computers with this configuration. + המשתמש הזה אינו רשאי לבצע גישה למחשבים עם הגדרה נוכחית + + + Enable usage of domain groups + אפשר שימוש בקבוצות בתחום + + + User groups backend: + שרת קבוצות משתמשים: + + + Missing user groups backend + חסר שרת קבוצות משתמשים + + + No default user groups plugin was found. Please check your installation! + לא נמצא פלאגין של קבוצות משתמשים. בדוק את ההתקנה שלך! + + + Restrict access to members of specific user groups + הגבל הרשאה לחברי קבוצה מסוימת + + + + AccessControlRuleEditDialog + + Edit access control rule + ערוך כלל בקרת גישה + + + General + כללי + + + enter a short name for the rule here + הכנס שם מקוצר לכלל כאן + + + Rule name: + שם כלל : + + + enter a description for the rule here + הכנס תיאור עבור כלל כאן + + + Rule description: + תיאור כלל: + + + Invert all conditions ("is/has" interpreted as "is/has not") + הפוך את כל התנאים (מקיים\קיים הופך ללא מקיים\קיים) + + + Conditions + תנאים + + + is member of group + חבר קבוצה + + + Accessing computer is localhost + המחשב הניגש הוא המקומי + + + Accessing user is logged on user + משתמש ניגש מחובר כמשתמש + + + Accessing user is already connected + משתמש ניגש כבר מחובר + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + אם יותר מאחד התנאים מופעל, כל אחד מהתנאים צריך להתקיים על מנת לקיים את התנאי ("וגם" לוגי). אם רק אחד מהתנאים צריך להתקיים, יש ליצור מספר כללי גישה + + + Action + פעולה + + + Allow access + אפשר גישה + + + Deny access + חסום גישה + + + Ask logged on user for permission + בקש אישור משתמש מחובר לגישה + + + None (rule disabled) + אין (כלל מושבת) + + + Accessing user + משתמש ניגש + + + Accessing computer + מחשב ניגש + + + Local (logged on) user + משתמש מקומי (מחובר) + + + Local computer + מחשב מקומי + + + Always process rule and ignore conditions + תמיד הפעל את החור והתעלם מהתנאים + + + No user logged on + אין אף משתמש מחובר + + + Accessing user has one or more groups in common with local (logged on) user + משתמש ניגש בעל אחת או יותר קבוצות במשותף עם המשתמש המקומי (המחובר) + + + Accessing computer and local computer are at the same location + מחשב ניגש ומחשב מקומי נמצאים באותו מיקום + + + is located at + ממוקם ב + + + + AccessControlRulesTestDialog + + Access control rules test + בדיקת כללי גישה + + + Accessing user: + משתמש ניגש: + + + Local computer: + מחשב מקומי: + + + Accessing computer: + מתחבר למחשב: + + + Please enter the following user and computer information in order to test the configured ruleset. + אנא הזן את המשתמש הבא ואת המידע אודות המחשב על מנת לבדוק את קבוצת החוקים + + + Local user: + משתמש מקומי: + + + Connected users: + משתמשים מחוברים: + + + The access in the given scenario is allowed. + הגישה במקרה הניתן מאופשרת + + + The access in the given scenario is denied. + הגישה במקרה הניתן נדחית + + + The access in the given scenario needs permission of the logged on user. + הגישה במקרה הניתן דורשת הרשאה מצד המשתמש המחובר. + + + ERROR: Unknown action + שגיאה: פעולה לא ידועה + + + Test result + תוצאות בדיקה + + + + AuthKeysConfigurationPage + + Authentication keys + מפתחות התחברות + + + Introduction + מבוא + + + Key file directories + נתיבים של קבצי מפתחות + + + Public key file base directory + נתיב בסיס למפתחות פומביים + + + Private key file base directory + נתיב בסיס למפתחות פרטיים + + + Available authentication keys + מפתחות התחברות זמינים + + + Create key pair + יצירת זוג מפתחות + + + Delete key + מחק מפתח + + + Import key + ייבא מפתח + + + Export key + ייצא מפתח + + + Set access group + הגדר קבוצת גישה + + + Key files (*.pem) + קבצי מפתחות (*.pem) + + + Authentication key name + שם מפתח אימות + + + Please enter the name of the user group or role for which to create an authentication key pair: + אנא הכניסו את שם קבוצת המשתמשים או התפקיד עבורם יש ליצור את זוג המפתחות + + + Do you really want to delete authentication key "%1/%2"? + אתה בטוח שברצונך למחוק את המפתח "%1/%2"? + + + Please select a key to delete! + נא לבחור מפתח למחיקה! + + + Please enter the name of the user group or role for which to import the authentication key: + נא להזין את שם קבוצת המשתמשים או התפקיד עבורם יש לייבא את מפתח האימות: + + + Please select a key to export! + יש לבחור מפתח לייצוא! + + + Please select a user group which to grant access to key "%1": + נא לבחור קבוצת משתמשים עבורה יש לתת גישה למפתח "%1": + + + Please select a key which to set the access group for! + נא לבחור מפתח עבורו יש להגדיר את קבוצת הגישה! + + + Please perform the following steps to set up key file authentication: + יש לעקוב אחר הלשבים הבאים על מנת להגדיר התחברות באמצעות מפתחות: + + + 1) Create a key pair on the master computer. + 1) צור זוג מפתחות על מחשב המאסטר + + + 2) Set an access group whose members should be allowed to access other computers. + 2) הגדר קבוצת גישה אשר חבריה אמורים לגשת למחשבים אחרים. + + + 3) Export the public key and import it on all client computers with the same name. + ייצא את המפתח הפומבי וייבא אותו בכל אחד ממחשבי הלקוחות עם שם זהה. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + אנה פנה אל <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">המדריך למנהל של Veyon</a> למידע נוסף. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + זוג מפתחות אימות כולל זוג מפתחות קריפטוגרפיים, מפתח פרטי ומפתח פומבי. +מפתח פרט מאפשר למשתמשים על מחשב המאסטר לגשת לכל שאר מחשבי הלקוח. +זה חשוב שרק מחשבים מורשים יוכלו לקרוא את המפתח הפרטי. +המפתח הפומבי משמש במחשבי לקוח על מנת שיוכלו לאמת בקשות לחיבורים נכנסים. + + + + AuthKeysManager + + Please check your permissions. + בדוק את ההרשאות שלך. + + + Key name contains invalid characters! + שם מפתח כולל תווים לא חוקיים! + + + Invalid key type specified! Please specify "%1" or "%2". + סוג מפתח לא חוקי נבחר! נא לבחור "%1" או "%2". + + + Specified key does not exist! Please use the "list" command to list all installed keys. + המפתח שנבחר אינו קיים. אנא השתמש בפקודה "list" על מנת למנות את כל המפתחות המותקנים. + + + One or more key files already exist! Please delete them using the "delete" command. + אחד או יותר מהמפתחות כבר קיימים! השתמשו בפקודה "delete" ומחקו אותם ראשית. + + + Creating new key pair for "%1" + יוצר מפתח חדש עבור "%1" + + + Failed to create public or private key! + לא ניתן היה ליצור מפתח פומבי או פרטי! + + + Newly created key pair has been saved to "%1" and "%2". + זוג המפתחות שנוצרו נשמרו ל"%1" ול""%2". + + + Could not remove key file "%1"! + לא ניתן היה להסיר את קובץ המפתח "%1"! + + + Could not remove key file directory "%1"! + לא ניתן היה להסיר את נתיב המפתחות "%1"! + + + Failed to create directory for output file. + לא ניתן היה ליצור נתיב לקובץ הפלט. + + + File "%1" already exists. + הקובץ "%1" כבר קיים. + + + Failed to write output file. + לא ניתן היה לכתוב את קובץ הפלט. + + + Key "%1/%2" has been exported to "%3" successfully. + המפתח "%1/%2" יוצא בהצלחה ל"%3". + + + Failed read input file. + לא ניתן היה לקלוט את קובץ הקלט + + + File "%1" does not contain a valid private key! + הקובץ "%1" לא כולל מפתח פרטי תקין! + + + File "%1" does not contain a valid public key! + הקובץ "%1" לא כולל מפתח פומבי תקין! + + + Failed to create directory for key file. + לא ניתן היה ליצור נתיב לקובץ המפתח. + + + Failed to write key file "%1". + כתיבת קובץ המפתח "%1" נכשלה. + + + Failed to set permissions for key file "%1"! + הגדרת ההרשאות לקובץ המפתח "%1" נכשלה. + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + המפתח "%1\%2" יובר בהצלחה. יש לבדוק את הרשאות הקובץ "%3" על מנת למנוע גישה לא מורשית. + + + Failed to convert private key to public key + לא ניתן היה להמיר מפתח פרטי למפתח פומבי. + + + Failed to create directory for private key file "%1". + יצירת התיב למפתח הפרטי "%1" נכשלה. + + + Failed to save private key in file "%1"! + + + + Failed to set permissions for private key file "%1"! + + + + Failed to create directory for public key file "%1". + + + + Failed to save public key in file "%1"! + + + + Failed to set permissions for public key file "%1"! + + + + Failed to set owner of key file "%1" to "%2". + + + + Failed to set permissions for key file "%1". + + + + Key "%1" is now accessible by user group "%2". + + + + <N/A> + + + + Failed to read key file. + + + + + AuthKeysPlugin + + Create new authentication key pair + + + + Delete authentication key + + + + List authentication keys + + + + Import public or private key + + + + Export public or private key + + + + Extract public key from existing private key + + + + Set user group allowed to access a key + + + + KEY + + + + ACCESS GROUP + קבוצת גישה + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + + NAME + + + + FILE + + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + + Please specify the command to display help for! + + + + TYPE + + + + PAIR ID + + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + שם + + + Type + סוג + + + Access group + קבוצת גישה + + + Pair ID + + + + + BuiltinDirectoryConfigurationPage + + Computers + מחשבים + + + Name + שם + + + Host address/IP + שם מחשב / IP + + + MAC address + כתובות MAC + + + Add new computer + הוסף מחשב חדש + + + Remove selected computer + הסר מחשבים שנבחרו + + + New computer + מחשב חדש + + + Builtin directory + + + + Locations & computers + + + + Locations + + + + Add new location + + + + Remove selected location + + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + + + + + BuiltinDirectoryPlugin + + Show help for specific command + הצג עזרה עבור פקודה מסויימת + + + Import objects from given file + יבוא אובייקטים מקובץ נתון + + + Export objects to given file + יצוא אובייקטים לקובץ נתון + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + סוג + + + Name + שם + + + Host address + + + + MAC address + כתובות MAC + + + Specified object not found. + + + + File "%1" does not exist! + + + + Can't open file "%1" for reading! + + + + Unknown argument "%1". + + + + Computer "%1" (host address: "%2" MAC address: "%3") + + + + Unclassified object "%1" with ID "%2" + + + + None + + + + Computer + מחשב + + + Root + + + + Invalid + + + + Error while parsing line %1. + + + + Network object directory which stores objects in local configuration + + + + Commands for managing the builtin network object directory + + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + + + + Parent UUID + + + + Add a location or computer + + + + Clear all locations and computers + + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + + + + FILE + + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + + + + NAME + + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + + + + + ComputerControlListModel + + Host/IP address: %1 + + + + Active features: %1 + + + + Online and connected + + + + Establishing connection + + + + Computer offline or switched off + + + + Authentication failed or access denied + + + + Disconnected + מנותק + + + No user logged on + אין אף משתמש מחובר + + + Logged on user: %1 + מחובר בתור: %1 + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + + + + Authentication error + שגיאה באימות + + + Remote access + גישה מרחוק + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + משתמש + + + Missing network object directory plugin + + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + + + + Add location + + + + Save computer/user list + שמור רשימת מחשבים / משתמשים + + + Select output filename + + + + CSV files (*.csv) + קובץ CSV (*.csv) + + + File error + + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + + + + Please specify a valid filename for the configuration export. + + + + Please specify a valid key. + + + + Specified key does not exist in current configuration! + + + + Please specify a valid value. + + + + Configure Veyon at command line + + + + Output file is not writable! + + + + Output directory is not writable! + + + + Configuration file is not readable! + + + + Clear system-wide Veyon configuration + + + + List all configuration keys and values + + + + Import configuration from given file + + + + Export configuration to given file + + + + Read and output configuration value for given key + + + + Write given value to given configuration key + + + + Unset (remove) given configuration key + + + + Commands for managing the configuration of Veyon + + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + + + + + DemoConfigurationPage + + Demo server + שרת דוגמא + + + Tunables + + + + ms + + + + Key frame interval + + + + Memory limit + הגבלת זיכרון + + + MB + + + + Update interval + + + + s + + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + עצור הדגמה + + + Window demo + חלון הדגמה + + + Give a demonstration by screen broadcasting + + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + תיבת דו-שיח לגישה לשולחן העבודה + + + Confirm desktop access + אשר גישה לשולחן העבודה + + + Never for this session + + + + Always for this session + + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + + DesktopServicesConfigurationPage + + Programs & websites + + + + Predefined programs + + + + Name + שם + + + Path + נתיב + + + Add new program + + + + Remove selected program + + + + Predefined websites + + + + Remove selected website + + + + URL + + + + New program + + + + New website + + + + + DesktopServicesFeaturePlugin + + Run program + הפעל תוכנית + + + Open website + פתח אתר אינטרנט + + + Click this button to open a website on all computers. + + + + Start programs and services in user desktop + + + + Click this button to run a program on all computers. + + + + Run program "%1" + + + + Custom program + + + + Open website "%1" + + + + Custom website + + + + + DocumentationFigureCreator + + Teacher + + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + + + + Port: + + + + Password: + סיסמה: + + + + FeatureControl + + Feature control + + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + + + + Destination directory + + + + Default source directory + + + + Options + + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + + + + Select one or more files to transfer + + + + Transfer files to remote computer + + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + ממשק משתמש + + + Language: + שפה: + + + Use system language setting + השתמש בשפת המערכת + + + Veyon + + + + Logging + + + + Log file directory + + + + Log level + + + + Nothing + + + + Only critical messages + רק הודעות קריטיות + + + Errors and critical messages + + + + Warnings and errors + + + + Information, warnings and errors + + + + Debug messages and everything else + + + + Limit log file size + + + + Clear all log files + + + + Log to standard error output + + + + Network object directory + + + + Backend: + + + + Update interval: + + + + %1 service + + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + + + + All log files were cleared successfully. + + + + Error + + + + Could not remove all log files. + + + + MB + + + + Rotate log files + + + + x + + + + seconds + שניות + + + Write to logging system of operating system + + + + Authentication + + + + Method: + + + + Logon authentication + + + + Key file authentication + + + + Test + בדיקה + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + בדיקת התחברות + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + + + + + LdapConfigurationPage + + Basic settings + הגדרות בסיסיות + + + General + כללי + + + LDAP server and port + + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + + + + e.g. OU=Computers + + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + הגדרות מתקדמות + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + + LDAP base DN test failed + + + + LDAP base DN test successful + + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + הכנס שם משתמש + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + + + + Please enter a group name whose members to query: + + + + group members + + + + Group not found + + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + הכנס שם מחשב + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + + + + users + משתמשים + + + user groups + קבוצות משתמשים + + + computer groups + קבוצות מחשבים + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + משתמש לא נמצא + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + מחשבים + + + LDAP %1 test failed + + + + LDAP %1 test successful + + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + + + + TLS certificate verification + + + + System defaults + + + + Never (insecure!) + + + + Custom CA certificate file + + + + None + + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + בדיקה + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + אימות משתמש + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + + + + enter search filter... + + + + + MainToolBar + + Configuration + הגדרות + + + Disable balloon tooltips + + + + Show icons only + + + + + MainWindow + + MainWindow + חלון ראשי + + + toolBar + + + + General + כללי + + + &File + + + + &Help + + + + &Quit + + + + Ctrl+Q + + + + Ctrl+S + + + + L&oad settings from file + + + + Ctrl+O + + + + About Qt + + + + Authentication impossible + + + + Configuration not writable + + + + Load settings from file + טען הגדרות מקובץ + + + Save settings to file + שמור הגדרות לקובץ + + + Unsaved settings + + + + There are unsaved settings. Quit anyway? + + + + Veyon Configurator + + + + Service + + + + Master + + + + Access control + + + + About Veyon + אודות Veyon + + + Auto + + + + About + אודות + + + %1 Configurator %2 + + + + JSON files (*.json) + + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + גישה נדחתה + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + צילומי מסך + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + אפס הגדרות + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + חפש משתמשים ומחשבים + + + Align computers to grid + + + + %1 Configurator + + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + + + + User configuration + הדגרות משתמש + + + Feature on computer double click: + + + + Features + + + + All features + + + + Disabled features + + + + Screenshots + צילומי מסך + + + <no feature> + + + + Basic settings + הגדרות בסיסיות + + + Behaviour + + + + Enforce selected mode for client computers + + + + Hide local computer + + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + + + + User interface + ממשק משתמש + + + Background color + + + + Thumbnail update interval + + + + ms + + + + Program start + + + + Modes and features + + + + User and computer name + + + + Only user name + + + + Only computer name + + + + Computer thumbnail caption + + + + Text color + + + + Sort order + + + + Computer and user name + + + + Computer locations + + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + פתח אתר אינטרנט + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + + + + Name: + + + + + PasswordDialog + + Username + שם משתמש + + + Password + סיסמה + + + Veyon Logon + + + + Authentication error + שגיאה באימות + + + Logon failed with given username and password. Please try again! + + + + Please enter your username and password in order to access computers. + + + + + PowerControlFeaturePlugin + + Power on + הדלקה + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + + Reboot + איתחול + + + Click this button to reboot all computers. + + + + Power down + כיבוי + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + + + + Confirm reboot + אשר הפעלה מחדש + + + Confirm power down + אשר כיבוי + + + Do you really want to reboot the selected computers? + + + + Do you really want to power down the selected computer? + + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + כיבוי + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + + + + Open a remote view for a computer without interaction. + + + + Remote control + שליטה מרחוק + + + Open a remote control window for a computer. + + + + Remote access + גישה מרחוק + + + Remote view or control a computer + + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + צפייה בלבד + + + Remote control + שליטה מרחוק + + + Send shortcut + + + + Fullscreen + מסך מלא + + + Window + חלון + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + + + + Menu + + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + + + + Connected. + מחובר. + + + Screenshot + צילום מסך + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + לדוגמה VLC + + + + ScreenLockFeaturePlugin + + Lock + נעילה + + + Unlock + שחרר + + + Lock screen and input devices of a computer + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + + Lock input devices + נעל מכשירי קלט + + + Unlock input devices + שחרר מכשירי קלט + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + לא ידוע + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + צילום מסך + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + צילום מסך + + + Use this function to take a screenshot of selected computers. + + + + Screenshots taken + צילום מסך בוצע + + + Screenshot of %1 computer have been taken successfully. + + + + Take screenshots of computers and save them locally. + + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + משתמש: + + + Computer: + מחשב: + + + Date: + תאריך: + + + Time: + זמן: + + + Show + הראה + + + Delete + מחק + + + Screenshot + צילום מסך + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + כללי + + + Autostart + הפעלה אוטומטית + + + Hide tray icon + הסתרת סמליל מגש + + + Start service + התחל שרות + + + Stopped + נעצר + + + Stop service + הפסק שרות + + + State: + מצב: + + + Enable firewall exception + הפעל חריגה בחומת אש + + + Allow connections from localhost only + אפשר חיבורים ממחשב מקומי בלבד + + + VNC server + שרת VNC + + + Plugin: + תוסף + + + Restart %1 Service + הפעל מחדש את השירות %1 + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + + Running + רץ + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + מספרי מפתחים (פורטים) + + + Veyon server + שרת Veyon + + + Internal VNC server + שרת VNC פנימי + + + Feature manager + מנהל מאפיינים + + + Demo server + שרת דוגמא + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + + + + Stopping service %1 + + + + Registering service %1 + + + + Unregistering service %1 + + + + Service control + שליטה בשירות + + + + ServiceControlPlugin + + Service is running + שירות רץ + + + Service is not running + שירות לא רץ + + + Configure and control Veyon service + הגדר ושלוט בשירות Veyon + + + Register Veyon Service + הוסף שירות Veyon + + + Unregister Veyon Service + מחק שירות Veyon + + + Start Veyon Service + הפעל שירות Veyon + + + Stop Veyon Service + עצור שירות Veyon + + + Restart Veyon Service + הפעל מחדש את שירות Veyon + + + Query status of Veyon Service + בדוק מצד של שירות Veyon + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + הקודם + + + Start/pause + הפעל\השהה + + + Next + הבא + + + Duration: + משך זמן: + + + + SpotlightPanel + + Add selected computers + הוספצ מחשבים נבחרים + + + Remove selected computers + הסרת מחשבים נבחרים + + + Update computers in realtime + עדכון מחשבים בזמן אמת + + + Spotlight + + + + Please select at least one computer to add. + נא לבחור לפחות מחשב אחד להוספה. + + + Please select at least one computer to remove. + נא לבחור לפחות מחשב אחד להסדרה. + + + Add computers by clicking with the middle mouse button or clicking the first button below. + הוסף מחשבים על ידי לחיצה על כפתור העכבר האמצעי או על ידי לחיצה על הכפתור למטה. + + + + SystemTrayIcon + + System tray icon + סמליל של מגש מערכת + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + ברירת מחדל (קבוצות משתמשים של מערכת) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + בדוק רכיבים פנימיים ופונקציות פנימיות של Veyon + + + Commands for testing internal components and functions of Veyon + פקודות לבדיקת רכיבים פנימיים של Veyon + + + + TextMessageDialog + + Send text message + שלחו הודעת טקסט + + + Use the field below to type your message which will be sent to all selected users. + השתמש בשדה מטה על מנת להקליד את ההודעה שתישלח לכל המשתמשים + + + + TextMessageFeaturePlugin + + Text message + הודעת טקסט + + + Use this function to send a text message to all users e.g. to assign them new tasks. + השתמש באפשרות זאת על מנת לשלוח הודעה לכל המשתמשים (לדוגמה, לתת משימה חדשה) + + + Message from teacher + הודעה מהמורה + + + Send a message to a user + שלח הודעה למשתמש + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + אפשר לכידה של חלונות בעלי שכבות (שקופים חלקית) + + + Poll full screen (leave this enabled per default) + + + + Low accuracy (turbo mode) + דיוק נמוך (מצב טורבו) + + + Builtin UltraVNC server configuration + הגדרות שרת UltraVNC בנוי + + + Enable multi monitor support + הפעל תמיכה במספר מסכים + + + Enable Desktop Duplication Engine on Windows 8 and newer + הפעל מנוע שכפול שולחן עבודה על ווינדוס 8 ומעלה + + + Maximum CPU usage + + + + + UserConfig + + No write access + אין גישת כתיבה + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + + + + + UserLoginDialog + + User login + התחברות משתמש + + + Please enter a username and password for automatic login on all computers. + נא להזין שם משתמש וסיסמה להתחברות אוטומטית בכל המחשבים + + + Username + שם משתמש + + + Password + סיסמה + + + + UserSessionControlPlugin + + Log in + התחברות + + + Click this button to log in a specific user on all computers. + לחץ על כפתור זה על מנת לחבר משתמש מסוים + + + Log off + ניתוק + + + Click this button to log off users from all computers. + לחץ על כפתור זה על מנת לנתק את כל המשתמשים מכל המחשבים. + + + Confirm user logoff + וידוא ניתוק משתמש + + + Do you really want to log off the selected users? + אתה באמת רוצה לנתק את המשתמשים הנבחרים? + + + User session control + + + + + VeyonCore + + [OK] + [הצלחה] + + + [FAIL] + [כישלון] + + + Invalid command! + פקודה לא תקינה! + + + Available commands: + פקודות זמינות + + + Invalid arguments given + ארגומנט לא תקין ניתן + + + Not enough arguments given - use "%1 help" for more information + + + + Unknown result! + תוצאה לא ידועה! + + + Available modules: + + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + תוסף אינו מורשה + + + INFO + מידע + + + ERROR + שגיאה + + + USAGE + שימוש + + + DESCRIPTION + הסבר + + + EXAMPLES + דוגמות + + + WARNING + אזהרה + + + + VeyonServiceControl + + Veyon Service + שירות Veyon + + + + VncViewWidget + + Establishing connection to %1 ... + + + + + WebApiConfigurationPage + + Web API + + + + General + כללי + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + + WindowsPlatformConfigurationPage + + Windows + ווינדוס + + + General + כללי + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + נעילת מסך + + + Hide taskbar + הסתר את שורת המשימות + + + Hide start menu + הסתר תפריט "התחל" + + + Hide desktop + הסתר שולחן עבודה + + + User authentication + אימות משתמש + + + Use alternative user authentication mechanism + השתמש במנגנון התחברות משתמש אלטרנטיבי + + + User login + התחברות משתמש + + + Input start delay + עיכוב התחלת הקלט + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + וודא התראה משפטית (הודעה שקופצת לפני שמשתמש מתחבר) + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + התוסף ממש פונקציות אבסטרקטיות של פלטפורמת Windows + + + + WindowsServiceControl + + The service "%1" is already installed. + השירות "%1" כבר מותקן. + + + The service "%1" could not be installed. + השירות "%1" לא הותקן. + + + The service "%1" has been installed successfully. + השירות "%1" הותקן בהצלחה. + + + The service "%1" could not be uninstalled. + השירות "%1" לא יכול היה להימחק. + + + The service "%1" has been uninstalled successfully. + השירות "%1" נמחק בהצלחה. + + + The start type of service "%1" could not be changed. + סוג ההפעלה של השירות "%1" לא יכול היה לעבור שינוי. + + + Service "%1" could not be found. + השירות "%1" לא יכול היה להימצא. + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + הגדרת שרת x11vnc המובנה + + + Custom x11vnc parameters: + פרמטרים מותאמים אישית של x11vnc: + + + Do not use X Damage extension + אין להשתמש בהרחבה X Damage + + + diff --git a/translations/veyon_hu.ts b/translations/veyon_hu.ts new file mode 100644 index 0000000..7bda56d --- /dev/null +++ b/translations/veyon_hu.ts @@ -0,0 +1,4080 @@ + + + + + AboutDialog + + About + Névjegy + + + Translation + Fordítás + + + License + Licensz + + + About Veyon + Veyon-ról + + + Contributors + Közreműködők + + + Version: + Verzió: + + + Website: + Weboldal: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + A Veyon esetleges fordítási hibáit kérem, jelezd a kisskalmandaniel@gmail.hu címre. + +Ha érdekel a Veyon fordítása (saját vagy egyéb nyelvre), esetleg meglévő fordítást szeretnél továbbfejleszteni, vedd fel a kapcsolatot a Veyon fejlesztőivel. + + + About %1 %2 + %1 %2 + + + Support Veyon project with a donation + Adakozással támogasd a Veyon projektet + + + + AccessControlPage + + Computer access control + Számítógéphozzáférés-vezérlés + + + Grant access to every authenticated user (default) + Hozzáférés engedélyezése az összes hitelesített felhasználó számára (alapértelmezett) + + + Test + Tesztelés + + + Process access control rules + Hozzáférési szabályok feldogozása + + + User groups authorized for computer access + Számítógép-hozzáférést engedélyezett felhasználói csoportok + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Adja meg azokat a csoportokat, melyek tagjainak hozzáférése kellene, hogy legyen a Veyon hálózatában lévő számítógépekhez. + + + Authorized user groups + Hitelesített felhasználói csoportok + + + All groups + Összes csoport + + + Access control rules + Hozzáférési szabályok + + + Add access control rule + Hozzáférési szabály létrehozása + + + Remove access control rule + Hozzáférési szabály eltávolítása + + + Move selected rule down + Kijelölt szabály mozgatása lefelé + + + Move selected rule up + Kijelölt szabály mozgatása felfelé + + + Edit selected rule + Kijelölt szabály módosítása + + + Enter username + Add meg a felhasználónevet + + + Please enter a user login name whose access permissions to test: + Kérem, add meg a felhasználó bejelentkezési nevét, akinek a hozzáférését teszteled: + + + Access allowed + Hozzáférés engedélyezve + + + The specified user is allowed to access computers with this configuration. + A kijelölt felhasználó hozzáférhet a számítógéphez a jelenlegi konfiguráció alapján. + + + Access denied + Hozzáférés megtagadva + + + The specified user is not allowed to access computers with this configuration. + A kijelölt felhasználó nem férhet hozzá a számítógéphez a jelenlegi konfiguráció alapján. + + + Enable usage of domain groups + Domain csoportok használatának bekapcsolása + + + User groups backend: + Felhasználói csoportok háttere: + + + Missing user groups backend + Hiányzik a felhasználói csoportok háttere + + + No default user groups plugin was found. Please check your installation! + Nincs alapértelmezett felhasználóicsoport-bővítmény. Kérem, ellenőrizd telepítésed! + + + Restrict access to members of specific user groups + Hozzáférés tiltása meghatározott felhasználói csoport tagjainak + + + + AccessControlRuleEditDialog + + Edit access control rule + Hozzáférési szabály módosítása + + + General + Általános + + + enter a short name for the rule here + írd ide a szabály rövid nevét + + + Rule name: + Szabály megnevezése: + + + enter a description for the rule here + írd ide néhány szót a szabályról + + + Rule description: + Szabály leírása: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Feltételek megfordítása ("van/volt" cseréje: "nincs/nem volt") + + + Conditions + Feltételek + + + is member of group + tagja a csoportnak + + + Accessing computer is localhost + A hozzáférő számítógép a helyi számítógép + + + Accessing user is logged on user + A hozzáférő felhasználó már bejelentkezett + + + Accessing user is already connected + A hozzáférő felhasználó már csatlakozott + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Ha egynél több feltétel aktív, az összes feltételnek teljesülnie kell, hogy a szabály életbe lépjen (logikai ÉS). Ha a szabályok közül csak egy teljesül (logikai VAGY), kérem, készíts több hozzáférés-vezérlési szabályt. + + + Action + Művelet + + + Allow access + Hozzáférés engedélyezése + + + Deny access + Hozzáférés tiltása + + + Ask logged on user for permission + Bejelentkezett felhasználótól jogosultság kérése + + + None (rule disabled) + Egyik sem (egyik szabály sincs bekapcsolva) + + + Accessing user + Hozzáférő felhasználó + + + Accessing computer + Hozzáférő számítógép + + + Local (logged on) user + Helyi (bejelentkezett) felhasználó + + + Local computer + Helyi számítógép + + + Always process rule and ignore conditions + Mindig hassanak a szabályok és hagyja figyelmen kívül a feltételeket + + + No user logged on + Egy felhasználó sem jelentkezett be + + + Accessing user has one or more groups in common with local (logged on) user + A hozzáférő felhasználó egy vagy több csoport közös tagja a helyi (bejelentkezett) felhasználóval + + + Accessing computer and local computer are at the same location + A hozzáférő számítógép a helyi számítógéppel egy helyszínen van + + + is located at + helyezkedik el + + + + AccessControlRulesTestDialog + + Access control rules test + Hozzáférési szabály tesztelése + + + Accessing user: + Hozzáférő felhasználó: + + + Local computer: + Helyi számítógép: + + + Accessing computer: + Hozzáférő számítógép: + + + Please enter the following user and computer information in order to test the configured ruleset. + Kérem, add meg a következő felhasználó- és számítógép-információt, hogy tesztelhessem a beállított szabályokat. + + + Local user: + Helyi felhasználó: + + + Connected users: + Csatlakozott felhasználó: + + + The access in the given scenario is allowed. + Az adott esetben a hozzáférés engedélyezett. + + + The access in the given scenario is denied. + Az adott esetben a hozzáférés tiltott. + + + The access in the given scenario needs permission of the logged on user. + Az adott esetben a hozzáféréshez a bejelentkezett felhasználó jogosultságára van szükség. + + + ERROR: Unknown action + HIBA: Ismeretlen művelet + + + Test result + Teszt eredménye: + + + + AuthKeysConfigurationPage + + Authentication keys + Hitelesítési kulcs + + + Introduction + Bevezetés + + + Key file directories + Kulcsfájlok mappái + + + Public key file base directory + Publikus kulcsfájl alapmappája + + + Private key file base directory + Privát kulcsfájl alapmappája + + + Available authentication keys + Elérhető hitelesítési kulcsok + + + Create key pair + Kulcspár létrehozása + + + Delete key + Kulcs törlése + + + Import key + Kulcs importálása + + + Export key + Kulcs exportálása + + + Set access group + Hozzáférési csoport beállítása + + + Key files (*.pem) + Kulcsfájlok (*.pem) + + + Authentication key name + Hitelesítési kulcs neve + + + Please enter the name of the user group or role for which to create an authentication key pair: + Kérem, add meg a felhasználó csoport vagy szerep nevét, amely számára létrehozod a hitelesítési kulcspárt: + + + Do you really want to delete authentication key "%1/%2"? + Biztos, hogy törli "%1%2" hitelesítési kulcsot? + + + Please select a key to delete! + Kérem, válaszd ki a törlendő kulcsot! + + + Please enter the name of the user group or role for which to import the authentication key: + Kérem, add meg a felhasználó csoport vagy szerep nevét, amely számára importálod a hitelesítési kulcsot: + + + Please select a key to export! + Kérem, válaszd ki az importálandó kulcsot! + + + Please select a user group which to grant access to key "%1": + Kérem, válassz felhasználó szerepet, amely számára hozzáférést adsz "%1" kulcshoz: + + + Please select a key which to set the access group for! + Kérem, válassz egy kulcsot, amelyhez hozzáférési csoportot állítasz be! + + + Please perform the following steps to set up key file authentication: + Kérem, hajtsd végre a következő lépéseket a kulcsfájl-hitelesítés beállításához: + + + 1) Create a key pair on the master computer. + 1) Hozz létre egy kulcspárt a mesterszámítógépen. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Állíts be egy hozzáférési csoportot, amelynek tagjai hozzáférhetnek a többi számítógéphez. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Exportáld a nyilvános kulcsot , majd importáld ugyanazon a néven az összes számítógépre. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Kérem, további információkért olvassa le a <a href="https://veyon.readthedocs.io/en/latest/admin/index.html"> Veyon üzemeltetői leírást </a>. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + A hitelesítési kulcspár kettő összetartozó kriptográfiai kulcsból áll, egy privát és egy publikus kulcsból. +A privát kulcs használatával a mester számítógép felhasználói hozzáférhetnek a kliens számítógépekhez. +Fontos, hogy csak hitelesített felhasználóknak legyen olvasási hozzáférése a privát kulcsfájlhoz. +A nyilvános kulcsrészt a kliens számítógépen használjuk a bejövő kapcsolatkérések hitelesítéséhez. + + + + AuthKeysManager + + Please check your permissions. + Kérem, ellenőrizd a jogosultságaidat. + + + Key name contains invalid characters! + A kulcs neve érvénytelen karaktereket tartalmaz. + + + Invalid key type specified! Please specify "%1" or "%2". + Érvénytelen kulcstípus-meghatározás. "%1" vagy "%2" lehetséges. + + + Specified key does not exist! Please use the "list" command to list all installed keys. + A megadott kulcs nem létezik! Az összes telepített kulcs listázásához használja a "list" parancsot. + + + One or more key files already exist! Please delete them using the "delete" command. + Egy vagy több kulcs már létezik! Kérem, törölje ezeket a "delete" paranccsal. + + + Creating new key pair for "%1" + "%1" számára új kulcspár létrehozása + + + Failed to create public or private key! + Publikus vagy privát kulcs importálási hiba! + + + Newly created key pair has been saved to "%1" and "%2". + Az újonnan létrehozott kulcspárt mentettük: "%1" és "%2". + + + Could not remove key file "%1"! + Nem sikerült eltávolítani "%1" kulcsfájlt! + + + Could not remove key file directory "%1"! + Nem sikerült eltávolítani "%1" kulcsfájl-mappát! + + + Failed to create directory for output file. + A kimeneti fájl számára a mappa létrehozása sikertelen. + + + File "%1" already exists. + "%1" fájl már létezik. + + + Failed to write output file. + Kimeneti fájl írási hiba. + + + Key "%1/%2" has been exported to "%3" successfully. + "%1/%2" kulcsot sikeresen importáltuk ide: "%3". + + + Failed read input file. + Bemenet fájl olvasási hiba. + + + File "%1" does not contain a valid private key! + "%1" fájl nem tartalmaz valid privát kulcsot! + + + File "%1" does not contain a valid public key! + "%1" fájl nem tartalmaz valid publikus kulcsot! + + + Failed to create directory for key file. + A kulcsfájl számára a mappa létrehozása sikertelen. + + + Failed to write key file "%1". + "%1" kulcsfájl írása sikertelen. + + + Failed to set permissions for key file "%1"! + "%1" kulcsfájl jogosultságainak beállítása sikertelen! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + "%1/%2" kulcsot sikeresen importáltuk. Kérem, az illetéktelen hozzáférés elkerülése érdekében ellenőrizd "%3" fájl jogosultsági beállításait. + + + Failed to convert private key to public key + Privát kulcs importálása publikus kulcsból sikertelen + + + Failed to create directory for private key file "%1". + "%1" privát kulcsfájl számára mappa létrehozása sikertelen. + + + Failed to save private key in file "%1"! + "%1" fájlba a privát kulcsfájl mentése sikertelen! + + + Failed to set permissions for private key file "%1"! + "%1" privát kulcsfájl jogosultságainak beállítása sikertelen! + + + Failed to create directory for public key file "%1". + "%1" publikus kulcsfájl számára mappa létrehozása sikertelen. + + + Failed to save public key in file "%1"! + "%1" fájlba a publikus kulcsfájl mentése sikertelen! + + + Failed to set permissions for public key file "%1"! + "%1" publikus kulcsfájl jogosultságainak beállítása sikertelen! + + + Failed to set owner of key file "%1" to "%2". + "%1" -> "%2" kulcsfájl tulajdonosának beállítása sikertelen. + + + Failed to set permissions for key file "%1". + "%1" kulcsfájl jogosultságainak beállítása sikertelen! + + + Key "%1" is now accessible by user group "%2". + "%2" csoport felhasználói hozzáférhetnek "%1" kulcshoz. + + + <N/A> + <N/A> + + + Failed to read key file. + Kulcsfájl olvasási hiba. + + + + AuthKeysPlugin + + Create new authentication key pair + Új hitelesítési kulcspár létrehozása + + + Delete authentication key + Hitelesítési kulcs törlése + + + List authentication keys + Hitelesítési kulcsok felsorolása + + + Import public or private key + Publikus vagy privát kulcsok importálása + + + Export public or private key + Publikus vagy privát kulcsok exportálása + + + Extract public key from existing private key + Publikus kulcs kicsomagolása egy már létező privát kulcsból + + + Set user group allowed to access a key + Felhasználói csoport hozzáférésének beállítása egy kulcshoz + + + KEY + KULCS + + + ACCESS GROUP + HOZZÁFÉRÉSI CSOPORT + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + Ez a parancs beállítja a fájlhozzáférési jogosultságot<KEY>értékre, így csak<ACCESS GROUP>felhasználói csoportnak van olvasási hozzáférése a fájlhoz. + + + NAME + NÉV + + + FILE + FÁJL + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Ez a parancs exportálja <KEY> hitelesítési kulcsot <FILE> fájlba. Ha <FILE> nincs megadva, akkor <KEY> nevéből és típusából állítunk elő egy nevet. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Ez a parancs importálja <KEY> hitelesítési kulcsot <FILE> fájlból. Ha <FILE> nincs megadva, akkor <KEY> nevéből és típusából állítunk elő egy nevet. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + Ez a parancs a beállított kulcskönyvtárban lévő összes elérhető hitelesítési kulcsot felsorolja. Ha "%1" lehetőség meg van adva, egy, a kulcs részleteit tartalmazó táblázat fog megjelenni. Ha a kulcs nem érhető el, például olvasási jogosultságok hiánya miatt, néhány részlete hiányozhat. + + + Please specify the command to display help for! + Kérem, válaszd ki az a parancsot, melynek súgóját megjelenítsük! + + + TYPE + TÍPUS + + + PAIR ID + PÁR ID + + + Command line support for managing authentication keys + Parancssori támogatás a hitelesítési kulcsok kezeléséhez + + + Commands for managing authentication keys + A hitelesítési kulcsokat kezelő parancsok + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + Ez a parancs egy új <NAME> nevű hitelesítési kulcspárt hoz létre és menti a privát és a publikus kulcsot a beállított kulcskönyvtárakba. A paraméter a kulcs megnevezése, ami csak betűket tartalmazhat. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + Ez a parancs törli <KEY> hitelesítési kulcsot a beállított kulcskönyvtárból. Kérem, figyelj arra, hogy törlés után a kulcs már nem állítható vissza. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + Ez a parancs kicsomagolja a nyilvános kulcsrészt <KEY> privát kulcsból és a megfelelő nyilvános kulcsként menti. Amikor egy másik mesterszámítógépet állítasz be, elegendő csak a privát kulcsot átmásolni. A publikus kulcs ebből már előállítható. + + + + AuthKeysTableModel + + Name + Megnevezés + + + Type + Típus + + + Access group + Hozzáférési csoport + + + Pair ID + Pár ID + + + + BuiltinDirectoryConfigurationPage + + Computers + Számítógépek + + + Name + Megnevezés + + + Host address/IP + Kiszolgáló címe/IP-je + + + MAC address + Fizikai cím + + + Add new computer + Új számítógép hozzáadása + + + Remove selected computer + Kiválasztott számítógép eltávolítása + + + New computer + Új számítógép + + + Builtin directory + Beépített mappa + + + Locations & computers + Helyszínek és számítógépek + + + Locations + Helyszínek + + + Add new location + Új helyszín hozzáadása + + + Remove selected location + Kiválasztott helyszín eltávolítása + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + CSV fájlok importálása parancssori interfészen keresztül lehetséges. Bővebb információért látogassa meg az <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online dokumentációt</a>. + + + New location + Új helyszín + + + + BuiltinDirectoryPlugin + + Show help for specific command + Konkrét parancsról segítség megjelenítése + + + Import objects from given file + Objektumok importálása a megadott fájlból + + + Export objects to given file + Objektumok exportálása a megadott fájlba + + + Invalid type specified. Valid values are "%1" or "%2". + Érvénytelen típusmeghatározás. "%1" vagy "%2" lehetséges. + + + Type + Típus + + + Name + Megnevezés + + + Host address + Kiszolgáló címe + + + MAC address + Fizikai cím + + + Specified object not found. + Az adott objektum nem található. + + + File "%1" does not exist! + "%1" fájl nem létezik! + + + Can't open file "%1" for reading! + "%1" fájl nem nyitható meg olvasásra! + + + Unknown argument "%1". + "%1" ismeretlen argumentum. + + + Computer "%1" (host address: "%2" MAC address: "%3") + "%1" számítógép (kiszolgáló címe: "%2" fizikai cím: "%3") + + + Unclassified object "%1" with ID "%2" + Be nem sorolt "%1" objektum, ID: "%2" + + + None + Egyik sem + + + Computer + Számítógép + + + Root + Root + + + Invalid + Érvénytelen + + + Error while parsing line %1. + %1. sor feldolgozásakor hiba keletkezett. + + + Network object directory which stores objects in local configuration + Hálózatobjektum-mappa, ami objektumokat tároló a helyi konfigurációban + + + Commands for managing the builtin network object directory + A beépített hálózatobjektum-mappát kezelő parancsok + + + No format string or regular expression specified! + Nincs formázott karakterlánc, illetve reguláris kifejezés! + + + Can't open file "%1" for writing! + "%1" fájl nem nyitható meg írásra! + + + No format string specified! + Nincs formázott karakterlánc! + + + Object UUID + Objektum UUID + + + Parent UUID + Szülő UUID + + + Add a location or computer + Egy helyszín vagy számítógép hozzáadása + + + Clear all locations and computers + Összes helyszín és számítógép törlése + + + Dump all or individual locations and computers + Az összes vagy néhány helyszín és számítógép mentése + + + List all locations and computers + Összes helyszín és számítógép felsorolása + + + Remove a location or computer + Egy helyszín vagy számítógép eltávolítása + + + Location "%1" + "%1" helyszín + + + Builtin (computers and locations in local configuration) + Beépített (a helyi konfigurációban lévő számítógépek és helyszínek) + + + Location + Helyszín + + + FILE + FÁJL + + + LOCATION + HELYSZÍN + + + FORMAT-STRING-WITH-PLACEHOLDERS + HELYÖRZŐKKEL-FORMÁZOTT-SZTRING + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + REGULÁRIS-KIFEJEZÉS-HELYŐRZŐVEL + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Objektumok importálás meghatározott szöveges fájlból egy vagy több helyőrzővel megadott formátum használatával. Érvényes helyőrzők a következők: %1 + + + Import simple CSV file to a single room + Egyszerű CSV fájl importálása egy szobába + + + Import CSV file with location name in first column + CSV fájl importálása a helyszín nevével az első oszlopában + + + Import text file with with key/value pairs using regular expressions + Szöveges fájl importálása kulcs/érték párokkal reguláris kifejezések használatával + + + Import arbitrarily formatted data + Önkényesen formázott adatok importálása + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Objektumok exportálása meghatározott szöveges fájlba egy vagy több helyőrzővel megadott formátum használatával. Érvényes helyőrzők a következők: %1 + + + Export all objects to a CSV file + Összes objektum exportálása CSV fájlba + + + Export all computers in a specific location to a CSV file + Egy megadott helyszín összes számítógépének exportálása CSV fájlba + + + TYPE + TÍPUS + + + NAME + NÉV + + + PARENT + SZÜLŐ + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + Egy objektumot ad hozzá, ahol %1 "%2" vagy "%3" egyike lehet. %4 értékét név vagy UUID határozhatja meg. + + + Add a room + Szoba hozzáadása + + + Add a computer to room %1 + Számítógép hozzáadása %1 szobához + + + OBJECT + OBJEKTUM + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Eltávolítja a meghatározott objektumot a mappából. %1 értékét a név vagy az UUID határozhatja meg. Egy helyszín eltávolítása a hozzá tartozó összes számítógépet is eltávolítja. + + + Remove a computer by name + Számítógép eltávolítása név alapján + + + Remove an object by UUID + Objektum eltávolítása UUID alapján + + + "Room 01" + "01 szoba" + + + "Computer 01" + "01 számítógép" + + + HOST ADDRESS + HOST ADDRESS + + + MAC ADDRESS + FIZIKAI CÍM + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Beépített VNC szerver (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Beépített VNC szerver (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Kiszolgáló/IP cím: %1 + + + Active features: %1 + Aktív szolgáltatások: %1 + + + Online and connected + Online és sikeresen csatlakozott + + + Establishing connection + Kapcsolat létesítése + + + Computer offline or switched off + A számítógép nincs hálózaton vagy kikapcsolták + + + Authentication failed or access denied + A hitelesítés sikertelen vagy a hozzáférés nem engedélyezett + + + Disconnected + Kapcsolat megszakadt + + + No user logged on + Egy felhasználó sem jelentkezett be + + + Logged on user: %1 + Bejelentkezett felhasználó: %1 + + + Location: %1 + Helyszín: %1 + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 szolgáltatás %2, 3%:%4 + + + Authentication error + Hitelesítési hiba + + + Remote access + Távoli elérés + + + User "%1" at host "%2" is now accessing this computer. + "%1" felhasználó "%2" kiszolgálóról éppen most eléri ezt a számítógépet. + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + "%1%" felhasználó "%2" kiszolgálóról megpróbálta eléri ezt a számítógépet, de nem sikerült hitelesítenie magát. + + + Access control error + Hozzáférés-vezérlési hiba + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + "%1%" felhasználó "%2" kiszolgálóról megpróbálta eléri ezt a számítógépet, de letiltottuk hozzáférés-hitelesítési beállítások miatt. + + + Active connections: + + + + + ComputerManager + + User + Felhasználó + + + Missing network object directory plugin + Hiányzik a hálózatobjektummappa-bővítmény + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Hiányzik az alapértelmezett hálózatobjektummappa-bővítmény. Ellenőrizd telepítésed vagy állítsd be egy másik hálózatobjektummappa-hátteret "%1" Konfigurátorral. + + + Location detection failed + A helyszínérzékelés nem sikerült + + + Computer name;Hostname;User + Számítógépnév;Kiszolgálónév;Felhasználó + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + Nem sikerült azt a helyszínt azonosítani, amelyikhez a számítógép tartozik. Ez a rendszer hibás konfigurálására utal. Ehelyett a számítógépkijelölés-panelen az összes helyszínt megjelenítjük. + + + + ComputerSelectPanel + + Computer search + Számítógép keresése + + + Add location + Helyszín hozzáadása: + + + Save computer/user list + Számítógép-/felhasználólista mentése + + + Select output filename + Válassz kimeneti fájlnevet + + + CSV files (*.csv) + CSV fájlok (*.csv) + + + File error + Fájlhiba + + + Could not write the computer and users list to %1! Please check the file access permissions. + Nem sikerült a számítógép- és a felhasználólista kiírása ide: %1. Ellenőrizd a hozzáférési jogosultságaidat. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Létező konfigurációs fájlt adj meg importáláshoz. + + + Please specify a valid filename for the configuration export. + Valós fájlnevet adj meg a konfiguráció exportálásához. + + + Please specify a valid key. + Valós kulcsot adj meg. + + + Specified key does not exist in current configuration! + A megadott kulcs nem létezik a jelenlegi konfigurációban! + + + Please specify a valid value. + Kérem, valós értéket adj meg. + + + Configure Veyon at command line + Veyon konfigurálása parancssorból + + + Output file is not writable! + Kimeneti fájl nem írható! + + + Output directory is not writable! + Kimeneti könyvtár nem írható! + + + Configuration file is not readable! + Konfigurációs fájl nem olvasható! + + + Clear system-wide Veyon configuration + Az egész rendszerre kiterjedő Veyon konfiguráció törlése + + + List all configuration keys and values + Összes konfigurációs kulcs és és értékének felsorolása + + + Import configuration from given file + Konfiguráció importálása adott fájlból + + + Export configuration to given file + Konfiguráció exportálása adott fájlba + + + Read and output configuration value for given key + Adott kulcs konfigurációs értékének olvasása és kimenete + + + Write given value to given configuration key + Adott érték írása adott konfigurációs kulcsba + + + Unset (remove) given configuration key + Adott konfigurációs kulcs kikapcsolása (eltávolítása) + + + Commands for managing the configuration of Veyon + Veyon konfigurálásához parancsok + + + Upgrade and save configuration of program and plugins + A program és bővítmények frissítése és konfigurációjuk mentése + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + Nem módosítható %1 szolgáltatás automatikusan induló tulajdonsága. + + + Could not configure the firewall configuration for the %1 Server. + Nem sikerült %1 szerver tűzfalbeállításait konfigurálni. + + + Could not configure the firewall configuration for the %1 Worker. + Nem sikerült %1 munkás tűzfalbeállításait konfigurálni. + + + Configuration is not writable. Please check your permissions! + A konfiguráció nem írható. Kérem, ellenőrizd a jogosultságaidat. + + + Could not apply platform-specific configuration settings. + A platformfüggő konfigurációs beállítások nem alkalmazhatóak. + + + + DemoClient + + %1 Demo + %1 demó + + + + DemoConfigurationPage + + Demo server + Demószerver + + + Tunables + Hangolhatók + + + ms + ms + + + Key frame interval + Kulcskeret intervallum + + + Memory limit + Memóriakorlát + + + MB + MB + + + Update interval + Frissítési időköz + + + s + mp + + + Slow down thumbnail updates while demo is running + Indexképek frissítésének lassítása a demó futása alatt. + + + + DemoFeaturePlugin + + Stop demo + Demó leállítása + + + Window demo + Ablakban futó demó + + + Give a demonstration by screen broadcasting + Bemutató megjelenítése képernyőbroadcasttal + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + Ebben a módban a képernyőd az összes számítógépen ablakban jelenik meg. A felhasználók eközben ablakot tudnak váltani. + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + Asztal-hozzáférés párbeszédablaka + + + Confirm desktop access + Asztal-hozzáférés megerősítése + + + Never for this session + Ennek a munkamenetnek semmikor + + + Always for this session + Ennek a munkamenetnek mindig + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + %1 felhasználó %2 számítógéptől próbálja elérni az asztalodat. Engedélyezel számára hozzáférést? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programok és weboldalak + + + Predefined programs + Előre definiált programok + + + Name + Megnevezés + + + Path + Útvonal + + + Add new program + Új program hozzáadása + + + Remove selected program + Kiválasztott program eltávolítása + + + Predefined websites + Előre definiált weboldal + + + Remove selected website + Kiválasztott weboldal eltávolítása + + + URL + URL + + + New program + Új program + + + New website + Új weboldal + + + + DesktopServicesFeaturePlugin + + Run program + Program futtatása + + + Open website + Weboldal megnyitása + + + Click this button to open a website on all computers. + Kattints erre a gombra, hogy az összes számítógépen megnyíljon egy weboldal. + + + Start programs and services in user desktop + Programok és szolgáltatások indítása a felhasználó asztalon + + + Click this button to run a program on all computers. + Kattints erre a gombra, hogy egy programot futtass az összes számítógépen. + + + Run program "%1" + "%1" program futtatása + + + Custom program + Egyéni program + + + Open website "%1" + "%1" weboldal megnyitása + + + Custom website + Egyéni weboldal + + + + DocumentationFigureCreator + + Teacher + Tanár + + + Room %1 + Szoba %1 + + + Please complete all tasks within the next 5 minutes. + Kérjük, fejezze be az összes feladatot a következő 5 percen belül. + + + Custom website + Egyéni weboldal + + + Open file manager + Fájlkezelő megnyitása + + + Start learning tool + Tanulási eszköz indítása + + + Play tutorial video + Bemutató videó lejátszása + + + Custom program + Egyéni program + + + Handout + Kiosztás + + + Texts to read + Olvasandó szövegek + + + generic-student-user + generic-student-user + + + + ExternalVncServer + + External VNC server + Külső VNC szerver + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Külső VNC szerver konfiguráció + + + Port: + Port: + + + Password: + Jelszó: + + + + FeatureControl + + Feature control + Szolgáltatásvezérlés + + + + FileTransferConfigurationPage + + File transfer + Fájlátvitel + + + Directories + Mappák + + + Destination directory + + + + Default source directory + + + + Options + Lehetőség + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + "%1" fájl nem nyitható meg olvasásra. Kérem, ellenőrizd a jogosultságaidat. + + + + FileTransferDialog + + File transfer + Fájlátvitel + + + Options + Lehetőség + + + Transfer only + Csak átvitel + + + Transfer and open file(s) with associated program + Átvitel és a fájl(ok) megnyitása a társított programmal + + + Transfer and open destination folder + Átvitel és a célmappa megnyitása + + + Files + Fájlok + + + Start + Indítás + + + Overwrite existing files + Fájlok felülírása + + + + FileTransferPlugin + + File transfer + Fájlátvitel + + + Click this button to transfer files from your computer to all computers. + Kattints erre gombra, hogy számítógépedről fájlokat küldj át az összes számítógépre. + + + Select one or more files to transfer + Válasszon ki egy vagy több fájlt az átvitelhez + + + Transfer files to remote computer + Fájlok átvitele távoli számítógépre + + + Received file "%1". + "%1" fájl fogadva. + + + Could not receive file "%1" as it already exists. + "%1" fájl nem fogadható, mert már létezik. + + + Could not receive file "%1" as it could not be opened for writing! + "%1" fájl nem fogadható, mert nem nyitható meg írásra! + + + + GeneralConfigurationPage + + User interface + Felhasználói felület + + + Language: + Nyelv: + + + Use system language setting + Rendszernyelv beállításának használata + + + Veyon + Veyon + + + Logging + Naplózás + + + Log file directory + Naplófájl mappája + + + Log level + Naplózás szintje + + + Nothing + Semmi + + + Only critical messages + Csak kritikus üzenetek + + + Errors and critical messages + Hibák és kritikus üzenetek + + + Warnings and errors + Figyelmeztetések és hibák + + + Information, warnings and errors + Információk, figyelmeztetések és hibák + + + Debug messages and everything else + Hibakeresési üzenetek és minden egyéb + + + Limit log file size + Naplófájl méretének korlátozása + + + Clear all log files + Összes naplófájl törlése + + + Log to standard error output + Naplózás standard hibakimenetre + + + Network object directory + Hálózatobjektum-mappa + + + Backend: + Háttér: + + + Update interval: + Frissítési időköz: + + + %1 service + %1 szolgáltatás + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + %1 szolgáltatást ideiglenesen le kell állítani, hogy a naplófájlokat eltávolíthassuk. Biztos, hogy folytatod? + + + Log files cleared + Naplófájlokat sikeresen törölted + + + All log files were cleared successfully. + Az összes naplófájlt sikeresen törölted. + + + Error + Hiba + + + Could not remove all log files. + Nem sikerült az összes naplófájl törlése. + + + MB + MB + + + Rotate log files + Naplófájlok rotálása + + + x + x + + + seconds + másodpercek + + + Write to logging system of operating system + Írás az operációs rendszer naplózási rendszerébe + + + Authentication + Hitelesítés + + + Method: + Módszer: + + + Logon authentication + Bejelentkezési hitelesítés + + + Key file authentication + Kulcsfájl hitelesítés + + + Test + Tesztelés + + + Authentication is set up properly on this computer. + A hitelesítés megfelelően van beállítva ezen a számítógépen. + + + Authentication keys are not set up properly on this computer. + A hitelesítési kulcsok nem megfelelően vannak beállítva ezen a számtógépen. + + + Authentication test + Hitelesítési teszt + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + LDAP tallózása + + + + LdapClient + + LDAP error description: %1 + LDAP hiba leírása: %1 + + + + LdapConfigurationPage + + Basic settings + Alapbeállítások + + + General + Általános + + + LDAP server and port + LDAP szerver és port + + + Bind DN + Kötés DN-je + + + Bind password + Kötés jelszava + + + Anonymous bind + Névtelen kötés + + + Use bind credentials + Kötéshitelesítés használata + + + Base DN + Alap DN + + + Fixed base DN + Fixált alap DN + + + e.g. dc=example,dc=org + például dc=valami, dc=hu + + + Discover base DN by naming context + Alap DN felfedezése névmeghatározással + + + e.g. namingContexts or defaultNamingContext + például namingContexts vagy defaultNamingContext + + + Environment settings + Környezeti beállítások + + + Object trees + Objektumfák + + + Computer tree + Számítógépfák + + + e.g. OU=Groups + például OU=Csoportok + + + User tree + Felhasználófa + + + e.g. OU=Users + például OU=Felhasználók + + + e.g. OU=Computers + például OU=Számítógépek + + + Group tree + Számítógépfa + + + Perform recursive search operations in object trees + Rekurzív keresési műveletek az objektumfákban + + + Object attributes + Objektum attribútumok + + + e.g. hwAddress + például hwAddress + + + e.g. member or memberUid + például member vagy memberUid + + + e.g. dNSHostName + például dNSHostName + + + Computer MAC address attribute + Számítógép fizikai címének attribútuma + + + Group member attribute + Csoporttagság attribútumok + + + e.g. uid or sAMAccountName + például uid vagy sAMAccountName + + + Advanced settings + Haladó beállítások + + + Optional object filters + Opcionális objektumszűrő + + + Filter for user groups + Felhasználói csoportok szűrője + + + Filter for users + Felhasználók szűrője + + + Filter for computer groups + Számítógépcsoportok szűrője + + + Group member identification + Csoporttagság azonosítója + + + Distinguished name (Samba/AD) + Megkülönböztetett név (Samba/AD) + + + List all groups of a user + Egy felhasználó összes csoportjának felsorolása + + + List all groups of a computer + Egy számítógép összes csoportjának felsorolása + + + Get computer object by IP address + Számítógép-objektumok előállítása IP címek alapján + + + LDAP connection failed + LDAP kapcsolat sikertelen + + + LDAP bind failed + LDAP kötés sikertelen + + + LDAP bind successful + LDAP kötés sikerült + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Sikeresen csatlakoztunk az LDAP szerverhez, és létrejött az LDAP kötés. Az LDAP alapszintű beállításai helyesek. + + + LDAP base DN test failed + LDAP alap DN teszt sikertelen + + + LDAP base DN test successful + LDAP alap DN teszt sikeres + + + LDAP naming context test failed + LDAP névkontextusteszt sikertelen + + + LDAP naming context test successful + LDAP névkontextus-teszt sikerült + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + Az LDAP névmeghatározását sikeresen lekérdeztük. A következő alap DN-t találtuk: +%1 + + + user tree + felhasználófa + + + group tree + csoportfa + + + computer tree + számítógépfa + + + Enter username + Add meg a felhasználónevet + + + Please enter a user login name (wildcards allowed) which to query: + Kérem, add meg a bejelentkezési felhasználónevet (helyettesítő karakterek megengedettek), melyeket lekérdezel: + + + user objects + felhasználói objektumok + + + Enter group name + Adja meg a csoport nevét + + + Please enter a group name whose members to query: + Kérem, add meg a csoport nevét, amely tagjait lekérdezed: + + + group members + csoporttagok + + + Group not found + Csoport nem találgató + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + "1%" nevű csoport nem található. Kérem, ellenőrizd a csoport nevét vagy a csoportfa paramétert. + + + Enter computer name + Add meg a számítógép nevét + + + computer objects + számítógép-objektumok + + + Enter computer DN + Add meg a számítógép DN-t + + + Please enter the DN of a computer whose MAC address to query: + Kérem, add meg a számítógép DN-jét, mely fizikai címét lekérdezed: + + + computer MAC addresses + számítógép fizikai címei + + + users + felhasználók + + + user groups + felhasználói csoportok + + + computer groups + számítógépcsoportok + + + Please enter a user login name whose group memberships to query: + Kérem, add meg a felhasználó bejelentkezési nevét, akinek csoporttagságait lekérdezed: + + + groups of user + felhasználó csoportjai + + + User not found + Felhasználó nem található + + + groups of computer + számítógép csoportjai + + + Computer not found + Számítógép nem található + + + Enter computer IP address + Add meg a számítógép IP címét + + + Please enter a computer IP address which to resolve to an computer object: + Kérem, add meg a számítógép IP címét, melyet számítógép-objektummá keressünk vissza: + + + computers + számítógépek + + + LDAP %1 test failed + %1 LDAP teszt sikertelen + + + LDAP %1 test successful + %1 LDAP teszt sikeres + + + The %1 has been queried successfully and %2 entries were found. + %1 objektum lekérdezése sikeres, %2 jegyezést találtunk. + + + %1 %2 have been queried successfully: + +%3 + %1 %2 lekérdezés sikeres: + +%3 + + + LDAP filter test failed + LDAP szűrő teszt sikertelen + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + A konfigurált szűrővel egy %1 objektumot sem sikerült lekérdezni. Kérem, ellenőrizd %1 LDAP szűrőt. + +%2 + + + LDAP filter test successful + LDAP szűrő teszt sikeres + + + %1 %2 have been queried successfully using the configured filter. + %1 %2-t sikeresen lekérdezted a a konfigurált szűrő használatával. + + + (only if different from group tree) + (csak ha eltér csoportfától) + + + Computer group tree + Számítógépcsoport-fa + + + computer group tree + számítógépcsoport-fa + + + Filter for computers + Számítógépek szűrője + + + e.g. room or computerLab + például szoba vagy számítógépLabor + + + Integration tests + Integrációs tesztek + + + Computer groups + Számítógépcsoportok + + + e.g. name or description + például név vagy leírás + + + Filter for computer containers + Számító géptároló szűrője + + + Computer containers or OUs + Számítógép tárolók vagy szervezeti egységek + + + Connection security + Csatlakozási biztonság + + + TLS certificate verification + TLS tanúsítványhitelesítés + + + System defaults + Rendszer alapértelmezett értékei + + + Never (insecure!) + Semmikor (nem biztonságos!) + + + Custom CA certificate file + Egyéni CA tanúsítványfájl + + + None + Egyik sem + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + például (objectClass=computer) + + + e.g. (objectClass=group) + például (objectClass=group) + + + e.g. (objectClass=person) + például (objectClass=person) + + + e.g. (objectClass=room) or (objectClass=computerLab) + például (objectClass=szoba) vagy (objectClass=számítógépLabor) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + például (objectClass=container) vagy (objectClass=organizationalUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + Nem sikerült lekérdezni az konfigurált alap DN-t. Kérem, ellenőrizd az alap DN paramétert. + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + Az LDAP alap DN-t sikeresen lekérdeztük. Az alábbi bejegyzéseket találtuk: + +%1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + Nem sikerült elnevezési kontextusokból lekérdezni az alap DN-t. Kérem, ellenőrizd az elnevezési kontextusok attribútuma paramétert. + +%1 + + + Certificate files (*.pem) + Tanúsítványfájlok (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + Nem sikerült kapcsolódni az LDAP szerverhez. Kérem, ellenőrizd a szerver paramétereit. + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + Nem sikerült kötést létrehozni a LDAP szerverrel. Kérem, ellenőrizd a szerver paramétereit és a kötést hitelesítő adatokat. + +%1 + + + Encryption protocol + Titkosítási protokoll + + + Computer location attribute + Számítógéphelyszín attribútum + + + Computer display name attribute + Számítógép megjelenítési név attribútum + + + Location name attribute + Helyszínnév attribútum + + + e.g. cn or displayName + pl. cn vagy displayName + + + Computer locations identification + Számítógéphelyszínek azonosítója + + + Identify computer locations (e.g. rooms) via: + Azonosítja a számítógép helyszínét (pl. szobák): + + + Location attribute in computer objects + Helyszín attribútuma számítógép-objektumokban + + + List all entries of a location + A helyszín összes bejegyzésének felsorolása + + + List all locations + Helyszínek felsorolása + + + Enter computer display name + Adja meg a számítógép megjelenítési nevét + + + Please enter a computer display name to query: + Adja meg a számítógép megjelenítési nevét a lekérdezéshez: + + + Enter computer location name + Adja meg a számítógép helyszínének nevét + + + Please enter the name of a computer location (wildcards allowed): + Adja meg egy számítógéphelyszín nevét (helyettesítő karakterek megengedettek): + + + computer locations + számítógéphelyszínek + + + Enter location name + Adja meg a helyszín nevét + + + Please enter the name of a location whose entries to query: + Adja meg azon helyszín nevét, amely bejegyzéseit le kívánja kérni: + + + location entries + Helyszín-bejegyzések + + + LDAP test failed + LDAP tesztelése sikertelen + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + Egy %1 sem kérhető le. Ellenőrizze %2 paraméter(eke)t vagy adja meg a már létező objektum nevét. + +%3 + + + and + és + + + LDAP test successful + LDAP tesztelése sikeres + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + Egy bejegyzést sem sikerült lekérni %1 konfigurációban. Ellenőrizze a "%2" paramétert. + +%3 + + + Browse + Tallózás + + + Test + Tesztelés + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + A kiszolgálónevet a teljesen minősített domain névként tároljuk (FQDN, például szerver.cegem.hu) + + + Computer hostname attribute + Számítógép kiszolgálónév attribútuma + + + Please enter a computer hostname to query: + Kérem, add meg a számítógép kiszolgálónevét, melyet lekérdezel: + + + Invalid hostname + Érvénytelen kiszolgálónév + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + Úgy konfiguráltad a rendszert, hogy az a számítógép kiszolgálónevét teljesen minősített domainnévként (FQDN) tárolja, de a kiszolgálónevet domain nélkül kell megadni. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + Úgy konfiguráltad a rendszert, hogy az a számítógép kiszolgálónevét domain nélküli egyszerű névként tárolja, de a kiszolgálónevet domain nélkül kell megadni. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + "%1" nevű felhasználó nem található. Kérem, ellenőrizd a felhasználónevet vagy a felhasználó-fa paramétert. + + + Enter hostname + Add meg a kiszolgálónevet + + + Please enter a computer hostname whose group memberships to query: + Kérem, add meg a számítógép kiszolgálónevét, melynek csoporttagságait lekérdezed: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + "%1" nevű kiszolgálónevű számítógép nem található. Kérem, ellenőrizd a kiszolgálónevet vagy a számítógépfa paramétert. + + + Hostname lookup failed + Kiszolgálónév keresése sikertelen + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + %1 IP címhez nem sikerült lekérdezni a kiszolgálónevet. Kérem, ellenőrizd a DNS szerver beállításait. + + + User login name attribute + A felhasználó bejelentkezési nevének attribútuma + + + Configured attribute for user login name or computer hostname (OpenLDAP) + A felhasználó bejelentkezési nevéhez vagy a számítógép kiszolgálónevéhez beállított attribútum (OpenLDAP) + + + computer containers + számítógép tárolók + + + + LdapPlugin + + Auto-configure the base DN via naming context + Alap DN automatikus konfigurálása névmeghatározással + + + Query objects from LDAP directory + Objektumok lekérdezése LDAP könyvtárból + + + Show help about command + Parancsról segítség megjelenítése + + + Commands for configuring and testing LDAP/AD integration + LDAP/AD integráció konfigurálásának és tesztelésének parancsai + + + Basic LDAP/AD support for Veyon + Alapvető LDAP/AD támogatás a Veyon számára + + + %1 (load computers and locations from LDAP/AD) + %1 (számítógépek és helyszínek betöltése LDAP/AD-ból) + + + %1 (load users and groups from LDAP/AD) + %1 (felhasználók és csoportok betöltése LDAP/AD-ból) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + Add meg a következő sémának megfelelő, érvényes LDAP url-t: "ldap[s]://[felhasználó[:jelszó]@]kiszolgáló[:port]" + + + No naming context attribute name given - falling back to configured value. + Hiányzik a névmeghatározás attribútum - visszaállunk a beállított értékekre. + + + Could not query base DN. Please check your LDAP configuration. + Nem sikerült az alap DN lekérdezése. Ellenőrizd az LDAP beállításait. + + + Configuring %1 as base DN and disabling naming context queries. + %1 beállítása alap DN-nek és a névmeghatározás-lekérdezések kikapcsolása. + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + Személyre szabott felhasználó-hitelesítő PAM szolgáltatás + + + User authentication + Felhasználó hitelesítés + + + Session management + Munkamenet-kezelés + + + Display manager users + Kezelő felhasználók megjelenítése + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + A bővítmény absztrakt függvényekkel egészíti ki a Linux platformot + + + + LocationDialog + + Select location + Helyszín kiválasztása: + + + enter search filter... + írd ide a keresési szűrőt... + + + + MainToolBar + + Configuration + Konfiguráció + + + Disable balloon tooltips + Buborék-eszköztippek kikapcsolása + + + Show icons only + Csak az ikonok megjelenítése + + + + MainWindow + + MainWindow + Fő ablak + + + toolBar + Eszköztár + + + General + Általános + + + &File + &Fájl + + + &Help + &Súgó + + + &Quit + &Kilépés + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + Beállítások be&töltése fájlból + + + Ctrl+O + Ctrl+O + + + About Qt + Qt névjegye + + + Authentication impossible + Hitelesítés nem lehetséges + + + Configuration not writable + Konfiguráció nem írható + + + Load settings from file + Beállítások betöltése fájlból + + + Save settings to file + Beállítások mentése fájlba + + + Unsaved settings + El nem mentett beállítások + + + There are unsaved settings. Quit anyway? + Vannak el nem mentett beállítások. Ennek ellenére kilépsz? + + + Veyon Configurator + Veyon Konfigurátor + + + Service + Szolgáltatás + + + Master + Mester + + + Access control + Hozzáférés-vezérlés + + + About Veyon + Veyon-ról + + + Auto + Automatikus + + + About + Névjegy + + + %1 Configurator %2 + %1 Konfigurátor %2 + + + JSON files (*.json) + JSON fájlok (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + A helyi konfiguráció háttere azt jelzi, hogy a konfiguráció nem írható. Futtasd %1 Konfigurátort magasabb jogosultságokkal. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + Nincsenek hitelesítési kulcsok vagy a jelenlegi kulcsok elavultak. Kérem, készíts új kulcsfájlokat %1 Konfigurátorral. Másik megoldás lehet bejelentkezési hitelesítés beállítása %1 Konfigurátorral. Különben nem tudod majd elérni a számítógépeket %1 használatával. + + + Access denied + Hozzáférés megtagadva + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + A helyi konfiguráció alapján nincs jogosultságod elérni a a hálózatban lévő számítógépeket. Kérem, jelentkezz be másik felhasználói fiókkal vagy kérd meg a rendszer üzemeltetőjét, hogy ellenőrizze a helyi konfigurációt. + + + Screenshots + Képernyőképek + + + Feature active + Aktív szolgáltatás + + + The feature "%1" is still active. Please stop it before closing %2. + "%1" szolgáltatás még aktív. Kérem, állítsa le, mielőtt %2-t bezárja + + + Reset configuration + Konfiguráció alapértelmezettre állítása + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Biztos, hogy a helyi konfiguráció és az összes beállítás értékét visszaállítod az alapértelmezett értékére? + + + Search users and computers + Felhasználók és számítógépek keresse + + + Align computers to grid + Számítógépek rácsba illesztése + + + %1 Configurator + %1 Konfigurátor + + + Insufficient privileges + Nem elegendő jogosultság + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + Nem sikerült rendszergazdai jogosultsággal indulni. Kérem, ellenőrizd, hogy sudo-típusú program telepítve van az asztali környezetedre. A program normál felhasználói jogosultságokkal fog futni. + + + Only show powered on computers + Csak a bekapcsolt számítógépek megjelenítése + + + &Save settings to file + Beállítások &mentése fájlba + + + &View + &Megtekint + + + &Standard + &Standard + + + &Advanced + &Haladó + + + Use custom computer arrangement + Egyéni számítógép-elhelyezés használata + + + Locations && computers + Helyszínek && számítógépek + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Mappák + + + User configuration + Felhasználói konfigurációk + + + Feature on computer double click: + Számítógépen a dupla kattintás: + + + Features + Szolgáltatások + + + All features + Összes szolgáltatás + + + Disabled features + Szolgáltatások kikapcsolása + + + Screenshots + Képernyőképek + + + <no feature> + <nincs szolgáltatás> + + + Basic settings + Alapbeállítások + + + Behaviour + Viselkedés + + + Enforce selected mode for client computers + Kiválasztott mód rákényszerítése a kliens számítógépekre + + + Hide local computer + Helyi számítógép elrejtése + + + Hide computer filter field + Számítógépszűrő mező elrejtése + + + Actions such as rebooting or powering down computers + Műveletek, mint a számítógépek újraindítása vagy kikapcsolása + + + User interface + Felhasználói felület + + + Background color + Háttérszín + + + Thumbnail update interval + Indexkép-frissítési időköz + + + ms + ms + + + Program start + Program indítása + + + Modes and features + Módok és szolgáltatások + + + User and computer name + Felhasználó- és számítógépnév + + + Only user name + Csak felhasználónév + + + Only computer name + Csak számítógépnév + + + Computer thumbnail caption + Számítógép indexképének felirata + + + Text color + Betűszín + + + Sort order + Rendezés + + + Computer and user name + Számítógép és felhasználónév + + + Computer locations + Számítógéphelyszínek + + + Show current location only + Csak a jelenlegi helyszín megjelenítése + + + Allow adding hidden locations manually + Rejtett helyszínek kézi hozzáadásának engedélyezése + + + Hide empty locations + Üres helyszínek elrejtése + + + Show confirmation dialog for potentially unsafe actions + Megerősítő párbeszédablak megjelentése potenciálisan veszélyes műveleteknél + + + Perform access control + Hozzáférés-vezérlés elvégzése + + + Automatically select current location + Jelenlegi helyszín automatikus kiválasztása + + + Automatically open computer select panel + Számítógépkiválasztási-panel automatikus megnyitása + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + Automatikus + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + Monitorozási + + + Builtin monitoring mode + Beépített monitorozási mód + + + This mode allows you to monitor all computers at one or more locations. + Ez az alapértelmezett mód, ami lehetővé teszi, hogy monitorozd az egy vagy több helyszínen lévő összes számítógépet. + + + + NetworkObjectTreeModel + + Locations/Computers + Helyszínek/számítógépek + + + + OpenWebsiteDialog + + Open website + Weboldal megnyitása + + + e.g. Veyon + pl. Veyon + + + Remember and add to website menu + Emlékezz és add a weboldal-menühöz + + + e.g. www.veyon.io + pl. www.veyon.io + + + Please enter the URL of the website to open: + Kérem, adja meg a megnyitandó weboldal URL-jét: + + + Name: + Név: + + + + PasswordDialog + + Username + Felhasználónév + + + Password + Jelszó + + + Veyon Logon + Veyon bejelentkezés + + + Authentication error + Hitelesítési hiba + + + Logon failed with given username and password. Please try again! + A megadott felhasználónévvel és jelszóval a bejelentkezés sikertelen. Próbáld újra! + + + Please enter your username and password in order to access computers. + Kérem, a számítógépek eléréséhez add meg felhasználóneved és jelszavad. + + + + PowerControlFeaturePlugin + + Power on + Bekapcsolás + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Kattints erre a gombra az összes számítógép bekapcsolásához. Így megúszod, hogy egyesével kapcsolgasd be őket. + + + Reboot + Újraindítás + + + Click this button to reboot all computers. + Kattints erre a gombra az összes számítógép újraindításához. + + + Power down + Kikapcsolás + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Kattints erre a gombra az összes számítógép kikapcsolásához. Így megúszod, hogy egyesével kapcsolgasd ki őket. + + + Power on/down or reboot a computer + Egy számítógép be-/kikapcsolása vagy újraindítása + + + Confirm reboot + Újraindítás megerősítése + + + Confirm power down + Kikapcsolás megerősítése + + + Do you really want to reboot the selected computers? + Biztos, hogy újraindítod a kiválasztott számítógépeket? + + + Do you really want to power down the selected computer? + Biztos, hogy kikapcsolod a kiválasztott számítógépeket? + + + Power on a computer via Wake-on-LAN (WOL) + Egy számítógép bekapcsolása hálózati ébresztéssel (WOL) + + + MAC ADDRESS + FIZIKAI CÍM + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + Ez a parancs egy hálózati ébresztő jelcsomagot (WOL) szór a hálózaton, hogy a megadott fizikai című számítógépeket bekapcsolja. + + + Please specify the command to display help for! + Kérem, válaszd ki az a parancsot, melynek súgóját megjelenítsük! + + + Invalid MAC address specified! + A fizikai cím érvénytelen! + + + Commands for controlling power status of computers + A számítógépek működési állapotát módosító parancsok + + + Power down now + Leállítás azonnal + + + Install updates and power down + Frissítések telepítése és leállítás + + + Power down after user confirmation + Leállítás a felhasználó jóváhagyása után + + + Power down after timeout + Leállítás a megadott idő lejárta után + + + The computer was remotely requested to power down. Do you want to power down the computer now? + A számítógép leállítását távolról kezdeményezték. Leállítja most a számítógépet? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + A számítógép %1 perc %2 másodperc múlva automatikusan le fog állni. Kérjük, mentse el munkáját és zárja be az összes futó programot. + + + + PowerDownTimeInputDialog + + Power down + Kikapcsolás + + + Please specify a timeout for powering down the selected computers: + Kérem, add meg, hogy kiválasztott számítógépek leállításának mennyi legyen az időtúllépése: + + + minutes + perc + + + seconds + másodperc + + + + RemoteAccessFeaturePlugin + + Remote view + Távoli megtekintés + + + Open a remote view for a computer without interaction. + Egy számítógépet tudsz megtekinteni távolról, beavatkozás nélkül. + + + Remote control + Távoli vezérlés + + + Open a remote control window for a computer. + Egy számítógépet tudsz távolról vezérelni. + + + Remote access + Távoli elérés + + + Remote view or control a computer + Egy számítógép távoli megtekintése vagy vezérlése + + + Please enter the hostname or IP address of the computer to access: + Kérem, add meg az elérni kívánt gép nevét vagy IP címét: + + + Show help about command + Parancsról segítség megjelenítése + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 - %2 távoli hozzáférés + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + Csak megtekintés + + + Remote control + Távoli vezérlés + + + Send shortcut + Parancsikon küldése + + + Fullscreen + Teljes képernyő + + + Window + Ablak + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Menü + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + %1 csatlakozik + + + Connected. + Sikeresen csatlakozott. + + + Screenshot + Képernyőkép + + + Exit + Kilépés + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Kérem, add meg a kiválasztott számítógép(ek)en futtatandó programokat vagy parancsokat. Több programot/parancsot soronként adj meg. + + + Run programs + Programok futtatása + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + például "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + Név: + + + Remember and add to program menu + Emlékezz és add a programmenühöz + + + e.g. VLC + pl. VLC + + + + ScreenLockFeaturePlugin + + Lock + Zárolás + + + Unlock + Zárolás feloldása + + + Lock screen and input devices of a computer + Számítógép képernyőjének és bemeneti eszközeinek zárolása + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Az összes felhasználó figyelmének elnyerés érdekében evvel a gombbal zárolhatod számítógépüket. Ebben a módban az összes bemeneti eszközt zároljuk és a képernyő elfeketedik. + + + Lock input devices + Beviteli eszközök zárolása + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + ismeretlen + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + Képernyőkép nem készíthető, mert '%1' mappa nem létezik és nem is hozható létre. + + + Screenshot + Képernyőkép + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + Képernyőkép + + + Use this function to take a screenshot of selected computers. + Használd ezt a funkciót, hogy képernyőképet készíts a kiválasztott számítógépekről. + + + Screenshots taken + Elkészített képernyőképek + + + Screenshot of %1 computer have been taken successfully. + %1 számítógépről sikeresen készítettél képernyőképet. + + + Take screenshots of computers and save them locally. + Számítógépekről képernyőképek készítése és mentése a helyi gépre. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Az összes képernyőképet felsoroljuk itt. Képernyőképet a számítógép helyi menüjében lévő "Képernyőkép" gombra kattintva készíthetsz. A képernyőképeket az alul lévő gombokkal tudod kezelni. + + + User: + Felhasználó: + + + Computer: + Számítógép: + + + Date: + Dátum: + + + Time: + Idő: + + + Show + Megjelenítés + + + Delete + Törlés + + + Screenshot + Képernyőkép + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Általános + + + Autostart + Automatikus indítás + + + Hide tray icon + Tálcaikon elrejtése + + + Start service + Szolgáltatás indítása + + + Stopped + Leállítva + + + Stop service + Szolgáltatás leállítása + + + State: + Állapot: + + + Enable firewall exception + Fűzfal-kivétel bekapcsolása + + + Allow connections from localhost only + Kapcsolatok engedélyezése csak a localhost-ból + + + VNC server + VNC szerver + + + Plugin: + Bővítmény: + + + Restart %1 Service + %1 szolgáltatás újraindítása + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Az összes beállítást sikeresen mentette. A változások életbe lépéséhez %1 szolgáltatást újra kell indítani. Újraindítod most? + + + Running + Jelenleg fut + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + A funkció bekapcsolása esetén a szerver minden egyes számítógépen létrejövő interaktív munkamenethez külön szerverfolyamatot indít. + + + Show notification on remote connection + Távoli csatlakozás esetén értesítés megjelenítése + + + Show notification when an unauthorized access is blocked + Értesítés jelenjen meg amikor egy nem hitelesített hozzáférést blokkolunk. + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + Beépített VNC + + + Feature manager + Funkciókezelő + + + Demo server + Demószerver + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + %1 szolgáltatás indítása + + + Stopping service %1 + %1 szolgáltatás leállítása + + + Registering service %1 + %1 szolgáltatás nyilvántartásban vétele + + + Unregistering service %1 + %1 szolgáltatás törlése a nyilvántartásból + + + Service control + Szolgáltatásvezérlés + + + + ServiceControlPlugin + + Service is running + Veyon szolgáltatás jelenleg fut + + + Service is not running + Veyon szolgáltatás jelenleg nem fut + + + Configure and control Veyon service + Veyon szolgáltatás konfigurálása és felügyelete + + + Register Veyon Service + Regisztrált Veyon szolgáltatás + + + Unregister Veyon Service + Nem regisztrált Veyon szolgáltatás + + + Start Veyon Service + Veyon szolgáltatás indítása + + + Stop Veyon Service + Veyon szolgáltatás leállítása + + + Restart Veyon Service + Veyon Szolgáltatás újraindítása + + + Query status of Veyon Service + Veyon Szolgáltatás állapotának lekérdezése + + + Commands for configuring and controlling Veyon Service + Veyon Szolgáltatás konfigurációs és felügyeleti parancsai + + + + ShellCommandLinePlugin + + Run command file + Parancsfájl futtatása + + + File "%1" does not exist! + "%1" fájl nem létezik! + + + Interactive shell and script execution for Veyon Control + Interaktív héj és szkriptfuttatás a Veyon vezérlésből + + + Commands for shell functionalities + Parancsok héjfunkciókhoz + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + Rendszer tálcaikon + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + Rendszerfelhasználói csoportok esetén a felhasználói csoportok háttere: + + + Default (system user groups) + Alapértelmezett (rendszerfelhasználói csoportok) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + A Veyon belső komponenseinek és funkciónak tesztelése + + + Commands for testing internal components and functions of Veyon + A Veyon belső komponensek és funkciók teszteléséhez parancsok + + + + TextMessageDialog + + Send text message + Szöveges üzenet küldése + + + Use the field below to type your message which will be sent to all selected users. + Az alábbi mezőbe gépeld az összes kiválasztott felhasználóknak küldendő üzenetedet. + + + + TextMessageFeaturePlugin + + Text message + Szöveges üzenet + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Használd ezt a funkciót, hogy üzenetet küldj az összes felhasználónak (például arról, hogy új feladatot adsz nekik). + + + Message from teacher + Üzenet a tanártól + + + Send a message to a user + Üzenet küldése egy felhasználónak + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Réteges (félig átlátszó) ablakok elkapásának bekapcsolása + + + Poll full screen (leave this enabled per default) + Beépített UltraVNC szerver konfiguráció + + + Low accuracy (turbo mode) + Alacsony pontosság (turbó mód) + + + Builtin UltraVNC server configuration + Beépített UltraVNC szerver konfiguráció + + + Enable multi monitor support + Többmonitoros támogatás bekapcsolása + + + Enable Desktop Duplication Engine on Windows 8 and newer + Asztalduplikációs motor bekapcsolása Windows 8 vagy újabb rendszeren + + + Maximum CPU usage + + + + + UserConfig + + No write access + Nincs írási hozzáférés + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Személyes beállításait nem sikerült menteni! Kérem, ellenőrizd a felhasználói konfigurációs fájl útvonalát %1 Konfigurátorban. + + + + UserLoginDialog + + User login + Felhasználó bejelentkezése + + + Please enter a username and password for automatic login on all computers. + Kérem, adj meg egy felhasználónevet és egy jelszót az automatikus bejelentkezéshez az összes számítógépen. + + + Username + Felhasználónév + + + Password + Jelszó + + + + UserSessionControlPlugin + + Log in + Bejelentkezés + + + Click this button to log in a specific user on all computers. + Kattints erre a gombra, hogy egy megadott felhasználó bejelentkezzen az összes számítógépre. + + + Log off + Kijelentkezés + + + Click this button to log off users from all computers. + Kattints erre a gombra, hogy a felhasználókat kijelentkeztesd az összes számítógépről. + + + Confirm user logoff + Felhasználó kijelentkeztetésének megerősítése + + + Do you really want to log off the selected users? + Biztos, hogy kijelentkezteted a kiválasztott felhasználókat? + + + User session control + Felhasználói munkamenet-vezérlés + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [SIKERTELEN] + + + Invalid command! + Érvénytelen parancs! + + + Available commands: + Elérhető műveletek: + + + Invalid arguments given + Érvénytelen az argumentum + + + Not enough arguments given - use "%1 help" for more information + Nincs elegendő argumentum - további információért használja a "%1 súgót" + + + Unknown result! + Ismeretlen végeredmény! + + + Available modules: + Elérhető modulok: + + + No module specified or module not found - available modules are: + A modul nincs megadva vagy nem található - az elérhető modulok: + + + Plugin not licensed + A bővítmény nem licencelt + + + INFO + INFO + + + ERROR + HIBA + + + USAGE + HASZNÁLAT + + + DESCRIPTION + LEÍRÁS + + + EXAMPLES + PÉLDÁK + + + WARNING + FIGYELMEZTETÉS + + + + VeyonServiceControl + + Veyon Service + Veyon szolgáltatás + + + + VncViewWidget + + Establishing connection to %1 ... + Kapcsolat létrehozása: %1 ... + + + + WebApiConfigurationPage + + Web API + + + + General + Általános + + + Network port + Hálózati port + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + mp + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + SAS generáció beállítását szoftverrel nem sikerült módosítani. Ctrl+Alt+Del küldése távoli vezérlésről nem fog működni. + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + Általános + + + Enable SAS generation by software (Ctrl+Alt+Del) + SAS generáció bekapcsolása szoftverrel (Ctrl+Alt+Del) + + + Screen lock + Képernyő zárolása + + + Hide taskbar + Tálca elrejtése + + + Hide start menu + Start menü elrejtése + + + Hide desktop + Asztal elrejtése + + + User authentication + Felhasználó hitelesítés + + + Use alternative user authentication mechanism + Alternatív felhasználói hitelesítési mechanizmus használata + + + User login + Felhasználó bejelentkezése + + + Input start delay + Add meg az indítási késleltetést + + + Simulated key presses interval + Billentyűlenyomási időköz szimulálása + + + Confirm legal notice (message displayed before user logs in) + Jogi értesítést erősítése (az üzenet a felhasználói bejelentkezés előtt jelenik meg) + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + A bővítmény absztrakt függvényekkel egészíti ki a Windows platformot + + + + WindowsServiceControl + + The service "%1" is already installed. + "%1" szolgáltatás már telepítve van. + + + The service "%1" could not be installed. + "%1" szolgáltatás nem telepíthető. + + + The service "%1" has been installed successfully. + "%1" szolgáltatást sikeresen telepítette. + + + The service "%1" could not be uninstalled. + "%1" szolgáltatás nem távolítható el. + + + The service "%1" has been uninstalled successfully. + "%1" szolgáltatást sikeresen eltávolította. + + + The start type of service "%1" could not be changed. + "%1" szolgáltatás indítási típusa nem módosítható. + + + Service "%1" could not be found. + "%1" szolgáltatás nem található. + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Beépített x11vnc szerver konfiguráció + + + Custom x11vnc parameters: + Egyéni x11vnc paraméterek: + + + Do not use X Damage extension + Ne használd az X Damage bővítményt + + + diff --git a/translations/veyon_id.ts b/translations/veyon_id.ts new file mode 100644 index 0000000..0adf424 --- /dev/null +++ b/translations/veyon_id.ts @@ -0,0 +1,4061 @@ + + + + + AboutDialog + + About + Tentang + + + Translation + Terjemahan + + + License + Lisensi + + + About Veyon + Tentang Veyon + + + Contributors + Kontributor + + + Version: + Versi: + + + Website: + Situs web: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Bahasa sekarang belum diterjemahkan (atau native Bahasa Inggris) + +Jika anda tertarik menerjemahkan Veyon pada bahasa lokal Anda atau bahasa lain atau anda ingin meningkatkan terjemahan yang telah ada, silakan hubungi pengembang Veyon! + + + About %1 %2 + Tentang %1 %2 + + + Support Veyon project with a donation + Dukung proyek Veyon dengan donasi + + + + AccessControlPage + + Computer access control + Akses kontrol komputer + + + Grant access to every authenticated user (default) + Berikan akses ke setiap pengguna yang diautentikasi (bawaan) + + + Test + Tes + + + Process access control rules + Memproses aturan kontrol akses + + + User groups authorized for computer access + Grup pengguna yang diotorisasi untuk akses komputer + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Silakan tambahkan grup yang anggotanya harus diotorisasi untuk mengakses komputer di jaringan Veyon Anda. + + + Authorized user groups + Kelompok pengguna yang diotorisasi + + + All groups + Semua grup + + + Access control rules + Aturan kontrol akses + + + Add access control rule + Tambah aturan kontrol akses + + + Remove access control rule + Hapus aturan kontrol akses + + + Move selected rule down + Pindahkan aturan yang dipilih ke bawah + + + Move selected rule up + Pindahkan aturan yang dipilih ke atas + + + Edit selected rule + Edit aturan yang dipilih + + + Enter username + Masukkan nama pengguna + + + Please enter a user login name whose access permissions to test: + Silakan masukkan nama login pengguna yang izin aksesnya untuk diuji: + + + Access allowed + Akses diijinkan + + + The specified user is allowed to access computers with this configuration. + Pengguna yang ditentukan diizinkan untuk mengakses komputer dengan konfigurasi ini. + + + Access denied + Akses ditolak + + + The specified user is not allowed to access computers with this configuration. + Pengguna yang ditentukan tidak diizinkan mengakses komputer dengan konfigurasi ini. + + + Enable usage of domain groups + Aktifkan penggunaan grup domain + + + User groups backend: + Kelompok pengguna backend: + + + Missing user groups backend + Backend grup pengguna tidak ada + + + No default user groups plugin was found. Please check your installation! + Tidak ditemukan pengaya grup pengguna bawaan. Sila cek instalasi Anda! + + + Restrict access to members of specific user groups + Batasi akses ke pengguna dari grup pengguna tertentu + + + + AccessControlRuleEditDialog + + Edit access control rule + Edit aturan kontrol akses + + + General + Umum + + + enter a short name for the rule here + masukkan nama singkat untuk aturan di sini + + + Rule name: + Nama aturan: + + + enter a description for the rule here + masukkan penjelasan untuk aturan di sini + + + Rule description: + Penjelasan aturan: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Balikkan semua kondisi ("adalah/telah" diartikan sebagai "adalah/belum") + + + Conditions + Kondisi + + + is member of group + adalah anggota grup + + + Accessing computer is localhost + Mengakses komputer adalah localhost + + + Accessing user is logged on user + Pengguna yang mengakses dicatat sebagai pengguna + + + Accessing user is already connected + Mengakses pengguna sudah terhubung + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Jika lebih dari satu kondisi diaktifkan, setiap kondisi harus dipenuhi untuk membuat aturan berlaku (logika AND). Jika hanya satu dari beberapa kondisi yang harus dipenuhi (OR logis), buat beberapa aturan kontrol akses. + + + Action + Aksi + + + Allow access + Ijinkan akses + + + Deny access + Larang akses + + + Ask logged on user for permission + Minta izin pengguna yang masuk untuk login + + + None (rule disabled) + Tidak ada (aturan dimatikan) + + + Accessing user + Mengakses Pengguna + + + Accessing computer + Mengakses Komputer + + + Local (logged on) user + Pengguna lokal (masuk log) + + + Local computer + Komputer lokal + + + Always process rule and ignore conditions + Selalu proses aturan dan abaikan kondisi + + + No user logged on + Tidak ada pengguna masuk + + + Accessing user has one or more groups in common with local (logged on) user + Pengguna yang mengakses memiliki satu atau beberapa grup yang sama dengan pengguna lokal (masuk) + + + Accessing computer and local computer are at the same location + Mengakses komputer dan komputer lokal berada di lokasi yang sama + + + is located at + berlokasi + + + + AccessControlRulesTestDialog + + Access control rules test + Pengujian aturan kontrol akses + + + Accessing user: + Mengakses pengguna: + + + Local computer: + Komputer lokal + + + Accessing computer: + Mengakses komputer: + + + Please enter the following user and computer information in order to test the configured ruleset. + Masukkan informasi pengguna dan komputer berikut untuk menguji kumpulan aturan yang dikonfigurasi. + + + Local user: + Pengguna lokal: + + + Connected users: + Pengguna terhubung: + + + The access in the given scenario is allowed. + Akses dalam skenario yang diberikan diizinkan. + + + The access in the given scenario is denied. + Akses dalam skenario yang diberikan ditolak. + + + The access in the given scenario needs permission of the logged on user. + Akses dalam skenario yang diberikan membutuhkan izin dari pengguna yang masuk. + + + ERROR: Unknown action + GALAT: Aksi tidak diketahui + + + Test result + Hasil test + + + + AuthKeysConfigurationPage + + Authentication keys + Kunci otentikasi + + + Introduction + Perkenalan + + + Key file directories + Direktori berkas kunci + + + Public key file base directory + Direktori basis berkas kunci publik + + + Private key file base directory + Direktori basis file kunci pribadi + + + Available authentication keys + Kunci autentikasi tersedia + + + Create key pair + Buat pasangan kunci + + + Delete key + Hapus kunci + + + Import key + Impor kunci + + + Export key + Ekspor kunci + + + Set access group + Setel grup akses + + + Key files (*.pem) + Berkas kunci (*.pem) + + + Authentication key name + Nama kunci otentikasi + + + Please enter the name of the user group or role for which to create an authentication key pair: + Silakan masukkan nama grup pengguna atau peran yang akan dibuat pasangan kunci otentikasi: + + + Do you really want to delete authentication key "%1/%2"? + Anda yakin ingin mengapus kunci otentikasi "%1/%2"? + + + Please select a key to delete! + Pilih kunci yang akan dihapus! + + + Please enter the name of the user group or role for which to import the authentication key: + + + + Please select a key to export! + Pilih kunci yang akan diekspor! + + + Please select a user group which to grant access to key "%1": + + + + Please select a key which to set the access group for! + + + + Please perform the following steps to set up key file authentication: + Silakan lakukan langkah-langkah berikut untuk mengatur otentikasi file utama: + + + 1) Create a key pair on the master computer. + 1) Buat pasangan kunci di komputer master. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Tetapkan grup akses yang anggotanya harus diizinkan untuk mengakses komputer lain. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Ekspor kunci publik dan impor pada semua komputer klien dengan nama yang sama. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Sepasang kunci otentikasi terdiri dari dua kunci kriptografi yang digabungkan, kunci pribadi dan kunci publik. +Kunci privat memungkinkan pengguna pada komputer master untuk mengakses komputer klien. +Penting bahwa hanya pengguna yang berwenang yang memiliki akses baca ke file kunci pribadi. +Kunci publik digunakan pada komputer klien untuk mengautentikasi permintaan koneksi masuk. + + + + AuthKeysManager + + Please check your permissions. + Bahasa: + + + Key name contains invalid characters! + Nama kunci berisi karakter invalid! + + + Invalid key type specified! Please specify "%1" or "%2". + + + + Specified key does not exist! Please use the "list" command to list all installed keys. + + + + One or more key files already exist! Please delete them using the "delete" command. + + + + Creating new key pair for "%1" + Membuat pasangan kunci baru untuk "% 1" + + + Failed to create public or private key! + Gagal membuat kunci public atau private! + + + Newly created key pair has been saved to "%1" and "%2". + + + + Could not remove key file "%1"! + Tidak dapat menghaps file kunci "%1"! + + + Could not remove key file directory "%1"! + Tidak dapat menghapus kunci file direktori "%1"! + + + Failed to create directory for output file. + Gagal dalam membuat direktori untuk output file. + + + File "%1" already exists. + File "%1" telah tersedia' + + + Failed to write output file. + Gagal menulis file keluaran. + + + Key "%1/%2" has been exported to "%3" successfully. + + + + Failed read input file. + Gagal membaca file input. + + + File "%1" does not contain a valid private key! + + + + File "%1" does not contain a valid public key! + + + + Failed to create directory for key file. + Gagal membuat direktori untuk file kunci. + + + Failed to write key file "%1". + + + + Failed to set permissions for key file "%1"! + + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + + + + Failed to convert private key to public key + + + + Failed to create directory for private key file "%1". + + + + Failed to save private key in file "%1"! + + + + Failed to set permissions for private key file "%1"! + + + + Failed to create directory for public key file "%1". + + + + Failed to save public key in file "%1"! + + + + Failed to set permissions for public key file "%1"! + + + + Failed to set owner of key file "%1" to "%2". + + + + Failed to set permissions for key file "%1". + + + + Key "%1" is now accessible by user group "%2". + + + + <N/A> + <Tidak Diketahui> + + + Failed to read key file. + + + + + AuthKeysPlugin + + Create new authentication key pair + + + + Delete authentication key + Hapus kunci otentikasi + + + List authentication keys + Daftar kunci otentikasi + + + Import public or private key + Impor kunci privat atau publik + + + Export public or private key + Ekspor kunci privat atau publik + + + Extract public key from existing private key + Ekstrak kunci publik dari kunci privat yang ada + + + Set user group allowed to access a key + Setel grup pengguna yang diijinkan untuk mengakses kunci + + + KEY + KUNCI + + + ACCESS GROUP + GRUP AKSES + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + + NAME + NAMA + + + FILE + BERKAS + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + + Please specify the command to display help for! + + + + TYPE + TIPE + + + PAIR ID + ID PASANGAN + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + Nama + + + Type + Jenis + + + Access group + Grup akses + + + Pair ID + ID Pasangan + + + + BuiltinDirectoryConfigurationPage + + Computers + Komputer + + + Name + Nama + + + Host address/IP + Alamat host/IP + + + MAC address + MAC address + + + Add new computer + Tambah komputer baru + + + Remove selected computer + Hapus komputer terpilih + + + New computer + Komputer baru + + + Builtin directory + + + + Locations & computers + Lokasi & komputer + + + Locations + Lokasi + + + Add new location + Tambahkan lokasi baru + + + Remove selected location + Hapus lokasi terpilih + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + Impor berkas CSV dimungkinkan melalui antarmuka baris perintah. Informasi lebih lanjut, lihat <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">dokumentasi daring</a>. + + + New location + Lokasi baru + + + + BuiltinDirectoryPlugin + + Show help for specific command + Tampilkan bantuan untuk perintah spesifik + + + Import objects from given file + Impor obyek dari berkas yang diberikan + + + Export objects to given file + + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + Jenis + + + Name + Nama + + + Host address + Alamat host + + + MAC address + MAC address + + + Specified object not found. + Obyek yang dimaksud tidak ditemukan + + + File "%1" does not exist! + Berkas "%1" tidak ada! + + + Can't open file "%1" for reading! + Tidak dapat membuka berkas "%1" untuk dibaca! + + + Unknown argument "%1". + Argumen "%1" tidak diketahui. + + + Computer "%1" (host address: "%2" MAC address: "%3") + Komputer "%1" (alamat host: "%2" MAC address: "%3") + + + Unclassified object "%1" with ID "%2" + + + + None + Tidak ada + + + Computer + Komputer + + + Root + + + + Invalid + Invalid + + + Error while parsing line %1. + + + + Network object directory which stores objects in local configuration + Direktori objek jaringan yang menyimpan objek dalam konfigurasi lokal + + + Commands for managing the builtin network object directory + Perintah untuk mengelola direktori objek jaringan terpasang + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + Tidak dapat membuka berkas "%1" untuk ditulisi! + + + No format string specified! + + + + Object UUID + Objek UUID + + + Parent UUID + Induk UUID + + + Add a location or computer + Tambahkan lokasi atau komputer + + + Clear all locations and computers + Bersihkan semua lokasi dan komputer + + + Dump all or individual locations and computers + + + + List all locations and computers + Daftar semua lokasi dan komputer + + + Remove a location or computer + Hapus lokasi atau komputer + + + Location "%1" + Lokasi "%1" + + + Builtin (computers and locations in local configuration) + Terpasang (komputer dan lokasi dalam konfigurasi lokal) + + + Location + Lokasi + + + FILE + BERKAS + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + TIPE + + + NAME + NAMA + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + + + + + ComputerControlListModel + + Host/IP address: %1 + Host/Alamat IP: %1 + + + Active features: %1 + Fitur aktif: %1 + + + Online and connected + Daring dan terhubung + + + Establishing connection + + + + Computer offline or switched off + + + + Authentication failed or access denied + + + + Disconnected + Terputus + + + No user logged on + Tidak ada pengguna masuk + + + Logged on user: %1 + + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + + + + Authentication error + Otentikasi gagal + + + Remote access + + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + Pengguna + + + Missing network object directory plugin + Pengaya direktori objek jaringan tidak ada + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Tidak ada pengaya direktori objek jaringan bawaan yang ditemukan. Silakan periksa instalasi Anda atau konfigurasikan backend direktori objek jaringan yang berbeda melalui konfigurator %1. + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + Tidak dapat menentukan lokasi komputer ini. Ini menunjukkan masalah dengan konfigurasi sistem. Semua lokasi akan ditampilkan di panel pilih komputer sebagai gantinya. + + + + ComputerSelectPanel + + Computer search + + + + Add location + + + + Save computer/user list + + + + Select output filename + + + + CSV files (*.csv) + + + + File error + + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Silakan tentukan berkas konfigurasi yang ada untuk diimpor. + + + Please specify a valid filename for the configuration export. + Silakan tentukan nama berkas yang valid untuk ekspor konfigurasi. + + + Please specify a valid key. + + + + Specified key does not exist in current configuration! + Kunci yang ditentukan tidak ada dalam konfigurasi saat ini! + + + Please specify a valid value. + + + + Configure Veyon at command line + + + + Output file is not writable! + Berkas keluaran tidak dapat ditulisi! + + + Output directory is not writable! + Direktori keluaran tidak dapat ditulisi! + + + Configuration file is not readable! + Berkas konfigurasi tidak terbaca! + + + Clear system-wide Veyon configuration + Hapus konfigurasi Veyon di seluruh sistem + + + List all configuration keys and values + Daftar semua konfigurasi kunci dan nilai + + + Import configuration from given file + Impor konfigurasi dari berkas yang diberikan + + + Export configuration to given file + Ekspor konfigurasi ke berkas yang diberikan + + + Read and output configuration value for given key + Baca dan hasilkan nilai konfigurasi untuk kunci yang diberikan + + + Write given value to given configuration key + Baca dan hasilkan nilai konfigurasi untuk kunci yang diberikan + + + Unset (remove) given configuration key + Batalkan (hapus) kunci konfigurasi yang diberikan + + + Commands for managing the configuration of Veyon + Perintah untuk mengelola konfigurasi Veyon + + + Upgrade and save configuration of program and plugins + Tingkatkan dan simpan konfigurasi program dan pengaya + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + + + + + DemoConfigurationPage + + Demo server + Server demo + + + Tunables + + + + ms + ms + + + Key frame interval + + + + Memory limit + + + + MB + MB + + + Update interval + + + + s + + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + + + + Window demo + + + + Give a demonstration by screen broadcasting + + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + + + + Confirm desktop access + Konfirmasi akses desktop + + + Never for this session + Tidak pernah untuk sesi ini + + + Always for this session + Selalu untuk sesi ini + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + + DesktopServicesConfigurationPage + + Programs & websites + + + + Predefined programs + + + + Name + Nama + + + Path + + + + Add new program + + + + Remove selected program + + + + Predefined websites + + + + Remove selected website + + + + URL + URL + + + New program + + + + New website + + + + + DesktopServicesFeaturePlugin + + Run program + + + + Open website + + + + Click this button to open a website on all computers. + + + + Start programs and services in user desktop + + + + Click this button to run a program on all computers. + + + + Run program "%1" + + + + Custom program + + + + Open website "%1" + + + + Custom website + + + + + DocumentationFigureCreator + + Teacher + + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + + + + Port: + + + + Password: + + + + + FeatureControl + + Feature control + + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + Direktori + + + Destination directory + + + + Default source directory + + + + Options + Opsi + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + Opsi + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + + + + Select one or more files to transfer + + + + Transfer files to remote computer + + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + Antarmuka pengguna + + + Language: + + + + Use system language setting + + + + Veyon + + + + Logging + + + + Log file directory + + + + Log level + + + + Nothing + + + + Only critical messages + + + + Errors and critical messages + + + + Warnings and errors + + + + Information, warnings and errors + + + + Debug messages and everything else + + + + Limit log file size + + + + Clear all log files + + + + Log to standard error output + + + + Network object directory + Direktori objek jaringan + + + Backend: + Backend: + + + Update interval: + + + + %1 service + + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + + + + All log files were cleared successfully. + + + + Error + + + + Could not remove all log files. + + + + MB + MB + + + Rotate log files + + + + x + + + + seconds + + + + Write to logging system of operating system + + + + Authentication + + + + Method: + + + + Logon authentication + + + + Key file authentication + + + + Test + Tes + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + Deskripsi masalah LDAP: %1 + + + + LdapConfigurationPage + + Basic settings + Setelan dasar + + + General + Umum + + + LDAP server and port + + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + + + + e.g. OU=Computers + + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Berhasil terhubung ke server LDAP dan melakukan ikatan LDAP. Pengaturan LDAP dasar dikonfigurasikan dengan benar. + + + LDAP base DN test failed + + + + LDAP base DN test successful + + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + Masukkan nama pengguna + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + + + + Please enter a group name whose members to query: + + + + group members + + + + Group not found + + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + + + + users + + + + user groups + + + + computer groups + + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + + + + LDAP %1 test failed + + + + LDAP %1 test successful + + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + Keamanan Koneksi + + + TLS certificate verification + Verifikasi sertifikat TLS + + + System defaults + + + + Never (insecure!) + + + + Custom CA certificate file + + + + None + Tidak ada + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + File sertifikat (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + Protokol enkripsi + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + Tampilkan semua masukan lokasi + + + List all locations + Daftar semua lokasi + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + Masukkan nama lokasi komputer + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + lokasi komputer + + + Enter location name + Masukkan nama lokasi + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + Tes + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + Tunjukkan bantuan tentang perintah + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + %1 (memuat komputer dan lokasi untuk LDAP/AD) + + + %1 (load users and groups from LDAP/AD) + %1 (memuat pengguna dan group untuk LDAP/AD) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Pengaya menerapkan fungsi abstrak untuk platform Linux + + + + LocationDialog + + Select location + + + + enter search filter... + + + + + MainToolBar + + Configuration + Konfigurasi + + + Disable balloon tooltips + Nonaktifkan tooltips balon + + + Show icons only + Hanya tampilkan icon + + + + MainWindow + + MainWindow + + + + toolBar + + + + General + Umum + + + &File + &File + + + &Help + &Bantuan + + + &Quit + &Keluar + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + + + + Ctrl+O + Ctrl+0 + + + About Qt + Tentang Qt + + + Authentication impossible + Otentikasi tidak dimungkinkan + + + Configuration not writable + Konfigurasi tidak dapat dirubah + + + Load settings from file + Memuat pengaturan dari file + + + Save settings to file + Simpan pengaturan kedalam file + + + Unsaved settings + Pengaturan belum tersimpan + + + There are unsaved settings. Quit anyway? + Terdapat pengaturan yang belum tersimpan, tetap keluar? + + + Veyon Configurator + Konfigurator Veyon + + + Service + Layanan + + + Master + + + + Access control + + + + About Veyon + Tentang Veyon + + + Auto + + + + About + Tentang + + + %1 Configurator %2 + + + + JSON files (*.json) + File JSON (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + Akses ditolak + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + Menurut konfigurasi lokal Anda tidak diperbolehkan mengakses komputer di jaringan. Silakan masuk dengan akun lain atau biarkan administrator sistem Anda memeriksa konfigurasi lokal. + + + Screenshots + Tangkapan layar + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + Pulihkan konfigurasi + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Apakah anda yakin untuk memulihkan konfigurasi lokal? + + + Search users and computers + Cari pengguna dan komputer + + + Align computers to grid + + + + %1 Configurator + + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + Tidak dapat memulai dengan hak administratif. Pastikan program seperti sudo diinstal untuk lingkungan desktop Anda! Program ini akan dijalankan dengan hak pengguna normal. + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Direktori + + + User configuration + Konfigurasi pengguna + + + Feature on computer double click: + Fitur pada komputer klik ganda: + + + Features + Fitur + + + All features + Semua fitur + + + Disabled features + Nonaktifkan fitur + + + Screenshots + Tangkapan layar + + + <no feature> + + + + Basic settings + Setelan dasar + + + Behaviour + Perilaku + + + Enforce selected mode for client computers + + + + Hide local computer + Sembunyikan komputer lokal + + + Hide computer filter field + Sembunyikan ruas filter komputer + + + Actions such as rebooting or powering down computers + Tindakan seperti menyalakan ulang atau mematikan komputer + + + User interface + Antarmuka pengguna + + + Background color + Warna Latar belakang + + + Thumbnail update interval + + + + ms + ms + + + Program start + + + + Modes and features + Mode dan fitur + + + User and computer name + Pengguna dan nama komputer + + + Only user name + Hanya nama pengguna + + + Only computer name + Hanya nama komputer + + + Computer thumbnail caption + + + + Text color + Warna teks + + + Sort order + Urutkan + + + Computer and user name + Komputer dan nama pengguna + + + Computer locations + Lokasi komputer + + + Show current location only + Hanya tampilkan lokasi sekarang + + + Allow adding hidden locations manually + + + + Hide empty locations + Sembunyikan lokasi kosong + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + + + + Name: + + + + + PasswordDialog + + Username + Nama Pengguna + + + Password + Kata sandi + + + Veyon Logon + Masuk Veyon + + + Authentication error + Otentikasi gagal + + + Logon failed with given username and password. Please try again! + + + + Please enter your username and password in order to access computers. + + + + + PowerControlFeaturePlugin + + Power on + Nyalakan + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + + Reboot + Nyalakan Ulang + + + Click this button to reboot all computers. + Klik tombol ini untuk menyalakan ulang semua komputer. + + + Power down + Matikan + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + + + + Confirm reboot + + + + Confirm power down + + + + Do you really want to reboot the selected computers? + + + + Do you really want to power down the selected computer? + + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + Perintah ini menyiarkan paket Wake-on-LAN (WOL) ke jaringan untuk menghidupkan komputer dengan alamat MAC yang diberikan. + + + Please specify the command to display help for! + + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + Matikan + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + + + + Open a remote view for a computer without interaction. + + + + Remote control + + + + Open a remote control window for a computer. + + + + Remote access + + + + Remote view or control a computer + + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + Tunjukkan bantuan tentang perintah + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + + + + Remote control + + + + Send shortcut + + + + Fullscreen + Layar penuh + + + Window + Jendela + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Menu + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + + + + Connected. + Tersambung. + + + Screenshot + Tangkapan layar + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + Jalankan program + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + Kunci + + + Unlock + Buka kunci + + + Lock screen and input devices of a computer + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + Tangkapan layar + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + Tangkapan layar + + + Use this function to take a screenshot of selected computers. + + + + Screenshots taken + + + + Screenshot of %1 computer have been taken successfully. + + + + Take screenshots of computers and save them locally. + + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + Pengguna: + + + Computer: + Komputer: + + + Date: + Tanggal: + + + Time: + Waktu: + + + Show + Tampilkan + + + Delete + Hapus + + + Screenshot + Tangkapan layar + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Umum + + + Autostart + Mulai otomatis + + + Hide tray icon + Sembunyikan ikon baki + + + Start service + Mulai layanan + + + Stopped + Dihentikan + + + Stop service + Hentikan layanan + + + State: + Status: + + + Enable firewall exception + Aktifkan pengecualian firewall + + + Allow connections from localhost only + + + + VNC server + + + + Plugin: + Pengaya: + + + Restart %1 Service + + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + + Running + Berjalan + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + + + + Sessions + Sesi sesi + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + Server demo + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + Mulai layanan %1 + + + Stopping service %1 + Matikan layanan %1 + + + Registering service %1 + Daftarkan layanan %1 + + + Unregistering service %1 + Batalkan pendaftaran layanan %1 + + + Service control + Kontrol Layanan + + + + ServiceControlPlugin + + Service is running + Layanan berjalan + + + Service is not running + Layanan tidak berjalan + + + Configure and control Veyon service + Konfigurasi dan kontrol layanan Veyon + + + Register Veyon Service + Daftarkan Layanan Veyon + + + Unregister Veyon Service + Batalkan Pendaftaran Layanan Veyon + + + Start Veyon Service + Mulai Layanan Veyon + + + Stop Veyon Service + Hentikan Layanan Veyon + + + Restart Veyon Service + Restart Layanan Veyon + + + Query status of Veyon Service + + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + Berkas "%1" tidak ada! + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + Ikon baki sistem + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + Kirim pesan teks + + + Use the field below to type your message which will be sent to all selected users. + + + + + TextMessageFeaturePlugin + + Text message + + + + Use this function to send a text message to all users e.g. to assign them new tasks. + + + + Message from teacher + Pesan dari guru + + + Send a message to a user + Kirim pesan pada pengguna + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + + + + Poll full screen (leave this enabled per default) + + + + Low accuracy (turbo mode) + + + + Builtin UltraVNC server configuration + + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + Nama Pengguna + + + Password + Kata sandi + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [GAGAL] + + + Invalid command! + Perintah salah! + + + Available commands: + Perintah yang tersedia: + + + Invalid arguments given + + + + Not enough arguments given - use "%1 help" for more information + + + + Unknown result! + Hasil tidak diketahui + + + Available modules: + Modul yang tersedia: + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + Pengaya tidak berlisensi + + + INFO + + + + ERROR + + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + Layanan Veyon + + + + VncViewWidget + + Establishing connection to %1 ... + + + + + WebApiConfigurationPage + + Web API + + + + General + Umum + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + Umum + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Pengaya menerapkan fungsi abstrak untuk platform Windows + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + + + + Custom x11vnc parameters: + + + + Do not use X Damage extension + + + + diff --git a/translations/veyon_it.ts b/translations/veyon_it.ts new file mode 100644 index 0000000..63b2337 --- /dev/null +++ b/translations/veyon_it.ts @@ -0,0 +1,4069 @@ + + + + + AboutDialog + + About + Informazioni su + + + Translation + Traduzione + + + License + Licenza + + + About Veyon + Informazioni su Veyon + + + Contributors + Contributori + + + Version: + Versione: + + + Website: + Sito Web: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + La lingua corrente e' stata tradotta da Albano Battistella (dall'inglese). + +Se sei interessato alla traduzione di Veyon nella tua lingua locale o in qualche altra lingua o vuoi migliorare una traduzione esistente, contatta uno sviluppatore Veyon! + + + About %1 %2 + Informazioni su %1 %2 + + + Support Veyon project with a donation + Supporta il progetto Veyon con una donazione + + + + AccessControlPage + + Computer access control + Controllo accesso al computer + + + Grant access to every authenticated user (default) + Garantire l'accesso a tutti gli utenti autenticati (predefinito) + + + Test + Prova + + + Process access control rules + Elabora le regole di controllo di accesso + + + User groups authorized for computer access + Gruppi di utenti autorizzati all'accesso al computer + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Aggiungere i gruppi i cui membri dovrebbero essere autorizzati ad accedere ai computer della rete Veyon. + + + Authorized user groups + Gruppi di utenti autorizzati + + + All groups + Tutti i gruppi + + + Access control rules + Regole di controllo d'accesso + + + Add access control rule + Aggiungi regola di controllo d'accesso + + + Remove access control rule + Rimuovi tutte le regole di controllo d'accesso + + + Move selected rule down + Sposta la regola selezionata in basso + + + Move selected rule up + Sposta la regola selezionata in alto + + + Edit selected rule + Modifica la regola selezionata + + + Enter username + Inserire il nome utente + + + Please enter a user login name whose access permissions to test: + Inserire un nome utente di login per provarne i permessi di accesso: + + + Access allowed + Accesso consentito + + + The specified user is allowed to access computers with this configuration. + All'utente specificato è consentito accedere ai computer con questa configurazione. + + + Access denied + Accesso negato + + + The specified user is not allowed to access computers with this configuration. + L'utente indicato non è abilitato ad accedere a computer con questa confiugurazione. + + + Enable usage of domain groups + Abilitare l'utilizzo di gruppi di dominio + + + User groups backend: + Backend del gruppo utenti: + + + Missing user groups backend + Backend dei gruppi utente non presente + + + No default user groups plugin was found. Please check your installation! + Non è stato trovato alcun plug-in predefinito per gruppi di utenti. Per favore controlla la tua installazione! + + + Restrict access to members of specific user groups + Limita l'accesso a specifici membri di gruppi utenti + + + + AccessControlRuleEditDialog + + Edit access control rule + Modifica la regola di controllo d'accesso + + + General + Generale + + + enter a short name for the rule here + inserire qui un nome compatto per la regola + + + Rule name: + Nome regola: + + + enter a description for the rule here + inserire qui una descrizione per la regola + + + Rule description: + Descrizione della regola: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Inverti tutte le condizioni ("è/ha" interpretato come "non è/ha") + + + Conditions + Condizioni + + + is member of group + è membro del gruppo + + + Accessing computer is localhost + Il computer in cui si sta entrando è localhost + + + Accessing user is logged on user + L'utente che sta entrando è registrato sull'utente + + + Accessing user is already connected + L'utente che sta entrando è già connesso + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Se è attivata più di una condizione, ciascuna condizione deve essere soddisfatta affinchè la regola venga applicata (AND logico). Se invece solo una condizione è richiesta (OR logico), allora per favore creare regole multiple di controllo accesso. + + + Action + Azione + + + Allow access + Permette l'accesso + + + Deny access + Nega l'accesso + + + Ask logged on user for permission + Chiedi permesso all'utente collegato + + + None (rule disabled) + Nessuna (regola disabilitata) + + + Accessing user + L'utente che sta entrando + + + Accessing computer + Il computer che sta entrando + + + Local (logged on) user + Utente locale (collegato) + + + Local computer + Computer locale + + + Always process rule and ignore conditions + Elabora sempre la regola ed ignora le condizioni + + + No user logged on + Nessun utente connesso + + + Accessing user has one or more groups in common with local (logged on) user + L'utente a cui si sta accedendo ha uno o più gruppi in comune con l'utente locale (l'utente ora loggato). + + + Accessing computer and local computer are at the same location + L'accesso al computer e al computer locale si trovano nella stessa posizione + + + is located at + si trova a + + + + AccessControlRulesTestDialog + + Access control rules test + Verifica regole di controllo d'accesso + + + Accessing user: + Utente che sta entrando: + + + Local computer: + Computer locale: + + + Accessing computer: + Computer che sta entrando: + + + Please enter the following user and computer information in order to test the configured ruleset. + Per favore immettere le seguenti informazioni su utente e computer per procedere al test dell'insieme di regole configurate. + + + Local user: + Utente locale: + + + Connected users: + Utenti connessi: + + + The access in the given scenario is allowed. + L'accesso allo scenario fornito è consentito. + + + The access in the given scenario is denied. + L'accesso allo scenario fornito è negato. + + + The access in the given scenario needs permission of the logged on user. + L'accesso allo scenario fornito richiede l'autorizzazione dell'utente connesso. + + + ERROR: Unknown action + ERRORE: azione sconosciuta + + + Test result + Risultati del test + + + + AuthKeysConfigurationPage + + Authentication keys + Chiavi di autenticazione + + + Introduction + Introduzione + + + Key file directories + Cartelle per i file delle chiavi + + + Public key file base directory + Cartella chiave Pubblica + + + Private key file base directory + Cartella chiave Privata + + + Available authentication keys + Chiavi di autenticazione disponibili + + + Create key pair + Crea una coppia di chiavi + + + Delete key + Cancella la chiave + + + Import key + Importa la chiave + + + Export key + Esporta la chiave + + + Set access group + Imposta il gruppo di accesso + + + Key files (*.pem) + File di chiave (*.pem) + + + Authentication key name + Nome della chiave di autenticazione + + + Please enter the name of the user group or role for which to create an authentication key pair: + Inserire il nome del gruppo utenti o del ruolo per cui si vuole creare una coppia di chiavi di autenticazione: + + + Do you really want to delete authentication key "%1/%2"? + Vuoi davvero eliminare la chiave di autenticazione %1/%2"? + + + Please select a key to delete! + Seleziona la chiave da eliminare! + + + Please enter the name of the user group or role for which to import the authentication key: + Inserire il nome del gruppo o ruolo dell'utente per il quale importare la chiave di autenticazione: + + + Please select a key to export! + Seleziona la chiave da esportare! + + + Please select a user group which to grant access to key "%1": + Seleziona un gruppo di utenti per concedere l'accesso alla chiave "%1": + + + Please select a key which to set the access group for! + Si prega di selezionare una chiave per impostare il gruppo di accesso! + + + Please perform the following steps to set up key file authentication: + Effettuare le seguenti operazioni per configurare l'autenticazione del file chiave: + + + 1) Create a key pair on the master computer. + 1) Creare una coppia di chiavi sul computer master. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Impostare un gruppo di accesso i cui membri dovrebbero poter accedere ad altri computer. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Esportare la chiave pubblica e importarla su tutti i computer client con lo stesso nome. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Per ulteriori informazioni, consultare il <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Manuale dell'amministratore di Veyon</a>. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Una coppia di chiavi di autenticazione è composta da due chiavi crittografiche accoppiate, una privata e una chiave pubblica. Una chiave privata consente agli utenti del computer master di accedere ai computer client. È importante che solo gli utenti autorizzati abbiano accesso in lettura al file della chiave privata. La chiave pubblica viene utilizzata sui computer client per autenticare la richiesta di connessione in entrata. + + + + AuthKeysManager + + Please check your permissions. + Per favore controlla le tue autorizzazioni. + + + Key name contains invalid characters! + Il nome della chiave contiene caratteri non validi! + + + Invalid key type specified! Please specify "%1" or "%2". + Tipo di chiave non valido specificato! Si prega di specificare "%1" o "%2". + + + Specified key does not exist! Please use the "list" command to list all installed keys. + La chiave specificata non esiste! Si prega di utilizzare il comando "lista" per elencare tutte le chiavi installate. + + + One or more key files already exist! Please delete them using the "delete" command. + Uno o più file chiave esistono già! Per favore cancellali usando il comando "cancella". + + + Creating new key pair for "%1" + Creazione di una nuova coppia di chiavi per "%1" + + + Failed to create public or private key! + Impossibile creare una chiave pubblica o privata! + + + Newly created key pair has been saved to "%1" and "%2". + La coppia di chiavi appena creata è stata salvata in "%1" e "%2". + + + Could not remove key file "%1"! + Impossibile rimuovere il file chiave "%1"! + + + Could not remove key file directory "%1"! + Impossibile rimuovere la directory del file di chiavi "%1"! + + + Failed to create directory for output file. + Impossibile creare la directory per il file di output. + + + File "%1" already exists. + Il file "%1" esiste già. + + + Failed to write output file. + Impossibile scrivere il file di output. + + + Key "%1/%2" has been exported to "%3" successfully. + Il tasto "%1 /%2" è stato esportato correttamente in "%3". + + + Failed read input file. + File di input di lettura non riuscito. + + + File "%1" does not contain a valid private key! + Il file "%1" non contiene una chiave privata valida! + + + File "%1" does not contain a valid public key! + Il file "%1" non contiene una chiave pubblica valida! + + + Failed to create directory for key file. + Impossibile creare la directory per il file chiave. + + + Failed to write key file "%1". + Impossibile scrivere il file chiave "%1". + + + Failed to set permissions for key file "%1"! + Impossibile impostare le autorizzazioni per il file chiave "%1"! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + Il tasto "%1 /%2" è stato importato correttamente. Controlla i permessi dei file di "%3" per impedire accessi non autorizzati. + + + Failed to convert private key to public key + Impossibile convertire la chiave privata in chiave pubblica + + + Failed to create directory for private key file "%1". + Impossibile creare la directory per il file di chiave privata "%1". + + + Failed to save private key in file "%1"! + Impossibile salvare la chiave privata nel file "%1"! + + + Failed to set permissions for private key file "%1"! + Impossibile impostare le autorizzazioni per il file della chiave privata "%1"! + + + Failed to create directory for public key file "%1". + Impossibile creare la directory per il file di chiave pubblica "%1". + + + Failed to save public key in file "%1"! + Impossibile salvare la chiave pubblica nel file "%1"! + + + Failed to set permissions for public key file "%1"! + Impossibile impostare le autorizzazioni per il file di chiave pubblica "%1"! + + + Failed to set owner of key file "%1" to "%2". + Impossibile impostare il proprietario del file chiave "%1" su "%2". + + + Failed to set permissions for key file "%1". + Impossibile impostare le autorizzazioni per il file chiave "%1". + + + Key "%1" is now accessible by user group "%2". + Il tasto "%1" è ora accessibile dal gruppo di utenti "%2". + + + <N/A> + <N/A> + + + Failed to read key file. + Impossibile leggere il file chiave. + + + + AuthKeysPlugin + + Create new authentication key pair + Crea una nuova coppia di chiavi di autenticazione + + + Delete authentication key + Elimina la chiave di autenticazione + + + List authentication keys + Elenca le chiavi di autenticazione + + + Import public or private key + Importa chiave pubblica o privata + + + Export public or private key + Esporta chiave pubblica o privata + + + Extract public key from existing private key + Estrai la chiave pubblica dalla chiave privata esistente + + + Set user group allowed to access a key + Imposta il gruppo utente autorizzato ad accedere a una chiave + + + KEY + CHIAVE + + + ACCESS GROUP + GRUPPO DI ACCESSO + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + Questo comando regola le autorizzazioni di accesso ai file in modo tale che solo il gruppo di utenti abbia accesso in lettura ad esso. + + + NAME + NOME + + + FILE + FILE + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Questo comando esporta la chiave di autenticazione<KEY> su <FILE>. Se <FILE>non viene specificato un nome sarà costruito dal nome e tipo di <KEY>. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Questo comando importa la chiave di autenticazione. Se non viene specificato un nome sarà costruito dal nome e tipo. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + Questo comando elenca tutte le chiavi di autenticazione disponibili nella directory delle chiavi configurate. Se viene specificata l'opzione "%1", verrà visualizzata una tabella con i dettagli chiave. Alcuni dettagli potrebbero mancare se una chiave non è accessibile, ad es. a causa della mancanza di permessi di lettura. + + + Please specify the command to display help for! + Si prega di specificare il comando per visualizzare la guida per! + + + TYPE + TIPO + + + PAIR ID + COPPIA ID + + + Command line support for managing authentication keys + Supporto da riga di comando per la gestione delle chiavi di autenticazione + + + Commands for managing authentication keys + Comandi per la gestione delle chiavi di autenticazione + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + Questo comando crea una nuova coppia di chiavi di autenticazione con nome <NAME> e salva la chiave privata e pubblica per le directory chiave configurate. Il parametro deve essere un nome per la chiave, che può contenere solo lettere. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + Questo comando cancella la chiave di autenticazione <KEY> dalla directory delle chiavi configurata. Si prega di notare che una chiave non può essere recuperata una volta che è stata cancellata. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + Questo comando estrae la parte della chiave pubblica dalla chiave privata <KEY> e lo salva come chiave pubblica corrispondente. Quando si configura un altro computer master, è quindi sufficiente trasferire solo la chiave privata. La chiave pubblica può quindi essere estratta. + + + + AuthKeysTableModel + + Name + Nome + + + Type + Tipo + + + Access group + Gruppo di accesso + + + Pair ID + Coppia ID + + + + BuiltinDirectoryConfigurationPage + + Computers + Computer + + + Name + Nome + + + Host address/IP + Indirizzo host/IP + + + MAC address + indirizzo MAC + + + Add new computer + Aggiungi un nuovo computer + + + Remove selected computer + Rimuovi il computer selezionato + + + New computer + Nuovo computer + + + Builtin directory + Directory incorporata + + + Locations & computers + Posizioni e computers + + + Locations + Posizioni + + + Add new location + Aggiungi nuova posizione + + + Remove selected location + Rimuovi la posizione selezionata + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + L'importazione di file CSV è possibile tramite l'interfaccia della riga di comando. Per ulteriori informazioni, consultare la documentazione online.<a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory"> + + + New location + Nuova posizione + + + + BuiltinDirectoryPlugin + + Show help for specific command + Mostra l'aiuto per un comando specifico + + + Import objects from given file + Importa oggetti dal file specificato + + + Export objects to given file + Esportare oggetti nel file specificato + + + Invalid type specified. Valid values are "%1" or "%2". + Tipo non valido specificato. I valori validi sono "%1" o "%2". + + + Type + Tipo + + + Name + Nome + + + Host address + Indirizzo dell'host + + + MAC address + indirizzo MAC + + + Specified object not found. + Oggetto specificato non trovato. + + + File "%1" does not exist! + Il file "%1" non esiste! + + + Can't open file "%1" for reading! + Impossibile aprire il file "%1" per la lettura! + + + Unknown argument "%1". + Argomento sconosciuto "%1". + + + Computer "%1" (host address: "%2" MAC address: "%3") + Computer "%1" (indirizzo host: "%2" indirizzo MAC: "%3") + + + Unclassified object "%1" with ID "%2" + Oggetto non classificato "%1" con ID "%2" + + + None + Nessuna + + + Computer + Computer + + + Root + Root + + + Invalid + Non valido + + + Error while parsing line %1. + Errore durante l'analisi della riga%1. + + + Network object directory which stores objects in local configuration + Directory dell'oggetto di rete che memorizza gli oggetti nella configurazione locale + + + Commands for managing the builtin network object directory + Comandi per la gestione della directory dell'oggetto di rete incorporato + + + No format string or regular expression specified! + Nessuna stringa di formato o espressione regolare specificata! + + + Can't open file "%1" for writing! + Impossibile aprire il file "%1" per la scrittura! + + + No format string specified! + Nessuna stringa di formato specificata! + + + Object UUID + Oggetto UUID + + + Parent UUID + Genitore UUID + + + Add a location or computer + Aggiungi una posizione o un computer + + + Clear all locations and computers + Cancella tutte le posizioni e i computer + + + Dump all or individual locations and computers + Scarica tutte le posizioni o i singoli computers + + + List all locations and computers + Elenca tutte le posizioni e i computer + + + Remove a location or computer + Rimuovi una posizione o un computer + + + Location "%1" + Posizione "%1" + + + Builtin (computers and locations in local configuration) + BuiltIn (computer e posizioni nella configurazione locale) + + + Location + Posizione + + + FILE + FILE + + + LOCATION + LUOGO + + + FORMAT-STRING-WITH-PLACEHOLDERS + FORMATO-STRINGA-CON-SEGNAPOSTO + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + ESPRESSIONE-REGOLARE-CON-SEGNAPOSTO + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Importa oggetti da un determinato file di testo utilizzando una stringa di formattazione o un'espressione regolare contenente una o più segnaposto. Segnaposto validi sono: %1 + + + Import simple CSV file to a single room + Importa un file CSV in una singola aula + + + Import CSV file with location name in first column + Importa un file CSV con una destinazione nella prima colonna + + + Import text file with with key/value pairs using regular expressions + Importa un file di testo con coppie di chiave/valore utilizzando espressioni regolari + + + Import arbitrarily formatted data + Importa dati formattati arbitrariamente + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Esporta oggetti nel file specificato utilizzando un formato stringa contenente uno o più segnaposto. Segnaposto validi sono: %1 + + + Export all objects to a CSV file + Esporta tutti gli oggetti in un file CSV + + + Export all computers in a specific location to a CSV file + Esporta tutti i computer di un specifico luogo in un file CSV + + + TYPE + TIPO + + + NAME + NOME + + + PARENT + GENITORE + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + Aggiunge un oggetto dove %1 può essere uno dei seguenti "%2" o "%3". %4 può essere individuato dal nome o dal codice UUID. + + + Add a room + Aggiunge un'aula + + + Add a computer to room %1 + Aggiunge un computer all'aula %1 + + + OBJECT + OGGETTO + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Cancella l'oggetto specificato dalla cartella %1 può essere specificato il nome o il codice UUID. Cancellando un ambiente saranno cancellati anche i relativi computer + + + Remove a computer by name + Cancella un computer identificato dal nome + + + Remove an object by UUID + Cancella un oggetto identificato dal codice UUID + + + "Room 01" + "Aula 01" + + + "Computer 01" + "Computer 01" + + + HOST ADDRESS + INDIRIZZO COMPUTER + + + MAC ADDRESS + MAC ADDRESS + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Server VNC incorporato (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Server VNC incorporato (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Host/indirizzo IP: %1 + + + Active features: %1 + Funzioni attive: %1 + + + Online and connected + Connessi e online + + + Establishing connection + Connessione in corso + + + Computer offline or switched off + Computer disconnesso o spento + + + Authentication failed or access denied + Autenticazione fallita o accesso negato + + + Disconnected + Disconnesso + + + No user logged on + Nessun utente connesso + + + Logged on user: %1 + Utente connesso: %1 + + + Location: %1 + Posizione: %1 + + + Veyon Server unreachable or not running + Veyon Server irraggiungibile o non in esecuzione + + + [no user] + [nessun utente] + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 Servizio %2 alle %3:%4 + + + Authentication error + Errore di autenticazione + + + Remote access + Accesso remoto + + + User "%1" at host "%2" is now accessing this computer. + L'utente "%1" sull'host "%2" sta ora accedendo a questo computer. + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + L'utente "%1" presso l' host "%2" ha tentato di accedere a questo computer ma non è stato possibile eseguire l'autenticazione. + + + Access control error + Errore di controllo dell'accesso + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + L'utente "%1" presso l'host "%2" ha tentato di accedere a questo computer ma è stato bloccato a causa delle impostazioni di controllo dell'accesso. + + + Active connections: + Connessioni attive: + + + + ComputerManager + + User + Utente + + + Missing network object directory plugin + Manca il plugin per l'elencazione degli oggetti di rete + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Non è stato trovato alcun plugin predefinito per l'elencazione degli oggetti di rete. Per favore controllare l'installazione o configurare un diverso backend per l'elenco degli oggetti di rete attraverso %1 Configurator. + + + Location detection failed + Rilevazione della posizione fallita + + + Computer name;Hostname;User + Nome del computer; Nome host; Utente + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + Impossibile determinare la posizione di questo computer. Questo indica un problema con la configurazione del sistema. Tutte le posizioni verranno visualizzate nel pannello di selezione del computer. + + + + ComputerSelectPanel + + Computer search + Ricerca computer + + + Add location + Aggiungi posizione + + + Save computer/user list + Salva la lista di computer/utenti + + + Select output filename + Seleziona il nome del file d'uscita + + + CSV files (*.csv) + File CSV (*.csv) + + + File error + Errore nel file + + + Could not write the computer and users list to %1! Please check the file access permissions. + Impossibile scrivere l'elenco computer e utenti su %1! Controllare i permessi di accesso al file. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Per favore indicare un file di configurazione esistente da importare. + + + Please specify a valid filename for the configuration export. + Per favore indicare un nome di file valido per esportare la configurazione. + + + Please specify a valid key. + Per favore indicare una chiave valida. + + + Specified key does not exist in current configuration! + La chiave indicata non esiste nella configurazione corrente! + + + Please specify a valid value. + Per favore indicare un valore valido. + + + Configure Veyon at command line + Non è possibile modificare gli argomenti passati a %1 + + + Output file is not writable! + Non si dispone dei permessi di scrittura per il file di destinazione. + + + Output directory is not writable! + Non si dispone dei permessi di scrittura nella cartella di destinazione + + + Configuration file is not readable! + Non è possibile leggere il file di configurazione! + + + Clear system-wide Veyon configuration + Eliminazione della configurazione di Veyon su tutto il sistema. + + + List all configuration keys and values + Lista di tutte le chiavi di configurazione e dei loro valori. + + + Import configuration from given file + Importa una configurazione da file. + + + Export configuration to given file + Esporta una configurazione su file. + + + Read and output configuration value for given key + Leggi e stampa i valori di configurazione delle chiavi date + + + Write given value to given configuration key + Imposta il valore della chiave di configurazione + + + Unset (remove) given configuration key + Dimentica (rimuovi) la chiave di configurazione data + + + Commands for managing the configuration of Veyon + Comandi di configurazione + + + Upgrade and save configuration of program and plugins + Aggiorna e salva la configurazione del programma e dei plugin + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + Impossibile modificare la proprietà autostart per il %1 Service. + + + Could not configure the firewall configuration for the %1 Server. + Impossibile configurare la configurazione del firewall per%1 Server. + + + Could not configure the firewall configuration for the %1 Worker. + Impossibile configurare la configurazione del firewall per%1 operatore. + + + Configuration is not writable. Please check your permissions! + La configurazione non è scrivibile. Per favore controlla le tue autorizzazioni! + + + Could not apply platform-specific configuration settings. + Impossibile applicare le impostazioni di configurazione specifiche della piattaforma. + + + + DemoClient + + %1 Demo + %1 Demo + + + + DemoConfigurationPage + + Demo server + Server per la modalità Presentazione + + + Tunables + Modificabili + + + ms + ms + + + Key frame interval + Intervallo del Key frame + + + Memory limit + Limite memoria + + + MB + MB + + + Update interval + Intervallo di aggiornamento + + + s + s + + + Slow down thumbnail updates while demo is running + Rallenta gli aggiornamenti delle miniature mentre la demo è in esecuzione + + + + DemoFeaturePlugin + + Stop demo + Ferma Presentazione + + + Window demo + Presenta in finestra + + + Give a demonstration by screen broadcasting + Esemplificare trasmettendo il proprio schermo + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + In questa modalità il tuo schermo sarà visualizzato in una finestra su tutti i computer client. Gli utenti, pertanto, potranno cambiare finestra e continuare il loro lavoro. + + + Demo + Demo + + + Share your screen or allow a user to share his screen with other users. + Condividi il tuo schermo o consenti a un utente di condividere il suo schermo con altri utenti. + + + Full screen demo + Demo a schermo intero + + + Share your own screen in fullscreen mode + Condividi il tuo schermo in modalità a schermo intero + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + In questa modalità lo schermo viene visualizzato in modalità a schermo intero su tutti i computer mentre i dispositivi di input degli utenti sono bloccati. + + + Share your own screen in a window + Condividi il tuo schermo in una finestra + + + Share selected user's screen in fullscreen mode + Condividi lo schermo dell'utente selezionato in modalità a schermo intero + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + In questa modalità lo schermo dell'utente selezionato viene visualizzato in modalità a schermo intero su tutti i computer mentre i dispositivi di input degli utenti sono bloccati. + + + Share selected user's screen in a window + Condividi lo schermo dell'utente selezionato in una finestra + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + In questa modalità lo schermo dell'utente selezionato viene visualizzato in una finestra su tutti i computer. Gli utenti possono passare ad altre finestre secondo necessità. + + + Please select a user screen to share. + Seleziona una schermata utente da condividere. + + + Please select only one user screen to share. + Seleziona solo uno schermo utente da condividere. + + + All screens + Tutti gli schermi + + + Screen %1 [%2] + Schermo %1 [%2] + + + + DesktopAccessDialog + + Desktop access dialog + Finestra di accesso al Desktop + + + Confirm desktop access + Conferma l'accesso + + + Never for this session + Mai per questa sessione + + + Always for this session + Sempre per questa sessione + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + L' utente %1 desidera collegarsi al tuo PC dal PC %2. Desideri concedergli l'accesso? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programmi e siti Web + + + Predefined programs + Programmi predefiniti + + + Name + Nome + + + Path + Percorso + + + Add new program + Aggiungi un nuovo programma + + + Remove selected program + Rimuovi il programma selezionato + + + Predefined websites + Siti Web predefiniti + + + Remove selected website + Rimuovi il sito Web selezionato + + + URL + URL + + + New program + Nuovo programma + + + New website + Nuovo sito web + + + + DesktopServicesFeaturePlugin + + Run program + Apri un programma + + + Open website + Apri sito web + + + Click this button to open a website on all computers. + Clicca su questo pulsante per aprire un sito internet su tutti i PC. + + + Start programs and services in user desktop + Attiva il programma sul PC remoto + + + Click this button to run a program on all computers. + Clicca su questo pulsante per lanciare un programma su tutti i PC + + + Run program "%1" + Esegui il programma "%1" + + + Custom program + Programma personalizzato + + + Open website "%1" + Apri il sito web "%1" + + + Custom website + Sito web personalizzato + + + + DocumentationFigureCreator + + Teacher + Insegnante + + + Room %1 + Stanza %1 + + + Please complete all tasks within the next 5 minutes. + Si prega di completare tutte le attività entro i prossimi 5 minuti. + + + Custom website + Sito web personalizzato + + + Open file manager + Apri il file manager + + + Start learning tool + Avvia strumento di apprendimento + + + Play tutorial video + Riproduci video tutorial + + + Custom program + Programma personalizzato + + + Handout + Volantino + + + Texts to read + Testi da leggere + + + generic-student-user + utente-generico-studente + + + + ExternalVncServer + + External VNC server + Server VNC esterno + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Parametri del server VNC esterno + + + Port: + Porta: + + + Password: + Password: + + + + FeatureControl + + Feature control + Controllo delle opzioni + + + + FileTransferConfigurationPage + + File transfer + Trasferimento di file + + + Directories + Cartelle + + + Destination directory + Directory di destinazione + + + Default source directory + Directory di origine predefinita + + + Options + Opzioni + + + Remember last source directory + Ricorda l'ultima directory di origine + + + Create destination directory if it does not exist + Crea la directory di destinazione se non esiste + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + Impossibile aprire il file "%1" per la lettura! Per favore controlla le tue autorizzazioni! + + + + FileTransferDialog + + File transfer + Trasferimento di file + + + Options + Opzioni + + + Transfer only + Solo trasferimento + + + Transfer and open file(s) with associated program + Trasferisci e apri i file(s) con il programma associato + + + Transfer and open destination folder + Trasferisci e apri la cartella di destinazione + + + Files + Files + + + Start + Inizio + + + Overwrite existing files + Sovrascrivi i file esistenti + + + + FileTransferPlugin + + File transfer + Trasferimento di file + + + Click this button to transfer files from your computer to all computers. + Fare clic su questo pulsante per trasferire i file dal computer a tutti i computer. + + + Select one or more files to transfer + Seleziona uno o più file da trasferire + + + Transfer files to remote computer + Trasferisci i file sul computer remoto + + + Received file "%1". + File ricevuto "%1". + + + Could not receive file "%1" as it already exists. + Impossibile ricevere il file "%1" già esiste. + + + Could not receive file "%1" as it could not be opened for writing! + Impossibile ricevere il file "%1" in quanto non può essere aperto per la scrittura! + + + + GeneralConfigurationPage + + User interface + Interfaccia Utente + + + Language: + Linguaggio: + + + Use system language setting + Utilizza la lingua predefinita + + + Veyon + Veyon + + + Logging + Fase di autenticazione + + + Log file directory + Cartella per il Log file + + + Log level + Livello di Log + + + Nothing + Nessun log + + + Only critical messages + Solo i messaggi critici + + + Errors and critical messages + Errori e messaggi critici + + + Warnings and errors + Avvisi (warnings) ed errori + + + Information, warnings and errors + Informazioni, avvisi e errori + + + Debug messages and everything else + Messaggi di debug e quant'altro + + + Limit log file size + Limite per il file di log + + + Clear all log files + Cancella tutti i file di log + + + Log to standard error output + Log sullo standard error output + + + Network object directory + Directory dell'oggetto di rete + + + Backend: + Backend: + + + Update interval: + Intervallo di aggiornamento: + + + %1 service + Servizio %1 + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + Il servizio %1 deve essere riavviato per consentire la rimozione dei file di log. Continuare? + + + Log files cleared + File di Log cancellati + + + All log files were cleared successfully. + Tutti i file di Log sono stati cancellati. + + + Error + Errore + + + Could not remove all log files. + Impossibile cancellare tutti i file di Log. + + + MB + MB + + + Rotate log files + Ruotare i file di registro + + + x + x + + + seconds + secondi + + + Write to logging system of operating system + Scrivi nel sistema di log del sistema operativo + + + Authentication + Autenticazione + + + Method: + Metodo: + + + Logon authentication + Autenticazione di accesso (Logon) + + + Key file authentication + Autenticazione con File chiave + + + Test + Prova + + + Authentication is set up properly on this computer. + L'autenticazione è configurata correttamente su questo computer. + + + Authentication keys are not set up properly on this computer. + Le chiavi di autenticazione non sono configurate correttamente su questo computer. + + + Authentication test + Test di autenticazione + + + + HeadlessVncServer + + Headless VNC server + Server VNC headless + + + + LdapBrowseDialog + + Browse LDAP + Sfoglia LDAP + + + + LdapClient + + LDAP error description: %1 + Descrizione errore LDAP: %1 + + + + LdapConfigurationPage + + Basic settings + Impostazioni di base + + + General + Generale + + + LDAP server and port + Indirizzo e porta del server LDAP + + + Bind DN + Bind DN + + + Bind password + Bind password + + + Anonymous bind + Anonymous bind + + + Use bind credentials + Use bind credentials + + + Base DN + Base DN + + + Fixed base DN + Fixed base DN + + + e.g. dc=example,dc=org + e.g. dc=example,dc=org + + + Discover base DN by naming context + Discover base DN by naming context + + + e.g. namingContexts or defaultNamingContext + e.g. namingContexts or defaultNamingContext + + + Environment settings + Impostazioni ambiente + + + Object trees + Alberatura oggetti + + + Computer tree + Alberatura Pc + + + e.g. OU=Groups + e.g. OU=Groups + + + User tree + Alberatura utenti + + + e.g. OU=Users + e.g. OU=Users + + + e.g. OU=Computers + e.g. OU=Computers + + + Group tree + Alberatura gruppi + + + Perform recursive search operations in object trees + Ricerca anche nelle sottocartelle + + + Object attributes + Attributi degli oggetti + + + e.g. hwAddress + e.g. hwAddress + + + e.g. member or memberUid + Per esempio membro o membroUid + + + e.g. dNSHostName + Per esempio dNSHostName + + + Computer MAC address attribute + Attributo dell'indirizzo MAC del computer + + + Group member attribute + Attributo del gruppo del membro + + + e.g. uid or sAMAccountName + Per esempio uid o sAMAccountName + + + Advanced settings + Impostazioni avanzate + + + Optional object filters + Filtro per oggetti opzionale + + + Filter for user groups + Filtro per gruppo utenti + + + Filter for users + Filtro Users + + + Filter for computer groups + Filtro Gruppo computer + + + Group member identification + Identificazione membro del gruppo + + + Distinguished name (Samba/AD) + Nome distinto (Samba/AD) + + + List all groups of a user + Visualizza i gruppi a cui appartiene un User + + + List all groups of a computer + Visualizza i gruppi a cui appartiene un computer + + + Get computer object by IP address + Identifica il computer mediante indirizzo IP + + + LDAP connection failed + Connessione al server LDAP fallita + + + LDAP bind failed + Il bind con il server LDAP è fallito. Verificare la sintassi delle credenziali + + + LDAP bind successful + Il bind con il server LDAP è riuscito. + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Collegato correttamente al server LDAP ed eseguito un binding LDAP. Le impostazioni LDAP di base sono configurate correttamente. + + + LDAP base DN test failed + Test LDAP base DN fallito + + + LDAP base DN test successful + Test LDAP base DN riuscito + + + LDAP naming context test failed + Test per contesto nominazione LDAP fallito + + + LDAP naming context test successful + Test di contesto di denominazione LDAP riuscito + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + LDAP interrogato con successo. Il seguente base DN è stato trovato: +%1 + + + user tree + albero utenti + + + group tree + albero gruppi + + + computer tree + albero computer + + + Enter username + Inserire il nome utente + + + Please enter a user login name (wildcards allowed) which to query: + Inserire un nome utente di login (caratteri speciali ammessi) per il quale effettuare la query + + + user objects + user objects + + + Enter group name + Inserire il nome del gruppo + + + Please enter a group name whose members to query: + Inserire il nome di un gruppo in cui ricercare i membri + + + group members + Membri del gruppo + + + Group not found + Gruppo non trovato + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + Non è possibile trovare il gruppo %1. Verificare il nome del gruppo inserito e i parametri LDAP dei gruppi. + + + Enter computer name + Inserire il nome del computer + + + computer objects + oggetti computer + + + Enter computer DN + Inserisci il DN del computer + + + Please enter the DN of a computer whose MAC address to query: + Inserisci il DN di un computer il cui indirizzo MAC e' da ricercare + + + computer MAC addresses + indirizzi MAC computer + + + users + utenti + + + user groups + gruppi di utenti + + + computer groups + gruppi computer + + + Please enter a user login name whose group memberships to query: + Si prega di inserire un nome utente di accesso di appartenenza al gruppo da interrogare: + + + groups of user + gruppi di utenti + + + User not found + Utente non trovato + + + groups of computer + gruppi di computer + + + Computer not found + Computer non trovato + + + Enter computer IP address + Inserisci indirizzo IP del computer + + + Please enter a computer IP address which to resolve to an computer object: + Si prega di inserire un indirizzo IP del computer da risolvere a un oggetto computer: + + + computers + computer + + + LDAP %1 test failed + Test %1 LDAP fallito + + + LDAP %1 test successful + Test %1 LDAP completato con successo + + + The %1 has been queried successfully and %2 entries were found. + Il %1 e' stato interrogato con successo e sono state trovate %2 voci. + + + %1 %2 have been queried successfully: + +%3 + %1 %2 sono stati interrogati con successo: + +%3 + + + LDAP filter test failed + Test filtro LDAP falllito + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + Impossibile interrogare qualsiasi %1 usando il filtro configurato. Si prega di verificare il filtro LPAP per %1. + +%2 + + + LDAP filter test successful + Test filtro LDAP completato con successo + + + %1 %2 have been queried successfully using the configured filter. + %1 %2 sono stati interrogati con successo usando il filtro configurato. + + + (only if different from group tree) + (solo se diverso dall'albero gruppo) + + + Computer group tree + Albero gruppo computer + + + computer group tree + albero gruppo computer + + + Filter for computers + Filtra per computer + + + e.g. room or computerLab + es. aula o computerLab + + + Integration tests + Test integrazione + + + Computer groups + Gruppi computer + + + e.g. name or description + es. nome o descrizione + + + Filter for computer containers + Filtra per contenitori computer + + + Computer containers or OUs + Contenitoru computer o OUs + + + Connection security + Sicurezza della connessione + + + TLS certificate verification + Verifica del certificato TLS + + + System defaults + Impostazioni di sistema + + + Never (insecure!) + Mai (insicuro!) + + + Custom CA certificate file + File certificato CA personalizzato + + + None + Nessuna + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + per esempio. (OgettoClasse = computer) + + + e.g. (objectClass=group) + per esempio:(Oggetto Classe = gruppo) + + + e.g. (objectClass=person) + per esempio: (oggetto Classe=persona) + + + e.g. (objectClass=room) or (objectClass=computerLab) + per esempio: (oggetto Classe=aula) or (oggetto Classe=Laboratorio computer) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + per esempio (oggetto Classe=container) o (oggetto Classe=Unità Organizzativa) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + Impossibile interrogare il DN base configurato. Controlla il parametro DN di base.%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + Il DN base LDAP è stato interrogato correttamente. Sono state trovate le seguenti voci: %1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + Impossibile interrogare il DN di base tramite i contesti di denominazione. Controlla il parametro dell'attributo del contesto di denominazione.%1 + + + Certificate files (*.pem) + File di certificato (* .pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + Impossibile connettersi al server LDAP. Controlla i parametri del server.%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + Impossibile collegarsi al server LDAP. Controlla i parametri del server e associa le credenziali.%1 + + + Encryption protocol + Protocollo di crittografia + + + Computer location attribute + Attributo di posizione del computer + + + Computer display name attribute + Attributo del nome di visualizzazione del computer + + + Location name attribute + Attributo del nome della posizione + + + e.g. cn or displayName + per esempio. cn o visualizza Nome + + + Computer locations identification + Identificazione delle posizioni dei computers + + + Identify computer locations (e.g. rooms) via: + Identifica le posizioni dei computers (ad es. aule) tramite: + + + Location attribute in computer objects + Attributo di posizione negli oggetti del computer + + + List all entries of a location + Elenca tutte le voci di una posizione + + + List all locations + Elenca tutte le posizioni + + + Enter computer display name + Inserisci il nome visualizzato sul computer + + + Please enter a computer display name to query: + Inserisci un nome visualizzato sul computer per eseguire una query: + + + Enter computer location name + Inserisci il nome della posizione del computer + + + Please enter the name of a computer location (wildcards allowed): + Si prega di inserire il nome di una posizione del computer (caratteri jolly consentiti): + + + computer locations + posizioni del computer + + + Enter location name + Inserisci il nome della posizione + + + Please enter the name of a location whose entries to query: + Si prega di inserire il nome di una posizione di cui voci per interrogare: + + + location entries + voci di posizione + + + LDAP test failed + Test LDAP fallito + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + Impossibile interrogare alcun%1. Controlla i parametro(i)%2 e inserisci il nome di un oggetto esistente. + +3% + + + and + e + + + LDAP test successful + Test LDAP riuscito + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + Impossibile interrogare le voci configurate %1. Si prega di controllare il parametro "%2". + +3% + + + Browse + Naviga + + + Test + Prova + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + Nomi host memorizzati come nomi di dominio completi (FQDN, ad es. Myhost.example.org) + + + Computer hostname attribute + Attributo hostname del computer + + + Please enter a computer hostname to query: + Inserire un nome host del computer da interrogare: + + + Invalid hostname + Hostname non valido + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + Hai configurato i nomi host dei computer da memorizzare come nomi di dominio completi (FQDN), ma hai inserito un nome host senza dominio. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + Hai configurato i nomi host dei computer da memorizzare come semplici nomi host senza un nome di dominio, ma hai inserito un nome host con una parte del nome di dominio. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + Impossibile trovare un utente con il nome "%1". Si prega di controllare il nome utente o il parametro dell'albero utente. + + + Enter hostname + Inserisci il nome host + + + Please enter a computer hostname whose group memberships to query: + Immettere un nome host del computer di cui si desidera eseguire la ricerca per appartenenza al gruppo: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + Impossibile trovare un computer con il nome host "%1". Si prega di controllare il nome host o il parametro dell'albero del computer. + + + Hostname lookup failed + Ricerca del nome host fallita + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + Impossibile cercare il nome host per l'indirizzo IP%1. Si prega di verificare le impostazioni del server DNS. + + + User login name attribute + Attributo del nome di accesso dell'utente + + + Configured attribute for user login name or computer hostname (OpenLDAP) + Attributo configurato per il nome di accesso dell'utente o il nome host del computer (OpenLDAP) + + + computer containers + computer containers + + + + LdapPlugin + + Auto-configure the base DN via naming context + Configura automaticamente la base DN tramite contesto di denominazione + + + Query objects from LDAP directory + Interrogare oggetti dalla directory LDAP + + + Show help about command + Mostra aiuto per i comandi + + + Commands for configuring and testing LDAP/AD integration + Comandi per la configurazione ed il testing dell'integrazione LDAP/AD + + + Basic LDAP/AD support for Veyon + Supporto LDAP / AD di base per Veyon + + + %1 (load computers and locations from LDAP/AD) + %1 (carica computer e posizioni da LDAP / AD) + + + %1 (load users and groups from LDAP/AD) + %1 (carica utenti e gruppi da LDAP / AD) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + Specifica un URL LDAP valido seguendo lo schema "ldap [s]: ://[user[:password]@]hostname[:port]" + + + No naming context attribute name given - falling back to configured value. + Nessun nome attributo al contesto di denominazione assegnato, che ricade sul valore configurato. + + + Could not query base DN. Please check your LDAP configuration. + Impossibile interrogare il DN di base. Si prega di verificare la configurazione LDAP. + + + Configuring %1 as base DN and disabling naming context queries. + Configurazione di%1 come DN di base e disabilitazione delle query di contesto dei nomi. + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + Servizio PAM personalizzato per l'autenticazione dell'utente + + + User authentication + Autenticazione utente + + + Session management + Gestione della sessione + + + Display manager users + Display gestione utenti + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Plugin che implementa funzioni astratte per la piattaforma Linux. + + + + LocationDialog + + Select location + Seleziona la posizione + + + enter search filter... + inserisci filtro di ricerca... + + + + MainToolBar + + Configuration + Configurazione + + + Disable balloon tooltips + Disabilita tooltip dei palloncini + + + Show icons only + Mostra solamente le icone + + + + MainWindow + + MainWindow + FinestraPrincipale + + + toolBar + BarraStrumenti + + + General + Generale + + + &File + &File + + + &Help + &Aiuto + + + &Quit + &Esci + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + C&arica impostazioni dal file + + + Ctrl+O + Ctrl+O + + + About Qt + Informazioni su Qt + + + Authentication impossible + Autenticazione impossibile + + + Configuration not writable + Il file di configurazione non è scrivibile + + + Load settings from file + Carica impostazioni da file + + + Save settings to file + Salva impostazioni su file + + + Unsaved settings + Impostazioni non salvate + + + There are unsaved settings. Quit anyway? + Alcune impostazioni non sono state salvate. Esco comunque? + + + Veyon Configurator + Configurazione Veyon + + + Service + Servizio + + + Master + Principale + + + Access control + Controllo accessi + + + About Veyon + Informazioni su Veyon + + + Auto + Automatico + + + About + Informazioni su + + + %1 Configurator %2 + %1 Configuratore %2 + + + JSON files (*.json) + File JSON (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + La configurazione locale backend ha riportato che la configurazione non e' salvabile! Si prega di eseguire il configuratore %1 con privilegi maggiori. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + Nessun file di chiave di autenticazione e' stato trovato o i correnti sono antiquati. Si prega di creare un nuovi file di chiave usandi il Configuratore %1. In alternativa, imposta l'autenticazione di accesso usando il Configuratore %1. Altrimenti non sarai in grado ad accedere ai computer usando %1. + + + Access denied + Accesso negato + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + Secondo la configurazione locale non ti e' permesso accedere ai computer nella rete. Si prega di effettuare il login con un account diverso o lasciar controllare al tuo amministatore di sistema la configurazione locale. + + + Screenshots + Catture dello schermo + + + Feature active + Funzione attiva + + + The feature "%1" is still active. Please stop it before closing %2. + La funzionalità "%1" e' ancora attiva. Arrestala prima di chiudere %2. + + + Reset configuration + Reimposta configurazione + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Vuoi veramente reimpostare la configurazione locale e riportare tutte le impostazioni valori originali? + + + Search users and computers + Cerca utenti e computer + + + Align computers to grid + Allinea i computer alla griglia + + + %1 Configurator + Opzioni di %1 + + + Insufficient privileges + Privilegi insufficienti + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + Impossibile iniziare con i privilegi di amministratore. Assicurati che sia installato un programma simile a sudo per il tuo ambiente desktop! Il programma verrà eseguito con i normali privilegi dell'utente + + + Only show powered on computers + Mostra solo i computer alimentati + + + &Save settings to file + & Salva le impostazioni nel file + + + &View + &Vista + + + &Standard + &Standard + + + &Advanced + &Avanzate + + + Use custom computer arrangement + Usa la disposizione personalizzata del computer + + + Locations && computers + Posizioni && computers + + + Slideshow + Presentazione + + + Spotlight + Spotlight + + + Adjust size of computer icons automatically + Regola automaticamente la dimensione delle icone del computer + + + + MasterConfigurationPage + + Directories + Cartelle + + + User configuration + Configurazione utente + + + Feature on computer double click: + Funzionalita' sul doppio clic del computer: + + + Features + Funzionalita' + + + All features + Tutte le funzionalita' + + + Disabled features + Funzionalita' disabilitate + + + Screenshots + Catture dello schermo + + + <no feature> + <nessuna funzionalita'> + + + Basic settings + Impostazioni di base + + + Behaviour + Comportamento + + + Enforce selected mode for client computers + Applicare la modalita' selezionata per i client computer + + + Hide local computer + Nascondi il computer locale + + + Hide computer filter field + Nascondi il campo filtri computer + + + Actions such as rebooting or powering down computers + Azioni come il riavvio o lo spegnimetno dei computer + + + User interface + Interfaccia Utente + + + Background color + Colore di sfondo + + + Thumbnail update interval + Intervallo di aggiornamento delle miniature + + + ms + ms + + + Program start + Avvio del programma + + + Modes and features + Modalità e caratteristiche + + + User and computer name + Nome dell'utente e del computer + + + Only user name + Solo il nome utente + + + Only computer name + Solo il nome del computer + + + Computer thumbnail caption + Miniatura anteprima computer + + + Text color + Colore del testo + + + Sort order + Ordinamento + + + Computer and user name + Computer e nome utente + + + Computer locations + Ubicazioni del computer + + + Show current location only + Mostra solo la posizione corrente + + + Allow adding hidden locations manually + Consenti l'aggiunta di posizioni nascoste manualmente + + + Hide empty locations + Nascondi posizioni vuote + + + Show confirmation dialog for potentially unsafe actions + Mostra la finestra di dialogo di conferma per le azioni potenzialmente pericolose + + + Perform access control + Eseguire il controllo degli accessi + + + Automatically select current location + Seleziona automaticamente la posizione corrente + + + Automatically open computer select panel + Apri automaticamente il pannello di selezione del computer + + + Hide local session + Nascondi sessione locale + + + px + px + + + Thumbnail spacing + Spaziatura delle miniature + + + Auto + Automatico + + + Thumbnail aspect ratio + Proporzioni delle miniature + + + Automatically adjust computer icon size + Regola automaticamente la dimensione dell'icona del computer + + + Open feature windows on the same screen as the main window + Apri le finestre delle funzioni sulla stessa schermata della finestra principale + + + + MonitoringMode + + Monitoring + Monitoraggio + + + Builtin monitoring mode + Modalita' di monitoraggio integrata + + + This mode allows you to monitor all computers at one or more locations. + Questa modalità consente di monitorare tutti i computer in una o più posizioni. + + + + NetworkObjectTreeModel + + Locations/Computers + Posizioni / Computers + + + + OpenWebsiteDialog + + Open website + Apri sito web + + + e.g. Veyon + per esempio, Veyon + + + Remember and add to website menu + Ricorda e aggiungi al menu del sito web + + + e.g. www.veyon.io + per esempio, www.veyon.io + + + Please enter the URL of the website to open: + Inserisci l'indirizzo del sito da aprire: + + + Name: + Nome + + + + PasswordDialog + + Username + Nome Utente + + + Password + Password + + + Veyon Logon + Logon Veyon + + + Authentication error + Errore di autenticazione + + + Logon failed with given username and password. Please try again! + Logon fallito con le credenziali fornite. Riprova! + + + Please enter your username and password in order to access computers. + Inserisci il tuo username e password per accedere ai computer + + + + PowerControlFeaturePlugin + + Power on + Accendi + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Premi questo bottone per accendere tutti i computer. In questo modo non devi accendere ogni computer a mano. + + + Reboot + Riavvia il PC + + + Click this button to reboot all computers. + Clicca questo bottone per riavviare tutti i computer. + + + Power down + Spegni + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Clicca questo bottone per spegnere tutti i computer. In questo modo non devi spegnere ogni computer a mano. + + + Power on/down or reboot a computer + Accendi/spegni o riavvia un computer + + + Confirm reboot + Conferma riavvio + + + Confirm power down + Conferma spegnimento + + + Do you really want to reboot the selected computers? + Vuoi veramente riavviare i computer selezionati? + + + Do you really want to power down the selected computer? + Vuoi veramente spegnere il computer selezionato? + + + Power on a computer via Wake-on-LAN (WOL) + Accendi un computer tramite Wake-on-LAN (WOL) + + + MAC ADDRESS + MAC ADDRESS + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + Questo comando trasmette un pacchetto Wake-on-LAN (WOL) alla rete per accendere il computer con l'indirizzo MAC specificato. + + + Please specify the command to display help for! + Si prega di specificare il comando per visualizzare la guida per! + + + Invalid MAC address specified! + Indirizzo MAC specificato non valido! + + + Commands for controlling power status of computers + Comandi per il controllo dello stato di alimentazione dei computers + + + Power down now + Spegni ora + + + Install updates and power down + Installa gli aggiornamenti e spegni + + + Power down after user confirmation + Spegni dopo la conferma dell'utente + + + Power down after timeout + Spegni dopo il timeout + + + The computer was remotely requested to power down. Do you want to power down the computer now? + Al computer è stato richiesto a distanza di spegnersi. Vuoi spegnere il computer adesso? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + Il computer verrà spento in %1 minuti, %2 secondi. + +Si prega di salvare il lavoro e chiudere tutti i programmi. + + + + PowerDownTimeInputDialog + + Power down + Spegni + + + Please specify a timeout for powering down the selected computers: + Si prega di specificare un timeout per spegnere i computer selezionati: + + + minutes + minuti + + + seconds + secondi + + + + RemoteAccessFeaturePlugin + + Remote view + Vista remota + + + Open a remote view for a computer without interaction. + Apri una vista remota per un computer senza interazione + + + Remote control + Controllo remoto + + + Open a remote control window for a computer. + Apri una finestra di controllo remoto per un computer + + + Remote access + Accesso remoto + + + Remote view or control a computer + Vista o controllo remoto di un computer + + + Please enter the hostname or IP address of the computer to access: + Inserisci l'hostname o l'indirizzo IP del computer al quale vuoi accedere: + + + Show help about command + Mostra aiuto per i comandi + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 -%2 Accesso remoto + + + %1 - %2 - %3 Remote Access + %1 - %2 - %3 Accesso Remoto + + + + RemoteAccessWidgetToolBar + + View only + Osserva solo + + + Remote control + Controllo remoto + + + Send shortcut + Invia scorciatoia + + + Fullscreen + Schermo intero + + + Window + Finestra + + + Ctrl+Alt+Del + Ctrl+Alt+Canc + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Menu + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Connessione con %1 + + + Connected. + Connesso. + + + Screenshot + Screenshot + + + Exit + Uscita + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Inserisci i programmi o i comandi da eseguire nei computer selezionati. Puoi separare programmi/comandi multipli andando a capo. + + + Run programs + Esegui programmi + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + es. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + Nome + + + Remember and add to program menu + Ricorda e aggiungi al menu del programma + + + e.g. VLC + per esempio, VLC + + + + ScreenLockFeaturePlugin + + Lock + Blocca + + + Unlock + Sbloccca + + + Lock screen and input devices of a computer + Blocca schermo e dispositivi di input di un computer + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Per richiamare la completa attenzione di tutti gli utenti, puoi bloccare i loro computer utilizzando questo pulsante. In questo modo tutti i dispositivi di input saranno bloccati e gli schermi saranno colorati di nero. + + + Lock input devices + Blocca i dispositivi di input + + + Unlock input devices + Sblocca i dispositivi di input + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + Per recuperare la totale attenzione di tutti gli utenti, puoi bloccare i loro computer utilizzando questo pulsante. In questa modalità tutti i dispositivi di input sono bloccati mentre il desktop è ancora visibile. + + + + Screenshot + + unknown + sconosciuto + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + Non e' stato possibile effettuare una screenshot in quanto la directory %1 non esiste e non e' stato possibile crearla. + + + Screenshot + Screenshot + + + Could not open screenshot file %1 for writing. + Impossibile aprire il file screenshot %1 per la scrittura. + + + + ScreenshotFeaturePlugin + + Screenshot + Screenshot + + + Use this function to take a screenshot of selected computers. + Usa questa funzione per fare una screenshot dei computer selezionati. + + + Screenshots taken + Screenshot eseguiti + + + Screenshot of %1 computer have been taken successfully. + Screenshot di %1 computer sono stati effettuati con successo. + + + Take screenshots of computers and save them locally. + Effettua screenshot dei computer e salvali in locale. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Tutte le screenshot da te effettuate sono elencate qui. Puoi effettuare screenshot cliccando l'elemento "Screenshot" nel menu di un computer. Le screenshot possono essere gestite utilizzando i pulsanti qui sotto. + + + User: + Utente: + + + Computer: + Computer + + + Date: + Data: + + + Time: + Ora: + + + Show + Mostra + + + Delete + Elimina + + + Screenshot + Screenshot + + + Do you really want to delete all selected screenshots? + Vuoi davvero eliminare tutti gli screenshot selezionati? + + + + ServiceConfigurationPage + + General + Generale + + + Autostart + Avvio automatico + + + Hide tray icon + Nascondi l'icona tray + + + Start service + Avvia il servizio + + + Stopped + Fermato + + + Stop service + ferma il servizio + + + State: + Stato: + + + Enable firewall exception + Abilita le eccezioni del firewall + + + Allow connections from localhost only + Consenti connessioni solo da questo computer 'localhost' + + + VNC server + Server VNC + + + Plugin: + Plugin: + + + Restart %1 Service + Riavvia Servizio %1 + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Tutte le impostazioni sono state salvate. Per farle funzionare, il servizio %1 deve essere riavviato. Riavviarlo ora? + + + Running + In esecuzione + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + Abilitando questa opzione, il servizio avvierà un processo server per ogni sessione interattiva su un computer. In genere ciò è necessario per supportare i server terminal. + + + Show notification on remote connection + Mostra notifica sulla connessione remota + + + Show notification when an unauthorized access is blocked + Mostra notifica quando viene bloccato un accesso non autorizzato + + + Sessions + Sessioni + + + Single session mode (create server instance for local/physical session only) + Modalità sessione singola (crea istanza del server solo per sessione locale/fisica) + + + Multi session mode (create server instance for each local and remote desktop session) + Modalità multisessione (crea istanza del server per ogni sessione desktop locale e remota) + + + Maximum session count + Numero massimo di sessioni + + + Network port numbers + Numeri di porta di rete + + + Veyon server + Server Veyon + + + Internal VNC server + Server VNC interno + + + Feature manager + Manager delle funzionalità + + + Demo server + Server per la modalità Presentazione + + + Miscellaneous network settings + Impostazioni di rete varie + + + + ServiceControl + + Starting service %1 + Avvio del servizio%1 + + + Stopping service %1 + Arresto del servizio%1 + + + Registering service %1 + Registrazione del servizio%1 + + + Unregistering service %1 + Servizio di annullamento della registrazione%1 + + + Service control + Controllo del servizio + + + + ServiceControlPlugin + + Service is running + Servizio in esecuzione + + + Service is not running + Servizio non in esecuzione + + + Configure and control Veyon service + Configura e controlla il servizio Veyon + + + Register Veyon Service + Registra Servizio Veyon + + + Unregister Veyon Service + Annullamento della registrazione del Servizio Veyon + + + Start Veyon Service + Avvia Servizio Veyon + + + Stop Veyon Service + Arresta Servizio Veyon + + + Restart Veyon Service + Riavvia Servizio Veyon + + + Query status of Veyon Service + Stato del query del Servizio Veyon + + + Commands for configuring and controlling Veyon Service + Comandi per configurare e controllare il Servizio Veyon + + + + ShellCommandLinePlugin + + Run command file + Esegui il file di comando + + + File "%1" does not exist! + Il file "%1" non esiste! + + + Interactive shell and script execution for Veyon Control + Esecuzione interattiva di shell e script per controllo Veyon + + + Commands for shell functionalities + Comandi per le funzionalità della shell + + + + SlideshowPanel + + Previous + Precedente + + + Start/pause + Avvia/pausa + + + Next + Prossimo + + + Duration: + Durata: + + + + SpotlightPanel + + Add selected computers + Aggiungi computer selezionati + + + Remove selected computers + Rimuovi i computer selezionati + + + Update computers in realtime + Aggiorna i computer in tempo reale + + + Spotlight + Spotlight + + + Please select at least one computer to add. + Seleziona almeno un computer da aggiungere. + + + Please select at least one computer to remove. + Seleziona almeno un computer da rimuovere. + + + Add computers by clicking with the middle mouse button or clicking the first button below. + Aggiungi computer facendo clic con il pulsante centrale del mouse o facendo clic sul primo pulsante in basso. + + + + SystemTrayIcon + + System tray icon + Icona barra di sistema + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + Backend gruppi di utenti per gruppi di utenti di sistema + + + Default (system user groups) + Predefinito (gruppi utenti di sistema) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + Testare componenti e funzioni interni di Veyon + + + Commands for testing internal components and functions of Veyon + Testare componenti e funzioni interne di Veyon + + + + TextMessageDialog + + Send text message + Invia un messaggio di testo + + + Use the field below to type your message which will be sent to all selected users. + Utilizza il campo qui sotto per scrivere il messaggio che vuoi inviare agli utenti selezionati. + + + + TextMessageFeaturePlugin + + Text message + Invia messaggio + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Utilizza questa funzione per inviare un messaggio di testo a tutti gli utenti. Ad esempio per assegnargli nuovi compiti. + + + Message from teacher + Messaggio dall'insegnante + + + Send a message to a user + Invia un messaggio ad un utente + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Abilita cattura di finestre in livelli (semi-trasparente) + + + Poll full screen (leave this enabled per default) + Schermo pieno (lasciarlo abilitato per default) + + + Low accuracy (turbo mode) + Bassa qualità (modalità turbo) + + + Builtin UltraVNC server configuration + Configurazione del server UltraVNC integrato + + + Enable multi monitor support + Abilita il supporto per più monitor + + + Enable Desktop Duplication Engine on Windows 8 and newer + Abilita motore di duplicazione Desktop su versioni di Windows 8 e successive + + + Maximum CPU usage + Utilizzo massimo della CPU + + + + UserConfig + + No write access + Nessun accesso in scrittura + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Impossibile salvare le tue impostazioni personali. Controlla il percorso file di configurazione utente utilizzando il Configuratore %1 + + + + UserLoginDialog + + User login + Login utente + + + Please enter a username and password for automatic login on all computers. + Inserisci un nome utente e una password per l'accesso automatico su tutti i computer. + + + Username + Nome Utente + + + Password + Password + + + + UserSessionControlPlugin + + Log in + Log in + + + Click this button to log in a specific user on all computers. + Fare clic su questo pulsante per accedere a un utente specifico su tutti i computer. + + + Log off + Disconnetti + + + Click this button to log off users from all computers. + Fare clic su questo pulsante per disconnettere gli utenti da tutti i computer. + + + Confirm user logoff + Conferma logout utente + + + Do you really want to log off the selected users? + Vuoi veramente far fare logout agli utenti selezionati? + + + User session control + Controllo sessione utente + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [FALLITO] + + + Invalid command! + Comando non valido! + + + Available commands: + Comandi disponibili: + + + Invalid arguments given + Argomenti dati non validi + + + Not enough arguments given - use "%1 help" for more information + Non sono stati dati abbastanza argomenti - usa "%1 help" per maggiori informazioni + + + Unknown result! + Risultato sconosciuto! + + + Available modules: + Moduli disponibili: + + + No module specified or module not found - available modules are: + Nessun modulo specificato o modulo non trovato - i moduli disponibili sono: + + + Plugin not licensed + Plugin non concesso in licenza + + + INFO + INFO + + + ERROR + ERRORE + + + USAGE + USO + + + DESCRIPTION + DESCRIZIONE + + + EXAMPLES + ESEMPI + + + WARNING + ATTENZIONE + + + + VeyonServiceControl + + Veyon Service + Servizio Veyon + + + + VncViewWidget + + Establishing connection to %1 ... + Connessione a %1 ... + + + + WebApiConfigurationPage + + Web API + Web API + + + General + Generale + + + Network port + Porta di rete + + + Enable WebAPI server + Abilita il server API Web + + + Connection settings + Impostazioni di connessione + + + Lifetime + A vita + + + h + h + + + s + s + + + Idle timeout + Timeout di inattività + + + Authentication timeout + Timeout di autenticazione + + + Maximum number of open connections + Numero massimo di connessioni aperte + + + Connection encryption + Crittografia della connessione + + + TLS certificate file + File del certificato TLS + + + TLS private key file + File della chiave privata TLS + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + Usa HTTPS con TLS 1.3 invece di HTTP + + + + WebApiPlugin + + Run WebAPI server + Esegui il server API Web + + + Failed to start WebAPI server at port %1 + Impossibile avviare il server API Web sulla porta %1 + + + WebAPI server running at port %1 + Server WebAPI in esecuzione sulla porta %1 + + + Provide access to a computer via HTTP + Fornire l'accesso a un computer tramite HTTP + + + Commands for running the WebAPI server + Comandi per l'esecuzione del server API Web + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + Non è possibile cambiare i settaggi del SAS via software. L'invio della combinazione Ctrl+Alt+Del da terminale remoto non produrrà risultati. + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + Generale + + + Enable SAS generation by software (Ctrl+Alt+Del) + Abilita generazione SAS via software (Ctrl+Alt+Del) + + + Screen lock + Blocco schermo + + + Hide taskbar + Nascondi la barra delle applicazioni + + + Hide start menu + Nascondi il menu di avvio + + + Hide desktop + Nascondi desktop + + + User authentication + Autenticazione utente + + + Use alternative user authentication mechanism + Utilizzare un meccanismo di autenticazione utente alternativo + + + User login + Login utente + + + Input start delay + Ritardo inizio ingresso + + + Simulated key presses interval + Simulato l'intervallo di pressioni dei tasti + + + Confirm legal notice (message displayed before user logs in) + Conferma avviso legale (messaggio visualizzato prima dell'accesso dell'utente) + + + Use input device interception driver + Usa il driver di intercettazione del dispositivo di input + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Plugin che implementa funzioni astratte per la piattaforma Windows + + + + WindowsServiceControl + + The service "%1" is already installed. + Il servizio "%1" è già installato. + + + The service "%1" could not be installed. + Il servizio "%1" non può essere installato. + + + The service "%1" has been installed successfully. + Il servizio "%1" è stato installato correttamente. + + + The service "%1" could not be uninstalled. + Il servizio "%1" non può essere disinstallato. + + + The service "%1" has been uninstalled successfully. + Il servizio "%1" è stato disinstallato correttamente. + + + The start type of service "%1" could not be changed. + Non è stato possibile modificare il tipo di avvio del servizio "%1". + + + Service "%1" could not be found. + Il servizio "%1" non è stato trovato. + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Configurazione del server x11vnc integrata + + + Custom x11vnc parameters: + Parametri per x11vnc personalizzati: + + + Do not use X Damage extension + Non usare l'estensione X Damage + + + diff --git a/translations/veyon_ja.ts b/translations/veyon_ja.ts new file mode 100644 index 0000000..dbe4244 --- /dev/null +++ b/translations/veyon_ja.ts @@ -0,0 +1,4063 @@ + + + + + AboutDialog + + About + 詳細 + + + Translation + 翻訳 + + + License + ライセンス + + + About Veyon + Veyonについて + + + Contributors + 貢献者 + + + Version: + バージョン: + + + Website: + ウェブサイト: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + 現在の言語(または現地の英語)は翻訳されていません。 + +この言語への翻訳や既存の翻訳の改善に興味がある方はVeyonの開発者に連絡してください。 + + + About %1 %2 + %1 %2について + + + Support Veyon project with a donation + 寄付してVeyonプロジェクトを支援する + + + + AccessControlPage + + Computer access control + コンピューターアクセスコントロール + + + Grant access to every authenticated user (default) + すべての認証されたユーザーにアクセスを許可(デフォルト) + + + Test + テスト + + + Process access control rules + アクセスコントロールのルールを処理 + + + User groups authorized for computer access + コンピューターへのアクセスを許可されたユーザーグループ + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + あなたのVeyonネットワーク上のコンピューターにアクセスすることを許可するメンバーのグループを追加してください。 + + + Authorized user groups + 認証されたユーザーグループ + + + All groups + すべてのグループ + + + Access control rules + アクセスコントロールルール + + + Add access control rule + アクセスコントロールルールを追加 + + + Remove access control rule + アクセスコントロールルールを削除 + + + Move selected rule down + 選択したルールを下げる + + + Move selected rule up + 選択したルールを上げる + + + Edit selected rule + 選択したルールを編集 + + + Enter username + ユーザー名を入力 + + + Please enter a user login name whose access permissions to test: + アクセス権限をテストするユーザー名を入力してください: + + + Access allowed + アクセス許可 + + + The specified user is allowed to access computers with this configuration. + このユーザーはこの設定でコンピューターにアクセス許可されています。 + + + Access denied + アクセス拒否 + + + The specified user is not allowed to access computers with this configuration. + このユーザーは設定によりコンピューターへのアクセスが拒否されました。 + + + Enable usage of domain groups + ドメイングループの使用を有効にする + + + User groups backend: + ユーザーグループのバックエンド + + + Missing user groups backend + ユーザーグループのバックエンドが不明 + + + No default user groups plugin was found. Please check your installation! + ユーザーグループのプラグインが見つかりませんでした。 +インストール状況を確認してください! + + + Restrict access to members of specific user groups + 特定のグループのメンバーのアクセスを制限する + + + + AccessControlRuleEditDialog + + Edit access control rule + アクセスコントロールルールを編集 + + + General + 一般 + + + enter a short name for the rule here + このルールの短い名前を入力 + + + Rule name: + ルール名: + + + enter a description for the rule here + このルールの説明を入力 + + + Rule description: + ルール説明: + + + Invert all conditions ("is/has" interpreted as "is/has not") + 反転する + + + Conditions + コンディション + + + is member of group + はグループのメンバーです + + + Accessing computer is localhost + アクセスしているコンピューターはローカルホストです + + + Accessing user is logged on user + アクセスしているユーザーはユーザーでログインしています + + + Accessing user is already connected + アクセスしているユーザーは既に接続されています + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + もし1つ以上のコンディションが有効にされている場合、ルールが有効になるようそれぞれのコンディションを調整する必要があります。もし複数のコンディションを使用したい場合複数のコントロールルールを作ってください。 + + + Action + 実行 + + + Allow access + アクセス許可 + + + Deny access + アクセス拒否 + + + Ask logged on user for permission + ログインしているユーザーに権限を確認 + + + None (rule disabled) + なし(ルール無効) + + + Accessing user + ユーザーにアクセス中 + + + Accessing computer + コンピューターにアクセス中 + + + Local (logged on) user + ローカルユーザー + + + Local computer + ローカルコンピューター + + + Always process rule and ignore conditions + 常にルールを有効にし、コンディションを無視する + + + No user logged on + ログインユーザーなし + + + Accessing user has one or more groups in common with local (logged on) user + アクセスしているユーザーは1つかそれ以上のグループにローカルユーザーと共通で所属しています + + + Accessing computer and local computer are at the same location + アクセスしているコンピューターとローカルコンピューターが同じ場所にあります + + + is located at + の場所 + + + + AccessControlRulesTestDialog + + Access control rules test + アクセスコントロールルールのテスト + + + Accessing user: + アクセス中のユーザー + + + Local computer: + ローカルコンピューター: + + + Accessing computer: + アクセス中のコンピューター: + + + Please enter the following user and computer information in order to test the configured ruleset. + 設定されたルールをテストするために以下のユーザーとコンピューター情報を入力してください。 + + + Local user: + ローカルユーザー: + + + Connected users: + 接続されたユーザー: + + + The access in the given scenario is allowed. + このシナリオでのアクセスが許可されています。 + + + The access in the given scenario is denied. + このシナリオでのアクセスが拒否されています。 + + + The access in the given scenario needs permission of the logged on user. + このシナリオでのアクセスにはログインしているユーザーの許可が必要です。 + + + ERROR: Unknown action + エラー:不明のアクション + + + Test result + テスト結果 + + + + AuthKeysConfigurationPage + + Authentication keys + 認証キー + + + Introduction + 紹介 + + + Key file directories + キーファイルの場所: + + + Public key file base directory + パブリックキーのファイルの場所 + + + Private key file base directory + プライベートキーのファイルの場所 + + + Available authentication keys + 使用可能な認証キー + + + Create key pair + キーを作成 + + + Delete key + キーを削除 + + + Import key + キーをインポート + + + Export key + キーをエクスポート + + + Set access group + アクセスグループを設定 + + + Key files (*.pem) + キーファイル(拡張子.pem) + + + Authentication key name + 認証キーの名前 + + + Please enter the name of the user group or role for which to create an authentication key pair: + 認証キーを作るユーザーグループかルールの名前を入力してください: + + + Do you really want to delete authentication key "%1/%2"? + 認証キー"%1/%2"を削除して良いですか? + + + Please select a key to delete! + 削除するキーを選択してください! + + + Please enter the name of the user group or role for which to import the authentication key: + 認証キーをインポートするユーザーグループかルールの名前を入力してください: + + + Please select a key to export! + エクスポートするキーを選択してください! + + + Please select a user group which to grant access to key "%1": + 認証キー:"%1"にアクセス許可するユーザーグループを選択してください: + + + Please select a key which to set the access group for! + アクセスグループに設定するキーを選択してください! + + + Please perform the following steps to set up key file authentication: + キーファイル認証を設定するために以下の手順を追ってください + + + 1) Create a key pair on the master computer. + 1.マスターPCで認証キーを作成 + + + 2) Set an access group whose members should be allowed to access other computers. + 2.他のコンピューターへのアクセスを許可するメンバーのグループを設定 + + + 3) Export the public key and import it on all client computers with the same name. + 3.パブリックキーをエクスポートし、すべてのクライアントPCに同じ名前でインポートしてください。 + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + 詳細は<a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon管理者マニュアル</a>を確認してください。 + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + 1つの認証キーのペアは、プライベートキーとパブリックキーの2つの暗号キーで構成されています。 +プライベートキーを使用すると、マスターコンピューターのユーザーがクライアントPCにアクセスすることができます。 +許可されたユーザーのみがプライベートキーを読む権限があるようにしてください。 +パブリックキーはクライアントPCが他からのアクセス要求を認証するために使用します。 + + + + AuthKeysManager + + Please check your permissions. + 権限を確認してください。 + + + Key name contains invalid characters! + キーの名前に使えない文字が入っています! + + + Invalid key type specified! Please specify "%1" or "%2". + 無効なキータイプが指定されました。"%1" か "%2"を指定してください。 + + + Specified key does not exist! Please use the "list" command to list all installed keys. + 指定したキーが見つかりませんでした。“list”コマンドを使用してキーをインストールしてください。 + + + One or more key files already exist! Please delete them using the "delete" command. + 1つ以上のキーが既に存在しています。“delete”コマンドを使用して削除してください。 + + + Creating new key pair for "%1" + "%1"に新しいキーペアを作成 + + + Failed to create public or private key! + パブリックキーかプライベートキーの作成に失敗しました! + + + Newly created key pair has been saved to "%1" and "%2". + 新しく作成されたキーは"%1" と "%2"に保存されました。 + + + Could not remove key file "%1"! + "%1"のキーファイルを削除できませんでした! + + + Could not remove key file directory "%1"! + "%1"キーの保存場所を削除できませんでした。 + + + Failed to create directory for output file. + 書き出しファイル用の保存場所の作成に失敗しました。 + + + File "%1" already exists. + ファイル "%1" が既に存在します。 + + + Failed to write output file. + 書き出しに失敗しました。 + + + Key "%1/%2" has been exported to "%3" successfully. + キー"%1/%2"を"%3"に書き出しました。 + + + Failed read input file. + ファイルの読み込みに失敗しました。 + + + File "%1" does not contain a valid private key! + ファイル( "%1")に有効なプライベートキーがありません。 + + + File "%1" does not contain a valid public key! + ファイル( "%1")に有効なパブリックキーがありません。 + + + Failed to create directory for key file. + キーファイル用のディレクトリーの作成に失敗しました。 + + + Failed to write key file "%1". + キーファイル"%1"の書き込みに失敗しました。 + + + Failed to set permissions for key file "%1"! + キーファイル"%1へのアクセス権限の設定に失敗しました。 + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + キー"%1/%2"のインポートに成功しました。許可されていないアクセスを防ぐため、"%3"へのアクセス権限を確認してください。 + + + Failed to convert private key to public key + プライベートキーからパブリックキーへの変換に失敗しました。 + + + Failed to create directory for private key file "%1". + プライベートキーファイル"%1"のディレクトリーの作成に失敗しました。 + + + Failed to save private key in file "%1"! + "%1"へのプライベートキーの保存に失敗しました。 + + + Failed to set permissions for private key file "%1"! + プライベートキー"%1"へのアクセス権限の設定に失敗しました。 + + + Failed to create directory for public key file "%1". + パブリックキーのファイル"%1"のディレクトリーの作成に失敗しました。 + + + Failed to save public key in file "%1"! + "%1"のファイルへのパブリックキーの保存に失敗しました。 + + + Failed to set permissions for public key file "%1"! + パブリックキー"%1"へのアクセス権限の設定に失敗しました。 + + + Failed to set owner of key file "%1" to "%2". + キーファイル"%1" から "%2"への所有者の設定に失敗しました。 + + + Failed to set permissions for key file "%1". + キーファイル"%1"へのアクセス権限の設定に失敗しました。 + + + Key "%1" is now accessible by user group "%2". + キー"%1"はユーザーグループ"%2"によってアクセス可能になりました。 + + + <N/A> + <N/A> + + + Failed to read key file. + キーファイルの読み込みに失敗しました。 + + + + AuthKeysPlugin + + Create new authentication key pair + 新しい認証キーキーペアの作成 + + + Delete authentication key + 認証キーを削除 + + + List authentication keys + 認証キーリスト + + + Import public or private key + パブリックまたはプライベートキーのインポート + + + Export public or private key + パブリックまたはプライベートキーのエクスポート + + + Extract public key from existing private key + 既存のプライベートキーからパブリックキーを作成 + + + Set user group allowed to access a key + ユーザーグループをキーにアクセス可能にする + + + KEY + キー + + + ACCESS GROUP + アクセスグループ + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + このコマンドはファイルのアクセス権限を、ユーザーグループ<ACCESS GROUP>がアクセスできるよう、<KEY>に設定します。 + + + NAME + 名前 + + + FILE + ファイル + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + このコマンドは認証キー<KEY>を<FILE>にエクスポートします。<FILE>が指定されていない場合、<KEY>の名前とタイプから新しい名前が作成されます。 + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + このコマンドは認証キー<KEY>を<FILE>からインポートします。<FILE>が指定されていない場合、<KEY>の名前とタイプから新しい名前が作成されます。 + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + このコマンドは設定されたキーディレクトリーにあるすべての有効な認証キーを一覧にします。"%1"が指定されていれば、キーの詳細が表で表示されます。キーにアクセス権限が無いなどキーにアクセスできなかった場合、いくつかの詳細が表示されない場合があります。 + + + Please specify the command to display help for! + ヘルプを表示するコマンドを指定してください! + + + TYPE + タイプ + + + PAIR ID + ペアID + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + 認証キーを管理するコマンド + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + このコマンドは<NAME>という名前で認証キーペアを作成し、設定されたキーの保存ディレクトリーに保存されます。このパラメーターは文字だけを含む名前である必要があります。 + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + このコマンドは認証キー<KEY>を設定されたーディレクトリーから削除します。一度削除したキーは復元できないことに注意してください。 + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + このコマンドはプライベートキー<KEY>からパブリックキーを作成し、対応するパブリックキーとして保存します。別のマスターPCを設定する際はプライベートキーを転送すると便利です。パブリックキーが作成されます。 + + + + AuthKeysTableModel + + Name + 名前 + + + Type + タイプ + + + Access group + アクセスグループ + + + Pair ID + ペアID + + + + BuiltinDirectoryConfigurationPage + + Computers + コンピューター + + + Name + 名前 + + + Host address/IP + ホストアドレス/IP + + + MAC address + MACアドレス + + + Add new computer + 新しいコンピューターを追加 + + + Remove selected computer + 選択したコンピューターを削除 + + + New computer + 新しいコンピューター + + + Builtin directory + ビルドインディクショナリー + + + Locations & computers + 場所&コンピューター + + + Locations + 場所 + + + Add new location + 新しい場所を追加 + + + Remove selected location + 選択した場所を削除 + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + コマンドラインでCSVを使ってインポートできます。詳細は<a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">オンラインドキュメント</a>を確認してください。 + + + New location + 新しい場所 + + + + BuiltinDirectoryPlugin + + Show help for specific command + 指定したコマンドのヘルプを表示 + + + Import objects from given file + ファイルからオブジェクトを追加 + + + Export objects to given file + ファイルへオブジェクトをエクスポート + + + Invalid type specified. Valid values are "%1" or "%2". + 無効なタイプが指定されました。有効な値は"%1" か "%2"です。 + + + Type + タイプ + + + Name + 名前 + + + Host address + ホストアドレス + + + MAC address + MACアドレス + + + Specified object not found. + 指定したオブジェクトが見つかりませんでした。 + + + File "%1" does not exist! + ファイル"%1"が存在しません! + + + Can't open file "%1" for reading! + "%1"の読み込みに失敗 + + + Unknown argument "%1". + 不明な因数"%1" + + + Computer "%1" (host address: "%2" MAC address: "%3") + コンピューター"%1"(ホストアドレス:"%2" MACアドレス:"%3") + + + Unclassified object "%1" with ID "%2" + 未分類のオブジェクト"%1"、ID"%2" + + + None + なし + + + Computer + コンピューター + + + Root + ルート + + + Invalid + 無効 + + + Error while parsing line %1. + "%1"の解析中にエラーが発生しました。 + + + Network object directory which stores objects in local configuration + ローカル設定でオブジェクトを保管するネットワークオブジェクトの保存先 + + + Commands for managing the builtin network object directory + ビルドインのネットワークオブジェクト保存先を管理するためのコマンド + + + No format string or regular expression specified! + ストリングや一般の表現が指定されていません。 + + + Can't open file "%1" for writing! + "%1"に書き込むことができませんでした。 + + + No format string specified! + フォーマットが指定されていません! + + + Object UUID + オブジェクトUUID + + + Parent UUID + ペアレントUUID + + + Add a location or computer + 場所かコンピューターを追加 + + + Clear all locations and computers + 全ての場所とコンピューターを削除 + + + Dump all or individual locations and computers + 全てまたは個別の場所かコンピューターを削除 + + + List all locations and computers + 全てのコンピューターと場所の一覧 + + + Remove a location or computer + 場所かコンピューターを削除 + + + Location "%1" + 場所"%1" + + + Builtin (computers and locations in local configuration) + ビルドイン(ローカル設定内のコンピューターと場所) + + + Location + 場所 + + + FILE + ファイル + + + LOCATION + 場所 + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + CSVファイルを1つの部屋にインポート + + + Import CSV file with location name in first column + 最初の列に場所の名前があるCSVファイルをインポート + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + タイプ + + + NAME + 名前 + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + + + + + ComputerControlListModel + + Host/IP address: %1 + + + + Active features: %1 + + + + Online and connected + + + + Establishing connection + + + + Computer offline or switched off + + + + Authentication failed or access denied + + + + Disconnected + + + + No user logged on + ログインユーザーなし + + + Logged on user: %1 + + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + + + + Authentication error + 認証エラー + + + Remote access + リモートアクセス + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + ユーザー + + + Missing network object directory plugin + + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + + + + Add location + + + + Save computer/user list + + + + Select output filename + + + + CSV files (*.csv) + + + + File error + + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + + + + Please specify a valid filename for the configuration export. + + + + Please specify a valid key. + + + + Specified key does not exist in current configuration! + + + + Please specify a valid value. + + + + Configure Veyon at command line + + + + Output file is not writable! + + + + Output directory is not writable! + + + + Configuration file is not readable! + + + + Clear system-wide Veyon configuration + + + + List all configuration keys and values + + + + Import configuration from given file + + + + Export configuration to given file + + + + Read and output configuration value for given key + + + + Write given value to given configuration key + + + + Unset (remove) given configuration key + + + + Commands for managing the configuration of Veyon + + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + + + + + DemoConfigurationPage + + Demo server + デモサーバー + + + Tunables + + + + ms + ms + + + Key frame interval + + + + Memory limit + + + + MB + + + + Update interval + + + + s + + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + + + + Window demo + ウィンドウデモ + + + Give a demonstration by screen broadcasting + + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + このモードでは、このPCの画面が全てのコンピューターの画面にウィンドウとして表示されます。全てのユーザーは必要に応じて他のウィンドウに切り替えることができます。 + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + + + + Confirm desktop access + デスクトップアクセスの確認 + + + Never for this session + + + + Always for this session + + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + + DesktopServicesConfigurationPage + + Programs & websites + + + + Predefined programs + + + + Name + 名前 + + + Path + + + + Add new program + + + + Remove selected program + + + + Predefined websites + + + + Remove selected website + + + + URL + URL + + + New program + + + + New website + + + + + DesktopServicesFeaturePlugin + + Run program + プログラムを起動 + + + Open website + + + + Click this button to open a website on all computers. + + + + Start programs and services in user desktop + + + + Click this button to run a program on all computers. + 全てのコンピューターでプログラムを起動します。 + + + Run program "%1" + "%1"プログラムを起動します + + + Custom program + カスタムプログラム + + + Open website "%1" + + + + Custom website + + + + + DocumentationFigureCreator + + Teacher + + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + カスタムプログラム + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + + + + Port: + + + + Password: + + + + + FeatureControl + + Feature control + + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + ディレクトリ + + + Destination directory + + + + Default source directory + + + + Options + オプション + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + オプション + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + + + + Select one or more files to transfer + + + + Transfer files to remote computer + コンピューターへファイルを転送 + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + ユーザーインターフェイス + + + Language: + + + + Use system language setting + + + + Veyon + + + + Logging + + + + Log file directory + ログファイルのディレクトリ + + + Log level + ログレベル + + + Nothing + 無し + + + Only critical messages + 致命的なエラーのみ + + + Errors and critical messages + エラーと致命的なエラー + + + Warnings and errors + 警告とエラー + + + Information, warnings and errors + + + + Debug messages and everything else + + + + Limit log file size + + + + Clear all log files + + + + Log to standard error output + + + + Network object directory + + + + Backend: + バックエンド: + + + Update interval: + + + + %1 service + + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + + + + All log files were cleared successfully. + + + + Error + + + + Could not remove all log files. + + + + MB + + + + Rotate log files + + + + x + + + + seconds + 秒 + + + Write to logging system of operating system + + + + Authentication + 認証 + + + Method: + + + + Logon authentication + ログオン認証 + + + Key file authentication + キーファイル認証 + + + Test + テスト + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + + + + + LdapConfigurationPage + + Basic settings + + + + General + 一般 + + + LDAP server and port + + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + + + + e.g. OU=Computers + + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + + LDAP base DN test failed + + + + LDAP base DN test successful + + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + ユーザー名を入力 + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + + + + Please enter a group name whose members to query: + + + + group members + + + + Group not found + + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + コンピュータのMACアドレス + + + users + + + + user groups + + + + computer groups + + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + + + + LDAP %1 test failed + + + + LDAP %1 test successful + + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + + + + TLS certificate verification + + + + System defaults + + + + Never (insecure!) + + + + Custom CA certificate file + + + + None + なし + + + TLS + + + + SSL + + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + テスト + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + コマンドに関するヘルプを表示する + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + %1 (LDAP/ADからコンピューターとその場所を読み込む) + + + %1 (load users and groups from LDAP/AD) + %1 (LDAP/ADからユーザーとグループを読み込む) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + + + + enter search filter... + + + + + MainToolBar + + Configuration + + + + Disable balloon tooltips + + + + Show icons only + + + + + MainWindow + + MainWindow + メインウィンドウ + + + toolBar + ツールバー + + + General + 一般 + + + &File + &ファイル + + + &Help + + + + &Quit + + + + Ctrl+Q + + + + Ctrl+S + + + + L&oad settings from file + + + + Ctrl+O + + + + About Qt + + + + Authentication impossible + + + + Configuration not writable + + + + Load settings from file + + + + Save settings to file + + + + Unsaved settings + + + + There are unsaved settings. Quit anyway? + + + + Veyon Configurator + + + + Service + + + + Master + + + + Access control + + + + About Veyon + Veyonについて + + + Auto + + + + About + 詳細 + + + %1 Configurator %2 + + + + JSON files (*.json) + + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + アクセス拒否 + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + + + + Align computers to grid + + + + %1 Configurator + + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + ディレクトリ + + + User configuration + + + + Feature on computer double click: + + + + Features + + + + All features + + + + Disabled features + + + + Screenshots + + + + <no feature> + + + + Basic settings + + + + Behaviour + + + + Enforce selected mode for client computers + + + + Hide local computer + + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + + + + User interface + ユーザーインターフェイス + + + Background color + + + + Thumbnail update interval + + + + ms + ms + + + Program start + + + + Modes and features + + + + User and computer name + + + + Only user name + + + + Only computer name + + + + Computer thumbnail caption + + + + Text color + + + + Sort order + + + + Computer and user name + + + + Computer locations + + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + このモードでは、全てのコンピューターを1つ以上の場所でモニターできるようになります。 + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + + + + Name: + + + + + PasswordDialog + + Username + ユーザー名 + + + Password + パスワード + + + Veyon Logon + Veyonログオン + + + Authentication error + 認証エラー + + + Logon failed with given username and password. Please try again! + 入力したユーザー名とパスワードでログインに失敗しました。再試行してください! + + + Please enter your username and password in order to access computers. + アクセスするにはユーザー名とパスワードを入力してください。 + + + + PowerControlFeaturePlugin + + Power on + 電源オン + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + + Reboot + + + + Click this button to reboot all computers. + + + + Power down + + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + 電源オン・オフ・再起動 + + + Confirm reboot + + + + Confirm power down + パワーダウンを確認 + + + Do you really want to reboot the selected computers? + + + + Do you really want to power down the selected computer? + + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + ヘルプを表示するコマンドを指定してください! + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + このコンピューターは遠隔操作でシャットダウンが要求されました。 +コンピューターをシャットダウンして良いですか? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + リモートビュー + + + Open a remote view for a computer without interaction. + 通知なしにリモートビューを開始 + + + Remote control + リモート操作 + + + Open a remote control window for a computer. + + + + Remote access + リモートアクセス + + + Remote view or control a computer + + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + コマンドに関するヘルプを表示する + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + + + + Remote control + リモート操作 + + + Send shortcut + + + + Fullscreen + フルスクリーン + + + Window + + + + Ctrl+Alt+Del + + + + Ctrl+Esc + + + + Alt+Tab + + + + Alt+F4 + + + + Win+Tab + + + + Win + + + + Menu + + + + Alt+Ctrl+F1 + + + + Connecting %1 + + + + Connected. + + + + Screenshot + + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + ロック + + + Unlock + ロック解除 + + + Lock screen and input devices of a computer + コンピューターのスクリーンと入力機器をロック + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + ユーザーの注意をひくためには、このボタンを使用して全てのコンピューターの入力をロック・黒画面を表示し、PCをロックできます。 + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + + + + Use this function to take a screenshot of selected computers. + + + + Screenshots taken + + + + Screenshot of %1 computer have been taken successfully. + + + + Take screenshots of computers and save them locally. + + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + + + + Computer: + + + + Date: + + + + Time: + + + + Show + + + + Delete + + + + Screenshot + + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + 一般 + + + Autostart + 自動起動 + + + Hide tray icon + 通知領域のアイコンを非表示 + + + Start service + サービスを起動 + + + Stopped + 停止 + + + Stop service + サービスを停止 + + + State: + 状態: + + + Enable firewall exception + + + + Allow connections from localhost only + + + + VNC server + + + + Plugin: + + + + Restart %1 Service + サービス%1を再起動します + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + + Running + + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + 許可されていないアクセスがブロックされた時、通知を表示 + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + デモサーバー + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + + + + Stopping service %1 + + + + Registering service %1 + + + + Unregistering service %1 + + + + Service control + + + + + ServiceControlPlugin + + Service is running + + + + Service is not running + + + + Configure and control Veyon service + + + + Register Veyon Service + + + + Unregister Veyon Service + + + + Start Veyon Service + + + + Stop Veyon Service + + + + Restart Veyon Service + + + + Query status of Veyon Service + + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + ファイル"%1"が存在しません! + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + + + + Use the field below to type your message which will be sent to all selected users. + + + + + TextMessageFeaturePlugin + + Text message + + + + Use this function to send a text message to all users e.g. to assign them new tasks. + + + + Message from teacher + 先生からのメッセージ + + + Send a message to a user + + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + + + + Poll full screen (leave this enabled per default) + + + + Low accuracy (turbo mode) + + + + Builtin UltraVNC server configuration + + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + ユーザー名 + + + Password + パスワード + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + ユーザーセッション制御 + + + + VeyonCore + + [OK] + + + + [FAIL] + + + + Invalid command! + + + + Available commands: + + + + Invalid arguments given + + + + Not enough arguments given - use "%1 help" for more information + + + + Unknown result! + + + + Available modules: + + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + + + + INFO + + + + ERROR + + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + + + + + VncViewWidget + + Establishing connection to %1 ... + + + + + WebApiConfigurationPage + + Web API + + + + General + 一般 + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + 一般 + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + スクリーンロック + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + + + + Custom x11vnc parameters: + + + + Do not use X Damage extension + + + + diff --git a/translations/veyon_ko.ts b/translations/veyon_ko.ts new file mode 100644 index 0000000..135d232 --- /dev/null +++ b/translations/veyon_ko.ts @@ -0,0 +1,4081 @@ + + + + + AboutDialog + + About + 정보 + + + Translation + 번역 + + + License + 라이센스 + + + About Veyon + Veyon에 대해서 + + + Contributors + 참여하신 분들 + + + Version: + 버전: + + + Website: + 웹 사이트: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + 현재 한국어 번역이 진행중입니다 (영어에서 한국어로). + +Veyon 번역에 관심이 있거나 번역을 개선하실 의향이 있으신 분들은 Veyon 개발자에게 연락바랍니다. + + + About %1 %2 + %1 %2 에 대하여 + + + Support Veyon project with a donation + Veyon 프로그램에 기부하여 지원하기 + + + + AccessControlPage + + Computer access control + 컴퓨터 접근제어 + + + Grant access to every authenticated user (default) + 인증된 모든 사용자에게 접근 허용(기본값) + + + Test + 테스트 + + + Process access control rules + 프로세스 접근제어 규칙 + + + User groups authorized for computer access + 컴퓨터 접근이 허용된 유저 그룹 + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Veyon 네트워크의 컴퓨터에 접근을 허용할 사용자가 속한 그룹을 추가 하세요. + + + Authorized user groups + 승인된 사용자 그룹 + + + All groups + 모든 그룹 + + + Access control rules + 접근 제어 규칙 + + + Add access control rule + 접근제어규칙 추가 + + + Remove access control rule + 접근제어규칙 제거 + + + Move selected rule down + 선택된 규칙을 아래로 이동 + + + Move selected rule up + 선택된 규칙 위로 이동 + + + Edit selected rule + 선택된 규칙 수정 + + + Enter username + 사용자 이름 입력 + + + Please enter a user login name whose access permissions to test: + 접근 권한을 시험할 사용자의 로그인 이름을 입력하세요: + + + Access allowed + 접근 허용됨 + + + The specified user is allowed to access computers with this configuration. + 선택된 사용자는 이 설정으로 컴퓨터 접근이 허가됨. + + + Access denied + 접근 거부됨 + + + The specified user is not allowed to access computers with this configuration. + 선택된 사용자는 이 설정으로 컴퓨터 접근이 거부 됨. + + + Enable usage of domain groups + 도메인 그룹 사용 허용 + + + User groups backend: + 유저그룹 백엔드: + + + Missing user groups backend + 유저그룹 백엔드 없음 + + + No default user groups plugin was found. Please check your installation! + 디폴트 유저 그룹 플러그인이 없습니다. 설치상태를 확인하세요! + + + Restrict access to members of specific user groups + 특정 그룹 사용자의 접근 제한 + + + + AccessControlRuleEditDialog + + Edit access control rule + 접근 제어 규칙 편집 + + + General + 일반사항 + + + enter a short name for the rule here + 규칙의 단축이름을 넣으세요 + + + Rule name: + 규칙이름: + + + enter a description for the rule here + 이곳에 규칙에 대한 설명을 넣으세요 + + + Rule description: + 규칙 설명: + + + Invert all conditions ("is/has" interpreted as "is/has not") + 모든 조건 반대로 ("is/has" 는 "is/has not" 로 변경됨) + + + Conditions + 조건 + + + is member of group + 는 그룹의 멤버임 + + + Accessing computer is localhost + 연결하는 컴퓨터는 로컬 호스트입니다 + + + Accessing user is logged on user + 접속하는 사용자는 로그온되어 있습니다 + + + Accessing user is already connected + 접속하는 사용자는 이미 연결되어 있습니다 + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + 한개 이상의 조건이 활성화 될 경우 규칙이 적용되려면 각각의 조건이 일치해야 한다 (논리적 AND). 여러개 중에서 하나의 조건만 맞아야 할 경우에는(논리적 OR) 다수의 접근 제어 규칙을 생성하세요 . + + + Action + 수행작업 + + + Allow access + 접근 허가 + + + Deny access + 접근 거부 + + + Ask logged on user for permission + 로그 온 된 사용자에게 허가요구 + + + None (rule disabled) + 없음 (규칙 비활성화) + + + Accessing user + 접속중인 사용자 + + + Accessing computer + 연결하는 컴퓨터 + + + Local (logged on) user + (로그온된)로컬 사용자 + + + Local computer + 로컬 컴퓨터 + + + Always process rule and ignore conditions + 조건 무시하고 항상 규칙 수행 + + + No user logged on + 로그온된 사용자 없음 + + + Accessing user has one or more groups in common with local (logged on) user + 연결하는 사용자는 로컬 사용자(로그온됨)와 하나 또는 그이상의 공통 그룹을 갖고 있습니다 + + + Accessing computer and local computer are at the same location + 같은 장소에 있는 다른 컴퓨터와 로컬 컴퓨터에 접근 + + + is located at + 에 위치함 + + + + AccessControlRulesTestDialog + + Access control rules test + 접근제어 규칙 시험 + + + Accessing user: + 접속하는 사용자: + + + Local computer: + 로컬 컴퓨터: + + + Accessing computer: + 연결하는 컴퓨터: + + + Please enter the following user and computer information in order to test the configured ruleset. + 설정된 규칙을 시험하기 위해 다음 사용자및 컴퓨터 정보를 입력하세요. + + + Local user: + 로컬 사용자: + + + Connected users: + 연결된 사용자: + + + The access in the given scenario is allowed. + 주어진 상황에 대한 접근이 허용됨. + + + The access in the given scenario is denied. + 주어진 상황에 대한 접근이 거부됨. + + + The access in the given scenario needs permission of the logged on user. + 주어진 상황에 대한 접근은 로그온된 사용자의 허가가 필요함. + + + ERROR: Unknown action + ERROR: 정의되지 않은 작업 + + + Test result + 시험 결과 + + + + AuthKeysConfigurationPage + + Authentication keys + 인증 키 + + + Introduction + 소개 + + + Key file directories + 키 파일 폴더 + + + Public key file base directory + 공개 키 화일 기본 폴더 + + + Private key file base directory + 개인 키 화일 기본 폴더 + + + Available authentication keys + 사용 가능한 인증키들 + + + Create key pair + 키 페어 생성 + + + Delete key + 키 삭제 + + + Import key + 키 불러오기 + + + Export key + 키 내보내기 + + + Set access group + 접근 그룹 설정 + + + Key files (*.pem) + 키 파일 (*.pem) + + + Authentication key name + 인증키 이름 + + + Please enter the name of the user group or role for which to create an authentication key pair: + 인증키 페어를 생성할 유저그룹의 이름 또는 임무를 입력하세요 + + + Do you really want to delete authentication key "%1/%2"? + 정말로 인증키 "%1/%2" 를 삭제하시겠습니까? + + + Please select a key to delete! + 삭제할 키를 선택하세요 + + + Please enter the name of the user group or role for which to import the authentication key: + 접근 키를 불러올 사용자 그룹이나 역할의 이름을 입력하세요: + + + Please select a key to export! + 내보낼 키를 선택하세요! + + + Please select a user group which to grant access to key "%1": + 키 "%1"에 대한 접근을 허용할 유저 그룹을 선택하세요: + + + Please select a key which to set the access group for! + 이 접근 그룹에 설정할 키를 선택하세요! + + + Please perform the following steps to set up key file authentication: + 키 파일 인증을 설정하기 위해 다음 단계들을 실행하세요: + + + 1) Create a key pair on the master computer. + 1) 마스터 컴퓨터에서 키페어를 생성하시오. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) 다른 컴퓨터에 접근을 허용할 멤버가 속한 그룹을 설정하세요. + + + 3) Export the public key and import it on all client computers with the same name. + 3) 공개키를 내보내고 그키를 같은 이름을 가진 모든 클라이언트 컴퓨터에서 읽어들이기. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + 더 자세한 정보는 <a href="https://veyon.readthedocs.io/en/latest/admin/index.html"> Veyon 관리자 매뉴얼을 참조하세요 </a> . + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + 인증 키는 두 부분으로 구성되어 있습니다, 공개 키 파트와 개인 키 파트. +개인 키를 사용하여 마스터 컴퓨터에서 클라이언트 컴퓨터에 접속할 수 있습니다. +오직 승인된 사용자만 개인 키 화일을 읽을 수 있도록 하는 것이 아주 중요합니다. +공개 키 파트는 클라이언트 컴퓨터에서 사용되며 들어오는 연결 요청이 허가된 것인지 검증하는데 사용됩니다. + + + + AuthKeysManager + + Please check your permissions. + 권한을 점검하시기 바랍니다. + + + Key name contains invalid characters! + 키 이름에 유효하지 않은 글자가 들어있습니다! + + + Invalid key type specified! Please specify "%1" or "%2". + 유효하지 않은 키 형식을 지정했습니다 ! "%1" 또는 "%2"를 지정하세요. + + + Specified key does not exist! Please use the "list" command to list all installed keys. + 선택한 키가 존재하지 않음! "list" 명령어를 이용해서 설치된 모든 키 리스트를 보세요. + + + One or more key files already exist! Please delete them using the "delete" command. + 하나 또는 그 이상의 키화일들이 이미 존재함! "delete" 명령어로 그 화일들을 삭제하세요. + + + Creating new key pair for "%1" + "%1"에대한 새로운 키페어를 생성하고 있습니다 + + + Failed to create public or private key! + 공개 또는 개인 키 생성 실패! + + + Newly created key pair has been saved to "%1" and "%2". + 새로 생성된 키 페어가 "%1" 과 "%2"로 저장되었습니다. + + + Could not remove key file "%1"! + "%1" 의 키 화일을 삭제할 수 없습니다! + + + Could not remove key file directory "%1"! + "%1" 의 키 화일 폴더를 삭제할 수 없습니다! + + + Failed to create directory for output file. + 출력 화일용 폴더를 생성할 수 없습니다. + + + File "%1" already exists. + 화일 "%1" 이 이미 존재합니다. + + + Failed to write output file. + 출력화일을 쓸수 없습니다. + + + Key "%1/%2" has been exported to "%3" successfully. + 키"%1" "%2"를 성공적으로 "%3" 로 내보냈습니다. + + + Failed read input file. + 입력 화일 읽기 실패. + + + File "%1" does not contain a valid private key! + 화일 "%1" 은 유효한 개인 키를 포함하고 있지 않습니다! + + + File "%1" does not contain a valid public key! + 화일 "%1" 은 유효한 공개키를 포함하고 있지 않습니다! + + + Failed to create directory for key file. + 키 화일용 폴더 생성 실패. + + + Failed to write key file "%1". + 키 화일 "%1"에 쓰는데 실패. + + + Failed to set permissions for key file "%1"! + 키 화일 "%1"의 권한설정에 실패했습니다! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + 키 "%1/%2" 를 성공적으로 불러왔습니다. 미승인 접근을 방지하기 위해 "%3" 의 화일 권한을 점검하세요. + + + Failed to convert private key to public key + 개인키를 공개키로 변환하기 실패. + + + Failed to create directory for private key file "%1". + 개인 키 화일용 폴더 생성 실패. "%1". + + + Failed to save private key in file "%1"! + 개인키를 화일 "%1"로 저장하는데 실패! + + + Failed to set permissions for private key file "%1"! + 개인 키 화일 "%1"의 권한설정에 실패했습니다! + + + Failed to create directory for public key file "%1". + 공개 키 화일용 폴더 생성 실패. "%1". + + + Failed to save public key in file "%1"! + 공개키를 화일 "%1"로 저장하는데 실패! + + + Failed to set permissions for public key file "%1"! + 공개 키 화일 "%1"의 권한설정에 실패했습니다! + + + Failed to set owner of key file "%1" to "%2". + 키 화일 "%1"의 소유자 권한을 "%2" 화일에 설정하는데 실패했습니다. + + + Failed to set permissions for key file "%1". + 키 화일 "%1"의 권한설정에 실패했습니다! + + + Key "%1" is now accessible by user group "%2". + 지금부터 사용자 그룹 "%2"는 키 "%1" 에 접근할 수 있습니다. + + + <N/A> + <N/A> + + + Failed to read key file. + 키 화일 읽기 실패. + + + + AuthKeysPlugin + + Create new authentication key pair + 새로운 인증키 페어 생성 + + + Delete authentication key + 인증키 삭제하게 + + + List authentication keys + 인증키 보여주기 + + + Import public or private key + 공개 또는 개인 접근 키 불러오기 + + + Export public or private key + 공개 또는 개인 접근 키 내보내기 + + + Extract public key from existing private key + 기존 개인키에서 공개키 뽑아내기 + + + Set user group allowed to access a key + 키에 접근을 허용할 유저 그룹을 설정하세요. + + + KEY + KEY + + + ACCESS GROUP + 접근 그룹 + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + 이 명령어는 <KEY> 에 대한 화일 접근 권한을 조정하여 유저 그룹 <ACCESS GROUP> 만 읽기 허가를 합니다. + + + NAME + 이름 + + + FILE + 화일 + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + 이 명려어는 인증키 <KEY> 를 <FILE>로 내 보냅니다. 만일 <FILE> 이 지정되지 않으면 <KEY>의 이름 과 형식을 이용해서 이름이 생성됩니다 . . + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + 이 명려어는 인증키 <KEY> 를 <FILE>에서 읽어 들입니다. 만일 <FILE> 이 지정되지 않으면 <KEY> 의 이름 과 형식을 이용해서 이름이 생성됩니다 . + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + 이 명령어는 설정된 키폴더에 있는 모든 인증키를 리스트해 줍니다. 만일 옵션 "%1"가 지정되면 키 상세 테이블이 대신 표시됩니다. 키가 접근 불가할 경우엔 일부 내용이 누락될 수 있습니다. 즉 e.g. 읽기 권한이 없는 경우등. + + + Please specify the command to display help for! + 도움말을 표시할 명령어를 지정하세요 + + + TYPE + TYPE + + + PAIR ID + 페어 ID + + + Command line support for managing authentication keys + 명령어 창에서 인증키 조작 지원 + + + Commands for managing authentication keys + 인증키 조작 명령어들 + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + 이 명령어는 이름 <NAME> 를 가진 새로운 인증 키 페어를 생성하고 개인키와 공개키를 지정된 키 폴더에 저장합니다. 파라메터는 반드시 글자만을 포함하는 키 이름이어야 함. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + 이 명령어는 키폴더에 설정된 인증키 <KEY> 를 삭제합니다. 이키를 삭제하면 다시 복구할 수 없습니다. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + 이 명령어는 개인키 <KEY> 를 통해서 공개키 파트를 추출하여 개인키에 대한 공개키로 저장합니다. 다른 마스터 컴퓨터를 설정할 경우, 개인기만 전송하는 것으로 충분합니다. 공개키는 이때 추출될수 있습니다. + + + + AuthKeysTableModel + + Name + 이름 + + + Type + 종류 + + + Access group + 접근 그룹 + + + Pair ID + 페어(짝) ID + + + + BuiltinDirectoryConfigurationPage + + Computers + 컴퓨터 + + + Name + 이름 + + + Host address/IP + 호스트주소/IP + + + MAC address + MAC주소 + + + Add new computer + 새 컴퓨터 추가 + + + Remove selected computer + 선택된 컴퓨터 삭제 + + + New computer + 새 컴퓨터 + + + Builtin directory + 게시물 폴더 + + + Locations & computers + 위치 및 컴퓨터들 + + + Locations + 위치 + + + Add new location + 새로운 위치 추가 + + + Remove selected location + 선택한 위치 삭제 + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + CSV 화일 불러오기는 커맨드라인 인터페이스로 가능합니다. 자세한 정보는, 다음 참조 <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">온라인 문서</a>. + + + New location + 새로운 위치 + + + + BuiltinDirectoryPlugin + + Show help for specific command + 특정 명령어에 대한 도움말 보여줌 + + + Import objects from given file + 지정된 화일에서 개체 가져오기 + + + Export objects to given file + 지정된 화일로 개체 내보내기 + + + Invalid type specified. Valid values are "%1" or "%2". + 잘못된 형식 지정됨. 유효한 값들은 "%1" 또는 "%2". + + + Type + 종류 + + + Name + 이름 + + + Host address + 호스트 주소 + + + MAC address + MAC주소 + + + Specified object not found. + 해당 개체를 찾을 수 없음 + + + File "%1" does not exist! + 화일 "%1" 이 없음! + + + Can't open file "%1" for reading! + 읽으려는 "%1" 파일을 열수 없음! + + + Unknown argument "%1". + 알수 없는 인자 "%1". + + + Computer "%1" (host address: "%2" MAC address: "%3") + 컴퓨터 "%1" (호스트 주소: "%2" MAC 주소: "%3") + + + Unclassified object "%1" with ID "%2" + 분류되지 않은 개체 "%1" 아이디 "%2" + + + None + 없음 + + + Computer + 컴퓨터 + + + Root + 루트 + + + Invalid + 무효 + + + Error while parsing line %1. + 라인 %1 을 파싱하는 중 오류. + + + Network object directory which stores objects in local configuration + 로컬 설정에 오브젝트를 저장하고 있는 네트워크 오브젝트 폴더 + + + Commands for managing the builtin network object directory + 내장 네트워크 오브젝트 폴더를 관리하는 명령어들 + + + No format string or regular expression specified! + 형식 문자열 또는 표현식이 지정되지 않음! + + + Can't open file "%1" for writing! + 쓰기 위한 "%1" 파일을 열수 없음! + + + No format string specified! + 형식 문자열이 지정되지 않음! + + + Object UUID + Object UUID + + + Parent UUID + 부모 UUID + + + Add a location or computer + 위치 또는 컴퓨터 추가 + + + Clear all locations and computers + 모든 위치및 컴퓨터 삭제 + + + Dump all or individual locations and computers + 모든 개별 위치및 컴퓨터 표시 + + + List all locations and computers + 모든 위치및 컴퓨터 리스트출력 + + + Remove a location or computer + 위치 또는 컴퓨터 삭제 + + + Location "%1" + 위치 "%1" + + + Builtin (computers and locations in local configuration) + 내장 (로컬 설정에 있는 컴퓨터 및 위치) + + + Location + 위치 + + + FILE + 화일 + + + LOCATION + 위치 + + + FORMAT-STRING-WITH-PLACEHOLDERS + FORMAT-STRING-WITH-PLACEHOLDERS + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + 하나 또는 여러개의 placeholders 를 포함하는 포맷지정자 또는 정규표현식을 사용하여 선택한 텍스트 화일에서 오브젝트를 가져옴. 유효한 placeholders 는 : %1 + + + Import simple CSV file to a single room + 간단한 CSV 화일을 싱글 룸에 불러오기 + + + Import CSV file with location name in first column + 위치 이름이 있는 CSV 화일을 첫번째 컬럼으로 불러오기 + + + Import text file with with key/value pairs using regular expressions + 정규표현식을 사용하여 key/value 짝을 가진 텍스트 화일 불러오기 + + + Import arbitrarily formatted data + 임의 형식의 데이터 불러오기 + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + 하나 또는 여러개의 placeholders 를 포함하는 포맷지정자를 사용하여 선택한 텍스트 화일로 오브젝트를 내보냄. 유효한 placeholders 는 : %1 + + + Export all objects to a CSV file + 모든 오브젝트를 CSV 화일로 내보내기 + + + Export all computers in a specific location to a CSV file + 특정 위치에 있는 모든 컴퓨터를 CSV 화일로 내보내기 + + + TYPE + TYPE + + + NAME + 이름 + + + PARENT + PARENT + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + 오브젝트 %1 가 "%2" 또는 "%3"를 추가함. %4 는 이름 또는 UUID로 지정할 수 있음. + + + Add a room + 룸 추가 + + + Add a computer to room %1 + 룸 %1 에 컴퓨터 추가하기 + + + OBJECT + OBJECT + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + 선택된 오브젝트를 디렉토리에서 제거함. %1 은 이름 또는 UUID로 지정할 수 있음. 위치를 삭제하면 관련된 모든 컴퓨터도 삭제함. + + + Remove a computer by name + 이름으로 컴퓨터 삭제하기 + + + Remove an object by UUID + UUID로 오브젝트 삭제하기 + + + "Room 01" + "룸 01" + + + "Computer 01" + "컴퓨터 01" + + + HOST ADDRESS + HOST ADDRESS + + + MAC ADDRESS + 맥 어드레스 + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Builtin VNC server (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Builtin VNC server (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + 호스트/IP 주소: %1 + + + Active features: %1 + 활성화된 기능들: %1 + + + Online and connected + 온라인 또는 연결됨 + + + Establishing connection + 연결중 + + + Computer offline or switched off + 컴퓨터 오프라인 또는 전원 꺼짐 + + + Authentication failed or access denied + 인증 실패 또는 접근 거부됨 + + + Disconnected + 연결 끊어짐 + + + No user logged on + 로그온된 사용자 없음 + + + Logged on user: %1 + 로그온된 사용자 : %1 + + + Location: %1 + 장소: %1 + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 서비스 %2 위치 %3:%4 + + + Authentication error + 인증 에러 + + + Remote access + 원격 접근 + + + User "%1" at host "%2" is now accessing this computer. + 호스트 "%2"의 사용자 "%1" 가 이 컴퓨터에 연결합니다. + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + 호스트 "%2"의 사용자 "%1" 가 이 컴퓨터에 연결하려고 하였으나 인증에 성공하지 못했습니다. + + + Access control error + 접근 제어 오류 + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + 호스트 "%2"의 사용자 "%1" 가 이 컴퓨터에 연결하려고 하였으나 접근제어 설정에 의해 블럭되었습니다. + + + Active connections: + + + + + ComputerManager + + User + 사용자 + + + Missing network object directory plugin + 네트워크 개체 디렉토리 플러그인이 없음 + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + 기본 네트워크 개체 폴더 플러그인 없음. 설치 상태를 점검하거나 또는 %1 설정을 이용하여 다른 네트워크 개체 폴더 백엔드를 설정하세요. + + + Location detection failed + 위치 탐색 실패 + + + Computer name;Hostname;User + 컴퓨터 이름;호스트 이름;사용자 + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + 이 컴퓨터의 위치를 찾을 수 없음. 시스템 설정에 문제가 있어 보입니다. 대신 모든 위치는 컴퓨터 선정 패널에 표시됩니다. + + + + ComputerSelectPanel + + Computer search + 컴퓨터 검색 + + + Add location + 위치 추가 + + + Save computer/user list + 컴퓨터/사용자 리스트 저장 + + + Select output filename + 출력 화일이름 선택 + + + CSV files (*.csv) + CSV files (*.csv) + + + File error + 화일 에러 + + + Could not write the computer and users list to %1! Please check the file access permissions. + 컴퓨터와 사용자 리스트를 %1에 저장하지 못함. 접근 권한을 확인하세요. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + 읽어 올 기존 설정 화일을 선택하세요. + + + Please specify a valid filename for the configuration export. + 설정을 내보낼 유효한 화일 이름을 지정하세요. + + + Please specify a valid key. + 유효한 키를 선택하세요. + + + Specified key does not exist in current configuration! + 현재 설정에 지정된 키가 존재하지 않습니다! + + + Please specify a valid value. + 유효한 값을 지정하세요 + + + Configure Veyon at command line + 커맨드 라인에서 Veyon 설정 + + + Output file is not writable! + 출력 화일에 쓸수 없습니다! + + + Output directory is not writable! + 출력 폴더에 저장할 수 없습니다! + + + Configuration file is not readable! + 설정화일을 읽을 수 없습니다! + + + Clear system-wide Veyon configuration + 시스템 상의 Veyon 설정 삭제 + + + List all configuration keys and values + 설정된 모든 키 및 값 출력 + + + Import configuration from given file + 지정된 화일에서 설정 가져오기 + + + Export configuration to given file + 지정된 화일로 설정 내보내기 + + + Read and output configuration value for given key + 해당 키에 대한 설정을 읽고 출력함 + + + Write given value to given configuration key + 입력된 값을 해당 설정 키에 쓰기 + + + Unset (remove) given configuration key + 해당 설정 키를 삭제(선택취소)함 + + + Commands for managing the configuration of Veyon + Veyon 설정 관리 명령어들 + + + Upgrade and save configuration of program and plugins + 프로그램과 플러그인의 설정을 업그레이드하고 저장 + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + %1 서비스에 대한 자동 시작 속성을 수정할 수 없습니다. + + + Could not configure the firewall configuration for the %1 Server. + %1 서버용 방화벽 설정을 변경할 수 없습니다. + + + Could not configure the firewall configuration for the %1 Worker. + %1 워커용 방화벽 설정을 변경할 수 없습니다. + + + Configuration is not writable. Please check your permissions! + 설정을 쓸수 없습니다. 권한을 점검하기 바랍니다! + + + Could not apply platform-specific configuration settings. + 플랫폼 전용 설정 세팅을 적용할 수 없습니다. + + + + DemoClient + + %1 Demo + %1 데모 + + + + DemoConfigurationPage + + Demo server + 데모 서버 + + + Tunables + 조정할수 있는 항목들 + + + ms + ms + + + Key frame interval + 키 프레임 인터벌 + + + Memory limit + 메모리 한계 + + + MB + MB + + + Update interval + 업데이트 간격 + + + s + s + + + Slow down thumbnail updates while demo is running + 데모가 실행중일 때 썸네일 업데이트 속도를 늦춤 + + + + DemoFeaturePlugin + + Stop demo + 데모 중지 + + + Window demo + 윈도우 데모 + + + Give a demonstration by screen broadcasting + 화면 전송으로 데모를 보여 줍니다. + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + 이 모드에서는 교사의 화면이 하나의 윈도우 창 으로 모든 컴퓨터에 표시됩니다. 필요할 경우 사용자들은 다른 윈도우 창으로 전환해서 자신의 작업을 계속 할 수 있습니다. + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + 데스크탑 접근 다이얼로그 + + + Confirm desktop access + 컴퓨터 접속 허가 + + + Never for this session + 이번 세션에서는 허락하지 않음 + + + Always for this session + 이번 세션에서는 항상 허락함 + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + 컴퓨터 %2의 사용자 %1 이 당신의 데스크탑에 접속을 원합니다. 접속을 허가 할까요 ? + + + + DesktopServicesConfigurationPage + + Programs & websites + 프로그램과 웹사이트들 + + + Predefined programs + 미리 지정된 프로그램들 + + + Name + 이름 + + + Path + 경로 + + + Add new program + 새 프로그램 추가 + + + Remove selected program + 선택한 프로그램 삭제 + + + Predefined websites + 미리 지정된 웹사이트들 + + + Remove selected website + 선택한 웹사이트 삭제 + + + URL + URL + + + New program + 새 프로그램 + + + New website + 새 웹사이트 + + + + DesktopServicesFeaturePlugin + + Run program + 프로그램 실행 + + + Open website + 웹사이트 열기 + + + Click this button to open a website on all computers. + 클릭하면 모든 컴퓨터에서 같은 웹사이트를 오픈합니다. + + + Start programs and services in user desktop + 사용자 데스크탑에서 프로그램과 서비스를 시작 + + + Click this button to run a program on all computers. + 클릭하면 모든 컴퓨터에서 프로그램을 실행합니다. + + + Run program "%1" + 프로그램 "%1" 실행 + + + Custom program + 사용자 프로그램 + + + Open website "%1" + 웹사이트 "%1" 열기 + + + Custom website + 사용자 지정 웹사이트 + + + + DocumentationFigureCreator + + Teacher + 교사 + + + Room %1 + 방 %1 + + + Please complete all tasks within the next 5 minutes. + 5분내로 모든 작업을 완료하시기 바랍니다. + + + Custom website + 사용자 지정 웹사이트 + + + Open file manager + 파일 관리자 열기 + + + Start learning tool + 학습툴 시작 + + + Play tutorial video + 튜토리얼 동영상 재생 + + + Custom program + 사용자 프로그램 + + + Handout + 자료 배포 핸드아웃 + + + Texts to read + 읽을 글자들 + + + generic-student-user + 일반-학생-사용자 + + + + ExternalVncServer + + External VNC server + 외부 VNC 서버 + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + 외부 VNC 서버 설정 + + + Port: + 포트: + + + Password: + 패스워드: + + + + FeatureControl + + Feature control + 기능제어 + + + + FileTransferConfigurationPage + + File transfer + 화일 전송 + + + Directories + 디렉토리 + + + Destination directory + + + + Default source directory + + + + Options + 옵션 + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + 화일 "%1"을 읽을 수 없습니다! 권한을 점검하기 바랍니다! + + + + FileTransferDialog + + File transfer + 화일 전송 + + + Options + 옵션 + + + Transfer only + 전송만 함 + + + Transfer and open file(s) with associated program + 전송한 후 화일과 연결되어 있는 프로그램으로 화일(들) 열기 + + + Transfer and open destination folder + 전송하고 도착 폴더를 열기 + + + Files + 화일 + + + Start + 시작 + + + Overwrite existing files + 기존 화일을 덮어 씀 + + + + FileTransferPlugin + + File transfer + 화일 전송 + + + Click this button to transfer files from your computer to all computers. + 다른 모든 컴퓨터로 화일을 전송하려면 이버튼을 클릭하세요 + + + Select one or more files to transfer + 전송하려는 하나 또는 여러개의 화일을 선택하세요 + + + Transfer files to remote computer + 원격 컴퓨터로 화일 전송 + + + Received file "%1". + 받은 화일 "%1". + + + Could not receive file "%1" as it already exists. + 화일 "%1" 이 이미 존재하므로 덮어 쓸 수 없습니다. + + + Could not receive file "%1" as it could not be opened for writing! + 화일 "%1" 이 쓸수 있도록 열리지 않아 화일을 받을 수 없습니다! + + + + GeneralConfigurationPage + + User interface + 유저 인터페이스 + + + Language: + 언어: + + + Use system language setting + 시스템 언어 설정 사용 + + + Veyon + Veyon + + + Logging + 로깅중 + + + Log file directory + 로그 화일 폴더 + + + Log level + 로그 수준 + + + Nothing + 로그 남기지 않음 + + + Only critical messages + 심각한 에러 메시지만 남김 + + + Errors and critical messages + 에러와 심각한 에러 메시지만 남김 + + + Warnings and errors + 경고와 에러 메시지 남김 + + + Information, warnings and errors + 정보, 경고와 에러 메시지 남김 + + + Debug messages and everything else + 디버그 메시지와 기타 모든 메시지 남김 + + + Limit log file size + 로그 파일 최대 크기 + + + Clear all log files + 모든 로그 파일 지우기 + + + Log to standard error output + 표준 에러 출력장치에 기록 + + + Network object directory + 네트워크 개체 폴더 + + + Backend: + 백엔드: + + + Update interval: + 화면업데이트 간격: + + + %1 service + %1 서비스 + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + 로그 화일을 삭제하기 위해 %1 서비스를 잠시 중지합니다. 계속할까요 ? + + + Log files cleared + 로그화일 삭제 + + + All log files were cleared successfully. + 모든 로그화일 삭제 성공. + + + Error + 에러 + + + Could not remove all log files. + 모든 로그화일을 삭제하지 못함. + + + MB + MB + + + Rotate log files + 로그화일 회전 + + + x + x + + + seconds + 초 + + + Write to logging system of operating system + 운영체계의 로깅시스템에 기록 + + + Authentication + 인증 + + + Method: + 방법: + + + Logon authentication + 로그온 인증 + + + Key file authentication + 키 화일 인증 + + + Test + 테스트 + + + Authentication is set up properly on this computer. + 이 컴퓨터에서 인증이 적절히 설정되었습니다. + + + Authentication keys are not set up properly on this computer. + 이 컴퓨터에서 인증키가 적절히 설정되지 않았습니다. + + + Authentication test + 인증 테스트 + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + LDAP 찾아보기 + + + + LdapClient + + LDAP error description: %1 + LDAP 에러 설명: %1 + + + + LdapConfigurationPage + + Basic settings + 기본 설정 + + + General + 일반사항 + + + LDAP server and port + LDAP 서버 및 포트 + + + Bind DN + DN 결합 + + + Bind password + 패스워드 결합 + + + Anonymous bind + 익명으로 결합 + + + Use bind credentials + 인증서 결합 사용 + + + Base DN + 기본 DN + + + Fixed base DN + 고정된 기본 DN + + + e.g. dc=example,dc=org + 예. dc=example,dc=org + + + Discover base DN by naming context + 이름으로 기본 DN 탐색 + + + e.g. namingContexts or defaultNamingContext + 예. namingContexts or defaultNamingContext + + + Environment settings + 환경설정 + + + Object trees + 개체 트리 + + + Computer tree + 컴퓨터 트리 + + + e.g. OU=Groups + 예. OU=Groups + + + User tree + 사용자 트리 + + + e.g. OU=Users + 예. OU=Users + + + e.g. OU=Computers + 예. OU=Computers + + + Group tree + 그룹 트리 + + + Perform recursive search operations in object trees + 개체 트리에서 재귀적 검색작업 수행 + + + Object attributes + 개체 특성 + + + e.g. hwAddress + 예. hwAddress + + + e.g. member or memberUid + 예. member or memberUid + + + e.g. dNSHostName + 예. dNSHostName + + + Computer MAC address attribute + 컴퓨터 MAC 주소 속성 + + + Group member attribute + 그룹멤버 속성 + + + e.g. uid or sAMAccountName + 예. uid or sAMAccountName + + + Advanced settings + 고급 설정 + + + Optional object filters + 선택적 개체필터 + + + Filter for user groups + 유저그룹 필터 + + + Filter for users + 유저필터 + + + Filter for computer groups + 컴퓨터 그룹 필터 + + + Group member identification + 그룹멤버 ID + + + Distinguished name (Samba/AD) + 식별이름 (Samba/AD) + + + List all groups of a user + 모든 유저그룹 표시 + + + List all groups of a computer + 모든 컴퓨터 그룹 표시 + + + Get computer object by IP address + IP주소로 컴퓨터 개체 가져오기 + + + LDAP connection failed + LDAP 연결실패 + + + LDAP bind failed + LDAP 결합실패 + + + LDAP bind successful + LDAP 결합 성공 + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + LDAP 서버 연결및 바인드에 성공했습니다. 기본 LDAP 설정이 올바르게 끝났습니다. + + + LDAP base DN test failed + LDAP 기본 DN 시험 실패 + + + LDAP base DN test successful + LDAP 기본 DN 시험 성공 + + + LDAP naming context test failed + LDAP naming context 시험실패 + + + LDAP naming context test successful + LDAP naming context 시험 성공 + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + LDAP 명칭 내용이 성공적으로 조회되었습니다. 기본 DN 이 발견되었습니다: +%1 + + + user tree + 사용자 트리 + + + group tree + 그룹 트리 + + + computer tree + 컴퓨터 트리 + + + Enter username + 사용자 이름 입력 + + + Please enter a user login name (wildcards allowed) which to query: + 검색할 유저로그인 이름을 입력하세요 (와일드카드 허용됨): + + + user objects + 사용자 개체 + + + Enter group name + 그룹이름 입력 + + + Please enter a group name whose members to query: + 검색할 그룹 이름을 입력하세요: + + + group members + 그룹 멤버 + + + Group not found + 그룹 검색되지 않음 + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + 이름 "%1" 그룹 검색되지 않음. 그룹이름 또는 그룹 트리 파라메터를 확인하세요. + + + Enter computer name + 그룹이름 입력 + + + computer objects + 컴퓨터 개체 + + + Enter computer DN + 컴퓨터 DN 입력 + + + Please enter the DN of a computer whose MAC address to query: + MAC 주소를 조회할 컴퓨터의 DN을 입력하세요 + + + computer MAC addresses + 컴퓨터 MAC 주소 + + + users + 사용자 + + + user groups + 사용자 그룹 + + + computer groups + 컴퓨터 그룹 + + + Please enter a user login name whose group memberships to query: + 그룹 멤버 속성을 검색할 사용자 로그인 이름을 입력하세요 : + + + groups of user + 사용자 그룹 + + + User not found + 사용자 검색되지 않음 + + + groups of computer + 컴퓨터 그룹 + + + Computer not found + 컴퓨터 발견되지 않음 + + + Enter computer IP address + IP 주소를 입력하세요 + + + Please enter a computer IP address which to resolve to an computer object: + 컴퓨터 개체로 변환할 컴퓨터 IP 주소를 입력하세요 + + + computers + 컴퓨터 + + + LDAP %1 test failed + LDAP %1 시험 실패 + + + LDAP %1 test successful + LDAP %1 시험 성공 + + + The %1 has been queried successfully and %2 entries were found. + %1 이 성공적으로 조회되고 %2 입력 값이 검색됨 + + + %1 %2 have been queried successfully: + +%3 + %1 %2 가 성공적으로 조회됨 + +%3 + + + LDAP filter test failed + LDAP 필터 시험 실패 + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + 설정된 필터를 사용하여 어떤 %1도 조회할 수 없음. %1에 대한 LDAP 필터를 점검하세요 + +%2 + + + LDAP filter test successful + LDAP 필터 시험 성공 + + + %1 %2 have been queried successfully using the configured filter. + 컴퓨터 필터를 사용한 %1 %2 검색 조회 성공 + + + (only if different from group tree) + (그룹 트리가 다를 경우에만) + + + Computer group tree + 컴퓨터 그룹 트리 + + + computer group tree + 컴퓨터 그룹 트리 + + + Filter for computers + 컴퓨터 필터 + + + e.g. room or computerLab + 예. room or computerLab + + + Integration tests + 결합 테스트 + + + Computer groups + 컴퓨터 그룹 + + + e.g. name or description + 예. 이름 또는 설명 + + + Filter for computer containers + 컴퓨터 컨테이너 필터 + + + Computer containers or OUs + 컴퓨터 컨테이너 또는 OU + + + Connection security + 연결 보안 + + + TLS certificate verification + TLS 인증 검증 + + + System defaults + 시스템 디폴트 + + + Never (insecure!) + 거부(보안 안됨) + + + Custom CA certificate file + 사용자 CA 인증 화일 + + + None + 없음 + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + e.g. (objectClass=computer) + + + e.g. (objectClass=group) + e.g. (objectClass=group) + + + e.g. (objectClass=person) + 예. (objectClass=person) + + + e.g. (objectClass=room) or (objectClass=computerLab) + e.g. (objectClass=room) or (objectClass=computerLab) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + 설정된 베이스 DN을 찾을 수 없습니다. 베이스 DN 파라메터를 점검하세요. + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + LDAP 베이스 DN 조회 성공. 다음 입력항목이 발견되었습니다: +%1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + 명칭 내용을 통해 베이스 DN 조회를 할 수 없습니다. 명칭 속성 파라메터를 점검하세요. +%1 + + + Certificate files (*.pem) + 인증화일 (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + LDAP 서버에 연결할 수 없음. 서버 파라메터를 점검하세요. + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + LDAP서버와 결합 불가. 결합인자와 서버 파라메터를 점검하세요. + +%1 + + + Encryption protocol + 암호화 프로토콜 + + + Computer location attribute + 컴퓨터 위치 및 속성 + + + Computer display name attribute + 컴퓨터 표시 이름 속성 + + + Location name attribute + 위치 이름 및 속성 + + + e.g. cn or displayName + 예, cn 또는 displayName + + + Computer locations identification + 컴퓨터 위치 ID + + + Identify computer locations (e.g. rooms) via: + 를 통해서 컴퓨터 위치 확인하기( 예, rooms) : + + + Location attribute in computer objects + 컴퓨터 오브젝트에서의 위치 속성 + + + List all entries of a location + 모든 위치의 엔트리 출력 + + + List all locations + 모든 위치 출력 + + + Enter computer display name + 컴퓨터 표시 이름 입력 + + + Please enter a computer display name to query: + 검색할 컴퓨터 디스플레이 이름을 입력하세요: + + + Enter computer location name + 컴퓨터 위치 이름 입력 + + + Please enter the name of a computer location (wildcards allowed): + 컴퓨터 위치의 이름을 입력하세요(와일드카드 허용됨): + + + computer locations + 컴퓨터 위치 + + + Enter location name + 위치 이름 입력 + + + Please enter the name of a location whose entries to query: + 조회할 위치 이름을 넣으세요: + + + location entries + 위치 엔트리 + + + LDAP test failed + LDAP 테스트 실패 + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + %1을 조회할 수 없음. %2 파라메터(들)을 점검하거나 또는 기존의 개체의 이름을 입력하세요 + +%3 + + + and + 및 + + + LDAP test successful + LDAP 테스트 성공 + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + 설정된 %1에서 엔트리 값을 찾지 못함. "%2" 파라메터를 점검하세요. + +%3 + + + Browse + 찾아보기 + + + Test + 테스트 + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + 호스트 이름이 완전히 검증된 도메인 이름으로 저장되었음 (FQDN, 예 myhost.example.org) + + + Computer hostname attribute + 컴퓨터 호스트 이름 속성 + + + Please enter a computer hostname to query: + 검색할 컴퓨터 호스트 이름을 입력하세요: + + + Invalid hostname + 잘못된 호스트 이름 + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + 컴퓨터 호스트 이름을 완전히 인증된 도메인 이름으로 저장되도록 설정했으나 도메인(FQDN) 이 입력되지 않았음. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + 컴퓨터 호스트 이름을 도메인 없는 단순 호스트 이름으로 저장되도록 설정하였으나 호스트 이름에 도메인 이름이 입력되있습니다. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + 사용자 이름 "%1" 검색되지 않음. 사용자 이름 또는 사용자 트리 파라메터를 확인하세요. + + + Enter hostname + 호스트 이름을 입력하세요 + + + Please enter a computer hostname whose group memberships to query: + 멤버 속성을 검색할 컴퓨터 호스트 이름을 입력하세요: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + 호스트 이름 "%1"인 컴퓨터 가 검색되지 않음. 호스트 이름 또는 컴퓨터 트리 파라메터를 확인하세요. + + + Hostname lookup failed + 호스트이름 검색 실패 + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + IP %1 에 대한 호스트 이름 검색 실패. DNS 서버 설정을 확인하세요 + + + User login name attribute + 사용자 로긴이름 속성 + + + Configured attribute for user login name or computer hostname (OpenLDAP) + 사용자 로그인 또는 컴퓨터 호스트 이름 (OpenLDAP)의 속성 설정됨 + + + computer containers + 컴퓨터 컨테이너들 + + + + LdapPlugin + + Auto-configure the base DN via naming context + 기본 DN 을 명칭 기반으로 자동 설정함 + + + Query objects from LDAP directory + 개체를 LADP 폴더에서 조회 + + + Show help about command + 명령어에 대한 도움말 보여줌 + + + Commands for configuring and testing LDAP/AD integration + LDAP/AD 통합을 설정하거나 제어하는 명령어 + + + Basic LDAP/AD support for Veyon + Veyon 기본 LDAP/AD 지원 + + + %1 (load computers and locations from LDAP/AD) + %1 (LDAP/AD에서 컴퓨터와 위치를 가져옴) + + + %1 (load users and groups from LDAP/AD) + %1 (LDAP/AD에서 사용자와 그룹을 가져옴) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + 다음 형식에 맞는 LDAP url을 입력하세요 "ldap[s]://[user[:password]@]hostname[:port]" + + + No naming context attribute name given - falling back to configured value. + 네이밍 컨텍스트 속성의 이름이 없습니다 - 이전에 설정된 값으로 되돌립니다 + + + Could not query base DN. Please check your LDAP configuration. + 베이스 DN을 조회할 수 없습니다. LDAP 설정을 확인하세요. + + + Configuring %1 as base DN and disabling naming context queries. + %1 을 기본 DN 으로 설정하고 네이밍 컨텍스트 조회를 비활성화 함. + + + + LinuxPlatformConfigurationPage + + Linux + 리눅스 + + + Custom PAM service for user authentication + 사용자 인증용 커스텀 PAM 서비스 + + + User authentication + 사용자 인증 + + + Session management + 세션 관리 + + + Display manager users + 매니저 유저를 표시 + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + 리눅스 플래폼용 플러그인 실행용 추상화 함수 + + + + LocationDialog + + Select location + 위치 선택 + + + enter search filter... + 검색 필터 입력 .... + + + + MainToolBar + + Configuration + 설정 + + + Disable balloon tooltips + 풍선 툴팁기능 해제 + + + Show icons only + 아이콘만 보여주기 + + + + MainWindow + + MainWindow + 메인 윈도우 + + + toolBar + 툴바 + + + General + 일반사항 + + + &File + 파일(&F) + + + &Help + &Help + + + &Quit + &Quit + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + &o 화일에서 설정 불러오기 + + + Ctrl+O + Ctrl+O + + + About Qt + Qt 정보 + + + Authentication impossible + 인증 불가 + + + Configuration not writable + 설정 저장 불가 + + + Load settings from file + 화일에서 설정 불러오기 + + + Save settings to file + 화일에 설정 저장 + + + Unsaved settings + 저장되지 않은 설정 + + + There are unsaved settings. Quit anyway? + 저장되지 않은 설정이 있습니다.그래도 끝낼까요 ? + + + Veyon Configurator + Veyon Configurator + + + Service + 서비스 + + + Master + 마스터 + + + Access control + 접근 제어 + + + About Veyon + Veyon에 대해서 + + + Auto + 자동 + + + About + 정보 + + + %1 Configurator %2 + %1 %2 설정 + + + JSON files (*.json) + JSON files (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + 설정을 저장할 수 없다는 로컬 설정 백엔드가 보고됨! %1 Configurator 를 관리자 권한으로 실행하세요. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + 인증 키 화일이 없거나 현재의 키 화일이 오래되었습니다. %1 Configurator 를 이용하여 새로운 키를 생성하세요. 다른 방법으로는 %1 Configurator 를 사용하여 로그온 인증을 설치하세요. 그렇지 않으면 %1을 사용하여 컴퓨터를 접속할 수 없습니다 + + + Access denied + 접근 거부됨 + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + 로컬 설정에 따라 당신은 네트워크상의 컴퓨터에 접근이 거부됩니다. 다른 사용자 이름으로 다시 로그인 하거나 시스템 관리자에게 로컬 설정 확인을 요청하세요 + + + Screenshots + 화면캡쳐 + + + Feature active + 기능 활성화 + + + The feature "%1" is still active. Please stop it before closing %2. + 기능 "%1" 이 현재 활성화 상태입니다. %2를 닫기 전에 중지시키세요 + + + Reset configuration + 설정 초기화 + + + Do you really want to reset the local configuration and revert all settings to their defaults? + 정말 로컬설정을 초기화하고 기본 설정값으로 되돌리시겠습니까? + + + Search users and computers + 사용자 또는 컴퓨터 검색 + + + Align computers to grid + 컴퓨터를 그리드에 맞춤 + + + %1 Configurator + %1 설정자 + + + Insufficient privileges + 권한 부족 + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + 관리자 권한으로 시작하지 못했습니다. 데스크탑 환경에 슈퍼유저 프로그램이 설치되었는지 확인하세요. 이 프로그램은 일반 유저 권한으로 실행됩니다. + + + Only show powered on computers + 전원이 켜진 컴퓨터만 보여주기 + + + &Save settings to file + &설정을 화일에 저장 + + + &View + &보기 + + + &Standard + &Standard 표준 + + + &Advanced + &Advanced 고급 + + + Use custom computer arrangement + 사용자가 정의한 컴퓨터 배치를 사용 + + + Locations && computers + 위치 && 컴퓨터 + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + 디렉토리 + + + User configuration + 사용자 설정 + + + Feature on computer double click: + 컴퓨터를 더블 클릭할 때의 기능 + + + Features + 기능 + + + All features + 모든 기능 항목 + + + Disabled features + 기능 비활성화 + + + Screenshots + 화면캡쳐 + + + <no feature> + <no feature> + + + Basic settings + 기본 설정 + + + Behaviour + 수행형태 + + + Enforce selected mode for client computers + 클라이언트 컴퓨터를 선택된 모드로 강제로 실행 + + + Hide local computer + 로컬컴퓨터 숨김 + + + Hide computer filter field + 컴퓨터 필터 필드 숨김 + + + Actions such as rebooting or powering down computers + 리부팅 또는 컴퓨터 파워 끄기 + + + User interface + 유저 인터페이스 + + + Background color + 백그라운드 색상 + + + Thumbnail update interval + 썸네일 그림 업데이트 시간간격 + + + ms + ms + + + Program start + 프로그램 시작 + + + Modes and features + 모드와 기능들 + + + User and computer name + 사용자 와 컴퓨터 이름 + + + Only user name + 전용 사용자 이름 + + + Only computer name + 전용 컴퓨터 이름 + + + Computer thumbnail caption + 컴퓨터 썸네일 캡션 + + + Text color + 글자색 + + + Sort order + 정렬 순서 + + + Computer and user name + 켬퓨터 및 사용자 이름 + + + Computer locations + 컴퓨터 위치 + + + Show current location only + 현재 위치만 표시 + + + Allow adding hidden locations manually + 수동으로 숨겨진 위치 추가를 허용 + + + Hide empty locations + 빈 위치 숨김 + + + Show confirmation dialog for potentially unsafe actions + 안전하지 않은 작동에 대한 확인 대화 창 보여주기 + + + Perform access control + 접근제어 실행 + + + Automatically select current location + 자동으로 현재 위치를 선택 + + + Automatically open computer select panel + 컴퓨터 선택 패너을 자동으로 열기 + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + 자동 + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + 모니터링 + + + Builtin monitoring mode + 게시판형 모니터링 모드 + + + This mode allows you to monitor all computers at one or more locations. + 이모드는 하나 또는 많은 위치에 있는 모든 컴퓨터들의 모니터를 가능하게 합니다. + + + + NetworkObjectTreeModel + + Locations/Computers + 위치/컴퓨터들 + + + + OpenWebsiteDialog + + Open website + 웹사이트 열기 + + + e.g. Veyon + 예: Veyon + + + Remember and add to website menu + 기억하고 웹 사이트 메뉴에 추가하기 + + + e.g. www.veyon.io + 예: www.veyon.io + + + Please enter the URL of the website to open: + 열어 볼 사이트의 주소 URL 을 입력하세요: + + + Name: + 이름: + + + + PasswordDialog + + Username + 사용자 이름 + + + Password + 암호 + + + Veyon Logon + Veyon 로그온 + + + Authentication error + 인증 에러 + + + Logon failed with given username and password. Please try again! + 입력한 사용자이름과 패스워드로 로그온 실패. 다시 시도하세요! + + + Please enter your username and password in order to access computers. + 컴퓨터에 접근 하려면 당신의 사용자이름과 패스워드를 입력하세요 + + + + PowerControlFeaturePlugin + + Power on + 전원켜기 + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + 이버튼 눌러서 모든 컴퓨터의 파워를 켭니다. 따라서 수동으로 한대씩 전원을 켤 필요가 없습니다 + + + Reboot + 재시작 + + + Click this button to reboot all computers. + 클릭하면 모든 컴퓨터를 리부팅. + + + Power down + 전원끄기 + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + 이버튼 눌러서 모든 컴퓨터의 파워를 끕니다. 따라서 수동으로 한대씩 전원을 끌 필요가 없습니다 + + + Power on/down or reboot a computer + 선택된 컴퓨터 파워 온/오프 또는 재시작 + + + Confirm reboot + 리부팅 확인 + + + Confirm power down + 컴퓨터 파워 끄기 확인 + + + Do you really want to reboot the selected computers? + 선택된 컴퓨터를 리부팅하시겠습니까 ? + + + Do you really want to power down the selected computer? + 선택된 컴퓨터 파워를 끄시겠습니까 ? + + + Power on a computer via Wake-on-LAN (WOL) + Wake-on-LAN(WOL)을 사용하여 컴퓨터 파워를 켬 + + + MAC ADDRESS + 맥 어드레스 + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + 이 명령어는 주어진 MAC 주소를 갖는 컴퓨터 파워를 켜기 위해 네트워크에 Wake-on-LAN(WOL) 패킷을 배포함. + + + Please specify the command to display help for! + 도움말을 표시할 명령어를 지정하세요 + + + Invalid MAC address specified! + 유효하지 않은 MAC 주소가 선택됨! + + + Commands for controlling power status of computers + 컴퓨터 파워 상태를 제어하기 위한 명령어들 + + + Power down now + 지금 전원 끄기 + + + Install updates and power down + 업데이트를 설치하고 전원 끄기 + + + Power down after user confirmation + 사용자 확인 후 전원 끄기 + + + Power down after timeout + 시간이 다 되면 전원 끄기 + + + The computer was remotely requested to power down. Do you want to power down the computer now? + 이 컴퓨터는 원격으로 종료를 요청했습니다. 컴퓨터를 지금 끄시겠습니까? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + 이컴퓨터는 %1 분, %2 초 후에 전원이 꺼집니다. + +작업을 저장하고 모든 프로그램을 닫기 바랍니다. + + + + PowerDownTimeInputDialog + + Power down + 전원끄기 + + + Please specify a timeout for powering down the selected computers: + 선택된 컴퓨터의 전원을 끝때까지의 시간을 입력하세요. + + + minutes + 분 + + + seconds + 초 + + + + RemoteAccessFeaturePlugin + + Remote view + 원격 화면 보기 + + + Open a remote view for a computer without interaction. + 상호 작용없이 원격 컴퓨터의 화면을 열기 + + + Remote control + 원격제어 + + + Open a remote control window for a computer. + 컴퓨터의 원격제어 윈도우를 열기 + + + Remote access + 원격 접근 + + + Remote view or control a computer + 감시화면 또는 컴퓨터 제어 + + + Please enter the hostname or IP address of the computer to access: + 연결할 컴퓨터의 호스트 이름 또는 IP를 입력하세요: + + + Show help about command + 명령어에 대한 도움말 보여줌 + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 - %2 원격 접근 + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + 보기만 하기 + + + Remote control + 원격제어 + + + Send shortcut + 바로가기 보내기 + + + Fullscreen + 전체화면 + + + Window + 윈도우 + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + 메뉴 + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + %1에 연결중 + + + Connected. + 연결됨 + + + Screenshot + 화면캡쳐 + + + Exit + 나가기 + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + 선택된 컴퓨터(들)에서 실행할 명령어 또는 프로그램을 입력하세요. 각각의 라인으로 다수의 프로그램/명령어를 구분할 수 있습니다. + + + Run programs + 프로그램 실행 + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + 예. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + 이름: + + + Remember and add to program menu + 기억하고 프로그램 메뉴에 추가하기 + + + e.g. VLC + 예: VLC + + + + ScreenLockFeaturePlugin + + Lock + 잠금 + + + Unlock + 잠금해제 + + + Lock screen and input devices of a computer + 컴퓨터의 입력장치와 화면을 잠금 + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + 사용자의 주의를 끌기 위해서 이버튼을 사용하여 사용자의 컴퓨터를 잠글수 있습니다. 이 모드에서 모든 입력 장치는 잠기며 화면은 검은색이 됩니다. + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + 알수없음 + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + 폴더 %1이 존재하지 않고 생성할 수 없기 때문에 화면캡쳐는 불가능합니다. + + + Screenshot + 화면캡쳐 + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + 화면캡쳐 + + + Use this function to take a screenshot of selected computers. + 선택된 컴퓨터의 화면을 캡쳐할 때 이기능을 사용합니다 + + + Screenshots taken + 화면 캡쳐됨 + + + Screenshot of %1 computer have been taken successfully. + %1 컴퓨터의 화면캡쳐 성공 + + + Take screenshots of computers and save them locally. + 컴퓨터의 화면을 캡쳐하고 로컬에 저장함 + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + 모든 화면 캡쳐 화일이 이곳에 표시됩니다. 컴퓨터 메뉴에서 "화면캡쳐"를 클릭하여 화면캡쳐를 할 수 있습니다. 아래 버튼으로 화면캡쳐를 관리할 수 있습니다. + + + User: + 사용자: + + + Computer: + 컴퓨터: + + + Date: + 날짜: + + + Time: + 시간: + + + Show + 보기 + + + Delete + 삭제 + + + Screenshot + 화면캡쳐 + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + 일반사항 + + + Autostart + 자동시작 + + + Hide tray icon + 트레이 아이콘 숨기기 + + + Start service + 서비스 시작 + + + Stopped + 멈춤 + + + Stop service + 서비스 중지 + + + State: + 상태: + + + Enable firewall exception + 방화벽 예외 적용 활성화 + + + Allow connections from localhost only + 로컬 호스트 연결만 허용함 + + + VNC server + VNC server + + + Plugin: + 플러그인: + + + Restart %1 Service + %1 서비스 재시작 + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + 모든 설정이 성공적으로 저장됨. %1 서비스가 작동하려면 재시작해야 합니다. 지금 재시작 할까요 ? + + + Running + 실행중 + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + 이 옵션을 활성화 시키면 서비스가 컴퓨터의 모든 대화 세션용 서버 프로세스를 실행합니다. +일반적으로 터미널 서버를 지원하는데 필요합니다. + + + Show notification on remote connection + 원격 연결이면 알림 보이기 + + + Show notification when an unauthorized access is blocked + 허가되지 않은 접속이 차단되었을때 알려줌 + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + 데모 서버 + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + 서비스 %1 시작중 + + + Stopping service %1 + 서비스 %1 중지중 + + + Registering service %1 + 서비스 %1 를 등록중 + + + Unregistering service %1 + 서비스 %1 를 등록 해제중 + + + Service control + 서비스 제어 + + + + ServiceControlPlugin + + Service is running + 서비스 실행중 + + + Service is not running + 서비스 실행중이 아님 + + + Configure and control Veyon service + Veyon 서비스 설정및 제어 + + + Register Veyon Service + Veyon 서비스 등록 + + + Unregister Veyon Service + Veyon 서비스 등록해지 + + + Start Veyon Service + Veyon 서비스 시작 + + + Stop Veyon Service + Veyon 서비스 중지 + + + Restart Veyon Service + Veyon 서비스 재시작 + + + Query status of Veyon Service + Veyon 서비스 상태 조회 + + + Commands for configuring and controlling Veyon Service + Veyon 서비스 설정 및 제어 명령어 + + + + ShellCommandLinePlugin + + Run command file + 명령어행 실행 + + + File "%1" does not exist! + 화일 "%1" 이 없음! + + + Interactive shell and script execution for Veyon Control + Veyon 제어용 대화형 쉘과 스크립트 실행 + + + Commands for shell functionalities + 쉘 기능용 명령어들 + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + 시스템 트레이 아이콘 + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + 시스템 유저그룹을 위한 사용자 그룹 백엔드 + + + Default (system user groups) + 기본 값(시스템 사용자 그룹들) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + 내부 Veyon 컴포넌트 및 기능을 시험 + + + Commands for testing internal components and functions of Veyon + Veyon의 내부 컴포넌트 및 기능을 시험하는 명령어들 + + + + TextMessageDialog + + Send text message + 문자 메세지 보내기 + + + Use the field below to type your message which will be sent to all selected users. + 선택된 사용자에세 메세지를 보내려면 아래 빈칸에 내용을 입력하세요 + + + + TextMessageFeaturePlugin + + Text message + 메세지 + + + Use this function to send a text message to all users e.g. to assign them new tasks. + 예를들어 모든 사용자에게 새로운 과제를 부과하는 문자 메세지를 보내려면 이기능을 사용 + + + Message from teacher + 교사의 메세지 + + + Send a message to a user + 사용자에게 메세지 보내기 + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + 계층화된 윈도우 캡쳐 활성화 (반 투명) + + + Poll full screen (leave this enabled per default) + 전체화면 (기본값으로 활성화시켜 놓음) + + + Low accuracy (turbo mode) + 저해상도 (터보모드) + + + Builtin UltraVNC server configuration + 내장 UltraVNC 서버 설정 + + + Enable multi monitor support + 다중 모니터 지원 활성화 + + + Enable Desktop Duplication Engine on Windows 8 and newer + 윈도8 또는 상위버전에서 데스크탑 복제엔진 활성화 + + + Maximum CPU usage + + + + + UserConfig + + No write access + 쓰기 권한 없음 + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + 개인 설정을 저장할 수 없음. %1 설정자를 이용하여 사용자 설정 화일을 경로를 확인하세요 + + + + UserLoginDialog + + User login + 유저 로긴 + + + Please enter a username and password for automatic login on all computers. + 자동으로 모든 컴퓨터에 로긴하려면 유저명과 패스워드를 입력하세요 + + + Username + 사용자 이름 + + + Password + 암호 + + + + UserSessionControlPlugin + + Log in + 로긴 + + + Click this button to log in a specific user on all computers. + 이 버튼을 클릭하여 모든 컴퓨터에 로긴 합니다 + + + Log off + 로그오프 + + + Click this button to log off users from all computers. + 이 버튼을 클릭하여 사용자를 모든 컴퓨터에서 로그오프하세요. + + + Confirm user logoff + 사용자 확인 로그오프 + + + Do you really want to log off the selected users? + 선택한 유저를 로그오프 하시겠습니까? + + + User session control + 사용자 세션제어 + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [실패] + + + Invalid command! + 잘못된 명령어 + + + Available commands: + 사용가능한 명령어: + + + Invalid arguments given + 인자가 잘못됨 + + + Not enough arguments given - use "%1 help" for more information + 필수 인자 미입력 - 자세한 정보는 %1 HELP를 사용하세요 + + + Unknown result! + 알수 없는 결과! + + + Available modules: + 사용가능한 모듈 + + + No module specified or module not found - available modules are: + 모듈이 지정되지 않았거나 찾을 수 없음 - 사용가능한 모듈들은: + + + Plugin not licensed + 플러그인 라이센스 안됨 + + + INFO + 정보 + + + ERROR + 에러 + + + USAGE + 사용법 + + + DESCRIPTION + 설명 + + + EXAMPLES + 예제 + + + WARNING + 경고 + + + + VeyonServiceControl + + Veyon Service + Veyon Service + + + + VncViewWidget + + Establishing connection to %1 ... + ... %1 로 연결 중입니다 + + + + WebApiConfigurationPage + + Web API + + + + General + 일반사항 + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + s + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + SAS 생성 설정을 소프트웨어로 변경할 수 없습니다. 원격제어로 Ctrl+Alt+Del 를 전송할 수 없습니다 ! + + + + WindowsPlatformConfigurationPage + + Windows + 윈도우 + + + General + 일반사항 + + + Enable SAS generation by software (Ctrl+Alt+Del) + 소프트웨어로 SAS (Ctrl+Alt+Del) 생성 허용 + + + Screen lock + 화면 잠금 + + + Hide taskbar + 작업 표시줄 숨기기 + + + Hide start menu + 시작 메뉴 숨기기 + + + Hide desktop + 데스크탑 숨김 + + + User authentication + 사용자 인증 + + + Use alternative user authentication mechanism + 대체 사용자 인증 메커니즘 사용 + + + User login + 유저 로긴 + + + Input start delay + 입력 시작 지연 + + + Simulated key presses interval + 시뮬레이션 키입력 간격 + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + 윈도우즈 플래폼용 플러그인 실행용 추상화 함수 + + + + WindowsServiceControl + + The service "%1" is already installed. + 서비스 "%1"은 이미 설치되어 있습니다. + + + The service "%1" could not be installed. + 서비스 "%1"을 설치할 수 없습니다. + + + The service "%1" has been installed successfully. + 서비스 "%1"을 성공적으로 설치했습니다. + + + The service "%1" could not be uninstalled. + 서비스 "%1"을 제거할 수 없습니다. + + + The service "%1" has been uninstalled successfully. + 서비스 "%1"을 성공적으로 제거했습니다. + + + The start type of service "%1" could not be changed. + 서비스 "%1"의 시작형식을 변경할 수 없음. + + + Service "%1" could not be found. + 서비스 "%1"을 찾을 수 없습니다. + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Builtin x11vnc 서버 설정 + + + Custom x11vnc parameters: + 사용자 x11vnc 파라메터 : + + + Do not use X Damage extension + X 손상 확장 사용하지 않음 + + + diff --git a/translations/veyon_lt.ts b/translations/veyon_lt.ts new file mode 100644 index 0000000..71ffd32 --- /dev/null +++ b/translations/veyon_lt.ts @@ -0,0 +1,4061 @@ + + + + + AboutDialog + + About + Apie programą + + + Translation + Vertimas + + + License + Licencija + + + About Veyon + Apie Veyon + + + Contributors + Autoriai + + + Version: + Versija + + + Website: + Tinklalapis: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Dabartinė naudojama kalba dar neišversta (arba pagrindinė yra Anglų) +Jeigu domina Veyon programos vertimas į vietinę ar kitą kalbą, arba norite patobulinti esamą vertimą, susisiekite su Veyon kūrėjais + + + About %1 %2 + Apie %1 %2 + + + Support Veyon project with a donation + Paremti Veyon projektą + + + + AccessControlPage + + Computer access control + Kompiuterio prieigos valdymas + + + Grant access to every authenticated user (default) + Suteikti prieigą kiekvienam prijungtam naudotojui (numatytasis) + + + Test + Testuoti + + + Process access control rules + Apdoroti prieigos valdymo taisykles + + + User groups authorized for computer access + Naudotojų grupės kurioms suteikta prieiga prie kompiuterio + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Pridėkite grupes kurių nariai kuriems turėtų būti leista prieiti prie kompiuterių esančių Veyon tinkle + + + Authorized user groups + Autorizuotų naudotojų grupės + + + All groups + Visos grupės + + + Access control rules + Prieigos valdymas + + + Add access control rule + Pridėti prieigos valdymo taisyklę + + + Remove access control rule + Pašalinti prieigos valdymo taisyklę + + + Move selected rule down + Pakelti pasirinktą taisyklę žemyn + + + Move selected rule up + Pakelti pasirinktą taisyklę aukštyn + + + Edit selected rule + Redaguoti pasirinktą taisyklę + + + Enter username + Įveskite naudotojo vardą + + + Please enter a user login name whose access permissions to test: + Įveskite naudotojo vardą kurio prieigos teises tikrinsite: + + + Access allowed + Prieiga leidžiama + + + The specified user is allowed to access computers with this configuration. + Pasirinktam naudotojui leidžiam prieigą prie kompiuterių su šia konfigūracija. + + + Access denied + Prieiga uždrausta + + + The specified user is not allowed to access computers with this configuration. + Pasirinktam naudotojui neleidžiama prieiga prie kompiuterių su šia konfigūracija. + + + Enable usage of domain groups + Įgalinti domenų grupių naudojimą + + + User groups backend: + Naudotojų grupių valdymas: + + + Missing user groups backend + Naudotojų grupių valdymas nerastas + + + No default user groups plugin was found. Please check your installation! + Nerasta standartinių naudotojų grupių plėtinio. Patikrinkite veyon programos įdiegimą! + + + Restrict access to members of specific user groups + Apriboti prieigą speficinių grupių nariams + + + + AccessControlRuleEditDialog + + Edit access control rule + Redaguoti prieigos valdymo taisyklę + + + General + Pagrindinis + + + enter a short name for the rule here + įveskite trumpą taisyklės pavadinimą + + + Rule name: + Taisyklės pavadinimas: + + + enter a description for the rule here + įveskite trumpą taisyklės aprašymą + + + Rule description: + Taisyklės aprašymas + + + Invert all conditions ("is/has" interpreted as "is/has not") + Invertuoti visas sąlygas ("yra turi" interpretuojama kaip "nėra/neturi") + + + Conditions + Sąlygos + + + is member of group + yra grupės narys + + + Accessing computer is localhost + Pasiekiamas kompiuteris yra vietiniame tinkle + + + Accessing user is logged on user + Prisijungiantis naudotojas yra prisijungęs naudotojas + + + Accessing user is already connected + Naudotojas kuris bando prisijungti jau yra prisijungęs + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Jeigu daugiau negu viena sąlyga yra aktyvuota kiekviena sąlyga turi atitikti nustatytus kriterijus, kad taisyklė veiktų (loginis IR). Jeigu viena iš kelių salygų atitinka (loginis ARBA). Prašome sukurti kelias prieigos valdymo taisykles. + + + Action + Veiksmai + + + Allow access + Suteikti prieigą + + + Deny access + Nesuteikti prieigos + + + Ask logged on user for permission + Prašyti prisijungusio naudotojo leidimo + + + None (rule disabled) + Nėra (Taisyklė išjungta) + + + Accessing user + Prisijungti kaip naudotojas + + + Accessing computer + Prisijungiama prie kompiuterio + + + Local (logged on) user + Vietinis (prisijungęs) naudotojas + + + Local computer + Vietinis kompiuteris + + + Always process rule and ignore conditions + Visados vykdyti šią taisyklę ir ignoruoti sąlygas + + + No user logged on + Nėra prisijungusių naudotojų + + + Accessing user has one or more groups in common with local (logged on) user + Naudotojas kurį bandoma pasiekti turi vieną ar daugiau bendrų grupių su vietiniu (prisijungusiu) naudotoju + + + Accessing computer and local computer are at the same location + Prisijungiantis kompiuteris ir vietinis kompiuteris yra toje pačioje vietoje + + + is located at + yra šioje vietoje + + + + AccessControlRulesTestDialog + + Access control rules test + Prieigos kontrolės taisyklių testavimas + + + Accessing user: + Prisijungiantis naudotojas + + + Local computer: + Vietinis kompiuteris: + + + Accessing computer: + Prisijungiantis kompiuteris: + + + Please enter the following user and computer information in order to test the configured ruleset. + Įveskite naudotojo ir kompiuterio informaciją jeigu norite patikrinti sukonfigūruotą taisyklių rinkinį + + + Local user: + Vietinis naudotojas: + + + Connected users: + Prijungti naudotojai: + + + The access in the given scenario is allowed. + Prieiga šiomis salygomis yra leidžiama. + + + The access in the given scenario is denied. + Prieiga šiomis salygomis yra draudžiama + + + The access in the given scenario needs permission of the logged on user. + Prieiga šiomis sąlygomis reikalauja patvirtinimo iš prisijungusio naudotojo + + + ERROR: Unknown action + KLAIDA: Nežinomas veiksmas + + + Test result + Testo rezultatai + + + + AuthKeysConfigurationPage + + Authentication keys + Prieigos raktai + + + Introduction + Įvadas + + + Key file directories + Direktorija raktų failams + + + Public key file base directory + Katalogas kuriame saugomas viešas raktas + + + Private key file base directory + Privataus rakto saugojimo direktorija + + + Available authentication keys + Galimi autorizavimo raktai + + + Create key pair + Sukurti raktų porą + + + Delete key + Ištrinti raktą + + + Import key + Importuoti raktą + + + Export key + Eksportuoti raktą + + + Set access group + Nustatyti prieigos grupę + + + Key files (*.pem) + Raktų failai (*.pem) + + + Authentication key name + Raktų failo vardas + + + Please enter the name of the user group or role for which to create an authentication key pair: + Įveskite naudotojų grupės vardą arba rolę kuriai bus kuriama raktų pora: + + + Do you really want to delete authentication key "%1/%2"? + Ar tikrai norite ištrinti prieigos raktą "%1/%2"? + + + Please select a key to delete! + Pasirinkite raktą kurį norite ištrinti! + + + Please enter the name of the user group or role for which to import the authentication key: + Įveskite naudotojų grupės vardą arba rolę kuriai bus importuojama raktų pora: + + + Please select a key to export! + Pasirinkite raktą kurį norite eksportuoti + + + Please select a user group which to grant access to key "%1": + Pasirinkite naudotojų grupę kuriai bus suteikta prieiga prie rakto "%1": + + + Please select a key which to set the access group for! + Pasirinkite vartotojų grupę kuriai norite nustatyti prieigos grupę + + + Please perform the following steps to set up key file authentication: + Atlikite šiuos veiksmus, kad nustatytumėte prieigą naudojant rakto failą + + + 1) Create a key pair on the master computer. + 1) Sukurkite raktų porą pagrindiniame kompiuteryje + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Nustatykite prieigos grupę, kurios nariams bus leista pasiekti kitus kompiuterius. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Eksportuokite viešajį raktą ir importuokite visuose klientų kompiuteriuose naudojant tą patį vardą. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Daugiau informacijos galite rasti <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administratoriaus Instrukcijoje</a> + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Autorizavimo raktų pora susideda iš dviejų kriptografinių raktų privataus ir viešojo. +Privatus raktas leidžia pagrindiniam kompiuteriui pasiekti klientų kompiuterius +Svarbu, kad tik autorizuoti naudotojai turėtų prieigą prie privataus rakto failo +Viešasis raktas skirtas kliento kompiuteriams patvirtinti įeinančio ryšio užklausą. + + + + AuthKeysManager + + Please check your permissions. + Patikrinkite savo leidimus. + + + Key name contains invalid characters! + Rakto varde yra neleistinų simbolių. + + + Invalid key type specified! Please specify "%1" or "%2". + Pasirinktas negalimas rakto tipas! Pasirinkite "%1" arba "%2". + + + Specified key does not exist! Please use the "list" command to list all installed keys. + Nurodytas raktas neegzistuoja! Pasinaudokite komanda "list" , kad peržiūrėtumėte visus įdiegtus raktus + + + One or more key files already exist! Please delete them using the "delete" command. + Vienas ar daugiau raktų failai jau egzistuoja! Ištrinkite juos naudojantis "delete" komanda. + + + Creating new key pair for "%1" + Sukurkite naują raktų porą "%1" + + + Failed to create public or private key! + Nepavyko sukurti viešojo arba privataus rakto + + + Newly created key pair has been saved to "%1" and "%2". + Naujai sukurta raktų pora buvo išsaugota: "%1" ir "%2". + + + Could not remove key file "%1"! + Nepavyko ištrinti rakto failo "%1"! + + + Could not remove key file directory "%1"! + Nepavyko ištrinti rakto failo direktorijos "%1"! + + + Failed to create directory for output file. + Nepavyko sukurti direktorijos išvesties failams + + + File "%1" already exists. + Failas "%1" jau egzistuoja + + + Failed to write output file. + Nepavyko įrašyti į išvesties failą. + + + Key "%1/%2" has been exported to "%3" successfully. + Raktas "%1/%2" eksportuotas į "%3" sėkmingai. + + + Failed read input file. + Nepavyko nuskaityti įvesties failo + + + File "%1" does not contain a valid private key! + Failas "%1" neturi tinkamo privataus rakto + + + File "%1" does not contain a valid public key! + Failas "%1" neturi tinkamo viešo rakto + + + Failed to create directory for key file. + Nepavyko sukurti direktorijos raktų failams. + + + Failed to write key file "%1". + Nepavyko įrašyti į rakto failą "%1". + + + Failed to set permissions for key file "%1"! + Nepavyko nustatyti prieigos teisių rakto failui "%1"! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + Raktai "%1/%2" buvo sėkmingai importuoti. Patikrinkite failų teises "%3" , kad būtų apribota neleistina prieiga. + + + Failed to convert private key to public key + Nepavyko konvertuoti privataus rakto į viešajį. + + + Failed to create directory for private key file "%1". + Nepavyko sukurti direktorijos privataus rakto failui "%1". + + + Failed to save private key in file "%1"! + Nepavyko išsaugoti privataus rakto "%1". + + + Failed to set permissions for private key file "%1"! + Nepavyko nustatyti teisių privataus rakto failui "%1"! + + + Failed to create directory for public key file "%1". + Nepavyko sukurti direktorijos viešojo rakto failui "%1". + + + Failed to save public key in file "%1"! + Nepavyko išsaugoti viešojo rakto "%1". + + + Failed to set permissions for public key file "%1"! + Nepavyko nustatyti prieigos teisių viešojo rakto failui "%1"! + + + Failed to set owner of key file "%1" to "%2". + Nepavyko nustatyti rakto failo šeimininko "%1" to "%2". + + + Failed to set permissions for key file "%1". + Nepavyko nustatyti prieigos teisių rakto failui "%1"! + + + Key "%1" is now accessible by user group "%2". + Raktas "%1" dabar yra pasiekiamas naudotojų grupės: "%2". + + + <N/A> + <N/A> + + + Failed to read key file. + Nepavyko nuskaityti rakto failo + + + + AuthKeysPlugin + + Create new authentication key pair + Sukurti naują prieigos raktų porą + + + Delete authentication key + Ištrinti prieigos raktą + + + List authentication keys + Atvaizduoti prieigos raktus + + + Import public or private key + Importuoti privatujį ar viešajį raktą + + + Export public or private key + Eksportuoti privatujį ar viešajį raktą + + + Extract public key from existing private key + Gauti viešajį raktą iš esamo privataus rakto + + + Set user group allowed to access a key + Nustatyta naudotojų grupė turi prieigą prie rakto. + + + KEY + RAKTAS + + + ACCESS GROUP + PRIEIGOS GRUPĖ + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + Ši komanda pakeičia failų prieigos teises į <KEY> tam, kad tik naudotojų grupė <ACCESS GROUP> turėtų skaitymo teises. + + + NAME + VARDAS + + + FILE + FAILAS + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Ši komanda eksportuoja prieigos raktą <KEY> į <FILE>. Jeigu <FILE> nėra nurodytas, vardas bus sukurtas iš vardo ir tipo <KEY>. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Ši komanda importuoja prieigos raktą <KEY> į <FILE>. Jeigu <FILE> nėra nurodytas, vardas bus sukurtas iš vardo ir tipo <KEY>. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + Ši komanda parodo visus galimus prieigos raktus, kurie yra sukonfigūruoti raktų direktorijoje. Jeigu opcija "%1" yra užpildyta, lentelė su rakto duomenimis bus parodyta. Ne visos detalės gali būti rodomos jeigu raktas yra nepasiekiamas, pavyzdžiui dėl teisių trūkumo. + + + Please specify the command to display help for! + Nurodykite komandą, kuriai norite peržiūrėti pagalbą! + + + TYPE + TIPAS + + + PAIR ID + POROS ID + + + Command line support for managing authentication keys + Komandinės eilutės prieiga autentifikacijos raktų valdymui + + + Commands for managing authentication keys + Komandos prieigos raktų valdymui + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + Ši komanda sukuria naują prieigos raktą su vardu <NAME> ir išsaugo privatų ir viešajį raktus į sukonfigūruotą raktų direktoriją. Parametras turi būti rakto vardas kuris gali būti sudarytas tik iš raidžių. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + Ši komanda ištrina prieigos raktą <KEY> iš sukonfigūruotų raktų direktorijos. Pastaba: ištrynus raktą jis negali būti atkurtas. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + Ši komanda sugeneruoja viešajį raktą iš privataus rakto <KEY> ir išsaugo kaip atitinkamą viešajį raktą. Kai bus nustatomas kitas pagrindinis kompiuteris, užtenka perkelti tik privatujį raktą. Viešasis raktas gali būti sugeneruotas iš privataus rakto. + + + + AuthKeysTableModel + + Name + Vardas + + + Type + Tipas + + + Access group + Prieigos grupė + + + Pair ID + Poros ID + + + + BuiltinDirectoryConfigurationPage + + Computers + Kompiuteriai + + + Name + Vardas + + + Host address/IP + Tinklo adresas/IP + + + MAC address + MAC adresas + + + Add new computer + Pridėti naują kompiuterį + + + Remove selected computer + Ištrinti pažymėtą kompiuterį + + + New computer + Naujas kompiuteris + + + Builtin directory + Standartinė direktorija + + + Locations & computers + Vietos ir kompiuteriai + + + Locations + Vietos + + + Add new location + Pridėti naują vietą + + + Remove selected location + Ištrinti pasirinktą vietą + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + CSV failų importavimas įmanomas naudojant komandinę eilutę. Daugiau informacijos žiūrėkite skiltyje <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">internetinėje dokumentacijoje </a>. + + + New location + Nauja vieta + + + + BuiltinDirectoryPlugin + + Show help for specific command + Parodyti specifinės komandos pagalbą + + + Import objects from given file + Importuoti objektus iš failo + + + Export objects to given file + Eksportuoti pasirinktus objektus į failą + + + Invalid type specified. Valid values are "%1" or "%2". + Nurodytas neteisingas tipas. Tinkamos vertės yra"%1" ar "%2". + + + Type + Tipas + + + Name + Vardas + + + Host address + Kompiuterio adresas + + + MAC address + MAC adresas + + + Specified object not found. + Nurodytas objektas nerastas. + + + File "%1" does not exist! + Failas "%1" neegzistuoja! + + + Can't open file "%1" for reading! + Negaliu atidaryti failo "%1" skaitymui! + + + Unknown argument "%1". + Nežinomas argumentas "%1". + + + Computer "%1" (host address: "%2" MAC address: "%3") + Kompiuteris "%1" (tinklo adresas: "%2" MAC adresas: "%3") + + + Unclassified object "%1" with ID "%2" + Nesuklasifikuotas objektas "%1" su ID "%2" + + + None + Nėra + + + Computer + Kompiuteris + + + Root + Pagrindinis + + + Invalid + Neteisingas + + + Error while parsing line %1. + Klaida šifruojant eilutę %1. + + + Network object directory which stores objects in local configuration + Tinklo objektų direktorija kurioje saugomi objektai vietinėje konfigūracijoje + + + Commands for managing the builtin network object directory + Komandos skirtos valdyti tinklo objektų direktoriją + + + No format string or regular expression specified! + Nenurodytas formatas, ar regex + + + Can't open file "%1" for writing! + Negalima atidaryti failo "%1" įrašymui! + + + No format string specified! + Nenurodytas formatas! + + + Object UUID + Objekto UUID + + + Parent UUID + Tėvinis UUID + + + Add a location or computer + Pridėti vietą ar kompiuterį + + + Clear all locations and computers + Išvalyti visas vietas ar kompiuterius + + + Dump all or individual locations and computers + Išvalyti visas ar individualias vietas ir kompiuterius + + + List all locations and computers + Atvaizduoti visas vietas ir kompiuterius + + + Remove a location or computer + Ištrinti vietą ar kompiuterį + + + Location "%1" + Vieta "%1" + + + Builtin (computers and locations in local configuration) + Kompiuteriai ar vietos vietinėje konfigūracijoje + + + Location + Vieta + + + FILE + FAILAS + + + LOCATION + VIETA + + + FORMAT-STRING-WITH-PLACEHOLDERS + FORMATUOTI-TEKSTĄ-SU-ŠABLONAIS + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + REGULIARI-IŠRAIŠKA-SU-ŠABLONU + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Importuoja objektus iš nurodyto tekstinio failo, naudojant numatytą formatą ar reguliarą formą kurioje yra viena ar daugiau užpildytų vietų. Tinkami šablonai yra: %1 + + + Import simple CSV file to a single room + Importuoti paprastą CSV failą į vieną kambarį + + + Import CSV file with location name in first column + Importuoti CSV failą su vietos pavadinimu pirmame stulpelyje + + + Import text file with with key/value pairs using regular expressions + Importuoti tekstinį failą su raktu/verčių poromis naudojant reguliarias išraiškas. + + + Import arbitrarily formatted data + Importuoti arbitruotai suformuotus duomenis + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Eksportuoja objektus į nurodytą tekstinį failą, naudojant numatytą formatą ar reguliarą išraišką kurioje yra viena ar daugiau užpildytų vietų. Tinkami šablonai yra: %1 + + + Export all objects to a CSV file + Eksportuoti visus objektus į CSV failą + + + Export all computers in a specific location to a CSV file + Eksportuoti kompiuterius iš nurodytos vietos į CSV failą + + + TYPE + TIPAS + + + NAME + VARDAS + + + PARENT + TĖVINIS + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + Prideda objektą kur %1 gali būti vienas iš "%2" ar "%3". %4 gali būti nurodyta pagal vardą arba UUID. + + + Add a room + Pridėti kambarį + + + Add a computer to room %1 + Pridėti kompiuterį prie kambario %1 + + + OBJECT + OBJEKTAS + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Pašalina nurodytą objektą iš direktorijos. %1 gali būti nurodytas pagal vardą ar UUID. Pašalinant vietą bus pašalinti visi susieti kompiuteriai. + + + Remove a computer by name + Ištrinti kompiuterį pagal vardą + + + Remove an object by UUID + Pašalinti objektą pagal UUID + + + "Room 01" + "Kambarys 01" + + + "Computer 01" + "Kompiuteris 01" + + + HOST ADDRESS + SERVERIO ADRESAS + + + MAC ADDRESS + MAC ADRESAS + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Integruotas VNC serveris (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Integruotas VNC serveris (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Serverio/IP adresas: %1 + + + Active features: %1 + Aktyvios funkcijos: %1 + + + Online and connected + Įjungtas ir prisijungta + + + Establishing connection + Jungiamasi + + + Computer offline or switched off + Kompiuteris nepasiekiamas arba išjungtas + + + Authentication failed or access denied + Autorizacija nepavyko arba prieiga negalima + + + Disconnected + Atsijungta + + + No user logged on + Nėra prisijungusių naudotojų + + + Logged on user: %1 + Prisijungęs naudotojas: %1 + + + Location: %1 + Vieta: %1 + + + Veyon Server unreachable or not running + Veyon Serveris nepasiekiamas ar nepaleistas + + + [no user] + [vartotojo nėra] + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 Tarnyba %2 vietoje %3:%4 + + + Authentication error + Autorizacijos klaida + + + Remote access + Nutolusi prieiga + + + User "%1" at host "%2" is now accessing this computer. + Naudotojas "%1" iš kompiuterio "%2" dabar prisjungęs prie šio kompiuterio. + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + Naudotojas "%1" iš kompiuterio "%2" bandė prisijungti prie šio kompiuterio, bet netinkamai autorizavosi. + + + Access control error + Prieigos valdymo klaida + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + Naudotojas "%1" iš kompiuterio "%2" bandė prisijungti prie šio kompiuterio, bet buvo užblokuotas dėl priėjimo kontrolės nustatymų. + + + Active connections: + Aktyvūs prisijungimai: + + + + ComputerManager + + User + Naudotojas + + + Missing network object directory plugin + Trūksta tinklo objektų direktorijos įskiepio + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + + Location detection failed + Nepavyko aptikti vietos + + + Computer name;Hostname;User + Kompiuterio vardas;Hostname;Naudotojas + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + Kompiuterio paieška + + + Add location + Pridėti vietą + + + Save computer/user list + Išsaugoti kompiuterių/naudotojų sąrašą + + + Select output filename + Pasirinkti išvesties failo vardą + + + CSV files (*.csv) + CSV failas (*.csv) + + + File error + Failo klaida + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + + + + Please specify a valid filename for the configuration export. + + + + Please specify a valid key. + Nurodykite tinkamą raktą + + + Specified key does not exist in current configuration! + + + + Please specify a valid value. + Nurodykite tinkamą vertę. + + + Configure Veyon at command line + Konfigūruoti Veyon naudojant komandinę eilutę + + + Output file is not writable! + Neįmanoma rašyti į išvesties failą! + + + Output directory is not writable! + Neįmanoma rašyti į išvesties direktoriją + + + Configuration file is not readable! + Neįmanoma nuskaityti konfigūracijos failo + + + Clear system-wide Veyon configuration + + + + List all configuration keys and values + + + + Import configuration from given file + Importuoti konfigūraciją iš nurodyto failo. + + + Export configuration to given file + Eksportuoti konfigūraciją į nurodytą failą + + + Read and output configuration value for given key + + + + Write given value to given configuration key + + + + Unset (remove) given configuration key + + + + Commands for managing the configuration of Veyon + + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + %1 prezentacija + + + + DemoConfigurationPage + + Demo server + Demonstracinis serveris + + + Tunables + Derinamieji + + + ms + ms + + + Key frame interval + Pagrindinio kadro intervalas + + + Memory limit + Atminties riba + + + MB + MB + + + Update interval + Atnaujinimo intervalas + + + s + s + + + Slow down thumbnail updates while demo is running + Sulėtinti miniatūrų atnaujinimą, kol vyksta prezentacija + + + + DemoFeaturePlugin + + Stop demo + Sustabdyti demonstraciją + + + Window demo + Prezentacija ekrane + + + Give a demonstration by screen broadcasting + Pateikite demonstraciją transliuodami ekraną + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Demo + Demonstracija + + + Share your screen or allow a user to share his screen with other users. + Dalintis savo ekranu, ar leisti vartotojui dalintis savo ekranu su kitais vartotojais. + + + Full screen demo + Pilno ekrano demonstracija. + + + Share your own screen in fullscreen mode + Dalintis savo ekranu pilno ekrano rėžime. + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + Šiame rėžime jūsų ekranas rodomas visiems kompiuteriams + + + Share your own screen in a window + Dalintis savo ekranu lange. + + + Share selected user's screen in fullscreen mode + Dalintis pasirinkto vartotojo ekranu pilnojo ekrano režime + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + Šiame režime bus rodomas pasirinkto vartotojo ekranas visiems vartotojams pilnojo ekrano režimu. Visuose kompiuteriuose įvesties įrenginiai yra užblokuoti. + + + Share selected user's screen in a window + Dalintis pasirinkto vartotojo ekranu lange. + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + Pasirinkite vartotoją kurio ekranu dalinsitės + + + Please select only one user screen to share. + Pasirinkite tik vieną vartotoją kurio ekranu dalinsitės + + + All screens + Visi ekranai + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + Darbalaukio prieigos dialogas + + + Confirm desktop access + Patvirtinti darbalaukio valdymą + + + Never for this session + Niekada šiame seanse + + + Always for this session + Visada šiame seanse + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + Naudotojas %1 iš kompiuterio %2 nori prisijungti prie jūsų kompiuterio. Ar norite tai leisti? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programos ir tinklalapiai + + + Predefined programs + Numatytosios programos + + + Name + Vardas + + + Path + Kelias + + + Add new program + Pridėti naują programą + + + Remove selected program + Pašalinti pasirinktą programą + + + Predefined websites + Numatytieji puslapiai + + + Remove selected website + Pašalinti pasirinktą puslapį + + + URL + URL + + + New program + Nauja programa + + + New website + Naujas puslapis + + + + DesktopServicesFeaturePlugin + + Run program + Paleisti programą + + + Open website + Atidaryti tinklalapį + + + Click this button to open a website on all computers. + Paspauskite šį mygtuką, kad atidarytumėte puslapį visuose kompiuteriuose. + + + Start programs and services in user desktop + Paleisti programas ar servisus naudotojo kompiuteryje + + + Click this button to run a program on all computers. + Paspauskite šį mygtuką, kad atidarytumėte programą visuose kompiuteriuose. + + + Run program "%1" + Paleisti programą "%1" + + + Custom program + Pasirinktinė programa + + + Open website "%1" + Atidaryti puslapį "%1" + + + Custom website + Pasirinktinis puslapis + + + + DocumentationFigureCreator + + Teacher + Mokytojas + + + Room %1 + Kambarys %1 + + + Please complete all tasks within the next 5 minutes. + Prašome užbaigti visas užduotis per artimiausias 5 minutes. + + + Custom website + Pasirinktinis puslapis + + + Open file manager + Atidaryti failų tvarkyklę + + + Start learning tool + Paleisti mokymosi įrankį + + + Play tutorial video + Paleisti mokomajį vaizdo įrašą + + + Custom program + Pasirinktinė programa + + + Handout + Padalomoji medžiaga + + + Texts to read + Medžiaga skaitymui + + + generic-student-user + bendrinis-besimokantysis-naudotojas + + + + ExternalVncServer + + External VNC server + Išorinis VNC serveris + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Išorinė VNC serverio konfigūracija + + + Port: + Prievadas: + + + Password: + Slaptažodis: + + + + FeatureControl + + Feature control + Funkcijų valdymas + + + + FileTransferConfigurationPage + + File transfer + Failų perkėlimas + + + Directories + Katalogai + + + Destination directory + + + + Default source directory + + + + Options + Nustatymai + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + Neįmanoma atidaryt failo "%1" skaitymui! Patikrinkite prieigos teises! + + + + FileTransferDialog + + File transfer + Failų perkėlimas + + + Options + Nustatymai + + + Transfer only + Tik perkelti + + + Transfer and open file(s) with associated program + Perkelti ir atidaryti failą(-us) su numatytąja programa + + + Transfer and open destination folder + Perkelti ir atidaryti failo katalogą + + + Files + Failai + + + Start + Pradėti + + + Overwrite existing files + Perrašyti esamus failus + + + + FileTransferPlugin + + File transfer + Failų perkėlimas + + + Click this button to transfer files from your computer to all computers. + Paspauskite šį mygtuką, kad perkelti failus iš Jūsų kompiuterio į visus kompiuterius + + + Select one or more files to transfer + Pasirinkite vieną ar daugiau failų perkėlimui + + + Transfer files to remote computer + Perkelti failus į nutolusį kompiuterį + + + Received file "%1". + Gautas failas "%1". + + + Could not receive file "%1" as it already exists. + Neįmanoma pasiekti failo "%1" kadangi failas jau egzistuoja. + + + Could not receive file "%1" as it could not be opened for writing! + Negalima priimti failo "%1" nes jo negalima atidaryti rašymui! + + + + GeneralConfigurationPage + + User interface + Naudotojo sąsaja + + + Language: + Kalba + + + Use system language setting + Naudoti sistemos kalbos nustatymą + + + Veyon + Veyon + + + Logging + Įrašymas į įvykių žurnalą + + + Log file directory + Įvykių žurnalo direktorija + + + Log level + Įvykių žurnalo lygmuo + + + Nothing + Nevygdyti įvykių žurnalo + + + Only critical messages + Tik kritines klaidas + + + Errors and critical messages + Visas klaidas + + + Warnings and errors + Įspėjimai ir klaidos + + + Information, warnings and errors + Informaciniai įspėjimai ir klaidos + + + Debug messages and everything else + Patarimų žinutės ir visa kita + + + Limit log file size + Limituoti žurnalo failo dydį + + + Clear all log files + Išvalyti visus įvykių žurnalo failus + + + Log to standard error output + Persijungti į įvykių žurnalą + + + Network object directory + Tinklo objektų direktorija + + + Backend: + Sisteminė konfigūracija + + + Update interval: + Atnaujinimo intervalai: + + + %1 service + %1 servisas + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + Servisas %1 turi būti sustabdytas laikinai, norint ištrinti įvykių žurnalo failus. Tęsti?? + + + Log files cleared + Įvykio failai ištrinti + + + All log files were cleared successfully. + Visi įvyko failai pašalinti sėkmingai. + + + Error + Klaida + + + Could not remove all log files. + Nepavyko pašalinti visų įvykio failų. + + + MB + MB + + + Rotate log files + Keisti įvykio žurnalo failus + + + x + x + + + seconds + Sekundės + + + Write to logging system of operating system + Įrašyti į operacinės sistemos įvykių žurnalą + + + Authentication + Autorizavimas + + + Method: + Metodas: + + + Logon authentication + Autorizacija naudojant prisijungimą prie sistemos + + + Key file authentication + Autorizuotis naudojant prieigos raktą + + + Test + Testuoti + + + Authentication is set up properly on this computer. + Autorizacija nustatyta tinkamai šiame kompiuteryje. + + + Authentication keys are not set up properly on this computer. + Autentifikacijos raktai nenustatyti tinkamai šiame kompiuteryje. + + + Authentication test + Autentifikacijos testas + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + Naršyti LDAP + + + + LdapClient + + LDAP error description: %1 + LDAP klaidos informacija: %1 + + + + LdapConfigurationPage + + Basic settings + Baziniai nustatymai + + + General + Pagrindinis + + + LDAP server and port + LDAP serverio adresas ir prievadas + + + Bind DN + Pririšti BN + + + Bind password + Pririšti slaptažodį + + + Anonymous bind + Anoniminis prisirišimas + + + Use bind credentials + Naudoti prisirišimo duomenis + + + Base DN + Bazinis DN + + + Fixed base DN + Fiksuotas bazinis DN + + + e.g. dc=example,dc=org + pvz. dc=pavyzdys, dc=org + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + pvz.: namingContexts arba defaultNamingContext + + + Environment settings + Aplinkos nustatymai + + + Object trees + Objektų medis + + + Computer tree + Kompiuterių medis + + + e.g. OU=Groups + pvz.: OU=Groups + + + User tree + Naudotojų medis + + + e.g. OU=Users + pvz.: OU=Users + + + e.g. OU=Computers + pvz.: OU=Computers + + + Group tree + Grupių medis + + + Perform recursive search operations in object trees + Vykdyti rekursyvią paiešką objektų medyje + + + Object attributes + Objekto atributai + + + e.g. hwAddress + pvz.: hwAddress + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + Kompiuterio MAC adreso atributai + + + Group member attribute + Grupių narių atributai + + + e.g. uid or sAMAccountName + + + + Advanced settings + Išplėstiniai nustatymai + + + Optional object filters + Pasirinktiniai filtrai + + + Filter for user groups + Filtras naudotojų grupėms + + + Filter for users + Filtras naudotojams + + + Filter for computer groups + Filtras kompiuterių grupėms + + + Group member identification + Grupių vartotojų indentifikacija + + + Distinguished name (Samba/AD) + Išskirtinis vardas (Samba/AD) + + + List all groups of a user + Atvaizduoti visas naudotojo grupes + + + List all groups of a computer + Atvaizduoti visas grupes kompiuteriui + + + Get computer object by IP address + Gaukite kompiuterio objektą pagal IP adresą + + + LDAP connection failed + Nepavyko prisijungti prie LDAP + + + LDAP bind failed + Nepavyko susieti su LDAP + + + LDAP bind successful + Susiejimas su LDAP sėkmingas + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Sėkmingai prisijungta prie LDAP serverio ir atliktas LDAP susiejimas. Pagrindiniai LDAP parametrai yra tinkamai sukonfigūruoti. + + + LDAP base DN test failed + Nepavyko atlikti LDAP bazinio DN testo + + + LDAP base DN test successful + LDAP bazinis DN testas sėkmingas + + + LDAP naming context test failed + Nepavyko atlikti LDAP pavadinimo konteksto testo + + + LDAP naming context test successful + LDAP įvardijimo konteksto testas sėkmingas + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + LDAP pavadinimų konteksto užklausa buvo sėkmingai pateikta. Rasta ši bazinė DN: +%1 + + + user tree + naudotojų medis + + + group tree + grupės medis + + + computer tree + kompiuterių medis + + + Enter username + Įveskite naudotojo vardą + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + naudotojo objektai + + + Enter group name + Įveskite grupės pavadinimą + + + Please enter a group name whose members to query: + Įveskite grupės pavadinimą, kurios narius užklausti: + + + group members + grupės nariai + + + Group not found + Grupė nerasta + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + Įveskite kompiuterio vardą + + + computer objects + kompiuterių objektai + + + Enter computer DN + Įveskite kompiuterio DN + + + Please enter the DN of a computer whose MAC address to query: + Įveskite kompiuterio DN, kurio MAC adresą norite užklausti: + + + computer MAC addresses + kompiuterių MAC adresai + + + users + naudotojai + + + user groups + naudotojų grupės + + + computer groups + kompiuterių grupės + + + Please enter a user login name whose group memberships to query: + + + + groups of user + naudotojo grupės + + + User not found + Naudotojas nerastas + + + groups of computer + kompiuterio grupės + + + Computer not found + Kompiuteris nerastas + + + Enter computer IP address + Įveskite kompiuterio IP adresą + + + Please enter a computer IP address which to resolve to an computer object: + Įveskite kompiuterio IP adresą, kurį reikia naudoti kompiuterio objektui atskirti: + + + computers + kompiuteriai + + + LDAP %1 test failed + LDAP %1 testas nepavyko + + + LDAP %1 test successful + LDAP %1 testas pavyko + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + Kompiuterių grupių medis + + + computer group tree + + + + Filter for computers + Filtrai kompiuteriams + + + e.g. room or computerLab + + + + Integration tests + Integraciniai testai + + + Computer groups + Kompiuterių grupės + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + Prisijungimo sauga + + + TLS certificate verification + TLS sertifikato patvirtinimas + + + System defaults + Gamykliniai nustatymai + + + Never (insecure!) + Niekada (nesaugu!) + + + Custom CA certificate file + Pasirinktinis CA sertifikato failas + + + None + Nėra + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + Sertifikatų failai (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + Šifravimo protokolas + + + Computer location attribute + Kompiuterio vietos atributai + + + Computer display name attribute + Kompiuterio atvaizduojamo vardo atributai + + + Location name attribute + Vietos pavadinimo atributai + + + e.g. cn or displayName + pvz.: cn arba displayName + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + Rodyti visas vietas + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + Įveskite vietovės pavadinimą + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + ir + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + Naršyti + + + Test + Testuoti + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + Klaidingas kompiuterio vardas + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + Įveskite kompiuterio vardą + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + Nepavyko ieškoti pagrindinio kompiuterio vardo + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + Prisijungimo vardo atributai + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + Parodyti pagalbos komandą + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + + + + User authentication + Naudotojo autorizavimas + + + Session management + Seanso valdymas + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + Pasirinkite vietą + + + enter search filter... + + + + + MainToolBar + + Configuration + Konfigūracija + + + Disable balloon tooltips + + + + Show icons only + + + + + MainWindow + + MainWindow + Pagrindinis langas + + + toolBar + Įrankių juosta + + + General + Pagrindinis + + + &File + &Failas + + + &Help + &Pagalba + + + &Quit + &Išeiti + + + Ctrl+Q + CTRL+Q + + + Ctrl+S + CTRL+S + + + L&oad settings from file + Įkelti nustatymus iš failo + + + Ctrl+O + CTRL+O + + + About Qt + Apie Qt + + + Authentication impossible + Autorizavimas neįmanomas + + + Configuration not writable + Konfiguracinis failas negali būti įrašytas + + + Load settings from file + Įkelti nustatymus iš failo + + + Save settings to file + Išsaugoti nustatymus į failą + + + Unsaved settings + Neišsaugoti nustatymai + + + There are unsaved settings. Quit anyway? + Egzistuoja dar neišsaugoti nustatymai. Vistiek išeiti? + + + Veyon Configurator + Veyon Configurator + + + Service + Tarnyba + + + Master + + + + Access control + Prieigos valdymas + + + About Veyon + Apie Veyon + + + Auto + + + + About + Apie programą + + + %1 Configurator %2 + + + + JSON files (*.json) + + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + Prieiga uždrausta + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + Ekrano nuotraukos + + + Feature active + Funkcija aktyvi + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + Atstatyti numatytają konfigūraciją + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + Ieškoti naudotojų bei kompiuterių + + + Align computers to grid + + + + %1 Configurator + + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + Į&rašyti nustatymus į failą + + + &View + &Rodyti + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + Vietos bei kompiuteriai + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Katalogai + + + User configuration + Naudotojo konfigūravimas + + + Feature on computer double click: + + + + Features + Funkcijos + + + All features + Visos funkcijos + + + Disabled features + Išjungtos funkcijos + + + Screenshots + Ekrano nuotraukos + + + <no feature> + + + + Basic settings + Baziniai nustatymai + + + Behaviour + Elgsena + + + Enforce selected mode for client computers + Įgalinti pasirinktą rėžimą kliento kompiuteriuose + + + Hide local computer + + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + + + + User interface + Naudotojo sąsaja + + + Background color + Fono spalva + + + Thumbnail update interval + Piktogramų atnaujinimo intervalas + + + ms + ms + + + Program start + + + + Modes and features + + + + User and computer name + Naudotojo ir kompiuterio vardas + + + Only user name + Tik naudotojo vardas + + + Only computer name + Tik kompiuterio vardas + + + Computer thumbnail caption + Kompiuterio piktogramos užrašas + + + Text color + Teksto spalva + + + Sort order + Rikiavimo tvarka + + + Computer and user name + Kompiuterio ir naudotojo vardas + + + Computer locations + Kompiuterių vietos + + + Show current location only + Parodyti dabartinę vietą + + + Allow adding hidden locations manually + + + + Hide empty locations + Slėpti tuščias vietas + + + Show confirmation dialog for potentially unsafe actions + Parodyti patvirtinimo langą galimai nesaugiems veiksmams + + + Perform access control + Atlikti prieigos kontrolę + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + Vietos/Kompiuteriai + + + + OpenWebsiteDialog + + Open website + Atidaryti tinklalapį + + + e.g. Veyon + pvz.: Veyon + + + Remember and add to website menu + + + + e.g. www.veyon.io + pvz.: www.veyon.io + + + Please enter the URL of the website to open: + + + + Name: + Vardas: + + + + PasswordDialog + + Username + Naudotojo vardas + + + Password + Slaptažodis + + + Veyon Logon + Veyon Prisijungimas + + + Authentication error + Autorizacijos klaida + + + Logon failed with given username and password. Please try again! + Nepavyko prisijungti su įvestu naudotojo vardu bei slaptažodžiu. Pabandykite dar kartą! + + + Please enter your username and password in order to access computers. + Įveskite savo naudotojo vardą ir slaptažodį, kad pasiektumėte kompiuterius. + + + + PowerControlFeaturePlugin + + Power on + Įjungti + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + + Reboot + Paleisti iš naujo + + + Click this button to reboot all computers. + Paspauskite šį mygtuką, kad paleistumėte iš naujo visus kompiuterius. + + + Power down + Išjungti + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + Į(iš)jungti kompiuterį ar paleisti iš naujo + + + Confirm reboot + Patvirtinti kompiuterio paleidimą iš naujo + + + Confirm power down + Patvirtinti kompiuterio išjungimą + + + Do you really want to reboot the selected computers? + Ar tikrai norite paleisti iš naujo pasirinktus kompiuterius? + + + Do you really want to power down the selected computer? + + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + MAC ADRESAS + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + Ši komanda persiunčia Wake-on-LAN(WOL) paketą tinklui, kad kompiuteris su nustatytu MAC adresu būtų įjungtas + + + Please specify the command to display help for! + Nurodykite komandą, kuriai norite peržiūrėti pagalbą! + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + Išjungti dabar + + + Install updates and power down + Įdiegti atnaujinimus ir išjungti + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + Išjungti + + + Please specify a timeout for powering down the selected computers: + + + + minutes + minutės + + + seconds + sekundės + + + + RemoteAccessFeaturePlugin + + Remote view + Nuotolinis rodymas + + + Open a remote view for a computer without interaction. + + + + Remote control + Nuotolinis valdymas + + + Open a remote control window for a computer. + + + + Remote access + Nutolusi prieiga + + + Remote view or control a computer + Žiūrėti arba valdyti kompiuterį nuotoliniu būdu + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + Parodyti pagalbos komandą + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + Tik peržiūra + + + Remote control + Nuotolinis valdymas + + + Send shortcut + + + + Fullscreen + Per visą ekraną + + + Window + Lange + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Meniu + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Jungiamasi su %1 + + + Connected. + Prisijungta. + + + Screenshot + Ekrano nuotrauka + + + Exit + Išeiti + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + Paleisti programas + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + pvz.: "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + Vardas: + + + Remember and add to program menu + + + + e.g. VLC + pvz.: VLC + + + + ScreenLockFeaturePlugin + + Lock + Užrakinti + + + Unlock + Atrakinti + + + Lock screen and input devices of a computer + Užrakinti ekraną ir įvesties įrenginius kompiuteryje + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + Nežinomas + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + Ekrano nuotrauka + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + Ekrano nuotrauka + + + Use this function to take a screenshot of selected computers. + + + + Screenshots taken + + + + Screenshot of %1 computer have been taken successfully. + + + + Take screenshots of computers and save them locally. + + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + Naudotojas: + + + Computer: + Kompiuteris: + + + Date: + Data: + + + Time: + Laikas: + + + Show + Rodyti + + + Delete + Ištrinti + + + Screenshot + Ekrano nuotrauka + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Pagrindinis + + + Autostart + Paleisti automatiškai + + + Hide tray icon + Paslėpti piktogramą Start juostoje + + + Start service + Paleisti tarnybą + + + Stopped + Sustabdytas + + + Stop service + Sustabdyti tarnybą + + + State: + Būsena: + + + Enable firewall exception + Pridėti išimtį ugniasienėje + + + Allow connections from localhost only + Leisti prisijungimus tik iš vietinio tinklo + + + VNC server + VNC serveris + + + Plugin: + Įskiepis: + + + Restart %1 Service + Perleisti %1 tarnybą + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Visi nustatymai išsaugoti sėkmingai. Tam, kad pakeitimai taptų aktyvūs, %1 tarnyba turi būti paleista iš naujo. Paleisti iš naujo dabar? + + + Running + Veikia + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + Rodyti pranešimą nuotoliniame kompiuteryje + + + Show notification when an unauthorized access is blocked + Rodyti pranešimą, kai neautorizuota prieiga užblokuota + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + Vidinis VNC serveris + + + Feature manager + Funkcijų valdymas + + + Demo server + Demonstracinis serveris + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + Paleidžiama tarnyba %1 + + + Stopping service %1 + Stabdoma tarnyba %1 + + + Registering service %1 + Užregistruojama tarnyba %1 + + + Unregistering service %1 + Išregistruojama tarnyba %1 + + + Service control + Serviso valdymas + + + + ServiceControlPlugin + + Service is running + Tarnyba vykdoma + + + Service is not running + Tarnyba nevykdoma + + + Configure and control Veyon service + Konfigūruoti ir kontroliuoti Veyon tarnybą + + + Register Veyon Service + Užregistruoti Veyon tarnybą + + + Unregister Veyon Service + Išregistruoti Veyon tarnybą + + + Start Veyon Service + Paleisti Veyon tarnybą + + + Stop Veyon Service + Sustabdyti Veyon tarnybą + + + Restart Veyon Service + Perkrauti Veyon tarnybą + + + Query status of Veyon Service + Užklausti Veyon tarnybos statuso + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + Paleisti komandos failą + + + File "%1" does not exist! + Failas "%1" neegzistuoja! + + + Interactive shell and script execution for Veyon Control + Interaktyvus shell ir script vykdymas + + + Commands for shell functionalities + Shell funkcijų komandos + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + Sistemos dėklo piktograma + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + Numatytasis (sistemos naudotojų grupės) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + Patikrinti vidinius Veyon komponentus ir funkcijas + + + Commands for testing internal components and functions of Veyon + Komandos naudojamos tikrinti vidinius komponentus ir funkcijas Veyon programinėje įrangoje + + + + TextMessageDialog + + Send text message + Nusiųsti tekstine žinutę + + + Use the field below to type your message which will be sent to all selected users. + Apačioje esančiame laukelyje įveskite žinutę kuri bus išsiųsta visiems pasirinktiems naudotojams + + + + TextMessageFeaturePlugin + + Text message + Tekstinė žinutė + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Naudokite šią funkciją norėdami išsiųsti žinutę visiems naudotojams, pvz., priskirti jiems naujas užduotis. + + + Message from teacher + Žinutė nuo mokytojo + + + Send a message to a user + Nusiųsti žinutę naudotojui + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Leisti įrašinėti sluoksniuotus ( permatomus ) langus + + + Poll full screen (leave this enabled per default) + Apklausa naudojant pilną ekraną (Palikite tai įjungta numatytuose nustatymuose) + + + Low accuracy (turbo mode) + Žemo tikslumo (Didelio greičio rėžimas) + + + Builtin UltraVNC server configuration + Integruoto UltraVNC serverio konfigūracija + + + Enable multi monitor support + Įgalinti daugiau nei vieno monitoriaus palaikymą + + + Enable Desktop Duplication Engine on Windows 8 and newer + Įgalinti darbalaukio dubliavimo variklį Windows 8 ar naujesnėje versijoje + + + Maximum CPU usage + + + + + UserConfig + + No write access + Neturite leidimo įrašyti + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Neįmanoma išsaugoti asmeninių nustatymų! Prašome patikrinti naudotojo konfigūracijos failo vietą naudodami %1 konfigūratorių. + + + + UserLoginDialog + + User login + Naudotojo prisijungimas + + + Please enter a username and password for automatic login on all computers. + Įveskite naudotojo vardą ir slaptažodį, kad galėtumėte automatiškai prisijungti visuose kompiuteriuose. + + + Username + Naudotojo vardas + + + Password + Slaptažodis + + + + UserSessionControlPlugin + + Log in + Prisijungti + + + Click this button to log in a specific user on all computers. + + + + Log off + Atsijungti + + + Click this button to log off users from all computers. + Paspauskite šį mygtuką, kad atjungtumėte naudotojus visuose kompiuteriuose. + + + Confirm user logoff + Patvirtinti naudotojo atsijungimą + + + Do you really want to log off the selected users? + Ar tikrai norite atjungti visus pasirinktus naudotojus? + + + User session control + Naudotojo seanso valdymas + + + + VeyonCore + + [OK] + [GERAI] + + + [FAIL] + [KLAIDA] + + + Invalid command! + Klaidinga komanda! + + + Available commands: + Galimos komandos: + + + Invalid arguments given + Klaidingi argumentai + + + Not enough arguments given - use "%1 help" for more information + Per mažai argumentų - naudokite "%1 help", norėdami gauti daugiau informacijos + + + Unknown result! + Nežinomas rezultatas! + + + Available modules: + Galimi moduliai: + + + No module specified or module not found - available modules are: + Nenurodytas modulis arba modulis nerastas - galimi moduliai yra: + + + Plugin not licensed + Įskiepis neįdiegtas + + + INFO + INFORMACIJA + + + ERROR + KLAIDA + + + USAGE + NAUDOJIMAS + + + DESCRIPTION + APIBŪDINIMAS + + + EXAMPLES + PAVYZDŽIAI + + + WARNING + ĮSPĖJIMAS + + + + VeyonServiceControl + + Veyon Service + Veyon Service + + + + VncViewWidget + + Establishing connection to %1 ... + Vykdomas sujungimas su %1 ... + + + + WebApiConfigurationPage + + Web API + + + + General + Pagrindinis + + + Network port + Tiklo prievadas + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + s + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + Neįmanoma išsaugoti SAS generavimo nustatymo programinėje įrangoje. Komandos Ctrl+Alt+Del siuntimas nuotoliniu valdymu neveiks! + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + Pagrindinis + + + Enable SAS generation by software (Ctrl+Alt+Del) + Įgalinti SAS generavimą programinėje įrangoje (Ctrl+Alt+Del) + + + Screen lock + Ekrano užraktas + + + Hide taskbar + Paslėpti užduočių juostą + + + Hide start menu + Paslėpti pradžios meniu + + + Hide desktop + Paslėpti darbalaukį + + + User authentication + Naudotojo autorizavimas + + + Use alternative user authentication mechanism + Naudoti alternatyvų naudotojo autentifikavimo mechanizmą + + + User login + Naudotojo prisijungimas + + + Input start delay + Įvesties vėlinimas + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Plėtinio įgyvendinimo abstrakčios funkcijos Windows platformai + + + + WindowsServiceControl + + The service "%1" is already installed. + Tarnyba "%1" jau yra įdiegta. + + + The service "%1" could not be installed. + Tarnyba "%1" negali būti įdiegta. + + + The service "%1" has been installed successfully. + Tarnyba "%1" buvo sėkmingai įdiegta. + + + The service "%1" could not be uninstalled. + Tarnyba "%1" negali būti išdiegta. + + + The service "%1" has been uninstalled successfully. + Tarnyba "%1" buvo sėkmingai išdiegta. + + + The start type of service "%1" could not be changed. + Paleisties tipas tarnybai "%1" negali būti pakeistas. + + + Service "%1" could not be found. + Tarnyba "%1" nerasta. + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Integruoto x11vnc serverio konfigūracija + + + Custom x11vnc parameters: + Pasirinktiniai x11vnc parametrai: + + + Do not use X Damage extension + Nenaudokite X Damage papildinio. + + + diff --git a/translations/veyon_lv.ts b/translations/veyon_lv.ts new file mode 100644 index 0000000..e6891fa --- /dev/null +++ b/translations/veyon_lv.ts @@ -0,0 +1,4058 @@ + + + + + AboutDialog + + About + Par + + + Translation + Tulkojumi + + + License + Licenze + + + About Veyon + Par Veyon + + + Contributors + Atbalstītāji + + + Version: + Versija: + + + Website: + Mājas lapa: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Pašreizējā valoda vēl nav pārtulkota. + +Ja esat ieinteresēti tulkot Veyon citā valodā vai uzlabot esošo tulkojumu, lūdzu sazinieties ar Veyon veidotājiem. + + + About %1 %2 + Par %1 %2 + + + Support Veyon project with a donation + Atbalsti Veyon projektu ar ziedojumu + + + + AccessControlPage + + Computer access control + Datora piekļuves kontrole + + + Grant access to every authenticated user (default) + Piešķirt piekļuvi katram autentificētam lietotājam (noklusējuma) + + + Test + Tests + + + Process access control rules + Procesa piekļuves kontroles noteikumi + + + User groups authorized for computer access + Lietotāju grupas, kurām ir atļauta piekļuve datoram + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Lūdzu pievienojiet grupu, kuras lietotājiem vajadzētu būt autorizētām piekļūt datoriem Jūsu Veyon tīklā. + + + Authorized user groups + Autorizētās lietotāju grupas + + + All groups + Visas grupas + + + Access control rules + Piekļuves kontroles noteikumi + + + Add access control rule + Pievienot piekļuves kontroles noteikumu + + + Remove access control rule + Noņemt piekļuves kontroles noteikumu + + + Move selected rule down + Pazemināt izvēlēto noteikumu + + + Move selected rule up + Paaugstināt izvēlēto noteikumu + + + Edit selected rule + Labot izvēlētās lomas + + + Enter username + Ievadi lietotājvārdu + + + Please enter a user login name whose access permissions to test: + Lūdzu ievadi lietotājvārdus, kuriem ļaut piekļuvi testam: + + + Access allowed + Piekļuve atļauta + + + The specified user is allowed to access computers with this configuration. + Izvēlētajam lietotājam ir atļauja piekļūt datoriem ar šiem iestatījumiem. + + + Access denied + Piekļuve liegta + + + The specified user is not allowed to access computers with this configuration. + + + + Enable usage of domain groups + + + + User groups backend: + + + + Missing user groups backend + + + + No default user groups plugin was found. Please check your installation! + + + + Restrict access to members of specific user groups + + + + + AccessControlRuleEditDialog + + Edit access control rule + Labot piekļuves kontroles noteikumu + + + General + Vispārīgi + + + enter a short name for the rule here + ievadi nosaukumu noteikumam šeit + + + Rule name: + Noteikuma nosaukums: + + + enter a description for the rule here + ievadu aprakstu noteikumam šeit + + + Rule description: + Noteikuma apraksts: + + + Invert all conditions ("is/has" interpreted as "is/has not") + + + + Conditions + Nosacījumi + + + is member of group + ir biedrs grupai + + + Accessing computer is localhost + + + + Accessing user is logged on user + + + + Accessing user is already connected + + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + + + + Action + Darbības + + + Allow access + Atļaut piekļuvi + + + Deny access + Liegt atļauju + + + Ask logged on user for permission + Vaicāt lietotājam atļauju + + + None (rule disabled) + Nav (noteikumi izslēgti) + + + Accessing user + Piekļūstošais lietotājs + + + Accessing computer + Piekļūstošais dators + + + Local (logged on) user + Vietējais (pieslēdzies) lietotājs + + + Local computer + Vietējais dators + + + Always process rule and ignore conditions + + + + No user logged on + Nav lietotājs pieslēdzies + + + Accessing user has one or more groups in common with local (logged on) user + + + + Accessing computer and local computer are at the same location + + + + is located at + + + + + AccessControlRulesTestDialog + + Access control rules test + Piekļuves kontroles noteikuma tests + + + Accessing user: + Piekļūstošais lietotājs: + + + Local computer: + Vietējais dators: + + + Accessing computer: + Piekļūstošais dators: + + + Please enter the following user and computer information in order to test the configured ruleset. + + + + Local user: + Vietējie lietotāji: + + + Connected users: + Pieslēgušies lietotāji: + + + The access in the given scenario is allowed. + Piekļuve izvēlētajam scenārijam ir atļauta. + + + The access in the given scenario is denied. + Piekļuve izvēlētajam scenārijam ir liegta. + + + The access in the given scenario needs permission of the logged on user. + + + + ERROR: Unknown action + Errors: Nezināma darbība + + + Test result + Testa rezultāti + + + + AuthKeysConfigurationPage + + Authentication keys + Autorizācijas atslēga + + + Introduction + Ievads + + + Key file directories + Atslēgfaila mapes + + + Public key file base directory + Publisko atslēgfailu atrašanās mape + + + Private key file base directory + Privāto atslēgfailu atrašanās mape + + + Available authentication keys + Pieejamās autentifikācijas atslēgas + + + Create key pair + Izveidot atslēgu + + + Delete key + Dzēst atslēgu + + + Import key + Iegult atslēgu + + + Export key + Izgult atslēgu + + + Set access group + Veidot piekļuves grupu + + + Key files (*.pem) + Atslēgu faili (*.pem) + + + Authentication key name + Autentifikācijas atslēgas nosaukums + + + Please enter the name of the user group or role for which to create an authentication key pair: + + + + Do you really want to delete authentication key "%1/%2"? + + + + Please select a key to delete! + Lūdzu, izvēlieties atslēgu kuru izdzēst! + + + Please enter the name of the user group or role for which to import the authentication key: + + + + Please select a key to export! + Lūdzu, izvēlieties atslēgfailu, kuru izvadīt! + + + Please select a user group which to grant access to key "%1": + + + + Please select a key which to set the access group for! + + + + Please perform the following steps to set up key file authentication: + + + + 1) Create a key pair on the master computer. + + + + 2) Set an access group whose members should be allowed to access other computers. + + + + 3) Export the public key and import it on all client computers with the same name. + + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + + + + + AuthKeysManager + + Please check your permissions. + Lūdzu, pārbaudiet atļaujas. + + + Key name contains invalid characters! + + + + Invalid key type specified! Please specify "%1" or "%2". + + + + Specified key does not exist! Please use the "list" command to list all installed keys. + + + + One or more key files already exist! Please delete them using the "delete" command. + + + + Creating new key pair for "%1" + + + + Failed to create public or private key! + + + + Newly created key pair has been saved to "%1" and "%2". + + + + Could not remove key file "%1"! + + + + Could not remove key file directory "%1"! + + + + Failed to create directory for output file. + + + + File "%1" already exists. + Fails "%1" jau eksistē. + + + Failed to write output file. + Neizdevās izveidot failu. + + + Key "%1/%2" has been exported to "%3" successfully. + + + + Failed read input file. + Neizdevās nolasīt failu. + + + File "%1" does not contain a valid private key! + + + + File "%1" does not contain a valid public key! + + + + Failed to create directory for key file. + Neizdevās izveidot mapi atslēgas failam. + + + Failed to write key file "%1". + Neizdevās ierakstīt atslēgas failā "%1". + + + Failed to set permissions for key file "%1"! + Neizdevās piešķirt atļaujas atslēgas failam "%1". + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + + + + Failed to convert private key to public key + + + + Failed to create directory for private key file "%1". + + + + Failed to save private key in file "%1"! + + + + Failed to set permissions for private key file "%1"! + + + + Failed to create directory for public key file "%1". + + + + Failed to save public key in file "%1"! + + + + Failed to set permissions for public key file "%1"! + + + + Failed to set owner of key file "%1" to "%2". + + + + Failed to set permissions for key file "%1". + + + + Key "%1" is now accessible by user group "%2". + + + + <N/A> + <N/A> + + + Failed to read key file. + Kļūda lasot atslēgfailu. + + + + AuthKeysPlugin + + Create new authentication key pair + + + + Delete authentication key + Izdzēst autentifikācijas failu + + + List authentication keys + + + + Import public or private key + + + + Export public or private key + + + + Extract public key from existing private key + + + + Set user group allowed to access a key + + + + KEY + ATSLĒGA + + + ACCESS GROUP + PIEKĻUVES GURPA + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + + NAME + VĀRDS + + + FILE + FAILS + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + + Please specify the command to display help for! + + + + TYPE + TIPS + + + PAIR ID + Pāra ID + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + Vārds + + + Type + Tips + + + Access group + Piekļuves grupas + + + Pair ID + Pāra ID + + + + BuiltinDirectoryConfigurationPage + + Computers + Datori + + + Name + Vārds + + + Host address/IP + Dalībnieku adreses un IP + + + MAC address + MAC adrese + + + Add new computer + Pievienot jaunu datoru + + + Remove selected computer + Izdzēst izvēlēto datoru + + + New computer + Jauns dators + + + Builtin directory + Iebūvētās mapes + + + Locations & computers + + + + Locations + + + + Add new location + + + + Remove selected location + + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + + + + + BuiltinDirectoryPlugin + + Show help for specific command + Parādīt palīdzību izvēlētajai komandai + + + Import objects from given file + + + + Export objects to given file + + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + Tips + + + Name + Vārds + + + Host address + Dalībnieku adreses + + + MAC address + MAC adrese + + + Specified object not found. + Izvēlētais mērķis nav atrasts. + + + File "%1" does not exist! + Fails "%1" neeksistē! + + + Can't open file "%1" for reading! + + + + Unknown argument "%1". + + + + Computer "%1" (host address: "%2" MAC address: "%3") + + + + Unclassified object "%1" with ID "%2" + + + + None + Nav + + + Computer + Dators + + + Root + Sākums + + + Invalid + Neatbilstošs + + + Error while parsing line %1. + Kļūda nolasot %1 līniju. + + + Network object directory which stores objects in local configuration + + + + Commands for managing the builtin network object directory + + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + Objekta UUID + + + Parent UUID + Vecāka UUID + + + Add a location or computer + + + + Clear all locations and computers + + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + + + + FILE + FAILS + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + TIPS + + + NAME + VĀRDS + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + MAC adrese + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + + + + + ComputerControlListModel + + Host/IP address: %1 + Dalībnieku adreses/IP: %1 + + + Active features: %1 + Aktīvās iespējas: %1 + + + Online and connected + Tiešsaistē un pieslēdzies + + + Establishing connection + Veido savienojumu + + + Computer offline or switched off + + + + Authentication failed or access denied + + + + Disconnected + Atslēdzies + + + No user logged on + Nav lietotājs pieslēdzies + + + Logged on user: %1 + + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + + + + Authentication error + Autentifikācijas kļūda + + + Remote access + Attālināta piekļuve + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + Lietotājs + + + Missing network object directory plugin + + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + Datora meklēšana + + + Add location + + + + Save computer/user list + Saglabāt datoru/lietotāju sarakstu + + + Select output filename + Izvēlies izejas faila nosaukumu + + + CSV files (*.csv) + CSV faili (*.csv) + + + File error + Faila kļūda + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + + + + Please specify a valid filename for the configuration export. + + + + Please specify a valid key. + + + + Specified key does not exist in current configuration! + + + + Please specify a valid value. + + + + Configure Veyon at command line + + + + Output file is not writable! + + + + Output directory is not writable! + + + + Configuration file is not readable! + + + + Clear system-wide Veyon configuration + + + + List all configuration keys and values + + + + Import configuration from given file + + + + Export configuration to given file + + + + Read and output configuration value for given key + + + + Write given value to given configuration key + + + + Unset (remove) given configuration key + + + + Commands for managing the configuration of Veyon + + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + %1 demonstrējums + + + + DemoConfigurationPage + + Demo server + Demonstrējuma serveris + + + Tunables + Noregulējumi + + + ms + ms + + + Key frame interval + + + + Memory limit + Atmiņas limits + + + MB + MB + + + Update interval + Atjaunot intervālu + + + s + s + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + Apturēt demonstrējumu + + + Window demo + Logā demonstrējums + + + Give a demonstration by screen broadcasting + Dot demonstrējumu caur ekrāna raidīšanu + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + + + + Confirm desktop access + + + + Never for this session + + + + Always for this session + + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + + DesktopServicesConfigurationPage + + Programs & websites + + + + Predefined programs + + + + Name + Vārds + + + Path + Ceļš + + + Add new program + Pievienot jaunu programmu + + + Remove selected program + Noņemt izvēlēto programmu + + + Predefined websites + + + + Remove selected website + + + + URL + URL + + + New program + + + + New website + + + + + DesktopServicesFeaturePlugin + + Run program + + + + Open website + Atvērt vietni + + + Click this button to open a website on all computers. + + + + Start programs and services in user desktop + + + + Click this button to run a program on all computers. + + + + Run program "%1" + Palaist programmu "%1" + + + Custom program + + + + Open website "%1" + + + + Custom website + + + + + DocumentationFigureCreator + + Teacher + + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + + + + Port: + + + + Password: + Parole: + + + + FeatureControl + + Feature control + + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + Mapes + + + Destination directory + + + + Default source directory + + + + Options + Iespējas + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + Iespējas + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + + + + Select one or more files to transfer + + + + Transfer files to remote computer + + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + + + + Language: + Valodas: + + + Use system language setting + + + + Veyon + Veyon + + + Logging + Žurnalēšana + + + Log file directory + Žurnālfaila mape + + + Log level + + + + Nothing + + + + Only critical messages + + + + Errors and critical messages + + + + Warnings and errors + + + + Information, warnings and errors + + + + Debug messages and everything else + + + + Limit log file size + Žurnālfaila izmēra ierobežojums + + + Clear all log files + + + + Log to standard error output + + + + Network object directory + + + + Backend: + + + + Update interval: + + + + %1 service + %1 pakalpojums + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + + + + All log files were cleared successfully. + + + + Error + Ķļūda + + + Could not remove all log files. + + + + MB + MB + + + Rotate log files + + + + x + x + + + seconds + sekundes + + + Write to logging system of operating system + + + + Authentication + Autentifikācija + + + Method: + + + + Logon authentication + + + + Key file authentication + + + + Test + Tests + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + + + + + LdapConfigurationPage + + Basic settings + + + + General + Vispārīgi + + + LDAP server and port + + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + + + + e.g. OU=Computers + + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + + LDAP base DN test failed + + + + LDAP base DN test successful + + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + Ievadi lietotājvārdu + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + Ievadi grupas nosaukumu + + + Please enter a group name whose members to query: + + + + group members + grupas dalībnieki + + + Group not found + Grupa nav atrasta + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + Ievadi datora nosaukumu + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + + + + users + lietotāji + + + user groups + lietotāju grupas + + + computer groups + datoru grupas + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + Lietotājs nav atrasts + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + datori + + + LDAP %1 test failed + + + + LDAP %1 test successful + + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + Datoru grupas + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + + + + TLS certificate verification + + + + System defaults + Sistēmas noklusējums + + + Never (insecure!) + + + + Custom CA certificate file + + + + None + Nav + + + TLS + + + + SSL + + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + Tests + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + Parādīt palīdzību par komandu + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + + + + enter search filter... + ievadi meklēšanas filtru... + + + + MainToolBar + + Configuration + + + + Disable balloon tooltips + + + + Show icons only + + + + + MainWindow + + MainWindow + + + + toolBar + + + + General + Vispārīgi + + + &File + &Fails + + + &Help + &Palīdzība + + + &Quit + &Iziet + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + + + + Ctrl+O + Ctrl+O + + + About Qt + + + + Authentication impossible + + + + Configuration not writable + + + + Load settings from file + + + + Save settings to file + + + + Unsaved settings + + + + There are unsaved settings. Quit anyway? + + + + Veyon Configurator + + + + Service + Pakalpojums + + + Master + + + + Access control + + + + About Veyon + Par Veyon + + + Auto + + + + About + Par + + + %1 Configurator %2 + + + + JSON files (*.json) + + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + Piekļuve liegta + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + Ekranšāviņi + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + + + + Align computers to grid + Sakārtot datorus režģī + + + %1 Configurator + + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Mapes + + + User configuration + + + + Feature on computer double click: + + + + Features + Iespējas + + + All features + Visas iespējas + + + Disabled features + Atslēgt iespējas + + + Screenshots + Ekranšāviņi + + + <no feature> + <no feature> + + + Basic settings + + + + Behaviour + + + + Enforce selected mode for client computers + + + + Hide local computer + + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + + + + User interface + + + + Background color + + + + Thumbnail update interval + + + + ms + ms + + + Program start + + + + Modes and features + + + + User and computer name + + + + Only user name + + + + Only computer name + + + + Computer thumbnail caption + + + + Text color + + + + Sort order + + + + Computer and user name + + + + Computer locations + + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + Atvērt vietni + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + + + + Name: + + + + + PasswordDialog + + Username + Lietotājvārds + + + Password + Parole + + + Veyon Logon + + + + Authentication error + Autentifikācijas kļūda + + + Logon failed with given username and password. Please try again! + + + + Please enter your username and password in order to access computers. + + + + + PowerControlFeaturePlugin + + Power on + Ieslēgt + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + + Reboot + Restartēt + + + Click this button to reboot all computers. + + + + Power down + Izslēgt + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + Ieslēgt/Izslēgt vai restartēt datoru + + + Confirm reboot + Apstiprināt restartēšanu + + + Confirm power down + Apstriprināt izslēgšanu + + + Do you really want to reboot the selected computers? + Vai Tu tiešām vēlies restartēt izvēlētos datorus? + + + Do you really want to power down the selected computer? + Vai Tu tiešām vēlies izslēgt izvēlētos datorus? + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + MAC adrese + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + Izslēgt + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + Attālināts skats + + + Open a remote view for a computer without interaction. + Atvērt attālinātu skatu datoram bez iejaukšanās + + + Remote control + Attālināta kontrole + + + Open a remote control window for a computer. + Atvērt attālinātu kontroles logu datoram + + + Remote access + Attālināta piekļuve + + + Remote view or control a computer + + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + Parādīt palīdzību par komandu + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 - %2 Attālināta piekļuve + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + Skats tikai + + + Remote control + Attālināta kontrole + + + Send shortcut + Nosūtīt saīsni + + + Fullscreen + Pilnekrāns + + + Window + Logs + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Izvēlne + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Pieslēdzas %1 + + + Connected. + Pieslēdzies. + + + Screenshot + Ekrānšāviņš + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + Palaist programmas + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + Slēgt + + + Unlock + Atslēgt + + + Lock screen and input devices of a computer + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + nezināms + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + Ekrānšāviņš + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + Ekrānšāviņš + + + Use this function to take a screenshot of selected computers. + Lieto šo funkciju lai uzņemtu ekrānšāviņu izvēlētajiem datoriem. + + + Screenshots taken + Ekrānšāviņš veikts + + + Screenshot of %1 computer have been taken successfully. + Ekrānšāviņs veiksmīgi uzņemts datoram %1. + + + Take screenshots of computers and save them locally. + Uzņemt datora ekrānšāviņu un saglabāt lokāli. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + Lietotājs: + + + Computer: + Dators: + + + Date: + Datums: + + + Time: + Laiks: + + + Show + Parādīt + + + Delete + Izdzēst + + + Screenshot + Ekrānšāviņš + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Vispārīgi + + + Autostart + Automātiskā palaišana + + + Hide tray icon + Paslēpt palodzes ikonu + + + Start service + Palaist pakalpojumu + + + Stopped + Apstādināts + + + Stop service + Apstādināt pakalpojumu + + + State: + + + + Enable firewall exception + Iespējot ugunsmūra izņēmumu + + + Allow connections from localhost only + + + + VNC server + VNC serveris + + + Plugin: + Spraudņi: + + + Restart %1 Service + Restartēt %1 pakalpojumu + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + + Running + Darbojas + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + Demonstrējuma serveris + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + Palaižas pakalpojums %1 + + + Stopping service %1 + Apstājas pakalpojums %1 + + + Registering service %1 + Reģistrēt pakalpojumu %1 + + + Unregistering service %1 + Atcelt reģistrāciju pakalpojumam %1 + + + Service control + Pakalpojuma kontrole + + + + ServiceControlPlugin + + Service is running + Pakalpojums darbojas + + + Service is not running + Pakalpojums nedarbojas + + + Configure and control Veyon service + + + + Register Veyon Service + Reģistrēt Veyon pakalpojumu + + + Unregister Veyon Service + Atcelt reģistrāciju Veyon pakalpojumam + + + Start Veyon Service + Sāknēt Veyon pakalpojumu + + + Stop Veyon Service + Apturēt Veyon pakalpojumu + + + Restart Veyon Service + Restartēt Veyon pakalpojumu + + + Query status of Veyon Service + + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + Palaist komandu failu + + + File "%1" does not exist! + Fails "%1" neeksistē! + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + Sistēmas teknes ikona + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + Nosūtīt teksta ziņojumu + + + Use the field below to type your message which will be sent to all selected users. + + + + + TextMessageFeaturePlugin + + Text message + Teksta ziņojums + + + Use this function to send a text message to all users e.g. to assign them new tasks. + + + + Message from teacher + Ziņojums no skolotāja + + + Send a message to a user + Nosūtīt ziņojumu lietotājam + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + + + + Poll full screen (leave this enabled per default) + + + + Low accuracy (turbo mode) + Zemu precizitāti (turbo režīms) + + + Builtin UltraVNC server configuration + + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + Nav rakstīšanas atļauja + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + Lietotājvārds + + + Password + Parole + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [FAIL] + + + Invalid command! + Neatbilstoša komanda! + + + Available commands: + Pieejamās komandas: + + + Invalid arguments given + + + + Not enough arguments given - use "%1 help" for more information + + + + Unknown result! + Nezināms rezultāts! + + + Available modules: + Pieejamie moduļi: + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + Spraudnis nav licenzēts + + + INFO + Info + + + ERROR + Kļūda + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + Veyon pakalpojums + + + + VncViewWidget + + Establishing connection to %1 ... + Veido savienojumu ar %1 ... + + + + WebApiConfigurationPage + + Web API + + + + General + Vispārīgi + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + s + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + Vispārīgi + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + + + + Custom x11vnc parameters: + + + + Do not use X Damage extension + + + + diff --git a/translations/veyon_mn.ts b/translations/veyon_mn.ts new file mode 100644 index 0000000..a913c7f --- /dev/null +++ b/translations/veyon_mn.ts @@ -0,0 +1,4056 @@ + + + + + AboutDialog + + About + Тухай + + + Translation + Орчуулга + + + License + Лиценз + + + About Veyon + + + + Contributors + + + + Version: + + + + Website: + + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + + + + About %1 %2 + + + + Support Veyon project with a donation + + + + + AccessControlPage + + Computer access control + + + + Grant access to every authenticated user (default) + + + + Test + Тест + + + Process access control rules + + + + User groups authorized for computer access + + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + + + + Authorized user groups + + + + All groups + Бүх групп + + + Access control rules + + + + Add access control rule + + + + Remove access control rule + + + + Move selected rule down + + + + Move selected rule up + + + + Edit selected rule + + + + Enter username + + + + Please enter a user login name whose access permissions to test: + + + + Access allowed + + + + The specified user is allowed to access computers with this configuration. + + + + Access denied + + + + The specified user is not allowed to access computers with this configuration. + + + + Enable usage of domain groups + + + + User groups backend: + + + + Missing user groups backend + + + + No default user groups plugin was found. Please check your installation! + + + + Restrict access to members of specific user groups + + + + + AccessControlRuleEditDialog + + Edit access control rule + + + + General + Ерөнхий + + + enter a short name for the rule here + + + + Rule name: + + + + enter a description for the rule here + + + + Rule description: + + + + Invert all conditions ("is/has" interpreted as "is/has not") + + + + Conditions + + + + is member of group + + + + Accessing computer is localhost + + + + Accessing user is logged on user + + + + Accessing user is already connected + + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + + + + Action + + + + Allow access + + + + Deny access + + + + Ask logged on user for permission + + + + None (rule disabled) + + + + Accessing user + + + + Accessing computer + + + + Local (logged on) user + + + + Local computer + + + + Always process rule and ignore conditions + + + + No user logged on + + + + Accessing user has one or more groups in common with local (logged on) user + + + + Accessing computer and local computer are at the same location + + + + is located at + + + + + AccessControlRulesTestDialog + + Access control rules test + + + + Accessing user: + + + + Local computer: + + + + Accessing computer: + + + + Please enter the following user and computer information in order to test the configured ruleset. + + + + Local user: + + + + Connected users: + + + + The access in the given scenario is allowed. + + + + The access in the given scenario is denied. + + + + The access in the given scenario needs permission of the logged on user. + + + + ERROR: Unknown action + + + + Test result + + + + + AuthKeysConfigurationPage + + Authentication keys + + + + Introduction + + + + Key file directories + + + + Public key file base directory + Нийтийн түлхүүрийг агуулсан сан + + + Private key file base directory + Хувийн түлхүүрийг агуулсан сан + + + Available authentication keys + + + + Create key pair + + + + Delete key + + + + Import key + + + + Export key + + + + Set access group + + + + Key files (*.pem) + + + + Authentication key name + + + + Please enter the name of the user group or role for which to create an authentication key pair: + + + + Do you really want to delete authentication key "%1/%2"? + + + + Please select a key to delete! + + + + Please enter the name of the user group or role for which to import the authentication key: + + + + Please select a key to export! + + + + Please select a user group which to grant access to key "%1": + + + + Please select a key which to set the access group for! + + + + Please perform the following steps to set up key file authentication: + + + + 1) Create a key pair on the master computer. + + + + 2) Set an access group whose members should be allowed to access other computers. + + + + 3) Export the public key and import it on all client computers with the same name. + + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + + + + + AuthKeysManager + + Please check your permissions. + + + + Key name contains invalid characters! + + + + Invalid key type specified! Please specify "%1" or "%2". + + + + Specified key does not exist! Please use the "list" command to list all installed keys. + + + + One or more key files already exist! Please delete them using the "delete" command. + + + + Creating new key pair for "%1" + + + + Failed to create public or private key! + + + + Newly created key pair has been saved to "%1" and "%2". + + + + Could not remove key file "%1"! + + + + Could not remove key file directory "%1"! + + + + Failed to create directory for output file. + + + + File "%1" already exists. + + + + Failed to write output file. + + + + Key "%1/%2" has been exported to "%3" successfully. + + + + Failed read input file. + + + + File "%1" does not contain a valid private key! + + + + File "%1" does not contain a valid public key! + + + + Failed to create directory for key file. + + + + Failed to write key file "%1". + + + + Failed to set permissions for key file "%1"! + + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + + + + Failed to convert private key to public key + + + + Failed to create directory for private key file "%1". + + + + Failed to save private key in file "%1"! + + + + Failed to set permissions for private key file "%1"! + + + + Failed to create directory for public key file "%1". + + + + Failed to save public key in file "%1"! + + + + Failed to set permissions for public key file "%1"! + + + + Failed to set owner of key file "%1" to "%2". + + + + Failed to set permissions for key file "%1". + + + + Key "%1" is now accessible by user group "%2". + + + + <N/A> + + + + Failed to read key file. + + + + + AuthKeysPlugin + + Create new authentication key pair + + + + Delete authentication key + + + + List authentication keys + + + + Import public or private key + + + + Export public or private key + + + + Extract public key from existing private key + + + + Set user group allowed to access a key + + + + KEY + + + + ACCESS GROUP + + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + + NAME + + + + FILE + + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + + Please specify the command to display help for! + + + + TYPE + + + + PAIR ID + + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + Нэр + + + Type + Төрөл + + + Access group + + + + Pair ID + + + + + BuiltinDirectoryConfigurationPage + + Computers + + + + Name + Нэр + + + Host address/IP + + + + MAC address + MAC хаяг + + + Add new computer + + + + Remove selected computer + + + + New computer + + + + Builtin directory + + + + Locations & computers + + + + Locations + + + + Add new location + + + + Remove selected location + + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + + + + + BuiltinDirectoryPlugin + + Show help for specific command + + + + Import objects from given file + + + + Export objects to given file + + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + Төрөл + + + Name + Нэр + + + Host address + + + + MAC address + MAC хаяг + + + Specified object not found. + + + + File "%1" does not exist! + + + + Can't open file "%1" for reading! + + + + Unknown argument "%1". + + + + Computer "%1" (host address: "%2" MAC address: "%3") + + + + Unclassified object "%1" with ID "%2" + + + + None + + + + Computer + + + + Root + + + + Invalid + + + + Error while parsing line %1. + + + + Network object directory which stores objects in local configuration + + + + Commands for managing the builtin network object directory + + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + + + + Parent UUID + + + + Add a location or computer + + + + Clear all locations and computers + + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + + + + FILE + + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + + + + NAME + + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + + + + + ComputerControlListModel + + Host/IP address: %1 + + + + Active features: %1 + + + + Online and connected + + + + Establishing connection + + + + Computer offline or switched off + + + + Authentication failed or access denied + + + + Disconnected + + + + No user logged on + + + + Logged on user: %1 + + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + + + + Authentication error + Баталгаажуулалтын алдаа + + + Remote access + + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + + + + Missing network object directory plugin + + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + + + + Add location + + + + Save computer/user list + + + + Select output filename + + + + CSV files (*.csv) + + + + File error + + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + + + + Please specify a valid filename for the configuration export. + + + + Please specify a valid key. + + + + Specified key does not exist in current configuration! + + + + Please specify a valid value. + + + + Configure Veyon at command line + + + + Output file is not writable! + + + + Output directory is not writable! + + + + Configuration file is not readable! + + + + Clear system-wide Veyon configuration + + + + List all configuration keys and values + + + + Import configuration from given file + + + + Export configuration to given file + + + + Read and output configuration value for given key + + + + Write given value to given configuration key + + + + Unset (remove) given configuration key + + + + Commands for managing the configuration of Veyon + + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + + + + + DemoConfigurationPage + + Demo server + Демо сервер + + + Tunables + + + + ms + + + + Key frame interval + + + + Memory limit + + + + MB + + + + Update interval + + + + s + + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + + + + Window demo + Дэлгэцээ харуулах + + + Give a demonstration by screen broadcasting + + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + + + + Confirm desktop access + Дэлгэцийн хандалтийг зөвшөөрөх + + + Never for this session + Хэзээ ч энэ горимд биш + + + Always for this session + Байнга энэ горимд + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + + DesktopServicesConfigurationPage + + Programs & websites + + + + Predefined programs + + + + Name + Нэр + + + Path + + + + Add new program + + + + Remove selected program + + + + Predefined websites + + + + Remove selected website + + + + URL + + + + New program + + + + New website + + + + + DesktopServicesFeaturePlugin + + Run program + + + + Open website + + + + Click this button to open a website on all computers. + + + + Start programs and services in user desktop + + + + Click this button to run a program on all computers. + + + + Run program "%1" + + + + Custom program + + + + Open website "%1" + + + + Custom website + + + + + DocumentationFigureCreator + + Teacher + Багш + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + + + + Port: + + + + Password: + + + + + FeatureControl + + Feature control + + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + Каталоги + + + Destination directory + + + + Default source directory + + + + Options + + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + + + + Select one or more files to transfer + + + + Transfer files to remote computer + + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + Хэрэглэгчийн интерфейс + + + Language: + Хэл: + + + Use system language setting + + + + Veyon + + + + Logging + Бүргэж байна + + + Log file directory + Нэвтрэх файлын сан + + + Log level + Нэвтрэх түвшин + + + Nothing + Идэвхгүй + + + Only critical messages + Зөвхөн чухал мессежүүд + + + Errors and critical messages + Алдаанууд болон чухал мессежүүд + + + Warnings and errors + Анхааруулгууд ба алдаанууд + + + Information, warnings and errors + Мэдээллүүд, анхааруулгууд болон алдаанууд + + + Debug messages and everything else + Алдааны репорт болон бүх зүйл + + + Limit log file size + Бүртгэлийн файлын хэмжээний хязгаар + + + Clear all log files + Бүх бүртгэлийн файлыг устгах + + + Log to standard error output + Нэвтрэх үеийн энгийн алдааны гаралт + + + Network object directory + + + + Backend: + + + + Update interval: + + + + %1 service + + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + Нэвтрэх файлууд устгагдсан + + + All log files were cleared successfully. + Бүх нэвтрэх файлууд амжилттай устгагдлаа. + + + Error + Алдаа + + + Could not remove all log files. + Бүх бүртгэлийн файлуудыг устгаж чадсангүй. + + + MB + + + + Rotate log files + + + + x + + + + seconds + секунд + + + Write to logging system of operating system + + + + Authentication + Баталгаажуулалт + + + Method: + + + + Logon authentication + Нэвтрэлт танилт + + + Key file authentication + Түлхүүр файлын баталгаажуулалт + + + Test + Тест + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + + + + + LdapConfigurationPage + + Basic settings + + + + General + Ерөнхий + + + LDAP server and port + + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + + + + e.g. OU=Computers + + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + + LDAP base DN test failed + + + + LDAP base DN test successful + + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + + + + Please enter a group name whose members to query: + + + + group members + + + + Group not found + + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + + + + users + + + + user groups + + + + computer groups + + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + + + + LDAP %1 test failed + + + + LDAP %1 test successful + + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + + + + TLS certificate verification + + + + System defaults + + + + Never (insecure!) + + + + Custom CA certificate file + + + + None + + + + TLS + + + + SSL + + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + Тест + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + + + + enter search filter... + + + + + MainToolBar + + Configuration + + + + Disable balloon tooltips + + + + Show icons only + + + + + MainWindow + + MainWindow + Үндсэн Цонх + + + toolBar + Багажны хэсэг + + + General + Ерөнхий + + + &File + &Файл + + + &Help + &Тусламж + + + &Quit + &Гарах + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + Т&охиргоог файлаас унших + + + Ctrl+O + CTRL+O + + + About Qt + Qt-ын тухай + + + Authentication impossible + Өөрийгөө таниулан нэвтэрч чадахгүй байна + + + Configuration not writable + Тохиргоо бичигдэх боломжгүй + + + Load settings from file + Тохиргоог файлаас унших + + + Save settings to file + Тохиргоог файлд хадгалах + + + Unsaved settings + Хадгалагдаагүй тохиргоо + + + There are unsaved settings. Quit anyway? + Тохиргоо хадгалагдаагүй байна. Шууд гарах уу? + + + Veyon Configurator + + + + Service + + + + Master + + + + Access control + + + + About Veyon + + + + Auto + + + + About + Тухай + + + %1 Configurator %2 + + + + JSON files (*.json) + + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + + + + Align computers to grid + + + + %1 Configurator + + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Каталоги + + + User configuration + + + + Feature on computer double click: + + + + Features + + + + All features + + + + Disabled features + + + + Screenshots + + + + <no feature> + + + + Basic settings + + + + Behaviour + + + + Enforce selected mode for client computers + + + + Hide local computer + + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + + + + User interface + Хэрэглэгчийн интерфейс + + + Background color + + + + Thumbnail update interval + + + + ms + + + + Program start + + + + Modes and features + + + + User and computer name + + + + Only user name + + + + Only computer name + + + + Computer thumbnail caption + + + + Text color + + + + Sort order + + + + Computer and user name + + + + Computer locations + + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + + + + Name: + + + + + PasswordDialog + + Username + Хэрэглэгчийн нэр + + + Password + Нууц үг + + + Veyon Logon + + + + Authentication error + Баталгаажуулалтын алдаа + + + Logon failed with given username and password. Please try again! + + + + Please enter your username and password in order to access computers. + + + + + PowerControlFeaturePlugin + + Power on + Асаах + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + + Reboot + Дахин ачааллах + + + Click this button to reboot all computers. + + + + Power down + Унтраах + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + + + + Confirm reboot + + + + Confirm power down + + + + Do you really want to reboot the selected computers? + + + + Do you really want to power down the selected computer? + + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + Унтраах + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + + + + Open a remote view for a computer without interaction. + + + + Remote control + Алсын удирдлага + + + Open a remote control window for a computer. + + + + Remote access + + + + Remote view or control a computer + + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + Зөвхөн харах + + + Remote control + Алсын удирдлага + + + Send shortcut + + + + Fullscreen + Бүтэн дэлгэц + + + Window + Цонх + + + Ctrl+Alt+Del + + + + Ctrl+Esc + + + + Alt+Tab + + + + Alt+F4 + + + + Win+Tab + + + + Win + + + + Menu + + + + Alt+Ctrl+F1 + + + + Connecting %1 + Холбогдож байна %1 + + + Connected. + Холбогдсон. + + + Screenshot + + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + + + + Unlock + + + + Lock screen and input devices of a computer + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + үл мэдэгдэх + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + + + + Use this function to take a screenshot of selected computers. + + + + Screenshots taken + + + + Screenshot of %1 computer have been taken successfully. + + + + Take screenshots of computers and save them locally. + + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + Хэрэглэгч: + + + Computer: + + + + Date: + Огноо: + + + Time: + Хугацаа: + + + Show + Харах + + + Delete + Устгах + + + Screenshot + + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Ерөнхий + + + Autostart + Автоматаар эхлэх + + + Hide tray icon + Тавиурын дүрсийг нуух + + + Start service + Сервисийг эхлүүлэх + + + Stopped + Зогссон + + + Stop service + Сервисийг зогсоох + + + State: + Төлөв: + + + Enable firewall exception + Онцгой хамгаалалтыг идэвхжүүлэх + + + Allow connections from localhost only + Зөвхөн дотоод холболтууддаа зөвшөөрөх + + + VNC server + + + + Plugin: + + + + Restart %1 Service + + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + + Running + Ажиллуулах + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + Демо сервер + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + + + + Stopping service %1 + + + + Registering service %1 + + + + Unregistering service %1 + + + + Service control + + + + + ServiceControlPlugin + + Service is running + + + + Service is not running + + + + Configure and control Veyon service + + + + Register Veyon Service + + + + Unregister Veyon Service + + + + Start Veyon Service + + + + Stop Veyon Service + + + + Restart Veyon Service + + + + Query status of Veyon Service + + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + Текст мессеж явуулах + + + Use the field below to type your message which will be sent to all selected users. + Доорх талбарт бүх сонгогдсон хэрэглэгчдэд илгээх мессежээ бичнэ. + + + + TextMessageFeaturePlugin + + Text message + Текст мессеж + + + Use this function to send a text message to all users e.g. to assign them new tasks. + + + + Message from teacher + Багшаас мессеж ирлээ + + + Send a message to a user + + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Үечилсэн бичилтийг идэвхжүүлэх(хагас-тодорхой) + + + Poll full screen (leave this enabled per default) + Бүтэн дэлгэцээрх санал асуулга(үүнийг идэвхтэйгээр орхих хэрэгтэй) + + + Low accuracy (turbo mode) + Бага нарийвчлал (өндөр хурдтай) + + + Builtin UltraVNC server configuration + + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + Бичих эрх байхгүй + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + Хэрэглэгчийн нэр + + + Password + Нууц үг + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + + + + + VeyonCore + + [OK] + + + + [FAIL] + + + + Invalid command! + + + + Available commands: + + + + Invalid arguments given + + + + Not enough arguments given - use "%1 help" for more information + + + + Unknown result! + + + + Available modules: + + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + + + + INFO + + + + ERROR + + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + + + + + VncViewWidget + + Establishing connection to %1 ... + Үүлчилгээг бүртгүүлэх боломжгүй %1 ... + + + + WebApiConfigurationPage + + Web API + + + + General + Ерөнхий + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + Ерөнхий + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + + + + Custom x11vnc parameters: + + + + Do not use X Damage extension + + + + diff --git a/translations/veyon_nl.ts b/translations/veyon_nl.ts new file mode 100644 index 0000000..ddf7ec4 --- /dev/null +++ b/translations/veyon_nl.ts @@ -0,0 +1,4065 @@ + + + + + AboutDialog + + About + Over + + + Translation + Vertaling + + + License + Licentie + + + About Veyon + Over Veyon + + + Contributors + Bijdragers + + + Version: + Versie + + + Website: + Website: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Huidige taal is nog niet vertaald (of in moedertaal English). + +Als je geïnteresseerd bent in het vertalen van Veyon in je eigen taal of een andere taal of de vertaling wilt verbeteren, neem dan contact op met een Veyon ontwikkelaar! + + + About %1 %2 + Over %1 %2 + + + Support Veyon project with a donation + Steun het Veyon project met een gift + + + + AccessControlPage + + Computer access control + Computer toegangscontrole + + + Grant access to every authenticated user (default) + Verleen toegang aan elke geauthenticeerde gebruiker (standaard) + + + Test + Test + + + Process access control rules + Verwerk toegangscontrole regels + + + User groups authorized for computer access + Gebruikersgroepen die bevoegd zijn voor toegang tot de computer + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Gelieve de groepen toe te voegen waarvan de leden bevoegd zouden zijn om toegang te krijgen tot computers in uw Veyon-netwerk. + + + Authorized user groups + Geautoriseerde gebruikersgroepen + + + All groups + Alle groepen + + + Access control rules + Toegangscontrole regels + + + Add access control rule + Voeg toegangsregelregel toe + + + Remove access control rule + Verwijder toegangsregelregel + + + Move selected rule down + Verplaats de geselecteerde regel naar beneden + + + Move selected rule up + Verplaatsde de geselecteerde regel naar boven + + + Edit selected rule + Bewerk de geselecteerde regel + + + Enter username + Voer gebruikersnaam in + + + Please enter a user login name whose access permissions to test: + Voer een gebruikersnaam in om toegangscontrole te testen: + + + Access allowed + Toegang toegestaan + + + The specified user is allowed to access computers with this configuration. + De opgegeven gebruiker is toegestaan om toegang tot computers te krijgen met deze configuratie. + + + Access denied + Toegang geweigerd + + + The specified user is not allowed to access computers with this configuration. + De opgegeven gebruiker is niet toegestaan om toegang tot computers te krijgen met deze configuratie. + + + Enable usage of domain groups + Gebruik van domeingroepen inschakelen + + + User groups backend: + Gebruikersgroepen backend + + + Missing user groups backend + Ontbrekende gebruikersgroepen backend + + + No default user groups plugin was found. Please check your installation! + Er werd geen standaard gebruikersgroepplugin gevonden. Controleer de installatie! + + + Restrict access to members of specific user groups + Beperk toegang tot leden van bepaalde groepen + + + + AccessControlRuleEditDialog + + Edit access control rule + Bewerk toegangscontrole regel + + + General + Algemeen + + + enter a short name for the rule here + vul hier een korte naam voor de regel in + + + Rule name: + Regel naam: + + + enter a description for the rule here + vul hier een beschrijving in voor de regel + + + Rule description: + Regelbeschrijving: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Keer all condities om ("is/heeft" geïnterpreteerd als "is/heeft niet") + + + Conditions + Voorwaarden + + + is member of group + is lid van groep + + + Accessing computer is localhost + Toegangzoekende computer is localhost + + + Accessing user is logged on user + Toegangzoekende gebruiker is de aangelogde gebruiker + + + Accessing user is already connected + Toegangzoekende gebruiker is reeds aangemeld + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Als er meerdere condities zijn geactiveerd, moet elke conditie voldoen om de regel toe te passen (logisch EN). Als slechts één van meerdere voorwaarden moet voldoen (logisch OF), maak dan meerdere toegangsregels aan. + + + Action + Actie + + + Allow access + Sta toegang toe + + + Deny access + Weiger toegang + + + Ask logged on user for permission + Vraag ingelogde gebruiker om toestemming + + + None (rule disabled) + Geen (regel uitgeschakeld) + + + Accessing user + Toegangzoekende gebruiker + + + Accessing computer + Toegangzoekende computer + + + Local (logged on) user + Lokale (ingelogde) gebruiker + + + Local computer + Lokale computer + + + Always process rule and ignore conditions + Verwerk altijd regel en negeer de voorwaarden + + + No user logged on + Geen gebruiker ingelogd + + + Accessing user has one or more groups in common with local (logged on) user + Toegangzoekende gebruiker heeft een of meerdere groepen gemeenschappelijk met de lokale (aangemelde) gebruiker + + + Accessing computer and local computer are at the same location + Toegangzoekende computer bevindt zich in hetzelfde lokaal als lokale computer + + + is located at + bevind zich in + + + + AccessControlRulesTestDialog + + Access control rules test + Toegangscontrole regels test + + + Accessing user: + Toegangzoekende gebruiker: + + + Local computer: + Lokale computer: + + + Accessing computer: + Toegangzoekende computer: + + + Please enter the following user and computer information in order to test the configured ruleset. + Voer de volgende gebruikers- en computerinformatie in om de geconfigureerde regelset te testen. + + + Local user: + Lokale gebruiker: + + + Connected users: + Geconnecteerde gebruikers: + + + The access in the given scenario is allowed. + De toegang in het gegeven scenario is toegestaan. + + + The access in the given scenario is denied. + De toegang in het gegeven scenario is geweigerd. + + + The access in the given scenario needs permission of the logged on user. + De toegang in het gegeven scenario heeft toestemming van de ingelogde gebruiker nodig. + + + ERROR: Unknown action + FOUT: onbekende actie + + + Test result + Testresultaat + + + + AuthKeysConfigurationPage + + Authentication keys + Authenticatie sleutels + + + Introduction + Inleiding + + + Key file directories + Sleutelbestand folder + + + Public key file base directory + Publieke sleutel basis bestands folder + + + Private key file base directory + Privé sleutel basis bestands folder + + + Available authentication keys + Beschikbare authenticatie sleutels + + + Create key pair + key pair aanmaken + + + Delete key + Verwijder key + + + Import key + Importeer key + + + Export key + Exporteer key + + + Set access group + Stel toegangsgroep in + + + Key files (*.pem) + Key bestanden (*.pem) + + + Authentication key name + Authenticatie sleutel naam + + + Please enter the name of the user group or role for which to create an authentication key pair: + Geef de naam van de gebruikers groep of rol waarvoor u een authenticatie sleutelpaar wil maken: + + + Do you really want to delete authentication key "%1/%2"? + Bent u zeker om met de verwijdering van authenticatiesleutel "%1/%2" door te gaan? + + + Please select a key to delete! + Gelieve de sleutel aan te duiden die u wilt verwijderen! + + + Please enter the name of the user group or role for which to import the authentication key: + Geef de naam van de gebruikers groep of rol waarvoor u een authenticatie sleutel wil importeren: + + + Please select a key to export! + Gelieve de sleutel aan te duiden die u wilt exporteren! + + + Please select a user group which to grant access to key "%1": + Selecteer een gebruikersgroep die je toegang wil geven tot key "%1": + + + Please select a key which to set the access group for! + Gelieve de sleutel aan te duiden waarvoor u de toegangsgroep wil instellen! + + + Please perform the following steps to set up key file authentication: + Gelieve volgende stappen te volgen om sleutel authenticatie in te stellen: + + + 1) Create a key pair on the master computer. + 1) Maak een key pair op de master computer. + + + 2) Set an access group whose members should be allowed to access other computers. + + + + 3) Export the public key and import it on all client computers with the same name. + 3) Exporteer de publieke key en importeer deze op alle client computers met dezelfde naam. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + + + + + AuthKeysManager + + Please check your permissions. + Gelieve uw rechten na te kijken. + + + Key name contains invalid characters! + Key naam bevat niet toegestane karakters + + + Invalid key type specified! Please specify "%1" or "%2". + + + + Specified key does not exist! Please use the "list" command to list all installed keys. + + + + One or more key files already exist! Please delete them using the "delete" command. + Eén of meer key files bestaan al! Verwijder ze door gebruik te maken van het commando "verwijder". + + + Creating new key pair for "%1" + Maak een nieuw sleutelpaar voor "%1" + + + Failed to create public or private key! + Aanmaken van de publieke sleutel of private sleutel is mislukt. + + + Newly created key pair has been saved to "%1" and "%2". + Het nieuwe sleutelpaar is opgeslagen op "%1" en "%2" + + + Could not remove key file "%1"! + Kan sleutelbestand "%1" niet verwijderen! + + + Could not remove key file directory "%1"! + + + + Failed to create directory for output file. + Maken van een uitvoermap is mislukt. + + + File "%1" already exists. + Bestand "%1" bestaat reeds. + + + Failed to write output file. + Schrijven van het uitvoerbestand is mislukt. + + + Key "%1/%2" has been exported to "%3" successfully. + + + + Failed read input file. + Lezen van het invoerbestand is mislukt. + + + File "%1" does not contain a valid private key! + Bestand "%1" bevat geen geldige private sleutel! + + + File "%1" does not contain a valid public key! + Bestand "%1" bevat geen geldige publieke sleutel! + + + Failed to create directory for key file. + Maken van de map voor sleutelbestand is mislukt. + + + Failed to write key file "%1". + Schrijven van sleutelbestand "%1" is mislukt. + + + Failed to set permissions for key file "%1"! + + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + + + + Failed to convert private key to public key + Omzetten van private sleutel naar publieke sleutel is mislukt. + + + Failed to create directory for private key file "%1". + Maken van de map voor het private sleutelbestand "%1" is mislukt. + + + Failed to save private key in file "%1"! + Schrijven van private sleutel in bestand "%1" is mislukt. + + + Failed to set permissions for private key file "%1"! + + + + Failed to create directory for public key file "%1". + + + + Failed to save public key in file "%1"! + + + + Failed to set permissions for public key file "%1"! + + + + Failed to set owner of key file "%1" to "%2". + + + + Failed to set permissions for key file "%1". + + + + Key "%1" is now accessible by user group "%2". + + + + <N/A> + <N/A> + + + Failed to read key file. + Lezen van het sleutelbestand is mislukt. + + + + AuthKeysPlugin + + Create new authentication key pair + + + + Delete authentication key + Verwijder authenticatie sleutel + + + List authentication keys + + + + Import public or private key + Importeer publieke of private sleutel + + + Export public or private key + Exporteer publieke of private sleutels + + + Extract public key from existing private key + + + + Set user group allowed to access a key + + + + KEY + SLEUTEL + + + ACCESS GROUP + TOEGANGS GROEP + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + + NAME + NAAM + + + FILE + BESTAND + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + + Please specify the command to display help for! + + + + TYPE + TYPE + + + PAIR ID + PAIR ID + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + Naam + + + Type + Type + + + Access group + Toegangsgroep + + + Pair ID + Paar ID + + + + BuiltinDirectoryConfigurationPage + + Computers + Computers + + + Name + Naam + + + Host address/IP + Host adres/IP + + + MAC address + MAC adres + + + Add new computer + Voeg nieuwe computer toe + + + Remove selected computer + Verwijder geselecteerde computer + + + New computer + Nieuwe computer + + + Builtin directory + + + + Locations & computers + Locaties & computers + + + Locations + + + + Add new location + + + + Remove selected location + + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + Nieuwe locatie + + + + BuiltinDirectoryPlugin + + Show help for specific command + + + + Import objects from given file + Importeer objecten van opgegeven bestand + + + Export objects to given file + Exporteer objecten van opgegeven bestand + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + Type + + + Name + Naam + + + Host address + + + + MAC address + MAC adres + + + Specified object not found. + Gevraagd object niet gevonnden. + + + File "%1" does not exist! + Bestand "%1" bestaat niet! + + + Can't open file "%1" for reading! + Kan bestand "%1" niet lezen! + + + Unknown argument "%1". + Onbekend argument "%1" + + + Computer "%1" (host address: "%2" MAC address: "%3") + + + + Unclassified object "%1" with ID "%2" + + + + None + Geen + + + Computer + Computer + + + Root + Root + + + Invalid + Ongeldig + + + Error while parsing line %1. + + + + Network object directory which stores objects in local configuration + + + + Commands for managing the builtin network object directory + + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + + + + Parent UUID + + + + Add a location or computer + + + + Clear all locations and computers + + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + Locatie + + + FILE + BESTAND + + + LOCATION + LOCATIE + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + TYPE + + + NAME + NAAM + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + Voeg een ruimte toe + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + Verwijder een computer op naam + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + MAC ADRES + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Ingebouwde VNC server (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Ingebouwde VNC server (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Host/IP adres: %1 + + + Active features: %1 + + + + Online and connected + Online en verbonden + + + Establishing connection + Verbinding aan het maken + + + Computer offline or switched off + Computer offline of uitgeschakeld + + + Authentication failed or access denied + Verificatie mislukt of toegang geweigerd + + + Disconnected + Ontkoppeld + + + No user logged on + Geen gebruiker ingelogd + + + Logged on user: %1 + Ingelogde gebruiker: %1 + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 Service %2 bij %3:%4 + + + Authentication error + Authenticatie fout + + + Remote access + Toegang op afstand + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + Gebruiker + + + Missing network object directory plugin + Ontbrekende netwerk object directory plugin + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Er is geen standaard plugin voor netwerkobjecten gevonden. Controleer uw installatie of configureer een andere netwerk object directory backend via% 1 Configurator. + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + Computer zoeken + + + Add location + + + + Save computer/user list + Bewaar computer/gebruiker lijst + + + Select output filename + Selecteer uitvoer bestandsnaam + + + CSV files (*.csv) + CSV bestanden (*.csv) + + + File error + Bestandsfout + + + Could not write the computer and users list to %1! Please check the file access permissions. + Kon de computer en gebruikerslijst niet schrijven naar% 1! Controleer de toegangsrechten voor het bestand. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Geef alstublieft een bestaand configuratiebestand op om te importeren. + + + Please specify a valid filename for the configuration export. + Geef alstublieft een geldige bestandsnaam op voor de configuratie export. + + + Please specify a valid key. + Geef alstublief een geldige sleutel op. + + + Specified key does not exist in current configuration! + Opgegeven sleutel bestaat niet in de huidige configuratie! + + + Please specify a valid value. + Geef alstublief een geldige waarde op. + + + Configure Veyon at command line + Configureer Veyon via opdrachtregel + + + Output file is not writable! + Uitvoerbestand is niet schrijfbaar! + + + Output directory is not writable! + Uitvoermap is niet schrijfbaar! + + + Configuration file is not readable! + Configuratiebestand is niet leesbaar! + + + Clear system-wide Veyon configuration + Veyon-configuratie helemaal wissen + + + List all configuration keys and values + Lijst alle configuratie sleutels en waarden op + + + Import configuration from given file + Importeer configuratie van opgegeven bestand + + + Export configuration to given file + Exporteer configuratie naar opgegeven bestand + + + Read and output configuration value for given key + Lees en lever configuratiewaarde voor de gegeven sleutel uit + + + Write given value to given configuration key + Schrijf de gegeven waarde naar de gegeven configuratiesleutel + + + Unset (remove) given configuration key + Ontkoppel (verwijder) de gegeven configuratiesleutel + + + Commands for managing the configuration of Veyon + Commando's voor het beheren van de configuratie van Veyon + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + Kon de autostart eigenschap niet wijzigen voor de %1 service. + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + %1 Demo + + + + DemoConfigurationPage + + Demo server + Demo server + + + Tunables + Tunables + + + ms + ms + + + Key frame interval + Keyframe interval + + + Memory limit + Geheugenlimiet + + + MB + MB + + + Update interval + Update interval + + + s + s + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + Stop demo + + + Window demo + Venster demo + + + Give a demonstration by screen broadcasting + Geef een demonstratie door het uitzenden van het scherm + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + In deze modus wordt uw scherm in een venster op alle computers weergegeven. De gebruikers kunnen, indien nodig, overstappen naar andere vensters. + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + Bureaublad toegangsdialoog + + + Confirm desktop access + Bevestig bureaublad toegang + + + Never for this session + Nooit voor deze sessie + + + Always for this session + Altijd voor deze sessie + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + De gebruiker %1 van computer %2 wil toegang krijgen tot uw bureaublad. Wilt u toegang geven? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programma's & websites + + + Predefined programs + + + + Name + Naam + + + Path + Pad + + + Add new program + Nieuw programma toevoegen + + + Remove selected program + Geselecteerd programma verwijderen + + + Predefined websites + + + + Remove selected website + Geselecteerde website verwijderen + + + URL + URL + + + New program + Nieuw programma + + + New website + Nieuwe website + + + + DesktopServicesFeaturePlugin + + Run program + Voer programma uit + + + Open website + Open website + + + Click this button to open a website on all computers. + Klik op deze knop om een website op alle computers te openen. + + + Start programs and services in user desktop + Start programma's en services in de gebruikersdesktop + + + Click this button to run a program on all computers. + Klik op deze knop om een programma op alle computers uit te voeren. + + + Run program "%1" + Programma "%1" uitvoeren + + + Custom program + Aangepast programma + + + Open website "%1" + Open website "%1" + + + Custom website + Aangepaste website + + + + DocumentationFigureCreator + + Teacher + Leraar + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + Aangepaste website + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + Aangepast programma + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + Externe VNC server + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Externe VNC server configuratie + + + Port: + Poort: + + + Password: + Wachtwoord: + + + + FeatureControl + + Feature control + Kenmerkencontrole + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + Mappen + + + Destination directory + + + + Default source directory + + + + Options + + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + Klik op deze knop om bestanden van jouw computer naar alle computers over te zetten. + + + Select one or more files to transfer + Selecteer één of meerdere bestanden om over te zetten. + + + Transfer files to remote computer + + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + Gebruikersomgeving + + + Language: + Taal: + + + Use system language setting + Gebruik de systeemtaalinstelling + + + Veyon + Veyon + + + Logging + Loggen + + + Log file directory + Logbestand map + + + Log level + Log niveau + + + Nothing + Niets + + + Only critical messages + Alleen kritieke berichten + + + Errors and critical messages + Fouten en kritieke berichten + + + Warnings and errors + Waarschuwingen en fouten + + + Information, warnings and errors + Informatie, waarschuwingen en foutmeldingen + + + Debug messages and everything else + Fout opsporings berichten en al het andere + + + Limit log file size + Beperk log bestandsgrootte + + + Clear all log files + Wis alle logbestanden + + + Log to standard error output + Log naar standaard fout uitgang + + + Network object directory + Netwerk object map + + + Backend: + Backend: + + + Update interval: + Update interval: + + + %1 service + %1 service + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + De service van %1 moet tijdelijk gestopt worden om de logbestanden te verwijderen. Doorgaan? + + + Log files cleared + Log bestanden gewist + + + All log files were cleared successfully. + Alle logbestanden zijn succesvol gewist. + + + Error + Fout + + + Could not remove all log files. + Kon alle log bestanden niet verwijderen. + + + MB + MB + + + Rotate log files + Roteer logbestanden + + + x + x + + + seconds + seconden + + + Write to logging system of operating system + + + + Authentication + Authenticatie + + + Method: + Methode: + + + Logon authentication + Aanmeldingsverificatie + + + Key file authentication + Sleutel bestand authenticatie + + + Test + Test + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + LDAP foutbeschrijving: %1 + + + + LdapConfigurationPage + + Basic settings + Basisinstellingen + + + General + Algemeen + + + LDAP server and port + LDAP server en poort + + + Bind DN + Bind DN + + + Bind password + Bind wachtwoord + + + Anonymous bind + Anonieme bind + + + Use bind credentials + Gebruik bind credentials + + + Base DN + Base DN + + + Fixed base DN + Vaste basis DN + + + e.g. dc=example,dc=org + bv. dc=example,dc=org + + + Discover base DN by naming context + Ontdek basis DN door naming context + + + e.g. namingContexts or defaultNamingContext + bv. namingContexts of defaultNamingContext + + + Environment settings + Omgevingsinstellingen + + + Object trees + Object boomstructuren + + + Computer tree + Computer boomstructuur + + + e.g. OU=Groups + bv. OU=Groups + + + User tree + Gebruiker boomstructuur + + + e.g. OU=Users + bv. OU=Users + + + e.g. OU=Computers + bv. OU=Computers + + + Group tree + Groep boomstructuur + + + Perform recursive search operations in object trees + Voer recursieve zoekacties uit in objectbomen + + + Object attributes + Object attributen + + + e.g. hwAddress + bv. hwAddress + + + e.g. member or memberUid + bv. member of memberUid + + + e.g. dNSHostName + bv. dNSHostName + + + Computer MAC address attribute + Computer MAC adres attribuut + + + Group member attribute + Groepslid attribuut + + + e.g. uid or sAMAccountName + bv. uid of sAMAccountName + + + Advanced settings + Geavanceerde instellingen + + + Optional object filters + Optionele objectfilters + + + Filter for user groups + Filter voor gebruikersgroepen + + + Filter for users + Filter voor gebruikers + + + Filter for computer groups + Filter voor computergroepen + + + Group member identification + Groepslid identificatie + + + Distinguished name (Samba/AD) + Onderscheidende naam (Samba/AD) + + + List all groups of a user + Lijst alle groepen van een gebruiker op + + + List all groups of a computer + Lijst alle groepen van een computer op + + + Get computer object by IP address + Verkrijg computer object door IP-adres + + + LDAP connection failed + LDAP connectie mislukt + + + LDAP bind failed + LDAP bind mislukt + + + LDAP bind successful + LDAP bind gelukt + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Succesvol geconnecteerd op de LDAP-server en een LDAP-bind uitgevoerd. De basis LDAP instellingen zijn correct geconfigureerd. + + + LDAP base DN test failed + LDAP base DN test mislukt + + + LDAP base DN test successful + LDAP base DN test gelukt + + + LDAP naming context test failed + LDAP naming context test mislukt + + + LDAP naming context test successful + LDAP naming context test gelukt + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + De LDAP naming context is succesvol bevraagd. De volgende basis DN werd gevonden: +%1 + + + user tree + gebruiker boomstructuur + + + group tree + groep boomstructuur + + + computer tree + computer boomstructuur + + + Enter username + Voer gebruikersnaam in + + + Please enter a user login name (wildcards allowed) which to query: + Vul alstublieft een gebruikersnaam in (wildcards toegestaan) die bevraagd moet worden: + + + user objects + gebruikersobjecten + + + Enter group name + Voer de groepsnaam in + + + Please enter a group name whose members to query: + Vul alstublieft een groepsnaam in waarvan de leden bevraagd moeten worden: + + + group members + groepsleden + + + Group not found + Groep niet gevonden + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + Kan een groep met de naam "%1" niet vinden. Controleer de groepsnaam of de groepsboomstructuur parameter. + + + Enter computer name + Voer de computernaam in + + + computer objects + computer objecten + + + Enter computer DN + Voer de computer DN in + + + Please enter the DN of a computer whose MAC address to query: + Vul alstublieft de DN in van een computer waarvan het MAC-adres moet worden bevraagd: + + + computer MAC addresses + computer MAC adressen + + + users + gebruikers + + + user groups + gebruikersgroepen + + + computer groups + computergroepen + + + Please enter a user login name whose group memberships to query: + Vul alstublieft een gebruikersnaam in van wie de groepslidmaatschappen moeten bevraagd worden: + + + groups of user + groepen van gebruiker + + + User not found + Gebruiker niet gevonden + + + groups of computer + groepen van computer + + + Computer not found + Computer niet gevonden + + + Enter computer IP address + Voer computer IP adres in + + + Please enter a computer IP address which to resolve to an computer object: + Voer het IP-adres van een computer in dat moet worden omgezet naar een computerobject: + + + computers + computers + + + LDAP %1 test failed + LDAP %1 test mislukt + + + LDAP %1 test successful + LDAP %1 test gelukt + + + The %1 has been queried successfully and %2 entries were found. + De %1 is met succes opgevraagd en %2 ingaven zijn gevonden. + + + %1 %2 have been queried successfully: + +%3 + %1 %2 zijn succesvol opgevraagd. + +%3 + + + LDAP filter test failed + LDAP filter test mislukt + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + Kon geen %1 opvragen met behulp van de ingestelde filter. Kontroleer de LDAP-filter op %1. + +%2 + + + LDAP filter test successful + LDAP-filtertest geslaagd + + + %1 %2 have been queried successfully using the configured filter. + %1 %2 zijn met succes opgevraagd gebruikmakend van de ingestelde filter. + + + (only if different from group tree) + (Alleen als verschillend van groep-structuur) + + + Computer group tree + Computer groep structuur + + + computer group tree + computer groep structuur + + + Filter for computers + Filter voor computers + + + e.g. room or computerLab + bv. lokaal of computerlabo + + + Integration tests + Intregatietests + + + Computer groups + Computergroepen + + + e.g. name or description + bv. naam of beschrijving + + + Filter for computer containers + Filter voor computer containers + + + Computer containers or OUs + Computer containers of OUs + + + Connection security + Verbindingsbeveiliging + + + TLS certificate verification + TLS certificaatverificatie + + + System defaults + Systeem standaard + + + Never (insecure!) + Nooit (onveilig!) + + + Custom CA certificate file + Aangepast CA certificaatbestand + + + None + Geen + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + bv. (objectClass=computer) + + + e.g. (objectClass=group) + bv. (objectClass=group) + + + e.g. (objectClass=person) + bv. (objectClass=person) + + + e.g. (objectClass=room) or (objectClass=computerLab) + bv. (objectClass=room) of (objectClass=computerLab) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + bv. (objectClass=container) of (objectClass=organizationalUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + Certificaatbestanden (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + Kon geen verbinding maken met de LDAP server. Controleer de server parameters. + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + Encryptie protocol + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + Test + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + Automatiseer de base DN dmv. naamgevingscontext + + + Query objects from LDAP directory + Objecten opvragen uit LDAP directory + + + Show help about command + Toon hulp over commando + + + Commands for configuring and testing LDAP/AD integration + Commando's voor het configureren en testen van LDAP/AD-integratie + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Plugin uitvoering van abstracte functies voor Linux + + + + LocationDialog + + Select location + + + + enter search filter... + vul zoekfilter in... + + + + MainToolBar + + Configuration + Configuratie + + + Disable balloon tooltips + Schakel ballontooltips uit + + + Show icons only + Toon enkel iconen + + + + MainWindow + + MainWindow + HoofdScherm + + + toolBar + werkbalk + + + General + Algemeen + + + &File + &Bestand + + + &Help + &Help + + + &Quit + &Stoppen + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + &Laad instellingen uit bestand + + + Ctrl+O + Ctrl+O + + + About Qt + Over Qt + + + Authentication impossible + Authenticatie onmogelijk + + + Configuration not writable + Configuratie niet schrijfbaar + + + Load settings from file + Instellingen uit bestand laden + + + Save settings to file + Instellingen opslaan naar bestand + + + Unsaved settings + Niet-opgeslagen instellingen + + + There are unsaved settings. Quit anyway? + Er zijn niet-opgeslagen instellingen. Toch stoppen? + + + Veyon Configurator + Veyon Configurator + + + Service + Service + + + Master + Master + + + Access control + Toegangscontrole + + + About Veyon + Over Veyon + + + Auto + Auto + + + About + Over + + + %1 Configurator %2 + %1 Configuratie %2 + + + JSON files (*.json) + JSON bestanden (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + De lokale backend configuratie rapporteert dat de configuratie niet-schrijfbaar is. Start de %1 configurator met admin of hogere previleges. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + Er werden geen verificatiesleutels gevonden of de huidige zijn verlopen. Maak nieuwe sleutelbestanden aan, of als alternatief stel logon verificatie in, met de %1 configurator. U krijgt geen toegang tot computers, gebruikmakend van %1, indien u niet een van beide gebruikt. + + + Access denied + Toegang geweigerd + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + Volgens de lokale instellingen hebt u geen toelating tot de computers in het netwerk. Login met een ander gebruikersaccount of laat uw systeemadministrator de lokale instellingen nazien. + + + Screenshots + Schermafbeeldingen + + + Feature active + Functie actief + + + The feature "%1" is still active. Please stop it before closing %2. + Het programma %1 is nog steeds actief. Stop het alvorens %2 af te sluiten. + + + Reset configuration + Reset configuratie + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Wilt u echt de lokale configuratie resetten en alle instellingen terugzetten naar hun standaardinstellingen? + + + Search users and computers + Zoek gebruikers en computers + + + Align computers to grid + Computers uitlijnen op rooster + + + %1 Configurator + %1 Configurator + + + Insufficient privileges + Onvoldoende toegangsrechten + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + Toon alleen de ingeschakelde computers + + + &Save settings to file + &Instellingen opslaan naar bestand + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Mappen + + + User configuration + Gebruikerconfiguratie + + + Feature on computer double click: + Functie op de computer bij double click: + + + Features + Kenmerken + + + All features + Alle kenmerken + + + Disabled features + Uitgeschakelde functies + + + Screenshots + Schermafbeeldingen + + + <no feature> + <no feature> + + + Basic settings + Basisinstellingen + + + Behaviour + Gedrag + + + Enforce selected mode for client computers + Forceer de geselecteerde mode voor client computers + + + Hide local computer + Verberg lokale computer + + + Hide computer filter field + Verberg computerfilter + + + Actions such as rebooting or powering down computers + Acties zoals herstarten of uitschakelen van computers + + + User interface + Gebruikersomgeving + + + Background color + Achtergrond kleur + + + Thumbnail update interval + Miniatuurweergave update interval + + + ms + ms + + + Program start + Start programma + + + Modes and features + + + + User and computer name + Gebruiker en computer naam + + + Only user name + Alleen gebruikersnaam + + + Only computer name + Alleen computernaam + + + Computer thumbnail caption + Bijschrift van de computerminiatuur + + + Text color + Tekstkleur + + + Sort order + + + + Computer and user name + + + + Computer locations + Computerlocaties + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + Auto + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + Toezicht + + + Builtin monitoring mode + Ingebouwde monitoring modus + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + Open website + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + Vul de URL in van de website die u wilt openen: + + + Name: + + + + + PasswordDialog + + Username + Gebruikersnaam + + + Password + Wachtwoord + + + Veyon Logon + Veyon inloggen + + + Authentication error + Authenticatie fout + + + Logon failed with given username and password. Please try again! + Inloggen mislukt met de opgegeven gebruikersnaam en wachtwoord. Probeer het opnieuw! + + + Please enter your username and password in order to access computers. + Geef uw gebruikersnaam en paswoord in om toegang tot de computers te krijgen. + + + + PowerControlFeaturePlugin + + Power on + Aanzetten + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Klik op deze knop om alle computers aan te zetten. Op deze manier hoef je niet elke computer handmatig aan te zetten. + + + Reboot + Herstarten + + + Click this button to reboot all computers. + Klik op deze knop om alle computer te herstarten. + + + Power down + Uitzetten + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Klik op deze knop om alle computers uit te schakelen. Op deze manier hoeft u niet elke computer apart manueel uit te schakelen. + + + Power on/down or reboot a computer + Computer aan-/uitzetten of herstarten + + + Confirm reboot + Bevestig heropstart + + + Confirm power down + Bevestig uischakelen + + + Do you really want to reboot the selected computers? + Wilt u werkelijk de geselecteerde computers heropstarten? + + + Do you really want to power down the selected computer? + Wilt u werkelijk de geselecteerde computers uitschakelen? + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + MAC ADRES + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + + + + Invalid MAC address specified! + Ongeldig MAC adres! + + + Commands for controlling power status of computers + + + + Power down now + Schakel nu uit + + + Install updates and power down + Installeer updates en schakel uit + + + Power down after user confirmation + + + + Power down after timeout + Schakel uit na timeout + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + Uitzetten + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + Meekijken op afstand + + + Open a remote view for a computer without interaction. + Open een remote view zonder interactie voor een computer. + + + Remote control + Overnemen op afstand + + + Open a remote control window for a computer. + Open een remote control venster voor een computer. + + + Remote access + Toegang op afstand + + + Remote view or control a computer + Meekijken of overnemen op afstand + + + Please enter the hostname or IP address of the computer to access: + Voer de hostnaam of het IP-adres van de computer in waartoe u toegang wil: + + + Show help about command + Toon hulp over commando + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 - %2 Externe toegang + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + Alleen bekijken + + + Remote control + Overnemen op afstand + + + Send shortcut + Stuur snelkoppeling + + + Fullscreen + Volledig scherm + + + Window + Venster + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Menu + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Verbinden met %1 + + + Connected. + Verbonden. + + + Screenshot + Schermafbeelding + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Voer alsjeblieft de programma's of commando's in om op de geselecteerde computer(s) te draaien. U kunt meerdere programma's / opdrachten per lijn scheiden. + + + Run programs + Voer programma's uit + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + bv. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + Vergrendelen + + + Unlock + Ontgrendelen + + + Lock screen and input devices of a computer + Blokkeer scherm en invoerapparaten van een computer + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Om de volledige aandacht van alle gebruikers te eisen, kunt u hun computers vergrendelen met deze knop. In deze modus zijn alle invoerapparaten vergrendeld en de schermen zijn zwart. + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + onbekend + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + Kon geen schermafbeelding maken omdat de map %1 niet bestaat en niet gemaakt kon worden. + + + Screenshot + Schermafbeelding + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + Schermafbeelding + + + Use this function to take a screenshot of selected computers. + Gebruik deze functie als je een schermafbeelding van de geselecteerde computers wilt maken. + + + Screenshots taken + Schermafbeeldingen gemaakt + + + Screenshot of %1 computer have been taken successfully. + Schermafbeelding van %1 computer is gemaakt. + + + Take screenshots of computers and save them locally. + Maak schermafbeeldingen van computers en sla ze lokaal op. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Alle screenshots die door u zijn gemaakt, worden hier vermeld. U kunt screenshots maken door op het "Screenshot" item in het contextmenu van een computer te klikken. De screenshots kunnen worden beheerd met behulp van onderstaande knoppen. + + + User: + Gebruiker: + + + Computer: + Computer: + + + Date: + Datum: + + + Time: + Tijd: + + + Show + Toon + + + Delete + Verwijder + + + Screenshot + Schermafbeelding + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Algemeen + + + Autostart + Automatische start + + + Hide tray icon + Pictogram in het systeemvak verbergen + + + Start service + Start service + + + Stopped + Gestopt + + + Stop service + Stop service + + + State: + Staat: + + + Enable firewall exception + Schakel firewall uitzondering in + + + Allow connections from localhost only + Sta alleen verbindingen van localhost toe + + + VNC server + VNC server + + + Plugin: + Plugin: + + + Restart %1 Service + Herstart %1 Service + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Alle instellingen zijn goed opgeslagen. Om de instellingen van kracht te laten worden moet de %1 service dienst opnieuw worden opgestart. Wil je deze nu herstarten? + + + Running + Gestart + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + Toon melding bij externe verbinding + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + Demo server + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + Service %1 wordt gestart + + + Stopping service %1 + Service %1 wordt gestopt + + + Registering service %1 + Service %1 wordt geregistreerd + + + Unregistering service %1 + + + + Service control + + + + + ServiceControlPlugin + + Service is running + Service is actief + + + Service is not running + Service is niet actief + + + Configure and control Veyon service + Configureer en beheer Veyon service + + + Register Veyon Service + Registreer Veyon Service + + + Unregister Veyon Service + Onregistreer Veyon Service + + + Start Veyon Service + Start Veyon Service + + + Stop Veyon Service + Stop Veyon Service + + + Restart Veyon Service + Herstart Veyon Service + + + Query status of Veyon Service + Bevraag status van Veyon Service + + + Commands for configuring and controlling Veyon Service + Commando's voor het configureren en besturen van Veyon Service + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + Bestand "%1" bestaat niet! + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + Systeemvak icoon + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + Stuur tekstbericht + + + Use the field below to type your message which will be sent to all selected users. + Gebruik de onderstaande veld om uw bericht dat voor alle geselecteerde gebruikers wordt verzonden te typen. + + + + TextMessageFeaturePlugin + + Text message + Tekstbericht + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Gebruik deze functie om een tekstbericht naar alle gebruikers te sturen om bijvoorbeeld hen taken toe te wijzen. + + + Message from teacher + Bericht van de leraar + + + Send a message to a user + Stuur een bericht naar een gebruiker + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Inschakelen capturing van gelaagde (semi-transparante) schermen + + + Poll full screen (leave this enabled per default) + Start het volledige scherm (laat dit standaard ingeschakeld) + + + Low accuracy (turbo mode) + Lage nauwkeurigheid (turbo-modus) + + + Builtin UltraVNC server configuration + Ingebouwde UltraVNC server configuratie + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + Geen schrijftoegang + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Kon uw persoonlijke instellingen niet opslaan! Controleer het gebruikersconfiguratiebestandspad door gebruik te maken van de %1 Configurator. + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + Gebruikersnaam + + + Password + Wachtwoord + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + Gebruikers sessie controle + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [FAIL] + + + Invalid command! + Ongeldig commando! + + + Available commands: + Beschikbare commando's: + + + Invalid arguments given + Ongeldige argumenten gegeven + + + Not enough arguments given - use "%1 help" for more information + Niet genoeg argumenten opgegeven - gebruik "%1 help" voor meer informatie + + + Unknown result! + Onbekend resultaat! + + + Available modules: + Beschikbare modules: + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + + + + INFO + + + + ERROR + + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + Veyon Service + + + + VncViewWidget + + Establishing connection to %1 ... + Verbinding tot stand brengen naar %1 ... + + + + WebApiConfigurationPage + + Web API + + + + General + Algemeen + + + Network port + Netwerkpoort + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + s + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + Kon de instelling voor SAS generatie door software niet wijzigen. Het versturen van Ctrl + Alt + Del via bediening op afstand zal niet werken! + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + Algemeen + + + Enable SAS generation by software (Ctrl+Alt+Del) + Zet SAS generatie door software aan (Ctrl + Alt + Del) + + + Screen lock + + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Uitvoering plugin abstracte functies voor Windows + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Ingebouwde x11vnc server configuratie + + + Custom x11vnc parameters: + Aangepaste x11vnc parameters: + + + Do not use X Damage extension + Gebruik geen X Damage extensie + + + diff --git a/translations/veyon_no_NO.ts b/translations/veyon_no_NO.ts new file mode 100644 index 0000000..6c5703b --- /dev/null +++ b/translations/veyon_no_NO.ts @@ -0,0 +1,4056 @@ + + + + + AboutDialog + + About + Om + + + Translation + Oversettelse + + + License + Lisens + + + About Veyon + Om Veyon + + + Contributors + + + + Version: + Versjon: + + + Website: + + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + + + + About %1 %2 + Om %1 %2 + + + Support Veyon project with a donation + + + + + AccessControlPage + + Computer access control + + + + Grant access to every authenticated user (default) + + + + Test + Test + + + Process access control rules + + + + User groups authorized for computer access + + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + + + + Authorized user groups + + + + All groups + Alle grupper + + + Access control rules + + + + Add access control rule + + + + Remove access control rule + + + + Move selected rule down + + + + Move selected rule up + + + + Edit selected rule + + + + Enter username + + + + Please enter a user login name whose access permissions to test: + + + + Access allowed + + + + The specified user is allowed to access computers with this configuration. + + + + Access denied + + + + The specified user is not allowed to access computers with this configuration. + + + + Enable usage of domain groups + + + + User groups backend: + + + + Missing user groups backend + + + + No default user groups plugin was found. Please check your installation! + + + + Restrict access to members of specific user groups + + + + + AccessControlRuleEditDialog + + Edit access control rule + + + + General + Generelt + + + enter a short name for the rule here + + + + Rule name: + + + + enter a description for the rule here + + + + Rule description: + + + + Invert all conditions ("is/has" interpreted as "is/has not") + + + + Conditions + + + + is member of group + + + + Accessing computer is localhost + + + + Accessing user is logged on user + + + + Accessing user is already connected + + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + + + + Action + + + + Allow access + Tillat tilgang + + + Deny access + + + + Ask logged on user for permission + + + + None (rule disabled) + + + + Accessing user + + + + Accessing computer + + + + Local (logged on) user + + + + Local computer + + + + Always process rule and ignore conditions + + + + No user logged on + Ingen bruker pålogget + + + Accessing user has one or more groups in common with local (logged on) user + + + + Accessing computer and local computer are at the same location + + + + is located at + + + + + AccessControlRulesTestDialog + + Access control rules test + + + + Accessing user: + + + + Local computer: + + + + Accessing computer: + + + + Please enter the following user and computer information in order to test the configured ruleset. + + + + Local user: + Lokal bruker: + + + Connected users: + + + + The access in the given scenario is allowed. + + + + The access in the given scenario is denied. + + + + The access in the given scenario needs permission of the logged on user. + + + + ERROR: Unknown action + + + + Test result + + + + + AuthKeysConfigurationPage + + Authentication keys + + + + Introduction + + + + Key file directories + + + + Public key file base directory + + + + Private key file base directory + + + + Available authentication keys + + + + Create key pair + + + + Delete key + + + + Import key + + + + Export key + + + + Set access group + + + + Key files (*.pem) + + + + Authentication key name + + + + Please enter the name of the user group or role for which to create an authentication key pair: + + + + Do you really want to delete authentication key "%1/%2"? + + + + Please select a key to delete! + + + + Please enter the name of the user group or role for which to import the authentication key: + + + + Please select a key to export! + + + + Please select a user group which to grant access to key "%1": + + + + Please select a key which to set the access group for! + + + + Please perform the following steps to set up key file authentication: + + + + 1) Create a key pair on the master computer. + + + + 2) Set an access group whose members should be allowed to access other computers. + + + + 3) Export the public key and import it on all client computers with the same name. + + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + + + + + AuthKeysManager + + Please check your permissions. + + + + Key name contains invalid characters! + + + + Invalid key type specified! Please specify "%1" or "%2". + + + + Specified key does not exist! Please use the "list" command to list all installed keys. + + + + One or more key files already exist! Please delete them using the "delete" command. + + + + Creating new key pair for "%1" + + + + Failed to create public or private key! + + + + Newly created key pair has been saved to "%1" and "%2". + + + + Could not remove key file "%1"! + + + + Could not remove key file directory "%1"! + + + + Failed to create directory for output file. + + + + File "%1" already exists. + + + + Failed to write output file. + + + + Key "%1/%2" has been exported to "%3" successfully. + + + + Failed read input file. + + + + File "%1" does not contain a valid private key! + + + + File "%1" does not contain a valid public key! + + + + Failed to create directory for key file. + + + + Failed to write key file "%1". + + + + Failed to set permissions for key file "%1"! + + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + + + + Failed to convert private key to public key + + + + Failed to create directory for private key file "%1". + + + + Failed to save private key in file "%1"! + + + + Failed to set permissions for private key file "%1"! + + + + Failed to create directory for public key file "%1". + + + + Failed to save public key in file "%1"! + + + + Failed to set permissions for public key file "%1"! + + + + Failed to set owner of key file "%1" to "%2". + + + + Failed to set permissions for key file "%1". + + + + Key "%1" is now accessible by user group "%2". + + + + <N/A> + + + + Failed to read key file. + + + + + AuthKeysPlugin + + Create new authentication key pair + + + + Delete authentication key + + + + List authentication keys + + + + Import public or private key + + + + Export public or private key + + + + Extract public key from existing private key + + + + Set user group allowed to access a key + + + + KEY + + + + ACCESS GROUP + + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + + NAME + + + + FILE + + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + + Please specify the command to display help for! + + + + TYPE + + + + PAIR ID + + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + Navn + + + Type + + + + Access group + + + + Pair ID + + + + + BuiltinDirectoryConfigurationPage + + Computers + Datamaskiner + + + Name + Navn + + + Host address/IP + + + + MAC address + + + + Add new computer + + + + Remove selected computer + + + + New computer + + + + Builtin directory + + + + Locations & computers + + + + Locations + + + + Add new location + + + + Remove selected location + + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + + + + + BuiltinDirectoryPlugin + + Show help for specific command + + + + Import objects from given file + + + + Export objects to given file + + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + + + + Name + Navn + + + Host address + + + + MAC address + + + + Specified object not found. + + + + File "%1" does not exist! + + + + Can't open file "%1" for reading! + + + + Unknown argument "%1". + + + + Computer "%1" (host address: "%2" MAC address: "%3") + + + + Unclassified object "%1" with ID "%2" + + + + None + + + + Computer + + + + Root + + + + Invalid + + + + Error while parsing line %1. + + + + Network object directory which stores objects in local configuration + + + + Commands for managing the builtin network object directory + + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + + + + Parent UUID + + + + Add a location or computer + + + + Clear all locations and computers + + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + + + + FILE + + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + + + + NAME + + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Innebygget VNC-server (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Innebygget VNC-server (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + + + + Active features: %1 + + + + Online and connected + + + + Establishing connection + + + + Computer offline or switched off + + + + Authentication failed or access denied + + + + Disconnected + Frakoblet + + + No user logged on + Ingen bruker pålogget + + + Logged on user: %1 + Pålogget bruker: %1 + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + + + + Authentication error + + + + Remote access + + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + Bruker + + + Missing network object directory plugin + + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + + + + Add location + + + + Save computer/user list + + + + Select output filename + + + + CSV files (*.csv) + CSV filer (*.csv) + + + File error + + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + + + + Please specify a valid filename for the configuration export. + + + + Please specify a valid key. + + + + Specified key does not exist in current configuration! + + + + Please specify a valid value. + + + + Configure Veyon at command line + + + + Output file is not writable! + + + + Output directory is not writable! + + + + Configuration file is not readable! + + + + Clear system-wide Veyon configuration + + + + List all configuration keys and values + + + + Import configuration from given file + + + + Export configuration to given file + + + + Read and output configuration value for given key + + + + Write given value to given configuration key + + + + Unset (remove) given configuration key + + + + Commands for managing the configuration of Veyon + + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + %1 Demo + + + + DemoConfigurationPage + + Demo server + Demo server + + + Tunables + + + + ms + ms + + + Key frame interval + + + + Memory limit + + + + MB + MB + + + Update interval + + + + s + s + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + Stopp demo + + + Window demo + + + + Give a demonstration by screen broadcasting + + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + + + + Confirm desktop access + + + + Never for this session + + + + Always for this session + + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + + DesktopServicesConfigurationPage + + Programs & websites + + + + Predefined programs + + + + Name + Navn + + + Path + + + + Add new program + + + + Remove selected program + + + + Predefined websites + + + + Remove selected website + + + + URL + + + + New program + + + + New website + + + + + DesktopServicesFeaturePlugin + + Run program + + + + Open website + + + + Click this button to open a website on all computers. + Klikk denne knappen for å åpne nettsiden på alle datamaskinene. + + + Start programs and services in user desktop + + + + Click this button to run a program on all computers. + + + + Run program "%1" + + + + Custom program + + + + Open website "%1" + + + + Custom website + + + + + DocumentationFigureCreator + + Teacher + Lærer + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + + + + Port: + Port: + + + Password: + Passord: + + + + FeatureControl + + Feature control + + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + + + + Destination directory + + + + Default source directory + + + + Options + + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + + + + Select one or more files to transfer + + + + Transfer files to remote computer + + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + + + + Language: + Språk: + + + Use system language setting + + + + Veyon + Veyon + + + Logging + + + + Log file directory + + + + Log level + + + + Nothing + + + + Only critical messages + + + + Errors and critical messages + + + + Warnings and errors + + + + Information, warnings and errors + + + + Debug messages and everything else + + + + Limit log file size + + + + Clear all log files + + + + Log to standard error output + + + + Network object directory + + + + Backend: + + + + Update interval: + + + + %1 service + + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + + + + All log files were cleared successfully. + + + + Error + + + + Could not remove all log files. + + + + MB + MB + + + Rotate log files + + + + x + x + + + seconds + sekunder + + + Write to logging system of operating system + + + + Authentication + + + + Method: + + + + Logon authentication + + + + Key file authentication + + + + Test + Test + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + + + + + LdapConfigurationPage + + Basic settings + + + + General + Generelt + + + LDAP server and port + + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + + + + e.g. OU=Computers + + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + + LDAP base DN test failed + + + + LDAP base DN test successful + + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + + + + Please enter a group name whose members to query: + + + + group members + + + + Group not found + + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + + + + users + Brukere + + + user groups + + + + computer groups + + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + datamaskiner + + + LDAP %1 test failed + + + + LDAP %1 test successful + + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + + + + TLS certificate verification + + + + System defaults + + + + Never (insecure!) + + + + Custom CA certificate file + + + + None + + + + TLS + + + + SSL + + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + Test + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + + + + enter search filter... + + + + + MainToolBar + + Configuration + + + + Disable balloon tooltips + + + + Show icons only + + + + + MainWindow + + MainWindow + + + + toolBar + + + + General + Generelt + + + &File + &Fil + + + &Help + &Hjelp + + + &Quit + &Avslutt + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + + + + Ctrl+O + Ctrl+O + + + About Qt + + + + Authentication impossible + + + + Configuration not writable + + + + Load settings from file + + + + Save settings to file + + + + Unsaved settings + + + + There are unsaved settings. Quit anyway? + + + + Veyon Configurator + + + + Service + + + + Master + + + + Access control + + + + About Veyon + Om Veyon + + + Auto + Auto + + + About + Om + + + %1 Configurator %2 + + + + JSON files (*.json) + JSON filer (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + Skjermbilder + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + + + + Align computers to grid + + + + %1 Configurator + + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + + + + User configuration + + + + Feature on computer double click: + + + + Features + + + + All features + + + + Disabled features + + + + Screenshots + Skjermbilder + + + <no feature> + + + + Basic settings + + + + Behaviour + + + + Enforce selected mode for client computers + + + + Hide local computer + + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + + + + User interface + + + + Background color + + + + Thumbnail update interval + + + + ms + ms + + + Program start + + + + Modes and features + + + + User and computer name + + + + Only user name + + + + Only computer name + + + + Computer thumbnail caption + + + + Text color + + + + Sort order + + + + Computer and user name + + + + Computer locations + + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + Auto + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + + + + Name: + + + + + PasswordDialog + + Username + + + + Password + + + + Veyon Logon + + + + Authentication error + + + + Logon failed with given username and password. Please try again! + + + + Please enter your username and password in order to access computers. + + + + + PowerControlFeaturePlugin + + Power on + + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + + Reboot + + + + Click this button to reboot all computers. + + + + Power down + + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + + + + Confirm reboot + + + + Confirm power down + + + + Do you really want to reboot the selected computers? + + + + Do you really want to power down the selected computer? + + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + + + + Open a remote view for a computer without interaction. + + + + Remote control + + + + Open a remote control window for a computer. + + + + Remote access + + + + Remote view or control a computer + + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + + + + Remote control + + + + Send shortcut + + + + Fullscreen + + + + Window + + + + Ctrl+Alt+Del + + + + Ctrl+Esc + + + + Alt+Tab + + + + Alt+F4 + + + + Win+Tab + + + + Win + + + + Menu + + + + Alt+Ctrl+F1 + + + + Connecting %1 + + + + Connected. + + + + Screenshot + + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + + + + Unlock + + + + Lock screen and input devices of a computer + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + + + + Use this function to take a screenshot of selected computers. + + + + Screenshots taken + + + + Screenshot of %1 computer have been taken successfully. + + + + Take screenshots of computers and save them locally. + + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + + + + Computer: + + + + Date: + + + + Time: + + + + Show + + + + Delete + + + + Screenshot + + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Generelt + + + Autostart + + + + Hide tray icon + + + + Start service + + + + Stopped + + + + Stop service + + + + State: + + + + Enable firewall exception + + + + Allow connections from localhost only + + + + VNC server + + + + Plugin: + + + + Restart %1 Service + + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + + Running + + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + Demo server + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + + + + Stopping service %1 + + + + Registering service %1 + + + + Unregistering service %1 + + + + Service control + + + + + ServiceControlPlugin + + Service is running + + + + Service is not running + + + + Configure and control Veyon service + + + + Register Veyon Service + + + + Unregister Veyon Service + + + + Start Veyon Service + + + + Stop Veyon Service + + + + Restart Veyon Service + + + + Query status of Veyon Service + + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + + + + Use the field below to type your message which will be sent to all selected users. + + + + + TextMessageFeaturePlugin + + Text message + + + + Use this function to send a text message to all users e.g. to assign them new tasks. + + + + Message from teacher + + + + Send a message to a user + + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + + + + Poll full screen (leave this enabled per default) + + + + Low accuracy (turbo mode) + + + + Builtin UltraVNC server configuration + + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + + + + Password + + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + + + + + VeyonCore + + [OK] + + + + [FAIL] + + + + Invalid command! + + + + Available commands: + + + + Invalid arguments given + + + + Not enough arguments given - use "%1 help" for more information + + + + Unknown result! + + + + Available modules: + + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + + + + INFO + + + + ERROR + + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + + + + + VncViewWidget + + Establishing connection to %1 ... + + + + + WebApiConfigurationPage + + Web API + + + + General + Generelt + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + s + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + Generelt + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + + + + Custom x11vnc parameters: + + + + Do not use X Damage extension + + + + diff --git a/translations/veyon_pl.ts b/translations/veyon_pl.ts new file mode 100644 index 0000000..0112313 --- /dev/null +++ b/translations/veyon_pl.ts @@ -0,0 +1,4078 @@ + + + + + AboutDialog + + About + O programie + + + Translation + Tłumaczenie + + + License + Licencja + + + About Veyon + O programie Veyon + + + Contributors + Współpraca + + + Version: + Wersja: + + + Website: + Strona WWW: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Bieżący język nie jest jeszcze do końca przetłumaczony. + +Jeżeli chcesz pomóc w tłumaczeniu programu lub chcesz poprawić istniejące tłumaczenie, skontaktuj się z twórcami Veyon. + + + About %1 %2 + O %1 %2 + + + Support Veyon project with a donation + Wesprzyj projekt Veyon za pomocą dotacji + + + + AccessControlPage + + Computer access control + Kontrola dostępu do komputerów + + + Grant access to every authenticated user (default) + Zezwól na dostęp wszystkim uwierzytelnionym użytkownikom (domyślne) + + + Test + Test + + + Process access control rules + Użyj reguł dostępu + + + User groups authorized for computer access + Grupy użytkowników z autoryzacją dostępu do komputerów + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Dodaj grupy użytkowników z autoryzacją dostępu do komputerów w twojej sieci Veyon. + + + Authorized user groups + Zautoryzowane grupy użytkowników + + + All groups + Wszystkie grupy + + + Access control rules + Reguły dostępu + + + Add access control rule + Dodaj regułę dostępu + + + Remove access control rule + Usuń regułę dostępu + + + Move selected rule down + Przesuń regułę niżej + + + Move selected rule up + Przesuń regułę wyżej + + + Edit selected rule + Edytuj wybraną regułę + + + Enter username + Wprowadź nazwę użytkownika + + + Please enter a user login name whose access permissions to test: + Wprowadź nazwę użytkownika, którego uprawnienia chcesz sprawdzić: + + + Access allowed + Dostęp zabroniony + + + The specified user is allowed to access computers with this configuration. + Użytkownik ma dostęp do komputerów przy tej konfiguracji. + + + Access denied + Odmowa dostępu + + + The specified user is not allowed to access computers with this configuration. + Użytkownik nie ma dostępu do komputerów przy tej konfiguracji. + + + Enable usage of domain groups + Włącz użycie grup domenowych + + + User groups backend: + Źródło grup użytkowników: + + + Missing user groups backend + Nieskonfigurowane źródło grup użytkowników + + + No default user groups plugin was found. Please check your installation! + Brak domyślnej wtyczki źródła grup użytkowników. Proszę sprawdzić poprawność instalacji programu! + + + Restrict access to members of specific user groups + Ogranicz dostęp do członków wybranych grup + + + + AccessControlRuleEditDialog + + Edit access control rule + Edytuj reguły dostępu + + + General + Ogólne + + + enter a short name for the rule here + Wprowadź tutaj skróconą nazwę reguły. + + + Rule name: + Nazwa reguły: + + + enter a description for the rule here + wprowadź opis reguły + + + Rule description: + Opis reguły: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Odwróć wszystkie warunki („jest / ma” interpretowane jako „nie jest / nie ma”) + + + Conditions + Warunki + + + is member of group + jest członkiem grupy + + + Accessing computer is localhost + Łączący się komputer to localhost + + + Accessing user is logged on user + Logujący się użytkownik jest już zalogowany + + + Accessing user is already connected + Logujący się użytkownik jest już połączony + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + eśli aktywowany jest więcej niż jeden warunek, każdy warunek musi zostać spełniony, aby reguła miała zastosowanie (logiczne AND). Jeśli tylko jeden z wielu warunków musi zostać spełniony (logiczne OR), proszę utwórz wiele reguł kontroli dostępu. + + + Action + Akcja + + + Allow access + Zezwól na dostęp. + + + Deny access + Odmów dostępu. + + + Ask logged on user for permission + Poproś zalogowanego użytkownika o dostęp + + + None (rule disabled) + Brak (reguła nieaktywna) + + + Accessing user + Logujący się użytkownik + + + Accessing computer + Łączący się komputer + + + Local (logged on) user + Użytkownik lokalny (zalogowany) + + + Local computer + Komputer lokalny + + + Always process rule and ignore conditions + Zawsze stosuj regułę i ignoruj warunki + + + No user logged on + Brak zalogowanych użytkowników. + + + Accessing user has one or more groups in common with local (logged on) user + Logujący się użytkownik ma jedną lub więcej grup wspólnych z użytkownikiem lokalnym (zalogowanym) + + + Accessing computer and local computer are at the same location + Logujący się komputer i lokalny komputer są w tej samej lokalizacji + + + is located at + jest w + + + + AccessControlRulesTestDialog + + Access control rules test + Test reguł dostępu + + + Accessing user: + Logujący się użytkownik: + + + Local computer: + Komputer lokalny: + + + Accessing computer: + Łączący się komputer: + + + Please enter the following user and computer information in order to test the configured ruleset. + Wprowadź następujące informacje o użytkowniku i komputerze, aby przetestować skonfigurowany zestaw reguł. + + + Local user: + Użytkownik lokalny: + + + Connected users: + Połączeni użytkownicy: + + + The access in the given scenario is allowed. + Dostęp w danym scenariuszu jest dozwolony. + + + The access in the given scenario is denied. + Odmowa dostępu w danym scenariuszu. + + + The access in the given scenario needs permission of the logged on user. + Dostęp w danym scenariuszu wymaga zgody zalogowanego użytkownika. + + + ERROR: Unknown action + Błąd: nieznana akcja. + + + Test result + Wynik testu + + + + AuthKeysConfigurationPage + + Authentication keys + Klucze uwierzytelniające + + + Introduction + Wprowadzenie + + + Key file directories + Lokalizacja plików kluczy + + + Public key file base directory + Katalog z plikami kluczy publicznych + + + Private key file base directory + Katalog z plikami kluczy prywatnych + + + Available authentication keys + Dostępne klucze uwierzytelniające + + + Create key pair + Stwórz parę kluczy + + + Delete key + Usuń klucz + + + Import key + Importuj klucz + + + Export key + Eksportuj klucz + + + Set access group + Ustaw grupę dostępu + + + Key files (*.pem) + Pliki kluczy (*.pem) + + + Authentication key name + Nazwa klucza uwierzytelniającego + + + Please enter the name of the user group or role for which to create an authentication key pair: + Wprowadź nazwę grupy użytkowników lub roli, dla której chcesz utworzyć parę kluczy uwierzytelniających: + + + Do you really want to delete authentication key "%1/%2"? + Czy na pewno chcesz usunąć klucz uwierzytelniający "%1/%2"? + + + Please select a key to delete! + Wybierz klucz do usunięcia! + + + Please enter the name of the user group or role for which to import the authentication key: + Wprowadź nazwę grupy użytkowników lub roli, dla której chcesz zaimportować klucz uwierzytelniający: + + + Please select a key to export! + Wybierz klucz do wyeksportowania! + + + Please select a user group which to grant access to key "%1": + Wybierz grupę użytkowników której chcesz przydzielić dostęp do klucza %1. + + + Please select a key which to set the access group for! + Wybierz klucz do którego chcesz przydzielić dostęp dla grupy! + + + Please perform the following steps to set up key file authentication: + Wykonaj poniższe kroki w celu ustawienia pliku klucza uwierzytelniającego: + + + 1) Create a key pair on the master computer. + )1 Stwórz parę kluczy uwierzytelniających na głównym komputerze. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Utwórz grupę użytkowników którzy mają mieć dostęp do innych komputerów. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Wyeksportuj klucz publiczny i zaimportuj go na wszystkich komputerach klienckich z taką samą nazwą. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Więcej informacji można znaleźć w <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Podręczniku administratora Veyon</a>. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Para kluczy uwierzytelniających składa się z dwóch kluczy kryptograficznych, prywatnego i publicznego. +Klucz prywatny umożliwia użytkownikom komputera głównego dostęp do komputerów klienta. +Ważne jest, aby jedynie autoryzowani użytkownicy mogli odczytać plik klucza prywatnego. +Klucz publiczny jest używany na komputerach klienta do uwierzytelnienia połączeń przychodzących. + + + + AuthKeysManager + + Please check your permissions. + Sprawdź swoje uprawnienia. + + + Key name contains invalid characters! + Nazwa klucza zawiera niedozwolone znaki! + + + Invalid key type specified! Please specify "%1" or "%2". + Niepoprawny typ klucza! Proszę podać "%1" lub "%2". + + + Specified key does not exist! Please use the "list" command to list all installed keys. + Wybrany klucz nie istnieje! Użyj komendy "list" aby wyświetlić wszystkie zainstalowane klucze. + + + One or more key files already exist! Please delete them using the "delete" command. + Jeden lub więcej plików kluczy już istnieje! Usuń je za pomocą polecenia „usuń”. + + + Creating new key pair for "%1" + Stwórz nową parę kluczy uwierzytelniających dla "%1" + + + Failed to create public or private key! + Błąd podczas tworzenia klucza! + + + Newly created key pair has been saved to "%1" and "%2". + Nowa para kluczy uwierzytelniających została zapisana do "%1" i "%2". + + + Could not remove key file "%1"! + Nie można usunąć pliku klucza „%1”! + + + Could not remove key file directory "%1"! + Nie można usunąć katalogu plików kluczy „%1”! + + + Failed to create directory for output file. + Nie udało się utworzyć katalogu dla pliku wyjściowego. + + + File "%1" already exists. + Plik "%1" istnieje. + + + Failed to write output file. + Nie udało się zapisać pliku wyjściowego. + + + Key "%1/%2" has been exported to "%3" successfully. + Klucz „%1 /%2” został pomyślnie wyeksportowany do „%3”. + + + Failed read input file. + Nie można odczytać pliku wejściowego. + + + File "%1" does not contain a valid private key! + Plik „%1” nie zawiera poprawnego klucza prywatnego! + + + File "%1" does not contain a valid public key! + Plik „%1” nie zawiera poprawnego klucza publicznego! + + + Failed to create directory for key file. + Nie można utworzyć folderu dla pliku klucza. + + + Failed to write key file "%1". + Nie można zapisać pliku klucza %1. + + + Failed to set permissions for key file "%1"! + Wystąpił błąd podczas ustawiania uprawnień dla pliku klucza %1! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + Klucz %1/%2 został zaimportowany poprawnie. Sprawdź uprawnienia pliku %3 w celu uniknięcia niepowołanego dostępu. + + + Failed to convert private key to public key + Błąd podczas konwertowania klucza prywatnego do klucza publicznego + + + Failed to create directory for private key file "%1". + Nie można stworzyć folderu dla pliku klucza prywatnego "%1". + + + Failed to save private key in file "%1"! + Błąd podczas zapisywania klucza prywatnego w pliku "%1"! + + + Failed to set permissions for private key file "%1"! + Nie można ustawić uprawnień dla pliku klucza prywatnego "%1"! + + + Failed to create directory for public key file "%1". + Nie można stworzyć folderu dla pliku klucza publicznego "%1". + + + Failed to save public key in file "%1"! + Błąd podczas zapisywania klucza publicznego w pliku "%1"! + + + Failed to set permissions for public key file "%1"! + Nie można ustawić uprawnień dla pliku klucza publicznego "%1"! + + + Failed to set owner of key file "%1" to "%2". + Nie można zamienić właściciela pliku kluczy „%1” na „%2”. + + + Failed to set permissions for key file "%1". + Nie można ustawić uprawnień dla pliku klucza "%1". + + + Key "%1" is now accessible by user group "%2". + Klucz "%1" jest teraz dostępny dla grupy użytkowników "%2". + + + <N/A> + Brak danych + + + Failed to read key file. + Nie można odczytać pliku klucza. + + + + AuthKeysPlugin + + Create new authentication key pair + Stwórz nową parę kluczy uwierzytelniających + + + Delete authentication key + Usuń klucz uwierzytelniający + + + List authentication keys + Lista kluczy uwierzytelniających + + + Import public or private key + Importuj klucz publiczny/prywatny + + + Export public or private key + Eksportuj klucz publiczny/prywatny + + + Extract public key from existing private key + Utwórz klucz publiczny z istniejącego klucza prywatnego + + + Set user group allowed to access a key + Wskaż grupę użytkowników która może uzyskać dostęp do klucza + + + KEY + KLUCZ + + + ACCESS GROUP + GRUPA DOSTĘPU + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + To polecenie dostosowuje uprawnienia dostępu do <KEY>w taki sposób, że tylko grupa <ACCESS GROUP> ma do niego dostęp do odczytu. + + + NAME + NAZWA + + + FILE + PLIK + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + To polecenie eksportuje klucz uwierzytelniający <KEY> do <FILE>. Jeśli <FILE>nie zostanie określony, nazwa zostanie utworzona na podstawie nazwy i typu <KEY>. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + To polecenie importuje klucz uwierzytelniający <KEY> z <FILE>. Jeśli <FILE>nie zostanie określony, nazwa <KEY> zostanie utworzona na podstawie nazwy i typu. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + To polecenie wyświetla listę wszystkich dostępnych kluczy uwierzytelniania w katalogu kluczy. Jeśli podano opcję „%1”, zamiast niej zostanie wyświetlona tabela ze szczegółami klucza. Niektórych szczegółów może brakować, jeśli klucz nie jest dostępny, np. z powodu braku uprawnień do odczytu. + + + Please specify the command to display help for! + Podaj polecenie aby wyświetlić pomoc! + + + TYPE + TYP + + + PAIR ID + PAIR ID + + + Command line support for managing authentication keys + Obsługa wiersza poleceń do zarządzania kluczami uwierzytelniającymi + + + Commands for managing authentication keys + Komendy do zarządzania kluczami uwierzytelniającymi + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + To polecenie tworzy nową parę kluczy uwierzytelniających z nazwą <NAME>i zapisuje klucz prywatny i publiczny w skonfigurowanych katalogach kluczy. Parametr musi być nazwą klucza, która może zawierać tylko litery. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + To polecenie usuwa klucz uwierzytelniający <KEY>ze skonfigurowanego katalogu kluczy. Pamiętaj, że klucza nie można odzyskać po jego usunięciu. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + To polecenie wyodrębnia część klucza publicznego z klucza prywatnego <KEY> i zapisuje ją jako odpowiedni klucz publiczny. Dlatego podczas konfigurowania innego komputera głównego wystarczy przesłać tylko klucz prywatny. Następnie można wyodrębnić klucz publiczny. + + + + AuthKeysTableModel + + Name + Nazwa + + + Type + Typ + + + Access group + Grupa dostępu + + + Pair ID + Pair ID + + + + BuiltinDirectoryConfigurationPage + + Computers + Komputery + + + Name + Nazwa + + + Host address/IP + adres hosta/IP + + + MAC address + Adres MAC + + + Add new computer + Dodaj nowy komputer + + + Remove selected computer + Usuń wybrany komputer + + + New computer + Nowy komputer + + + Builtin directory + Wbudowany katalog + + + Locations & computers + Sale i komputery + + + Locations + Sale + + + Add new location + Dodaj nową salę + + + Remove selected location + Usuń wybraną lokalizację + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + Import plików CSV jest możliwy poprzez interfejs wiersza poleceń. Aby uzyskać więcej informacji, zobacz <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">dokumentację online</a>. + + + New location + Nowa lokalizacja + + + + BuiltinDirectoryPlugin + + Show help for specific command + Pokaż pomoc dla wskazanej komendy + + + Import objects from given file + Importuj obiekty ze wskazanego pliku + + + Export objects to given file + Eksportuj obiekty do pliku + + + Invalid type specified. Valid values are "%1" or "%2". + Podano nieprawidłowy typ. Prawidłowe wartości to „%1” lub „%2”. + + + Type + Typ + + + Name + Nazwa + + + Host address + Adres hosta + + + MAC address + Adres MAC + + + Specified object not found. + Nie znaleziono wybranego obiektu. + + + File "%1" does not exist! + Plik "%1" nie istnieje! + + + Can't open file "%1" for reading! + Nie można odczytać pliku "%1"! + + + Unknown argument "%1". + Nieprawidłowy argument "%1". + + + Computer "%1" (host address: "%2" MAC address: "%3") + Komputer "%1" (adres hosta: "%2" adres MAC: "%3") + + + Unclassified object "%1" with ID "%2" + Nieklasyfikowany obiekt „%1” o identyfikatorze „%2" + + + None + Brak + + + Computer + Komputer + + + Root + Administrator + + + Invalid + Nieprawidłowy + + + Error while parsing line %1. + Błąd podczas analizowania linii %1. + + + Network object directory which stores objects in local configuration + Katalog obiektów sieciowych przechowujący obiekty w konfiguracji lokalnej + + + Commands for managing the builtin network object directory + Polecenia do zarządzania wbudowanym katalogiem obiektów sieciowych + + + No format string or regular expression specified! + Nie podano prawidłowego ciągu ani wyrażenia! + + + Can't open file "%1" for writing! + Nie można otworzyć pliku "%1" w celu zapisu! + + + No format string specified! + Nie podano prawidłowego ciągu! + + + Object UUID + UUID Obiektu + + + Parent UUID + Nadrzędny UUID + + + Add a location or computer + Dodaj lokalizację lub komputer + + + Clear all locations and computers + Wyczyść wszystkie lokalizacje i komputery + + + Dump all or individual locations and computers + Zrzuć wszystkie lub pojedyncze lokalizacje i komputery + + + List all locations and computers + Wyświetl wszystkie lokalizacje i komputery + + + Remove a location or computer + Usuń lokalizację lub komputer + + + Location "%1" + Lokalizacja "%1" + + + Builtin (computers and locations in local configuration) + Wbudowane (komputery i lokalizacje w konfiguracji lokalnej) + + + Location + Lokalizacja + + + FILE + PLIK + + + LOCATION + LOKALIZACJA + + + FORMAT-STRING-WITH-PLACEHOLDERS + FORMAT-STRING-WITH-PLACEHOLDERS + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Importuje obiekty z określonego pliku tekstowego przy użyciu podanego ciągu lub wyrażenia zawierającego jeden lub wiele symboli zastępczych. Prawidłowe symbole zastępcze to: %1 + + + Import simple CSV file to a single room + Zaimportuj plik CSV + + + Import CSV file with location name in first column + Zaimportuj plik CSV z nazwą lokalizacji w pierwszej kolumnie + + + Import text file with with key/value pairs using regular expressions + Zaimportuj plik tekstowy z parami klucz / wartość za pomocą wyrażeń regularnych + + + Import arbitrarily formatted data + Importuj dowolnie sformatowane dane + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Eksportuje obiekty do określonego pliku tekstowego przy użyciu ciągu formatu podanego zawierającego jeden lub wiele symboli zastępczych. Prawidłowe symbole zastępcze to: %1 + + + Export all objects to a CSV file + Wyeksportuj wszystkie obiekty do pliku CSV + + + Export all computers in a specific location to a CSV file + Wyeksportuj wszystkie komputery w określonej lokalizacji do pliku CSV + + + TYPE + TYP + + + NAME + NAZWA + + + PARENT + NADRZĘDNY + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + Dodaje obiekt, w którym %1 może być jednym z „%2” lub „%3”. %4 można określić według nazwy lub UUID. + + + Add a room + Dodaj salę + + + Add a computer to room %1 + Dodaj komputer do sali %1 + + + OBJECT + OBIEKT + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Usuwa określony obiekt z katalogu. %1 można określić według nazwy lub UUID. Usunięcie lokalizacji spowoduje również usunięcie wszystkich powiązanych komputerów. + + + Remove a computer by name + Usuń komputer według nazwy + + + Remove an object by UUID + Usuń obiekt za pomocą UUID + + + "Room 01" + "Sala 01" + + + "Computer 01" + "Komputer 01" + + + HOST ADDRESS + ADRES HOSTA + + + MAC ADDRESS + ADRES MAC + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Wbudowany serwer VNC (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Wbudowany serwer VNC (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Host/adres IP: %1 + + + Active features: %1 + Aktywne funkcje: %1 + + + Online and connected + Online i połączony + + + Establishing connection + Nawiązywanie połączenia + + + Computer offline or switched off + Komputer nie połączony z siecią lub wyłączony + + + Authentication failed or access denied + Uwierzytelnienie nie powiodło się lub dostęp jest zabroniony + + + Disconnected + Rozłączono + + + No user logged on + Brak zalogowanych użytkowników. + + + Logged on user: %1 + Zalogowano użytkownika: %1 + + + Location: %1 + Lokalizacja: %1 + + + Veyon Server unreachable or not running + Serwer Veyon nieosiagalny lub nie uruchomiony + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 Usługa %2 w %3:%4 + + + Authentication error + Błąd uwierzytelnienia + + + Remote access + Zdalny dostęp + + + User "%1" at host "%2" is now accessing this computer. + Użytkownik „%1” na hoście „%2” uzyskuje teraz dostęp do tego komputera. + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + Użytkownik „%1” na hoście „%2” próbował uzyskać dostęp do tego komputera, ale nie mógł się pomyślnie uwierzytelnić. + + + Access control error + Błąd dostępu + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + Użytkownik „%1” na hoście „%2” próbował uzyskać dostęp do tego komputera, ale został zablokowany z powodu ustawień kontroli dostępu. + + + Active connections: + Aktywne połączenia: + + + + ComputerManager + + User + Użytkownik + + + Missing network object directory plugin + Brak wtyczki katalogu obiektów sieciowych + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Nie znaleziono domyślnej wtyczki katalogu obiektów sieciowych. Sprawdź swoją instalację lub skonfiguruj inny backend katalogu obiektów sieciowych za pomocą Konfiguratora %1. + + + Location detection failed + Wykrywanie lokalizacji nie powiodło się + + + Computer name;Hostname;User + Nazwa komputera; Nazwa Hosta; Użytkownik + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + Nie można określić lokalizacji tego komputera. Wskazuje to na problem z konfiguracją systemu. W panelu wyboru komputera pokazane zostaną wszystkie lokalizacje. + + + + ComputerSelectPanel + + Computer search + Znajdź komputer + + + Add location + Dodaj lokalizację + + + Save computer/user list + Zapisz listę komputerów/użytkowników + + + Select output filename + Wybierz nazwę pliku wyjściowego + + + CSV files (*.csv) + pliki CSV (*csv) + + + File error + Błąd pliku + + + Could not write the computer and users list to %1! Please check the file access permissions. + Nie można zapisać listy komputerów i użytkowników w %1! Sprawdź prawa dostępu do pliku + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Wskaż istniejący plik konfiguracyjny w celu zaimportowania. + + + Please specify a valid filename for the configuration export. + Wskaż nazwę wyeksportowanego pliku konfiguracyjnego. + + + Please specify a valid key. + Wskaż poprawny klucz + + + Specified key does not exist in current configuration! + Wskazany klucz nie istnieje w bieżącej konfiguracji! + + + Please specify a valid value. + Proszę wpisać prawidłową wartość. + + + Configure Veyon at command line + Konfiguruj Veyon za pomocą wiersza polecenia + + + Output file is not writable! + Plik wyjściowy nie ma uprawnień zapisu! + + + Output directory is not writable! + Lokalizacja wyjściowa nie ma uprawnień zapisu! + + + Configuration file is not readable! + Plik konfiguracyjny nie nadaje się do odczytu! + + + Clear system-wide Veyon configuration + Wyczyść konfigurację programu Veyon + + + List all configuration keys and values + Wyświetl wszystkie klucze konfiguracyjne i wartości + + + Import configuration from given file + Zaimportuj konfiguracje z podanego pliku + + + Export configuration to given file + Wyeksportuj konfiguracje do podanego pliku + + + Read and output configuration value for given key + Odczyt i wyjście wartości konfiguracji dla danego klucza + + + Write given value to given configuration key + Zapisz podaną wartość do podanego klucza konfiguracji + + + Unset (remove) given configuration key + Odznacz (usuń) dany klucz konfiguracji + + + Commands for managing the configuration of Veyon + Komendy do zarządzania konfiguracją programu Veyon + + + Upgrade and save configuration of program and plugins + Aktualizuj i zapisz konfigurację programu i rozszerzeń + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + Nie można zmodyfikować właściwości autostartu dla usługi %1. + + + Could not configure the firewall configuration for the %1 Server. + Nie można ustawić konfiguracji zapory dla serwera %1. + + + Could not configure the firewall configuration for the %1 Worker. + Nie można ustawić konfiguracji zapory sieciowej dla stacji %1. + + + Configuration is not writable. Please check your permissions! + Nie można zapisać konfiguracji. Sprawdź swoje uprawnienia! + + + Could not apply platform-specific configuration settings. + Nie można zastosować ustawień konfiguracji specyficznych dla platformy. + + + + DemoClient + + %1 Demo + Prezentacja %1 + + + + DemoConfigurationPage + + Demo server + Serwer demo + + + Tunables + Dostrajalne + + + ms + ms + + + Key frame interval + Interwał między klatkami + + + Memory limit + Limit pamięci + + + MB + MB + + + Update interval + Interwał odświeżania: + + + s + s + + + Slow down thumbnail updates while demo is running + Spowalniaj aktualizacje miniatur podczas działania wersji demonstracyjnej + + + + DemoFeaturePlugin + + Stop demo + Zakończ demonstrację + + + Window demo + Demonstracja w trybie okienkowym + + + Give a demonstration by screen broadcasting + Transmituj demonstrację na ekran + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + W tym trybie twój ekran jest wyświetlany w oknie na wszystkich komputerach. Użytkownicy mogą się przełączać między oknami. + + + Demo + Demo + + + Share your screen or allow a user to share his screen with other users. + Udostępnij swój ekran lub zezwól użytkownikowi na udostępnienie jego ekranu innym użytkownikom. + + + Full screen demo + Demo pełnoekranowe + + + Share your own screen in fullscreen mode + Udostępnij swój ekran w trybie pełnoekranowym + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + W tym trybie ekran jest wyświetlany w trybie pełnoekranowym na wszystkich komputerach, podczas gdy urządzenia wejściowe użytkowników są zablokowane. + + + Share your own screen in a window + Udostępnij swój ekran w oknie + + + Share selected user's screen in fullscreen mode + Udostępnij ekran wybranego użytkownika w trybie pełnoekranowym + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + W tym trybie ekran wybranego użytkownika jest wyświetlany w trybie pełnoekranowym na wszystkich komputerach, podczas gdy urządzenia wejściowe użytkowników są zablokowane. + + + Share selected user's screen in a window + Udostępnij ekran wybranego użytkownika w oknie + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + W tym trybie ekran wybranego użytkownika jest wyświetlany w oknie na wszystkich komputerach. Użytkownicy mogą w razie potrzeby przełączać się do innych okien. + + + Please select a user screen to share. + Wybierz ekran użytkownika do udostępnienia. + + + Please select only one user screen to share. + Wybierz tylko jeden ekran użytkownika do udostępnienia. + + + All screens + Wszystkie ekrany + + + Screen %1 [%2] + Ekran %1 [%2} + + + + DesktopAccessDialog + + Desktop access dialog + Okno dialogowe dostępu do pulpitu + + + Confirm desktop access + Potwierdź dostęp do pulpitu + + + Never for this session + Nigdy dla tej sesji + + + Always for this session + Zawsze dla tej sesji + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + Użytkownik %1 komputera %2 chce uzyskać dostęp do twojego pulpitu. Czy chcesz przydzielić dostęp? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programy i strony internetowe + + + Predefined programs + Predefiniowane programy + + + Name + Nazwa + + + Path + Ścieżka + + + Add new program + Dodaj nowy program + + + Remove selected program + Usuń wybrane programy + + + Predefined websites + Predefiniowane strony + + + Remove selected website + Usuń wybrane strony + + + URL + Adres + + + New program + Nowy program + + + New website + Nowa strona + + + + DesktopServicesFeaturePlugin + + Run program + Uruchom program + + + Open website + Otwórz stronę WWW + + + Click this button to open a website on all computers. + Przyciśnij ten przycisk żeby otworzyć stronę WWW na wszystkich komputerach + + + Start programs and services in user desktop + Uruchom programy i usługi na pulpicie użytkownika + + + Click this button to run a program on all computers. + Przyciśnij ten przycisk żeby uruchomić program na wszystkich komputerach + + + Run program "%1" + Uruchom program "%1" + + + Custom program + Inny program + + + Open website "%1" + Otwórz stronę "%1" + + + Custom website + Inna strona + + + + DocumentationFigureCreator + + Teacher + Nauczyciel + + + Room %1 + Sala %1 + + + Please complete all tasks within the next 5 minutes. + Zakończ wszystkie zadania w ciągu najbliższych 5 minut. + + + Custom website + Inna strona + + + Open file manager + Otwórz menedżera plików + + + Start learning tool + Uruchom narzędzie do nauki + + + Play tutorial video + Odtwórz film instruktażowy + + + Custom program + Inny program + + + Handout + Materiały informacyjne + + + Texts to read + Teksty do przeczytania + + + generic-student-user + generic-student-user + + + + ExternalVncServer + + External VNC server + Zewnętrzny serwer VNC + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Konfiguracja zewnętrznego serwera VNC + + + Port: + Port: + + + Password: + Hasło: + + + + FeatureControl + + Feature control + Zarządzanie funkcjami + + + + FileTransferConfigurationPage + + File transfer + Transfer plików + + + Directories + Lokalizacje + + + Destination directory + Katalog docelowy + + + Default source directory + Domyślny katalog źródłowy + + + Options + Opcje + + + Remember last source directory + Zapamiętaj ostatni katalog źródłowy + + + Create destination directory if it does not exist + Utwórz katalog docelowy jeśli nie istnieje + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + Nie można otworzyć pliku „%1”! Sprawdź uprawnienia! + + + + FileTransferDialog + + File transfer + Transfer plików + + + Options + Opcje + + + Transfer only + Tylko wyślij + + + Transfer and open file(s) with associated program + Wyślij plik(i) i otwórz za pomocą skojarzonego programu + + + Transfer and open destination folder + Wyślij plik i otwórz folder docelowy + + + Files + Pliki + + + Start + Uruchom + + + Overwrite existing files + Nadpisz istniejące pliki + + + + FileTransferPlugin + + File transfer + Transfer plików + + + Click this button to transfer files from your computer to all computers. + Kliknij ten przycisk, aby przesłać pliki z twojego komputera na wszystkie komputery. + + + Select one or more files to transfer + Wybierz jeden lub więcej plików do przesłania + + + Transfer files to remote computer + Prześlij pliki na komputer zdalny + + + Received file "%1". + Odebrano plik „%1”. + + + Could not receive file "%1" as it already exists. + Nie można odebrać pliku „%1”, ponieważ już istnieje. + + + Could not receive file "%1" as it could not be opened for writing! + Nie można odebrać pliku „%1”, ponieważ nie można go otworzyć do zapisu! + + + + GeneralConfigurationPage + + User interface + Interfejs użytkownika: + + + Language: + Język: + + + Use system language setting + Użyj systemowych ustawień językowych + + + Veyon + Veyon + + + Logging + Logowanie + + + Log file directory + Katalog plików dziennika logowania + + + Log level + Poziom logowania + + + Nothing + Nic + + + Only critical messages + Tylko komunikaty o błędach krytycznych + + + Errors and critical messages + Błędy i komunikaty o błędach krytycznych + + + Warnings and errors + Ostrzeżenia i błedy + + + Information, warnings and errors + Informacje, ostrzeżenia i błędy + + + Debug messages and everything else + Komunikaty debugowania i wszystkie pozostałe + + + Limit log file size + Ogranicz rozmiar pliku dziennika + + + Clear all log files + Wyczyść wszystkie pliki dzienników + + + Log to standard error output + Loguj do standardowego wyjścia błędów + + + Network object directory + Adres obiektu sieciowego + + + Backend: + Zaplecze: + + + Update interval: + Interwał odświeżania: + + + %1 service + Usługa %1 + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + Usługa %1 musi zostać zatrzymana w celu usunięcia plików dziennika. Kontynuować? + + + Log files cleared + Pliki dziennika zostały usunięte + + + All log files were cleared successfully. + Wszystkie pliki dziennika zostały pomyślnie usunięte. + + + Error + Błąd + + + Could not remove all log files. + Nie można usunąć wszystkich plików dziennika. + + + MB + MB + + + Rotate log files + Zamieniaj logi + + + x + x + + + seconds + sekundy + + + Write to logging system of operating system + Zapisz w logach systemu operacyjnego + + + Authentication + Uwierzytelnienie + + + Method: + Metoda: + + + Logon authentication + Uwierzytelnienie z użyciem loginu + + + Key file authentication + Uwierzytelnienie z użyciem klucza + + + Test + Test + + + Authentication is set up properly on this computer. + Uwierzytelnianie jest poprawnie skonfigurowane na tym komputerze. + + + Authentication keys are not set up properly on this computer. + Klucze uwierzytelniające nie są poprawnie skonfigurowane na tym komputerze. + + + Authentication test + Test uwierzytelnienia + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + Przeglądaj LDAP + + + + LdapClient + + LDAP error description: %1 + Opis błędu LDAP: %1 + + + + LdapConfigurationPage + + Basic settings + Ustawienia podstawowe + + + General + Ogólne + + + LDAP server and port + Serwer LDAP i port + + + Bind DN + Wskaż nazwę domeny + + + Bind password + wskaż hasło + + + Anonymous bind + Anonimowy dostęp + + + Use bind credentials + Użyj wskazanych poświadczeń + + + Base DN + Podstawowy serwer DNS + + + Fixed base DN + Zapasowy serwer DNS + + + e.g. dc=example,dc=org + np. dc=przykład,dc=org + + + Discover base DN by naming context + Rozpoznaj podstawową nazwę z DN wskazując kontekst + + + e.g. namingContexts or defaultNamingContext + np. namingContexts lub defaultNamingContext + + + Environment settings + Ustawienia środowiska + + + Object trees + Drzewa obiektów + + + Computer tree + Drzewo komputerów + + + e.g. OU=Groups + np. OU=Groups + + + User tree + Drzewo użytkownika + + + e.g. OU=Users + np. OU=Users + + + e.g. OU=Computers + np. OU=Computers + + + Group tree + Drzewo grupy + + + Perform recursive search operations in object trees + Wykonuj rekurencyjne operacje wyszukiwania w drzewach obiektów + + + Object attributes + Atrybuty obiektów + + + e.g. hwAddress + np. hwAddress + + + e.g. member or memberUid + np. member lub memberUid + + + e.g. dNSHostName + np. dNSHostName + + + Computer MAC address attribute + Atrybut adresu MAC komputera + + + Group member attribute + Atrybut członka grupy + + + e.g. uid or sAMAccountName + np. uid lub sAMAccountName + + + Advanced settings + Ustawienia zaawansowane + + + Optional object filters + Opcjonalne filtry obiektów + + + Filter for user groups + Filtruj według grup użytkowników + + + Filter for users + Filtruj według użytkowników + + + Filter for computer groups + Filtruj według grup komputerów + + + Group member identification + Identyfikacja członka grupy + + + Distinguished name (Samba/AD) + Nazwa wyróżniająca (SAMBA/AD) + + + List all groups of a user + Wyświetl wszystkie grupy użytkownika + + + List all groups of a computer + Wyświetl wszystkie grupy komputera + + + Get computer object by IP address + Wybierz komputer za pomocą adresu IP + + + LDAP connection failed + Połączenie LDAP zakończone niepowodzeniem + + + LDAP bind failed + Podłączenie LDAP nie powiodło się + + + LDAP bind successful + Podłączenie LDAP powiodło się + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Pomyślnie nawiązano połączenie z serwerem LDAP. Podstawowe ustawienia LDAP są poprawnie skonfigurowane. + + + LDAP base DN test failed + Test podstawowego DN z LDAP nie powiódł się + + + LDAP base DN test successful + Test podstawowego DN z LDAP powiódł się + + + LDAP naming context test failed + Test LDAP nie powiódł się + + + LDAP naming context test successful + Test kontekstu LDAP powiódł się + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + drzewo użytkowników + + + group tree + drzewo grupy + + + computer tree + drzewo komputera + + + Enter username + Wprowadź nazwę użytkownika + + + Please enter a user login name (wildcards allowed) which to query: + Wprowadź nazwę logowania użytkownika (dozwolone są symbole), którą chcesz sprawdzić: + + + user objects + obiekty użytkownika + + + Enter group name + Wprowadź nazwę grupy + + + Please enter a group name whose members to query: + Podaj nazwę grupy, którą chcesz sprawdzić: + + + group members + Członkowie grupy + + + Group not found + Nie znaleziono grupy + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + Nie można znaleźć grupy o nazwie „%1”. Sprawdź nazwę grupy lub jej drzewo. + + + Enter computer name + Wprowadź nazwę komputera + + + computer objects + Obiekty komputerowe + + + Enter computer DN + Wpisz DN komputera + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + adres MAC komputera + + + users + użytkownicy + + + user groups + grupy użytkowników + + + computer groups + grupy komputerów + + + Please enter a user login name whose group memberships to query: + Wprowadź nazwę użytkownika, którego członkostwo w grupach ma być wyszukiwane: + + + groups of user + grupy użytkownika + + + User not found + Nie znaleziono użytkownika + + + groups of computer + grupy komputera + + + Computer not found + Nie znaleziono komputera + + + Enter computer IP address + Wprowadź adres IP komputera + + + Please enter a computer IP address which to resolve to an computer object: + Wprowadź adres IP komputera, który ma zostać przekształcony w obiekt komputerowy: + + + computers + komputery + + + LDAP %1 test failed + test LDAP %1 zakończony niepowodzeniem + + + LDAP %1 test successful + test LDAP %1 zakończony powodzeniem + + + The %1 has been queried successfully and %2 entries were found. + Zapytanie o %1 zakończyło się powodzeniem i znaleziono %2 wpisów. +  + + + %1 %2 have been queried successfully: + +%3 + Zadania %1 %2 zostały pomyślnie wykonane + +%3 + + + LDAP filter test failed + test filtrów LDAP zakończony niepowodzeniem + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + Nie można wysłać zapytania do żadnego %1 przy użyciu skonfigurowanego filtra. Sprawdź filtr LDAP dla %1 + +%2 + + + LDAP filter test successful + test filtrów LDAP zakończony powodzeniem + + + %1 %2 have been queried successfully using the configured filter. + Zadanie %1 %2 zakończyło się pomyślnie przy użyciu skonfigurowanego filtra. + + + (only if different from group tree) + (tylko jeśli różni się od drzewa grupy) + + + Computer group tree + Drzewo grup komputerowych + + + computer group tree + drzewo grup komputerowych + + + Filter for computers + Filtruj komputery + + + e.g. room or computerLab + np. room lub computerLab + + + Integration tests + Testy integracyjne + + + Computer groups + Grupy komputerów + + + e.g. name or description + np. nazwa lub opis + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + Bezpieczeństwo połączenia + + + TLS certificate verification + Weryfikacja certyfikatu TLS + + + System defaults + Ustawienia domyślne + + + Never (insecure!) + Nigdy (niebezpieczne!) + + + Custom CA certificate file + Inny plik certyfikatu + + + None + Brak + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + np. (objectClass=komputer) + + + e.g. (objectClass=group) + np. (objectClass=grupa) + + + e.g. (objectClass=person) + np. (objectClass=osoba) + + + e.g. (objectClass=room) or (objectClass=computerLab) + np. (objectClass=sala) lub (objectClass=PracowniaKomputerowa) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + np. (objectClass=kontener) lub (objectClass=JednostkaOrganizacyjna) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + Pliki certyfikatów (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + Nie można połączyć się z serwerem LDAP. Sprawdź parametry serwera. + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + Nie można powiązać z serwerem LDAP. Sprawdź parametry serwera i powiąż poświadczenia. + +%1 + + + Encryption protocol + Protokół szyfrowania + + + Computer location attribute + Atrybut lokalizacji komputera + + + Computer display name attribute + Atrybut wyświetlanej nazwy komputera + + + Location name attribute + Atrybut nazwy lokalizacji + + + e.g. cn or displayName + np. cn or displayName + + + Computer locations identification + Identyfikacja lokalizacji komputera + + + Identify computer locations (e.g. rooms) via: + Zidentyfikuj lokalizacje komputerów (np. sale) przez: + + + Location attribute in computer objects + + + + List all entries of a location + Wyświetl wszystkie wpisy lokalizacji + + + List all locations + Wyświetl wszystkie lokalizacje + + + Enter computer display name + Wpisz wyświetlaną nazwę komputera + + + Please enter a computer display name to query: + Wprowadź wyświetlaną nazwę komputera, aby wysłać zapytanie: + + + Enter computer location name + Wprowadź nazwę lokalizacji komputera + + + Please enter the name of a computer location (wildcards allowed): + Wprowadź nazwę lokalizacji komputera (dozwolone są znaki specjalne): + + + computer locations + lokalizacje komputerów + + + Enter location name + Wpisz nazwę lokalizacji + + + Please enter the name of a location whose entries to query: + Wprowadź nazwę lokalizacji, której wpisy dotyczą zapytania: + + + location entries + lokalizacje + + + LDAP test failed + Test LDAP nie powiódł się + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + Nie można wysłać zapytania do żadnego %1. Sprawdź parametr(y) %2 i wprowadź nazwę istniejącego obiektu. + +%3 + + + and + oraz + + + LDAP test successful + Test LDAP powiódł się + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + Nie można wyszukać żadnych wpisów w skonfigurowanym %1. Sprawdź parametr „%2”. + +%3 + + + Browse + Przeglądaj + + + Test + Test + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + Nazwy hostów przechowywane jako w pełni kwalifikowane nazwy domen (FQDN, np. myhost.example.org) +  + + + Computer hostname attribute + Atrybut nazwy hosta komputera + + + Please enter a computer hostname to query: + Wprowadź nazwę hosta, aby wysłać zapytanie: + + + Invalid hostname + Nieprawidłowa nazwa hosta + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + Skonfigurowałeś nazwy komputerów, aby były przechowywane jako w pełni kwalifikowane nazwy domen (FQDN), ale wprowadziłeś nazwę hosta bez domeny. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + Skonfigurowałeś nazwy komputerów, aby były przechowywane jako proste nazwy hostów bez nazwy domeny, ale wprowadziłeś nazwę hosta z częścią nazwy domeny. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + Nie można znaleźć użytkownika o nazwie „%1”. Sprawdź nazwę użytkownika lub parametr drzewa użytkownika. + + + Enter hostname + Wpisz nazwę hosta + + + Please enter a computer hostname whose group memberships to query: + Wprowadź nazwę hosta, którego członkostwo w grupach ma być wyszukiwane: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + Nie można znaleźć komputera o nazwie hosta „%1”. Sprawdź nazwę hosta lub parametr drzewa komputera. + + + Hostname lookup failed + Wyszukiwanie nazwy hosta nie powiodło się + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + Nie można wyszukać nazwy hosta dla adresu IP %1. Sprawdź ustawienia serwera DNS. + + + User login name attribute + Atrybut nazwy logowania użytkownika + + + Configured attribute for user login name or computer hostname (OpenLDAP) + Skonfigurowany atrybut użytkownik lub komputer (OpenLDAP) + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + Zapytaj obiekty z katalogu LDAP + + + Show help about command + Pokaż pomoc dotyczącą komendy + + + Commands for configuring and testing LDAP/AD integration + Polecenia do konfigurowania i testowania integracji LDAP / AD + + + Basic LDAP/AD support for Veyon + Podstawowa obsługa LDAP / AD dla Veyon + + + %1 (load computers and locations from LDAP/AD) + %1 (wczytywanie komputerów i lokacji z LDAP / AD) + + + %1 (load users and groups from LDAP/AD) + %1 (wczytywanie użytkowników i grupy z LDAP / AD) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + Podaj prawidłowy adres url LDAP zgodnie ze schematem „ldap[s]://[użytkownik[:hasło]@]nazwahosta[:port]" + + + No naming context attribute name given - falling back to configured value. + Nie podano poprawnie - powrót do skonfigurowanej wartości. + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + Niestandardowa usługa PAM do uwierzytelniania użytkownika + + + User authentication + Uwierzytelnianie użytkownika + + + Session management + Zarządzanie sesją + + + Display manager users + Wyświetl użytkowników z uprawnieniami managera + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Wtyczka włączająca specyficzne funkcje dla platformy Linux + + + + LocationDialog + + Select location + Wybierz lokalizację + + + enter search filter... + Wprowadź filtr wyszukiwania... + + + + MainToolBar + + Configuration + Konfiguracja + + + Disable balloon tooltips + Wyłącz dymki z podpowiedziami + + + Show icons only + Pokaż tylko ikony + + + + MainWindow + + MainWindow + Główne okno + + + toolBar + Pasek narzędzi + + + General + Ogólne + + + &File + &Plik + + + &Help + &Pomoc + + + &Quit + &Wyjście + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + &Załaduj ustawienia z pliku + + + Ctrl+O + Ctrl+O + + + About Qt + O Qt + + + Authentication impossible + Uwierzytelnienie niemożliwe + + + Configuration not writable + Nie można zapisać konfiguracji + + + Load settings from file + Wczytaj ustawienia z pliku + + + Save settings to file + Zapisz ustawienia do pliku + + + Unsaved settings + Niezapisane ustawienia + + + There are unsaved settings. Quit anyway? + Istnieją niezapisane zmiany. Wyjść mimo to? + + + Veyon Configurator + Konfigurator Veyon + + + Service + Usługa + + + Master + Master + + + Access control + Kontrola dostępu + + + About Veyon + O programie Veyon + + + Auto + Auto + + + About + O programie + + + %1 Configurator %2 + %1 Konfigurator %2 + + + JSON files (*.json) + Pliki JSON (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + Lokalny backend konfiguracji poinformował, że konfiguracji nie można zapisać! Uruchom konfigurator %1 z wyższymi uprawnieniami. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + Nie znaleziono plików kluczy uwierzytelniających lub twoje obecne są nieaktualne. Utwórz nowe pliki kluczy za pomocą Konfiguratora %1. Alternatywnie skonfiguruj uwierzytelnianie logowania za pomocą Konfiguratora %1. W przeciwnym razie nie będziesz mieć dostępu do komputerów przy użyciu %1. + + + Access denied + Odmowa dostępu + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + Zgodnie z lokalną konfiguracją nie masz dostępu do komputerów w sieci. Zaloguj się przy użyciu innego konta lub pozwól administratorowi systemu sprawdzić konfigurację lokalną. + + + Screenshots + Zrzuty ekranu + + + Feature active + Aktywne funkcje + + + The feature "%1" is still active. Please stop it before closing %2. + Funkcja "%1" jest wciąż aktywna. Zatrzymaj ją przed zamknięciem %2. + + + Reset configuration + Resetuj konfigurację + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Czy na pewno chcesz zresetować lokalną konfigurację i przywrócić wszystkie ustawienia do wartości domyślnych? + + + Search users and computers + Wyszukaj użytkowników i komputery + + + Align computers to grid + Dopasuj komputery w siatkę + + + %1 Configurator + Konfigurator %1 + + + Insufficient privileges + Niewystarczające uprawnienia + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + Nie można rozpocząć z uprawnieniami administratora. Upewnij się, że w środowisku zainstalowany program podobny do sudo! Program zostanie uruchomiony z normalnymi uprawnieniami użytkownika. + + + Only show powered on computers + Pokaż tylko włączone komputery + + + &Save settings to file + Zapi&sz ustawienia do pliku + + + &View + Podgląd + + + &Standard + &Standard + + + &Advanced + Z&aawansowany + + + Use custom computer arrangement + Użyj niestandardowego ustawienia komputera + + + Locations && computers + Lokalizacje i komputery + + + Slideshow + Pokaz slajdów + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Lokalizacje + + + User configuration + Konfiguracja użytkownika + + + Feature on computer double click: + Działanie po podwójnym kliknięciu na komputerze: + + + Features + Funkcje + + + All features + Wszystkie funkcje + + + Disabled features + Wyłączone funkcje + + + Screenshots + Zrzuty ekranu + + + <no feature> + <no feature> + + + Basic settings + Ustawienia podstawowe + + + Behaviour + Zachowanie + + + Enforce selected mode for client computers + Wymuszaj wybrany tryb dla komputerów klienckich + + + Hide local computer + Ukryj komputer lokalny + + + Hide computer filter field + Ukryj pole filtra komputera + + + Actions such as rebooting or powering down computers + Akcje takie jak uruchom ponownie lub wyłącz komputery + + + User interface + Interfejs użytkownika: + + + Background color + kolor tła + + + Thumbnail update interval + Interwał odświeżania miniatur: + + + ms + ms + + + Program start + Uruchamianie programu + + + Modes and features + Tryby i funkcje + + + User and computer name + Nazwa użytkownika i komputera + + + Only user name + Tylko nazwa użytkownika + + + Only computer name + Tylko nazwa komputera + + + Computer thumbnail caption + Sekcja miniatur komputera + + + Text color + Kolor tekstu + + + Sort order + Kolejność sortowania + + + Computer and user name + Nazwa komputera i użytkownika + + + Computer locations + Lokalizacje komputerów + + + Show current location only + Pokaż tylko bieżącą lokalizację + + + Allow adding hidden locations manually + Zezwalaj na ręczne dodawanie ukrytych lokalizacji + + + Hide empty locations + Ukryj puste lokalizacje + + + Show confirmation dialog for potentially unsafe actions + Pokaż okno dialogowe potwierdzenia dla potencjalnie niebezpiecznych działań + + + Perform access control + Wykonaj weryfikację dostępu + + + Automatically select current location + Automatycznie wybierz bieżącą lokalizację + + + Automatically open computer select panel + Automatycznie otwórz panel wyboru komputera + + + Hide local session + Ukryj lokalną sesję + + + px + + + + Thumbnail spacing + Odstęp między miniaturami + + + Auto + Auto + + + Thumbnail aspect ratio + Proporcje miniatury + + + Automatically adjust computer icon size + Automatycznie dostosuj rozmiar ikony komputera + + + Open feature windows on the same screen as the main window + Otwieraj okna funkcji na tym samym ekranie, co okno główne + + + + MonitoringMode + + Monitoring + Monitoring + + + Builtin monitoring mode + Wbudowany tryb monitorowania + + + This mode allows you to monitor all computers at one or more locations. + Ten tryb umożliwia monitorowanie wszystkich komputerów w co najmniej jednej lokalizacji. + + + + NetworkObjectTreeModel + + Locations/Computers + Lokalizacje/Komputery + + + + OpenWebsiteDialog + + Open website + Otwórz stronę WWW + + + e.g. Veyon + np. Veyon + + + Remember and add to website menu + Zapamiętaj i dodaj do menu strony + + + e.g. www.veyon.io + np.: www.veyon.io + + + Please enter the URL of the website to open: + Wprowadź adres strony do otwarcia: + + + Name: + Nazwa: + + + + PasswordDialog + + Username + Nazwa użytkownika + + + Password + Hasło + + + Veyon Logon + Logowanie Veyon + + + Authentication error + Błąd uwierzytelnienia + + + Logon failed with given username and password. Please try again! + Nie można się zalogować z użytym loginem i hasłem. Spróbuj ponownie! + + + Please enter your username and password in order to access computers. + Aby uzyskać dostęp do komputerów wprowadź nazwę użytkownika i hasło. + + + + PowerControlFeaturePlugin + + Power on + Uruchom + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Kliknij ten przycisk żeby uruchomić wszystkie komputery. Dzięki tej metodzie nie musisz uruchamiać ręcznie każdego komputera. + + + Reboot + Uruchom ponownie + + + Click this button to reboot all computers. + Naciśnij ten przycisk, aby uruchomić ponownie wszystkie komputery. + + + Power down + Wyłącz + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Kliknij ten przycisk żeby wyłączyć wszystkie komputery. Dzięki tej metodzie nie musisz wyłączać ręcznie każdego komputera. + + + Power on/down or reboot a computer + Włącz, wyłącz lub zresetuj komputer + + + Confirm reboot + Potwierdź ponowne uruchomienie + + + Confirm power down + Potwierdź wyłączenie + + + Do you really want to reboot the selected computers? + Czy na pewno chcesz uruchomić ponownie wybrane komputery? + + + Do you really want to power down the selected computer? + Czy na pewno chcesz wyłączyć wybrany komputer? + + + Power on a computer via Wake-on-LAN (WOL) + Włącz komputer przez Wake-on-LAN (WOL) + + + MAC ADDRESS + ADRES MAC + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + To polecenie wysyła pakiet Wake-on-LAN (WOL) do sieci w celu włączenia komputera o podanym adresie MAC. + + + Please specify the command to display help for! + Podaj polecenie aby wyświetlić pomoc! + + + Invalid MAC address specified! + Podano nieprawidłowy adres MAC! + + + Commands for controlling power status of computers + Polecenia do kontrolowania stanu zasilania komputerów + + + Power down now + Wyłącz natychmiast + + + Install updates and power down + Zainstaluj aktualizacje i wyłącz + + + Power down after user confirmation + Wyłącz po potwierdzeniu przez użytkownika + + + Power down after timeout + Wyłącz po upływie limitu czasu + + + The computer was remotely requested to power down. Do you want to power down the computer now? + Komputer został zdalnie poproszony o wyłączenie. Czy chcesz teraz wyłączyć komputer? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + Komputer zostanie wyłączony za %1 minut, %2 sekund. + +Zapisz swoją pracę i zamknij wszystkie programy. + + + + PowerDownTimeInputDialog + + Power down + Wyłącz + + + Please specify a timeout for powering down the selected computers: + Określ limit czasu dla wyłączania wybranych komputerów: + + + minutes + minut + + + seconds + sekund + + + + RemoteAccessFeaturePlugin + + Remote view + Zdalny podgląd + + + Open a remote view for a computer without interaction. + Otwórz podgląd zdalnego komputera bez wchodzenia w interakcję z nim. + + + Remote control + Zdalna kontrola + + + Open a remote control window for a computer. + Otwórz okno zdalnego sterowania komputerem. + + + Remote access + Zdalny dostęp + + + Remote view or control a computer + Zdalny podgląd lub sterowanie komputerem + + + Please enter the hostname or IP address of the computer to access: + Wprowadź nazwę lub adres IP komputera, do którego chcesz uzyskać dostęp: + + + Show help about command + Pokaż pomoc dotyczącą komendy + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 -%2 dostęp zdalny + + + %1 - %2 - %3 Remote Access + %1 - %2 - %3 Zdalny dostęp + + + + RemoteAccessWidgetToolBar + + View only + Tylko podgląd + + + Remote control + Zdalna kontrola + + + Send shortcut + Wyślij skrót + + + Fullscreen + Pełny ekran + + + Window + Okno + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Alt+Tab + + + Win + Win + + + Menu + Menu + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Łączenie z %1 + + + Connected. + Połączono. + + + Screenshot + Zrzut ekranu + + + Exit + Wyjście + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Wprowadź nazwę programów lub komendy do uruchomienia na zdalnym komputerze(komputerach). Możesz oddzielić je, zapisując w osobnych wierszach. + + + Run programs + Uruchom programy + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + np. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + Nazwa: + + + Remember and add to program menu + Zapamiętaj i dodaj do menu programów + + + e.g. VLC + np.: VLC + + + + ScreenLockFeaturePlugin + + Lock + Zablokuj + + + Unlock + Odblokuj + + + Lock screen and input devices of a computer + Zablokuj ekran i urządzenia wejścia komputera + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Żeby zdobyć pełną uwagę użytkowników, możesz zablokować ich komputery za pomocą tego przycisku. W tym trybie wszystkie urządzenia wejścia oraz ekran są zablokowane. + + + Lock input devices + Zablokuj urządzenia wejściowe + + + Unlock input devices + Odblokuj urządzenie wejściowe + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + Aby odzyskać pełną uwagę wszystkich użytkowników, możesz zablokować ich komputery za pomocą tego przycisku. W tym trybie wszystkie urządzenia wejściowe są zablokowane, podczas gdy pulpit jest nadal widoczny.  + + + + Screenshot + + unknown + nieznane + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + Nie można przechwycić zrzutu ekranu. Folder %1 nie istnieje i nie można go stworzyć. + + + Screenshot + Zrzut ekranu + + + Could not open screenshot file %1 for writing. + Nie udało się zapisać zrzutu ekranu do pliku %1 + + + + ScreenshotFeaturePlugin + + Screenshot + Zrzut ekranu + + + Use this function to take a screenshot of selected computers. + Użyj tej funkcji, aby wykonać zrzut ekranu wybranych komputerów. + + + Screenshots taken + Wykonane zrzuty ekranu + + + Screenshot of %1 computer have been taken successfully. + Wykonano zrzut ekranu komputera %1. + + + Take screenshots of computers and save them locally. + Wykonaj zrzuty ekranów komputerów i zapisz je lokalnie. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Wszystkie zrzuty ekranu zrobione przez ciebie będą pokazane tutaj. Możesz wykonać zrzut ekranu poprzez wciśnięcie przycisku "Zrzut ekranu" w menu kontekstowym. Zrzutami ekranu można zarządzać przyciskami poniżej. + + + User: + Użytkownik: + + + Computer: + Komputer: + + + Date: + Data: + + + Time: + Czas: + + + Show + Pokaż + + + Delete + Usuń + + + Screenshot + Zrzut ekranu + + + Do you really want to delete all selected screenshots? + Czy na pewno chcesz usunąć wszystkie zaznaczone zrzuty ekranu? + + + + ServiceConfigurationPage + + General + Ogólne + + + Autostart + Autostart + + + Hide tray icon + Ukryj ikonę na pasku zadań + + + Start service + Uruchom usługę + + + Stopped + Zatrzymana + + + Stop service + Zatrzymaj usługę + + + State: + Stan + + + Enable firewall exception + Uruchom wyjątki w zaporze sieciowej + + + Allow connections from localhost only + Zezwalaj tylko na połączenia z localhosta + + + VNC server + Serwer VNC + + + Plugin: + Wtyczka: + + + Restart %1 Service + Uruchom ponownie usługę %1 + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Wszystkie ustawienia zostały zapisane poprawnie. Żeby zaczęły działać, usługa %1 musi zostać uruchomiona ponownie. Czy chcesz zrobić to teraz? + + + Running + Uruchomiona + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + Włączenie tej opcji spowoduje, że usługa uruchomi proces serwera dla każdej interaktywnej sesji na komputerze. +Zazwyczaj jest to wymagane do obsługi serwerów terminali. + + + Show notification on remote connection + Pokaż powiadomienie o zdalnym połączeniu + + + Show notification when an unauthorized access is blocked + Pokaż powiadomienie, gdy zostanie zablokowany nieautoryzowany dostęp + + + Sessions + Sesje + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + Tryb wielosesyjny (utwórz instancję serwera dla każdej sesji pulpitu lokalnego i zdalnego) + + + Maximum session count + Maksymalna liczba sesji + + + Network port numbers + + + + Veyon server + Serwer Veyon + + + Internal VNC server + Wewnętrzny serwer VNC + + + Feature manager + Menedżer funkcji + + + Demo server + Serwer demo + + + Miscellaneous network settings + Różne ustawienia sieci + + + + ServiceControl + + Starting service %1 + Uruchamianie usługi %1 + + + Stopping service %1 + Zatrzymywanie usługi %1 + + + Registering service %1 + Rejestrowanie usługi %1 + + + Unregistering service %1 + Wyrejestrowywanie usługi %1 + + + Service control + Zarządzenie usługą + + + + ServiceControlPlugin + + Service is running + Usługa jest uruchomiona + + + Service is not running + Usługa nie jest uruchomiona + + + Configure and control Veyon service + Konfiguruj i zarządzaj usługą Veyon + + + Register Veyon Service + Rejestracja usługi Veyon + + + Unregister Veyon Service + Wyrejestrowanie usługi Veyon + + + Start Veyon Service + Uruchom usługę Veyon + + + Stop Veyon Service + Zatrzymaj usługę Veyon + + + Restart Veyon Service + Uruchom ponownie usługę Veyon + + + Query status of Veyon Service + Status kolejki usługi Veyon + + + Commands for configuring and controlling Veyon Service + Komendy do konfiguracji i zarządzania usługą Veyon + + + + ShellCommandLinePlugin + + Run command file + Uruchom plik komend + + + File "%1" does not exist! + Plik "%1" nie istnieje! + + + Interactive shell and script execution for Veyon Control + Interaktywne wykonywanie powłok i skryptów dla Veyon Control + + + Commands for shell functionalities + Komendy dotyczące funkcjonalności powłoki + + + + SlideshowPanel + + Previous + Poprzedni + + + Start/pause + Start/pauza + + + Next + Następny + + + Duration: + Czas trwania: + + + + SpotlightPanel + + Add selected computers + Dodaj wybrane komputery + + + Remove selected computers + Usuń wybrane komputery + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + Ikona obszaru powiadamiania + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + Domyślnie (systemowe grupy użytkowników) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + Testuj wewnętrzne komponenty i funkcje Veyon + + + Commands for testing internal components and functions of Veyon + Polecenia do testowania wewnętrznych komponentów i funkcji Veyon + + + + TextMessageDialog + + Send text message + Wyślij wiadomość tekstową + + + Use the field below to type your message which will be sent to all selected users. + W polu poniżej napisz wiadomość, która ma zostać wysłana do wybranych użytkowników. + + + + TextMessageFeaturePlugin + + Text message + Wiadomość tekstowa + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Użyj ten funkcji do wysłania wiadomości tekstowej do wszystkich użytkowników, np. przydzielenie nowego zadania. + + + Message from teacher + Wiadomość od nauczyciela + + + Send a message to a user + Wyślij wiadomość do użytkownika + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Włącz przechwytywanie półprzezroczystych okien + + + Poll full screen (leave this enabled per default) + Rozciągnij na pełny ekran (domyślnie pozostaw włączone) + + + Low accuracy (turbo mode) + Niska dokładność (tryb turbo) + + + Builtin UltraVNC server configuration + Konfiguracja wbudowanego serwera UltraVNC + + + Enable multi monitor support + Włącz obsługę wielu monitorów + + + Enable Desktop Duplication Engine on Windows 8 and newer + Włącz mechanizm kopiowania pulpitu w systemie Windows 8 i nowszych + + + Maximum CPU usage + Maksymalne wykorzystanie CPU + + + + UserConfig + + No write access + Brak prawa edycji + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Nie można zapisać ustawień osobistych! Sprawdź ścieżkę pliku konfiguracji użytkownika za pomocą Konfiguratora %1. + + + + UserLoginDialog + + User login + Login użytkownika + + + Please enter a username and password for automatic login on all computers. + Wprowadź nazwę użytkownika i hasło do automatycznego logowania na wszystkich komputerach. + + + Username + Nazwa użytkownika + + + Password + Hasło + + + + UserSessionControlPlugin + + Log in + Zaloguj + + + Click this button to log in a specific user on all computers. + Kliknij ten przycisk, aby zalogować określonego użytkownika na wszystkich komputerach. + + + Log off + Wyloguj + + + Click this button to log off users from all computers. + Kliknij ten przycisk, aby wylogować użytkowników ze wszystkich komputerów. + + + Confirm user logoff + Potwierdź wylogowanie użytkownika + + + Do you really want to log off the selected users? + Czy naprawdę chcesz wylogować wybranych użytkowników? + + + User session control + Kontrola sesji użytkownika + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [BŁĄD] + + + Invalid command! + Niepoprawna komenda! + + + Available commands: + Dostępne polecenia: + + + Invalid arguments given + Podano złe argumenty + + + Not enough arguments given - use "%1 help" for more information + Podano za mało argumentów - użyj „%1 help”, aby uzyskać więcej informacji + + + Unknown result! + Nieznany wynik! + + + Available modules: + Dostępne moduły: + + + No module specified or module not found - available modules are: + Nie wybrano modułu lub nie może zostać on znaleziony. Dostępne moduły: + + + Plugin not licensed + Nielicencjonowana wtyczka + + + INFO + INFORMACJA + + + ERROR + BŁĄD + + + USAGE + UŻYCIE + + + DESCRIPTION + OPIS + + + EXAMPLES + PRZYKŁADY + + + WARNING + OSTRZEŻENIE + + + + VeyonServiceControl + + Veyon Service + Usługa Veyon + + + + VncViewWidget + + Establishing connection to %1 ... + Nawiązywanie połączenia z %1 + + + + WebApiConfigurationPage + + Web API + Web API + + + General + Ogólne + + + Network port + Port sieciowy + + + Enable WebAPI server + Włącz serwer WebAPI + + + Connection settings + Ustawienia połączenia + + + Lifetime + + + + h + g + + + s + s + + + Idle timeout + limit czasu bezczynności + + + Authentication timeout + Limit czasu uwierzytelniania + + + Maximum number of open connections + Maksymalna liczba otwartych połączeń + + + Connection encryption + Szyfrowanie połączenia + + + TLS certificate file + plik certyfikatu TLS + + + TLS private key file + prywatny klucz TLS + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + Używaj HTTPS z TLS 1.3 zamiast HTTP + + + + WebApiPlugin + + Run WebAPI server + Uruchom serwer WebAPI + + + Failed to start WebAPI server at port %1 + Nie udało się uruchomić serwera WebAPI na porcie %1 + + + WebAPI server running at port %1 + Serwer WebAPI uruchomiony na porcie %1 + + + Provide access to a computer via HTTP + Zapewnij dostęp do komputera przez HTTP + + + Commands for running the WebAPI server + Polecenia do uruchamiania serwera WebAPI + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + Nie można zmienić ustawienia systemowego Zdalne wywołanie Ctrl + Alt + Del nie działa! + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + Ogólne + + + Enable SAS generation by software (Ctrl+Alt+Del) + Uruchom generowanie SAS przez oprogramowanie (Ctrl+Alt+Del) + + + Screen lock + blokada ekranu + + + Hide taskbar + Ukryj pasek zadań + + + Hide start menu + Ukryj Menu Start + + + Hide desktop + Ukryj pulpit + + + User authentication + Uwierzytelnianie użytkownika + + + Use alternative user authentication mechanism + Użyj alternatywnego mechanizmu uwierzytelniania użytkownika + + + User login + Login użytkownika + + + Input start delay + Opóźnienie rozpoczęcia pisania + + + Simulated key presses interval + Interwał naciskania klawiszy + + + Confirm legal notice (message displayed before user logs in) + Potwierdź informację prawną (komunikat wyświetlany przed zalogowaniem się użytkownika) + + + Use input device interception driver + Użyj sterownika przechwytywania urządzenia wejściowego + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Wtyczka dodająca specyficzne funkcje dla platformy Windows + + + + WindowsServiceControl + + The service "%1" is already installed. + Usługa „%1” jest już zainstalowana. + + + The service "%1" could not be installed. + Nie można zainstalować usługi „%1”. + + + The service "%1" has been installed successfully. + Usługa „%1” została pomyślnie zainstalowana. + + + The service "%1" could not be uninstalled. + Nie można odinstalować usługi „%1”. + + + The service "%1" has been uninstalled successfully. + Usługa „%1” została pomyślnie odinstalowana. + + + The start type of service "%1" could not be changed. + Nie można zmienić typu usługi „%1”. + + + Service "%1" could not be found. + Nie można znaleźć usługi „%1”. + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Konfiguracja wbudowanego serwera x11vnc + + + Custom x11vnc parameters: + Niestandardowe parametry x11vnc: + + + Do not use X Damage extension + Nie używaj rozszerzenia X Damage + + + diff --git a/translations/veyon_pt_BR.ts b/translations/veyon_pt_BR.ts new file mode 100644 index 0000000..7ed9c20 --- /dev/null +++ b/translations/veyon_pt_BR.ts @@ -0,0 +1,4066 @@ + + + + + AboutDialog + + About + Sobre + + + Translation + Tradução + + + License + Licença + + + About Veyon + Sobre o Veyon + + + Contributors + Contribuidores + + + Version: + Versão: + + + Website: + Website: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Idioma atual não traduzido ainda (ou Inglês nativo) + +Se você tem interesse em traduzir o Veyon para o seu idioma local, ou outro idioma, ou melhorar uma tradução existente, por favor entre em contato com um desenvolvedor Veyon! + + + About %1 %2 + Sobre %1 %2 + + + Support Veyon project with a donation + Apoie o projeto Veyon com uma doação + + + + AccessControlPage + + Computer access control + Controle de acesso do computador + + + Grant access to every authenticated user (default) + Conceder acesso para cada usuário autenticado (padrão) + + + Test + Teste + + + Process access control rules + Regras de controle de acesso do processo + + + User groups authorized for computer access + Grupos de usuário autorizados para acesso do computador + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Adicione os grupos cujos membros devem estar autorizados a acessar computadores na sua rede Veyon. + + + Authorized user groups + Grupos de usuários autorizados + + + All groups + Todos os grupos + + + Access control rules + Regras de controle de acesso + + + Add access control rule + Adicionar regra de controle de acesso + + + Remove access control rule + Remover regra de controle de acesso + + + Move selected rule down + Mover para baixo a regra selecionada + + + Move selected rule up + Mover para cima a regra selecionada + + + Edit selected rule + Editar a regra selecionada + + + Enter username + Digite o nome do usuário + + + Please enter a user login name whose access permissions to test: + Digite o login do usuário para testar suas permissões de acesso: + + + Access allowed + Acesso permitido + + + The specified user is allowed to access computers with this configuration. + O usuário especificado pode acessar computadores com esta configuração. + + + Access denied + Acesso negado + + + The specified user is not allowed to access computers with this configuration. + O usuário especificado não pode acessar computadores com esta configuração. + + + Enable usage of domain groups + Habilitar o uso de grupos de domínio + + + User groups backend: + Back-end dos grupos de usuários: + + + Missing user groups backend + Sem Back-end dos grupos de usuários: + + + No default user groups plugin was found. Please check your installation! + Nenhum plugin padrão de grupos de usuários foi encontrado. Verifique sua instalação! + + + Restrict access to members of specific user groups + Restringir acesso para membros de determinados grupos + + + + AccessControlRuleEditDialog + + Edit access control rule + Editar regras de controle de acesso + + + General + Geral + + + enter a short name for the rule here + digite aqui um nome curto para a regra + + + Rule name: + Nome da regra: + + + enter a description for the rule here + digite aqui uma descrição para a regra + + + Rule description: + Descrição da regra: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Inverter todas as condições + + + Conditions + Condições + + + is member of group + é membro do grupo + + + Accessing computer is localhost + O computador acessando é localhost + + + Accessing user is logged on user + O usuário acessando está logado como usuário + + + Accessing user is already connected + O usuário acessando já está conectado + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Se mais de uma condição for ativada, todas devem ser atendidas para que a regra seja aplicada (E lógico). Se apenas uma das múltiplas condições tiver que ser atendida (OU lógico), crie múltiplas regras de controle de acesso. + + + Action + Ação + + + Allow access + Permitir acesso + + + Deny access + Negar acesso + + + Ask logged on user for permission + Pedir permissão para um usuário logado + + + None (rule disabled) + Nenhuma (regras desativadas) + + + Accessing user + Usuário acessando + + + Accessing computer + Computador acessando + + + Local (logged on) user + Usuários locais (logados) + + + Local computer + Computador local + + + Always process rule and ignore conditions + Sempre processe as regras e ignore as condições + + + No user logged on + Nenhum usuário logado + + + Accessing user has one or more groups in common with local (logged on) user + O acesso ao usuário tem um ou mais grupos em comum com o usuário local (logado) + + + Accessing computer and local computer are at the same location + O computador de acesso e o computador local estão no mesmo local + + + is located at + está localizado em + + + + AccessControlRulesTestDialog + + Access control rules test + Teste das regras de controle de acesso + + + Accessing user: + Acessando usuário: + + + Local computer: + Computador local: + + + Accessing computer: + Acessando computador: + + + Please enter the following user and computer information in order to test the configured ruleset. + Digite as informações seguintes sobre o usuário e o computador para testar o conjunto de regras configurado. + + + Local user: + Usuário local: + + + Connected users: + Usuários conectados: + + + The access in the given scenario is allowed. + O acesso é permitido na configuração atual. + + + The access in the given scenario is denied. + O acesso é negado na configuração atual. + + + The access in the given scenario needs permission of the logged on user. + O acesso na configuração atual requer a permissão de um usuário logado. + + + ERROR: Unknown action + ERRO: Ação desconhecida + + + Test result + Resultado do teste + + + + AuthKeysConfigurationPage + + Authentication keys + Chaves de autenticação + + + Introduction + Introdução + + + Key file directories + Diretórios do arquivo chave + + + Public key file base directory + Diretório base do arquivo de chave pública + + + Private key file base directory + Diretório base do arquivo de chave privada + + + Available authentication keys + Chaves de autenticação disponíveis + + + Create key pair + Criar um par de chaves + + + Delete key + Apagar chave + + + Import key + Importar chave + + + Export key + Exportar chave + + + Set access group + Definir grupo de acesso + + + Key files (*.pem) + Arquivos chaves (*.pem) + + + Authentication key name + Nome da chave de autenticação + + + Please enter the name of the user group or role for which to create an authentication key pair: + Por favor insira o nome do grupo de usuários ou função para o qual deseja criar um par de chaves de autenticação: + + + Do you really want to delete authentication key "%1/%2"? + Tem certeza que deseja deletar a chave de autenticação "%1/%2"? + + + Please select a key to delete! + Por favor, selecione uma chave para deletar! + + + Please enter the name of the user group or role for which to import the authentication key: + Por favor insira o nome do grupo de usuários ou função para o qual deseja importar a chave de autenticação: + + + Please select a key to export! + Por favor, selecione uma chave para exportar! + + + Please select a user group which to grant access to key "%1": + Por favor, selecione um grupo de usuários para conceder acesso a chave "%1": + + + Please select a key which to set the access group for! + Selecione uma chave para a atribuir o grupo de acesso! + + + Please perform the following steps to set up key file authentication: + Por favor, realize os passos a seguir para configurar a autenticação por arquivo chave: + + + 1) Create a key pair on the master computer. + 1) Crie um par de chaves no computador master. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Defina um grupo de acesso, cujos membros devem ter permissão para acessar outros computadores. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Exporte a chave pública e importe ela, com o mesmo nome, nos computadores clientes. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Por favor, consulte o <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Manual de administração do Veyon </a> para mais informações. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Um par de chaves de autenticação consiste em duas chaves criptografadas, uma chave privada e uma chave pública. +A chave privada concede acesso ao computador cliente através do computador master. +É importante que somente usuários autorizados tenham acesso de leitura ao arquivo da chave privada. +A chave pública é usada no computadores clientes para autenticar as requisições de conexão do computador master. + + + + AuthKeysManager + + Please check your permissions. + Por favor verifique suas permissões. + + + Key name contains invalid characters! + Chave contém caracteres inválidos! + + + Invalid key type specified! Please specify "%1" or "%2". + Chave inválida! Especifique "%1" or "%2". + + + Specified key does not exist! Please use the "list" command to list all installed keys. + A chave especificada não existe! Use o comando "list" para listar todas as chaves instaladas. + + + One or more key files already exist! Please delete them using the "delete" command. + Um ou mais arquivos de chave já existem! Exclua-os usando o comando "delete". + + + Creating new key pair for "%1" + Criando um novo par de chaves para "%1" + + + Failed to create public or private key! + Falha ao criar uma chave pública o privada! + + + Newly created key pair has been saved to "%1" and "%2". + O par de chaves recém-criado foi salvo em "% 1" e "% 2". + + + Could not remove key file "%1"! + Não foi possível remover o arquivo da chave "%1"! + + + Could not remove key file directory "%1"! + Não foi possível remover a pasta da chave "%1"! + + + Failed to create directory for output file. + Falha ao criar a pasta para guardar as chaves. + + + File "%1" already exists. + Arquivo "%1" já existe. + + + Failed to write output file. + Falha ao gravar no arquivo. + + + Key "%1/%2" has been exported to "%3" successfully. + Chave "%1/%2" foi exportada para "%3" com sucesso. + + + Failed read input file. + Falha ao ler o arquivo. + + + File "%1" does not contain a valid private key! + O arquivo "%1" não possui uma chave privada válida! + + + File "%1" does not contain a valid public key! + O arquivo "%1" não possui uma chave pública válida! + + + Failed to create directory for key file. + Falha ao criar a pasta para o arquivo de chave. + + + Failed to write key file "%1". + Falha ao escrever no arquivo da chave "%1". + + + Failed to set permissions for key file "%1"! + Falha ao definir as permissões para o arquivo de chave "%1"! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + A chave "%1/%2" foi importada com sucesso. Verifique as permissões de arquivo de "%3" para evitar acessos não autorizados. + + + Failed to convert private key to public key + Falha ao converter chave privada em chave pública. + + + Failed to create directory for private key file "%1". + Falha ao criar diretório para o arquivo de chave privada "%1". + + + Failed to save private key in file "%1"! + Falha ao salvar a chave privada no arquivo "%1"! + + + Failed to set permissions for private key file "%1"! + Falha ao definir permissões para o arquivo de chave privada "%1"! + + + Failed to create directory for public key file "%1". + Falha ao criar diretório para o arquivo de chave pública "%1". + + + Failed to save public key in file "%1"! + Falha ao salvar a chave pública no arquivo "%1"! + + + Failed to set permissions for public key file "%1"! + Falha ao definir permissões para o arquivo de chave pública "%1"! + + + Failed to set owner of key file "%1" to "%2". + Falha ao definir o proprietário da chave "%1" para "%2". + + + Failed to set permissions for key file "%1". + Falha ao definir permissões para o arquivo de chave "%1". + + + Key "%1" is now accessible by user group "%2". + A chave "%1" agora está acessível ao grupo de usuários "%2". + + + <N/A> + <N/A> + + + Failed to read key file. + Falha ao ler o arquivo de chave. + + + + AuthKeysPlugin + + Create new authentication key pair + Criar um novo par de chaves de autenticação + + + Delete authentication key + Apagar chave de autenticação + + + List authentication keys + Listar chaves de autenticação + + + Import public or private key + Importar chave pública ou privada + + + Export public or private key + Exportar chave pública ou privada + + + Extract public key from existing private key + Extrair chave pública de um chave privada existente + + + Set user group allowed to access a key + Definir grupo de usuários com permissão para acessar uma chave + + + KEY + CHAVE + + + ACCESS GROUP + GRUPO DE ACESSO + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + Este comando ajusta as permissões de acesso ao arquivo <KEY> de modo que apenas o grupo de usuários <ACCESS GROUP> tenha acesso de leitura a ele. + + + NAME + NOME + + + FILE + ARQUIVO + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + Este comando lista todas as chaves de autenticação disponíveis no diretório de chaves configurado. Se a opção "%1" for especificada, uma tabela com detalhes da chave será exibida. Alguns detalhes podem estar faltando se uma chave não estiver acessível. devido à falta de permissões de leitura. + + + Please specify the command to display help for! + Especifique o comando para o qual exibir ajuda! + + + TYPE + Tipo + + + PAIR ID + ID DO PAR + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + Comandos para gerenciar chaves de autenticação + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + Nome + + + Type + Tipo + + + Access group + Grupo de acesso + + + Pair ID + ID do par + + + + BuiltinDirectoryConfigurationPage + + Computers + Computadores + + + Name + Nome + + + Host address/IP + Endereço do Host/IP + + + MAC address + Endereço MAC + + + Add new computer + Adicionar novo computador + + + Remove selected computer + Remover computador selecionado + + + New computer + Novo computador + + + Builtin directory + Diretório integrado + + + Locations & computers + Locais e computadores + + + Locations + Locais + + + Add new location + Adicionar novo local + + + Remove selected location + Remover local selecionado + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + Novo local + + + + BuiltinDirectoryPlugin + + Show help for specific command + Exibir ajuda para o comando específico + + + Import objects from given file + Importar objetos de arquivo + + + Export objects to given file + Exportar objetos para arquivo + + + Invalid type specified. Valid values are "%1" or "%2". + Tipo inválido especificado. Os valores válidos são "%1" ou "%2". + + + Type + Tipo + + + Name + Nome + + + Host address + Ip do host + + + MAC address + Endereço MAC + + + Specified object not found. + Objeto não encontrado. + + + File "%1" does not exist! + O arquivo "%1" não existe! + + + Can't open file "%1" for reading! + Não é possível abrir o arquivo "%1" para leitura! + + + Unknown argument "%1". + Argumento desconhecido "%1". + + + Computer "%1" (host address: "%2" MAC address: "%3") + Computador "%1" (endereço do host: "%2" endereço MAC: "%3") + + + Unclassified object "%1" with ID "%2" + Objeto não classificado "%1" com ID "%2" + + + None + Nenhum + + + Computer + Computador + + + Root + Root + + + Invalid + Inválido + + + Error while parsing line %1. + Erro ao analisar a linha %1. + + + Network object directory which stores objects in local configuration + Diretório de objetos de rede que armazena objetos em configuração local + + + Commands for managing the builtin network object directory + Comandos para gerenciar o diretório interno de objetos de rede + + + No format string or regular expression specified! + Nenhuma string de formato ou expressão regular especificada! + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + Objeto UUID + + + Parent UUID + + + + Add a location or computer + + + + Clear all locations and computers + + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + + + + FILE + ARQUIVO + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + Tipo + + + NAME + NOME + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + OBJETO + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + Remover um computador pelo nome + + + Remove an object by UUID + + + + "Room 01" + "Sala 01" + + + "Computer 01" + "Computador 01" + + + HOST ADDRESS + Endereço host + + + MAC ADDRESS + Endereço MAC + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Servidor VNC embutido (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Servidor VNC embutido (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Servidor/endereço IP: %1 + + + Active features: %1 + Funcionalidades ativas: %1 + + + Online and connected + Online e conectado + + + Establishing connection + Estabelecendo conexão + + + Computer offline or switched off + Computador offline ou desligado + + + Authentication failed or access denied + Falha na autenticação ou acesso negado + + + Disconnected + Desconectado + + + No user logged on + Nenhum usuário logado + + + Logged on user: %1 + Usuário logado: %1 + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 Serviço %2 em %3:%4 + + + Authentication error + Erro de autenticação + + + Remote access + Acesso remoto + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + Erro no controle de acesso + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + Usuário + + + Missing network object directory plugin + Está faltando o plugin de diretório de objetos de rede + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Nenhum plugin de diretório de objetos de rede padrão foi encontrado. Por favor verifique sua instalação ou configure um backend de diretório de objetos de rede diferente via o Configurador %1. + + + Location detection failed + + + + Computer name;Hostname;User + Nome do computador;Hostname;Usuário + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + Pesquisa de computador + + + Add location + Adicionar local + + + Save computer/user list + Salvar lista de computador/usuário + + + Select output filename + Selecione o nome do arquivo de saída + + + CSV files (*.csv) + Arquivos CSV (*.csv) + + + File error + Erro de arquivo + + + Could not write the computer and users list to %1! Please check the file access permissions. + Não foi possível salvar a lista de computadores e usuários em %1! Verifique as permissões de acesso ao arquivo. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Por favor especifique um arquivo existente para importar. + + + Please specify a valid filename for the configuration export. + Por favor especifique um nome valido para o arquivo exportado. + + + Please specify a valid key. + Por favor especifique uma chave válida. + + + Specified key does not exist in current configuration! + Chave especificada não existe na configuração atual. + + + Please specify a valid value. + Por favor especifique um valor valido. + + + Configure Veyon at command line + Configurar Veyon pela linha de comando. + + + Output file is not writable! + Arquivo de saída não é gravável! + + + Output directory is not writable! + Diretório de saída não é gravável! + + + Configuration file is not readable! + O arquivo de configuração não pode ser lido! + + + Clear system-wide Veyon configuration + Apagar a configuração Veyon em todo o sistema + + + List all configuration keys and values + Listar todas as chaves de configuração e valores + + + Import configuration from given file + Importar configuração de um dado arquivo + + + Export configuration to given file + Exportar configuração de um dado arquivo + + + Read and output configuration value for given key + Valor de configuração de leitura e saída para a chave + + + Write given value to given configuration key + Escrever um valor para a chave de configuração + + + Unset (remove) given configuration key + Remover a chave de configuração + + + Commands for managing the configuration of Veyon + Comandos para gerenciar a configuração do Veyon + + + Upgrade and save configuration of program and plugins + Atualize e salve a configuração do programa e plug-ins + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + Não foi possível modificar a propriedade autostart (início automático) para o Serviço %1. + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + A configuração não é gravável. Por favor, verifique suas permissões! + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + Exibição %1 + + + + DemoConfigurationPage + + Demo server + Servidor de exibição + + + Tunables + Tunables + + + ms + ms + + + Key frame interval + Intervalo de frame da chave + + + Memory limit + Limite de memória + + + MB + MB + + + Update interval + Intervalo de atualização + + + s + s + + + Slow down thumbnail updates while demo is running + Diminua a velocidade das atualizações de miniaturas enquanto a demonstração está em execução + + + + DemoFeaturePlugin + + Stop demo + Parar exibição + + + Window demo + Exibir em janela + + + Give a demonstration by screen broadcasting + Exibir por transmissão de tela + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + Neste modo a sua tela está sendo exibida em uma janela em todos os computadores. Os usuários podem mudar para outras janelas como necessário. + + + Demo + Demonstração + + + Share your screen or allow a user to share his screen with other users. + Compartilhe sua tela ou permita que um usuário compartilhe sua tela com outros usuários. + + + Full screen demo + Demonstração em tela inteira + + + Share your own screen in fullscreen mode + Compartilhe sua própria tela em modo de tela inteira + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + Neste modo, sua tela está sendo exibida em modo de tela inteira em todos os computadores enquanto os dispositivos de entrada dos usuários estão bloqueados. + + + Share your own screen in a window + Compartilhe sua própria tela em uma janela + + + Share selected user's screen in fullscreen mode + Compartilhe a tela do usuário selecionado em modo de tela inteira + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + Neste modo, a tela do usuário selecionado é exibida em modo de tela inteira em todos os computadores enquanto os dispositivos de entrada dos usuários estão bloqueados. + + + Share selected user's screen in a window + Compartilhar a tela do usuário selecionado em uma janela + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + Neste modo, a tela do usuário selecionado é exibida em uma janela em todos os computadores. Os usuários podem alternar para outras janelas conforme necessário. + + + Please select a user screen to share. + Selecione uma tela de um usuário para compartilhar. + + + Please select only one user screen to share. + Selecione apenas uma tela de usuário para compartilhar. + + + All screens + Todas as telas + + + Screen %1 [%2] + Tela %1 [%2] + + + + DesktopAccessDialog + + Desktop access dialog + Caixa de diálogo de acesso ao desktop + + + Confirm desktop access + Confirmar acesso ao desktop + + + Never for this session + Nunca para esta sessão + + + Always for this session + Sempre para esta sessão + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + O usuário %1 no computador %2 deseja acessar o seu desktop. Permitir o acesso? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programas e sites + + + Predefined programs + Programas predefinidos + + + Name + Nome + + + Path + Caminho + + + Add new program + Adicionar novo programa + + + Remove selected program + Remover programa selecionado + + + Predefined websites + Sites predefinidos + + + Remove selected website + Remover sites selecionados + + + URL + URL + + + New program + Novo programa + + + New website + Novo website + + + + DesktopServicesFeaturePlugin + + Run program + Executar programa + + + Open website + Abrir website + + + Click this button to open a website on all computers. + Clique neste botão para abrir um website em todos os computadores. + + + Start programs and services in user desktop + Iniciar programas e serviços no desktop do usuário + + + Click this button to run a program on all computers. + Clique neste botão para executar um programa em todos os computadores. + + + Run program "%1" + Executar programa "%1" + + + Custom program + Programa personalizado + + + Open website "%1" + Abrir site "%1" + + + Custom website + Website personalizado + + + + DocumentationFigureCreator + + Teacher + Professor + + + Room %1 + Sala "%1" + + + Please complete all tasks within the next 5 minutes. + Por favor, complete todas as tarefas nos próximos 5 minutos. + + + Custom website + Website personalizado + + + Open file manager + Abrir gerenciador de arquivos + + + Start learning tool + Iniciar ferramenta de aprendizagem + + + Play tutorial video + Iniciar vídeo tutorial + + + Custom program + Programa personalizado + + + Handout + + + + Texts to read + Textos para ler + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + Servidor VNC externo + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Configuração do servidor VNC externo + + + Port: + Porta: + + + Password: + Senha: + + + + FeatureControl + + Feature control + Controle de funcionalidades + + + + FileTransferConfigurationPage + + File transfer + Transferência de arquivo + + + Directories + Diretórios + + + Destination directory + Diretório de destino + + + Default source directory + Diretório padrão de origem + + + Options + Opções + + + Remember last source directory + Lembrar último diretório de origem + + + Create destination directory if it does not exist + Crie o diretório de destino se ele não existir + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + Não foi possível abrir o arquivo "%1" para leitura! Por favor, verifique suas permissões! + + + + FileTransferDialog + + File transfer + Transferência de arquivo + + + Options + Opções + + + Transfer only + Apenas transferir + + + Transfer and open file(s) with associated program + Transferir e abrir arquivo(s) com programa associado + + + Transfer and open destination folder + Transferir e abrir a pasta de destino + + + Files + Arquivos + + + Start + Iniciar + + + Overwrite existing files + Sobrescrever arquivos existentes + + + + FileTransferPlugin + + File transfer + Transferência de arquivo + + + Click this button to transfer files from your computer to all computers. + Clique neste botão para transferir arquivos de seu computador para todos os computadores. + + + Select one or more files to transfer + Selecione um ou mais arquivos para transferir + + + Transfer files to remote computer + Transferir arquivos para computador remoto + + + Received file "%1". + Arquivo recebido: "%1". + + + Could not receive file "%1" as it already exists. + Não foi possível receber o arquivo "%1" porque ele já existe. + + + Could not receive file "%1" as it could not be opened for writing! + Não foi possível receber o arquivo "%1" porque ele não pôde ser aberto para gravação! + + + + GeneralConfigurationPage + + User interface + Interface do usuário + + + Language: + Idioma: + + + Use system language setting + Usar a configuração de idioma do sistema + + + Veyon + Veyon + + + Logging + Registrando + + + Log file directory + Diretório de arquivo de log + + + Log level + Nível de log + + + Nothing + Nada + + + Only critical messages + Somente mensagens críticas + + + Errors and critical messages + Mensagens críticas e de erros + + + Warnings and errors + Avisos e erros + + + Information, warnings and errors + Informações, avisos e erros + + + Debug messages and everything else + Mensagens de depuração e tudo o mais + + + Limit log file size + Limite do tamanho do arquivo de log + + + Clear all log files + Remover todos os arquivos de log + + + Log to standard error output + Log para a saída de erro padrão + + + Network object directory + Diretório de objetos de rede + + + Backend: + Backend: + + + Update interval: + Intervalo de atualização: + + + %1 service + serviço %1 + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + O serviço %1 precisa ser interrompido temporariamente para remover os arquivos de log. Continuar? + + + Log files cleared + Arquivos de log limpos + + + All log files were cleared successfully. + Todos os arquivos de log foram limpos com sucesso. + + + Error + Erro + + + Could not remove all log files. + Não foi possível remover todos os arquivos de log. + + + MB + MB + + + Rotate log files + Rodar arquivos de log + + + x + x + + + seconds + segundos + + + Write to logging system of operating system + Grave no sistema de registro do sistema operacional + + + Authentication + Autenticação + + + Method: + Método: + + + Logon authentication + Autenticação de logon + + + Key file authentication + Autenticação de arquivo de chave + + + Test + Teste + + + Authentication is set up properly on this computer. + A autenticação está configurada corretamente neste computador. + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + Teste de autenticação + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + Pesquisar LDAP + + + + LdapClient + + LDAP error description: %1 + Descrição de erro LDAP: %1 + + + + LdapConfigurationPage + + Basic settings + Configurações básicas + + + General + Geral + + + LDAP server and port + Servidor LDAP e porta + + + Bind DN + Bind DN + + + Bind password + Senha bind + + + Anonymous bind + Bind anônimo + + + Use bind credentials + Utilizar credenciais de bind + + + Base DN + Base DN + + + Fixed base DN + Base DN fixo + + + e.g. dc=example,dc=org + e.g. dc=exemplo,dc=org + + + Discover base DN by naming context + Descobrir o DN base por contexto de nomeação + + + e.g. namingContexts or defaultNamingContext + e.g. namingContexts ou defaultNamingContext + + + Environment settings + Configurações de ambiente + + + Object trees + Árvores de objeto + + + Computer tree + Árvore de computador + + + e.g. OU=Groups + e.g. OU=Grupos + + + User tree + Árvore de usuário + + + e.g. OU=Users + e.g. OU=Usuários + + + e.g. OU=Computers + e.g. OU=Computadores + + + Group tree + Árvore de grupo + + + Perform recursive search operations in object trees + Executar operações de pesquisa recursivas em árvores de objeto + + + Object attributes + Atributos de objeto + + + e.g. hwAddress + e.g. hwAddress + + + e.g. member or memberUid + e.g. member ou memberUid + + + e.g. dNSHostName + e.g. dNSHostName + + + Computer MAC address attribute + Atributo de endereço MAC do computador + + + Group member attribute + Atributo de membro do grupo + + + e.g. uid or sAMAccountName + e.g. uid ou sAMAccountName + + + Advanced settings + Configurações avançadas + + + Optional object filters + Filtros de objeto opcionais + + + Filter for user groups + FIltro para grupos de usuários + + + Filter for users + FIltro para usuários + + + Filter for computer groups + Filtro para grupos de computador + + + Group member identification + Identificação de membro do grupo + + + Distinguished name (Samba/AD) + Nome distinto (Samba/AD) + + + List all groups of a user + Listar todos os grupos de um usuário + + + List all groups of a computer + Listar todos os grupos de um computador + + + Get computer object by IP address + Obter objeto de computador pelo endereço IP + + + LDAP connection failed + Falha na conexão LDAP + + + LDAP bind failed + Falha no bind de LDAP + + + LDAP bind successful + Bind de LDAP bem sucedido + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Conexão com o servidor LDAP foi feita com sucesso e foi executado um bind de LDAP. As configurações LDAP básicas estão configuradas corretamente. + + + LDAP base DN test failed + Falha no teste de base DN LDAP + + + LDAP base DN test successful + Teste de base DN LDAP bem sucedido + + + LDAP naming context test failed + Falha no teste de contexto de nomeação LDAP + + + LDAP naming context test successful + Teste de contexto de nomeação LDAP bem sucedido + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + O contexto de nomeação LDAP foi consultado com sucesso. As seguintes entradas foram encontradas: +%1 + + + user tree + árvore de usuário + + + group tree + árvore de grupo + + + computer tree + árvore de computador + + + Enter username + Digite o nome do usuário + + + Please enter a user login name (wildcards allowed) which to query: + Por favor digite um nome de login de usuário (wildcards permitidos) para ser consultado: + + + user objects + objetos de usuário + + + Enter group name + Digite o nome do grupo + + + Please enter a group name whose members to query: + Por favor digite um nome de um grupo cujos membros devem ser consultados: + + + group members + Membros do grupo + + + Group not found + Grupo não encontrado + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + Não foi possível encontrar um grupo com o nome "%1". Por favor verifique o nome do grupo ou o parâmetro de árvore. + + + Enter computer name + Digite o nome do computador + + + computer objects + objetos do computador + + + Enter computer DN + Digite o DN do computador + + + Please enter the DN of a computer whose MAC address to query: + Por favor digite o DN de um computador cujos endereços MAC devem ser consultados: + + + computer MAC addresses + endereços MAC dos computadores + + + users + usuários + + + user groups + grupos de usuários + + + computer groups + grupos de computadores + + + Please enter a user login name whose group memberships to query: + Por favor digite um nome de login cujas associações de grupo devem ser consultadas: + + + groups of user + Grupos do usuário + + + User not found + Usuário não encontrado + + + groups of computer + grupos do computador + + + Computer not found + Computador não encontrado + + + Enter computer IP address + Digite o endereço IP do computador + + + Please enter a computer IP address which to resolve to an computer object: + Por favor digite um endereço de IP que deve ser resolvido para um objeto: + + + computers + computadores + + + LDAP %1 test failed + Falha no teste LDAP %1 + + + LDAP %1 test successful + Teste LDAP %1 bem sucedido + + + The %1 has been queried successfully and %2 entries were found. + O %1 foi consultado com sucesso e %2 entradas foram encontradas. + + + %1 %2 have been queried successfully: + +%3 + %1 %2 foram consultados com sucesso: + +%3 + + + LDAP filter test failed + Falha no teste de filtro LDAP + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + Não foi possível consultar nenhum %1 usando o filtro configurado. Por favor verifique o filtro LDAP para %1. + +%2 + + + LDAP filter test successful + Teste de filtro LDAP bem sucedido + + + %1 %2 have been queried successfully using the configured filter. + %1 %2 foram consultados com sucesso usando o filtro configurado. + + + (only if different from group tree) + (apenas se for diferente da árvore de grupo) + + + Computer group tree + Árvore de grupos de computador + + + computer group tree + árvore de grupos de computador + + + Filter for computers + Filtro para computadores + + + e.g. room or computerLab + e.g. sala ou salaDeComputador + + + Integration tests + Testes de integração + + + Computer groups + Grupos de computadores + + + e.g. name or description + e.g. nome ou descrição + + + Filter for computer containers + Filtro para contêineres de computador + + + Computer containers or OUs + Contêineres de computador ou OUs + + + Connection security + Segurança da conexão + + + TLS certificate verification + Verificação de certificado TLS + + + System defaults + Padrões do sistems + + + Never (insecure!) + Nunca (inseguro!) + + + Custom CA certificate file + Arquivo personalizado de certificado CA + + + None + Nenhum + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + ex.: (objectClass=computer) + + + e.g. (objectClass=group) + ex.: (objectClass=group) + + + e.g. (objectClass=person) + ex.: (objectClass=person) + + + e.g. (objectClass=room) or (objectClass=computerLab) + ex.: (objectClass=room) or (objectClass=computerLab) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + ex.: (objectClass=container) ou (objectClass=organizationalUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + Arquivos de certificados (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + Protocolo de encriptação + + + Computer location attribute + Atributo de nome de localização do computador + + + Computer display name attribute + Atributo de nome de exibição do computador + + + Location name attribute + Atributo de nome de localização + + + e.g. cn or displayName + Ex.: cn ou displayName + + + Computer locations identification + Identificação de localização do computador + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + Lista todas as entradas de um local + + + List all locations + Liste todos os locais + + + Enter computer display name + Insira o nome de exibição do computador + + + Please enter a computer display name to query: + Insira um nome de exibição do computador para consultar: + + + Enter computer location name + Insira o nome do local do computador + + + Please enter the name of a computer location (wildcards allowed): + Insira o nome de um local de computador (caracteres curinga permitidos): + + + computer locations + localizações de computador + + + Enter location name + Insira o nome do local + + + Please enter the name of a location whose entries to query: + Insira o nome de um local cujas entradas deseja consultar: + + + location entries + + + + LDAP test failed + Teste LDAP falhou. + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + e + + + LDAP test successful + Teste LDAP bem sucedio + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + Pesquisar + + + Test + Teste + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + Atributo de hostname do computador + + + Please enter a computer hostname to query: + + + + Invalid hostname + Hostname inválido + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + Você configurou nomes de host de computador para serem armazenados como nomes de domínio totalmente qualificados (FQDN), mas inseriu um nome de host sem domínio. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + Você configurou nomes de host de computador para serem armazenados como nomes de host simples sem um nome de domínio, mas inseriu um nome de host com uma parte do nome de domínio. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + Não foi possível encontrar um usuário com o nome "%1". Verifique o nome de usuário ou o parâmetro da árvore do usuário. + + + Enter hostname + Insira o nome do host + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + Não foi possível encontrar um computador com o nome de host "%1". Verifique o nome do host ou o parâmetro da árvore do computador. + + + Hostname lookup failed + A pesquisa de nome de host falhou + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + Não foi possível pesquisar o nome do host para o endereço IP "%1". Verifique as configurações do servidor DNS. + + + User login name attribute + Atributo de nome de login do usuário + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + Auto-configurar o DN base via contexto de nomeação + + + Query objects from LDAP directory + Objetos de query do diretório LDAP + + + Show help about command + Mostrar ajuda sobre comando + + + Commands for configuring and testing LDAP/AD integration + Comandos para configurar e testar integração LDAP/AD + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + Autenticaçã ode usuário + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Plugin implementando funções abstratas para a plataforma Linux + + + + LocationDialog + + Select location + + + + enter search filter... + insira o filtro de pesquisa... + + + + MainToolBar + + Configuration + Configuração + + + Disable balloon tooltips + Desativar balões tooltip + + + Show icons only + Exibir apenas ícones + + + + MainWindow + + MainWindow + JanelaPrincipal + + + toolBar + Ferrramentas + + + General + Geral + + + &File + &Arquivo + + + &Help + A&juda + + + &Quit + &Sair + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + Carregar c&onfigurações do arquivo + + + Ctrl+O + Ctrl+O + + + About Qt + Sobre o QT + + + Authentication impossible + Autenticação impossível + + + Configuration not writable + Configuração não gravável + + + Load settings from file + Carregar configurações de um arquivo + + + Save settings to file + Salvar configurações para arquivo + + + Unsaved settings + Configurações não salvas + + + There are unsaved settings. Quit anyway? + Existem configurações não salvas. Sair mesmo assim? + + + Veyon Configurator + Configurador Veyon + + + Service + Serviço + + + Master + Mestre + + + Access control + Controle de acesso + + + About Veyon + Sobre o Veyon + + + Auto + Auto + + + About + Sobre + + + %1 Configurator %2 + %1 Configurador %2 + + + JSON files (*.json) + arquivos JSON (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + O backend de configuração local relatou que a configuração não é gravável! Por favor execute o Configurador %1 com privilégios mais altos. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + Nenhum arquivo de autenticação de chave foi encontrado ou as existentes estão desatualizadas. Por favor crie novos arquivos de chave usando o Configurador %1. Alternativamente, configure a autenticação local usando o Configurador %1. Caso contrário você não poderá acessar computadores usando o %1. + + + Access denied + Acesso negado + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + De acordo com a configuração local você não tem permissão de acessar computadores na rede. Por favor entre com uma conta diferente or deixe o seu administrador de sistemas verificar a configuração local. + + + Screenshots + Capturas de tela + + + Feature active + Funcionalidade ativo + + + The feature "%1" is still active. Please stop it before closing %2. + A funcionalidade "%1" ainda está ativa. Por favor pare-a antes de fechar o %2. + + + Reset configuration + Resetar configuração + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Você quer mesmo resetar a configuração local e revertê-las para o modo padrão? + + + Search users and computers + Buscar usuários e computadores + + + Align computers to grid + + + + %1 Configurator + Configurador %1 + + + Insufficient privileges + Privilégios insuficientes + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Diretórios + + + User configuration + Configuração de usuário + + + Feature on computer double click: + No clique duplo: + + + Features + Funcionalidades + + + All features + Todos as funcionalidades + + + Disabled features + Funcionalidades desativadas + + + Screenshots + Capturas de tela + + + <no feature> + <no feature> + + + Basic settings + Configurações básicas + + + Behaviour + Comportamento + + + Enforce selected mode for client computers + Aplicar modo selecionado para computadores cliente + + + Hide local computer + Ocultar computador local + + + Hide computer filter field + Ocultar campo de filtros de computadores + + + Actions such as rebooting or powering down computers + Ações como reiniciar ou desligar computadores + + + User interface + Interface do usuário + + + Background color + Cor de plano de fundo + + + Thumbnail update interval + + + + ms + ms + + + Program start + Iniciar programa + + + Modes and features + Modos e funcionalidades + + + User and computer name + + + + Only user name + + + + Only computer name + + + + Computer thumbnail caption + + + + Text color + + + + Sort order + + + + Computer and user name + + + + Computer locations + + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + Auto + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + Monitoramento + + + Builtin monitoring mode + Modo de monitoramento embutido + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + Abrir website + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + Por favor digite o URL do website a ser aberto: + + + Name: + Nome: + + + + PasswordDialog + + Username + Nome de usuário + + + Password + Senha + + + Veyon Logon + Logon Veyon + + + Authentication error + Erro de autenticação + + + Logon failed with given username and password. Please try again! + Falha no logon com o nome de usuário e senha fornecidos. Por favor tente novamente! + + + Please enter your username and password in order to access computers. + Por favor digite o seu nome de usuário e senha para poder acessar os computadores. + + + + PowerControlFeaturePlugin + + Power on + Ligar + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Clique neste botão para ligar todos os computadores. Desse jeito você não precisará ligar cada computador manualmente. + + + Reboot + Reiniciar + + + Click this button to reboot all computers. + Clique neste botão para reiniciar todos os computadores. + + + Power down + Desligar + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Clique neste botão para desligar todos os computadores. Desse jeito você não precisará desligar cada computador manualmente. + + + Power on/down or reboot a computer + Ligar/desligar ou reiniciar um computador + + + Confirm reboot + Confirmar reiniciamento + + + Confirm power down + Confirmar desligamento + + + Do you really want to reboot the selected computers? + Tem certeza que quer reiniciar todos os computadores selecionados? + + + Do you really want to power down the selected computer? + Tem certeza que quer desligar os computadores selecionados? + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + Endereço MAC + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + Especifique o comando para o qual exibir ajuda! + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + Desligar + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + Exibir tela + + + Open a remote view for a computer without interaction. + Exibe a tela de um computador sem interação. + + + Remote control + Controlar remotamente + + + Open a remote control window for a computer. + Abra uma janela de controle remoto para um computador. + + + Remote access + Acesso remoto + + + Remote view or control a computer + Exibir tela ou controlar um computador + + + Please enter the hostname or IP address of the computer to access: + Por favor digite o hostname ou endereço IP do computador para acessar: + + + Show help about command + Mostrar ajuda sobre comando + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + Visualizar somente + + + Remote control + Controlar remotamente + + + Send shortcut + Enviar atalho + + + Fullscreen + Tela cheia + + + Window + Janela + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Menu + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Conectando %1 + + + Connected. + Conectado. + + + Screenshot + Captura de tela + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Por favor digite os programas ou comandos para executar no(s) computador(s) selecionado(s). Você pode separar múltiplos programas/comandos por linha. + + + Run programs + Executar programas + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + e.g. "C:\Arquivos de Programas\VideoLAN\VLC\vlc.exe" + + + Name: + Nome: + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + Bloquear computador + + + Unlock + Desbloquear computador + + + Lock screen and input devices of a computer + Bloquear a tela e dispositivos de entrada de um computador + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Com esse botão, você pode bloquear os computadores de todos os usuários para chamar a atenção deles. Nesse modo todos os dispositivos de entrada são travados e as telas são desligadas. + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + desconhecido + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + Não foi possível tirar uma captura de tela porque o diretório %1 não existe e não pôde ser criado. + + + Screenshot + Captura de tela + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + Captura de tela + + + Use this function to take a screenshot of selected computers. + Utilize esta função para tirar uma captura de tela dos computadores selecionados. + + + Screenshots taken + Capturas de tela tiradas + + + Screenshot of %1 computer have been taken successfully. + Captura de tela do computador %1 foi tirada com sucesso + + + Take screenshots of computers and save them locally. + Tire capturas de tela de computadores e salve-as localmente. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Todas as capturas de tela tiradas por você estão listadas aqui. Você pode tirar capturas de tela clicando no item "Captura de tela" em um menu de contexto de um computador. As capturas de tela podem ser gerenciadas usando os botões abaixo. + + + User: + Usuário: + + + Computer: + Computador: + + + Date: + Data: + + + Time: + Hora: + + + Show + Exibir + + + Delete + Excluir + + + Screenshot + Captura de tela + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Geral + + + Autostart + Iniciar automaticamente + + + Hide tray icon + Ocultar ícone da área de notificação + + + Start service + Iniciar serviço + + + Stopped + Parado + + + Stop service + Parar serviço + + + State: + Estado: + + + Enable firewall exception + Habilitar exceção de firewall + + + Allow connections from localhost only + Permitir conexões de localhost apenas + + + VNC server + Servidor VNC + + + Plugin: + Plugin: + + + Restart %1 Service + Reiniciar Serviço %1 + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Todas as configurações foram salvas com sucesso. Para entrarem em efeito, o serviço %1 precisa ser reiniciado. Reiniciá-lo agora? + + + Running + Em execução + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + Servidor de exibição + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + + + + Stopping service %1 + + + + Registering service %1 + + + + Unregistering service %1 + + + + Service control + + + + + ServiceControlPlugin + + Service is running + Serviço em execução + + + Service is not running + Serviço não está em execução + + + Configure and control Veyon service + Configurar e controlar serviço Veyon + + + Register Veyon Service + Registrar serviço Veyon + + + Unregister Veyon Service + Cancelar registro do serviço Veyon + + + Start Veyon Service + Iniciar Serviço Veyon + + + Stop Veyon Service + Parar Serviço Veyon + + + Restart Veyon Service + Reiniciar Serviço Veyon + + + Query status of Veyon Service + Estado de query do Serviço Veyon + + + Commands for configuring and controlling Veyon Service + Comandos para configurar e controlar o Serviço Veyon + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + O arquivo "%1" não existe! + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + Duração: + + + + SpotlightPanel + + Add selected computers + Adicionar computadores selecionados + + + Remove selected computers + Remover computadores selecionados + + + Update computers in realtime + Atualizar computadores em tempo real + + + Spotlight + + + + Please select at least one computer to add. + Selecione pelo menos um computador para adicionar. + + + Please select at least one computer to remove. + Selecione pelo menos um computador para remover. + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + Ícone de bandeja do sistema + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + Enviar mensagem de texto + + + Use the field below to type your message which will be sent to all selected users. + Use o campo abaixo para digitar sua mensagem que será enviada para todos os usuários selecionados. + + + + TextMessageFeaturePlugin + + Text message + Mensagem de texto + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Use esta função para enviar uma mensagem de texto para todos usuários e.g. para atribuir-lhes novas tarefas. + + + Message from teacher + Mensagem do Professor + + + Send a message to a user + Enviar uma mensagem para um usuário + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Habilitar captura de janelas em camadas (semi-transparente) + + + Poll full screen (leave this enabled per default) + Captar em tela cheia (deixar esta opção habilitada por padrão) + + + Low accuracy (turbo mode) + Baixa precisão (modo turbo) + + + Builtin UltraVNC server configuration + Configuração de servidor UltraVNC embutido + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + Máximo uso de CPU + + + + UserConfig + + No write access + Sem acesso de escrita + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Não foi possível salvar suas configurações pessoais! Por favor verifique o arquivo de configuração usando o configurador %1. + + + + UserLoginDialog + + User login + Usuário: + + + Please enter a username and password for automatic login on all computers. + Insira um nome de usuário e senha para login automático em todos os computadores. + + + Username + Nome de usuário + + + Password + Senha + + + + UserSessionControlPlugin + + Log in + Entrar + + + Click this button to log in a specific user on all computers. + Clique neste botão para fazer login de um usuário específico em todos os computadores. + + + Log off + Sair + + + Click this button to log off users from all computers. + Clique neste botão para fazer logoff de usuários de todos os computadores. + + + Confirm user logoff + Confirmar logoff + + + Do you really want to log off the selected users? + Você realmente deseja deslogar o usuário selecionado? + + + User session control + Controle de sessão do usuário. + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [FALHA] + + + Invalid command! + Comando inválido! + + + Available commands: + Comandos disponíveis: + + + Invalid arguments given + Argumentos inválidos + + + Not enough arguments given - use "%1 help" for more information + Argumentos dados insuficientes - use "Ajuda %1" para mais informação + + + Unknown result! + Resultado desconhecido! + + + Available modules: + Módulos disponíveis: + + + No module specified or module not found - available modules are: + Nenhum módulo especificado ou módulo não encontrado - os módulos disponíveis são: + + + Plugin not licensed + Plugin não licenciado + + + INFO + INFORMAÇÃO + + + ERROR + ERRO + + + USAGE + USO + + + DESCRIPTION + DESCRIÇÃO + + + EXAMPLES + EXEMPLOS + + + WARNING + ATENÇÃO + + + + VeyonServiceControl + + Veyon Service + + + + + VncViewWidget + + Establishing connection to %1 ... + Estabelecendo conexão para %1 ... + + + + WebApiConfigurationPage + + Web API + + + + General + Geral + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + s + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + Não foi possível mudar a configuração para geração SAS por software. Enviar Ctrl+Alt+Del via controle remoto não vai funcionar! + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + Geral + + + Enable SAS generation by software (Ctrl+Alt+Del) + Permitir geração SAS por software (Ctrl+Alt+Del) + + + Screen lock + Bloquear tela + + + Hide taskbar + Ocultar barra de tarefas + + + Hide start menu + Ocultar menu iniciar + + + Hide desktop + Ocultar desktop + + + User authentication + Autenticaçã ode usuário + + + Use alternative user authentication mechanism + Usar mecanismo de autenticação alternativo + + + User login + Usuário: + + + Input start delay + Inserir delay de início + + + Simulated key presses interval + Intervalo de simulação de pressionamento de teclas + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Plugin implementando funções abstratas para a plataforma WIndows + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + Serviço "%1" não foi encontrado. + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Configuração de servidor x11vnc embutida + + + Custom x11vnc parameters: + parâmetros x11vnc personalizados: + + + Do not use X Damage extension + Não use extensão X Damage + + + diff --git a/translations/veyon_pt_PT.ts b/translations/veyon_pt_PT.ts new file mode 100644 index 0000000..13efa74 --- /dev/null +++ b/translations/veyon_pt_PT.ts @@ -0,0 +1,4058 @@ + + + + + AboutDialog + + About + Sobre + + + Translation + Tradução + + + License + Licença + + + About Veyon + Sobre o Veyon + + + Contributors + Colaboradores + + + Version: + Versão: + + + Website: + Website: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Idioma atual ainda não traduzido (ou inglês nativo). + +Se está interessado em traduzir o Veyon para o seu idioma ou quer melhorar uma tradução contacte um programador do Veyon! + + + About %1 %2 + Sobre o %1 %2 + + + Support Veyon project with a donation + Suporte o projeto Veyon com um donativo + + + + AccessControlPage + + Computer access control + Controlo de acesso ao computador + + + Grant access to every authenticated user (default) + Permite acesso a todos os utilizadores autenticados (por defeito) + + + Test + Teste + + + Process access control rules + Processar regras de controlo de acesso + + + User groups authorized for computer access + Grupos de utilizadores autorizados por acesso ao computador + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Adicione os grupos cujos membros devem estar autorizados a aceder aos computadores na sua rede Veyon. + + + Authorized user groups + Grupos de utilizador autorizados + + + All groups + Todos os grupos + + + Access control rules + Regras de controlo do acesso + + + Add access control rule + Adicionar regra de controlo do acesso + + + Remove access control rule + Remover regra de controlo do acesso + + + Move selected rule down + Mover para baixo a regra selecionada + + + Move selected rule up + Mover para cima a regra selecionada + + + Edit selected rule + Editar a regra selecionada + + + Enter username + Introduza o nome de utilizador + + + Please enter a user login name whose access permissions to test: + + + + Access allowed + Acesso permitido + + + The specified user is allowed to access computers with this configuration. + O utilizador especificado pode aceder a computadores com esta configuração. + + + Access denied + Acesso negado + + + The specified user is not allowed to access computers with this configuration. + O utilizador especificado não pode aceder a computadores com esta configuração. + + + Enable usage of domain groups + + + + User groups backend: + + + + Missing user groups backend + + + + No default user groups plugin was found. Please check your installation! + + + + Restrict access to members of specific user groups + + + + + AccessControlRuleEditDialog + + Edit access control rule + Editar regras de regra de controlo do acesso + + + General + Geral + + + enter a short name for the rule here + digite aqui um nome curto para a regra + + + Rule name: + Nome da regra: + + + enter a description for the rule here + digite aqui uma descrição para a regra + + + Rule description: + Descrição da regra: + + + Invert all conditions ("is/has" interpreted as "is/has not") + + + + Conditions + Condições + + + is member of group + é membro do grupo + + + Accessing computer is localhost + + + + Accessing user is logged on user + + + + Accessing user is already connected + O utilizador a aceder já está conectado + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + + + + Action + Ação + + + Allow access + Permitir acesso + + + Deny access + Negar acesso + + + Ask logged on user for permission + + + + None (rule disabled) + Nenhuma (regras desativadas) + + + Accessing user + Utilizador a aceder + + + Accessing computer + Computador a aceder + + + Local (logged on) user + + + + Local computer + Computador local + + + Always process rule and ignore conditions + Processar sempre as regras e ignorar as condições + + + No user logged on + + + + Accessing user has one or more groups in common with local (logged on) user + + + + Accessing computer and local computer are at the same location + + + + is located at + + + + + AccessControlRulesTestDialog + + Access control rules test + Teste das regras de controlo do acesso + + + Accessing user: + Utilizador a aceder: + + + Local computer: + Computador local: + + + Accessing computer: + Computador a aceder: + + + Please enter the following user and computer information in order to test the configured ruleset. + + + + Local user: + Utilizador local: + + + Connected users: + Utiliadores conectados: + + + The access in the given scenario is allowed. + + + + The access in the given scenario is denied. + + + + The access in the given scenario needs permission of the logged on user. + + + + ERROR: Unknown action + + + + Test result + + + + + AuthKeysConfigurationPage + + Authentication keys + + + + Introduction + + + + Key file directories + + + + Public key file base directory + + + + Private key file base directory + + + + Available authentication keys + + + + Create key pair + + + + Delete key + + + + Import key + + + + Export key + + + + Set access group + + + + Key files (*.pem) + + + + Authentication key name + + + + Please enter the name of the user group or role for which to create an authentication key pair: + + + + Do you really want to delete authentication key "%1/%2"? + + + + Please select a key to delete! + + + + Please enter the name of the user group or role for which to import the authentication key: + + + + Please select a key to export! + + + + Please select a user group which to grant access to key "%1": + + + + Please select a key which to set the access group for! + + + + Please perform the following steps to set up key file authentication: + + + + 1) Create a key pair on the master computer. + + + + 2) Set an access group whose members should be allowed to access other computers. + + + + 3) Export the public key and import it on all client computers with the same name. + + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + + + + + AuthKeysManager + + Please check your permissions. + + + + Key name contains invalid characters! + + + + Invalid key type specified! Please specify "%1" or "%2". + + + + Specified key does not exist! Please use the "list" command to list all installed keys. + + + + One or more key files already exist! Please delete them using the "delete" command. + + + + Creating new key pair for "%1" + + + + Failed to create public or private key! + + + + Newly created key pair has been saved to "%1" and "%2". + + + + Could not remove key file "%1"! + + + + Could not remove key file directory "%1"! + + + + Failed to create directory for output file. + + + + File "%1" already exists. + + + + Failed to write output file. + + + + Key "%1/%2" has been exported to "%3" successfully. + + + + Failed read input file. + + + + File "%1" does not contain a valid private key! + + + + File "%1" does not contain a valid public key! + + + + Failed to create directory for key file. + + + + Failed to write key file "%1". + + + + Failed to set permissions for key file "%1"! + + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + + + + Failed to convert private key to public key + + + + Failed to create directory for private key file "%1". + + + + Failed to save private key in file "%1"! + + + + Failed to set permissions for private key file "%1"! + + + + Failed to create directory for public key file "%1". + + + + Failed to save public key in file "%1"! + + + + Failed to set permissions for public key file "%1"! + + + + Failed to set owner of key file "%1" to "%2". + + + + Failed to set permissions for key file "%1". + + + + Key "%1" is now accessible by user group "%2". + + + + <N/A> + + + + Failed to read key file. + + + + + AuthKeysPlugin + + Create new authentication key pair + + + + Delete authentication key + + + + List authentication keys + + + + Import public or private key + + + + Export public or private key + + + + Extract public key from existing private key + + + + Set user group allowed to access a key + + + + KEY + + + + ACCESS GROUP + + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + + NAME + + + + FILE + + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + + Please specify the command to display help for! + + + + TYPE + + + + PAIR ID + + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + + + + Type + + + + Access group + + + + Pair ID + + + + + BuiltinDirectoryConfigurationPage + + Computers + + + + Name + + + + Host address/IP + + + + MAC address + + + + Add new computer + + + + Remove selected computer + + + + New computer + + + + Builtin directory + + + + Locations & computers + + + + Locations + + + + Add new location + + + + Remove selected location + + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + + + + + BuiltinDirectoryPlugin + + Show help for specific command + + + + Import objects from given file + + + + Export objects to given file + + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + + + + Name + + + + Host address + + + + MAC address + + + + Specified object not found. + + + + File "%1" does not exist! + + + + Can't open file "%1" for reading! + + + + Unknown argument "%1". + + + + Computer "%1" (host address: "%2" MAC address: "%3") + + + + Unclassified object "%1" with ID "%2" + + + + None + + + + Computer + + + + Root + + + + Invalid + + + + Error while parsing line %1. + + + + Network object directory which stores objects in local configuration + + + + Commands for managing the builtin network object directory + + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + + + + Parent UUID + + + + Add a location or computer + + + + Clear all locations and computers + + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + + + + FILE + + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + + + + NAME + + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + + + + + ComputerControlListModel + + Host/IP address: %1 + + + + Active features: %1 + + + + Online and connected + + + + Establishing connection + + + + Computer offline or switched off + + + + Authentication failed or access denied + + + + Disconnected + + + + No user logged on + + + + Logged on user: %1 + + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + + + + Authentication error + + + + Remote access + + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + + + + Missing network object directory plugin + + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + + + + Add location + + + + Save computer/user list + + + + Select output filename + + + + CSV files (*.csv) + + + + File error + + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + + + + Please specify a valid filename for the configuration export. + + + + Please specify a valid key. + + + + Specified key does not exist in current configuration! + + + + Please specify a valid value. + + + + Configure Veyon at command line + + + + Output file is not writable! + + + + Output directory is not writable! + + + + Configuration file is not readable! + + + + Clear system-wide Veyon configuration + + + + List all configuration keys and values + + + + Import configuration from given file + + + + Export configuration to given file + + + + Read and output configuration value for given key + + + + Write given value to given configuration key + + + + Unset (remove) given configuration key + + + + Commands for managing the configuration of Veyon + + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + + + + + DemoConfigurationPage + + Demo server + + + + Tunables + + + + ms + + + + Key frame interval + + + + Memory limit + + + + MB + + + + Update interval + + + + s + + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + + + + Window demo + Demonstração em janela + + + Give a demonstration by screen broadcasting + + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + + + + Confirm desktop access + Confirme acesso ao Ambiente de Trabalho + + + Never for this session + + + + Always for this session + + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + + DesktopServicesConfigurationPage + + Programs & websites + + + + Predefined programs + + + + Name + + + + Path + + + + Add new program + + + + Remove selected program + + + + Predefined websites + + + + Remove selected website + + + + URL + + + + New program + + + + New website + + + + + DesktopServicesFeaturePlugin + + Run program + + + + Open website + + + + Click this button to open a website on all computers. + + + + Start programs and services in user desktop + + + + Click this button to run a program on all computers. + + + + Run program "%1" + + + + Custom program + + + + Open website "%1" + + + + Custom website + + + + + DocumentationFigureCreator + + Teacher + + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + + + + Port: + + + + Password: + + + + + FeatureControl + + Feature control + + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + + + + Destination directory + + + + Default source directory + + + + Options + + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + + + + Select one or more files to transfer + + + + Transfer files to remote computer + + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + + + + Language: + + + + Use system language setting + + + + Veyon + + + + Logging + + + + Log file directory + + + + Log level + + + + Nothing + + + + Only critical messages + + + + Errors and critical messages + + + + Warnings and errors + + + + Information, warnings and errors + + + + Debug messages and everything else + + + + Limit log file size + + + + Clear all log files + + + + Log to standard error output + + + + Network object directory + + + + Backend: + + + + Update interval: + + + + %1 service + + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + + + + All log files were cleared successfully. + + + + Error + + + + Could not remove all log files. + + + + MB + + + + Rotate log files + + + + x + + + + seconds + + + + Write to logging system of operating system + + + + Authentication + + + + Method: + + + + Logon authentication + + + + Key file authentication + + + + Test + Teste + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + + + + + LdapConfigurationPage + + Basic settings + + + + General + Geral + + + LDAP server and port + + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + + + + e.g. OU=Computers + + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + + LDAP base DN test failed + + + + LDAP base DN test successful + + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + Introduza o nome de utilizador + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + + + + Please enter a group name whose members to query: + + + + group members + + + + Group not found + + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + + + + users + + + + user groups + + + + computer groups + + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + + + + LDAP %1 test failed + + + + LDAP %1 test successful + + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + + + + TLS certificate verification + + + + System defaults + + + + Never (insecure!) + + + + Custom CA certificate file + + + + None + + + + TLS + + + + SSL + + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + Teste + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + + + + enter search filter... + + + + + MainToolBar + + Configuration + + + + Disable balloon tooltips + + + + Show icons only + + + + + MainWindow + + MainWindow + Janela Principal + + + toolBar + Barra de Farementas + + + General + Geral + + + &File + + + + &Help + + + + &Quit + + + + Ctrl+Q + + + + Ctrl+S + + + + L&oad settings from file + + + + Ctrl+O + + + + About Qt + + + + Authentication impossible + + + + Configuration not writable + + + + Load settings from file + + + + Save settings to file + + + + Unsaved settings + + + + There are unsaved settings. Quit anyway? + + + + Veyon Configurator + + + + Service + + + + Master + + + + Access control + + + + About Veyon + Sobre o Veyon + + + Auto + + + + About + Sobre + + + %1 Configurator %2 + + + + JSON files (*.json) + + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + Acesso negado + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + + + + Align computers to grid + + + + %1 Configurator + + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + + + + User configuration + + + + Feature on computer double click: + + + + Features + + + + All features + + + + Disabled features + + + + Screenshots + + + + <no feature> + + + + Basic settings + + + + Behaviour + + + + Enforce selected mode for client computers + + + + Hide local computer + + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + + + + User interface + + + + Background color + + + + Thumbnail update interval + + + + ms + + + + Program start + + + + Modes and features + + + + User and computer name + + + + Only user name + + + + Only computer name + + + + Computer thumbnail caption + + + + Text color + + + + Sort order + + + + Computer and user name + + + + Computer locations + + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + + + + Name: + + + + + PasswordDialog + + Username + + + + Password + + + + Veyon Logon + + + + Authentication error + + + + Logon failed with given username and password. Please try again! + + + + Please enter your username and password in order to access computers. + + + + + PowerControlFeaturePlugin + + Power on + Ligar + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + + Reboot + Reiniciar + + + Click this button to reboot all computers. + + + + Power down + Desligar + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + + + + Confirm reboot + + + + Confirm power down + + + + Do you really want to reboot the selected computers? + + + + Do you really want to power down the selected computer? + + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + Desligar + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + + + + Open a remote view for a computer without interaction. + + + + Remote control + Controlo remoto + + + Open a remote control window for a computer. + + + + Remote access + + + + Remote view or control a computer + + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + + + + Remote control + Controlo remoto + + + Send shortcut + + + + Fullscreen + + + + Window + Janela + + + Ctrl+Alt+Del + + + + Ctrl+Esc + + + + Alt+Tab + + + + Alt+F4 + + + + Win+Tab + + + + Win + + + + Menu + + + + Alt+Ctrl+F1 + + + + Connecting %1 + Ligando %1 + + + Connected. + Ligado + + + Screenshot + + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + + + + Unlock + + + + Lock screen and input devices of a computer + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + + + + Use this function to take a screenshot of selected computers. + + + + Screenshots taken + + + + Screenshot of %1 computer have been taken successfully. + + + + Take screenshots of computers and save them locally. + + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + + + + Computer: + + + + Date: + + + + Time: + + + + Show + + + + Delete + + + + Screenshot + + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Geral + + + Autostart + + + + Hide tray icon + + + + Start service + + + + Stopped + + + + Stop service + + + + State: + + + + Enable firewall exception + + + + Allow connections from localhost only + + + + VNC server + + + + Plugin: + + + + Restart %1 Service + + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + + Running + + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + + + + Stopping service %1 + + + + Registering service %1 + + + + Unregistering service %1 + + + + Service control + + + + + ServiceControlPlugin + + Service is running + + + + Service is not running + + + + Configure and control Veyon service + + + + Register Veyon Service + + + + Unregister Veyon Service + + + + Start Veyon Service + + + + Stop Veyon Service + + + + Restart Veyon Service + + + + Query status of Veyon Service + + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + Enviar mensagem de texto + + + Use the field below to type your message which will be sent to all selected users. + + + + + TextMessageFeaturePlugin + + Text message + Mensagem de texto + + + Use this function to send a text message to all users e.g. to assign them new tasks. + + + + Message from teacher + + + + Send a message to a user + + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + + + + Poll full screen (leave this enabled per default) + + + + Low accuracy (turbo mode) + + + + Builtin UltraVNC server configuration + + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + + + + Password + + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + + + + + VeyonCore + + [OK] + + + + [FAIL] + + + + Invalid command! + + + + Available commands: + + + + Invalid arguments given + + + + Not enough arguments given - use "%1 help" for more information + + + + Unknown result! + + + + Available modules: + + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + + + + INFO + + + + ERROR + + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + + + + + VncViewWidget + + Establishing connection to %1 ... + + + + + WebApiConfigurationPage + + Web API + + + + General + Geral + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + Geral + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + + + + Custom x11vnc parameters: + + + + Do not use X Damage extension + + + + diff --git a/translations/veyon_ru.ts b/translations/veyon_ru.ts new file mode 100644 index 0000000..ea71f9b --- /dev/null +++ b/translations/veyon_ru.ts @@ -0,0 +1,4083 @@ + + + + + AboutDialog + + About + О программе + + + Translation + Перевод + + + License + Лицензия + + + About Veyon + О программе Veyon + + + Contributors + Авторы + + + Version: + Версия: + + + Website: + Веб-сайт: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Перевод на данный язык ещё не осуществлён. + +Если Вы желаете добавить перевод программы Veyon на новый язык или улучшить уже существующий, пожалуйста, свяжитесь с разработчиками Veyon! + + + About %1 %2 + О программе %1 %2 + + + Support Veyon project with a donation + Поддержите проект Veyon пожертвованием + + + + AccessControlPage + + Computer access control + Контроль доступа к компьютеру + + + Grant access to every authenticated user (default) + Предоставлять доступ каждому аутентифицированному пользователю (по умолчанию) + + + Test + Тестировать + + + Process access control rules + Правила контроля доступа к процессу + + + User groups authorized for computer access + Группы пользователей, разрешенные для доступа к компьютеру + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Добавьте группы, участники которых должны иметь право доступа к компьютерам в вашей сети Veyon. + + + Authorized user groups + Разрешенные группы пользователей + + + All groups + Все группы + + + Access control rules + Правила контроля доступа + + + Add access control rule + Добавить правило контроля доступа + + + Remove access control rule + Удалить правило контроля доступа + + + Move selected rule down + Переместить выделенное правило вниз + + + Move selected rule up + Переместить выделенное правило вверх + + + Edit selected rule + Редактировать выделенное правило + + + Enter username + Введите имя пользователя + + + Please enter a user login name whose access permissions to test: + Введите имя пользователя для входа в систему, чтобы протестировать права доступа: + + + Access allowed + Доступ разрешен + + + The specified user is allowed to access computers with this configuration. + Указанному пользователю разрешено обращаться к компьютерам с этой конфигурацией. + + + Access denied + Доступ запрещён + + + The specified user is not allowed to access computers with this configuration. + Указанному пользователю не разрешается обращаться к компьютерам с этой конфигурацией. + + + Enable usage of domain groups + Включить использование доменных групп + + + User groups backend: + Группы пользователей: + + + Missing user groups backend + Отсутствующие группы пользователей + + + No default user groups plugin was found. Please check your installation! + Не был найден плагин по умолчанию для пользовательских групп. Проверьте свою установку! + + + Restrict access to members of specific user groups + Ограничить доступ участникам указанных групп пользователей + + + + AccessControlRuleEditDialog + + Edit access control rule + Изменить правило контроля доступа + + + General + Главное + + + enter a short name for the rule here + Введите здесь короткое название правила + + + Rule name: + Название правила: + + + enter a description for the rule here + Введите здесь описание правила + + + Rule description: + Описание правила: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Инвертировать все условия («быть / иметь» интерпретируется как «не быть / не иметь») + + + Conditions + Условия + + + is member of group + является членом группы + + + Accessing computer is localhost + Доступ к компьютеру как localhost + + + Accessing user is logged on user + Доступ к пользователю осуществляется пользователем + + + Accessing user is already connected + Доступ к пользователю уже подключен + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Если активировано более одного условия, каждое условие должно соответствовать для применения правила (логическое И). Если требуется только одно из нескольких условий (логическое ИЛИ), создайте несколько правил контроля доступа. + + + Action + Действие + + + Allow access + Разрешить доступ + + + Deny access + Запретить доступ + + + Ask logged on user for permission + Запросить у вошедшего пользователя полномочия + + + None (rule disabled) + Нет (правило отключено) + + + Accessing user + Доступ к пользователю + + + Accessing computer + Доступ к компьютеру + + + Local (logged on) user + Локальный (вошедший в систему) пользователь + + + Local computer + Локальный компьютер + + + Always process rule and ignore conditions + Всегда обрабатывать правило и игнорировать условия + + + No user logged on + Пользователь не вошел + + + Accessing user has one or more groups in common with local (logged on) user + Доступ к пользователю имеет одна или несколько групп, совместно с локальным пользователем (вошедшим в систему) + + + Accessing computer and local computer are at the same location + Компьютер для доступа расположен в том же месте, что и локальный компьютер + + + is located at + расположен в + + + + AccessControlRulesTestDialog + + Access control rules test + Проверка правил контроля доступа + + + Accessing user: + Доступ к пользователю: + + + Local computer: + Локальный компьютер: + + + Accessing computer: + Доступ к компьютеру: + + + Please enter the following user and computer information in order to test the configured ruleset. + Пожалуйста, введите следующую информацию о пользователе и компьютере, чтобы проверить настроенный набор правил. + + + Local user: + Локальный пользователь: + + + Connected users: + Подключенные пользователи: + + + The access in the given scenario is allowed. + Доступ в данном сценарии разрешен. + + + The access in the given scenario is denied. + Доступ в данном сценарии отклонен. + + + The access in the given scenario needs permission of the logged on user. + Доступ в данном сценарии требует разрешения зарегистрированного пользователя. + + + ERROR: Unknown action + ОШИБКА: Неизвестное действие + + + Test result + Результат испытаний + + + + AuthKeysConfigurationPage + + Authentication keys + Ключи аутентификации + + + Introduction + Вступление + + + Key file directories + Файловый каталог с ключами + + + Public key file base directory + Каталог, содержащий открытые ключи + + + Private key file base directory + Каталог, содержащий закрытые ключи + + + Available authentication keys + Доступные ключи аутентификации + + + Create key pair + Создать ключевую пару + + + Delete key + Удалить ключ + + + Import key + Импорт ключа + + + Export key + Экспорт ключа + + + Set access group + Установить группу доступа + + + Key files (*.pem) + Ключевые файлы (*.pem) + + + Authentication key name + Имя ключа аутентификации + + + Please enter the name of the user group or role for which to create an authentication key pair: + Введите имя группы пользователей или роли, для которой необходимо создать ключевую пару аутентификации: + + + Do you really want to delete authentication key "%1/%2"? + Вы действительно хотите удалить ключ аутентификации «%1/%2»? + + + Please select a key to delete! + Выберите ключ для удаления! + + + Please enter the name of the user group or role for which to import the authentication key: + Введите имя группы пользователей или роли, для которой необходимо импортировать ключ аутентификации: + + + Please select a key to export! + Выберите ключ для экспорта! + + + Please select a user group which to grant access to key "%1": + Выберите группу пользователей, которая предоставит доступ к ключу «%1»: + + + Please select a key which to set the access group for! + Выберите ключ, для которого необходимо установить группу доступа! + + + Please perform the following steps to set up key file authentication: + Для настройки проверки подлинности ключа выполните следующие действия: + + + 1) Create a key pair on the master computer. + 1) Создайте ключевую пару на главном компьютере. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Установите группу доступа, члены которой должны иметь доступ к другим компьютерам. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Экспортируйте открытый ключ и импортируйте его на всех клиентских компьютерах с тем же именем. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Дополнительную информацию см.<a href="https://veyon.readthedocs.io/en/latest/admin/index.html">В Руководстве администратора Veyon</a>. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Ключевая пара аутентификации состоит из двух связанных криптографических ключей, частного и открытого ключа. +Закрытый ключ позволяет пользователям на главном компьютере обращаться к клиентским компьютерам. +Важно, чтобы только авторизованные пользователи имели доступ к чтению в файл закрытого ключа. +Открытый ключ используется на клиентских компьютерах для аутентификации входящего запроса на соединение. + + + + AuthKeysManager + + Please check your permissions. + Пожалуйста, проверьте свои права доступа. + + + Key name contains invalid characters! + Имя ключа содержит недопустимые символы! + + + Invalid key type specified! Please specify "%1" or "%2". + Указан недопустимый тип ключа! Укажите «%1» или «%2». + + + Specified key does not exist! Please use the "list" command to list all installed keys. + Указанный ключ не существует! Пожалуйста, используйте команду «list», чтобы перечислить все установленные ключи. + + + One or more key files already exist! Please delete them using the "delete" command. + Один или несколько ключевых файлов уже существуют! Удалите их, используя команду «удалить». + + + Creating new key pair for "%1" + Создание новой ключевой пары для «%1» + + + Failed to create public or private key! + Не удалось создать открытый или закрытый ключ! + + + Newly created key pair has been saved to "%1" and "%2". + Вновь созданная пара ключей была сохранена в "%1" и "%2". + + + Could not remove key file "%1"! + Не удалось удалить файл ключа "%1"! + + + Could not remove key file directory "%1"! + Не удалось удалить каталог файла ключа "%1"! + + + Failed to create directory for output file. + Не удалось создать каталог для файла вывода данных. + + + File "%1" already exists. + Файл "%1" уже существует. + + + Failed to write output file. + Не удалось выполнить запись в файл вывода. + + + Key "%1/%2" has been exported to "%3" successfully. + Ключ "%1/%2" был успешно экспортирован в "%3". + + + Failed read input file. + Не удалось прочитать файл входных данных. + + + File "%1" does not contain a valid private key! + В файле "%1" не содержится корректного закрытого ключа! + + + File "%1" does not contain a valid public key! + В файле "%1" не содержится корректного открытого ключа! + + + Failed to create directory for key file. + Не удалось создать каталог для файла ключа. + + + Failed to write key file "%1". + Не удалось записать файл ключа "%1". + + + Failed to set permissions for key file "%1"! + Не удалось установить права доступа к файлу ключа "%1"! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + Ключ "%1/%2» был успешно импортирован. Пожалуйста, проверьте права доступа к файлу "%3», чтобы предотвратить неуполномоченный доступ. + + + Failed to convert private key to public key + Не удалось превратить закрытый ключ в открытый + + + Failed to create directory for private key file "%1". + Не удалось создать каталог файла закрытого ключа "%1". + + + Failed to save private key in file "%1"! + Не удалось сохранить закрытый ключ в файл "%1"! + + + Failed to set permissions for private key file "%1"! + Не удалось установить права доступа к файлу закрытого ключа "%1"! + + + Failed to create directory for public key file "%1". + Не удалось создать каталог для файла открытого ключа "%1". + + + Failed to save public key in file "%1"! + Не удалось сохранить открытый ключ в файл "%1"! + + + Failed to set permissions for public key file "%1"! + Не удалось установить права доступа к файлу открытого ключа "%1"! + + + Failed to set owner of key file "%1" to "%2". + Не удалось установить владельца файла ключа "%1" в значение "%2". + + + Failed to set permissions for key file "%1". + Не удалось установить права доступа к файлу ключа "%1". + + + Key "%1" is now accessible by user group "%2". + Ключ "%1" теперь доступен для группы пользователей "%2". + + + <N/A> + <н/д> + + + Failed to read key file. + Не удалось прочитать файл ключа. + + + + AuthKeysPlugin + + Create new authentication key pair + Создать новую пару ключей для аутентификации + + + Delete authentication key + Удалить ключ аутентификации + + + List authentication keys + Вывести список ключей аутентификации + + + Import public or private key + Импортировать открытый или закрытый ключ + + + Export public or private key + Экспортировать открытый или закрытый ключ + + + Extract public key from existing private key + Добыть открытый ключ из имеющегося закрытого ключа + + + Set user group allowed to access a key + Установить группу пользователей, которые будут иметь доступ к ключу + + + KEY + КЛЮЧ + + + ACCESS GROUP + ГРУППА ДОСТУПА + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + Эта команда корректирует права доступа к файлу <КЛЮЧ> так, что право на чтение этого файла получают только участники группы <ГРУППА ДОСТУПА>. + + + NAME + ИМЯ + + + FILE + ФАЙЛ + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Эта команда экспортирует ключ аутентификации <КЛЮЧ> в файл <ФАЙЛ>. Если файл <ФАЙЛ> не указан, то его название будет построено на основе данных о названии и типе ключа <КЛЮЧ>. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Эта команда импортирует ключ аутентификации <КЛЮЧ> из файла <файл> Если файл <ФАЙЛ> не указан, то его название будет построено на основе данных о названии и типе ключа <КЛЮЧ>. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + Эта команда выводит список всех ключей аутентификации в настроенном каталоге ключей. Если выбрана опция "%1", то вместо списка будет выведена таблица с подробностями про ключи. Некоторые параметры ключа могут быть не показаны, если доступ к ключу ограничен, например из-за нехватки прав на чтение файла ключа. + + + Please specify the command to display help for! + Пожалуйста, укажите команду, для которой следует показать справку! + + + TYPE + ТИП + + + PAIR ID + ИД ПАРЫ + + + Command line support for managing authentication keys + Поддержка управления ключами аутентификации в командной строке + + + Commands for managing authentication keys + Команды для управления ключами аутентификации + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + Эта команда создаёт новую пару ключей для аутентификации под названием <ИМЯ> и сохраняет закрытый и открытый ключи в настроенных каталогах ключей. Параметром должно быть имя ключа, которое должно состоять только из букв. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + Эта команда удаляет ключ аутентификации <КЛЮЧ> из настроенного каталога ключей. Пожалуйста, обратите внимание, что после изъятия ключ нельзя будет восстановить. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + Эта команда добывает часть, связанную с открытым ключом, из закрытого ключа <КЛЮЧ> и сохраняет её в соответствующий файла открытого ключа. Поэтому при настройке дополнительного основного компьютера достаточно перенести на него только закрытый ключ. После переноса открытый ключ можно просто добыть. + + + + AuthKeysTableModel + + Name + Имя + + + Type + Тип + + + Access group + Группа доступа + + + Pair ID + Ид. пары + + + + BuiltinDirectoryConfigurationPage + + Computers + Компьютеры + + + Name + Имя + + + Host address/IP + Адрес хоста/IP + + + MAC address + MAC-адрес + + + Add new computer + Добавить новый компьютер + + + Remove selected computer + Удалить выбранный компьютер + + + New computer + Новый компьютер + + + Builtin directory + Встроенный каталог + + + Locations & computers + Места и компьютеры + + + Locations + Места + + + Add new location + Добавить новое место + + + Remove selected location + Удалить выделенное место + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + Импорт файлов CSV можно выполнить с помощью интерфейса командной строки. Подробное описание можно найти в <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">интернет-документации</a>. + + + New location + Новое место + + + + BuiltinDirectoryPlugin + + Show help for specific command + Показать справку по определённой команде + + + Import objects from given file + Импортировать объекты из указанного файла + + + Export objects to given file + Экспортировать объекты в указанный файл + + + Invalid type specified. Valid values are "%1" or "%2". + Указан некорректный тип. Корректными являются значения "%1" и "%2". + + + Type + Тип + + + Name + Имя + + + Host address + Адрес хоста + + + MAC address + MAC-адрес + + + Specified object not found. + Указанного объекта не найдено. + + + File "%1" does not exist! + Файла "%1" не существует! + + + Can't open file "%1" for reading! + Не удалось открыть файл "%1" для чтения! + + + Unknown argument "%1". + Неизвестный аргумент "%1". + + + Computer "%1" (host address: "%2" MAC address: "%3") + Компьютер "%1" (адрес хоста: "%2" MAC-адрес: "%3") + + + Unclassified object "%1" with ID "%2" + Неклассифицированный объект "%1" с идентификатором "%2" + + + None + Нет + + + Computer + Компьютер + + + Root + Корень + + + Invalid + Некорректный + + + Error while parsing line %1. + Ошибка при обработке строки %1. + + + Network object directory which stores objects in local configuration + Каталог объектов сети, где хранятся объекты в локальной конфигурации + + + Commands for managing the builtin network object directory + Команды для управления каталогом встроенных объектов сети + + + No format string or regular expression specified! + Нет строки форматирования или не указано регулярное выражение! + + + Can't open file "%1" for writing! + Не удалось открыть файл "%1" для записи! + + + No format string specified! + Не указано строки формата! + + + Object UUID + UUID объекта + + + Parent UUID + Родительский UUID + + + Add a location or computer + Добавить место или компьютер + + + Clear all locations and computers + Удалить все записи мест и компьютеров + + + Dump all or individual locations and computers + Создать дамп всех или отдельных мест и компьютеров + + + List all locations and computers + Вывести список всех мест и компьютеров + + + Remove a location or computer + Удалить запись места или компьютера + + + Location "%1" + Место "%1" + + + Builtin (computers and locations in local configuration) + Встроенное (компьютеры и места в локальных настройках) + + + Location + Место + + + FILE + ФАЙЛ + + + LOCATION + МЕСТО + + + FORMAT-STRING-WITH-PLACEHOLDERS + ФОРМАТИРОВАННАЯ-СТРОКА-С-ЗАМЕНИТЕЛЯМИ + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + РЕГУЛЯРНОЕ-ВЫРАЖЕНИЕ-С-ЗАМЕНИТЕЛЯМИ + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Импортирует объекты из указанного текстового файла с использованием заданной строки форматирования или регулярного выражения, содержащей один или несколько заменителей. Действующими заменителями являются: %1 + + + Import simple CSV file to a single room + Импортировать простой файл CSV в отдельную запись класса + + + Import CSV file with location name in first column + Импортировать файл CSV с названием места в первом столбце + + + Import text file with with key/value pairs using regular expressions + Импортировать текстовый файл с парами ключ-значение на основе регулярных выражений + + + Import arbitrarily formatted data + Импортировать произвольно отформатированные данные + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Экспортирует объекты в указанный текстовый файл с использованием заданной строки форматирования, содержащей один или несколько заменителей. Действующими заменителями являются: %1 + + + Export all objects to a CSV file + Экспортировать все объекты в файл CSV + + + Export all computers in a specific location to a CSV file + Экспортировать все записи компьютеров в указанном месте в файл CSV + + + TYPE + ТИП + + + NAME + ИМЯ + + + PARENT + РОДИТЕЛЬ + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + Добавляет объект, где %1 может быть одной из записей, "%2" или "%3". %4 можно указать по названию или UUID. + + + Add a room + Добавить класс + + + Add a computer to room %1 + Добавить компьютер к классу %1 + + + OBJECT + ОБЪЕКТ + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Удаляет указанный объект из каталога. %1 можно указать по названию или UUID. Изъятие записи места приводит к изъятию всех связанных с ним записей компьютеров. + + + Remove a computer by name + Удалить компьютер по названию + + + Remove an object by UUID + Удалить объект по UUID + + + "Room 01" + "Класс 01" + + + "Computer 01" + "Компьютер 01" + + + HOST ADDRESS + АДРЕСА ХОСТА + + + MAC ADDRESS + MAC-АДРЕСА + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Встроенный сервер VNC (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Встроенный сервер VNC (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Хост/IP-адрес: %1 + + + Active features: %1 + Задействованные возможности: %1 + + + Online and connected + Онлайн и подключен + + + Establishing connection + Установка соединения + + + Computer offline or switched off + Компьютер в автономном режиме или выключен + + + Authentication failed or access denied + Ошибка аутентификации или доступ запрещен + + + Disconnected + Отключено + + + No user logged on + Пользователь не вошел + + + Logged on user: %1 + Вход в систему под пользователем: %1 + + + Location: %1 + Место: %1 + + + Veyon Server unreachable or not running + Сервер Veyon недоступен или не запущен + + + [no user] + [нет пользователя] + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 Сервис %2 в %3:%4 + + + Authentication error + Ошибка аутенфикации + + + Remote access + Удалённый доступ + + + User "%1" at host "%2" is now accessing this computer. + Сейчас доступ к этому компьютеру имеет пользователь "%1" на хосте "%2". + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + Пользователь "%1" на узле "%2" пытался получить доступ к этому компьютеру, но не смог пройти аутентификацию. + + + Access control error + Ошибка управления доступом + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + Пользователь "%1" на узле "%2" пытался получить доступ к этому компьютеру, но был заблокирован из-за параметров управления доступом. + + + Active connections: + Активные соединения: + + + + ComputerManager + + User + Пользователь + + + Missing network object directory plugin + Отсутствует плагин каталога сетевых объектов + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Не найден ни один плагин каталога сетевых объектов по умолчанию. Пожалуйста, проверьте корректность установки или настройте другой сервер каталога объектов сети через конфигуратор %1. + + + Location detection failed + Не удалось определить место + + + Computer name;Hostname;User + Имя компьютера; Имя хоста; Пользователь + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + Не удалось определить место этого компьютера. Это означает, что в настройках системы есть проблемы. На панели выбора компьютера будут показаны все места. + + + + ComputerSelectPanel + + Computer search + Компьютерный поиск + + + Add location + Добавить место + + + Save computer/user list + Сохранить список компьютеров / пользователей + + + Select output filename + Выберите имя выходного файла + + + CSV files (*.csv) + CSV-файлы (*.csv) + + + File error + Ошибка файла + + + Could not write the computer and users list to %1! Please check the file access permissions. + Не удалось записать список компьютеров и пользователей в %1! Пожалуйста, проверьте права доступа к файлу. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Пожалуйста, укажите существующий файл конфигурации для импорта. + + + Please specify a valid filename for the configuration export. + Пожалуйста, укажите допустимое имя файла для экспорта конфигурации. + + + Please specify a valid key. + Пожалуйста, укажите действующий ключ. + + + Specified key does not exist in current configuration! + Указанный ключ не существует в текущей конфигурации! + + + Please specify a valid value. + Укажите действительное значение. + + + Configure Veyon at command line + Настройка Veyon в командной строке + + + Output file is not writable! + Выходной файл не доступен для записи! + + + Output directory is not writable! + Каталог вывода не доступен для записи! + + + Configuration file is not readable! + Файл конфигурации не читается! + + + Clear system-wide Veyon configuration + Очистка всей системы Veyon + + + List all configuration keys and values + Список всех ключей и значений конфигурации + + + Import configuration from given file + Импорт конфигурации из указанного файла + + + Export configuration to given file + Экспорт конфигурации в указанный файл + + + Read and output configuration value for given key + Чтение и вывод значений конфигурации для заданного ключа + + + Write given value to given configuration key + Записать указанное значение в заданный ключ конфигурации + + + Unset (remove) given configuration key + Отменить (удалить) данный ключ конфигурации + + + Commands for managing the configuration of Veyon + Команды для управления настройками Veyon + + + Upgrade and save configuration of program and plugins + Обновить и сохранить настройки программы и дополнений + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + Не удалось изменить свойство автозапуска для сервиса %1. + + + Could not configure the firewall configuration for the %1 Server. + Не удалось настроить брандмауэр для сервера %1. + + + Could not configure the firewall configuration for the %1 Worker. + Не удалось настроить брандмауэр для рабочей станции %1. + + + Configuration is not writable. Please check your permissions! + Файл настроек непригоден для записи. Пожалуйста, проверьте права доступа к нему! + + + Could not apply platform-specific configuration settings. + Не удалось применить специфические для платформы параметры настроек. + + + + DemoClient + + %1 Demo + Демо %1 + + + + DemoConfigurationPage + + Demo server + Демонстрационный сервер + + + Tunables + Параметры настройки + + + ms + мс + + + Key frame interval + Интервал между ключевыми кадрами + + + Memory limit + Предел памяти + + + MB + МБ + + + Update interval + Интервал обновления + + + s + с + + + Slow down thumbnail updates while demo is running + Замедлить обновление миниатюр, пока запущена демонстрация + + + + DemoFeaturePlugin + + Stop demo + Оставить демонстрацию + + + Window demo + Демо в окне + + + Give a demonstration by screen broadcasting + Выполнить демонстрацию трансляцией изображения на экране + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + В этом режиме изображение с вашего экрана будет показано в окне на всех компьютерах. Пользователи, если захотят, смогут переключаться на другие окна. + + + Demo + Демонстрационный режим + + + Share your screen or allow a user to share his screen with other users. + Поделиться своим экраном или разрешить другим пользователям делиться своим экраном. + + + Full screen demo + Полноэкранная демонстрация + + + Share your own screen in fullscreen mode + Демонстрировать собственный экран в полноэкранном режиме + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + В этом режиме изображение с экрана вашего компьютера будет демонстрироваться на весь экран на всех компьютерах, а устройства ввода данных на компьютерах будут заблокированы. + + + Share your own screen in a window + Демонстрировать собственный экран в оконном режиме + + + Share selected user's screen in fullscreen mode + Демонстрировать экран выбранного пользователя в полноэкранном режиме + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + В этом режиме экран выбранного пользователя отображается в полноэкранном режиме на всех компьютерах, в то время как устройства ввода пользователей заблокированы. + + + Share selected user's screen in a window + Демонстрировать экран выбранного пользователя в оконном режиме + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + В этом режиме экран выбранного пользователя отображается в окне на всех компьютерах. При необходимости пользователи могут переключаться на другие окна. + + + Please select a user screen to share. + Выберите экран пользователя, который вы хотите демонстрировать. + + + Please select only one user screen to share. + Пожалуйста, выберите только один пользовательский экран для демонстрации. + + + All screens + Все экраны + + + Screen %1 [%2] + Экран %1 [%2] + + + + DesktopAccessDialog + + Desktop access dialog + Диалог доступа к рабочему столу + + + Confirm desktop access + Подтверждать доступ к рабочему столу + + + Never for this session + Никогда в этом сеансе + + + Always for this session + Всегда в этом сеансе + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + Пользователь %1 компьютера %2 пытается подключиться к Вашему рабочему столу. Разрешить ему доступ к Вашему рабочему столу? + + + + DesktopServicesConfigurationPage + + Programs & websites + Программы и сайты + + + Predefined programs + Предварительно определённые программы + + + Name + Имя + + + Path + Путь + + + Add new program + Добавить новую программу + + + Remove selected program + Удалить выделенную программу + + + Predefined websites + Предварительно определённые сайты + + + Remove selected website + Удалить выбранный сайт + + + URL + Адрес + + + New program + Новая программа + + + New website + Новый сайт + + + + DesktopServicesFeaturePlugin + + Run program + Запустить программу + + + Open website + Открыть веб-сайт + + + Click this button to open a website on all computers. + Нажмите эту кнопку для открытия веб-сайта на всех компьютерах. + + + Start programs and services in user desktop + Запуск программ и сервисов на рабочем столе пользователя + + + Click this button to run a program on all computers. + Нажмите эту кнопку для запуска программы на всех компьютерах. + + + Run program "%1" + Выполнить программу "%1" + + + Custom program + Необычная программа + + + Open website "%1" + Открыть сайт "%1" + + + Custom website + Необычный сайт + + + + DocumentationFigureCreator + + Teacher + Учитель + + + Room %1 + Класс %1 + + + Please complete all tasks within the next 5 minutes. + Пожалуйста, завершите все задачи в течение следующих 5 минут. + + + Custom website + Необычный сайт + + + Open file manager + Открыть программу для управления файлами + + + Start learning tool + Запустить инструмент обучения + + + Play tutorial video + Воспроизвести обучающее видео + + + Custom program + Необычная программа + + + Handout + Бесплатный образец + + + Texts to read + Фрагменты текста для чтения + + + generic-student-user + типичный-ученик-пользователь + + + + ExternalVncServer + + External VNC server + Внешний VNC-сервер + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Настройки внешнего VNC-сервера + + + Port: + Порт: + + + Password: + Пароль: + + + + FeatureControl + + Feature control + Управление функциями + + + + FileTransferConfigurationPage + + File transfer + Передача файлов + + + Directories + Каталоги + + + Destination directory + Целевой каталог + + + Default source directory + Исходный каталог по умолчанию + + + Options + Параметры + + + Remember last source directory + Запомнить последний исходный каталог + + + Create destination directory if it does not exist + Создайте целевой каталог, если он не существует + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + Не удалось открыть файл "%1" для чтения! Пожалуйста, проверьте, есть ли у вас достаточные права доступа! + + + + FileTransferDialog + + File transfer + Передача файлов + + + Options + Параметры + + + Transfer only + Только передача + + + Transfer and open file(s) with associated program + Передать и открыть файл(ы) с помощью связанной программы + + + Transfer and open destination folder + Передать и открыть папку назначения + + + Files + Файлы + + + Start + Начать + + + Overwrite existing files + Перезаписать существующие файлы + + + + FileTransferPlugin + + File transfer + Передача файлов + + + Click this button to transfer files from your computer to all computers. + Нажмите эту кнопку, чтобы передать файлы с компьютера на все компьютеры. + + + Select one or more files to transfer + Выберите один или несколько файлов для передачи + + + Transfer files to remote computer + Передать файлы на другой компьютер + + + Received file "%1". + Получен файл "%1". + + + Could not receive file "%1" as it already exists. + Не удалось получить файл "%1", поскольку такой файл уже существует. + + + Could not receive file "%1" as it could not be opened for writing! + Не удалось получить файл "%1", поскольку не удалось открыть соответствующий файл для записи данных! + + + + GeneralConfigurationPage + + User interface + Интерфейс пользователя + + + Language: + Язык: + + + Use system language setting + Использовать настройки системного языка + + + Veyon + Veyon + + + Logging + Ведение журнала + + + Log file directory + Каталог, содержащий файл журнала + + + Log level + Уровень журналирования + + + Nothing + Отключить журналирование + + + Only critical messages + Только критические ошибки + + + Errors and critical messages + Все ошибки + + + Warnings and errors + Предупреждения и ошибки + + + Information, warnings and errors + Информационные сообщения, предупреждения и ошибки + + + Debug messages and everything else + Все сообщения вместе с отладочной информацией + + + Limit log file size + Ограничить размер файла журнала + + + Clear all log files + Очистить все файлы журналов + + + Log to standard error output + Обычное журналирование + + + Network object directory + Каталог сетевых объектов + + + Backend: + Бэкэнд: + + + Update interval: + Интервал обновления: + + + %1 service + сервис %1 + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + Сервис %1 должен быть временно приостановлен для того, чтобы удалить файлы журналов. Продолжить? + + + Log files cleared + Файлы журналов были очищены + + + All log files were cleared successfully. + Все файлы журналов были успешно очищены. + + + Error + Ошибка + + + Could not remove all log files. + Невозможно удалить все файлы журналов. + + + MB + МБ + + + Rotate log files + Освежить файлы журналов + + + x + x + + + seconds + секунд + + + Write to logging system of operating system + Записывать в журнал операционной системы + + + Authentication + Аутентификация + + + Method: + Метод: + + + Logon authentication + Аутентификация при входе в систему + + + Key file authentication + Аутентификация с помощью ключа доступа + + + Test + Тестировать + + + Authentication is set up properly on this computer. + На этом компьютере аутентификация установлена правильно. + + + Authentication keys are not set up properly on this computer. + Ключи аутентификации на этом компьютере установлены неправильно. + + + Authentication test + Тест аутентификации + + + + HeadlessVncServer + + Headless VNC server + Автоматический VNC-сервер + + + + LdapBrowseDialog + + Browse LDAP + Навигация LDAP + + + + LdapClient + + LDAP error description: %1 + Описание ошибки LDAP: %1 + + + + LdapConfigurationPage + + Basic settings + Основные настройки + + + General + Главное + + + LDAP server and port + LDAP сервер и порт + + + Bind DN + DN для привязки + + + Bind password + Пароль привязки + + + Anonymous bind + Анонимная привязка + + + Use bind credentials + Регистрационные данные привязки + + + Base DN + Корневой DN + + + Fixed base DN + Фиксированный корневой DN + + + e.g. dc=example,dc=org + например, dc=example,dc=org + + + Discover base DN by naming context + Определить корневой DN по контексту имён + + + e.g. namingContexts or defaultNamingContext + например, namingContexts или defaultNamingContext + + + Environment settings + Настройки среды + + + Object trees + Деревья объектов + + + Computer tree + Дерево компьютеров + + + e.g. OU=Groups + например, OU=Groups + + + User tree + Дерево пользователей + + + e.g. OU=Users + например, OU=Users + + + e.g. OU=Computers + например, OU=Computers + + + Group tree + Дерево групп + + + Perform recursive search operations in object trees + Выполнить рекурсивные действия по поиску в деревьях объектов + + + Object attributes + Атрибуты объектов + + + e.g. hwAddress + например, hwAddress + + + e.g. member or memberUid + например, member или memberUid + + + e.g. dNSHostName + например, dNSHostName + + + Computer MAC address attribute + Атрибут MAC-адреса компьютера + + + Group member attribute + Атрибут членства в группе + + + e.g. uid or sAMAccountName + например, uid или sAMAccountName + + + Advanced settings + Расширенные настройки + + + Optional object filters + Опциональные фильтры объектов + + + Filter for user groups + Фильтр для групп пользователей + + + Filter for users + Фильтр для пользователей + + + Filter for computer groups + Фильтр для групп компьютеров + + + Group member identification + Идентификация участников группы + + + Distinguished name (Samba/AD) + Уникальное имя (Samba/AD) + + + List all groups of a user + Список всех групп пользователя + + + List all groups of a computer + Список всех групп компьютера + + + Get computer object by IP address + Получить объект компьютера по IP-адресу + + + LDAP connection failed + Неудачная попытка установить LDAP-соединение + + + LDAP bind failed + Ошибка привязки к LDAP + + + LDAP bind successful + Успешная привязка к LDAP + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Выполнено успешное соединение с сервером LDAP и привязка к LDAP. Должным образом настроены основные параметры LDAP. + + + LDAP base DN test failed + Не удалось пройти проверку корневого DN LDAP + + + LDAP base DN test successful + Успешная проверка корневого DN LDAP + + + LDAP naming context test failed + Не удалось пройти проверку контекста именования LDAP + + + LDAP naming context test successful + Успешно пройдена проверка контекста именования LDAP + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + Успешно выполнен опрос контекста именования LDAP. Выявлен такой корневой DN: +%1 + + + user tree + дерево пользователей + + + group tree + дерево групп + + + computer tree + дерево компьютеров + + + Enter username + Введите имя пользователя + + + Please enter a user login name (wildcards allowed) which to query: + Выберите запись пользователя (можно использовать символы-заменители), данные которого следует получить: + + + user objects + пользовательские объекты + + + Enter group name + Введите имя группы + + + Please enter a group name whose members to query: + Пожалуйста, укажите имя группы, участников которой следует определить: + + + group members + участники группы + + + Group not found + Группа не найдена + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + Не удалось найти группу с именем "%1". Пожалуйста, проверьте, правильно ли указано имя группы или параметр дерева групп. + + + Enter computer name + Введите имя компьютера + + + computer objects + объекты компьютеров + + + Enter computer DN + Укажите DN компьютера + + + Please enter the DN of a computer whose MAC address to query: + Пожалуйста, укажите DN компьютера, запрос по MAC-адресу которого следует прислать: + + + computer MAC addresses + MAC-адреса компьютера + + + users + пользователи + + + user groups + группы пользователей + + + computer groups + группы компьютеров + + + Please enter a user login name whose group memberships to query: + Пожалуйста, укажите имя записи пользователя, для кого следует получить данные об участии в группах: + + + groups of user + группы пользователя + + + User not found + Пользователь не найден + + + groups of computer + группы компьютера + + + Computer not found + Компьютер не найден + + + Enter computer IP address + Введите IP адрес компьютера + + + Please enter a computer IP address which to resolve to an computer object: + Пожалуйста, укажите IP-адрес компьютера, с которой следует определить объект компьютера: + + + computers + компьютеры + + + LDAP %1 test failed + Ошибка тестирования LDAP %1 + + + LDAP %1 test successful + Успешное тестирование LDAP %1 + + + The %1 has been queried successfully and %2 entries were found. + Успешно опрошено %1, выявлено %2 записей. + + + %1 %2 have been queried successfully: + +%3 + %1 %2 успешно опрошено: + +%3 + + + LDAP filter test failed + Ошибка тестирования фильтрации LDAP + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + Не удалось выполнить опрос для одного %1 с использованием настроенной фильтрации. Пожалуйста, проверьте, правильно ли указан фильтр LDAP для %1. + +%2 + + + LDAP filter test successful + Успешная проверка фильтрации LDAP + + + %1 %2 have been queried successfully using the configured filter. + %1 %2 успешно опрошен с помощью настроенного фильтра. + + + (only if different from group tree) + (только если отличается от дерева групп) + + + Computer group tree + Дерево групп компьютеров + + + computer group tree + дерево групп компьютеров + + + Filter for computers + Фильтр для компьютеров + + + e.g. room or computerLab + например, room или computerLab + + + Integration tests + Тестирования интеграции + + + Computer groups + Группы компьютеров + + + e.g. name or description + например, имя или описание + + + Filter for computer containers + Фильтр для контейнеров компьютеров + + + Computer containers or OUs + Контейнеры компьютеров или OU + + + Connection security + Защита соединения + + + TLS certificate verification + Проверка сертификата TLS + + + System defaults + Системные установки по умолчанию + + + Never (insecure!) + Никогда (небезопасно!) + + + Custom CA certificate file + Необычный файл службы сертификации (CA) + + + None + Нет + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + например, (objectClass=computer) + + + e.g. (objectClass=group) + например, (objectClass=group) + + + e.g. (objectClass=person) + например, (objectClass=person) + + + e.g. (objectClass=room) or (objectClass=computerLab) + например, (objectClass=room) или (objectClass=computerLab) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + например, (objectClass=container) или (objectClass=organizationalUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + Не удалось опросить настроенный базовый DN. Пожалуйста, проверите, правильно ли указан параметр базового DN. + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + Успешно опрошен базовый DN LDAP. Найдены следующие записи: + +%1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + Не удалось опросить базовый DN через контексты именования. Пожалуйста, проверите, правильно ли указан параметр атрибута контекста именования. + +%1 + + + Certificate files (*.pem) + Файлы сертификатов (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + Не удалось соединиться с сервером LDAP. Пожалуйста, проверьте, правильно ли указаны параметры сервера. + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + Не удалось привязаться к серверу LDAP. Пожалуйста, проверьте, правильно ли указаны параметры сервера и регистрационные данные привязки. + +%1 + + + Encryption protocol + Протокол шифрования + + + Computer location attribute + Атрибут места компьютера + + + Computer display name attribute + Атрибут показываемого названия компьютера + + + Location name attribute + Атрибут названия места + + + e.g. cn or displayName + например cn или displayName + + + Computer locations identification + Идентификация мест компьютеров + + + Identify computer locations (e.g. rooms) via: + Идентифицировать места компьютеров (например классы) на основе: + + + Location attribute in computer objects + Атрибут места в объектах компьютеров + + + List all entries of a location + Вывести список всех записей места + + + List all locations + Вывести список всех мест + + + Enter computer display name + Введите показываемое название компьютера + + + Please enter a computer display name to query: + Пожалуйста, укажите показываемое имя компьютера для запроса: + + + Enter computer location name + Введите название места компьютера + + + Please enter the name of a computer location (wildcards allowed): + Пожалуйста, введите имя места компьютера (можно использовать символы-заменители): + + + computer locations + места компьютеров + + + Enter location name + Введите название места + + + Please enter the name of a location whose entries to query: + Пожалуйста, укажите название места, запрос к участникам которого следует выполнить: + + + location entries + записи мест + + + LDAP test failed + Ошибка тестирования LDAP + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + Не удалось выполнить опрос для одного %1. Пожалуйста, проверьте параметр(ы) %2, или укажите имя имеющегося обьекта. + +%3 + + + and + и + + + LDAP test successful + Успешное тестирование LDAP + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + Не удалось опросить ни одну из записей в настроенной %1. Пожалуйста, проверьте, правильно ли указан параметр "%2". + +%3 + + + Browse + Навигация + + + Test + Тестировать + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + Имена хостов сохранены как полные доменные имена (FQDN, например myhost.example.org) + + + Computer hostname attribute + Атрибут имени хоста компьютера + + + Please enter a computer hostname to query: + Пожалуйста, укажите имя хоста компьютера для запроса: + + + Invalid hostname + Некорректное имя хоста + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + Вы настроили программу на хранение полных доменных имён хостов компьютеров (FQDN), но указали имя хоста без домена. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + Вы настроили программу на хранение простых имён хостов компьютеров, но указали название хоста вместе с именем домена. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + Не удалось найти пользователя с именем "%1". Пожалуйста, проверьте, правильно ли указано имя пользователя или параметр дерева пользователей. + + + Enter hostname + Введите имя хоста + + + Please enter a computer hostname whose group memberships to query: + Пожалуйста, укажите имя хоста компьютера, для кого следует получить данные об участии в группах: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + Не удалось найти компьютер с именем хоста "%1". Пожалуйста, проверьте, правильно ли указано имя хоста или параметр дерева компьютеров. + + + Hostname lookup failed + Ошибка поиска имени хоста + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + Не удалось выполнить поиск имени хоста для IP-адреса %1. Пожалуйста, проверьте, правильно ли указаны параметры вашего сервера DNS. + + + User login name attribute + Атрибут имени пользователя для входа + + + Configured attribute for user login name or computer hostname (OpenLDAP) + Настроенный атрибут для имени пользователя для входа или имени хоста компьютера (OpenLDAP) + + + computer containers + контейнеры компьютеров + + + + LdapPlugin + + Auto-configure the base DN via naming context + Автоматическое конфигурирование базового DN через контекст имён + + + Query objects from LDAP directory + Опросить объекты из каталога LDAP + + + Show help about command + Показать помощь по команде + + + Commands for configuring and testing LDAP/AD integration + Команды для конфигурирования и тестирования интеграции LDAP/AD + + + Basic LDAP/AD support for Veyon + Базовая поддержка LDAP/AD в Veyon + + + %1 (load computers and locations from LDAP/AD) + %1 (загрузить записи компьютеров и мест с LDAP/AD) + + + %1 (load users and groups from LDAP/AD) + %1 (загрузить записи пользователей и групп с LDAP/AD) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + Пожалуйста, укажите корректный адрес LDAP в следующем формате: "ldap[s]://[пользователь[:пароль]@]название_узла[:порт]" + + + No naming context attribute name given - falling back to configured value. + Не указан атрибут контекста именования - возвращаемся к настроенному значению. + + + Could not query base DN. Please check your LDAP configuration. + Не удалось обработать запрос по корневому DN. Пожалуйста, проверьте, правильно ли настроен LDAP. + + + Configuring %1 as base DN and disabling naming context queries. + Настраиваем %1 как корневой DN и выключаем запросы по контексту именования. + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + Нетипичная служба PAM для аутентификации пользователей + + + User authentication + Аутентификация пользователей + + + Session management + Управление сеансами + + + Display manager users + Пользователи управления дисплеем + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Плагин, который реализует абстрактные функции на платформе Linux + + + + LocationDialog + + Select location + Выбрать место + + + enter search filter... + введите фильтр поиска... + + + + MainToolBar + + Configuration + Конфигурация + + + Disable balloon tooltips + Отключить всплывающие подсказки + + + Show icons only + Показывать только значки + + + + MainWindow + + MainWindow + Главное окно + + + toolBar + Панель инструментов + + + General + Главное + + + &File + &Файл + + + &Help + &Помощь + + + &Quit + &Выход + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + Загрузить настройки из файла + + + Ctrl+O + Ctrl+O + + + About Qt + О пакете Qt + + + Authentication impossible + Невозможно выполнить аутентификацию + + + Configuration not writable + Файл конфигурации не доступен для записи + + + Load settings from file + Загрузить настройки из файла + + + Save settings to file + Сохранить настройки в файл + + + Unsaved settings + Несохранённые настройки + + + There are unsaved settings. Quit anyway? + Существуют несохранённые настройки. Выйти, не сохранив их? + + + Veyon Configurator + Конфигуратор Veyon + + + Service + Сервис + + + Master + Мастер + + + Access control + Контроль доступа + + + About Veyon + О программе Veyon + + + Auto + Авто + + + About + О программе + + + %1 Configurator %2 + %1 Конфигуратор %2 + + + JSON files (*.json) + Файлы JSON (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + Локальный сервер сообщил, что конфигурация защищена от записи. Запустите конфигуратор %1 с более высокими привилегиями. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + Не найдены файлы ключей аутентификации или они устарели. Пожалуйста, создайте новые файлы, используя конфигуратор %1. Как альтернатива установите аутентификацию входа, используя конфигуратор %1. Иначе Вы не сможете получить доступ к компьютерам с помощью %1. + + + Access denied + Доступ запрещён + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + Согласно локальных настроек вам запрещён доступ к компьютерам в сети. Пожалуйста, войдите из-под другой учётной записи или попросите администратора вашей системы изменить локальные настройки соответствующим образом. + + + Screenshots + Скриншоты + + + Feature active + Функция активна + + + The feature "%1" is still active. Please stop it before closing %2. + Функция "%1" всё ещё активна. Пожалуйста, остановите её перед закрытием %2. + + + Reset configuration + Сбросить конфигурацию + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Вы действительно хотите сбросить локальную конфигурацию и вернуть все настройки по умолчанию? + + + Search users and computers + Поиск пользователей и компьютеров + + + Align computers to grid + Выравнивать компьютеры по сетке + + + %1 Configurator + Конфигуратор %1 + + + Insufficient privileges + Недостаточные права доступа + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + Не удалось запустить программу с правами администратора. Пожалуйста, убедитесь, что в рабочей среде установлена sudo-подобная программа! Программа будет запущена с правами доступа обычного пользователя. + + + Only show powered on computers + Показывать только работающие компьютеры + + + &Save settings to file + &Сохранить параметры в файл + + + &View + &Просмотр + + + &Standard + С&тандартный + + + &Advanced + &Рвсширенный + + + Use custom computer arrangement + Использовать необычное расположение компьютеров + + + Locations && computers + Места && компьютеры + + + Slideshow + Слайд-шоу + + + Spotlight + Акцент + + + Adjust size of computer icons automatically + Автоматическая регулировка размера значков компьютера + + + + MasterConfigurationPage + + Directories + Каталоги + + + User configuration + Пользовательская конфигурация + + + Feature on computer double click: + Действие в ответ на двойной щелчок на компьютере: + + + Features + Функции + + + All features + Все функции + + + Disabled features + Отключенные функции + + + Screenshots + Скриншоты + + + <no feature> + <нет функции> + + + Basic settings + Основные настройки + + + Behaviour + Поведение + + + Enforce selected mode for client computers + Принудительно выбранный режим для компьютеров-клиентов + + + Hide local computer + Скрыть локальный компьютер + + + Hide computer filter field + Скрыть поле фильтрации компьютеров + + + Actions such as rebooting or powering down computers + Действия, в частности, перезагрузка и выключение компьютеров + + + User interface + Интерфейс пользователя + + + Background color + Цвет фона + + + Thumbnail update interval + Интервал обновления миниатюры + + + ms + мс + + + Program start + Запуск программы + + + Modes and features + Режимы и возможности + + + User and computer name + Пользователь и имя компьютера + + + Only user name + Только имя пользователя + + + Only computer name + Только имя компьютера + + + Computer thumbnail caption + Подпись миниатюры компьютера + + + Text color + Цвет текста + + + Sort order + Порядок сортировки + + + Computer and user name + Имя компьютера и пользователя + + + Computer locations + Места компьютеров + + + Show current location only + Показать только текущее место + + + Allow adding hidden locations manually + Разрешить добавление скрытых мест вручную + + + Hide empty locations + Скрыть пустые места + + + Show confirmation dialog for potentially unsafe actions + Показывать окно подтверждения для потенциально опасных действий + + + Perform access control + Выполнить управление доступом + + + Automatically select current location + Автоматически выбирать текущее место + + + Automatically open computer select panel + Автоматически открыть панель выбора компьютера + + + Hide local session + Скрыть локальные сессии + + + px + пикселей + + + Thumbnail spacing + Интервал миниатюр + + + Auto + Авто + + + Thumbnail aspect ratio + Соотношение сторон миниатюры + + + Automatically adjust computer icon size + Автоматически настраивать размер значка компьютера + + + Open feature windows on the same screen as the main window + Открывать окно функций на том же экране, что и главное окно + + + + MonitoringMode + + Monitoring + Мониторинг + + + Builtin monitoring mode + Встроенный режим мониторинга + + + This mode allows you to monitor all computers at one or more locations. + В этом режиме вы можете наблюдать за всеми компьютерами в одном или нескольких местах. + + + + NetworkObjectTreeModel + + Locations/Computers + Места/компьютеры + + + + OpenWebsiteDialog + + Open website + Открыть веб-сайт + + + e.g. Veyon + например Veyon + + + Remember and add to website menu + Запомнить и добавить в меню сайтов + + + e.g. www.veyon.io + например www.veyon.io + + + Please enter the URL of the website to open: + Введите URL-адрес веб-сайта для открытия: + + + Name: + Имя: + + + + PasswordDialog + + Username + Имя пользователя + + + Password + Пароль + + + Veyon Logon + Вход в систему Veyon + + + Authentication error + Ошибка аутенфикации + + + Logon failed with given username and password. Please try again! + Не удалось войти, используя указанные имя пользователя и пароль. Пожалуйста, повторите попытку! + + + Please enter your username and password in order to access computers. + Пожалуйста, введите имя пользователя и пароль для доступа компьютеров. + + + + PowerControlFeaturePlugin + + Power on + Включить + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Нажмите эту кнопку для включения всех компьютеров. Это позволит вам не включать каждый компьютер вручную. + + + Reboot + Перезагрузить + + + Click this button to reboot all computers. + Нажмите эту кнопку, чтобы перезагрузить все компьютеры. + + + Power down + Выключить + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Нажмите эту кнопку для выключения всех компьютеров. Это позволит вам не выключать каждый компьютер вручную. + + + Power on/down or reboot a computer + Включить/выключить или перезагрузить компьютер + + + Confirm reboot + Потверждение перезагрузки + + + Confirm power down + Потверждение выключения + + + Do you really want to reboot the selected computers? + Вы действительно хотите перезагрузить выбранные компьютеры? + + + Do you really want to power down the selected computer? + Вы действительно хотите выключить выбранный компьютер? + + + Power on a computer via Wake-on-LAN (WOL) + Включить компьютер через пробуждение по сигналу из локальной сети Wake-on-LAN (WOL) + + + MAC ADDRESS + MAC-АДРЕСА + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + Эта команда транслирует пакет Wake-on-LAN (WOL) в сеть с целью включения питания на компьютере с указанным MAC-адресом. + + + Please specify the command to display help for! + Пожалуйста, укажите команду, для которой следует показать справку! + + + Invalid MAC address specified! + Указан некорректный MAC-адрес! + + + Commands for controlling power status of computers + Команды для управления состоянием питания компьютеров + + + Power down now + Отключить сейчас + + + Install updates and power down + Установить обновления и выключить + + + Power down after user confirmation + Отключить после подтверждения + + + Power down after timeout + Выключить по истечении времени ожидания + + + The computer was remotely requested to power down. Do you want to power down the computer now? + Компьютер получил удалённую команду по выключению. Хотите выключить компьютер сейчас? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + Компьютер выключится через %1 минут, %2 секунд. + +Пожалуйста, сохраните результаты вашей работы и завершите работу всех программ. + + + + PowerDownTimeInputDialog + + Power down + Выключить + + + Please specify a timeout for powering down the selected computers: + Пожалуйста, укажите время ожидания выключения выбранного компьютера: + + + minutes + минут + + + seconds + секунд + + + + RemoteAccessFeaturePlugin + + Remote view + Удалённый просмотр + + + Open a remote view for a computer without interaction. + Открыть панель удалённого просмотра компьютера без вмешательства. + + + Remote control + Удалённое управление + + + Open a remote control window for a computer. + Открыть окно удалённого управления компьютером. + + + Remote access + Удалённый доступ + + + Remote view or control a computer + Удалённый просмотр или управление компьютером + + + Please enter the hostname or IP address of the computer to access: + Пожалуйста, укажите имя хоста или IP-адрес компьютера, доступ к которому необходимо получить: + + + Show help about command + Показать помощь по команде + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 — %2, удалённый доступ + + + %1 - %2 - %3 Remote Access + %1 - %2 - %3 Удаленный доступ + + + + RemoteAccessWidgetToolBar + + View only + Только трансляция + + + Remote control + Удалённое управление + + + Send shortcut + Отправить сокращение + + + Fullscreen + На весь экран + + + Window + В окне + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Меню + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Соединение с %1 + + + Connected. + Подключен. + + + Screenshot + Скриншот + + + Exit + Выход + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Пожалуйста, введите программы или команды для их выполнения на выбранных компьютерах. Вы можете записать несколько программ/команд, по одной в строке. + + + Run programs + Запустить программы + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + например, "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + Имя: + + + Remember and add to program menu + Запомнить и добавить в меню программ + + + e.g. VLC + например VLC + + + + ScreenLockFeaturePlugin + + Lock + Заблокировать + + + Unlock + Разблокировать + + + Lock screen and input devices of a computer + Заблокировать экран и входные устройства компьютера + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Чтобы завладеть вниманием пользователей, вы можете заблокировать их компьютеры с помощью этой кнопки. В этом режиме все устройства ввода данных будут заблокированы, а экраны станут чёрными. + + + Lock input devices + Заблокировать устройства ввода + + + Unlock input devices + Разблокировать устройства ввода + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + Вы можете использовать эту кнопку для привлечения полного внимания всех пользователей и блокировки их настольных компьютеров. В этом режиме блокируются все устройства ввода. + + + + Screenshot + + unknown + неизвестный + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + Невозможно сделать снимок экрана, так как директория %1 не существует и не может быть создана. + + + Screenshot + Скриншот + + + Could not open screenshot file %1 for writing. + Не удалось открыть файл снимка экрана %1 для записи. + + + + ScreenshotFeaturePlugin + + Screenshot + Скриншот + + + Use this function to take a screenshot of selected computers. + Воспользуйтесь этой возможностью для создания снимков экранов выбранных компьютеров. + + + Screenshots taken + Созданные снимки + + + Screenshot of %1 computer have been taken successfully. + Снимок экрана компьютера %1 успешно создан. + + + Take screenshots of computers and save them locally. + Создание снимков экранов компьютеров и их локальное хранение. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Здесь приведен список всех сделанных вами снимков окон. Вы можете создавать снимки экрана, выбирая пункт "Снимок экрана" в контекстном меню компьютера. Управлять снимками окон можно с помощью расположенных ниже кнопок. + + + User: + Пользователь: + + + Computer: + Компьютер: + + + Date: + Дата: + + + Time: + Время: + + + Show + Показать + + + Delete + Удалить + + + Screenshot + Скриншот + + + Do you really want to delete all selected screenshots? + Вы действительно хотите удалить выбранные скриншоты? + + + + ServiceConfigurationPage + + General + Главное + + + Autostart + Автозапуск + + + Hide tray icon + Скрыть иконку в трее + + + Start service + Запустить сервис + + + Stopped + Остановлен + + + Stop service + Остановить сервис + + + State: + Состояние: + + + Enable firewall exception + Включить исключения брандмауэра + + + Allow connections from localhost only + Разрешить только локальные подключения + + + VNC server + VNC-сервер + + + Plugin: + Плагин: + + + Restart %1 Service + Перезапуск сервиса %1 + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Все настройки были успешно сохранены. Для того чтобы они вступили в силу необходимо перезапустить сервис %1. Перезапустить его сейчас? + + + Running + Работает + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + Включение этого пункта приведёт к тому, что сервис будет запускать процесс сервера для каждого интерактивного сеанса на компьютере. +По умолчанию эта настройка требуется для реализации поддержки терминальных серверов. + + + Show notification on remote connection + Показывать оповещение по удалённому доступу + + + Show notification when an unauthorized access is blocked + Показывать оповещения, когда программа блокирует неавторизованный доступ + + + Sessions + Сессии + + + Single session mode (create server instance for local/physical session only) + Режим одиночного сеанса (создание сервера только для локального / физического сеанса) + + + Multi session mode (create server instance for each local and remote desktop session) + Многосессионный режим (создание сервера для каждого сеанса локального и удаленного рабочего стола) + + + Maximum session count + Максимальное количество сеансов + + + Network port numbers + Номера сетевых портов + + + Veyon server + Veyon сервер + + + Internal VNC server + Встроенный VNC-сервер + + + Feature manager + Менеджер функций + + + Demo server + Демонстрационный сервер + + + Miscellaneous network settings + Дополнительные настройки сети + + + + ServiceControl + + Starting service %1 + Запускаем сервис %1 + + + Stopping service %1 + Останавливаем сервис %1 + + + Registering service %1 + Регистрируем сервис %1 + + + Unregistering service %1 + Отменяем регистрацию сервиса %1 + + + Service control + Управление сервисам + + + + ServiceControlPlugin + + Service is running + Сервис работает + + + Service is not running + Сервис не работает + + + Configure and control Veyon service + Настройка и управление сервисом Veyon + + + Register Veyon Service + Регистрация сервиса Veyon + + + Unregister Veyon Service + Отмена регистрация сервиса Veyon + + + Start Veyon Service + Запустить сервис Veyon + + + Stop Veyon Service + Остановить сервис Veyon + + + Restart Veyon Service + Перезапустить сервис Veyon + + + Query status of Veyon Service + Определить состояние сервиса Veyon + + + Commands for configuring and controlling Veyon Service + Команды для настройки и управления сервисом Veyon + + + + ShellCommandLinePlugin + + Run command file + Запустить командный файл + + + File "%1" does not exist! + Файла "%1" не существует! + + + Interactive shell and script execution for Veyon Control + Интерактивная оболочка и средство выполнения скриптов для Управления Veyon + + + Commands for shell functionalities + Команды для возможностей оболочки + + + + SlideshowPanel + + Previous + Предыдущий + + + Start/pause + Старт/пауза + + + Next + Следующий + + + Duration: + Продолжительность: + + + + SpotlightPanel + + Add selected computers + Добавить выбранные компьютеры + + + Remove selected computers + Удалить выбранные компьютеры + + + Update computers in realtime + Обновлять компьютеры в реальном времени + + + Spotlight + Акцент + + + Please select at least one computer to add. + Выберите хотя бы один компьютер для добавления. + + + Please select at least one computer to remove. + Выберите хотя бы один компьютер для удаления. + + + Add computers by clicking with the middle mouse button or clicking the first button below. + Добавьте компьютеры, щелкнув средней кнопкой мыши или нажав первую кнопку ниже. + + + + SystemTrayIcon + + System tray icon + Иконка в системном трее + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + Модуль групп пользователей для групп пользователей системы + + + Default (system user groups) + По умолчанию (группы пользователей системы) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + Выполнить проверку внутренних компонентов и функций Veyon + + + Commands for testing internal components and functions of Veyon + Команды для проверки внутренних компонентов и функций Veyon + + + + TextMessageDialog + + Send text message + Послать текстовое сообщение + + + Use the field below to type your message which will be sent to all selected users. + Используйте это поле снизу для набора сообщения, которое хотите послать всем выбранным пользователям. + + + + TextMessageFeaturePlugin + + Text message + Текстовое сообщение + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Используйте эту функцию для посылки текстового сообщения всем пользователям, например, сказав им о новых задачах и т. д. + + + Message from teacher + Сообщение от учителя + + + Send a message to a user + Послать сообщение пользователю + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Включить захват с помощью полупрозрачного окна + + + Poll full screen (leave this enabled per default) + Опрос полного экрана (оставьте включенным по умолчанию) + + + Low accuracy (turbo mode) + Низкая чёткость (высокая скорость) + + + Builtin UltraVNC server configuration + Настройка встроенного сервера UltraVNC + + + Enable multi monitor support + Включить поддержку нескольких мониторов + + + Enable Desktop Duplication Engine on Windows 8 and newer + Включить двигатель дублирования рабочего стола в Windows 8 и более новых версиях + + + Maximum CPU usage + Максимальное использование ЦП + + + + UserConfig + + No write access + Нет доступа на запись + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Не удалось сохранить ваши личные настройки! Пожалуйста, проверьте, правильно ли указан путь к файлу настроек пользователей с помощью конфигуратора %1. + + + + UserLoginDialog + + User login + Имя пользователя + + + Please enter a username and password for automatic login on all computers. + Пожалуйста, введите имя пользователя и пароль для автоматического входа на все компьютеры. + + + Username + Имя пользователя + + + Password + Пароль + + + + UserSessionControlPlugin + + Log in + Залогиниться + + + Click this button to log in a specific user on all computers. + Нажмите эту кнопку, чтобы войти в систему под определённым пользователем на всех компьютерах. + + + Log off + Разлогиниться + + + Click this button to log off users from all computers. + Нажмите эту кнопку для разлогинивания пользователей со всех компьютеров. + + + Confirm user logoff + Потверждение выхода пользователя + + + Do you really want to log off the selected users? + Вы действительно хотите выполнить выход из системы для выбранных пользователей? + + + User session control + Управление сеансами пользователей + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [НЕУДАЧА] + + + Invalid command! + Неправильная команда! + + + Available commands: + Доступные команды: + + + Invalid arguments given + Даны неправильные аргументы + + + Not enough arguments given - use "%1 help" for more information + Дано недостаточно аргументов - используйте "справку %1" для большей информации + + + Unknown result! + Неизвестный результат! + + + Available modules: + Доступные модули: + + + No module specified or module not found - available modules are: + Не указан модуль или модуль не найден. Доступные модули: + + + Plugin not licensed + Плагин не лицензирован + + + INFO + ИНФОРМАЦИЯ + + + ERROR + ОШИБКА + + + USAGE + ИСПОЛЬЗОВАНИЕ + + + DESCRIPTION + ОПИСАНИЕ + + + EXAMPLES + ПРИМЕРЫ + + + WARNING + ПРЕДУПРЕЖДЕНИЕ + + + + VeyonServiceControl + + Veyon Service + Сервис Veyon + + + + VncViewWidget + + Establishing connection to %1 ... + Восстановление соединения с %1 ... + + + + WebApiConfigurationPage + + Web API + Веб-API + + + General + Главное + + + Network port + Сетевой порт + + + Enable WebAPI server + Включить Веб-API сервер + + + Connection settings + Настройки соединения + + + Lifetime + Срок службы + + + h + ч. + + + s + с + + + Idle timeout + Время ожидания бездействия + + + Authentication timeout + Время ожидания на подключение + + + Maximum number of open connections + Максимальное число открытых соединений + + + Connection encryption + Шифрование соединения + + + TLS certificate file + Файл TLS сертификата + + + TLS private key file + Файл приватного ключа TLS + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + Использовать HTTPS совместно с TLS 1.3 вместо HTTP + + + + WebApiPlugin + + Run WebAPI server + Включить Веб-API сервер + + + Failed to start WebAPI server at port %1 + Не удалось запустить веб-API сервер на порту %1 + + + WebAPI server running at port %1 + Веб-API сервер запущен на порту %1 + + + Provide access to a computer via HTTP + Обеспечить доступ к компьютеру по HTTP + + + Commands for running the WebAPI server + Команды для запуска Веб-API сервера + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + Не удалось изменить параметр для программного создания SAS. Удалённой отправки Ctrl+Alt+Del не будет! + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + Главное + + + Enable SAS generation by software (Ctrl+Alt+Del) + Включить программное создания SAS (Ctrl+Alt+Del) + + + Screen lock + Блокировка экрана + + + Hide taskbar + Скрыть панель задач + + + Hide start menu + Скрыть меню запуска + + + Hide desktop + Скрыть рабочий стол + + + User authentication + Аутентификация пользователей + + + Use alternative user authentication mechanism + Использовать альтернативный механизм аутентификации пользователей + + + User login + Имя пользователя + + + Input start delay + Задержка входа + + + Simulated key presses interval + Имитированный интервал между нажатиями клавиш + + + Confirm legal notice (message displayed before user logs in) + Подтвердить официальное уведомление (сообщение отображается перед входом пользователя) + + + Use input device interception driver + Использовать драйвер перехвата устройства ввода + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Плагин, который реализует абстрактные функции на платформе Windows + + + + WindowsServiceControl + + The service "%1" is already installed. + Служба «%1» уже установлена. + + + The service "%1" could not be installed. + Не удалось установить службу «%1». + + + The service "%1" has been installed successfully. + Служба «%1» была успешно установлена. + + + The service "%1" could not be uninstalled. + Не удалось деинсталлировать службу «%1». + + + The service "%1" has been uninstalled successfully. + Служба «%1» была успешно деинсталирована. + + + The start type of service "%1" could not be changed. + Не удалось изменить тип запуска службы «%1». + + + Service "%1" could not be found. + Не удалось найти службу «%1». + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Настройка встроенного сервера x11vnc + + + Custom x11vnc parameters: + Нетипичные параметры x11vnc: + + + Do not use X Damage extension + Не использовать расширение X Damage + + + diff --git a/translations/veyon_sl.ts b/translations/veyon_sl.ts new file mode 100644 index 0000000..def131a --- /dev/null +++ b/translations/veyon_sl.ts @@ -0,0 +1,4081 @@ + + + + + AboutDialog + + About + Vizitka + + + Translation + Prevod + + + License + Licenca + + + About Veyon + O programu + + + Contributors + Sodelavci + + + Version: + Različica: + + + Website: + Spletna stran: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Trenutni jezik še ni preveden (ali je materni angleški jezik). + +Če ste zainteresirani za prevajanje Veyon v svoj lokalni ali drug jezik ali želite izboljšati obstoječi prevod, se obrnite na razvijalca Veyon! + + + About %1 %2 + Vizitka %1 %2 + + + Support Veyon project with a donation + Podprite projekt Veyon s prispevkom + + + + AccessControlPage + + Computer access control + Nadzor dostopa do računalnika + + + Grant access to every authenticated user (default) + Dodeli dostop do vsakega preverjenega uporabnika (privzeto) + + + Test + Preizkus + + + Process access control rules + Pravila za nadzor dostopa do procesov + + + User groups authorized for computer access + Uporabniške skupine, pooblaščene za dostop do računalnika + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Prosimo, dodajte skupine, katerih člani bi morali biti pooblaščeni za dostop do računalnikov v vašem omrežju Veyon + + + Authorized user groups + Pooblaščene skupine uporabnikov + + + All groups + Vse skupine + + + Access control rules + Pravila za nadzor dostopa + + + Add access control rule + Dodajte pravilo za nadzor dostopa + + + Remove access control rule + Odstranite pravilo za nadzor dostopa + + + Move selected rule down + Premaknite izbrano pravilo navzdol + + + Move selected rule up + Premaknite izbrano pravilo navzgor + + + Edit selected rule + Uredite izbrano pravilo + + + Enter username + Vnesite uporabniško ime + + + Please enter a user login name whose access permissions to test: + Vnesite uporabniško ime za prijavo, kateremu je dovoljen dostop za preizkus: + + + Access allowed + Dostop dovoljen + + + The specified user is allowed to access computers with this configuration. + Navedeni uporabnik lahko dostopa do računalnikov s to konfiguracijo. + + + Access denied + Dostop zavrnjen + + + The specified user is not allowed to access computers with this configuration. + Navedeni uporabnik ne sme dostopati do računalnikov s to konfiguracijo. + + + Enable usage of domain groups + Omogočite uporabo skupinam domen + + + User groups backend: + Uporabniške skupine ozadja: + + + Missing user groups backend + Manjkajoče skupine uporabnikov ozadja + + + No default user groups plugin was found. Please check your installation! + Noben privzeti vtičnik uporabniških skupin ni bil najden. Prosimo, preverite namestitev! + + + Restrict access to members of specific user groups + Omejite dostop do članov določenih skupin uporabnikov + + + + AccessControlRuleEditDialog + + Edit access control rule + Urejanje pravila za nadzor dostopa + + + General + Splošno + + + enter a short name for the rule here + vpišite kratko ima za pravilo + + + Rule name: + Ime pravila: + + + enter a description for the rule here + tukaj vnesite opis pravila + + + Rule description: + Opis pravila: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Obrni vse pogoje ("je/ima" razloženo kot "je/ni") + + + Conditions + Pogoji + + + is member of group + je član skupine + + + Accessing computer is localhost + Dostopni računalnik je lokalni gostitelj + + + Accessing user is logged on user + Dostopni uporabnik je prijavljen na uporabnik + + + Accessing user is already connected + Dostopni uporabnik je že povezan + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Če je aktiviranih več pogojev, se mora izpolniti vsak pogoj, da se pravilo uporabi (logični AND). Če se mora izpolniti samo eden od več pogojev (logični OR), ustvarite več pravil za nadzor dostopa. + + + Action + Dejanje + + + Allow access + Dovoli dostop + + + Deny access + Prepovej dostop + + + Ask logged on user for permission + Vprašaj prijavljenega uporabnika za dovoljenje + + + None (rule disabled) + Brez (pravila onemogočena) + + + Accessing user + Dostopni uporabnik + + + Accessing computer + Dostopni računalnik + + + Local (logged on) user + Lokalni (prijavljeni) uporabnik + + + Local computer + Lokalni računalnik + + + Always process rule and ignore conditions + Vedno obdelaj pravila in prezri pogoje + + + No user logged on + Uporabnik ni prijavljen na + + + Accessing user has one or more groups in common with local (logged on) user + Dostopni uporabnik ima eno ali več skupnin, skupnih z lokalnim (prijavljenim) uporabnikom + + + Accessing computer and local computer are at the same location + Dostop do računalnika in lokalni računalnik sta na isti lokaciji + + + is located at + se nahaja na + + + + AccessControlRulesTestDialog + + Access control rules test + Test pravila za nadzor dostopa + + + Accessing user: + Dostopni uporabnik: + + + Local computer: + Lokalni računalnik: + + + Accessing computer: + Dostopni računalnik: + + + Please enter the following user and computer information in order to test the configured ruleset. + Prosim, vnesite naslednje podatke o uporabniku in računalniku, da preizkusite nastavljen pravilnik. + + + Local user: + Lokalni uporabnik: + + + Connected users: + Povezani uporabniki: + + + The access in the given scenario is allowed. + Dostop v danem scenariju je dovoljen. + + + The access in the given scenario is denied. + Dostop v danem scenariju je zavrnjen. + + + The access in the given scenario needs permission of the logged on user. + Dostop v danem scenariju potrebuje dovoljenje prijavljenega uporabnika + + + ERROR: Unknown action + NAPAKA: neznano dejanje + + + Test result + Rezultati testa + + + + AuthKeysConfigurationPage + + Authentication keys + Ključi preverjanja pristnosti + + + Introduction + Uvod + + + Key file directories + Imeniki datoteke ključa + + + Public key file base directory + Imenik datoteke z javnim ključem + + + Private key file base directory + Imenik datoteke z zasebnim ključem + + + Available authentication keys + Razpoložljivi ključi za preverjanje pristnosti + + + Create key pair + Ustvari par ključev + + + Delete key + Izbriši ključ + + + Import key + Uvozi ključ + + + Export key + Izvozi ključ + + + Set access group + Nastavite skupino za dostop + + + Key files (*.pem) + Datoteke ključa (*.pem) + + + Authentication key name + Ime ključa za preverjanje pristnosti + + + Please enter the name of the user group or role for which to create an authentication key pair: + Vnesite ime skupine uporabnikov ali vzorec za ustvarjanje para ključev za preverjanje pristnosti: + + + Do you really want to delete authentication key "%1/%2"? + Ali res želite izbrisati ključ za preverjanje pristnosti "%1/%2"? + + + Please select a key to delete! + Prosimo, izberite ključ za brisanje! + + + Please enter the name of the user group or role for which to import the authentication key: + Vnesite ime uporabniške skupine ali vzorca, za katero želite uvoziti ključ za preverjanje pristnosti: + + + Please select a key to export! + Prosimo, izberite ključ za izvoz! + + + Please select a user group which to grant access to key "%1": + Izberite uporabniško skupino, ki naj odobri dostop do ključa "%1": + + + Please select a key which to set the access group for! + Izberite ključ, za katerega želite nastaviti skupino za dostop! + + + Please perform the following steps to set up key file authentication: + Če želite nastaviti overjanje datotek ključa, naredite naslednje: + + + 1) Create a key pair on the master computer. + 1) Ustvarite par ključev na glavnem računalniku. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Nastavite skupino za dostop, katere člani bi morali imeti dostop do drugih računalnikov. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Izvozite javni ključ in ga uvozite v vse odjemalske računalnike z istim imenom. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Prosimo, obiščite <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon skrbniški priročnik</a> za več informacij. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Par ključev za preverjanje pristnosti je sestavljen iz dveh povezanih kriptografskih ključev, zasebnega in javnega ključa. +Zasebni ključ omogoča uporabnikom na glavnem računalniku dostop do odjemalskih računalnikov. +Pomembno je, da imajo samo pooblaščeni uporabniki dostop do datoteke zasebnega ključa. +Javni ključ se uporablja v odjemalskih računalnikih za preverjanje pristnosti dohodne povezave. + + + + AuthKeysManager + + Please check your permissions. + Prosimo, preverite svoja dovoljenja. + + + Key name contains invalid characters! + Ime ključa vsebuje neveljavne znake! + + + Invalid key type specified! Please specify "%1" or "%2". + Določena je neveljavna vrsta ključa! Prosim, navedite "%1" ali "%2". + + + Specified key does not exist! Please use the "list" command to list all installed keys. + Določen ključ ne obstaja! Uporabite ukaz "seznam", za seznam vseh nameščenih ključev. + + + One or more key files already exist! Please delete them using the "delete" command. + Ena ali več datotek ključa že obstaja! Izbrišite jih z ukazom "izbriši". + + + Creating new key pair for "%1" + Ustvarjanje novega para ključev za "%1" + + + Failed to create public or private key! + Ni uspelo ustvariti javnega ali zasebnega ključa! + + + Newly created key pair has been saved to "%1" and "%2". + Na novo ustvarjen par ključev je shranjen v "%1" in "%2". + + + Could not remove key file "%1"! + Datoteke ključa "%1" ni bilo mogoče odstraniti! + + + Could not remove key file directory "%1"! + Imenika datoteke ključa "%1" ni bilo mogoče odstraniti! + + + Failed to create directory for output file. + Ni bilo mogoče ustvariti imenika za izhodno datoteko. + + + File "%1" already exists. + Datoteka "%1" že obstaja. + + + Failed to write output file. + Ni mogoče zapisati izhodne datoteke. + + + Key "%1/%2" has been exported to "%3" successfully. + Ključ "%1/%2" je bil uspešno izvožen v "%3". + + + Failed read input file. + Ni mogoče prebrati vhodne datoteke. + + + File "%1" does not contain a valid private key! + Datoteka "%1" ne vsebuje veljavnega zasebnega ključa! + + + File "%1" does not contain a valid public key! + Datoteka "%1" ne vsebuje veljavnega javnega ključa! + + + Failed to create directory for key file. + Mape ključev ni bilo mogoče ustvariti. + + + Failed to write key file "%1". + Neuspeh pri pisanju datoteke ključev "%1". + + + Failed to set permissions for key file "%1"! + Ni bilo mogoče nastaviti dovoljenj za datoteko ključev "%1"! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + Ključ "%1/%2" je bil uspešno uvožen. Prosimo, preverite dovoljenja za datoteko "%3". da bi preprečili nepooblaščene dostope. + + + Failed to convert private key to public key + Ni bilo mogoče pretvoriti zasebnega ključa v javnega + + + Failed to create directory for private key file "%1". + Ni bilo mogoče ustvariti imenika za datoteko zasebnega ključa "%1". + + + Failed to save private key in file "%1"! + Ni bilo mogoče shraniti zasebnega ključa v datoteko "%1"! + + + Failed to set permissions for private key file "%1"! + Ni bilo mogoče nastaviti dovoljenj za datoteko zasebnega ključa "%1"! + + + Failed to create directory for public key file "%1". + Ni bilo mogoče ustvariti imenika za datoteko javnega ključa "%1". + + + Failed to save public key in file "%1"! + Ni bilo mogoče shraniti javnega ključa v datoteko "%1"! + + + Failed to set permissions for public key file "%1"! + Ni bilo mogoče nastaviti dovoljenj za datoteko javnega ključa "%1"! + + + Failed to set owner of key file "%1" to "%2". + Ni bilo mogoče določiti lastnika datoteke ključa "%1" na "%2". + + + Failed to set permissions for key file "%1". + Ni bilo mogoče nastaviti dovoljenj za datoteko ključa "%1". + + + Key "%1" is now accessible by user group "%2". + Ključ "%1" je sedaj dostopen skupini uporabnikov "%2". + + + <N/A> + <N/A> + + + Failed to read key file. + Ni mogoče prebrati datoteke ključa. + + + + AuthKeysPlugin + + Create new authentication key pair + Ustvarite nov par ključev za preverjanje pristnosti + + + Delete authentication key + Izbrišite ključ za preverjanje pristnosti + + + List authentication keys + Seznam ključev za preverjanje pristnosti + + + Import public or private key + Uvozite javni ali zasebni ključ + + + Export public or private key + Izvozite javni ali zasebni ključ + + + Extract public key from existing private key + Izvleči javni ključ iz obstoječega zasebnega ključa + + + Set user group allowed to access a key + Nastavite uporabniško skupino za dostop do ključa + + + KEY + KLJUČ + + + ACCESS GROUP + SKUPINA ZA DOSTOP + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + Ta ukaz prilagodi dovoljenja za dostop do datotek <KEY> tako, da ima dostop samo do skupine uporabnikov <ACCESS GROUP>bralni dostop do njega. + + + NAME + IME + + + FILE + DATOTEKA + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Ta ukaz izvozi ključ za preverjanje pristnosti <KEY> to <FILE>. Če <FILE> ni podano ime bo sestavljeno iz imena in vrste<KEY>. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Ta ukaz uvozi ključ za preverjanje pristnosti <KEY> iz<FILE>. Če <FILE> ni določena, bo ime zgrajeno iz imena in tipa <KEY>. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + Ta ukaz navaja vse razpoložljive ključe za overjanje v nastavljenem imeniku ključev. Če je možnost "%1" določena, bo prikazana tabela s podatki ključa namesto tega. Nekateri podatki morda manjkajo, če ključ ni dostopen, npr. zaradi pomanjkanja dovoljenj za branje. + + + Please specify the command to display help for! + Navedite ukaz za prikaz pomoči! + + + TYPE + VRSTA + + + PAIR ID + PAR ID + + + Command line support for managing authentication keys + Podpora za ukazno vrstico za upravljanje ključev za preverjanje pristnosti + + + Commands for managing authentication keys + Ukazi za upravljanje ključev za preverjanje pristnosti + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + Ta ukaz ustvari nov par ključev za preverjanje pristnosti z imenom <NAME> in shrani zasebni in javni ključ v konfigurirane ključne imenike. Parameter mora biti ime za ključ, ki lahko vsebuje le črke. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + Ta ukaz izbriše ključ za overjanje <KEY> iz nastavljenega imenika ključev. Upoštevajte, da ključa ni mogoče obnoviti, ko je bil izbrisan. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + Ta ukaz izdela del javnega ključa iz zasebnega ključa <KEY> in ga shrani kot ustrezen javni ključ. Pri nastavljanju drugega glavnega računalnika je torej dovolj, da prenesete samo zasebni ključ. Nato se lahko izvleče javni ključ. + + + + AuthKeysTableModel + + Name + Ime + + + Type + Vrsta + + + Access group + Dostopna skupina + + + Pair ID + Par ID + + + + BuiltinDirectoryConfigurationPage + + Computers + Računalniki + + + Name + Ime + + + Host address/IP + Ime gostitelja/IP + + + MAC address + MAC naslov + + + Add new computer + Dodaj nov računalnik + + + Remove selected computer + Odstrani izbrani računalnik + + + New computer + Nov računalnik + + + Builtin directory + Vgrajen imenik + + + Locations & computers + Lokacije in računalniki + + + Locations + Lokacije + + + Add new location + Dodaj novo lokacijo + + + Remove selected location + Odstrani izbrano lokacijo + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + Uvoz CSV datotek je možen prek vmesnika ukazne vrstice. Za več informacij, glejte <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">spletna dokumentacija</a>. + + + New location + Nova lokacija + + + + BuiltinDirectoryPlugin + + Show help for specific command + Prikaži pomoč za določen ukaz + + + Import objects from given file + Uvozi predmete iz dane datoteke + + + Export objects to given file + Izvozi predmete v dano datoteko + + + Invalid type specified. Valid values are "%1" or "%2". + Naveden je neveljaven tip. Veljavne vrednosti so "%1" ali "%2". + + + Type + Vrsta + + + Name + Ime + + + Host address + Naslov gostitelja + + + MAC address + MAC naslov + + + Specified object not found. + Določen predmet ni bil najden. + + + File "%1" does not exist! + Datoteka "%1" ne obstaja! + + + Can't open file "%1" for reading! + Ne morem odpreti datoteke "%1" za branje! + + + Unknown argument "%1". + Neznan argument "%1" + + + Computer "%1" (host address: "%2" MAC address: "%3") + Računalnik "%1" (naslov gostitelja: "%2" MAC naslov: "%3") + + + Unclassified object "%1" with ID "%2" + Nerazporejen predmet "%1" z ID "%2" + + + None + Brez + + + Computer + Računalnik + + + Root + Koren + + + Invalid + Neveljavno + + + Error while parsing line %1. + Napaka pri razčlenjevanju vrstice %1. + + + Network object directory which stores objects in local configuration + Imenik omrežnih objektov, ki shranjuje predmete v lokalni konfiguraciji + + + Commands for managing the builtin network object directory + Ukazi za upravljanje vgrajenega omrežnega imenika + + + No format string or regular expression specified! + Ni navedena vrsta niza ali regularnega izraza! + + + Can't open file "%1" for writing! + Ne morem odpreti datoteke "%1" za pisanje! + + + No format string specified! + Nobena vrsta niza ni določena! + + + Object UUID + Objekt UUID + + + Parent UUID + Starševski UUID + + + Add a location or computer + Dodaj lokacijo ali računalnik + + + Clear all locations and computers + Počistite vse lokacije in računalnike + + + Dump all or individual locations and computers + Odstranite vse ali posamezne lokacije in računalnike + + + List all locations and computers + Seznam vseh lokacij in računalnikov + + + Remove a location or computer + Odstrani lokacijo ali računalnik + + + Location "%1" + Lokacija "%1" + + + Builtin (computers and locations in local configuration) + Vgrajeni (računalniki in lokacije v lokalni konfiguraciji) + + + Location + Lokacija + + + FILE + DATOTEKA + + + LOCATION + LOKACIJA + + + FORMAT-STRING-WITH-PLACEHOLDERS + FORMAT-STRING-WITH-PLACEHOLDERS + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Uvozi predmete iz podane besedilne datoteke z uporabo podanega formata niza ali regularnega izraza, ki vsebuje enega ali več ograd. Veljavne ograde so: %1 + + + Import simple CSV file to a single room + Uvozite preprosto CSV datoteko v eno sobo + + + Import CSV file with location name in first column + Uvozite datoteko CSV z imenom lokacije v prvi stolpec + + + Import text file with with key/value pairs using regular expressions + Uvozite besedilno datoteko s parom ključ/vrednost z uporabo regularnih izrazov + + + Import arbitrarily formatted data + Uvozi poljubno formatirane podatke + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Izvozi predmete v podano besedilno datoteko z uporabo podanega niza formatov, ki vsebuje eno ali več ograd. Veljavne ograde so: %1 + + + Export all objects to a CSV file + Izvozite vse objekte v datoteko CSV + + + Export all computers in a specific location to a CSV file + Izvozite vse računalnike na določeni lokaciji v datoteko CSV + + + TYPE + VRSTA + + + NAME + IME + + + PARENT + STARŠ + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + Dodajte objekt, kjer je lahko %1 eden od "%2" ali "%3". %4 lahko določite z imenom ali UUID. + + + Add a room + Dodajte sobo + + + Add a computer to room %1 + Dodajte računalnik v sobo %1 + + + OBJECT + PREDMET + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Odstrani podani predmet iz imenika. %1 lahko določite z imenom ali UUID. Če odstranite lokacijo, boste odstranili tudi vse povezane računalnike. + + + Remove a computer by name + Odstranite računalnik po imenu + + + Remove an object by UUID + Odstranite predmet z UUID + + + "Room 01" + "Soba 01" + + + "Computer 01" + "Računalnik 01" + + + HOST ADDRESS + NASLOV GOSTITELJA + + + MAC ADDRESS + MAC naslov + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Vgrajen VNC strežnik (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Vgrajen VNC strežnik (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Gostitelj/IP naslov: %1 + + + Active features: %1 + Aktivne funkcije: %1 + + + Online and connected + Spletno in povezano + + + Establishing connection + Vzpostavljam povezavo + + + Computer offline or switched off + Računalnik je brez povezave ali izklopljen + + + Authentication failed or access denied + Preverjanje pristnosti ni uspelo ali je dostop zavrnjen + + + Disconnected + Odklopljen + + + No user logged on + Uporabnik ni prijavljen na + + + Logged on user: %1 + Prijavljen uporabnik: %1 + + + Location: %1 + Lokacija: %1 + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 Servis %2 pri %3:%4 + + + Authentication error + Napaka preverjanja pristnosti + + + Remote access + Oddaljen dostop + + + User "%1" at host "%2" is now accessing this computer. + Uporabnik "%1" pri gostitelju"%2" zdaj dostopa do tega računalnika + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + Uporabnik + + + Missing network object directory plugin + Manjka vtičnik imenika omrežnega objekta + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Nobenega privzetega vtičnika imenika omrežnega objekta ni bilo mogoče najti. Preverite svojo namestitev ali nastavite drugačen imenik ozadja objekta omrežja preko %1 konfiguratorja. + + + Location detection failed + Iskanje lokacije ni uspelo + + + Computer name;Hostname;User + Ime računalnika;Ime gostitelja;Uporabnik + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + Ne morem določiti lokacije tega računalnika. To kaže na težavo s konfiguracijo sistema. Namesto tega bodo v izbranem oknu računalnika prikazane vse lokacije. + + + + ComputerSelectPanel + + Computer search + Preišči računalnik + + + Add location + Dodaj lokacijo + + + Save computer/user list + Shrani seznam računalnikov/uporabnikov + + + Select output filename + Izberi izhodno ime datoteke + + + CSV files (*.csv) + CSV datoteka (*.csv) + + + File error + Napaka v datoteki + + + Could not write the computer and users list to %1! Please check the file access permissions. + Računalnika in uporabnikov ni mogoče vpisati v seznam %1! Preverite dovoljenja za dostop do datotek. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Navedite obstoječo konfiguracijsko datoteko za uvoz. + + + Please specify a valid filename for the configuration export. + Navedite veljavno ime datoteke za izvoz konfiguracije. + + + Please specify a valid key. + Prosimo, navedite veljaven ključ. + + + Specified key does not exist in current configuration! + Določen ključ v trenutni konfiguraciji ne obstaja! + + + Please specify a valid value. + Navedite veljavno vrednost. + + + Configure Veyon at command line + Konfigurirajte Veyon v ukazni vrstici + + + Output file is not writable! + Izhodna datoteka ni zapisljiva! + + + Output directory is not writable! + Izhodna mapa ni zapisljiva! + + + Configuration file is not readable! + Konfiguracijska datoteka ni berljiva! + + + Clear system-wide Veyon configuration + Počisti celotno sistemsko Veyon konfiguracijo + + + List all configuration keys and values + Seznam vseh konfiguracijskih ključev in vrednosti + + + Import configuration from given file + Uvozite konfiguracijo iz dane datoteke + + + Export configuration to given file + Izvozite konfiguracijo v določeno datoteko + + + Read and output configuration value for given key + Preberite in izpišite konfiguracijsko vrednost za določen ključ + + + Write given value to given configuration key + Napiši dano vrednost določenemu konfiguracijskemu ključu + + + Unset (remove) given configuration key + Odstranite dodeljeni konfiguracijski ključ + + + Commands for managing the configuration of Veyon + Ukazi za upravljanje konfiguracije Veyon + + + Upgrade and save configuration of program and plugins + Nadgradite in shranite konfiguracijo programa in vtičnikov + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + Lastnosti samodejnega zagona za storitev %1 ni bilo mogoče spremeniti. + + + Could not configure the firewall configuration for the %1 Server. + Konfiguracije požarnega zidu za strežnik %1 ni bilo mogoče konfigurirati. + + + Could not configure the firewall configuration for the %1 Worker. + Konfiguracije požarnega zidu za delavca %1 ni bilo mogoče konfigurirati. + + + Configuration is not writable. Please check your permissions! + Konfiguracija ni zapisljiva. Prosimo, preverite svoja dovoljenja! + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + %1 Demo + + + + DemoConfigurationPage + + Demo server + Demonstracijski strežnik + + + Tunables + Nastavki + + + ms + ms + + + Key frame interval + Interval ključnega okvira + + + Memory limit + Omejitev pomnilnika + + + MB + MB + + + Update interval + Interval posodobitve + + + s + s + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + Ustavi predavanje + + + Window demo + Vodi v oknu + + + Give a demonstration by screen broadcasting + Podaj predstavitev z zaslonom predvajanja + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + V tem načinu se zaslon prikaže v oknu na vseh računalnikih. Uporabniki lahko po potrebi preklopijo na druga okna. + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + Dialog za dostop do namizja + + + Confirm desktop access + Potrdi dostop do namizja + + + Never for this session + Nikoli za to sejo + + + Always for this session + Zmeraj za to sejo + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + Uporabnik %1 na računalniku %2 želi dostopati do vašega namizja. Želite odobriti dostop? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programi in spletne strani + + + Predefined programs + Vnaprej določeni programi + + + Name + Ime + + + Path + Pot + + + Add new program + Dodaj nov program + + + Remove selected program + Odstrani izbrani program + + + Predefined websites + Vnaprej določene spletne strani + + + Remove selected website + Odstrani izbrano spletno mesto + + + URL + URL + + + New program + Nov program + + + New website + Nova spletna stran + + + + DesktopServicesFeaturePlugin + + Run program + Zaženi program + + + Open website + Odpri spletno stran + + + Click this button to open a website on all computers. + Kliknite ta gumb, da odprete spletno mesto na vseh računalnikih. + + + Start programs and services in user desktop + Zaženite programe in storitve na uporabniškem namizju + + + Click this button to run a program on all computers. + Kliknite ta gumb za zagon programa na vseh računalnikih. + + + Run program "%1" + Zaženi program "%1" + + + Custom program + Program po meri + + + Open website "%1" + Odpri spletno stran "%1" + + + Custom website + Spletna stran po meri + + + + DocumentationFigureCreator + + Teacher + Učitelj + + + Room %1 + Soba %1 + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + Spletna stran po meri + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + Program po meri + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + Zunanji VNC strežnik + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Konfiguracija zunanjega strežnika VNC + + + Port: + Vrata: + + + Password: + Geslo: + + + + FeatureControl + + Feature control + Funkcija nadzora + + + + FileTransferConfigurationPage + + File transfer + Prenos datoteke + + + Directories + Imeniki + + + Destination directory + + + + Default source directory + + + + Options + Možnosti + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + Žal ne morem odpreti datoteke "%1" za branje! Prosimo, preverite svoja dovoljenja! + + + + FileTransferDialog + + File transfer + Prenos datoteke + + + Options + Možnosti + + + Transfer only + Samo prenos + + + Transfer and open file(s) with associated program + Prenesi in odpri datoteko(e) s pripadajočim programom + + + Transfer and open destination folder + Prenesi in odpri ciljno mapo + + + Files + Datoteke + + + Start + Začetek + + + Overwrite existing files + Prepiši obstoječe datoteke + + + + FileTransferPlugin + + File transfer + Prenos datoteke + + + Click this button to transfer files from your computer to all computers. + Klikni na ta gumb za prenos datotek iz vašega računalnika na vse računalnike. + + + Select one or more files to transfer + Izberi eno ali več datotek za prenos + + + Transfer files to remote computer + Prenesi datoteke na oddaljeni računalnik + + + Received file "%1". + Prejeta datoteka "%1". + + + Could not receive file "%1" as it already exists. + Ne morem prejeti datoteke "%1", ker že obstaja. + + + Could not receive file "%1" as it could not be opened for writing! + Datoteke "%1" ni bilo mogoče prejeti, ker je ni bilo mogoče odpreti za pisanje! + + + + GeneralConfigurationPage + + User interface + Uporabniški vmesnik + + + Language: + Jezik: + + + Use system language setting + Uporabi sistemske nastavitve jezika + + + Veyon + Veyon + + + Logging + Beleženje + + + Log file directory + Imenik datoteke dnevnikov + + + Log level + Nivo beleženja + + + Nothing + Nič + + + Only critical messages + Samo kritična sporočila + + + Errors and critical messages + Napake in kritična sporočila + + + Warnings and errors + Opozorila in napake + + + Information, warnings and errors + Informacije, opozorila in napake + + + Debug messages and everything else + Razhroščevanje sporočil in vsega ostalega + + + Limit log file size + Omeji velikost dnevniške datoteke + + + Clear all log files + Počisti vse dnevniške datoteke + + + Log to standard error output + Dnevnik na standardni izhod za napako + + + Network object directory + Imenik omrežnih objektov + + + Backend: + Ozadje: + + + Update interval: + Posodobi interval: + + + %1 service + %1 storitev + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + Storitev %1 je treba začasno ustaviti, da odstranite datoteke dnevnika. Nadaljujem? + + + Log files cleared + Počiščene dnevniške datoteke + + + All log files were cleared successfully. + Vse dnevniške datoteke so bile uspešno počiščene. + + + Error + Napaka + + + Could not remove all log files. + Ni bilo možno odstranit vseh dnevniških datotek. + + + MB + MB + + + Rotate log files + Zavrti datoteke dnevnika + + + x + x + + + seconds + sekund + + + Write to logging system of operating system + Zapiši v sistem beleženja operacijskega sistema + + + Authentication + Preverjanje pristnosti + + + Method: + Metoda: + + + Logon authentication + Preverjanje pristnosti prijave + + + Key file authentication + Preverjanje pristnosti z datotečnim ključem + + + Test + Preizkus + + + Authentication is set up properly on this computer. + Preverjanje pristnosti je pravilno nastavljeno v tem računalniku. + + + Authentication keys are not set up properly on this computer. + Ključi za preverjanje pristnosti v tem računalniku niso pravilno nastavljeni. + + + Authentication test + Preverjanje pristnosti + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + Brskaj po LDAP + + + + LdapClient + + LDAP error description: %1 + Opis napake LDAP: %1 + + + + LdapConfigurationPage + + Basic settings + Osnovne nastavitve + + + General + Splošno + + + LDAP server and port + LDAP strežnik in vrata + + + Bind DN + DN povezave + + + Bind password + Geslo za povezovanje + + + Anonymous bind + Anonimna povezava + + + Use bind credentials + Uporabi povezovalne poverilnice + + + Base DN + Osnovni DN + + + Fixed base DN + Stalni base DN + + + e.g. dc=example,dc=org + npr. dc=primer,dc=org + + + Discover base DN by naming context + Odkrij base DN s kontekstnim imenovanjem + + + e.g. namingContexts or defaultNamingContext + npr. namingContexts ali defaultNamingContext + + + Environment settings + Nastavitve okolja + + + Object trees + Objektna drevesa + + + Computer tree + Računalniško drevo + + + e.g. OU=Groups + npr. OU=Skupine + + + User tree + Uporabniško drevo + + + e.g. OU=Users + npr. OU=Uporabniki + + + e.g. OU=Computers + npr. OU=Računalniki + + + Group tree + Skupinsko drevo + + + Perform recursive search operations in object trees + Opravite rekurzivne operacije iskanja predmetnih dreves + + + Object attributes + Atributi objekta + + + e.g. hwAddress + npr. hwNaslov + + + e.g. member or memberUid + npr. član ali članUid + + + e.g. dNSHostName + npr. dNSHostName + + + Computer MAC address attribute + Atribut MAC naslova računalnika + + + Group member attribute + Atribut člana skupine + + + e.g. uid or sAMAccountName + npr. uid ali sAMAccountName + + + Advanced settings + Napredne nastavitve + + + Optional object filters + Izbirni filtri objektov + + + Filter for user groups + Filter za skupine uporabnikov + + + Filter for users + Filter za uporabnike + + + Filter for computer groups + Filter za računalniške skupine + + + Group member identification + Identifikacija članov skupine + + + Distinguished name (Samba/AD) + Razločno ime (Samba/AD) + + + List all groups of a user + Seznam vseh skupin uporabnika + + + List all groups of a computer + Seznam vseh skupin računalnika + + + Get computer object by IP address + Pridobi računalniški predmet po naslovu IP + + + LDAP connection failed + Neuspela povezava na LDAP + + + LDAP bind failed + Povezava LDAP ni uspela + + + LDAP bind successful + Povezava LDAP je uspela + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Uspelo se je povezati s strežnikom LDAP in opraviti povezavo LDAP. Osnovne nastavitve LDAP so pravilno konfigurirane. + + + LDAP base DN test failed + LDAP base DN test ni uspel + + + LDAP base DN test successful + LDAP base DN test je uspel + + + LDAP naming context test failed + Ozadje poimenovanja LDAP ni uspelo + + + LDAP naming context test successful + Ozadje poimenovanja LDAP je uspelo + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + Ozadje imena poimenovanja LDAP je bilo uspešno preverjeno. Najden je bil naslednji osnovni DN: +%1 + + + user tree + uporabniško drevo + + + group tree + skupinsko drevo + + + computer tree + računalniško drevo + + + Enter username + Vnesite uporabniško ime + + + Please enter a user login name (wildcards allowed) which to query: + Vnesite uporabniško ime za prijavo (omogočeni so nadomestni znaki) za poizvedbo: + + + user objects + uporabnikovi predmeti + + + Enter group name + Vnesi ime skupine + + + Please enter a group name whose members to query: + Vnesite ime skupine, katerega člani lahko poizvedujejo: + + + group members + člani skupine + + + Group not found + Skupine ni bilo mogoče najti + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + Ni bilo mogoče najti skupine z imenom "%1". Preverite ime skupine ali parameter drevesa skupine. + + + Enter computer name + Vnesite ime računalnika + + + computer objects + računalniški predmeti + + + Enter computer DN + Vnesite DN računalnika + + + Please enter the DN of a computer whose MAC address to query: + Vnesite DN računalnika, katerega naslov MAC je za poizvedbo: + + + computer MAC addresses + računalnik MAC naslovov + + + users + uporabniki + + + user groups + skupine uporabnikov + + + computer groups + skupine računalnikov + + + Please enter a user login name whose group memberships to query: + Prosimo, vnesite uporabniško ime za prijavo katerega člani skupine želite poizvedovati: + + + groups of user + skupine uporabnika + + + User not found + Uporabnik ni najden + + + groups of computer + skupine računalnika + + + Computer not found + Računalnika ni bilo mogoče najti + + + Enter computer IP address + Vnesite računalnikov IP naslov + + + Please enter a computer IP address which to resolve to an computer object: + Vnesite IP naslov računalnika, ki ga želite razrešiti na računalniški objekt: + + + computers + računalniki + + + LDAP %1 test failed + Preizkus LDAP %1 ni uspel + + + LDAP %1 test successful + Preizkus LDAP %1 je uspel + + + The %1 has been queried successfully and %2 entries were found. + %1 je bila uspešna poizvedba in ugotovljeno je, da je bilo %2 vnosov. + + + %1 %2 have been queried successfully: + +%3 + %1 %2 je bilo uspešno vprašanih: + +%3 + + + LDAP filter test failed + Test LDAP filtra ni uspel + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + Ni bilo mogoče izvesti poizvedbe %1 z uporabo nastavljenega filtra. Preverite filter LDAP za %1. + +%2 + + + LDAP filter test successful + Test LDAP filtra je uspel + + + %1 %2 have been queried successfully using the configured filter. + %1 %2 je bilo uspešno vprašanih z uporabo nastavljenega filtra. + + + (only if different from group tree) + (le, če je drugačen od drevesa skupine) + + + Computer group tree + Drevo računalniške skupine + + + computer group tree + drevo računalniške skupine + + + Filter for computers + Filter za računalnike + + + e.g. room or computerLab + npr. soba ali računalnikLab + + + Integration tests + Integracijski testi + + + Computer groups + Računalniške skupine + + + e.g. name or description + npr. ime ali opis + + + Filter for computer containers + Filter za računalniške vsebnike + + + Computer containers or OUs + Računalniški vsebniki OU + + + Connection security + Varnost povezave + + + TLS certificate verification + Preverjanje TLS certifikata + + + System defaults + Sistemsko privzeto + + + Never (insecure!) + Nikoli (nezanesljivo!) + + + Custom CA certificate file + CA certifikatna datoteka po meri + + + None + Brez + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + npr. (objectClass = računalnik) + + + e.g. (objectClass=group) + npr. (objectClass = skupina) + + + e.g. (objectClass=person) + npr. (objectClass = oseba) + + + e.g. (objectClass=room) or (objectClass=computerLab) + npr. (objectClass = soba) ali npr. (objectClass = računalnikLab) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + npr. (objectClass = vsebnik) ali npr. (objectClass = organizacijska enota) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + Ni mogoča poizvedba nastavljene base DN. Preverite base DN parameter. + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + Poizvedba LDAP base DN je bila uspešna. Ugotovljeni so bili naslednji vnosi: + +%1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + Neuspešna poizvedba base DN preko kontekstnih poimenovanj . Preverite parameter atributa konteksta poimenovanja. + +%1 + + + Certificate files (*.pem) + Datoteke potrdil (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + Povezave s strežnikom LDAP ni bilo mogoče vzpostaviti. Preverite parametre strežnika. + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + Ne morem se povezati na strežnik LDAP. Preverite parametre strežnika in povezovalne poverilnice. + +%1 + + + Encryption protocol + Protokol šifriranja + + + Computer location attribute + Atribut lokacije računalnika + + + Computer display name attribute + Atribut prikaznega imena računalnika + + + Location name attribute + Atribut imena mesta + + + e.g. cn or displayName + npr. cn ali prikazno ime + + + Computer locations identification + Identifikacija lokacij računalnika + + + Identify computer locations (e.g. rooms) via: + Prepoznaj lokacije računalnika (npr. sobe) preko: + + + Location attribute in computer objects + Atribut lokacije v računalniških objektih + + + List all entries of a location + Seznam vseh vnosov lokacije + + + List all locations + Seznam vseh lokacij + + + Enter computer display name + Vnesi prikazno ime računalnika + + + Please enter a computer display name to query: + Vnesi prikazno ime računalnika za poizvedbo: + + + Enter computer location name + Vnesi ime lokacije računalnika + + + Please enter the name of a computer location (wildcards allowed): + Vnesi ime lokacije računalnika (dovoljeni nadomestni znaki): + + + computer locations + lokacije računalnika + + + Enter location name + Vnesi ime lokacije + + + Please enter the name of a location whose entries to query: + Vnesi ime lokacije, katere vnosi so za poizvedbo: + + + location entries + vnosi lokacije + + + LDAP test failed + Preizkus LDAP ni uspel + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + Za noben %1 ni bila mogoča poizvedba. Preverite parametre %2 in vnesite ime obstoječega objekta. + +%3 + + + and + in + + + LDAP test successful + Test LDAP uspešen + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + Ni bila mogoča poizvedba o vnosih v konfiguriranem %1. Preverite parameter "%2". + +%3 + + + Browse + Brskanje + + + Test + Preizkus + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + Imena gostiteljev so shranjena kot popolna imena domen (FQDN, npr. myhost.example.org) + + + Computer hostname attribute + Atribut gostiteljskega računalnika + + + Please enter a computer hostname to query: + Vnesite ime gostiteljskega računalnika za poizvedbo: + + + Invalid hostname + Neveljavno ime gostitelja + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + Konfigurirali ste računalniška imena gostiteljev, ki jih želite shraniti kot popolne domene (FQDN), vendar ste vnesli ime gostitelja brez domene. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + Računalniška imena gostiteljev ste konfigurirali za shranjevanje kot preprosta imena gostiteljev brez imena domene, vendar ste vnesli ime gostitelja z delom imena domene. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + Uporabnika z imenom "%1" ni bilo mogoče najti. Preverite uporabniško ime ali parameter drevesa uporabnika. + + + Enter hostname + Vnesite ime gostitelja + + + Please enter a computer hostname whose group memberships to query: + Vnesite ime gostitelja računalnika, ki je član v skupini za poizvedbo: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + Ni bilo mogoče najti računalnika z imenom gostitelja "%1". Preverite ime gostitelja ali parameter drevesa računalnika. + + + Hostname lookup failed + Iskanje imena gostitelja ni uspelo + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + Ni bilo mogoče iskati imena gostitelja za IP naslov %1. Preverite nastavitve strežnika DNS. + + + User login name attribute + Atribut uporabniškega imena za prijavo + + + Configured attribute for user login name or computer hostname (OpenLDAP) + Konfiguriran atribut za uporabniško ime za prijavo ali ime gostitelja računalnika (OpenLDAP) + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + Samodejno konfiguriraj base DN preko konteksta imenovanja + + + Query objects from LDAP directory + Poizvedba predmetov iz imenika LDAP + + + Show help about command + Prikaži pomoč o ukazu + + + Commands for configuring and testing LDAP/AD integration + Ukazi za konfiguriranje in testiranje integracije LDAP/AD + + + Basic LDAP/AD support for Veyon + Osnovna LDAP/AD podpora za Veyon + + + %1 (load computers and locations from LDAP/AD) + %1 (naložite računalnike in lokacije iz LDAP/AD) + + + %1 (load users and groups from LDAP/AD) + %1 (naložite uporabnike in skupine iz LDAP/AD) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Vtičnik izvajanja abstraktnih funkcij za platformo Linux + + + + LocationDialog + + Select location + Izberite lokacijo + + + enter search filter... + vnesi iskalni filter... + + + + MainToolBar + + Configuration + Nastavitve + + + Disable balloon tooltips + Onemogoči namige v balončkih + + + Show icons only + Prikaži samo ikone + + + + MainWindow + + MainWindow + Glavno okno + + + toolBar + Orodna vrstica + + + General + Splošno + + + &File + Datoteka (&F) + + + &Help + Pomoč (&H) + + + &Quit + Končaj (&Q) + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + Nal&oži nastavitve iz datoteke + + + Ctrl+O + Ctrl+O + + + About Qt + Vizitka Qt + + + Authentication impossible + Preverjanje pristnosti ni mogoče + + + Configuration not writable + Nastavitve niso zapisljive + + + Load settings from file + Naloži nastavitve iz datoteke + + + Save settings to file + Shrani nastavitve v datoteko + + + Unsaved settings + Neshranjene nastavitve + + + There are unsaved settings. Quit anyway? + Nekatere nastavitve niso shranjene. Vseeno končam? + + + Veyon Configurator + Veyon konfigurator + + + Service + Storitev + + + Master + Glavni + + + Access control + Nadzor dostopa + + + About Veyon + O programu + + + Auto + Samodejno + + + About + Vizitka + + + %1 Configurator %2 + %1 konfigurator %2 + + + JSON files (*.json) + JSON datoteke (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + Lokalno konfiguracijsko ozadje je sporočilo, da konfiguracije ni mogoče zapisati! Konfiguratorja %1 zaženite z višjimi pravicami. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + Datoteke ključa za preverjanje pristnosti niso bile najdene, ali so vaše trenutne zastarele. Ustvarite nove datoteke ključa z uporabo konfiguratorja %1. Druga možnost je nastaviti preverjanje pristnosti pri prijavi z uporabo %1 konfiguratorja. V nasprotnem primeru ne boste mogli dostopati do računalnikov z uporabo %1. + + + Access denied + Dostop zavrnjen + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + Glede na lokalno konfiguracijo nimate dovoljenega dostopa do računalnikov v omrežju. Prijavite se z drugim računom ali pustite skrbniku sistema, da preveri lokalno konfiguracijo. + + + Screenshots + Zaslonske slike + + + Feature active + Aktivna funkcija + + + The feature "%1" is still active. Please stop it before closing %2. + Funkcija "%1" je še vedno aktivna. Pred zapiranjem %2 jo ustavite. + + + Reset configuration + Ponastavitev konfiguracije + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Ali res želite ponastaviti lokalno konfiguracijo in spremeniti vse nastavitve na njihove privzete nastavitve? + + + Search users and computers + Iskanje uporabnikov in računalnikov + + + Align computers to grid + Poravnaj računalnike z mrežo + + + %1 Configurator + %1 konfigurator + + + Insufficient privileges + Nezadostni privilegiji + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + Ni bilo mogoče začeti z administrativnimi pravicami. Prosimo, prepričajte se, da je podoben program nameščen za vaše namizno okolje! Program bo deloval z običajnimi uporabniškimi pravicami. + + + Only show powered on computers + Pokaži samo priklope na računalnikih + + + &Save settings to file + &Shrani nastavitve v datoteko + + + &View + &Pogled + + + &Standard + &Standardno + + + &Advanced + &Napredno + + + Use custom computer arrangement + Uporabite urejanje računalnika po meri + + + Locations && computers + Lokacije && računalniki + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Imeniki + + + User configuration + Uporabniška konfiguracija + + + Feature on computer double click: + Funkcija dvojnega klika na računalniku: + + + Features + Funkcije + + + All features + Vse funkcije + + + Disabled features + Onemogoči funkcije + + + Screenshots + Zaslonske slike + + + <no feature> + <brez funkcije> + + + Basic settings + Osnovne nastavitve + + + Behaviour + Obnašanje + + + Enforce selected mode for client computers + Sproži izbrani način za odjemalske računalnike + + + Hide local computer + Skrij lokalni računalnik + + + Hide computer filter field + Skrij polje računalniškega filtra + + + Actions such as rebooting or powering down computers + Ukrepi, kot so ponovni zagon ali izklop računalnikov + + + User interface + Uporabniški vmesnik + + + Background color + Barva ozadja + + + Thumbnail update interval + Interval posodabljanja sličic + + + ms + ms + + + Program start + Zagon programa + + + Modes and features + Načini in funkcije + + + User and computer name + Ime uporabnika in računalnika + + + Only user name + Samo uporabniško ime + + + Only computer name + Samo ime računalnika + + + Computer thumbnail caption + Napis sličice računalnika + + + Text color + Barva besedila + + + Sort order + Vrstni red + + + Computer and user name + Računalnik in uporabniško ime + + + Computer locations + Lokacije računalnika + + + Show current location only + Prikaži samo trenutno lokacijo + + + Allow adding hidden locations manually + Dovolite ročno dodajanje skritih lokacij + + + Hide empty locations + Skrij prazne lokacije + + + Show confirmation dialog for potentially unsafe actions + Pokaži potrditveno okno za morebitna nevarna dejanja + + + Perform access control + Opravite nadzor dostopa + + + Automatically select current location + Samodejno izberi trenutno lokacijo + + + Automatically open computer select panel + Samodejno odpri izbrano okno računalnika + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + Samodejno + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + Nadzor + + + Builtin monitoring mode + Vgrajen način nadzora + + + This mode allows you to monitor all computers at one or more locations. + Ta način omogoča nadzor vseh računalnikov na eni ali več lokacijah. + + + + NetworkObjectTreeModel + + Locations/Computers + Lokacije/Računalniki + + + + OpenWebsiteDialog + + Open website + Odpri spletno stran + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + Vnesite URL spletnega mesta, ki ga želite odpreti: + + + Name: + + + + + PasswordDialog + + Username + Uporabniško ime + + + Password + Geslo + + + Veyon Logon + Veyon prijava + + + Authentication error + Napaka preverjanja pristnosti + + + Logon failed with given username and password. Please try again! + Prijava z neodobrenim uporabniškim imenom in geslom ni uspela. Prosim, poskusite ponovno! + + + Please enter your username and password in order to access computers. + Vnesite svoje uporabniško ime in geslo za dostop do računalnikov. + + + + PowerControlFeaturePlugin + + Power on + Vključi + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Kliknite ta gumb za vklop vseh računalnikih. Na ta način vam ni treba ročno vklapljati vsakega računalnika. + + + Reboot + Ponovni zagon + + + Click this button to reboot all computers. + Kliknite ta gumb, če želite znova zagnati vse računalnike. + + + Power down + Izklopi + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Kliknite ta gumb, da izklopite vse računalnike. Na ta način vam ni treba ročno izklopiti vsakega računalnika. + + + Power on/down or reboot a computer + Vklop / Izklop / Ponovni zagon računalnika + + + Confirm reboot + Potrdi ponovni zagon + + + Confirm power down + Potrdi izklop + + + Do you really want to reboot the selected computers? + Ali res želite ponovno zagnati izbrane računalnike? + + + Do you really want to power down the selected computer? + Ste prepričani, da želite izklopiti izbrani računalnik? + + + Power on a computer via Wake-on-LAN (WOL) + Zagon računalnika preko Wake-on-LAN (WOL) + + + MAC ADDRESS + MAC naslov + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + Ta ukaz oddaja paket Wake-on-LAN (WOL) v omrežje, da bi lahko vklopil računalnik z danim naslovom MAC. + + + Please specify the command to display help for! + Navedite ukaz za prikaz pomoči! + + + Invalid MAC address specified! + Neveljavni MAC naslov + + + Commands for controlling power status of computers + Ukazi za nadzor stanja napajanja računalnikov + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + Izklopi + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + Oddaljeni pogled + + + Open a remote view for a computer without interaction. + Odpri oddaljeni pogled za računalnik brez interakcije. + + + Remote control + Nadzor na daljavo + + + Open a remote control window for a computer. + Odpri okno daljinskega upravljalnika za računalnik. + + + Remote access + Oddaljen dostop + + + Remote view or control a computer + Oddaljeni pogled ali nadzor računalnika + + + Please enter the hostname or IP address of the computer to access: + Vnesite ime gostitelja ali naslov IP računalnika za dostop: + + + Show help about command + Prikaži pomoč o ukazu + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 - %2 oddaljen dostop + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + Samo pogled + + + Remote control + Nadzor na daljavo + + + Send shortcut + Pošlji bližnjico + + + Fullscreen + Celozaslonsko + + + Window + Okno + + + Ctrl+Alt+Del + Ctrl+Alt+Delete + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Meni + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Povezujem %1 + + + Connected. + Povezan. + + + Screenshot + Zaslonska slika + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Prosimo, vnesite programe ali ukaze, ki se izvajajo v izbranih računalnikih. Več programov / ukazov lahko ločite po vrstici. + + + Run programs + Zaženi programe + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + npr. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + Zakleni + + + Unlock + Odkleni + + + Lock screen and input devices of a computer + Zakleni zaslon in vhodne naprave računalnika + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Če želite povrniti celotno pozornost vseh uporabnikov, lahko z uporabo tega gumba zaklenete njihove računalnike. V tem načinu so vse vhodne naprave zaklenjene in zasloni so črni. + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + neznano + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + Ni mogoče vzeti posnetka zaslona, ker imenik %1 ne obstaja in ga ni mogoče ustvariti. + + + Screenshot + Zaslonska slika + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + Zaslonska slika + + + Use this function to take a screenshot of selected computers. + To funkcijo uporabite za posnetek zaslona izbranih računalnikov. + + + Screenshots taken + Vzeti posnetki zaslona + + + Screenshot of %1 computer have been taken successfully. + Posnetek zaslona računalnika %1 uspešno sprejet. + + + Take screenshots of computers and save them locally. + Posnemite zaslone računalnikov in jih shranite lokalno. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Vsi posnetke zaslona, ki ste jih naredili, so navedeni tukaj. Lahko snamate posnetke zaslona s klikom na postavko "Posnetek zaslona" v kontekstnem meniju računalnika. S posnetki zaslonov lahko upravljate z uporabo spodnjih gumbov. + + + User: + Uporabnik: + + + Computer: + Računalnik: + + + Date: + Datum: + + + Time: + Čas: + + + Show + Prikaži + + + Delete + Briši + + + Screenshot + Zaslonska slika + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Splošno + + + Autostart + Samodejni zagon + + + Hide tray icon + Skrij ikono v orodni vrstici + + + Start service + Zaženi storitev + + + Stopped + Ustavljeno + + + Stop service + Ustavi storitev + + + State: + Stanje: + + + Enable firewall exception + Omogoči izjemo požarnega zidu + + + Allow connections from localhost only + Omogočite povezave samo z lokalnega gostitelja + + + VNC server + VNC strežnik + + + Plugin: + Vtičnik: + + + Restart %1 Service + Ponovno zaženi %1 storitev + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Vse nastavitve so bile uspešno shranjene. Za uveljavitev je treba storitev %1 ponovno zagnati. Ali znova zaženem zdaj? + + + Running + V teku + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + Če omogočite to možnost, bo storitev zagnala proces strežnika za vsako interaktivno sejo v računalniku. +Običajno je to potrebno za podporo terminalskih strežnikov. + + + Show notification on remote connection + Prikaži obvestilo o oddaljeni povezavi + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + Notranji VNC strežnik + + + Feature manager + Upravitelj funkcij + + + Demo server + Demonstracijski strežnik + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + Zagon storitve %1 + + + Stopping service %1 + Zaustavitev storitve %1 + + + Registering service %1 + Registracija storitve %1 + + + Unregistering service %1 + Odregistracija storitve %1 + + + Service control + Nadzor storitve + + + + ServiceControlPlugin + + Service is running + Storitev teče + + + Service is not running + Storitev ne teče + + + Configure and control Veyon service + Konfigurirajte in nadzirajte Veyon storitev + + + Register Veyon Service + Registriraj Veyon storitev + + + Unregister Veyon Service + Odregistriraj Veyon storitev + + + Start Veyon Service + Zaženi Veyon storitev + + + Stop Veyon Service + Zaustavi Veyon storitev + + + Restart Veyon Service + Ponovno zaženi Veyon storitev + + + Query status of Veyon Service + Stanje poizvedbe storitve Veyon + + + Commands for configuring and controlling Veyon Service + Ukazi za konfiguracijo in nadzor Veyon storitve + + + + ShellCommandLinePlugin + + Run command file + Zaženi ukazno datoteko + + + File "%1" does not exist! + Datoteka "%1" ne obstaja! + + + Interactive shell and script execution for Veyon Control + Interaktivna lupina in izvedba skripte za nadzor Veyon + + + Commands for shell functionalities + Ukazi za funkcionalnosti lupine + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + Ikona sistemske vrstice + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + Uporabniške skupine ozadja za skupine uporabnikov sistema + + + Default (system user groups) + Privzeto (skupine uporabnikov sistema) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + Pošlji sporočilo + + + Use the field below to type your message which will be sent to all selected users. + Uporabite spodnje polje za vpis sporočila, ki ga želite poslati izbranim uporabnikom. + + + + TextMessageFeaturePlugin + + Text message + Sporočilo + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Uporabite to funkcijo za pošiljanje sporočil vsem uporabnikom npr. za dodelitev nalog + + + Message from teacher + Sporočilo predavatelja + + + Send a message to a user + Pošlji sporočilo uporabniku + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Omogoči zajemanje plastnih (delno prosojnih) oken + + + Poll full screen (leave this enabled per default) + Prenašaj polni zaslon (pustite to omogočeno na privzeto) + + + Low accuracy (turbo mode) + Majhna natančnost (turbo način) + + + Builtin UltraVNC server configuration + Vgrajena konfiguracija UltraVNC strežnika + + + Enable multi monitor support + Omogoči podporo za več monitorjev + + + Enable Desktop Duplication Engine on Windows 8 and newer + Omogoči pogon za podvajanje namizja računalnika na Windows 8 in novejših + + + Maximum CPU usage + + + + + UserConfig + + No write access + Ni pravice pisanja + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Vaših osebnih nastavitev ni bilo mogoče shraniti! Preverite pot do konfiguracijske datoteke uporabnika s konfiguratorjem %1. + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + Uporabniško ime + + + Password + Geslo + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + Nadzor seje uporabnika + + + + VeyonCore + + [OK] + [V REDU] + + + [FAIL] + [SPODLETELO] + + + Invalid command! + Neveljaven ukaz! + + + Available commands: + Razpoložljivi ukazi: + + + Invalid arguments given + Neveljavni argumenti so podani + + + Not enough arguments given - use "%1 help" for more information + Ni dovolj argumentov - uporabite "%1 pomoč" za več informacij + + + Unknown result! + Neznan rezultat! + + + Available modules: + Razpoložljivi moduli: + + + No module specified or module not found - available modules are: + Ni določenega modula ali modula ni mogoče najti - razpoložljivi moduli so: + + + Plugin not licensed + Vtičnik ni licenciran + + + INFO + INFO + + + ERROR + NAPAKA + + + USAGE + UPORABA + + + DESCRIPTION + OPIS + + + EXAMPLES + PRIMERI + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + Veyon storitev + + + + VncViewWidget + + Establishing connection to %1 ... + Vzspostavljam povezavo na %1 ... + + + + WebApiConfigurationPage + + Web API + + + + General + Splošno + + + Network port + Omrežna vrata + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + s + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + Nastavitve za generiranje SAS s programsko opremo ni bilo mogoče spremeniti. Pošiljanje Ctrl+Alt+Del prek daljinskega upravljalnika ne bo delovalo! + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + Splošno + + + Enable SAS generation by software (Ctrl+Alt+Del) + Omogoči generiranje SAS s programsko opremo (Ctrl+Alt+Del) + + + Screen lock + + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + Uporabite alternativni mehanizem za preverjanje pristnosti uporabnika + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Vtičnik izvajanja abstraktnih funkcij za platformo Windows + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Vgrajena konfiguracija strežnika x11vnc + + + Custom x11vnc parameters: + Parametri x11vnc po meri: + + + Do not use X Damage extension + Ne uporabljajte X škodljive razširitve + + + diff --git a/translations/veyon_sr.ts b/translations/veyon_sr.ts new file mode 100644 index 0000000..bd30cf8 --- /dev/null +++ b/translations/veyon_sr.ts @@ -0,0 +1,4083 @@ + + + + + AboutDialog + + About + O nama + + + Translation + Prevod + + + License + Licenca + + + About Veyon + O Veyonu + + + Contributors + Saradnici + + + Version: + Verzija + + + Website: + Websajt: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Trenutni jezik nije još preveden (ili maternji Engleski). + +Ako ste zainteresovani za prevodjenje Veyon na vaš lokalni ili drugi jezik ili želite poboljšati postojeći prevod,molimo kontaktirajte Veyon programera! + + + About %1 %2 + O %1 %2 + + + Support Veyon project with a donation + Podržite Veyon projekat sa donacijom + + + + AccessControlPage + + Computer access control + Kontrola pristupa računaru + + + Grant access to every authenticated user (default) + Odobriti pristup autorizovanom korisniku (podrazumevano) + + + Test + Test + + + Process access control rules + Obraditi pravila kontrole pristupa + + + User groups authorized for computer access + Autorizovane grupe korisnika za pristup računaru + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Molimo dodajte grupe čiji članovi trebaju biti autorizovani za pristup računarima u vašoj Veyon mreži. + + + Authorized user groups + Autorizovane grupe korisnika + + + All groups + Sve grupe + + + Access control rules + Pravila kotrole pristupa + + + Add access control rule + Dodaj pravilo kontrole pristupa + + + Remove access control rule + Ukloni pravilo kotrole pristupa + + + Move selected rule down + Pomeri obeleženo pravilo dole + + + Move selected rule up + Pomeri pravilo gore + + + Edit selected rule + Izmeni obeleženo pravilo + + + Enter username + Unesi korisničko ime + + + Please enter a user login name whose access permissions to test: + Molim unesite korisničko ime prijave čiji pristup je dozvoljen za test: + + + Access allowed + Pristup dozvoljen + + + The specified user is allowed to access computers with this configuration. + Navedenom korisniku je dozvoljen pristup računarima sa ovom konfiguracijom. + + + Access denied + Pristup odbijen + + + The specified user is not allowed to access computers with this configuration. + Navedenom korisniku nije dozvoljen pristup računarima sa ovom konfiguracijom. + + + Enable usage of domain groups + Omogući korišćenje domen grupa + + + User groups backend: + Pozadina grupe korisnika: + + + Missing user groups backend + Nedostaje pozadina grupe korisnika + + + No default user groups plugin was found. Please check your installation! + Nije pronađjen nijedan dodatak za grupe korisnika. Molimo proverite vašu instalaciju! + + + Restrict access to members of specific user groups + Ograniči pristup članovima određjenih grupa korisnika + + + + AccessControlRuleEditDialog + + Edit access control rule + Izmeni pristup pravila kontrole + + + General + Opšte + + + enter a short name for the rule here + unesite kratko ime za pravilo ovde + + + Rule name: + Ime pravila: + + + enter a description for the rule here + unesite opis za pravilo ovde + + + Rule description: + Opis pravila: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Zamenite sve uslove ("is / has" je protumačeno kao "je / nije") + + + Conditions + Uslovi + + + is member of group + je član grupe + + + Accessing computer is localhost + Pristup kompjuteru je lokalni + + + Accessing user is logged on user + Pristupni korisnik je prijavljen + + + Accessing user is already connected + Pristupni korisnik je već povezan + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Ako je aktivirano više od jednog uslova, svaki se uslov mora ispuniti da bi se pravilo primenilo (logično I). Ako mora biti ispunjen samo jedan od više uslova (logički ILI),kreirajte više pravila kontrole pristupa. + + + Action + Akcija + + + Allow access + Dozvoli pristup + + + Deny access + Odbi pristup + + + Ask logged on user for permission + Pitajte prijavljenog korisnika za dozvolu + + + None (rule disabled) + Nema (pravilo onemogućeno) + + + Accessing user + Pristup korisniku + + + Accessing computer + Pristup računaru + + + Local (logged on) user + Lokalni (prijavljeni) korisnik + + + Local computer + Lokalni računar + + + Always process rule and ignore conditions + Uvek obradi pravilo i ignoriši uslove + + + No user logged on + Nijedan korisnik nije prijavljen + + + Accessing user has one or more groups in common with local (logged on) user + Korisnik koji ima pristup ima jednu ili više grupa zajedničkih s lokalnim (prijavljenim) korisnikom + + + Accessing computer and local computer are at the same location + Pristupni računar i lokalni računar su na istoj lokaciji + + + is located at + nalazi se na + + + + AccessControlRulesTestDialog + + Access control rules test + Testiranje pravila kontrole pristupa + + + Accessing user: + Pristupni korisnik: + + + Local computer: + Lokalni računar: + + + Accessing computer: + Pristupni računar: + + + Please enter the following user and computer information in order to test the configured ruleset. + Molimo unesite sledeće podatke o korisniku i računaru kako biste testirali konfigurisani skup pravila. + + + Local user: + Lokalni korisnik: + + + Connected users: + Povezani korisnici: + + + The access in the given scenario is allowed. + Dozvoljen je pristup u datom scenariju. + + + The access in the given scenario is denied. + Odbijen je pristup u datom scenariju + + + The access in the given scenario needs permission of the logged on user. + Za pristup datom scenariju potrebne su dozvole prijavljenog korisnika. + + + ERROR: Unknown action + GREŠKA: Nepoznata akcija + + + Test result + Rezultati testa + + + + AuthKeysConfigurationPage + + Authentication keys + Ključevi za autentifikaciju + + + Introduction + Uvod + + + Key file directories + Ključne datoteke direktorijuma + + + Public key file base directory + Osnovni direktorijum datoteka javnih ključeva + + + Private key file base directory + Osnovni direktorijum datoteka privatnih ključeva + + + Available authentication keys + Dostupni ključevi za proveru autentičnosti + + + Create key pair + Kreirajte par ključeva + + + Delete key + Obriši ključ + + + Import key + Uvozni ključ + + + Export key + Izvozni ključ + + + Set access group + Podesite grupu pristupa + + + Key files (*.pem) + Ključne datoteke (*.pem) + + + Authentication key name + Naziv ključa za autentifikaciju + + + Please enter the name of the user group or role for which to create an authentication key pair: + Unesite ime korisničke grupe ili uloge za kreiranje parova ključeva za provjeru autentičnosti: + + + Do you really want to delete authentication key "%1/%2"? + Želite li stvarno izbrisati autentifikacijski ključ "% 1 /% 2"? + + + Please select a key to delete! + Odaberite ključ za brisanje! + + + Please enter the name of the user group or role for which to import the authentication key: + Unesite ime korisničke grupe ili uloge za koju želite uvesti ključ za autentifikaciju: + + + Please select a key to export! + Odaberite ključ za izvoz! + + + Please select a user group which to grant access to key "%1": + Odaberite grupu korisnika koja će odobriti pristup ključu "% 1": + + + Please select a key which to set the access group for! + Odaberite ključ za koji ćete podesiti pristupnu grupu! + + + Please perform the following steps to set up key file authentication: + Molimo vas da izvršite sledeće korake za podešavanje provjere autentičnosti datoteka: + + + 1) Create a key pair on the master computer. + 1) Na glavnom računaru napravite par ključeva. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Postavite pristupnu grupu čiji članovi trebaju imati pristup drugim računarima. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Izvezite javni ključ i uvezite ga na sve računare klijenta sa istim nazivom. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Više informacija potražite u Priručniku za administratore Veyon.<a href="https://veyon.readthedocs.io/en/latest/admin/index.html"> + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Par ključeva za autentifikaciju sastoji se od dva povezana kriptografska ključa, privatnog i javnog ključa. +Privatni ključ omogućava korisnicima na matičnom računaru pristup klijentima. +Važno je da su samo ovlašćeni korisnici pročitali pristup datoteci privatnog ključa. +Javni ključ koristi se na klijentskim računarima za proveru dolaznog zahteva za povezivanje. + + + + AuthKeysManager + + Please check your permissions. + Proverite vaše dozvole. + + + Key name contains invalid characters! + Ime ključa sadrži nevažeće znakove! + + + Invalid key type specified! Please specify "%1" or "%2". + Navedena je nevažeća vrsta ključa! Navedite "% 1" ili "% 2". + + + Specified key does not exist! Please use the "list" command to list all installed keys. + Navedeni ključ ne postoji! Upotrebite komandu "list" za popis svih instaliranih ključeva. + + + One or more key files already exist! Please delete them using the "delete" command. + Jedna ili više ključnih datoteka već postoje! Izbrišite ih pomoću komande "delete". + + + Creating new key pair for "%1" + Izrada novog para ključeva za "% 1" + + + Failed to create public or private key! + Nije uspelo kreiranje javnog ili privatnog ključa! + + + Newly created key pair has been saved to "%1" and "%2". + Novostvoreni par ključeva sačuvan je u „% 1“ i „% 2“. + + + Could not remove key file "%1"! + Nije moguće ukloniti datoteku s ključevima "% 1"! + + + Could not remove key file directory "%1"! + Nije moguće ukloniti direktorijum ključnih datoteka "% 1"! + + + Failed to create directory for output file. + Nije uspelo kreiranje direktorijuma za izlaznu datoteku. + + + File "%1" already exists. + Datoteka "% 1" već postoji. + + + Failed to write output file. + Pisanje izlazne datoteke nije uspelo. + + + Key "%1/%2" has been exported to "%3" successfully. + Ključ "% 1 /% 2" je uspešno izvezen u "% 3". + + + Failed read input file. + Učitavanje ulazne datoteke nije uspelo. + + + File "%1" does not contain a valid private key! + Datoteka "% 1" ne sadrži validni privatni ključ! + + + File "%1" does not contain a valid public key! + Datoteka "% 1" ne sadrži validni javni ključ! + + + Failed to create directory for key file. + Nije uspelo kreiranje direktorijuma za ključnu datoteku. + + + Failed to write key file "%1". + Nije uspelo pisanje datoteke ključa "% 1". + + + Failed to set permissions for key file "%1"! + Nije moguće postaviti dozvole za ključnu datoteku "% 1"! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + Ključ "% 1 /% 2" je uspešno uvežen. Proverite dozvole datoteka "% 3" kako biste sprečili neovlašćeni pristup. + + + Failed to convert private key to public key + Nije uspelo konvertovati privatni ključ u javni ključ + + + Failed to create directory for private key file "%1". + Nije uspelo kreiranje direktorijuma za datoteku privatnog ključa "% 1". + + + Failed to save private key in file "%1"! + Privatni ključ u datoteci "% 1" nije uspešno sačuvan! + + + Failed to set permissions for private key file "%1"! + Neuspešno postavljanje dozvola za datoteku privatnog ključa "% 1"! + + + Failed to create directory for public key file "%1". + Neuspešno kreiranje direktorijuma za datoteku javnog ključa "% 1". + + + Failed to save public key in file "%1"! + Javni ključ u datoteci "%1" nije uspešno sačuvan! + + + Failed to set permissions for public key file "%1"! + Nije moguće postaviti dozvole za datoteku javnog ključa "% 1"! + + + Failed to set owner of key file "%1" to "%2". + Neuspešno postavljanje vlasnika datoteke ključa "% 1" na "% 2". + + + Failed to set permissions for key file "%1". + Neuspešno postavljanje dozvola za ključnu datoteku "% 1". + + + Key "%1" is now accessible by user group "%2". + Ključ "% 1" je sada dostupan grupi korisnika "% 2". + + + <N/A> + <N/A> + + + Failed to read key file. + Nije moguće pročitati datoteku s ključevima. + + + + AuthKeysPlugin + + Create new authentication key pair + Kreirajte novi par ključeva za autentifikaciju + + + Delete authentication key + Izbriši ključ za provjeru autentičnosti + + + List authentication keys + Lista ključeva za proveru autentičnosti + + + Import public or private key + Uvezite javni ili privatni ključ + + + Export public or private key + Izvoz javnog ili privatnog ključa + + + Extract public key from existing private key + Izdvojite javni ključ iz postojećeg privatnog ključa + + + Set user group allowed to access a key + Postavite korisničkoj grupi dozvoljen pristup ključu + + + KEY + KLJUČ + + + ACCESS GROUP + PRISTUPNA GRUPA + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + Ova naredba prilagođjava dozvole za pristup datoteci na <KEY> tako da samo korisnička grupa <ACCESS GROUP> može pročitati pristup. + + + NAME + IME + + + FILE + FAJL + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Ova naredba izvozi ključ autentičnosti <KEY> u <FILE>.Ako <FILE> nije specifičan i ime će se konstruisati iz imena i tipa <KEY>. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Ova komanda uvozi autentičnost ključa <KEY> od <FILE>.Ako <FILE> nije specifičan ime će biti konstruisano od imena i tipa <KEY>. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + Ova naredba navodi sve dostupne ključeve za proveru autentičnosti u konfiguracionom direktorijumu ključeva. Ako je navedena opcija "% 1",specifična tablica s ključnim detaljima biće prikazana umesto opcija. Neki detalji možda nedostaju ako ključ nije dostupan, npr. zbog nedostatka dozvola za čitanje. + + + Please specify the command to display help for! + Molimo navedite naredbu za prikaz pomoći! + + + TYPE + TIP + + + PAIR ID + PAR ID + + + Command line support for managing authentication keys + Podrška komandne linije za upravljanje ključevima autentičnosti + + + Commands for managing authentication keys + Komande za upravljanje ključevima autentičnosti + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + Ova naredba kreira novi par ključeva za provjeru autentičnosti s imenom <NAME> i sačuvaće privatni i javni ključ u konfiguracione direktorijume ključeva. Parametar mora biti naziv ključa koji može sadržavati samo slova. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + Ova naredba briše ključ za proveru autentičnosti <KEY> iz konfiguracionog direktorija ključeva. Imajte na umu da ključ nije moguće vratiti nakon što ga izbrišete. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + Ova naredba izdvaja deo javnog ključa iz privatnog ključa <KEY> i sačuvaće ga kao odgovarajući javni ključ. Pri podešavanju drugog glavnog računara dovoljno je preneti samo privatni ključ. Javni ključ se zatim može izvući. + + + + AuthKeysTableModel + + Name + Ime + + + Type + Tip + + + Access group + Pristupna grupa + + + Pair ID + Par ID + + + + BuiltinDirectoryConfigurationPage + + Computers + Računari + + + Name + Ime + + + Host address/IP + Host adresa/IP + + + MAC address + MAC adresa + + + Add new computer + Dodaj nov računar + + + Remove selected computer + Ukloni obeležene računar + + + New computer + Novi računar + + + Builtin directory + Ugradjeni direktorijum + + + Locations & computers + Lokacije & računari + + + Locations + Lokacije + + + Add new location + Dodaj novu lokaciju + + + Remove selected location + Ukloni obeleženu lokaciju + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + Uvoz CSV datoteka moguć je putem sistema komandne linije. Za više informacija pogledajte <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory"> online dokumentaciju </a>. + + + New location + Nova lokacija + + + + BuiltinDirectoryPlugin + + Show help for specific command + Prikaži pomoć za specifičnu komandu + + + Import objects from given file + Uvoz predmeta iz date datoteke + + + Export objects to given file + Izvoz predmeta iz date datoteke + + + Invalid type specified. Valid values are "%1" or "%2". + Naveden nevažeći tip. Važeće vrednosti su „% 1“ ili „% 2“. + + + Type + Tip + + + Name + Ime + + + Host address + Domaćin adresa + + + MAC address + MAC adresa + + + Specified object not found. + Navedeni objekt nije pronađjen. + + + File "%1" does not exist! + Datoteka "%1" nije pronadjena! + + + Can't open file "%1" for reading! + Nije moguće otvoriti datoteku "% 1" za čitanje! + + + Unknown argument "%1". + Nepoznati argument "% 1". + + + Computer "%1" (host address: "%2" MAC address: "%3") + Računar "% 1" (adresa domaćina: "% 2" MAC adresa: "% 3") + + + Unclassified object "%1" with ID "%2" + Nerazvrstani objekt "% 1" sa ID-om "% 2" + + + None + Nijedan + + + Computer + Računar + + + Root + Ruta + + + Invalid + Nevažeći + + + Error while parsing line %1. + Greška prilikom raščlanjivanja linije% 1. + + + Network object directory which stores objects in local configuration + Mrežni objektni direktorijum koji je sačuvan objekat u lokalnoj konfiguraciji + + + Commands for managing the builtin network object directory + Komande za upravljanje ugrađenim direktorijom mrežnih objekata + + + No format string or regular expression specified! + Nije naveden niz formata ili regularni izraz! + + + Can't open file "%1" for writing! + Nije moguće otvoriti datoteku "% 1" za pisanje! + + + No format string specified! + Nije naveden specifičan niz formata! + + + Object UUID + Objekat UUID + + + Parent UUID + Roditeljski UUID + + + Add a location or computer + Dodaj lokaciju ili računar + + + Clear all locations and computers + Očistite sve lokacije i računare + + + Dump all or individual locations and computers + Izbacite sve ili pojedine lokacije i računare + + + List all locations and computers + Lista svih lokacija i računara + + + Remove a location or computer + Ukloni lokaciju ili računar + + + Location "%1" + Lokacija "%1" + + + Builtin (computers and locations in local configuration) + Ugrađeni (računari i lokacije u lokalnoj konfiguraciji) + + + Location + Lokacija + + + FILE + FAJL + + + LOCATION + LOKACIJA + + + FORMAT-STRING-WITH-PLACEHOLDERS + FORMAT-NIZ-SA-PLOČA + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + REGULARNO-IZRAŽENJE-SA-PLOČA + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Uvozi objekte iz zadane tekstualne datoteke pomoću datog niza formata ili regularnog izraza koji sadrži jedno ili više rezervi. Važeća rezervisana mesta su:% 1 + + + Import simple CSV file to a single room + Uvezi jednostavnu CSV datoteku u jednu prostoriju + + + Import CSV file with location name in first column + Uvezi CSV datoteku sa imenom lokacije u prvu kolonu + + + Import text file with with key/value pairs using regular expressions + Uvezi tekstualnu datoteku s parovima ključ / vrednost koristeći regularne izraze + + + Import arbitrarily formatted data + Uvezi proizvoljno formatirane podatke + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Izvezi objekte u navedenu tekstualnu datoteku koristeći zadani format niza koji sadrži jedno ili više rezervi. Važeća rezervisana mesta su:% 1 + + + Export all objects to a CSV file + Izvezite sve objekte u CSV datoteku + + + Export all computers in a specific location to a CSV file + Izvezite sve računare na određenoj lokaciji u CSV datoteku + + + TYPE + TIP + + + NAME + IME + + + PARENT + RODITELJ + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + Dodaje objekt u kojem % 1 može biti jedan od "% 2" ili "% 3". % 4 može se odrediti imenom ili UUID-om. + + + Add a room + Dodaj prostoriju + + + Add a computer to room %1 + Dodaj računar u prostoriju %1 + + + OBJECT + OBJEKAT + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Uklanja navedeni objekt iz direktorijuma. % 1 može se odrediti imenom ili UUID-om. Ako uklonite lokaciju, uklonite i sva povezana računara. + + + Remove a computer by name + Uklonite računar po imenu + + + Remove an object by UUID + Uklonite objekt po UUID + + + "Room 01" + "Prostorija 01" + + + "Computer 01" + "Računar 01" + + + HOST ADDRESS + DOMAĆIN ADRESA + + + MAC ADDRESS + MAC ADRESA + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Ugrađeni VNC server (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Ugrađeni VNC server (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Domaćin/IP adresa: %1 + + + Active features: %1 + Aktivne karakteristike:% 1 + + + Online and connected + Online i povezan + + + Establishing connection + Uspostavljanje veze + + + Computer offline or switched off + Računar nije mreži ili je isključen + + + Authentication failed or access denied + Autentifikacija nije uspela ili je pristup odbijen + + + Disconnected + Nepovezan + + + No user logged on + Nijedan korisnik nije prijavljen + + + Logged on user: %1 + Prijavljeni korisnik:% 1 + + + Location: %1 + Lokacija: %1 + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 Usluga %2 na %3:%4 + + + Authentication error + Greška autentifikacije + + + Remote access + Udaljeni pristup + + + User "%1" at host "%2" is now accessing this computer. + Korisnik "%1" kod domaćina "%2" sada pristupa ovom računaru. + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + Korisnik "% 1" kod domaćina "% 2" pokušao je pristupiti ovom računaru, ali nije uspeo uspješno proveriti identitet. + + + Access control error + Greška kontrole pristupa + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + Korisnik "% 1" kod računara "% 2" pokušao je pristupiti ovom računaru, ali je blokiran zbog postavki kontrole pristupa. + + + Active connections: + + + + + ComputerManager + + User + Korisnik + + + Missing network object directory plugin + Nedostaje dodatak mrežnog objekta direktorijuma + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Nije pronađen nijedan podrazumevani dodatak mrežnog objekta. Proverite vašu instalaciju ili konfigurišite drugačiju poziciju direktorijuma mrežnih objekata putem% 1 Konfigurator. + + + Location detection failed + Otkrivanje lokacije nije uspelo + + + Computer name;Hostname;User + Ime računara; Ime domaćina; Korisnik + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + Ne mogu odrediti lokaciju ovog računara. Ovo ukazuje na problem sa konfiguracijom sistema. Sve lokacije će se umesto toga prikazati na ploči za odabir računara. + + + + ComputerSelectPanel + + Computer search + Pretraživanje računara + + + Add location + Dodaj lokaciju + + + Save computer/user list + Sačuvaj listu računara/korisnika + + + Select output filename + Odaberite naziv izlazne datoteke + + + CSV files (*.csv) + CSV datoteke (* .csv) + + + File error + Greška datoteke + + + Could not write the computer and users list to %1! Please check the file access permissions. + Ne može se zapisati lista računara i korisnika na% 1! Proverite dozvole za pristup datoteci. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Navedite postojeću konfiguracijsku datoteku za uvoz. + + + Please specify a valid filename for the configuration export. + Navedite valjano ime datoteke za izvoz konfiguracije + + + Please specify a valid key. + Molimo navedite važeći ključ. + + + Specified key does not exist in current configuration! + Navedeni ključ ne postoji u trenutnoj konfiguraciji! + + + Please specify a valid value. + Navedite valjanu vrednost. + + + Configure Veyon at command line + Konfigurišite Veyon u komandnoj liniji + + + Output file is not writable! + Izlazna datoteka se ne može pisati! + + + Output directory is not writable! + Izlazni direktorijum nije zapisan! + + + Configuration file is not readable! + Konfiguracijska datoteka nije čitljiva! + + + Clear system-wide Veyon configuration + Jasna konfiguracija Veyon na celom sistemu + + + List all configuration keys and values + Navedi sve konfiguracijske ključeve i vrednosti + + + Import configuration from given file + Uvezi konfiguraciju iz date datoteke + + + Export configuration to given file + Izvoz konfiguracije u datu datoteku + + + Read and output configuration value for given key + Pročitajte i iznesite konfiguracione vrednosti za zadani ključ + + + Write given value to given configuration key + Zapišite zadatu vrednost zadatom konfiguracijskom ključu + + + Unset (remove) given configuration key + Poništite (uklonite) dati konfiguracijski ključ + + + Commands for managing the configuration of Veyon + Naredbe za upravljanje konfiguracijom Veyon-a + + + Upgrade and save configuration of program and plugins + Nadogradite i spremite konfiguraciju programa i dodataka + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + Nije moguće izmeniti svojstvo automatskog pokretanja za uslugu% 1. + + + Could not configure the firewall configuration for the %1 Server. + Nije moguće konfigurisati konfiguraciju zaštitnog zida za% 1 Server. + + + Could not configure the firewall configuration for the %1 Worker. + Nije moguće konfigurisati konfiguraciju zaštitnog zida za% 1 Radnika. + + + Configuration is not writable. Please check your permissions! + Konfiguracija nije upisiva. Molimo proverite svoje dozvole! + + + Could not apply platform-specific configuration settings. + Ne mogu se primeniti postavke konfiguracije specifične za platformu. + + + + DemoClient + + %1 Demo + %1 Demonstracija + + + + DemoConfigurationPage + + Demo server + Demo server + + + Tunables + Tunables + + + ms + ms + + + Key frame interval + Interval ključnog okvira + + + Memory limit + Memorijski limit + + + MB + MB + + + Update interval + Interval ažuriranja + + + s + s + + + Slow down thumbnail updates while demo is running + Usporite ažuriranje sličica dok je demonstracija aktivna + + + + DemoFeaturePlugin + + Stop demo + Zaustavi demonstraciju + + + Window demo + Prozor demonstracije + + + Give a demonstration by screen broadcasting + Pokažite demonstraciju ekranskim emitovanjem + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + U ovom režimu se vaš ekran prikazuje u prozoru na svim računarima. Korisnici se po potrebi mogu prebaciti na druge prozore. + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + Dijalog pristupa radnoj površini + + + Confirm desktop access + Potvrdite pristup radnoj površini + + + Never for this session + Nikada za ovu sesiju + + + Always for this session + Uvek za ovu sesiju + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + Korisnik % 1 na računaru % 2 želi pristupiti vašoj radnoj površini. Želite li odobriti pristup? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programi & websajtovi + + + Predefined programs + Unapred definisani programi + + + Name + Ime + + + Path + Putanja + + + Add new program + Dodaj novi program + + + Remove selected program + Ukloni obeležen program + + + Predefined websites + Unapred definisani websajtovi + + + Remove selected website + Ukloni obeležen websajt + + + URL + URL + + + New program + Novi program + + + New website + Novi websajt + + + + DesktopServicesFeaturePlugin + + Run program + Pokreni program + + + Open website + Otvori web lokaciju + + + Click this button to open a website on all computers. + Klikni ovo dugme da biste otvorili web lokaciju na svim računarima. + + + Start programs and services in user desktop + Pokrenite programe i usluge na radnoj površini korisnika + + + Click this button to run a program on all computers. + Kliknite ovo dugme da biste pokrenuli program na svim računarima. + + + Run program "%1" + Pokreni program "%1" + + + Custom program + Prilagođjeni program + + + Open website "%1" + Otvori web lokaciju "%1" + + + Custom website + Prilagodjena web lokacija + + + + DocumentationFigureCreator + + Teacher + Nastavnik + + + Room %1 + Prostorija %1 + + + Please complete all tasks within the next 5 minutes. + Molimo ispunite sve zadatke u narednih 5 minuta. + + + Custom website + Prilagodjena web lokacija + + + Open file manager + Otvorite menadžer datoteka + + + Start learning tool + Pokrenite alat za učenje + + + Play tutorial video + Reprodukujte video tutorial + + + Custom program + Prilagođjeni program + + + Handout + Brošura + + + Texts to read + Tekstovi za čitanje + + + generic-student-user + opšti-student-korisnik + + + + ExternalVncServer + + External VNC server + Spoljni VNC server + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Spoljna VNC server konfiguracija + + + Port: + Port: + + + Password: + Lozinka: + + + + FeatureControl + + Feature control + Kontrola funkcija + + + + FileTransferConfigurationPage + + File transfer + Prenos datoteka + + + Directories + Direktorijumi + + + Destination directory + + + + Default source directory + + + + Options + Opcije + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + Nije moguće otvoriti datoteku "% 1" za čitanje! Molimo proverite svoje dozvole! + + + + FileTransferDialog + + File transfer + Prenos datoteka + + + Options + Opcije + + + Transfer only + Samo prenos + + + Transfer and open file(s) with associated program + Prenos i otvaranje datoteke(a) s pripadajućim programom + + + Transfer and open destination folder + Prenos i otvaranje odredišne fascikle + + + Files + Datoteke + + + Start + Pokreni + + + Overwrite existing files + Prepišite postojeće datoteke + + + + FileTransferPlugin + + File transfer + Prenos datoteka + + + Click this button to transfer files from your computer to all computers. + Kliknite ovo dugme za prenos datoteka od vašeg računara na sve računare. + + + Select one or more files to transfer + Obeležite jednu ili više datoteka za prenos. + + + Transfer files to remote computer + Prenos datoteka na upravljani računar + + + Received file "%1". + Primljena datoteka "%1". + + + Could not receive file "%1" as it already exists. + Nije moguće primiti datoteku "% 1" jer ona već postoji. + + + Could not receive file "%1" as it could not be opened for writing! + Ne mogu primiti datoteku "% 1" jer se ne može otvoriti za pisanje! + + + + GeneralConfigurationPage + + User interface + Korisnički interfejs + + + Language: + Jezik: + + + Use system language setting + Koristi podešeni sistemski jezik + + + Veyon + Veyon + + + Logging + Prijavljivanje + + + Log file directory + Dnevnik datoteka direktorijuma + + + Log level + Nivo zapisa + + + Nothing + Ništa + + + Only critical messages + Samo kritične poruke + + + Errors and critical messages + Greške i kritične poruke + + + Warnings and errors + Upozorenja i greške + + + Information, warnings and errors + Informacija,upozorenja i greške + + + Debug messages and everything else + Poruke za uklanjanje grešaka i sve ostalo + + + Limit log file size + Ograničite veličinu datoteke dnevnika + + + Clear all log files + Očistite sve datoteke dnevnika + + + Log to standard error output + Prijavite se na standardni izlaz greške + + + Network object directory + Mrežni direktorijum objekata + + + Backend: + Pozadina: + + + Update interval: + Ažuriraj interval: + + + %1 service + %1 usluga + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + Uslugu %1 potrebno je privremeno zaustaviti kako biste uklonili datoteke dnevnika. Nastaviti? + + + Log files cleared + Datoteke dnevnika su očišćene + + + All log files were cleared successfully. + Sve datoteke dnevnika uspešno su izbrisane. + + + Error + Greška + + + Could not remove all log files. + Nije bilo moguće ukloniti sve datoteke dnevnika. + + + MB + MB + + + Rotate log files + Rotacija datoteka dnevnika + + + x + x + + + seconds + sekunde + + + Write to logging system of operating system + Zapisati u sistem za prijavu operativnog sistema + + + Authentication + Autentifikacija + + + Method: + Metod: + + + Logon authentication + Provera autentičnosti + + + Key file authentication + Autentičnost ključa datoteke + + + Test + Test + + + Authentication is set up properly on this computer. + Autentifikacija je na ovom računaru pravilno postavljena. + + + Authentication keys are not set up properly on this computer. + Ključevi za autentifikaciju nisu pravilno postavljeni na ovom računaru. + + + Authentication test + Test provere autentičnosti + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + Pregledajte LDAP + + + + LdapClient + + LDAP error description: %1 + Opis greške LDAP:% 1 + + + + LdapConfigurationPage + + Basic settings + Osnovna podešavanja + + + General + Opšte + + + LDAP server and port + LDAP server i port + + + Bind DN + Vezati DN + + + Bind password + Vezati lozinku + + + Anonymous bind + Anonimni vez + + + Use bind credentials + Koristite veze akreditiva + + + Base DN + Baza DN + + + Fixed base DN + Fiksna baza DN + + + e.g. dc=example,dc=org + npr. dc = primer, dc = org + + + Discover base DN by naming context + Otkrijte bazu DN imenovanjem konteksta + + + e.g. namingContexts or defaultNamingContext + npr. imenovanje Konteksta ili podrazumevanoImenovanjeKonteksta + + + Environment settings + Postavke okruženja + + + Object trees + Objekt prikaz stablo + + + Computer tree + Rašunar prikaz stablo + + + e.g. OU=Groups + npr. OU=Grupe + + + User tree + Korisnik prikaz stablo + + + e.g. OU=Users + npr. OU=Korisnici + + + e.g. OU=Computers + npr.OU=Računari + + + Group tree + Grupa prikaz stabla + + + Perform recursive search operations in object trees + Izvođenje rekurzivnih pretraga u stablima objekata + + + Object attributes + Objekat atributi + + + e.g. hwAddress + npr.hwAdresa + + + e.g. member or memberUid + npr. član ili članUid + + + e.g. dNSHostName + npr.dNSHostName + + + Computer MAC address attribute + Atribut računarske MAC adrese + + + Group member attribute + Atribut člana grupe + + + e.g. uid or sAMAccountName + npr.uid ili sAMAccountName + + + Advanced settings + Napredna podešavanja + + + Optional object filters + Opcionalni filteri objekata + + + Filter for user groups + Filter za korisničke grupe + + + Filter for users + Filter za korisnike + + + Filter for computer groups + Filter za grupe računara + + + Group member identification + Identifikacija člana grupe + + + Distinguished name (Samba/AD) + Istaknuto ime (Samba / AD) + + + List all groups of a user + Lista svih grupa korisnika + + + List all groups of a computer + Lista svih grupa računara + + + Get computer object by IP address + Nabavite predmet računara putem IP adrese + + + LDAP connection failed + LDAP veza je neuspešna + + + LDAP bind failed + Neuspešna LDAP veza + + + LDAP bind successful + LDAP vezanje je uspešno + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Uspešno se povezao s LDAP serverom i izvršio LDAP vezanje. Osnovne postavke LDAP-a ispravno su konfigurisane. + + + LDAP base DN test failed + DN-test za LDAP bazu je neuspešan + + + LDAP base DN test successful + LDAP baza DN test je uspešan + + + LDAP naming context test failed + Neuspešan kontekstni test LDAP imenovanja + + + LDAP naming context test successful + LDAP kontekstni test imenovanja je uspešan + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + Kontekst imenovanja LDAP uspešno je prosledio upit. Pronađjena je sledeća baza DN: +%1 + + + user tree + stablo korisnika + + + group tree + stablo grupe + + + computer tree + stablo računara + + + Enter username + Unesi korisničko ime + + + Please enter a user login name (wildcards allowed) which to query: + Unesite korisničko ime za prijavu (dozvoljeni zamenski znakovi) za upite: + + + user objects + korisnički objekti + + + Enter group name + Unesi ime grupe + + + Please enter a group name whose members to query: + Molimo unesite naziv grupe čiji će članovi zatražiti: + + + group members + članovi grupe + + + Group not found + Grupa nije pronadjena + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + Nije bilo moguće pronaći grupu sa nazivom "% 1". Proverite naziv grupe ili parametar stabla grupe. + + + Enter computer name + Unesite ime računara + + + computer objects + računarski objekti + + + Enter computer DN + Unesite DN računara + + + Please enter the DN of a computer whose MAC address to query: + Molimo unesite DN računara čija je MAC adresa upitana: + + + computer MAC addresses + računarske MAC adrese + + + users + korisnici + + + user groups + korisničke grupe + + + computer groups + računarske grupe + + + Please enter a user login name whose group memberships to query: + Unesite korisničko ime za prijavu čija će članarina grupe biti upitna: + + + groups of user + grupe korisnika + + + User not found + Korisnik nije pronadjen + + + groups of computer + grupe računara + + + Computer not found + Računar nije pronadjen + + + Enter computer IP address + Unesite IP adresu računara + + + Please enter a computer IP address which to resolve to an computer object: + Unesite računarsku IP adresu koja se može rešiti na računarskom objektu: + + + computers + računari + + + LDAP %1 test failed + LDAP% 1 test je neuspešan + + + LDAP %1 test successful + LDAP% 1 test je uspešan + + + The %1 has been queried successfully and %2 entries were found. + % 1 je uspešno upitan i pronađjeni su unosi % 2. + + + %1 %2 have been queried successfully: + +%3 + % 1% 2 uspešno je prosledjen upit: + +%3 + + + LDAP filter test failed + Provera LDAP filtera nije uspela + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + Nije moguće proslediti upit nijednog % 1 pomoću konfigurisanog filtera. Molimo proverite LDAP filter za% 1. + +%2 + + + LDAP filter test successful + Provera LDAP filtera uspešna + + + %1 %2 have been queried successfully using the configured filter. + % 1% 2 uspešno je ispitan pomoću konfigurisanog filtera. + + + (only if different from group tree) + (samo ako se razlikuje od stabla grupe) + + + Computer group tree + Stablo računarskih grupa + + + computer group tree + stablo računarske grupe + + + Filter for computers + Filter za računare + + + e.g. room or computerLab + npr. soba ili kompjuterska laboratorija + + + Integration tests + Integracijski testovi + + + Computer groups + Računarske grupe + + + e.g. name or description + npr. ime ili opis + + + Filter for computer containers + Filter za kontejnere računara + + + Computer containers or OUs + Kontejneri za računare ili OU + + + Connection security + Sigurnost veze + + + TLS certificate verification + Potvrda TLS certifikata + + + System defaults + Podrazumevane postavke sistema + + + Never (insecure!) + Nikada (nesigurno!) + + + Custom CA certificate file + Prilagođena datoteka certifikata CA + + + None + Nijedan + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + npr. (objectClass = računar) + + + e.g. (objectClass=group) + npr. (objectClass = grupa) + + + e.g. (objectClass=person) + npr. (objectClass = osoba) + + + e.g. (objectClass=room) or (objectClass=computerLab) + npr. (objectClass = soba) ili (objectClass = kompjuterska laboratorija) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + npr. (objectClass = kontejner) ili (objectClass = organizationUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + Ne može se zatražiti konfigurisana baza DN. Molimo proverite osnovni DN parametar. + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + LDAP baza DN uspješno prosledjen upit. Pronađjeni su sledeći unosi: + +%1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + Nije moguć upit baznog DN-a putem imenovanja konteksta. Molimo proverite parametar atributa konteksta imenovanja. + +%1 + + + Certificate files (*.pem) + Datoteke certifikata (* .pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + Ne mogu se povezati s LDAP serverom. Molimo proverite parametre servera. + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + Nije se moglo povezati na LDAP server. Proverite parametre servera i povežite akreditive. + +%1 + + + Encryption protocol + Protokol šifrovanja + + + Computer location attribute + Atribut lokacije računara + + + Computer display name attribute + Atribut prikaza računarskog imena + + + Location name attribute + Atribut naziva lokacije + + + e.g. cn or displayName + npr. cn ili displayName + + + Computer locations identification + Identifikacija računarskih lokacija + + + Identify computer locations (e.g. rooms) via: + Prepoznajte lokacije računara (npr. Sobe) putem: + + + Location attribute in computer objects + Atribut lokacije u računarskim objektima + + + List all entries of a location + Lista svih unosa lokacije + + + List all locations + Lista svih lokacija + + + Enter computer display name + Unesite ime računara za prikaz + + + Please enter a computer display name to query: + Unesite prikazno ime računara za upit: + + + Enter computer location name + Unesite naziv lokacije računara + + + Please enter the name of a computer location (wildcards allowed): + Unesite ime lokacije računara (dozvoljene zamenske karte): + + + computer locations + lokacije računara + + + Enter location name + Unesite naziv lokacije + + + Please enter the name of a location whose entries to query: + Unesite naziv lokacije koji se unosi traže: + + + location entries + unosi lokacije + + + LDAP test failed + LDAP test neuspešan + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + Nije moguće postaviti nijedan %1. Proverite parametar(e) %2 i unesite ime postojećeg objekta. + +%3 + + + and + i + + + LDAP test successful + LDAP test je uspešan + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + Ne može se zatražiti ni jedan unos u konfigurisanom %1. Molimo proverite parametar "%2". + +%3 + + + Browse + Pregledajte + + + Test + Test + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + Imena hosta sačuvana su kao potpuno kvalifikovana imena domena (FQDN, npr. Myhost.example.org) + + + Computer hostname attribute + Atribut imena domaćina računara + + + Please enter a computer hostname to query: + Da biste poslali upit unesite ime domaćin računara: + + + Invalid hostname + Nevažeće ime domaćina + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + Konfigurisali ste imena domaćina računara da se sačuvaju kao potpuno kvalifikovana imena domena (FQDN), ali ste unieli ime računara domaćina bez domena. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + Konfigurisali ste imena domaćina računara da se sačuvaju kao jednostavna imena računara bez imena domena, ali ste unieli ime računara s delom imena domena. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + Ne mogu pronaći korisnika s imenom "% 1". Proverite korisničko ime ili parametar korisničkog stabla. + + + Enter hostname + Unesite ime domaćina + + + Please enter a computer hostname whose group memberships to query: + Unesite ime računara domaćina čije će članstvo u grupi biti upitno: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + Ne mogu pronaći računar s imenom domaćina "% 1". Molimo proverite naziv domaćina ili parametar stabla računara. + + + Hostname lookup failed + Pretraživanje imena hosta nije uspelo + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + Nije moguće potražiti ime hosta za IP adresu% 1. Proverite postavke DNS servera. + + + User login name attribute + Atribut korisničkog imena za prijavu + + + Configured attribute for user login name or computer hostname (OpenLDAP) + Konfigurisani atribut za korisničko ime za prijavu ili domaćin računara (OpenLDAP) + + + computer containers + kontejneri za računare + + + + LdapPlugin + + Auto-configure the base DN via naming context + Automatski konfigurišite osnovni DN putem konteksta imenovanja + + + Query objects from LDAP directory + Upit objekata iz LDAP direktorijuma + + + Show help about command + Pokažite pomoć o naredbi + + + Commands for configuring and testing LDAP/AD integration + Naredbe za konfigurisanje i testiranje LDAP / AD integracije + + + Basic LDAP/AD support for Veyon + Osnovna podrška za LDAP / AD za Veyon + + + %1 (load computers and locations from LDAP/AD) + %1 (učitavanje računara i lokacija iz LDAP / AD) + + + %1 (load users and groups from LDAP/AD) + %1 (učitavanje korisnika i grupa iz LDAP / AD) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + Navedite valjani LDAP URL prateći šemu "ldap[s]://[user[:password]@]hostname[:port]" + + + No naming context attribute name given - falling back to configured value. + Nije dodato ime atributa konteksta imenovanja - vraća se na konfigurisanu vrednost. + + + Could not query base DN. Please check your LDAP configuration. + Ne može se zatražiti baza DN. Molimo proverite LDAP konfiguraciju. + + + Configuring %1 as base DN and disabling naming context queries. + Konfigurisanje %1 kao osnovnog DN-a i onemogućavanje imenovanja kontekstnih upita. + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + Prilagođena PAM usluga za proveru autentičnosti korisnika + + + User authentication + Autentifikacija korisnika + + + Session management + Upravljanje sesijama + + + Display manager users + Prikaz menadžera korisnika + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Dodatak implementira apstraktne funkcije za Linux platformu + + + + LocationDialog + + Select location + Obeleži lokaciju + + + enter search filter... + unesite filter pretraživanja ... + + + + MainToolBar + + Configuration + Konfiguracija + + + Disable balloon tooltips + Onemogućite balon opisa alatki + + + Show icons only + Prikaži samo ikone + + + + MainWindow + + MainWindow + Glavni prozor + + + toolBar + alatna traka + + + General + Opšte + + + &File + &Datoteka + + + &Help + &Pomoć + + + &Quit + &Odustati + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + Učitajte postavke iz datoteke + + + Ctrl+O + Ctrl+O + + + About Qt + O QT + + + Authentication impossible + Autentifikacija je nemoguća + + + Configuration not writable + Konfiguracija se ne može prepisati + + + Load settings from file + Učitajte postavke iz datoteka + + + Save settings to file + Sačuvaj postavke u datoteku + + + Unsaved settings + Nečuvaj podešavanja + + + There are unsaved settings. Quit anyway? + Postoje ne sačuvana podešavanja. Prestati svakako? + + + Veyon Configurator + Veyon Konfigurator + + + Service + Servis + + + Master + Glavni + + + Access control + Pristup kontroli + + + About Veyon + O Veyonu + + + Auto + Automatski + + + About + O nama + + + %1 Configurator %2 + %1 Konfigurator %2 + + + JSON files (*.json) + JSON datoteke (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + Lokalni pomoćni program za konfiguraciju izveštava da konfiguracija ne može biti napisana! Molimo pokrenite% 1 konfigurator s većim privilegijama. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + Nisu pronađene datoteke ključa za autentifikaciju ili su vaše trenutne zastarele. Napravite nove datoteke s ključevima pomoću konfiguratora %1. Alternativno, postavite proveru autentičnosti za prijavu koristeći Konfigurator %1. Inače nećete moći pristupiti računarima koristeći %1. + + + Access denied + Pristup odbijen + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + Prema lokalnoj konfiguraciji nije vam dopušten pristup računarima u mreži. Prijavite se pomoću drugog naloga ili dopustite administratoru sistema da proveri lokalnu konfiguraciju. + + + Screenshots + Snimci ekrana + + + Feature active + Aktivna funkcija + + + The feature "%1" is still active. Please stop it before closing %2. + Funkcija "%1" i dalje je aktivna. Molim zaustavite pre zatvaranja %2. + + + Reset configuration + Resetujte konfiguraciju + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Želite li stvarno resetovati lokalnu konfiguraciju i vratiti sve postavke na njihove početne vrednosti? + + + Search users and computers + Pretražite korisnike i računare + + + Align computers to grid + Poravnajte računare po mreži + + + %1 Configurator + %1 Konfigurator + + + Insufficient privileges + Nedovoljno privilegija + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + Ne mogu započeti s administrativnim privilegijama. Molimo vas proverite da li je instaliran program sličan sudo-u za radnu površinu! Program će se pokrenuti sa uobičajenim korisničkim privilegijama. + + + Only show powered on computers + Pokaži samo na pokrenutim računarima + + + &Save settings to file + %Sačuvajte postavke u datoteci + + + &View + %Vidi + + + &Standard + &Standardno + + + &Advanced + &Napredno + + + Use custom computer arrangement + Koristite prilagođjeni raspored računara + + + Locations && computers + Lokacije i računari + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Direktorijumi + + + User configuration + Korisnička konfiguracija + + + Feature on computer double click: + Dvostruki klik na računaru: + + + Features + Karakteristike + + + All features + Sve karakteristike + + + Disabled features + Onemogući karakteristike + + + Screenshots + Snimci ekrana + + + <no feature> + <no feature> + + + Basic settings + Osnovna podešavanja + + + Behaviour + Ponašanje + + + Enforce selected mode for client computers + Pojačajte odabrani režim za klijentske računare + + + Hide local computer + Sakri lokalni računar + + + Hide computer filter field + Sakri polje filtera računara + + + Actions such as rebooting or powering down computers + Radnje poput ponovnog pokretanja ili isključivanja računara + + + User interface + Korisnički interfejs + + + Background color + Boja pozadine + + + Thumbnail update interval + Interval ažuriranja sličica + + + ms + ms + + + Program start + Startovanje programa + + + Modes and features + Načini i funkcije + + + User and computer name + Ime korisnika i računara + + + Only user name + Samo korisničko ime + + + Only computer name + Samo ime računara + + + Computer thumbnail caption + Opis računara u sličicama + + + Text color + Boja teksta + + + Sort order + Poredjaj prema + + + Computer and user name + Ime računara i korisnika + + + Computer locations + Lokacije računara + + + Show current location only + Prikaži samo trenutnu lokaciju + + + Allow adding hidden locations manually + Dopusti ručno dodavanje skrivenih lokacija + + + Hide empty locations + Sakri prazne lokacije + + + Show confirmation dialog for potentially unsafe actions + Prikaži potvrdni dijalog za potencijalno nesigurne radnje + + + Perform access control + Izvršite kontrolu pristupa + + + Automatically select current location + Automatski odaberite trenutnu lokaciju + + + Automatically open computer select panel + Automatski otvorite panel za odabir računara + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + Automatski + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + Nadgledanje + + + Builtin monitoring mode + Ugrađeni način praćenja + + + This mode allows you to monitor all computers at one or more locations. + Ovaj način rada omogućuje vam praćenje svih računara na jednoj ili više lokacija. + + + + NetworkObjectTreeModel + + Locations/Computers + Lokacije/Kompjuteri + + + + OpenWebsiteDialog + + Open website + Otvori web lokaciju + + + e.g. Veyon + npr. Veyon + + + Remember and add to website menu + Zapamti i dodaj meniju web stranice + + + e.g. www.veyon.io + npr. www.veyon.io + + + Please enter the URL of the website to open: + Molimo unesite adresu-URL web stranice za otvaranje: + + + Name: + Ime: + + + + PasswordDialog + + Username + Korisničko ime + + + Password + Šifra/lozinka + + + Veyon Logon + Prijava u Veyon + + + Authentication error + Greška autentifikacije + + + Logon failed with given username and password. Please try again! + Prijava je neuspešna sa datim korisničkim imenom i šifrom. Molimo pokušajte ponovo! + + + Please enter your username and password in order to access computers. + Molimo unesite vaše korisničko ime i šifru za pristup kompjuterima. + + + + PowerControlFeaturePlugin + + Power on + Uključite + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Klik na ovo dugme za uključivanje svih kompjutra. Na ovaj način ne morate uključivati svaki računar ručno. + + + Reboot + Ponovno podizanje sistema + + + Click this button to reboot all computers. + Klik na ovo dugme za ponovno podizanje sistema na svim kompjuterima. + + + Power down + Isključiti + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Klik na ovo dugme za isključivanje svih kompjutera. Na ovaj način ne morate isključivati svaki kompjuter ručno. + + + Power on/down or reboot a computer + Uključite/Isključite ili pokrenite ponovo sistem kompjutera + + + Confirm reboot + Potvrdite ponovno podizanje sistema + + + Confirm power down + Potvrdite isključivanje + + + Do you really want to reboot the selected computers? + Da li stvarno želite ponovo pokrenuti sistem izabranih kompjutera? + + + Do you really want to power down the selected computer? + Da li stvarno želite isključiti izabrane kompjutere? + + + Power on a computer via Wake-on-LAN (WOL) + Uključivanje kompjutera preko Wake-on-LAN (WOL) + + + MAC ADDRESS + MAC ADRESA + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + Ova komanda emituje Wake-on-LAN (WOL) paket na mreži da bi bio uključen kompjuter sa datom MAC adresom. + + + Please specify the command to display help for! + Molimo navedite naredbu za prikaz pomoći! + + + Invalid MAC address specified! + Specificirana je neispravna MAC adresa! + + + Commands for controlling power status of computers + Komande za kontrolu statusa napajanja kompjutera + + + Power down now + Isključiti odmah + + + Install updates and power down + Instalisati ažuriranja i isključiti + + + Power down after user confirmation + Isključiti posle potvrde od strane korisnika + + + Power down after timeout + Isključiti posle isteka vremena + + + The computer was remotely requested to power down. Do you want to power down the computer now? + Isključenje kompjutera je bilo daljinski zahtevano. Želite li isključiti kompjuter odmah? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + Kompjuter će biti isljučen za %1 minuta, %2 sekundi. + +Molimo snimite/spasite Vaš rad i zatvorite sve programe. + + + + PowerDownTimeInputDialog + + Power down + Isključiti + + + Please specify a timeout for powering down the selected computers: + Molimo odredite vreme potrebno za isključenje izabranih kompjutera: + + + minutes + minuta + + + seconds + sekundi + + + + RemoteAccessFeaturePlugin + + Remote view + Daljinski prikaz + + + Open a remote view for a computer without interaction. + Otvorite daljinski prikaz kompjutera bez interakcije. + + + Remote control + Daljinska kontrola + + + Open a remote control window for a computer. + Otvorite prozor daljinske kontrole kompjutera. + + + Remote access + Udaljeni pristup + + + Remote view or control a computer + Daljinski prikaz ili kontrola kompjutera + + + Please enter the hostname or IP address of the computer to access: + Molimo unesite ime ili IP adresu kompjutera kojem pristupate: + + + Show help about command + Pokažite pomoć o naredbi + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 - %2 Daljinski pristup + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + Samo prikaz + + + Remote control + Daljinska kontrola + + + Send shortcut + Pošalji prečicu + + + Fullscreen + Celi ekran + + + Window + Prozor + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Meni + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + Spajanje %1 + + + Connected. + Spojen. + + + Screenshot + Snimka ekrana + + + Exit + Izlaz + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Molimo unesite programe ili komande da se mogu izvršavati na odabranom kompjuteru(ima). Možete odvojiti više programa/komandi po liniji. + + + Run programs + Pokreni programe + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + npr. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + Ime: + + + Remember and add to program menu + Zapamtiti i dodati programskom meniju + + + e.g. VLC + npr. VLC + + + + ScreenLockFeaturePlugin + + Lock + Zaključaj + + + Unlock + Otključaj + + + Lock screen and input devices of a computer + Zaključajte ekran i ulazne uređaje kompjutera + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Da biste privukli punu pažnju svih korisnika možete zaključati njihove kompjutere upotrebom ovog dugmeta. Na ovaj način svi ulazni uređaji su zaključani a ekrani su zatamnjeni. + + + Lock input devices + Zaključaj ulazne uređaje + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + nepoznato + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + Ne može se napraviti snimak ekrana jer direktorij %1 ne postoji i ne može biti kreiran. + + + Screenshot + Snimka ekrana + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + Snimka ekrana + + + Use this function to take a screenshot of selected computers. + Upotrebite ovu funkciju da napravite snimak ekrana izabranog kompjutera. + + + Screenshots taken + Snimka ekrana napravljena + + + Screenshot of %1 computer have been taken successfully. + Snimka ekrana %1 kompjutera je napravljena uspešno. + + + Take screenshots of computers and save them locally. + Uzmi snimku ekrana kompjutera i sačuvaj je lokalno. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Sve snimke ekrana koje ste napravili su prikazane ovdje. Možete napraviti snimku ekrana klikom na "Screenshot" u meniju koji dobijete desnim klikom na kompjuteru. snimke ekrana mogu biti upravljane upotrebom dugmadi ispod. + + + User: + Korisnik: + + + Computer: + Kompjuter: + + + Date: + Datum: + + + Time: + Vreme: + + + Show + Prikazati + + + Delete + Pobrisati + + + Screenshot + Snimka ekrana + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Opšte + + + Autostart + Autostart + + + Hide tray icon + Sakrij ikonu u tray-u + + + Start service + Startuj servis + + + Stopped + Zaustavljen + + + Stop service + Zaustavi servis + + + State: + Stanje: + + + Enable firewall exception + Uključi firewall izuzetak + + + Allow connections from localhost only + Dozvoli samo konekcije od lokalnog kompjutera + + + VNC server + VNC server + + + Plugin: + Plugin: + + + Restart %1 Service + Ponovo pokreni %1 servis + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Sve postavke su uspješno spašene. Da bi se primijenile servis %1 treba biti ponovo pokrenut. Želite li sada ponovo pokrenuti? + + + Running + Pokrenut + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + Omogućavanjem ove opcije postići ćete da servis pokrene server proces za svaku interaktivnu sesiju na kompjuteru. +Obično je ovo zahtevano kao podrška trminal serverima. + + + Show notification on remote connection + Prikaži obaveštenje na daljinskoj vezi + + + Show notification when an unauthorized access is blocked + Prikaži obavest kada je zabranjeni pristup blokiran. + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + Interni VNC server + + + Feature manager + Upravitelj svojstava + + + Demo server + Demo server + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + Startanje servisa %1 + + + Stopping service %1 + Zaustavljanje servisa %1 + + + Registering service %1 + Registrovanje servisa %1 + + + Unregistering service %1 + Deregistrovanje servisa %1 + + + Service control + Kontrola servisa + + + + ServiceControlPlugin + + Service is running + Servis se izvršava + + + Service is not running + Servis se ne izvršava + + + Configure and control Veyon service + Konfiguracija i kontrola Veyon servisa + + + Register Veyon Service + Registracija Veyon servisa + + + Unregister Veyon Service + Deregistracija Veyon servisa + + + Start Veyon Service + Pokreni Veyon servis + + + Stop Veyon Service + Zaustavi Veyon servis + + + Restart Veyon Service + Ponovo pokreni Veyon servis + + + Query status of Veyon Service + Status ispitivanja Veyon servisa + + + Commands for configuring and controlling Veyon Service + Komande za konfiguraciju i kontrolu Veyon servisa + + + + ShellCommandLinePlugin + + Run command file + Pokreni komandnu datoteku + + + File "%1" does not exist! + Datoteka "%1" nije pronadjena! + + + Interactive shell and script execution for Veyon Control + Interaktivno izvršavanje ljuske i skripte za Veyon Control + + + Commands for shell functionalities + Komande za funkcije ljuske - shell + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + Ikona sistem tray-a + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + Podrška korisničkih grupa za sistemske korisničke grupe + + + Default (system user groups) + Zadano (sistemske korisničke grupe) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + Test internih komponenti i funkcija Veyon-a + + + Commands for testing internal components and functions of Veyon + Komande za testiranje internih komponenti i funkcija Veyon-a + + + + TextMessageDialog + + Send text message + Pošalji tekstualnu poruku + + + Use the field below to type your message which will be sent to all selected users. + Upotrebi polje ispod da napišeš poruku koja će biti poslana svim izabranim korisnicima. + + + + TextMessageFeaturePlugin + + Text message + Tekstualna poruka + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Upotrebi ovu funkciju za slanje tekst poruke svim korisnicima npr. da im dodelite zadatke. + + + Message from teacher + Poruka od predavača + + + Send a message to a user + Pošalji poruku korisniku + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Omogući hvatanje polu providnih prozora + + + Poll full screen (leave this enabled per default) + Preko celog ekrana (ostaviti uključeno kao zadano) + + + Low accuracy (turbo mode) + Mala preciznost (brzi način) + + + Builtin UltraVNC server configuration + Konfiguracija ugrađenog UltraVNC servera + + + Enable multi monitor support + Uključi više ekransku podršku + + + Enable Desktop Duplication Engine on Windows 8 and newer + Uključi Desktop Duplication Engine- mogućnost dupliciranja desktopa na Windows 8 i novijim + + + Maximum CPU usage + + + + + UserConfig + + No write access + Nema pristupa za pisanje + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Ne mogu se spasiti vaše lične postavke! Molimo proverite put do korisničke konfiguraciske datoteke upotrebom %1 Configurator-a + + + + UserLoginDialog + + User login + Prijava korisnika + + + Please enter a username and password for automatic login on all computers. + Molimo unesite korisničko ime i šifru za automatsku prijavu na svim kompjuterima. + + + Username + Korisničko ime + + + Password + Šifra/lozinka + + + + UserSessionControlPlugin + + Log in + Prijavite se + + + Click this button to log in a specific user on all computers. + Klik na ovo dugme za prijavu određenog korisnika na sve kompjutere. + + + Log off + Odjava + + + Click this button to log off users from all computers. + Klik na ovo dugme za odjavu korisnika sa svih kompjutera. + + + Confirm user logoff + Potvrdite odjavu korisnika + + + Do you really want to log off the selected users? + Želite li stvarno odjaviti izabrane korisnike? + + + User session control + Kontrola sesije korisnika + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [NEIZVRŠENO] + + + Invalid command! + Pogrešna komanda! + + + Available commands: + Dostupne komande: + + + Invalid arguments given + Određeni-dati pogrešni argumenti + + + Not enough arguments given - use "%1 help" for more information + Nije dato dovoljno argumenata - upotrebiti "1 help" za više informacija + + + Unknown result! + Nepoznat rezultat! + + + Available modules: + Dostupni moduli: + + + No module specified or module not found - available modules are: + Nije specificiran modul ili modul nije nađen - dostupni moduli su: + + + Plugin not licensed + Plugin nije licenciran + + + INFO + INFO + + + ERROR + GREŠKA + + + USAGE + UPOTREBA + + + DESCRIPTION + OPIS + + + EXAMPLES + PRIMJERI + + + WARNING + UPOZORENJE + + + + VeyonServiceControl + + Veyon Service + Veyon servis + + + + VncViewWidget + + Establishing connection to %1 ... + Uspostava konekcije prema %1 ... + + + + WebApiConfigurationPage + + Web API + + + + General + Opšte + + + Network port + Mrežni port + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + s + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + Postavka za programsko SAS generisanje ne može biti promjenjena. Slanje Ctrl+Alt+Del putem daljinske kontrole neće raditi! + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + Opšte + + + Enable SAS generation by software (Ctrl+Alt+Del) + Omogućavanje programskog SAS generisanja (Ctrl+Alt+Del) + + + Screen lock + Ekran zaključan + + + Hide taskbar + Sakriti traku zadataka + + + Hide start menu + Sakriti start meni + + + Hide desktop + Sakriti radnu površinu + + + User authentication + Autentifikacija korisnika + + + Use alternative user authentication mechanism + Upotrebiti alternativni mehanizam provere korisnika + + + User login + Prijava korisnika + + + Input start delay + Odgoda pokretanja ulaza + + + Simulated key presses interval + Interval simultanih pritisaka tipki + + + Confirm legal notice (message displayed before user logs in) + Potvrdite pravnu napomenu (poruka se prikazuje pre nego što se korisnik prijavi) + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Plugin implementiranje apstraktnih funkcija za Windows platformu + + + + WindowsServiceControl + + The service "%1" is already installed. + Servis "%1" je već instaliran. + + + The service "%1" could not be installed. + Servis "%1" ne može biti instaliran. + + + The service "%1" has been installed successfully. + Servis "%1" je instaliran uspešno. + + + The service "%1" could not be uninstalled. + Servis "%1" ne može biti deinstalisan. + + + The service "%1" has been uninstalled successfully. + Servis "%1" je deinstalisan uspešno. + + + The start type of service "%1" could not be changed. + Tip startanja servisa "%1" ne može biti promenjen. + + + Service "%1" could not be found. + Servis "%1" ne može biti pronađen. + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Konfiguracija ugrađenog x11vnc servera + + + Custom x11vnc parameters: + Prilagođeni x11vnc parametri: + + + Do not use X Damage extension + Ne upotrebljavajte X Demage ekstenziju + + + diff --git a/translations/veyon_sv.ts b/translations/veyon_sv.ts new file mode 100644 index 0000000..251cf5a --- /dev/null +++ b/translations/veyon_sv.ts @@ -0,0 +1,4058 @@ + + + + + AboutDialog + + About + Om + + + Translation + Översättning + + + License + Licens + + + About Veyon + Om Veyon + + + Contributors + Medarbetare + + + Version: + Version: + + + Website: + Hemsida + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Nuvarande språk ej översatt än (eller originalspråk Engelska). + +Om du är intresserad av att översätta Veyon till ditt lokala eller ett ett annat språk, vänligen kontakta en Veyon-utvecklare! + + + About %1 %2 + Om %1 %2 + + + Support Veyon project with a donation + Stöd Veyon-projektet med en donation + + + + AccessControlPage + + Computer access control + Datoråtkomstskontroll + + + Grant access to every authenticated user (default) + Bevilja åtkomst för alla autentiserade användare (standard) + + + Test + Test + + + Process access control rules + Processåtkomstkontroll-regler + + + User groups authorized for computer access + Användargrupper auktoriserade för datoråtkomst + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Vänligen lägg till grupperna vilkas medlemmar ska vara auktoriserade för att komma åt datorer i ditt Veyon-nätverk. + + + Authorized user groups + Auktoriserade användargrupper + + + All groups + Alla grupper + + + Access control rules + Åtkomstregler + + + Add access control rule + Lägg till åtkomstregel + + + Remove access control rule + Ta bort åtkomstregel + + + Move selected rule down + Flytta vald regel nedåt + + + Move selected rule up + Flytta vald regel uppåt + + + Edit selected rule + Redigera vald regel + + + Enter username + Skriv in användarnamn + + + Please enter a user login name whose access permissions to test: + Vänligen skriv in ett användarnamn vars åtkomstbehörigheter ska testas: + + + Access allowed + Åtkomst beviljad + + + The specified user is allowed to access computers with this configuration. + Den specificerade användaren är tillåten att komma åt datorer med den här konfigurationen. + + + Access denied + Åtkomst nekad + + + The specified user is not allowed to access computers with this configuration. + Den specificerade användaren är inte tillåten att komma åt datorer med den här konfigurationen. + + + Enable usage of domain groups + Aktivera användandet av domängrupper + + + User groups backend: + Backend för användargrupper: + + + Missing user groups backend + Saknad backend för användargrupper + + + No default user groups plugin was found. Please check your installation! + Inget standard användargrupp-plugin kunde hittas. Vänligen kontrollera din installation! + + + Restrict access to members of specific user groups + + + + + AccessControlRuleEditDialog + + Edit access control rule + Redigera åtkomstregel + + + General + Allmänt + + + enter a short name for the rule here + skriv in ett kort namn på regeln här + + + Rule name: + Regelnamn: + + + enter a description for the rule here + skriv in en beskrivning av regeln här + + + Rule description: + Regelbeskrivning: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Invertera alla villkor ("är/har" tolkas som "är/har inte") + + + Conditions + Villkor + + + is member of group + är medlem av gruppen + + + Accessing computer is localhost + Åtkomstdator är localhost + + + Accessing user is logged on user + Åtkomstanvändare är inloggad användare + + + Accessing user is already connected + Åtkomstanvändare är redan ansluten + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Om fler än ett villkor aktiveras behöver varje villkor mötas för att regeln ska gälla (logiskt OCH). Om endast en av flera villkor behöver mötas (logiskt ELLER) vänligen skapa flera åtkomstregler. + + + Action + Åtgärd + + + Allow access + Bevilja åtkomst + + + Deny access + Neka åtkomst + + + Ask logged on user for permission + Fråga inloggad användare om tillstånd + + + None (rule disabled) + Inga (regel inaktiverad) + + + Accessing user + Åtkomstanvändare + + + Accessing computer + Åtkomstdator + + + Local (logged on) user + Lokal (inloggad) användare + + + Local computer + Lokal dator + + + Always process rule and ignore conditions + Bearbeta alltid regel- och ignoreringsvillkor + + + No user logged on + Ingen användare inloggad + + + Accessing user has one or more groups in common with local (logged on) user + Åtkomstanvändaren har en eller flera grupper gemensamt med lokal (inloggad) användare + + + Accessing computer and local computer are at the same location + + + + is located at + + + + + AccessControlRulesTestDialog + + Access control rules test + Åtkomstregel-test + + + Accessing user: + + + + Local computer: + + + + Accessing computer: + + + + Please enter the following user and computer information in order to test the configured ruleset. + + + + Local user: + + + + Connected users: + + + + The access in the given scenario is allowed. + + + + The access in the given scenario is denied. + + + + The access in the given scenario needs permission of the logged on user. + + + + ERROR: Unknown action + + + + Test result + + + + + AuthKeysConfigurationPage + + Authentication keys + + + + Introduction + + + + Key file directories + + + + Public key file base directory + + + + Private key file base directory + + + + Available authentication keys + + + + Create key pair + + + + Delete key + + + + Import key + + + + Export key + + + + Set access group + + + + Key files (*.pem) + + + + Authentication key name + + + + Please enter the name of the user group or role for which to create an authentication key pair: + + + + Do you really want to delete authentication key "%1/%2"? + + + + Please select a key to delete! + + + + Please enter the name of the user group or role for which to import the authentication key: + + + + Please select a key to export! + + + + Please select a user group which to grant access to key "%1": + + + + Please select a key which to set the access group for! + + + + Please perform the following steps to set up key file authentication: + + + + 1) Create a key pair on the master computer. + + + + 2) Set an access group whose members should be allowed to access other computers. + + + + 3) Export the public key and import it on all client computers with the same name. + + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + + + + + AuthKeysManager + + Please check your permissions. + + + + Key name contains invalid characters! + + + + Invalid key type specified! Please specify "%1" or "%2". + + + + Specified key does not exist! Please use the "list" command to list all installed keys. + + + + One or more key files already exist! Please delete them using the "delete" command. + + + + Creating new key pair for "%1" + + + + Failed to create public or private key! + + + + Newly created key pair has been saved to "%1" and "%2". + + + + Could not remove key file "%1"! + + + + Could not remove key file directory "%1"! + + + + Failed to create directory for output file. + + + + File "%1" already exists. + + + + Failed to write output file. + + + + Key "%1/%2" has been exported to "%3" successfully. + + + + Failed read input file. + + + + File "%1" does not contain a valid private key! + + + + File "%1" does not contain a valid public key! + + + + Failed to create directory for key file. + + + + Failed to write key file "%1". + + + + Failed to set permissions for key file "%1"! + + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + + + + Failed to convert private key to public key + + + + Failed to create directory for private key file "%1". + + + + Failed to save private key in file "%1"! + + + + Failed to set permissions for private key file "%1"! + + + + Failed to create directory for public key file "%1". + + + + Failed to save public key in file "%1"! + + + + Failed to set permissions for public key file "%1"! + + + + Failed to set owner of key file "%1" to "%2". + + + + Failed to set permissions for key file "%1". + + + + Key "%1" is now accessible by user group "%2". + + + + <N/A> + + + + Failed to read key file. + + + + + AuthKeysPlugin + + Create new authentication key pair + + + + Delete authentication key + + + + List authentication keys + + + + Import public or private key + + + + Export public or private key + + + + Extract public key from existing private key + + + + Set user group allowed to access a key + + + + KEY + + + + ACCESS GROUP + + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + + NAME + + + + FILE + + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + + Please specify the command to display help for! + + + + TYPE + + + + PAIR ID + + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + + + + Type + + + + Access group + + + + Pair ID + + + + + BuiltinDirectoryConfigurationPage + + Computers + + + + Name + + + + Host address/IP + + + + MAC address + + + + Add new computer + + + + Remove selected computer + + + + New computer + + + + Builtin directory + + + + Locations & computers + + + + Locations + + + + Add new location + + + + Remove selected location + + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + + + + + BuiltinDirectoryPlugin + + Show help for specific command + + + + Import objects from given file + + + + Export objects to given file + + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + + + + Name + + + + Host address + + + + MAC address + + + + Specified object not found. + + + + File "%1" does not exist! + + + + Can't open file "%1" for reading! + + + + Unknown argument "%1". + + + + Computer "%1" (host address: "%2" MAC address: "%3") + + + + Unclassified object "%1" with ID "%2" + + + + None + + + + Computer + + + + Root + + + + Invalid + + + + Error while parsing line %1. + + + + Network object directory which stores objects in local configuration + + + + Commands for managing the builtin network object directory + + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + + + + Parent UUID + + + + Add a location or computer + + + + Clear all locations and computers + + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + + + + FILE + + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + + + + NAME + + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + + + + + ComputerControlListModel + + Host/IP address: %1 + + + + Active features: %1 + + + + Online and connected + + + + Establishing connection + + + + Computer offline or switched off + + + + Authentication failed or access denied + + + + Disconnected + + + + No user logged on + Ingen användare inloggad + + + Logged on user: %1 + + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + + + + Authentication error + Autentiseringsfel + + + Remote access + + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + + + + Missing network object directory plugin + + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + + + + Add location + + + + Save computer/user list + + + + Select output filename + + + + CSV files (*.csv) + + + + File error + + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + + + + Please specify a valid filename for the configuration export. + + + + Please specify a valid key. + + + + Specified key does not exist in current configuration! + + + + Please specify a valid value. + + + + Configure Veyon at command line + + + + Output file is not writable! + + + + Output directory is not writable! + + + + Configuration file is not readable! + + + + Clear system-wide Veyon configuration + + + + List all configuration keys and values + + + + Import configuration from given file + + + + Export configuration to given file + + + + Read and output configuration value for given key + + + + Write given value to given configuration key + + + + Unset (remove) given configuration key + + + + Commands for managing the configuration of Veyon + + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + + + + + DemoConfigurationPage + + Demo server + Demoserver + + + Tunables + + + + ms + + + + Key frame interval + + + + Memory limit + + + + MB + + + + Update interval + + + + s + + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + + + + Window demo + Demo i fönster + + + Give a demonstration by screen broadcasting + + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + + + + Confirm desktop access + + + + Never for this session + Aldrig för denna session + + + Always for this session + Alltid för denna session + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + + DesktopServicesConfigurationPage + + Programs & websites + + + + Predefined programs + + + + Name + + + + Path + + + + Add new program + + + + Remove selected program + + + + Predefined websites + + + + Remove selected website + + + + URL + + + + New program + + + + New website + + + + + DesktopServicesFeaturePlugin + + Run program + + + + Open website + + + + Click this button to open a website on all computers. + + + + Start programs and services in user desktop + + + + Click this button to run a program on all computers. + + + + Run program "%1" + + + + Custom program + + + + Open website "%1" + + + + Custom website + + + + + DocumentationFigureCreator + + Teacher + Lärare + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + + + + Port: + + + + Password: + + + + + FeatureControl + + Feature control + + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + + + + Destination directory + + + + Default source directory + + + + Options + + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + + + + Select one or more files to transfer + + + + Transfer files to remote computer + + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + Användargränssnitt + + + Language: + + + + Use system language setting + + + + Veyon + + + + Logging + Loggning + + + Log file directory + Loggfilskatalog + + + Log level + Loggnivå + + + Nothing + Ingenting + + + Only critical messages + Endast kritiska meddelanden + + + Errors and critical messages + Fel och kritiska meddelanden + + + Warnings and errors + Varningar och fel + + + Information, warnings and errors + Information, varningar och fel + + + Debug messages and everything else + Felsökningsmeddelanden och allting annat + + + Limit log file size + Begränsa storlek på loggfil + + + Clear all log files + Töm alla loggfiler + + + Log to standard error output + + + + Network object directory + + + + Backend: + + + + Update interval: + + + + %1 service + + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + + + + All log files were cleared successfully. + + + + Error + + + + Could not remove all log files. + + + + MB + + + + Rotate log files + + + + x + + + + seconds + sekunder + + + Write to logging system of operating system + + + + Authentication + Autentisering + + + Method: + + + + Logon authentication + + + + Key file authentication + + + + Test + Test + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + + + + + LdapConfigurationPage + + Basic settings + + + + General + Allmänt + + + LDAP server and port + + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + + + + e.g. OU=Computers + + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + + LDAP base DN test failed + + + + LDAP base DN test successful + + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + Skriv in användarnamn + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + + + + Please enter a group name whose members to query: + + + + group members + + + + Group not found + + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + + + + users + + + + user groups + + + + computer groups + + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + + + + LDAP %1 test failed + + + + LDAP %1 test successful + + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + + + + TLS certificate verification + + + + System defaults + + + + Never (insecure!) + + + + Custom CA certificate file + + + + None + + + + TLS + + + + SSL + + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + Test + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + + + + enter search filter... + + + + + MainToolBar + + Configuration + + + + Disable balloon tooltips + + + + Show icons only + + + + + MainWindow + + MainWindow + Huvudfönster + + + toolBar + verktygsrad + + + General + Allmänt + + + &File + &Arkiv + + + &Help + &Hjälp + + + &Quit + A&vsluta + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + &Läs in inställningar från fil + + + Ctrl+O + Ctrl+O + + + About Qt + Om Qt + + + Authentication impossible + + + + Configuration not writable + Konfigurationen är inte skrivbar + + + Load settings from file + Läs in inställningar från fil + + + Save settings to file + Spara inställningar till fil + + + Unsaved settings + Osparade inställningar + + + There are unsaved settings. Quit anyway? + Det finns osparade inställningar. Avsluta ändå? + + + Veyon Configurator + + + + Service + + + + Master + + + + Access control + + + + About Veyon + Om Veyon + + + Auto + + + + About + Om + + + %1 Configurator %2 + + + + JSON files (*.json) + + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + Åtkomst nekad + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + + + + Align computers to grid + + + + %1 Configurator + + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + + + + User configuration + + + + Feature on computer double click: + + + + Features + + + + All features + + + + Disabled features + + + + Screenshots + + + + <no feature> + + + + Basic settings + + + + Behaviour + + + + Enforce selected mode for client computers + + + + Hide local computer + + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + + + + User interface + Användargränssnitt + + + Background color + + + + Thumbnail update interval + + + + ms + + + + Program start + + + + Modes and features + + + + User and computer name + + + + Only user name + + + + Only computer name + + + + Computer thumbnail caption + + + + Text color + + + + Sort order + + + + Computer and user name + + + + Computer locations + + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + + + + Name: + + + + + PasswordDialog + + Username + Användarnamn + + + Password + Lösenord + + + Veyon Logon + + + + Authentication error + Autentiseringsfel + + + Logon failed with given username and password. Please try again! + + + + Please enter your username and password in order to access computers. + + + + + PowerControlFeaturePlugin + + Power on + Starta + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + + Reboot + Starta om + + + Click this button to reboot all computers. + + + + Power down + Stäng av + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + + + + Confirm reboot + + + + Confirm power down + + + + Do you really want to reboot the selected computers? + + + + Do you really want to power down the selected computer? + + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + Stäng av + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + + + + Open a remote view for a computer without interaction. + + + + Remote control + Fjärrstyrning + + + Open a remote control window for a computer. + + + + Remote access + + + + Remote view or control a computer + + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + Visa endast + + + Remote control + Fjärrstyrning + + + Send shortcut + + + + Fullscreen + Helskärm + + + Window + Fönster + + + Ctrl+Alt+Del + + + + Ctrl+Esc + + + + Alt+Tab + + + + Alt+F4 + + + + Win+Tab + + + + Win + + + + Menu + + + + Alt+Ctrl+F1 + + + + Connecting %1 + Ansluter %1 + + + Connected. + Ansluten. + + + Screenshot + + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + + + + Unlock + + + + Lock screen and input devices of a computer + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + okänd + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + + + + Use this function to take a screenshot of selected computers. + + + + Screenshots taken + + + + Screenshot of %1 computer have been taken successfully. + + + + Take screenshots of computers and save them locally. + + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + Användare: + + + Computer: + + + + Date: + Datum: + + + Time: + Tid: + + + Show + Visa + + + Delete + Ta bort + + + Screenshot + + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Allmänt + + + Autostart + Automatisk start + + + Hide tray icon + Dölj aktivitetsikonen + + + Start service + Starta tjänst + + + Stopped + Stoppad + + + Stop service + Stoppa tjänst + + + State: + Tillstånd: + + + Enable firewall exception + Aktivera brandväggsundantag + + + Allow connections from localhost only + + + + VNC server + + + + Plugin: + + + + Restart %1 Service + + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + + Running + Kör + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + Demoserver + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + + + + Stopping service %1 + + + + Registering service %1 + + + + Unregistering service %1 + + + + Service control + + + + + ServiceControlPlugin + + Service is running + + + + Service is not running + + + + Configure and control Veyon service + + + + Register Veyon Service + + + + Unregister Veyon Service + + + + Start Veyon Service + + + + Stop Veyon Service + + + + Restart Veyon Service + + + + Query status of Veyon Service + + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + Skicka textmeddelande + + + Use the field below to type your message which will be sent to all selected users. + + + + + TextMessageFeaturePlugin + + Text message + Textmeddelande + + + Use this function to send a text message to all users e.g. to assign them new tasks. + + + + Message from teacher + + + + Send a message to a user + + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Aktivera fångst av flerskiktade fönster (semi-transparant) + + + Poll full screen (leave this enabled per default) + + + + Low accuracy (turbo mode) + Låg precision (turboläge) + + + Builtin UltraVNC server configuration + + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + Ingen skrivåtkomst + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + Användarnamn + + + Password + Lösenord + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + + + + + VeyonCore + + [OK] + + + + [FAIL] + + + + Invalid command! + + + + Available commands: + + + + Invalid arguments given + + + + Not enough arguments given - use "%1 help" for more information + + + + Unknown result! + + + + Available modules: + + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + + + + INFO + + + + ERROR + + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + + + + + VncViewWidget + + Establishing connection to %1 ... + Etablerar anslutning till %1... + + + + WebApiConfigurationPage + + Web API + + + + General + Allmänt + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + Allmänt + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + + + + Custom x11vnc parameters: + + + + Do not use X Damage extension + + + + diff --git a/translations/veyon_th.ts b/translations/veyon_th.ts new file mode 100644 index 0000000..e306727 --- /dev/null +++ b/translations/veyon_th.ts @@ -0,0 +1,4060 @@ + + + + + AboutDialog + + About + เกี่ยวกับ + + + Translation + การแปลภาษา + + + License + ลิขสิทธิ์ + + + About Veyon + เกี่ยวกับ Veyon + + + Contributors + ผู้มีส่วนช่วยเหลือ + + + Version: + เวอร์ชั่น: + + + Website: + เว็บไซต์: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + + + + About %1 %2 + เกี่ยวกับ %1 %2 + + + Support Veyon project with a donation + สนับสนุนโครงการ Veyon ด้วยการบริจาค + + + + AccessControlPage + + Computer access control + + + + Grant access to every authenticated user (default) + + + + Test + ทดสอบ + + + Process access control rules + + + + User groups authorized for computer access + + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + + + + Authorized user groups + กลุ่มผู้ใช้ที่ได้รับอนุญาต + + + All groups + กลุ่มทั้งหมด + + + Access control rules + + + + Add access control rule + + + + Remove access control rule + + + + Move selected rule down + + + + Move selected rule up + + + + Edit selected rule + แก้ไขกฏที่เลือก + + + Enter username + ใส่ชื่อผู้ใช้ + + + Please enter a user login name whose access permissions to test: + + + + Access allowed + การเข้าถึงได้รับอนุญาต + + + The specified user is allowed to access computers with this configuration. + + + + Access denied + การเข้าถึงถูกปฏิเสธ + + + The specified user is not allowed to access computers with this configuration. + + + + Enable usage of domain groups + เปิดอนุญาตการให้ใช้งานกลุ่มโดเมน + + + User groups backend: + + + + Missing user groups backend + + + + No default user groups plugin was found. Please check your installation! + + + + Restrict access to members of specific user groups + + + + + AccessControlRuleEditDialog + + Edit access control rule + + + + General + ทั่วไป + + + enter a short name for the rule here + + + + Rule name: + ชื่อกฏ: + + + enter a description for the rule here + + + + Rule description: + คำอธิบายกฏ: + + + Invert all conditions ("is/has" interpreted as "is/has not") + + + + Conditions + เงื่อนไข + + + is member of group + เป็นสมาชิกของกลุ่ม + + + Accessing computer is localhost + + + + Accessing user is logged on user + + + + Accessing user is already connected + + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + + + + Action + การกระทำ + + + Allow access + อนุญาตเข้าถึง + + + Deny access + ปฏิเสธการเข้าถึง + + + Ask logged on user for permission + ถามผู้ใช้ที่อยู่ในระบบเพื่อขออนุญาต + + + None (rule disabled) + ไม่มี (ปิดการใช้งานกฏ) + + + Accessing user + ผู้ใช้ที่เข้าถึง + + + Accessing computer + คอมพิวเตอร์ที่เข้าถึง + + + Local (logged on) user + ผู้ใช้ท้องที่ (อยู่ในระบบ) + + + Local computer + คอมพิวเตอร์ท้องที่ + + + Always process rule and ignore conditions + + + + No user logged on + ไม่มีผู้ใช้เข้าสู่ระบบ + + + Accessing user has one or more groups in common with local (logged on) user + + + + Accessing computer and local computer are at the same location + + + + is located at + + + + + AccessControlRulesTestDialog + + Access control rules test + + + + Accessing user: + + + + Local computer: + + + + Accessing computer: + + + + Please enter the following user and computer information in order to test the configured ruleset. + + + + Local user: + + + + Connected users: + ผู้ใช้ที่เชื่อมต่ออยู่: + + + The access in the given scenario is allowed. + + + + The access in the given scenario is denied. + + + + The access in the given scenario needs permission of the logged on user. + + + + ERROR: Unknown action + ข้อผิดพลาด: การกระทำที่ไม่รู้จัก + + + Test result + ผลการทดสอบ + + + + AuthKeysConfigurationPage + + Authentication keys + + + + Introduction + + + + Key file directories + ไดเรกทอรีเก็บไฟล์กุญแจ + + + Public key file base directory + + + + Private key file base directory + + + + Available authentication keys + + + + Create key pair + สร้างคู่กุญแจ + + + Delete key + ลบกุญแจ + + + Import key + นำเข้ากุญแจ + + + Export key + ส่งออกกุญแจ + + + Set access group + + + + Key files (*.pem) + ไฟล์กุญแจ (*.pem) + + + Authentication key name + + + + Please enter the name of the user group or role for which to create an authentication key pair: + + + + Do you really want to delete authentication key "%1/%2"? + + + + Please select a key to delete! + โปรดเลือกกุญแจที่จะลบก่อน! + + + Please enter the name of the user group or role for which to import the authentication key: + + + + Please select a key to export! + กรุณาเลือกกุญแจที่จะส่งออกก่อน! + + + Please select a user group which to grant access to key "%1": + + + + Please select a key which to set the access group for! + + + + Please perform the following steps to set up key file authentication: + + + + 1) Create a key pair on the master computer. + 1) สร้างคู่กุญแจบนคอมพิวเตอร์หลัก + + + 2) Set an access group whose members should be allowed to access other computers. + 2) ตั้งกลุ่มการเข้าถึง โดยสมาชิกใดบ้างที่จะสามารถเข้าถึงคอมพิวเตอร์เครื่องอื่นได้ + + + 3) Export the public key and import it on all client computers with the same name. + 3) ส่งออกไฟล์กุญแจสาธารณะ และนำเข้าลงในเครื่องคอมพิวเตอร์ลูกข่ายโดยใช้ชื่อไฟล์เดียวกัน + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + คู่กุญแจยืนยันตัวตนนั้นจะประกอบด้วยกุญแจคริปโตกราฟิกสองดอกกุญแจสาธารณะและกุญแจส่วนตัว +กุญแจส่วนตัวจะอนุญาตให้ผู้ใช้งานในเครื่องมาสเตอร์ในการการเข้าถึงเครื่องลูกข่าย +ซึ่งเป็นสิ่งที่สำคัญว่า ต้องกำหนดให้ผู้ใช้ที่ได้รับการอนุญาตเท่านั้นที่จะสามารถอ่านไฟล์กุญแจส่วนตัวได้ +ไฟล์กุญแจสาธารณะจะใช้บนเครื่องลูกข่ายเพื่อรับการยืนยันตัวตนจากการร้องขอการเชื่อมต่อที่เข้ามา + + + + AuthKeysManager + + Please check your permissions. + กรุณาตรวจสอบสิทธิ์อนุญาตของคุณ + + + Key name contains invalid characters! + ชื่อไฟล์กุญแจประกอบด้วยตัวอักษรที่ไม่สามารถใช้ได้ + + + Invalid key type specified! Please specify "%1" or "%2". + + + + Specified key does not exist! Please use the "list" command to list all installed keys. + + + + One or more key files already exist! Please delete them using the "delete" command. + + + + Creating new key pair for "%1" + กำลังสร้างคู่กุญแจใหม่สำหรับ "%1" + + + Failed to create public or private key! + ล้มเหลวในการสร้างกุญแจสาธารณะหรือส่วนตัว + + + Newly created key pair has been saved to "%1" and "%2". + คู่กุญแจที่ถูกสร้างขึ้นใหม่ได้บันทึกลงใน "%1" และ "%2" + + + Could not remove key file "%1"! + ไม่สามารถลบไฟล์กุญแจ "%1" ได้! + + + Could not remove key file directory "%1"! + + + + Failed to create directory for output file. + + + + File "%1" already exists. + ไฟล์ "%1" มีอยู่แล้ว + + + Failed to write output file. + + + + Key "%1/%2" has been exported to "%3" successfully. + + + + Failed read input file. + + + + File "%1" does not contain a valid private key! + + + + File "%1" does not contain a valid public key! + + + + Failed to create directory for key file. + + + + Failed to write key file "%1". + + + + Failed to set permissions for key file "%1"! + + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + + + + Failed to convert private key to public key + + + + Failed to create directory for private key file "%1". + + + + Failed to save private key in file "%1"! + + + + Failed to set permissions for private key file "%1"! + + + + Failed to create directory for public key file "%1". + + + + Failed to save public key in file "%1"! + + + + Failed to set permissions for public key file "%1"! + + + + Failed to set owner of key file "%1" to "%2". + + + + Failed to set permissions for key file "%1". + + + + Key "%1" is now accessible by user group "%2". + + + + <N/A> + <N/A> + + + Failed to read key file. + ไม่สามารถอ่านไฟล์กุญแจได้ + + + + AuthKeysPlugin + + Create new authentication key pair + สร้างคู่กุญแจยืนยันตัวตนใหม่ + + + Delete authentication key + ลบกุญแจยืนยันตัวตน + + + List authentication keys + + + + Import public or private key + + + + Export public or private key + + + + Extract public key from existing private key + + + + Set user group allowed to access a key + + + + KEY + + + + ACCESS GROUP + + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + + + + NAME + + + + FILE + + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + + + + Please specify the command to display help for! + + + + TYPE + + + + PAIR ID + + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + ชื่อ + + + Type + ประเภท + + + Access group + กลุ่มการเข้าถึง + + + Pair ID + + + + + BuiltinDirectoryConfigurationPage + + Computers + คอมพิวเตอร์ + + + Name + ชื่อ + + + Host address/IP + โฮสแอดแดรส/IP + + + MAC address + MAC แอดแดรส + + + Add new computer + เพิ่มคอมพิวเตอร์ใหม่ + + + Remove selected computer + ลบคอมพิวเตอร์ที่เลือก + + + New computer + คอมพิวเตอร์ใหม่ + + + Builtin directory + + + + Locations & computers + สถานที่และคอมพิวเตอร์ + + + Locations + สถานที่ + + + Add new location + เพิ่มสถานที่ใหม่ + + + Remove selected location + ลบสถานที่ที่เลือก + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + สถานที่ใหม่ + + + + BuiltinDirectoryPlugin + + Show help for specific command + + + + Import objects from given file + + + + Export objects to given file + + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + ประเภท + + + Name + ชื่อ + + + Host address + โฮสแอดแดรส + + + MAC address + MAC แอดแดรส + + + Specified object not found. + + + + File "%1" does not exist! + ไฟล์ "%1" ไม่มีอยู่! + + + Can't open file "%1" for reading! + + + + Unknown argument "%1". + + + + Computer "%1" (host address: "%2" MAC address: "%3") + + + + Unclassified object "%1" with ID "%2" + + + + None + ไม่มี + + + Computer + คอมพิวเตอร์ + + + Root + + + + Invalid + ไม่ถูกต้อง + + + Error while parsing line %1. + + + + Network object directory which stores objects in local configuration + + + + Commands for managing the builtin network object directory + + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + + + + Parent UUID + + + + Add a location or computer + เพิ่มสถานที่หรือคอมพิวเตอร์ + + + Clear all locations and computers + ล้างรายการสถานที่และคอมพิวเตอร์ทั้งหมด + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + + + + FILE + + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + + + + NAME + + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + เพิ่มห้อง + + + Add a computer to room %1 + เพิ่มคอมพิวเตอร์ไปยังห้อง %1 + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + "ห้อง 01" + + + "Computer 01" + "คอมพิวเตอร์ 01" + + + HOST ADDRESS + + + + MAC ADDRESS + + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + เซิฟเวอร์ VNC ในตัว (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + เซิฟเวอร์ VNC ในตัว (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + + + + Active features: %1 + + + + Online and connected + ออนไลน์และเชื่อมต่อแล้ว + + + Establishing connection + กำลังสร้างการเชื่อมต่อ + + + Computer offline or switched off + คอมพิวเตอร์ออฟไลน์หรือปิดเครื่องอยู่ + + + Authentication failed or access denied + + + + Disconnected + ถูกตัดการเชื่อมต่อ + + + No user logged on + ไม่มีผู้ใช้เข้าสู่ระบบ + + + Logged on user: %1 + ผู้ใช้ที่เข้าสู่ระบบ: %1 + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + + + + Authentication error + การยืนยันตัวตนผิดพลาด + + + Remote access + การเข้าถึงระยะไกล + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + ผู้ใช้ + + + Missing network object directory plugin + + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + การค้นหาคอมพิวเตอร์ + + + Add location + เพิ่มสถานที่ + + + Save computer/user list + + + + Select output filename + + + + CSV files (*.csv) + ไฟล์ CSV (*.csv) + + + File error + ไฟล์มีข้อผิดพลาด + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + + + + Please specify a valid filename for the configuration export. + + + + Please specify a valid key. + + + + Specified key does not exist in current configuration! + + + + Please specify a valid value. + + + + Configure Veyon at command line + + + + Output file is not writable! + + + + Output directory is not writable! + + + + Configuration file is not readable! + + + + Clear system-wide Veyon configuration + + + + List all configuration keys and values + + + + Import configuration from given file + + + + Export configuration to given file + + + + Read and output configuration value for given key + + + + Write given value to given configuration key + + + + Unset (remove) given configuration key + + + + Commands for managing the configuration of Veyon + + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + สาธิต %1 + + + + DemoConfigurationPage + + Demo server + เซิฟเวอร์สาธิต + + + Tunables + + + + ms + ms + + + Key frame interval + + + + Memory limit + จำกัดหน่วยความจำ + + + MB + MB + + + Update interval + + + + s + วิ + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + หยุดการสาธิต + + + Window demo + สาธิตแบบมีหน้าต่าง + + + Give a demonstration by screen broadcasting + อธิบายสาธิตด้วยการถ่ายทอดสดจากหน้าจอ + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + + + + Confirm desktop access + + + + Never for this session + + + + Always for this session + + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + + DesktopServicesConfigurationPage + + Programs & websites + โปรแกรมและเว็บไซต์ + + + Predefined programs + โปรแกรมที่กำหนดไว้ + + + Name + ชื่อ + + + Path + + + + Add new program + เพิ่มโปรแกรมใหม่ + + + Remove selected program + ลบโปรแกรมที่เลือก + + + Predefined websites + + + + Remove selected website + + + + URL + URL + + + New program + โปรแกรมใหม่ + + + New website + เว็บไซต์ใหม่ + + + + DesktopServicesFeaturePlugin + + Run program + สั่งเปิดโปรแกรม + + + Open website + เปิดเว็บไซต์ + + + Click this button to open a website on all computers. + คลิกที่ปุ่มนี้เพื่อเปิดเว็บไซต์ทั้งหมด + + + Start programs and services in user desktop + + + + Click this button to run a program on all computers. + คลิกปุ่มนี้เพื่อสั่งเปิดโปรแกรมบนเครื่องคอมพิวเตอร์ทุกเครื่อง + + + Run program "%1" + สั่งเปิดโปรแกรม "%1" + + + Custom program + โปรแกรมกำหนดเอง + + + Open website "%1" + เปิดเว็บไซต์ "%1" + + + Custom website + เว็บไซต์กำหนดเอง + + + + DocumentationFigureCreator + + Teacher + ครู + + + Room %1 + ห้อง %1 + + + Please complete all tasks within the next 5 minutes. + กรุณาทำงานตามที่ได้มอบหมายที่ให้ไว้ภายใน 5 นาทีข้างหน้า + + + Custom website + เว็บไซต์กำหนดเอง + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + โปรแกรมกำหนดเอง + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + เซิฟเวอร์ VNC ภายนอก + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + การตั้งค่าเซิฟเวอร์ VNC ภายนอก + + + Port: + พอร์ท: + + + Password: + รหัสผ่าน: + + + + FeatureControl + + Feature control + + + + + FileTransferConfigurationPage + + File transfer + การถ่ายโอนไฟล์ + + + Directories + + + + Destination directory + + + + Default source directory + + + + Options + ตัวเลือก + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + การถ่ายโอนไฟล์ + + + Options + ตัวเลือก + + + Transfer only + ถ่ายโอนไฟล์เท่านั้น + + + Transfer and open file(s) with associated program + ถ่ายโอนและเปิดไฟล์ด้วยโปรแกรมที่เกี่ยวข้อง + + + Transfer and open destination folder + ถ่ายโอนและเปิดโฟลเดอร์ที่เก็บปลายทาง + + + Files + ไฟล์ + + + Start + เริ่ม + + + Overwrite existing files + บันทึกทับไฟล์ที่มีอยู่แล้ว + + + + FileTransferPlugin + + File transfer + การถ่ายโอนไฟล์ + + + Click this button to transfer files from your computer to all computers. + คลิกที่ปุ่มนี้เพื่อถ่ายโอนไฟล์ในคอมพิวเตอร์ของคุณไปยังคอมพิวเตอร์ทุกเครื่อง + + + Select one or more files to transfer + เลือกไฟล์อย่างน้อยหนึ่งไฟล์เพื่อนำไปถ่ายโอน + + + Transfer files to remote computer + ส่งไฟล์ไปยังคอมพิวเตอร์ระยะไกล + + + Received file "%1". + ได้รับไฟล์ "%1" + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + ส่วนติดต่อผู้ใช้ + + + Language: + ภาษา: + + + Use system language setting + ใช้การตั้งค่าภาษาตามระบบ + + + Veyon + Veyon + + + Logging + + + + Log file directory + + + + Log level + + + + Nothing + ไม่มี + + + Only critical messages + + + + Errors and critical messages + + + + Warnings and errors + + + + Information, warnings and errors + + + + Debug messages and everything else + + + + Limit log file size + + + + Clear all log files + + + + Log to standard error output + + + + Network object directory + + + + Backend: + + + + Update interval: + + + + %1 service + + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + + + + All log files were cleared successfully. + + + + Error + + + + Could not remove all log files. + + + + MB + MB + + + Rotate log files + + + + x + x + + + seconds + วินาที + + + Write to logging system of operating system + + + + Authentication + การยืนยันตัวตน + + + Method: + วิธีการ: + + + Logon authentication + การยืนยันตัวตนด้วยการลงชื่อเข้าใช้ + + + Key file authentication + การยืนยันตัวตนด้วยไฟล์กุญแจ + + + Test + ทดสอบ + + + Authentication is set up properly on this computer. + การยืนยันตัวตนถูกติดตั้งบนคอมพิวเตอร์ได้อย่างถูกต้องแล้ว + + + Authentication keys are not set up properly on this computer. + การยืนยันตัวตนยังไม่ได้รับถูกติดตั้งบนคอมพิวเตอร์อย่างถูกต้อง + + + Authentication test + การทดสอบการยืนยันตัวตน + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + + + + + LdapConfigurationPage + + Basic settings + การตั้งค่าพื้นฐาน + + + General + ทั่วไป + + + LDAP server and port + + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + เช่น dc=example,dc=org + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + เช่น OU=Users + + + e.g. OU=Computers + เช่น OU=Computers + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + การตั้งค่าขั้นสูง + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + + LDAP base DN test failed + การทดสอบ LDAP base DN ล้มเหลว + + + LDAP base DN test successful + การทดสอบ LDAP base DN สำเร็จลุล่วง + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + ใส่ชื่อผู้ใช้ + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + + + + Please enter a group name whose members to query: + + + + group members + สมาชิกกลุ่ม + + + Group not found + ไม่พบกลุ่ม + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + + + + users + + + + user groups + + + + computer groups + + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + ไม่พบผู้ใช้ + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + + + + LDAP %1 test failed + การทดสอบ LDAP %1 ล้มเหลว + + + LDAP %1 test successful + การทดสอบ LDAP %1 สำเร็จลุล่วง + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + + + + TLS certificate verification + + + + System defaults + + + + Never (insecure!) + ไม่เลย (ไม่ปลอดภัย!) + + + Custom CA certificate file + + + + None + ไม่มี + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + เช่น (objectClass=computer) + + + e.g. (objectClass=group) + เช่น (objectClass=group) + + + e.g. (objectClass=person) + เช่น (objectClass=person) + + + e.g. (objectClass=room) or (objectClass=computerLab) + เช่น (objectClass=room) หรือ (objectClass=computerLab) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + เช่น (objectClass=container) หรือ (objectClass=organizationalUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + แสดงรายชื่อสถานที่ทั้งหมด + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + กรุณากรอกชื่อของสถานที่ของคอมพิวเตอร์ (อนุญาตใช้อักขระแทนได้ เช่น ดอกจัน): + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + การทดสอบ LDAP ล้มเหลว + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + การทดสอบ LDAP สำเร็จลุล่วง + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + ทดสอบ + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + การยืนยันตัวตนผู้ใช้ + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + เลือกสถานที่ + + + enter search filter... + + + + + MainToolBar + + Configuration + + + + Disable balloon tooltips + + + + Show icons only + แสดงไอคอนเท่านั้น + + + + MainWindow + + MainWindow + + + + toolBar + + + + General + ทั่วไป + + + &File + ไ&ฟล์ + + + &Help + วิ&ธีใช้ + + + &Quit + &ออก + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + โ&หลดการตั้งค่าจากไฟล์ + + + Ctrl+O + Ctrl+O + + + About Qt + เกี่ยวกับ Qt + + + Authentication impossible + + + + Configuration not writable + + + + Load settings from file + โหลดการตั้งค่าจากไฟล์ + + + Save settings to file + บันทึกการตั้งค่าลงในไฟล์ + + + Unsaved settings + + + + There are unsaved settings. Quit anyway? + + + + Veyon Configurator + Veyon Configurator + + + Service + + + + Master + + + + Access control + + + + About Veyon + เกี่ยวกับ Veyon + + + Auto + + + + About + เกี่ยวกับ + + + %1 Configurator %2 + + + + JSON files (*.json) + ไฟล์ JSON (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + การเข้าถึงถูกปฏิเสธ + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + ภาพถ่ายหน้าจอ + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + ฟีเจอร์ "%1" ยังถูกในระหว่างการใช้งานอยู่ กรุณาหยุดการใช้งานก่อนปิด %2 + + + Reset configuration + + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + + + + Align computers to grid + + + + %1 Configurator + + + + Insufficient privileges + สิทธิไม่เพียงพอ + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + แสดงเฉพาะคอมพิวเตอร์ที่เปิดอยู่เท่านั้น + + + &Save settings to file + + + + &View + &มุมมอง + + + &Standard + &มาตรฐาน + + + &Advanced + &ขั้นสูง + + + Use custom computer arrangement + ใช้การจัดเรียงคอมพิวเตอร์แบบกำหนดเอง + + + Locations && computers + สถานที่ && คอมพิวเตอร์ + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + + + + User configuration + + + + Feature on computer double click: + + + + Features + + + + All features + + + + Disabled features + + + + Screenshots + ภาพถ่ายหน้าจอ + + + <no feature> + <no feature> + + + Basic settings + การตั้งค่าพื้นฐาน + + + Behaviour + + + + Enforce selected mode for client computers + + + + Hide local computer + ซ่อนคอมพิวเตอร์ท้องที่ + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + การกระทำอย่างเช่นการสั่งบู๊ตเครื่องใหม่ หรือการสั่งปิดคอมพิวเตอร์ + + + User interface + ส่วนติดต่อผู้ใช้ + + + Background color + สีพื้นหลัง + + + Thumbnail update interval + + + + ms + ms + + + Program start + + + + Modes and features + + + + User and computer name + ผู้ใช้และชื่อคอมพิวเตอร์ + + + Only user name + เฉพาะชื่อผู้ใช้ + + + Only computer name + เฉพาะชื่อคอมพิวเตอร์ + + + Computer thumbnail caption + + + + Text color + สีของข้อความ + + + Sort order + การจัดเรียงลำดับ + + + Computer and user name + คอมพิวเตอร์และชื่อผู้ใช้ + + + Computer locations + สถานที่ของคอมพิวเตอร์ + + + Show current location only + แสดงสถานที่ปัจจุบันเท่านั้น + + + Allow adding hidden locations manually + + + + Hide empty locations + ซ่อนสถานที่ว่างเปล่า + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + สังเกตการณ์ + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + สถานที่/คอมพิวเตอร์ + + + + OpenWebsiteDialog + + Open website + เปิดเว็บไซต์ + + + e.g. Veyon + เช่น Veyon + + + Remember and add to website menu + จำไว้และเพิ่มไปยังเมนูเว็บไซต์ + + + e.g. www.veyon.io + เช่น www.veyon.io + + + Please enter the URL of the website to open: + กรุณากรอก URL ของเว็บไซต์ที่ต้องการเปิด: + + + Name: + ชื่อ: + + + + PasswordDialog + + Username + ชื่อผู้ใช้ + + + Password + รหัสผ่าน + + + Veyon Logon + + + + Authentication error + การยืนยันตัวตนผิดพลาด + + + Logon failed with given username and password. Please try again! + + + + Please enter your username and password in order to access computers. + + + + + PowerControlFeaturePlugin + + Power on + เปิดเครื่อง + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + คลิกที่นี่เพื่อเปิดเครื่องคอมพิวเตอร์ทั้งหมด วิธีนี้จะทำให้คุณไม่ต้องเดินไปเปิดเครื่องทีละเครื่อง + + + Reboot + รีบู๊ต + + + Click this button to reboot all computers. + คลิกปุ่มนี้ เพื่อทำการรีบู๊ตคอมพิวเตอร์ทุกเครื่อง + + + Power down + ปิดเครื่อง + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + คลิกที่ปุ่มนี้เพื่อทำการสั่งปิดเครื่องคอมพิวเตอร์ทั้งหมด วิธีนี้จะทำให้คุณไม่จำเป็นต้องเดินไปปิดคอมพิวเตอร์ด้วยตัวเองทีละเครื่อง + + + Power on/down or reboot a computer + เปิด/ปิดเครื่อง หรือรีสตาร์ทคอมพิวเตอร์ใหม่ + + + Confirm reboot + ยืนยันการรีบู๊ต + + + Confirm power down + ยืนยันการปิดเครื่อง + + + Do you really want to reboot the selected computers? + + + + Do you really want to power down the selected computer? + คุณต้องการที่จะสั่งปิดเครื่องคอมพิวเตอร์ที่เลือกไว้หรือไม่? + + + Power on a computer via Wake-on-LAN (WOL) + เปิดเครื่องคอมพิวเตอร์โดยใช้ Wake-on-LAN (WOL) + + + MAC ADDRESS + + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + ปิดเครื่องตอนนี้ + + + Install updates and power down + ติดตั้งอัปเดตแล้วปิดเครื่อง + + + Power down after user confirmation + ปิดเครื่องหลังจากการยืนยันของผู้ใช้ + + + Power down after timeout + ปิดเครื่องหลังจากหมดเวลา + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + คอมพิวเตอร์จะปิดลงใน %1 นาที %2 วินาที +กรุณาบันทึกงานให้เรียบร้อยแล้วปิดโปรแกรมทุกโปรแกรม + + + + PowerDownTimeInputDialog + + Power down + ปิดเครื่อง + + + Please specify a timeout for powering down the selected computers: + + + + minutes + นาที + + + seconds + วินาที + + + + RemoteAccessFeaturePlugin + + Remote view + มองระยะไกล + + + Open a remote view for a computer without interaction. + เปิดการดูจากระยะไกลโดยไม่ต้องมีการควบคุมอะไร + + + Remote control + ควบคุมระยะไกล + + + Open a remote control window for a computer. + + + + Remote access + การเข้าถึงระยะไกล + + + Remote view or control a computer + ดูจากระยะไกลหรือเข้าควบคุมคอมพิวเตอร์ + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + ดูเท่านั้น + + + Remote control + ควบคุมระยะไกล + + + Send shortcut + ส่งคีย์ลัด + + + Fullscreen + เต็มหน้าจอ + + + Window + หน้าต่าง + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + เมนู + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + กำลังเชื่อมต่อ %1 + + + Connected. + เชื่อมต่อแล้ว + + + Screenshot + ภาพหน้าจอ + + + Exit + ออก + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + สั่งเปิดโปรแกรม + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + เช่น "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + ชื่อ: + + + Remember and add to program menu + จำไว้และเพิ่มไปยังเมนูโปรแกรม + + + e.g. VLC + เช่น VLC + + + + ScreenLockFeaturePlugin + + Lock + ล็อก + + + Unlock + ปลดล็อก + + + Lock screen and input devices of a computer + ล็อกหน้าจอและอุปกรณ์นำเข้าของคอมพิวเตอร์ + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + เพื่อที่จะดึงความสนใจจากผู้ใช้ทั้งหมดให้กลับมา คุณสามารถล็อกคอมพิวเตอร์ของเขาโดยใช้ปุ่มนี้ ในโหมดนี้ อุปกรณ์ประเภทนำเข้าข้อมูล เช่น เมาส์ คีย์บอร์ด จะถูกล็อกและหน้าจอจะดับลง + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + ไม่รู้จัก + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + ภาพหน้าจอ + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + ภาพหน้าจอ + + + Use this function to take a screenshot of selected computers. + ใช้ฟังก์ชั่นนี้เพื่อเก็บภาพหน้าจอจากคอมพิวเตอร์ที่เลือกไว้ + + + Screenshots taken + ถ่ายภาพหน้าจอแล้ว + + + Screenshot of %1 computer have been taken successfully. + + + + Take screenshots of computers and save them locally. + + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + ผู้ใช้: + + + Computer: + คอมพิวเตอร์: + + + Date: + วันที่: + + + Time: + เวลา: + + + Show + แสดง + + + Delete + ลบ + + + Screenshot + ภาพหน้าจอ + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + ทั่วไป + + + Autostart + เริ่มอัตโนมัติ + + + Hide tray icon + + + + Start service + เริ่ม service + + + Stopped + หยุดแล้ว + + + Stop service + หยุด service + + + State: + + + + Enable firewall exception + เปิดใช้ข้อยกเว้นของไฟร์วอลล์ + + + Allow connections from localhost only + อนุญาตการเชื่อมต่อจาก localhost เท่านั้น + + + VNC server + เซิฟเวอร์ VNC + + + Plugin: + ปลั๊กอิน: + + + Restart %1 Service + เริ่มต้นเซอร์วิส %1 ใหม่ + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + + Running + กำลังทำงาน + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + แสดงการแจ้งเตือนเมื่อมีการเชื่อมต่อจากระยะไกล + + + Show notification when an unauthorized access is blocked + แสดงการแจ้งเตือนเมื่อการเข้าถึงที่ไม่ได้รับอนุญาตถูกบล็อก + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + เซิฟเวอร์สาธิต + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + กำลังเริ่มเซอร์วิส %1 + + + Stopping service %1 + กำลังหยุดเซอร์วิส %1 + + + Registering service %1 + กำลังลงทะเบียนเซอร์วิส %1 + + + Unregistering service %1 + กำลังถอนการลงทะเบียนเซอร์วิส %1 + + + Service control + การควบคุมเซอร์วิส + + + + ServiceControlPlugin + + Service is running + เซอร์วิสกำลังทำงาน + + + Service is not running + เซอร์วิสไม่ได้ทำงาน + + + Configure and control Veyon service + ตั้งค่าและควบคุม Veyon Service + + + Register Veyon Service + ลงทะเบียน Veyon Service + + + Unregister Veyon Service + ถอนการลงทะเบียน Veyon Service + + + Start Veyon Service + เริ่ม Veyon Service + + + Stop Veyon Service + หยุด Veyon Service + + + Restart Veyon Service + เริ่ม Veyon Service ใหม่ + + + Query status of Veyon Service + + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + ไฟล์ "%1" ไม่มีอยู่! + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + ส่งข้อความอักษระ + + + Use the field below to type your message which will be sent to all selected users. + + + + + TextMessageFeaturePlugin + + Text message + ข้อความอักษระ + + + Use this function to send a text message to all users e.g. to assign them new tasks. + ใช้ฟังก์ชั่นนี้เพื่อส่งข้อความอักษระให้ผู้ใช้ทุกคน เช่น การสั่งงานใหม่ เป็นต้น + + + Message from teacher + ข้อความจากครูผู้สอน + + + Send a message to a user + ส่งข้อความไปยังผู้ใช้ + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + + + + Poll full screen (leave this enabled per default) + + + + Low accuracy (turbo mode) + ความแม่นยำต่ำ (โหมดเทอร์โบ) + + + Builtin UltraVNC server configuration + การตั้งค่าเซิฟเวอร์ UltraVNC ในตัว + + + Enable multi monitor support + เปิดใช้การสนับสนุนการแสดงหลายหน้าจอ + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + ชื่อผู้ใช้ + + + Password + รหัสผ่าน + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + ล็อกออฟ + + + Click this button to log off users from all computers. + คลิกปุ่มนี้เพื่อล็อกออฟผู้ใช้จากคอมพิวเตอร์ทุกเครื่อง + + + Confirm user logoff + ยืนยันการล็อกออฟผู้ใช้ + + + Do you really want to log off the selected users? + คุณต้องการล็อกออฟผู้ใช้ที่เลือกไว้หรือไม่ + + + User session control + + + + + VeyonCore + + [OK] + [เรียบร้อย] + + + [FAIL] + [ล้มเหลว] + + + Invalid command! + คำสั่งไม่ถูกต้อง! + + + Available commands: + + + + Invalid arguments given + + + + Not enough arguments given - use "%1 help" for more information + + + + Unknown result! + ผลลัพธ์ที่ไม่รู้จัก! + + + Available modules: + + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + + + + INFO + + + + ERROR + + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + Veyon Service + + + + VncViewWidget + + Establishing connection to %1 ... + กำลังสร้างการเชื่อมต่อไปยัง %1 ... + + + + WebApiConfigurationPage + + Web API + + + + General + ทั่วไป + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + วิ + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + ทั่วไป + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + ล็อกจอ + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + ซ่อนเดสก์ท็อป + + + User authentication + การยืนยันตัวตนผู้ใช้ + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + + + + + WindowsServiceControl + + The service "%1" is already installed. + เซอร์วิส "%1" ถูกติดตั้งอยู่ก่อนแล้ว + + + The service "%1" could not be installed. + เซอร์วิส "%1" ไม่สามารถทำการติดตั้งได้ + + + The service "%1" has been installed successfully. + เซอร์วิส "%1" ได้รับการติดตั้งอย่างเสร็จสมบูรณ์แล้ว + + + The service "%1" could not be uninstalled. + เซอร์วิส "%1" ไม่สามารถทำการยกเลิกการติดตั้งได้ + + + The service "%1" has been uninstalled successfully. + เซอร์วิส "%1" ได้รับการยกเลิกการติดตั้งอย่างเสร็จสมบูรณ์แล้ว + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + + + + Custom x11vnc parameters: + + + + Do not use X Damage extension + ห้ามใช่ส่วนเสริม X Damage เป็นอันขาด + + + diff --git a/translations/veyon_tr.ts b/translations/veyon_tr.ts new file mode 100644 index 0000000..c9eb962 --- /dev/null +++ b/translations/veyon_tr.ts @@ -0,0 +1,4075 @@ + + + + + AboutDialog + + About + Hakkında + + + Translation + Çeviri + + + License + Lisans + + + About Veyon + Veyon Hakkında + + + Contributors + Katkıda Bulunanlar + + + Version: + Sürüm: + + + Website: + Web Sitesi: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Geçerli dilde henüz bir çeviri yapılmamış (veya anadili İngilizce) . + +Veyon'u kendi dilinizde veya başka bir dile çevirmek istiyorsanız veya var olan bir çeviriyi geliştirmek istiyorsanız, lütfen bir Veyon geliştiricisine başvurun! + + + About %1 %2 + %1 %2 Hakkında + + + Support Veyon project with a donation + Bağış ile Veyon projesini destekle + + + + AccessControlPage + + Computer access control + Bilgisayarlara erişim denetimi + + + Grant access to every authenticated user (default) + Yetkilendirilmiş her kullanıcıya erişim ver (öntamılı) + + + Test + Sına + + + Process access control rules + İşlem erişim denetim kuralları + + + User groups authorized for computer access + Bilgisayar erişimi için yetkili kullanıcı kümeleri + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Veyon ağınızdaki bilgisayarlara erişmeye yetkili olacak kullanıcıları içeren kümeleri ekleyin. + + + Authorized user groups + Yetkili kullanıcı kümeleri + + + All groups + Tüm kümeler + + + Access control rules + Erişim denetim kuralları + + + Add access control rule + Erişim denetim kuralı ekle + + + Remove access control rule + Erişim denetim kuralını sil + + + Move selected rule down + Seçilen rolü aşağı taşı + + + Move selected rule up + Seçilen rolü yukarı taşı + + + Edit selected rule + Seçilen rolü düzenle + + + Enter username + Kullanıcı adı gir + + + Please enter a user login name whose access permissions to test: + Sınamak için izinlere erişecek bir kullanıcı giriş adı girin: + + + Access allowed + Erişime izin verildi + + + The specified user is allowed to access computers with this configuration. + Bu yapılandırmayla, belirtilen kullanıcının bilgisayarlara erişmesine izin verilmiştir. + + + Access denied + Erişim reddedildi + + + The specified user is not allowed to access computers with this configuration. + Bu yapılandırmayla, belirtilen kullanıcının bilgisayarlara erişmesine izin verilmemiştir. + + + Enable usage of domain groups + Alan adı kümelerinin kullanımını etkinleştir + + + User groups backend: + Kullanıcı kümeleri arka ucu: + + + Missing user groups backend + Eksik kullanıcı kümeleri arka ucu + + + No default user groups plugin was found. Please check your installation! + Öntanımlı kullanıcı kümeleri eklentisi bulunamadı. Lütfen kurulumunuzu gözden geçirin! + + + Restrict access to members of specific user groups + Giriş yetkisini sadece belirli kullanıcı grup üyeleriyle sınırla + + + + AccessControlRuleEditDialog + + Edit access control rule + Erişim denetimi kuralını düzenle + + + General + Genel + + + enter a short name for the rule here + burada kural için kısa bir ad girin + + + Rule name: + Kural adı: + + + enter a description for the rule here + burada kural için kısa bir açıklama girin + + + Rule description: + Kural açıklaması: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Tüm koşulları tersine çevir ("is/has" [-dır], "is/has not" [...değildir] olarak yorumlanır) + + + Conditions + Koşullar + + + is member of group + kümenin üyesidir + + + Accessing computer is localhost + Giriş yapan bilgisayar yerel sunucudur + + + Accessing user is logged on user + Erişilen kullanıcı oturumu açmış + + + Accessing user is already connected + Erişilen kullanıcı zaten bağlı + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Eğer birden çok koşul etkinse, kuralın uygulanması için her koşulun yerine getirilmesi gerekiyorsa (mantıksal VE). Eğer birden çok koşulun yalnızca birinin yerine getirilmesi gerekiyorsa (mantıksal VEYA) lütfen çoğul erişim denetimi kuralları oluşturun. + + + Action + Eylem + + + Allow access + Erişime izin ver + + + Deny access + Erişimi reddet + + + Ask logged on user for permission + Oturum açan kullanıcıdan izin al + + + None (rule disabled) + Yok (kural devre dışı) + + + Accessing user + Kullanıcıya erişim + + + Accessing computer + Bilgisayara erişim + + + Local (logged on) user + Yerel (oturum açmış) kullanıcı + + + Local computer + Yerel bilgisayar + + + Always process rule and ignore conditions + Her zaman kuralı işle ve koşulları yok say + + + No user logged on + Oturum açan kullanıcı yok + + + Accessing user has one or more groups in common with local (logged on) user + Erişilen kullanıcı, yerel (oturum açmış) kullanıcıyla ortak bir veya daha çok kümeye sahip + + + Accessing computer and local computer are at the same location + Giriş yapan bilgisayar ve yerel bilgisayar aynı lokasyonda + + + is located at + bulunduğu yer + + + + AccessControlRulesTestDialog + + Access control rules test + Erişim denetim kuralları + + + Accessing user: + Kullanıcıya erişim: + + + Local computer: + Yerel bilgisayar: + + + Accessing computer: + Bilgisayara erişim: + + + Please enter the following user and computer information in order to test the configured ruleset. + Lütfen, yapılandırılmış kural setini sınamak için takip eden kullanıcı ve bilgisayar bilgisini girin. + + + Local user: + Yerel kullanıcı: + + + Connected users: + Bağlı kullanıcılar: + + + The access in the given scenario is allowed. + Verilen senaryodaki erişime izin verildi. + + + The access in the given scenario is denied. + Verilen senaryodaki erişim reddedildi. + + + The access in the given scenario needs permission of the logged on user. + Verilen senaryodaki erişim, oturum açmış kullanıcının iznini gerektiriyor. + + + ERROR: Unknown action + HATA: Bilinmeyen eylem + + + Test result + Sınama sonuçları + + + + AuthKeysConfigurationPage + + Authentication keys + Yetkilendirme anahtarları + + + Introduction + Giriş + + + Key file directories + Anahtar dosyası dizinleri + + + Public key file base directory + Genel anahtar dizin yolu + + + Private key file base directory + Özel anahtar dizin yolu + + + Available authentication keys + Kullanılabilir yetkilendirme anahtarları + + + Create key pair + Anahtar çifti oluştur + + + Delete key + Anahtarı sil + + + Import key + Anahtarı içe aktar + + + Export key + Anahtarı dışa aktar + + + Set access group + Erişim grubunu ayarla + + + Key files (*.pem) + Anahtar dosyaları (*.pem) + + + Authentication key name + Yetkilendirme anahtarı adı + + + Please enter the name of the user group or role for which to create an authentication key pair: + Lütfen yetkilendirme anahtarı çifti oluşturulacak kullanıcı kümesinin veya rolün adını girin: + + + Do you really want to delete authentication key "%1/%2"? + "%1/%2" yetkilendirme anahtarını silmek istediğinize emin misiniz? + + + Please select a key to delete! + Lütfen silinecek anahtar seçin! + + + Please enter the name of the user group or role for which to import the authentication key: + Lütfen içe aktarılacak yetkilendirme anahtarı için kullanıcı kümesinin veya rolün adını girin: + + + Please select a key to export! + Lütfen dışa aktarılacak anahtar seçin! + + + Please select a user group which to grant access to key "%1": + Lütfen "%1" anahtarına erişim izni verilecek bir kullanıcı grubu seçin: + + + Please select a key which to set the access group for! + Lütfen erişim grubunu ayarlamak için bir anahtar seçin! + + + Please perform the following steps to set up key file authentication: + Anahtar dosyası yetkilendirmesi için lütfen bu adımları gerçekleştirin: + + + 1) Create a key pair on the master computer. + 1) Ana bilgisayarda anahtar çifti oluşturun. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Diğer bilgisayarlara giriş yetkisi olan bir giriş grubu oluşturun. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Genel anahtarı dışarı verin ve tüm katılımcı bilgisayarlara aynı isimle ekleyin. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Lütfen <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Yönetici Kılavuzuna </a> daha fazla bilgi için göz atın. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Bir kimlik doğrulama anahtarı çifti, özel ve genel anahtar olmak üzere iki bağlantılı şifreleme anahtarından oluşur. +Özel anahtar, ana bilgisayardaki kullanıcıların istemci bilgisayarlara erişmesine olanak tanır. +Özel anahtar dosyasına yalnızca yetkili kullanıcıların okuma erişimi olması önemlidir. +Genel anahtar, istemci bilgisayarlarda gelen bağlantı isteğinin kimliğini doğrulamak için kullanılır. + + + + AuthKeysManager + + Please check your permissions. + Lütfen izinlerinizi gözden geçirin. + + + Key name contains invalid characters! + Anahtar adı geçersiz karakterler içeriyor! + + + Invalid key type specified! Please specify "%1" or "%2". + Geçersiz anahtar türü belirtildi! Lütfen "%1" veya "%2" belirtin. + + + Specified key does not exist! Please use the "list" command to list all installed keys. + Belirtilen anahtar yok! Lütfen tüm kurulu anahtarları listelemek için "list" komutunu kullanın. + + + One or more key files already exist! Please delete them using the "delete" command. + Bir veya daha çok anahtar zaten var! Lütfen onları silmek için "delete" komutunu kullanın. + + + Creating new key pair for "%1" + "%1" için yeni anahtar çifti oluşturuluyor + + + Failed to create public or private key! + Genel veya özel anahtar oluşturulamadı! + + + Newly created key pair has been saved to "%1" and "%2". + Yeni oluşturulan anahtar çifti "%1" ve "%2" konumuna kaydedildi. + + + Could not remove key file "%1"! + "%1" anahtar dosyası kaldırılamadı! + + + Could not remove key file directory "%1"! + "%1" anahtar dosyası dizini kaldırılamadı! + + + Failed to create directory for output file. + Çıktı dosyası için dizin oluşturulamadı. + + + File "%1" already exists. + "%1" dosyası zaten var. + + + Failed to write output file. + Çıktı dosyasına yazılamadı. + + + Key "%1/%2" has been exported to "%3" successfully. + "%1/%2" anahtarı başarıyla "%3" konumuna aktarıldı. + + + Failed read input file. + Girdi dosyası okunamadı. + + + File "%1" does not contain a valid private key! + "%1" dosyası geçerli özel anahtar içermiyor! + + + File "%1" does not contain a valid public key! + "%1" dosyası geçerli genel anahtar içermiyor! + + + Failed to create directory for key file. + Anahtar dosyası için dizin oluşturulamadı. + + + Failed to write key file "%1". + "%1" anahtar dosyasına yazılamadı. + + + Failed to set permissions for key file "%1"! + "%1" anahtar dosyası için izinler ayarlanamadı! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + "%1/%2" anahtarı başarıyla içe aktarıldı. Lütfen yetkisiz erişimleri önlemek için "%3" dosyasının izinlerini gözden geçirin. + + + Failed to convert private key to public key + Özel anahtar genel anahtara dönüştürülemedi + + + Failed to create directory for private key file "%1". + "%1" özel anahtar dosyası için dizin oluşturulamadı. + + + Failed to save private key in file "%1"! + Özel anahtar "%1" dosyasına kaydedilemedi! + + + Failed to set permissions for private key file "%1"! + "%1" özel anahtar dosyası için izinler ayarlanamadı! + + + Failed to create directory for public key file "%1". + "%1" genel anahtar dosyası için dizin oluşturulamadı. + + + Failed to save public key in file "%1"! + Genel anahtar "%1" dosyasına kaydedilemedi! + + + Failed to set permissions for public key file "%1"! + "%1" genel anahtar dosyası için izinler ayarlanamadı! + + + Failed to set owner of key file "%1" to "%2". + Anahtar dosyasının sahibi "%1" iken "%2" yapılamadı. + + + Failed to set permissions for key file "%1". + "%1" anahtar dosyası için izinler ayarlanamadı. + + + Key "%1" is now accessible by user group "%2". + "%1" anahtarı artık "%2" kullanıcı kümesi tarafından erişilebilir. + + + <N/A> + <N/A> + + + Failed to read key file. + Anahtar dosyası okunamadı. + + + + AuthKeysPlugin + + Create new authentication key pair + Yeni yetkilendirme anahtar çifti oluştur + + + Delete authentication key + Yetkilendirme anahtarını sil + + + List authentication keys + Yetkilendirme anahtarlarını listele + + + Import public or private key + Genel veya özel anahtar içe aktar + + + Export public or private key + Genel veya özel anahtar dışa aktar + + + Extract public key from existing private key + Var olan özel anahtardan genel anahtar elde et + + + Set user group allowed to access a key + Bir anahtara erişmesine izin verilen kullanıcı grubunu ayarla + + + KEY + ANAHTAR + + + ACCESS GROUP + ERİŞİM GRUBU + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + Bu komut, dosya erişim izinlerini yalnızca <ACCESS GROUP> kullanıcı grubunun okuma erişimine sahip olacağı şekilde <KEY> olarak ayarlar. + + + NAME + İSİM + + + FILE + DOSYA + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Bu komut, <KEY> kimlik doğrulama anahtarını <FILE> öğesine dışa aktarır. <FILE> belirtilmezse, <KEY> adından ve türünden bir ad oluşturulur. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Bu komut, <FILE> öğesinden <KEY> kimlik doğrulama anahtarını alır. <FILE> belirtilmezse, <KEY> adından ve türünden bir ad oluşturulur. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + Bu komut, yapılandırılmış anahtar dizinindeki tüm kullanılabilir kimlik doğrulama anahtarlarını listeler. "%1" seçeneği belirtilirse, bunun yerine anahtar ayrıntılarını içeren bir tablo görüntülenir. Bir anahtara erişilemiyorsa bazı ayrıntılar eksik olabilir; okuma izinlerinin olmaması nedeniyle. + + + Please specify the command to display help for! + Lütfen yardım görüntüleme komutunu belirtin! + + + TYPE + TİP + + + PAIR ID + ÇİFT ID + + + Command line support for managing authentication keys + + + + Commands for managing authentication keys + Kimlik doğrulama anahtarlarını yönetme komutları + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + Bu komut, <NAME> adında yeni bir kimlik doğrulama anahtarı çifti oluşturur ve özel ve genel anahtarı yapılandırılmış anahtar dizinlerine kaydeder. Parametre, anahtar için yalnızca harf içerebilecek bir ad olmalıdır. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + Bu komut, <KEY> kimlik doğrulama anahtarını yapılandırılmış anahtar dizininden siler. Bir anahtar silindikten sonra kurtarılamayacağını lütfen unutmayın. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + Bu komut, ortak anahtar bölümünü <KEY> özel anahtarından çıkarır ve karşılık gelen ortak anahtar olarak kaydeder. Başka bir ana bilgisayar kurarken, yalnızca özel anahtarı aktarmak yeterlidir. Genel anahtar daha sonra çıkarılabilir. + + + + AuthKeysTableModel + + Name + Ad + + + Type + Tür + + + Access group + Erişim grubu + + + Pair ID + Çift ID + + + + BuiltinDirectoryConfigurationPage + + Computers + Bilgisayarlar + + + Name + Ad + + + Host address/IP + Ana makine adresi/IP + + + MAC address + MAC adresi + + + Add new computer + Yeni bilgisayar ekle + + + Remove selected computer + Seçilen bilgisayarı kaldır + + + New computer + Yeni bilgisayar + + + Builtin directory + Yerleşik dizin + + + Locations & computers + Konumlar ve bilgisayarlar + + + Locations + Konumlar + + + Add new location + Yeni konum ekle + + + Remove selected location + Seçilen konumu sil + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + CSV dosyalarının içe aktarılması, komut satırı arayüzü üzerinden mümkündür. Daha fazla bilgi için <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory"> çevrimiçi belgelere </a> bakın. + + + New location + Yeni konum + + + + BuiltinDirectoryPlugin + + Show help for specific command + Komutlar için yardım göster + + + Import objects from given file + Verilen dosyadan nesne içe aktarma + + + Export objects to given file + Nesneleri verilen dosyaya aktarma + + + Invalid type specified. Valid values are "%1" or "%2". + Geçersiz tür belirtildi. Geçerli değerler "%1" veya "%2" dir. + + + Type + Tür + + + Name + Ad + + + Host address + Host adresi + + + MAC address + MAC adresi + + + Specified object not found. + Belirtilen nesne bulunamadı. + + + File "%1" does not exist! + "%1" dosyası mevcut değil! + + + Can't open file "%1" for reading! + "%1" dosyası okumak için açılamıyor! + + + Unknown argument "%1". + Bilinmeyen argüman "%1". + + + Computer "%1" (host address: "%2" MAC address: "%3") + Bilgisayar "%1" (ana bilgisayar adresi: "%2" MAC adresi: "%3") + + + Unclassified object "%1" with ID "%2" + "%2" kimliğine sahip sınıflandırılmamış nesne "%1" + + + None + Yok + + + Computer + Bilgisayar + + + Root + Kök + + + Invalid + Geçersiz + + + Error while parsing line %1. + %1 satırı ayrıştırılırken hata oluştu. + + + Network object directory which stores objects in local configuration + Nesneleri yerel yapılandırmada depolayan ağ nesnesi dizini + + + Commands for managing the builtin network object directory + Yerleşik ağ nesnesi dizinini yönetme komutları + + + No format string or regular expression specified! + Biçim dizesi veya normal ifade belirtilmedi! + + + Can't open file "%1" for writing! + "%1" dosyası yazmak için açılamıyor! + + + No format string specified! + Biçim dizesi belirtilmedi! + + + Object UUID + Nesne UUID + + + Parent UUID + Üst UUID + + + Add a location or computer + Konum veya bilgisayar ekleyin + + + Clear all locations and computers + Tüm konumları ve bilgisayarları temizle + + + Dump all or individual locations and computers + Tüm veya tek tek konumları ve bilgisayarları boşalt + + + List all locations and computers + Tüm yerleri ve bilgisayarları listele + + + Remove a location or computer + Bir konumu veya bilgisayarı kaldır + + + Location "%1" + Konum "%1" + + + Builtin (computers and locations in local configuration) + Yerleşik (yerel yapılandırmadaki bilgisayarlar ve konumlar) + + + Location + Konum + + + FILE + DOSYA + + + LOCATION + KONUM + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Verilen biçim dizesini veya bir veya birden çok yer tutucu içeren normal ifadeyi kullanarak nesneleri belirtilen metin dosyasından içe aktarır. Geçerli yer tutucular:%1 + + + Import simple CSV file to a single room + Basit CSV dosyasını tek bir odaya aktar + + + Import CSV file with location name in first column + İlk sütunda konum adına sahip CSV dosyasını içe aktar + + + Import text file with with key/value pairs using regular expressions + Normal ifadeleri kullanarak anahtar/değer çiftleriyle metin dosyasını içe aktar + + + Import arbitrarily formatted data + Rasgele biçimlendirilmiş verileri içe aktar + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Bir veya daha fazla yer tutucu içeren verilen biçim dizesini kullanarak nesneleri belirtilen metin dosyasına aktarır. Geçerli yer tutucular: %1 + + + Export all objects to a CSV file + Tüm nesneleri CSV dosyasına aktar + + + Export all computers in a specific location to a CSV file + Belirli bir konumdaki tüm bilgisayarları bir CSV dosyasına aktar + + + TYPE + TİP + + + NAME + İSİM + + + PARENT + VELİ + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + %1 öğesinin "%2" veya "%3" öğelerinden biri olabileceği bir nesne ekler. %4, ad veya UUID ile belirtilebilir. + + + Add a room + Bir oda ekle + + + Add a computer to room %1 + %1 odasına bir bilgisayar ekleyin + + + OBJECT + NESNE + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Belirtilen nesneyi dizinden kaldırır. %1, ad veya UUID ile belirtilebilir. Konum kaldırıldığında ilgili tüm bilgisayarlar da kaldırılır. + + + Remove a computer by name + Bilgisayarı ada göre kaldır + + + Remove an object by UUID + Bir nesneyi UUID'e göre kaldır + + + "Room 01" + "Oda 01" + + + "Computer 01" + "Bilgisayar 01" + + + HOST ADDRESS + HOST ADRESİ + + + MAC ADDRESS + MAC ADRESİ + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Dahili VNC sunucusu (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Dahili VNC sunucusu (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Ana makine/IP adresi: %1 + + + Active features: %1 + Etkin özellikler: %1 + + + Online and connected + Çevrim içi ve bağlı + + + Establishing connection + Bağlantı kuruluyor + + + Computer offline or switched off + Bilgisayar çevrim dışı veya kapalı + + + Authentication failed or access denied + Yetkilendirme başarısız veya erişim reddedildi + + + Disconnected + Bağlı değil + + + No user logged on + Oturum açan kullanıcı yok + + + Logged on user: %1 + Oturum açan kullanıcı: %1 + + + Location: %1 + Konum: %1 + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 Hizmet %2'de %3:%4 + + + Authentication error + Yetkilendirme hatası + + + Remote access + Uzaktan erişim + + + User "%1" at host "%2" is now accessing this computer. + "%2" ana bilgisayarındaki "%1" kullanıcısı bu bilgisayara erişiyor. + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + "%2" ana bilgisayarındaki "%1" kullanıcısı bu bilgisayara erişmeye çalıştı, ancak kimlik doğrulaması başarıyla gerçekleştirilemedi. + + + Access control error + Erişim kontrolü hatası + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + "%2" ana bilgisayarındaki "%1" kullanıcısı bu bilgisayara erişmeye çalıştı, ancak erişim denetimi ayarları nedeniyle engellendi. + + + Active connections: + Etkin bağlantılar: + + + + ComputerManager + + User + Kullanıcı + + + Missing network object directory plugin + Eksik ağ nesnesi dizini eklentisi + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Öntanımlı ağ nesnesi dizini eklentisi bulunamadı. Lütfen kurulumunuzu gözden geçirin veya %1 Yapılandırıcı aracılığıyla başka bir ağ nesnesi dizini eklentisi yapılandırın. + + + Location detection failed + Konum algılama başarısız + + + Computer name;Hostname;User + Bilgisayar adı; Ana bilgisayar adı; Kullanıcı + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + Bu bilgisayarın konumu belirlenemedi. Bu, sistem yapılandırmasında bir sorun olduğunu gösterir. Bunun yerine tüm konumlar bilgisayar seçim panelinde gösterilir. + + + + ComputerSelectPanel + + Computer search + Bilgisayar ara + + + Add location + Konum ekle + + + Save computer/user list + Bilgisayar/kullanıcı listesini kaydet + + + Select output filename + Çıktı dosya adı seç + + + CSV files (*.csv) + CSV dosyaları (*.csv) + + + File error + Dosya hatası + + + Could not write the computer and users list to %1! Please check the file access permissions. + Bilgisayar ve kullanıcı listesi %1 konumuna yazılamadı! Lütfen dosya erişim izinlerini gözden geçirin. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Lütfen içe aktarmak için var olan yapılandırma dosyasını belirtin. + + + Please specify a valid filename for the configuration export. + Lütfen yapılandırmayı dışarı aktarmak için geçerli bir ad belirtin. + + + Please specify a valid key. + Lütfen geçerli bir anahtar belirtin. + + + Specified key does not exist in current configuration! + Belirtilen anahtar geçerli yapılandırmada bulunmuyor! + + + Please specify a valid value. + Lütfen geçerli bir değer belirtin. + + + Configure Veyon at command line + Veyon'u komut satırında yapılandır + + + Output file is not writable! + Çıktı dosyası yazılabilir değil! + + + Output directory is not writable! + Çıktı dizini yazılabilir değil! + + + Configuration file is not readable! + Yapılandırma dosyası okunabilir değil! + + + Clear system-wide Veyon configuration + Sistem geneli Veyon yapılandırmasını temizle + + + List all configuration keys and values + Tüm yapılandırma anahtarları ve değerlerini listele + + + Import configuration from given file + Yapılandırmayı verilen dosyadan içe aktar + + + Export configuration to given file + Yapılandırmayı verilen dosyaya dışa aktar + + + Read and output configuration value for given key + Verilen anahtar için yapılandırma değerini oku ve çıktıla + + + Write given value to given configuration key + Verilen değeri verilen yapılandırma anahtarına yaz + + + Unset (remove) given configuration key + Verilen yapılandırma anahtarını kaldır + + + Commands for managing the configuration of Veyon + Veyon'un yapılandırmasını yönetmek için komutlar + + + Upgrade and save configuration of program and plugins + Program ve eklentilerin yapılandırmasını yükseltin ve kaydedin + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + %1 Hizmeti için kendiliğinden başlatma özelliği değiştirilemedi. + + + Could not configure the firewall configuration for the %1 Server. + %1 sunucusu için güvenlik duvarı yapılandırması yapılandırılamadı. + + + Could not configure the firewall configuration for the %1 Worker. + %1 çalışan için güvenlik duvarı yapılandırması yapılandırılamadı. + + + Configuration is not writable. Please check your permissions! + Yapılandırma yazılabilir değil. Lütfen izinlerinizi kontrol edin! + + + Could not apply platform-specific configuration settings. + Platforma özgü yapılandırma ayarları uygulanamadı. + + + + DemoClient + + %1 Demo + %1 Tanıtımı + + + + DemoConfigurationPage + + Demo server + Tanıtım sunucusu + + + Tunables + Ayarlanabilir + + + ms + ms + + + Key frame interval + Anahtar çerçeve aralığı + + + Memory limit + Bellek sınırı + + + MB + MB + + + Update interval + Güncelleme aralığı + + + s + s + + + Slow down thumbnail updates while demo is running + Demo çalışırken küçük resim güncellemelerini yavaşlatın + + + + DemoFeaturePlugin + + Stop demo + Tanıtımı durdur + + + Window demo + Pencere kipinde gösterim + + + Give a demonstration by screen broadcasting + Ekran yayını ile bir tanıtım gerçekleştir + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + Bu kipte, ekranınız tüm bilgisayarlarda bir pencerede görüntülenir. Kullanıcılar, gerektiğinde diğer pencerelere geçiş yapabilirler. + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + Masaüstü erişim iletişim penceresi + + + Confirm desktop access + Masaüstü erişimini onayla + + + Never for this session + Bu oturum için asla + + + Always for this session + Bu oturum için her zaman + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + %2 bilgisayarındaki %1 kullanıcısı masaüstünüze erişmek istiyor. İzin vermek ister misiniz? + + + + DesktopServicesConfigurationPage + + Programs & websites + Programlar ve web siteleri + + + Predefined programs + Önceden tanımlanmış programlar + + + Name + Ad + + + Path + Yol + + + Add new program + Yeni program ekle + + + Remove selected program + Seçili programı kaldır + + + Predefined websites + Önceden tanımlanmış web siteleri + + + Remove selected website + Seçilen web sitesini kaldır + + + URL + URL + + + New program + Yeni program + + + New website + Yeni web sitesi + + + + DesktopServicesFeaturePlugin + + Run program + Program çalıştır + + + Open website + Web sitesi aç + + + Click this button to open a website on all computers. + Tüm bilgisayarlarda bir web sitesi açmak için bu düğmeye tıklayın. + + + Start programs and services in user desktop + Kullanıcı masaüstünde program ve hizmetler başlat + + + Click this button to run a program on all computers. + Tüm bilgisayarlarda bir program çalıştırmak için bu düğmeye tıklayın. + + + Run program "%1" + "%1" programını çalıştır + + + Custom program + Özel program + + + Open website "%1" + Web sitesini "%1" açın + + + Custom website + Özel web sitesi + + + + DocumentationFigureCreator + + Teacher + Öğretmen + + + Room %1 + Oda %1 + + + Please complete all tasks within the next 5 minutes. + Lütfen tüm görevleri önümüzdeki 5 dakika içinde tamamlayın. + + + Custom website + Özel web sitesi + + + Open file manager + Dosya yöneticisini aç + + + Start learning tool + Öğrenme aracını başlat + + + Play tutorial video + Eğitici videoyu oynat + + + Custom program + Özel program + + + Handout + Elden çıkar + + + Texts to read + Okunacak metinler + + + generic-student-user + Jenerik-öğrenci-kullanıcı + + + + ExternalVncServer + + External VNC server + Harici VNC sunucu + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Harici VNC sunucu yapılandırması + + + Port: + Bağlantı noktası: + + + Password: + Parola: + + + + FeatureControl + + Feature control + Özellik denetimi + + + + FileTransferConfigurationPage + + File transfer + Dosya transferi + + + Directories + Dizinler + + + Destination directory + + + + Default source directory + + + + Options + Seçenekler + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + "%1" dosyası okumak için açılamadı! Lütfen izinlerinizi kontrol edin! + + + + FileTransferDialog + + File transfer + Dosya transferi + + + Options + Seçenekler + + + Transfer only + Yalnızca aktar + + + Transfer and open file(s) with associated program + İlişkili programla dosya aktarma ve açma + + + Transfer and open destination folder + Hedef klasörü aktarma ve açma + + + Files + Dosyalar + + + Start + Başlat + + + Overwrite existing files + Mevcut dosyaların üzerine yaz + + + + FileTransferPlugin + + File transfer + Dosya transferi + + + Click this button to transfer files from your computer to all computers. + Dosyaları bilgisayarınızdan tüm bilgisayarlara aktarmak için bu düğmeyi tıklatın. + + + Select one or more files to transfer + Aktarılacak bir veya daha fazla dosya seçin + + + Transfer files to remote computer + Uzak bilgisayara dosya aktarma + + + Received file "%1". + Alınan dosya "%1". + + + Could not receive file "%1" as it already exists. + Zaten var olduğundan "%1" dosyası alınamadı. + + + Could not receive file "%1" as it could not be opened for writing! + Yazılmak için açılamadığı için "%1" dosyası alınamadı! + + + + GeneralConfigurationPage + + User interface + Kullanıcı arayüzü + + + Language: + Dil: + + + Use system language setting + Sistem dil ayarını kullan + + + Veyon + Veyon + + + Logging + Günlükleme + + + Log file directory + Günlük dosyası dizini + + + Log level + Günlükleme düzeyi + + + Nothing + Hiçbir şey + + + Only critical messages + Yalnızca ciddi iletiler + + + Errors and critical messages + Hatalar ve ciddi iletiler + + + Warnings and errors + Uyarılar ve hatalar + + + Information, warnings and errors + Bilgi, uyarılar ve hatalar + + + Debug messages and everything else + Hata ayıklama iletileri ve diğer her şey + + + Limit log file size + Günlük dosya boyutunu sınırla + + + Clear all log files + Tüm günlük dosyalarını temizle + + + Log to standard error output + Standart hata çıktısına günlükle + + + Network object directory + Ağ nesnesi dizini + + + Backend: + Arka uç: + + + Update interval: + Güncelleme aralığı: + + + %1 service + %1 hizmeti + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + Günlük dosyalarının kaldırılması için %1 hizmetinin geçici olarak durdurulması gerekiyor. Devam et? + + + Log files cleared + Günlük dosyaları temizlendi + + + All log files were cleared successfully. + Tüm günlük dosyaları başarıyla temizlendi. + + + Error + Hata + + + Could not remove all log files. + Tüm günlük dosyaları silinemedi. + + + MB + MB + + + Rotate log files + Günlük dosyalarını döndür + + + x + x + + + seconds + saniye + + + Write to logging system of operating system + İşletim sisteminin günlükleme sistemine yaz + + + Authentication + Kimlik + + + Method: + Yöntem: + + + Logon authentication + Oturum açma yetkilendirmesi + + + Key file authentication + Anahtar dosya yetkilendirmesi + + + Test + Sına + + + Authentication is set up properly on this computer. + Kimlik doğrulama bu bilgisayarda doğru şekilde ayarlandı. + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + Kimlik doğrulama testi + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + LDAP'ye göz at + + + + LdapClient + + LDAP error description: %1 + LDAP hata açıklaması: %1 + + + + LdapConfigurationPage + + Basic settings + Temel ayarlar + + + General + Genel + + + LDAP server and port + LDAP sunucusu ve bağlantı noktası + + + Bind DN + DN bağla + + + Bind password + Şifre bağla + + + Anonymous bind + Kimliksiz bağla + + + Use bind credentials + Bağlama yetkileri kullan + + + Base DN + Temel DN + + + Fixed base DN + Durağan temel DN + + + e.g. dc=example,dc=org + örn. dc=ornek,dc=org + + + Discover base DN by naming context + Temel DN'yi adlandırma bağlamına göre keşfet + + + e.g. namingContexts or defaultNamingContext + örn. namingContexts veya defaultNamingContext + + + Environment settings + Ortam ayarları + + + Object trees + Nesne ağaçları + + + Computer tree + Bilgisayar ağacı + + + e.g. OU=Groups + örn. OU=Kümeler + + + User tree + Kullanıcı ağacı + + + e.g. OU=Users + örn. OU=Kullanıcılar + + + e.g. OU=Computers + örn. OU=Bilgisayarlar + + + Group tree + Küme ağacı + + + Perform recursive search operations in object trees + Nesne ağaçlarında özyinelemeli arama işlemleri gerçekleştir + + + Object attributes + Nesne öznitelikleri + + + e.g. hwAddress + örn. hwAddress + + + e.g. member or memberUid + örn. member veya memberUid + + + e.g. dNSHostName + örn. dNSHostName + + + Computer MAC address attribute + Bilgisayar MAC adresi özniteliği + + + Group member attribute + Küme üyesi özniteliği + + + e.g. uid or sAMAccountName + örn. uid veya sAMAccountName + + + Advanced settings + Gelişmiş ayarlar + + + Optional object filters + İsteğe bağlı nesne süzgeçleri + + + Filter for user groups + Kullanıcı kümeleri için süzgeç + + + Filter for users + Kullanıcılar için süzme + + + Filter for computer groups + Bilgisayar kümeleri için süzme + + + Group member identification + Küme üyesi kimliği + + + Distinguished name (Samba/AD) + Ayırt edilen ad (Samba/AD) + + + List all groups of a user + Kullanıcının tüm kümelerini listele + + + List all groups of a computer + Bilgisayarın tüm kümelerini listele + + + Get computer object by IP address + IP adresiyle bilgisayar nesnesi al + + + LDAP connection failed + LDAP bağlantısı başarısız + + + LDAP bind failed + LDAP bağlantısı başarısız + + + LDAP bind successful + LDAP bağlantısı başarılı + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + LDAP sunucusu bağlantısı başarılı. Temel LDAP ayarları düzgün şekilde yapılandırıldı. + + + LDAP base DN test failed + LDAP temel DN sınaması başarısız + + + LDAP base DN test successful + LDAP temel DN sınaması başarılı + + + LDAP naming context test failed + LDAP adlandırma bağlamı sınaması başarısız + + + LDAP naming context test successful + LDAP adlandırma bağlamı sınaması başarılı + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + LDAP adlandırma bağlamı başarıyla sorgulandı. Şu temel DN bulundu: +%1 + + + user tree + kullanıcı ağacı + + + group tree + küme ağacı + + + computer tree + bilgisayar ağacı + + + Enter username + Kullanıcı adı gir + + + Please enter a user login name (wildcards allowed) which to query: + Sorgulanacak kullanıcı giriş adını (joker karakterlere izin verilir) girin: + + + user objects + kullanıcı nesneleri + + + Enter group name + Küme adı gir + + + Please enter a group name whose members to query: + Üyeleri sorgulanacak küme adını girin: + + + group members + küme üyeleri + + + Group not found + Küme bulunamadı + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + "%1" adlı bir küme bulunamadı.. Lütfen küme adını veya küme ağacı parametresini gözden geçirin. + + + Enter computer name + Bilgisayar adı gir + + + computer objects + bilgisayar nesneleri + + + Enter computer DN + Bilgisayar DN'sini gir + + + Please enter the DN of a computer whose MAC address to query: + Lütfen MAC adresi sorgulanacak bir bilgisayarın DN'sini girin: + + + computer MAC addresses + bilgisayar MAC adresleri + + + users + kullanıcılar + + + user groups + kullanıcı kümeleri + + + computer groups + bilgisayar kümeleri + + + Please enter a user login name whose group memberships to query: + Küme üyelikleri sorgulanacak kullanıcı giriş adı girin: + + + groups of user + kullanıcı kümeleri + + + User not found + Kullanıcı bulunamadı + + + groups of computer + bilgisayar kümeleri + + + Computer not found + Bilgisayar bulunamadı + + + Enter computer IP address + Bilgisayar IP adresi gir + + + Please enter a computer IP address which to resolve to an computer object: + Bilgisayar nesnesine çıkan bir bilgisayar IP adresi girin: + + + computers + bilgisayarlar + + + LDAP %1 test failed + LDAP %1 sınaması başarısız + + + LDAP %1 test successful + LDAP %1 sınaması başarılı + + + The %1 has been queried successfully and %2 entries were found. + %1 sorgulandı ve %2 girdi bulundu. + + + %1 %2 have been queried successfully: + +%3 + %1 %2 başarıyla sorgulandı. + +%3 + + + LDAP filter test failed + LDAP süzgeç sınaması başarısız + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + Yapılandırılmış süzgeç kullanılarak herhangi bir %1 sorgulanamadı. Lütfen %1 için LDAP süzgecini denetleyin. + +%2 + + + LDAP filter test successful + LDAP süzgeç sınaması başarılı + + + %1 %2 have been queried successfully using the configured filter. + Yapılandırılmış süzgeç kullanılarak %1 %2 başarıyla sorgulandı. + + + (only if different from group tree) + (eğer yalnızca küme ağacından farklıysa) + + + Computer group tree + Bilgisayar kümesi ağacı + + + computer group tree + bilgisayar kümesi ağacı + + + Filter for computers + Bilgisayarlar için filtrele + + + e.g. room or computerLab + örn. room veya computerLab + + + Integration tests + Tümleştirme sınamaları + + + Computer groups + Bilgisayar kümeleri + + + e.g. name or description + örn. ad ve tanımlama + + + Filter for computer containers + Bilgisayar taşıyıcıları için filtrele + + + Computer containers or OUs + + + + Connection security + Bağlantı güvenliği + + + TLS certificate verification + TLS sertifika doğrulaması + + + System defaults + Sistem varsayılanları + + + Never (insecure!) + Asla (güvensiz!) + + + Custom CA certificate file + Özel CA sertifika dosyası + + + None + Yok + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + örnek: (nesneSınıfı=bilgisayar) + + + e.g. (objectClass=group) + örnek: (nesneSınıfı=grup) + + + e.g. (objectClass=person) + örnek: (nesneSınıfı=kişi) + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + Yapılandırılmış temel DN kuyruklaması başarısız. Lütfen temel DN değişkenini kontrol ediniz. + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + Sertifika dosyaları (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + LDAP sunucusuna bağlanılamadı. Lütfen sunucu parametrelerini gözden geçirin. + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + LDAP sunucusuna bağlanılamadı. Lütfen sunucu değişkenlerini ve bağlama kimliklerini kontrol edin. + +%1 + + + Encryption protocol + Şifreleme iletişim kuralı + + + Computer location attribute + Bilgisayar konum niteliği + + + Computer display name attribute + Bilgisayar görüntü adı niteliği + + + Location name attribute + Konum adı niteliği + + + e.g. cn or displayName + + + + Computer locations identification + Bilgisayar konumları tanımlaması + + + Identify computer locations (e.g. rooms) via: + Bilgisayar konumlarını şu şekilde tanımla (örn. odalar): + + + Location attribute in computer objects + + + + List all entries of a location + Bir konumun tüm girdilerini listele + + + List all locations + Tüm konumları listele + + + Enter computer display name + Bilgisayar ekran adı gir + + + Please enter a computer display name to query: + Lütfen sıralama için bir bilgisayar ekran adı girin: + + + Enter computer location name + Bilgisayar konum adı gir + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + bilgisayar konumları + + + Enter location name + Konum adı gir + + + Please enter the name of a location whose entries to query: + + + + location entries + konum girdileri + + + LDAP test failed + LDAP testi başarısız + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + ve + + + LDAP test successful + LDAP testi başarılı + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + Gözat + + + Test + Sına + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + Bilgisayar ana makine adı niteliği + + + Please enter a computer hostname to query: + + + + Invalid hostname + Geçersiz ana makine adı + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + Ana makine adı gir + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + Sunucu adı araması başarısız + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + Kullanıcı giriş niteliği + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + bilgisayar taşıyıcıları + + + + LdapPlugin + + Auto-configure the base DN via naming context + Temel DN'yi adlandırma bağlamı aracılığıyla kendiliğinden yapılandır + + + Query objects from LDAP directory + LDAP dizininden nesneleri sorgula + + + Show help about command + Komut hakkında bilgi göster + + + Commands for configuring and testing LDAP/AD integration + LDAP/AD tümelemesini yapılandırmak ve sınamak için komutlar + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + Kullanıcı kimlik doğrulaması için özel PAM hizmeti + + + User authentication + Kullanıcı doğrulama + + + Session management + Oturum yönetimi + + + Display manager users + Görüntü yöneticisi kullanıcıları + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Eklenti, Linux platformu için soyut işlevleri yerine getirir + + + + LocationDialog + + Select location + Konum seç + + + enter search filter... + arama süzgeci gir... + + + + MainToolBar + + Configuration + Yapılandırma + + + Disable balloon tooltips + Balon iouçlarını devre dışı bırak + + + Show icons only + Yalnızca simgeleri göster + + + + MainWindow + + MainWindow + Ana Pencere + + + toolBar + Araç çubuğu + + + General + Genel + + + &File + &Dosya + + + &Help + &Yardım + + + &Quit + &Çıkış + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + Ayarları d&osyadan yükle + + + Ctrl+O + Ctrl+O + + + About Qt + Qt Hakkında + + + Authentication impossible + Kimlik doğrulama imkansız + + + Configuration not writable + Yapılandırma yazılabilir değil + + + Load settings from file + Ayarları dosyadan yükle + + + Save settings to file + Ayarları dosyaya kaydet + + + Unsaved settings + Kaydedilmemiş ayarlar + + + There are unsaved settings. Quit anyway? + Kaydedilmemiş ayarlar var. Yine de çıkmak istiyor musunuz? + + + Veyon Configurator + Veyon Yapılandırıcı + + + Service + Hizmet + + + Master + Usta + + + Access control + Erişim denetimi + + + About Veyon + Veyon Hakkında + + + Auto + Kendiliğinden + + + About + Hakkında + + + %1 Configurator %2 + %1 Yapılandırıcı %2 + + + JSON files (*.json) + JSON dosyaları (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + Yerel yapılandırma arka ucu, yapılandırmanın yazılabilir olmadığını bildirdi! Lütfen %1 Yapılandırıcıyı yüksek ayrıcalıklarla çalıştırın. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + Yetkilendirme anahtar dosyaları yok veya var olanlar geçersiz. Lütfen, %1 Yapılandırıcı kullanarak yeni anahtar dosyalar oluşturun. Diğer bir seçenek olarak, %1 Yapılandırıcıyı kullanarak giriş yetkilendirmesini ayarlayın. Bunu dışında %1 kullanarak bilgisayarlara erişemezsiniz. + + + Access denied + Erişim reddedildi + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + Yerel yapılandırmaya göre, ağdaki bilgisayarlara erişim izniniz yok. Lütfen başka bir hesapla giriş yapın veya sistem yöneticinizin yerel yapılandırmayı gözden geçirmesini isteyin. + + + Screenshots + Ekran görüntüleri + + + Feature active + Özellik etkin + + + The feature "%1" is still active. Please stop it before closing %2. + "%1" özelliği hala etkin. Lütfen %2'yi kapatmadan önce durdurun. + + + Reset configuration + Ayarları sıfırla + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Yerel yapılandırmayı sıfırlamak ve tüm ayarları öntanımlılarına geri döndürmek istediğinize emin misiniz? + + + Search users and computers + Kullanıcı ve bilgisayarları ara + + + Align computers to grid + Bilgisayarları ızgaraya hizala + + + %1 Configurator + %1 Yapılandırıcı + + + Insufficient privileges + Yetersiz öncelikler + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + Yönetici öncelikleriyle başlatılamıyor. Lütfen masaüstü ortamınız için sudo benzeri programın kurulu olduğundan emin olun! Program sıradan kullanıcı öncelikleriyle çalışacak. + + + Only show powered on computers + Yalnızca gücü açık bilgisayarları göster + + + &Save settings to file + Ayarları dosyaya &kaydet + + + &View + &Görünüm + + + &Standard + &Standart + + + &Advanced + &Gelişmiş + + + Use custom computer arrangement + Özel bilgisayar düzenlemesi kullan + + + Locations && computers + Konumlar ve && bilgisayarlar + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + Dizinler + + + User configuration + Kullanıcı yapılandırması + + + Feature on computer double click: + Bilgisayarda çift tıklama özelliği: + + + Features + Özellikler + + + All features + Tüm özellikler + + + Disabled features + Devre dışı özellikler + + + Screenshots + Ekran görüntüleri + + + <no feature> + <no feature> + + + Basic settings + Temel ayarlar + + + Behaviour + Davranış + + + Enforce selected mode for client computers + İstemci bilgisayarları seçilen kipe zorla + + + Hide local computer + Tüm bilgisayarları gizle + + + Hide computer filter field + Bilgisayar süzme alanını gizle + + + Actions such as rebooting or powering down computers + Bilgisayarları yeniden başlatma veya kapatma eylemleri gibi + + + User interface + Kullanıcı arayüzü + + + Background color + Arka plan rengi + + + Thumbnail update interval + Küçük resim güncelleme aralığı + + + ms + ms + + + Program start + Program başlat + + + Modes and features + Modlar ve özellikler + + + User and computer name + Kullanıcı ve bilgisayar adı + + + Only user name + Yalnızca kullanıcı adı + + + Only computer name + Yalnızca bilgisayar adı + + + Computer thumbnail caption + Bilgisayar küçük resminin açıklaması + + + Text color + Metin rengi + + + Sort order + Sıralama düzeni + + + Computer and user name + Bilgisayar ve kullanıcı adı + + + Computer locations + Bilgisayar konumları + + + Show current location only + Yalnızca geçerli konumu göster + + + Allow adding hidden locations manually + Gizli yerleri manuel olarak eklemeye izin ver + + + Hide empty locations + Boş yerleri gizle + + + Show confirmation dialog for potentially unsafe actions + Güvenli olmayabilecek işlemler için onay iletişim kutusunu göster + + + Perform access control + Erişim kontrolü gerçekleştir + + + Automatically select current location + Geçerli konumu otomatik olarak seç + + + Automatically open computer select panel + Bilgisayar seçme panelini otomatik olarak aç + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + Kendiliğinden + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + Gözlemleme + + + Builtin monitoring mode + Dahili gözlemleme kipi + + + This mode allows you to monitor all computers at one or more locations. + Bu mod, bir veya daha fazla konumdaki tüm bilgisayarları izlemenizi sağlar. + + + + NetworkObjectTreeModel + + Locations/Computers + Konumlar/Bilgisayarlar + + + + OpenWebsiteDialog + + Open website + Web sitesi aç + + + e.g. Veyon + Örn. Veyon + + + Remember and add to website menu + Unutmayın ve web sitesi menüsüne ekleyin + + + e.g. www.veyon.io + örn. www.veyon.io + + + Please enter the URL of the website to open: + Lütfen açılacak web sitesinin URL'sini girin: + + + Name: + İsim: + + + + PasswordDialog + + Username + Kullanıcı adı + + + Password + Parola + + + Veyon Logon + Veyon Girişi + + + Authentication error + Yetkilendirme hatası + + + Logon failed with given username and password. Please try again! + Verilen kullanıcı adı ve parola ile giriş başarısız. Lütfen yeniden deneyin! + + + Please enter your username and password in order to access computers. + Lütfen bilgisayarlara erişmek için kullanıcı adı ve parolanızı girin. + + + + PowerControlFeaturePlugin + + Power on + Sistemi aç + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Tüm bilgisayarları açmak için bu düğmeye tıklayın. Bu yolla, her bilgisayarı elle açmanıza gerek kalmaz. + + + Reboot + Yeniden başlat + + + Click this button to reboot all computers. + Tüm bilgisayarları yeniden başlatmak için bu düğmeye tıklayın. + + + Power down + Sistemi kapat + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Tüm bilgisayarları kapatmak için bu düğmeye tıklayın. Bu yolla, her bilgisayarı elle kapatmanıza gerek kalmaz. + + + Power on/down or reboot a computer + Bir bilgisayarı aç/kapat veya yeniden başlat + + + Confirm reboot + Yeniden başlatmayı onayla + + + Confirm power down + Sistemi kapatmayı onayla + + + Do you really want to reboot the selected computers? + Seçilen bilgisayarları yeniden başlatmak istediğinize emin misiniz? + + + Do you really want to power down the selected computer? + Seçilen bilgisayarları kapatmak istediğinize emin misiniz? + + + Power on a computer via Wake-on-LAN (WOL) + Bir bilgisayarı ağdan uyandır (WOL) + + + MAC ADDRESS + MAC ADRESİ + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + Bu komut, verilen MAC adresine sahip bilgisayarı ağ üzerinden uyandırmak için Ağdan-Uyandırma (WOL) paketi yayımlar + + + Please specify the command to display help for! + Lütfen yardım görüntüleme komutunu belirtin! + + + Invalid MAC address specified! + Geçersiz MAC adresi tanımlandı! + + + Commands for controlling power status of computers + Bilgisayarların güç durumunu kontrol etmek için komutlar + + + Power down now + Gücü şimdi kapat + + + Install updates and power down + Güncellemeleri kurduktan sonra gücü kapat + + + Power down after user confirmation + Kullanıcı onayından sonra gücü kapat + + + Power down after timeout + Belirli süre sonunda gücü kapat + + + The computer was remotely requested to power down. Do you want to power down the computer now? + Bu bilgisayarın kapatılması istendi. Şimdi kapatmak ister misiniz? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + Bilgisayarınız %1 dakika, %2 saniye sonra kapatılacak. + +Lütfen çalışmalarınızı kaydedip tüm açık pencereleri kapatın. + + + + PowerDownTimeInputDialog + + Power down + Sistemi kapat + + + Please specify a timeout for powering down the selected computers: + Lütfen seçilen bilgisayarları kapatmak için bir zaman aşımı süresi belirtin: + + + minutes + dakika + + + seconds + saniye + + + + RemoteAccessFeaturePlugin + + Remote view + Uzaktan görüntüle + + + Open a remote view for a computer without interaction. + Etkileşim olmadan bir bilgisayarı uzaktan görüntüle. + + + Remote control + Uzaktan denetim + + + Open a remote control window for a computer. + Bir bilgisayar için uzaktan yönetim denetimi aç. + + + Remote access + Uzaktan erişim + + + Remote view or control a computer + Bilgisayarı uzaktan görüntüle veya denetle + + + Please enter the hostname or IP address of the computer to access: + Bilgisayara erişim için lütfen bir ana makine adı veya IP adresi giriniz: + + + Show help about command + Komut hakkında bilgi göster + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 -%2 Uzaktan Erişim + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + Yalnızca izle + + + Remote control + Uzaktan denetim + + + Send shortcut + Kısayol gönder + + + Fullscreen + Tam ekran + + + Window + Pencere + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Menü + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + %1 bağlanıyor + + + Connected. + Bağlandı. + + + Screenshot + Ekran görüntüsü + + + Exit + Çıkış + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Lütfen seçilen bilgisayar(lar)da çalıştırılacak programları veya komutları girin. Birden çok program/komut satır ile ayrılabilir. + + + Run programs + Program çalıştır + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + örnek. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + İsim: + + + Remember and add to program menu + Unutmayın ve program menüsüne ekleyin + + + e.g. VLC + örn. VLC + + + + ScreenLockFeaturePlugin + + Lock + Kilitle + + + Unlock + Kilidi kaldır + + + Lock screen and input devices of a computer + Bir bilgisayarın giriş aygıtlarını ve ekranını kilitle + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Tüm kullanıcının dikkatini toplamak için bilgisayarlarını bu düğmeyi kullanarak kilitleyebilirsiniz. Bu kipte tüm giriş aygıtları kilitlenir ve ekranlar karartılır. + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + bilinmeyen + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + %1 dizini olmadığı ve oluşturulamadığı için bir ekran görüntüsü alınamadı. + + + Screenshot + Ekran görüntüsü + + + Could not open screenshot file %1 for writing. + %1 ekran görüntüsü dosyası yazmak için açılamadı. + + + + ScreenshotFeaturePlugin + + Screenshot + Ekran görüntüsü + + + Use this function to take a screenshot of selected computers. + Seçilen bilgisayarların ekran görüntüsünü almak için bu işlevi kullan. + + + Screenshots taken + Ekran görüntüleri alındı + + + Screenshot of %1 computer have been taken successfully. + %1 bilgisayarının ekran görüntüsü başarıyla alındı. + + + Take screenshots of computers and save them locally. + Bilgisayarların ekran görüntülerini al ve yerel olarak kaydet. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Sizin tarafınızdan alınan tüm ekran görüntüleri burada listelenmiştir. Bir bilgisayarın sağ tuş menüsünde "ekran görüntüsü" ögesini tıklayarak ekran görüntülerini alabilir. Aşağıdaki düğmeler kullanılarak ekran görüntüleri yönetilebilir. + + + User: + Kullanıcı: + + + Computer: + Bilgisayar: + + + Date: + Tarih: + + + Time: + Zaman: + + + Show + Göster + + + Delete + Sil + + + Screenshot + Ekran görüntüsü + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Genel + + + Autostart + Kendiliğinden başlat + + + Hide tray icon + Tepsi simgesini gizle + + + Start service + Hizmeti başlat + + + Stopped + Durduruldu + + + Stop service + Hizmeti durdur + + + State: + Durum: + + + Enable firewall exception + Güvenlik duvarı özel durumu etkinleştir + + + Allow connections from localhost only + Yalnızca yerel sunucu bağlantılarına izin ver + + + VNC server + VNC sunucusu + + + Plugin: + Eklenti: + + + Restart %1 Service + %1 Hizmetini Yeniden Başlat + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Tüm ayarlar başarıyla kaydedildi. Ayarların geçerli olması için %1 hizmetinin yeniden başlatılması gerekiyor. Şimdi yeniden başlatılsın mı? + + + Running + Çalışıyor + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + Bu seçeneğin etkinleştirilmesi, hizmetin bilgisayardaki her etkileşimli oturum için bir sunucu işlemi başlatmasını sağlar. +Bu genellikle terminal sunucularını desteklemek için gereklidir. + + + Show notification on remote connection + Uzak bağlantıda bildirim göster + + + Show notification when an unauthorized access is blocked + Yetkisiz erişim engellendiğinde bildirim göster + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + Tanıtım sunucusu + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + Hizmet başlatılıyor %1 + + + Stopping service %1 + Hizmet durduruluyor %1 + + + Registering service %1 + Hizmet kaydediliyor %1 + + + Unregistering service %1 + Hizmet kaydedilimiyor %1 + + + Service control + Hizmet kontrolü + + + + ServiceControlPlugin + + Service is running + Hizmet çalışıyor + + + Service is not running + Hizmet çalışmıyor + + + Configure and control Veyon service + Veyon hizmetini ayarla ve denetle + + + Register Veyon Service + Veyon Hizmetini Kaydet + + + Unregister Veyon Service + Veyon Hizmetinin Kaydını Kaldır + + + Start Veyon Service + Veyon Hizmetini Başlat + + + Stop Veyon Service + Veyon Hizmetini Durdur + + + Restart Veyon Service + Veyon Hizmetini Yeniden Başlat + + + Query status of Veyon Service + Veyon Hizmetinin sorgu durumu + + + Commands for configuring and controlling Veyon Service + Veyon Hizmetini yapılandırmak ve denetlemek için komutlar + + + + ShellCommandLinePlugin + + Run command file + Komut dosyasını çalıştır + + + File "%1" does not exist! + "%1" dosyası mevcut değil! + + + Interactive shell and script execution for Veyon Control + Veyon Kontrol için etkileşimli kabuk ve komut dosyası yürütme + + + Commands for shell functionalities + Kabuk işlevleri için komutlar + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + Sistem tepsisi simgesi + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + Sistem kullanıcı grupları için kullanıcı grupları arka ucu + + + Default (system user groups) + Varsayılan (sistem kullanıcı grupları) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + Dahili Veyon bileşenlerini ve fonksiyonlarını test et + + + Commands for testing internal components and functions of Veyon + Veyon'un dahili bileşenlerini ve işlevlerini test etme komutları + + + + TextMessageDialog + + Send text message + Metin iletisi gönder + + + Use the field below to type your message which will be sent to all selected users. + Tüm seçili kullanıcılara gönderilecek iletinizi yazmak için aşağıdaki alanı kullanın. + + + + TextMessageFeaturePlugin + + Text message + Metin iletisi + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Tüm kullanıcılara metin iletisi göndermek için bu işlevi kullanın, örneğin onlara yeni görevler atamak için. + + + Message from teacher + Öğretmenden ileti + + + Send a message to a user + Kullanıcıya bir ileti gönder + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Katmanlandırılmış (yarı saydam) pencerelerin yakalanmasını etkinleştir + + + Poll full screen (leave this enabled per default) + Anket tam ekranı (bunu varsayılan olarak etkin bırakın) + + + Low accuracy (turbo mode) + Düşük hassasiyet (turbo kipi) + + + Builtin UltraVNC server configuration + Dahili UltraVNC sunucu yapılandırması + + + Enable multi monitor support + Çoklu monitör desteğini etkinleştir + + + Enable Desktop Duplication Engine on Windows 8 and newer + Windows 8 ve daha yeni sürümlerde Masaüstü Çoğaltma Altyapısını etkinleştir + + + Maximum CPU usage + + + + + UserConfig + + No write access + Erişim izni yok + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Kişisel ayarlarınız kaydedilemedi! Lütfen kullanıcı yapılandırma dosyasının yolunu, %1 Yapılandırıcıyı kullanarak gözden geçirin. + + + + UserLoginDialog + + User login + Kullanıcı Girişi + + + Please enter a username and password for automatic login on all computers. + Lütfen tüm bilgisayarlarda otomatik oturum açmak için bir kullanıcı adı ve şifre girin. + + + Username + Kullanıcı adı + + + Password + Parola + + + + UserSessionControlPlugin + + Log in + Giriş + + + Click this button to log in a specific user on all computers. + Tüm bilgisayarlarda belirli bir kullanıcıya giriş yapmak için bu düğmeyi tıklatın. + + + Log off + Çıkış + + + Click this button to log off users from all computers. + Tüm bilgisayarlardaki kullanıcıların oturumlarını kapatmak için bu düğmeyi tıklayın. + + + Confirm user logoff + Kullanıcı oturumunu kapatmayı onayla + + + Do you really want to log off the selected users? + Seçilen kullanıcıların oturumunu gerçekten kapatmak istiyor musunuz? + + + User session control + Kullanıcı oturum denetimi + + + + VeyonCore + + [OK] + [TAMAM] + + + [FAIL] + [BAŞARISIZ] + + + Invalid command! + Geçersiz komut! + + + Available commands: + Kullanılabilir komutlar: + + + Invalid arguments given + Geçersiz argümanlar verildi + + + Not enough arguments given - use "%1 help" for more information + Yeteri kadar argüman verilmedi - ayrıntılı bilgi için "%1 help" kullanın + + + Unknown result! + Bilinmeyen sonuç! + + + Available modules: + Kullanılabilir modüller: + + + No module specified or module not found - available modules are: + Modül belirtilmedi veya modül bulunamadı - kullanılabilir modüller: + + + Plugin not licensed + Eklenti lisanslı değil + + + INFO + BİLGİ + + + ERROR + HATA + + + USAGE + KULLANIMI + + + DESCRIPTION + AÇIKLAMA + + + EXAMPLES + ÖRNEKLER + + + WARNING + UYARI + + + + VeyonServiceControl + + Veyon Service + Veyon Hizmeti + + + + VncViewWidget + + Establishing connection to %1 ... + %1 ile bağlantı kuruluyor... + + + + WebApiConfigurationPage + + Web API + + + + General + Genel + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + s + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + SAS üretimi ayarı yazılımla değiştirilemedi. Uzaktan kontrol ile Ctrl + Alt + Del gönderme özelliği çalışmaz! + + + + WindowsPlatformConfigurationPage + + Windows + Pencereler + + + General + Genel + + + Enable SAS generation by software (Ctrl+Alt+Del) + Yazılım tarafından SAS oluşturulmasını etkinleştir (Ctrl+Alt+Del) + + + Screen lock + Ekran kilidi + + + Hide taskbar + Görev çubuğunu gizle + + + Hide start menu + Başlat menüsünü gizle + + + Hide desktop + Masaüstünü gizle + + + User authentication + Kullanıcı doğrulama + + + Use alternative user authentication mechanism + Kullanıcı yetkilendirmesi için alternatif mekanizma kullan + + + User login + Kullanıcı Girişi + + + Input start delay + Girdi başlangıç gecikmesi + + + Simulated key presses interval + Taklit edilmiş tuş basış süresi + + + Confirm legal notice (message displayed before user logs in) + Yasal bilgilendirmeyi onaylat (kullanıcı oturum açmadan önce mesaj gösterilir) + + + Use input device interception driver + Girdi cihazı müdahale sürücüsü kullan + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Eklenti, Windows platformu için soyut işlevleri yerine getirir + + + + WindowsServiceControl + + The service "%1" is already installed. + "%1" hizmeti zaten yüklü. + + + The service "%1" could not be installed. + "%1" hizmeti yüklenemedi. + + + The service "%1" has been installed successfully. + "%1" hizmeti başarıyla yüklendi. + + + The service "%1" could not be uninstalled. + "%1" hizmeti kaldırılamadı. + + + The service "%1" has been uninstalled successfully. + "%1" hizmeti başarıyla kaldırıldı. + + + The start type of service "%1" could not be changed. + "%1" hizmetinin başlangıç ​​türü değiştirilemedi. + + + Service "%1" could not be found. + "%1" hizmeti bulunamadı. + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Dahili x11vnc sunucusu yapılandırması + + + Custom x11vnc parameters: + Özel x11vnc parametreleri: + + + Do not use X Damage extension + X Damage eklentisini kullanma + + + diff --git a/translations/veyon_uk.ts b/translations/veyon_uk.ts new file mode 100644 index 0000000..6517f92 --- /dev/null +++ b/translations/veyon_uk.ts @@ -0,0 +1,4082 @@ + + + + + AboutDialog + + About + Про програму + + + Translation + Переклад + + + License + Ліцензування + + + About Veyon + Про Veyon + + + Contributors + Учасники розробки + + + Version: + Версія: + + + Website: + Сайт: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Програму ще не перекладено поточною мовою (або налаштовано на використання англійської). + +Якщо ви хочете перекласти Veyon вашою рідною або якоюсь іншою мовою або хочете удосконалити наявний переклад, будь ласка, зв’яжіться із розробником Veyon! + + + About %1 %2 + Про %1 %2 + + + Support Veyon project with a donation + Підтримати проект Veyon фінансово + + + + AccessControlPage + + Computer access control + Керування доступом до комп’ютерів + + + Grant access to every authenticated user (default) + Надавати доступу усім розпізнаним користувачам (типовий варіант) + + + Test + Перевірити + + + Process access control rules + Обробка правил керування доступом + + + User groups authorized for computer access + Групи користувачів, які уповноважено для доступу до комп’ютера + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Будь ласка, додайте групи, учасники яких повинні будуть проходити розпізнавання, щоб отримати доступу до комп’ютерів у вашій мережі Veyon. + + + Authorized user groups + Уповноважені групи користувачів + + + All groups + Всі групи + + + Access control rules + Правила керування доступом + + + Add access control rule + Додати правило керування доступом + + + Remove access control rule + Вилучити правило керування доступом + + + Move selected rule down + Пересунути позначене правило нижче + + + Move selected rule up + Пересунути позначене правило вище + + + Edit selected rule + Змінити позначене правило + + + Enter username + Введіть ім’я користувача + + + Please enter a user login name whose access permissions to test: + Будь ласка, вкажіть назву облікового запису користувача, чиї права доступу слід перевірити: + + + Access allowed + Доступ дозволено + + + The specified user is allowed to access computers with this configuration. + Вказаному користувачеві дозволено доступ до комп’ютерів з цими налаштуваннями. + + + Access denied + Доступ заборонено + + + The specified user is not allowed to access computers with this configuration. + Вказаному користувачеві заборонено доступ до комп’ютерів з цими налаштуваннями. + + + Enable usage of domain groups + Увімкнути використання груп доменів + + + User groups backend: + Модуль груп користувачів: + + + Missing user groups backend + Не вистачає модуля обробки груп користувачів + + + No default user groups plugin was found. Please check your installation! + Не знайдено типового додатка груп користувачів. Будь ласка, перевірте, чи належним чином встановлено програму! + + + Restrict access to members of specific user groups + Обмежити доступ учасникам вказаних груп користувачів + + + + AccessControlRuleEditDialog + + Edit access control rule + Змінити правило керування доступом + + + General + Загальні + + + enter a short name for the rule here + тут можна вказати скорочену назву правила + + + Rule name: + Назва правила: + + + enter a description for the rule here + тут можна вказати опис правила + + + Rule description: + Опис правила: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Інверсія усіх умов («є або має» буде вважатися записом «не є або не має») + + + Conditions + Умови + + + is member of group + є учасником групи + + + Accessing computer is localhost + Комп’ютер для доступу є локальним + + + Accessing user is logged on user + Користувач для доступу є розпізнаним користувачем системи + + + Accessing user is already connected + Користувача для доступу вже з’єднано + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Якщо вказано декілька умов, для застосування правила має бути виконано усі ці умови (логічне «І»). Якщо має бути виконано хоч одну з декількох умов (логічне «АБО»), вам слід створити декілька відповідних правил керування доступом. + + + Action + Дія + + + Allow access + Дозволити доступ + + + Deny access + Заборонити доступ + + + Ask logged on user for permission + Запитувати розпізнаного користувача системи щодо доступу + + + None (rule disabled) + Немає (правило вимкнено) + + + Accessing user + Користувач для доступу + + + Accessing computer + Комп’ютер для доступу + + + Local (logged on) user + Локальний користувач (у системі) + + + Local computer + Локальний комп’ютер + + + Always process rule and ignore conditions + Завжди обробляти правило і ігнорувати умови + + + No user logged on + Немає користувачів у системі + + + Accessing user has one or more groups in common with local (logged on) user + Користувач для доступу є учасником однієї або декількох груп, які є спільними із локальним (поточним) користувачем + + + Accessing computer and local computer are at the same location + Комп’ютер для доступу розташовано у тому самому місці, що і локальний комп’ютер + + + is located at + розташовано у + + + + AccessControlRulesTestDialog + + Access control rules test + Перевірка правил керування доступом + + + Accessing user: + Користувач для доступу: + + + Local computer: + Локальний комп’ютер: + + + Accessing computer: + Комп’ютер для доступу: + + + Please enter the following user and computer information in order to test the configured ruleset. + Будь ласка, вкажіть нижче дані щодо користувача і комп’ютера, щоб перевірити налаштований набір правил. + + + Local user: + Локальний користувач: + + + Connected users: + З’єднані користувачі: + + + The access in the given scenario is allowed. + Доступ за вказаним сценарієм дозволено. + + + The access in the given scenario is denied. + Доступ за вказаним сценарієм заборонено. + + + The access in the given scenario needs permission of the logged on user. + Доступ за вказаним сценарієм потребує дозволу від користувача, який увійшов до системи. + + + ERROR: Unknown action + ПОМИЛКА: невідома дія + + + Test result + Результат тестування + + + + AuthKeysConfigurationPage + + Authentication keys + Ключі розпізнавання + + + Introduction + Вступ + + + Key file directories + Каталоги файлів ключів + + + Public key file base directory + Основний каталог файлів відкритих ключів + + + Private key file base directory + Основний каталог файлів закритих ключів + + + Available authentication keys + Доступні ключі розпізнавання + + + Create key pair + Створити пару ключів + + + Delete key + Вилучити ключ + + + Import key + Імпорт ключа + + + Export key + Експортувати ключ + + + Set access group + Встановити групу доступу + + + Key files (*.pem) + файли ключів (*.pem) + + + Authentication key name + Назва ключа для розпізнавання + + + Please enter the name of the user group or role for which to create an authentication key pair: + Будь ласка, вкажіть назву групи користувачів або роль, для якої слід створити пару ключів для розпізнавання: + + + Do you really want to delete authentication key "%1/%2"? + Ви справді хочете вилучити ключ розпізнавання «%1/%2»? + + + Please select a key to delete! + Будь ласка, виберіть ключ для вилучення! + + + Please enter the name of the user group or role for which to import the authentication key: + Будь ласка, вкажіть назву групи користувачів або роль, для якої слід імпортувати ключ розпізнавання: + + + Please select a key to export! + Будь ласка, виберіть ключ для експортування! + + + Please select a user group which to grant access to key "%1": + Будь ласка, виберіть групу користувачів, які слід надати доступу до ключа «%1»: + + + Please select a key which to set the access group for! + Будь ласка, виберіть ключ, доступ до якого слід встановити для групи! + + + Please perform the following steps to set up key file authentication: + Будь ласка, виконайте такі кроки, щоб налаштувати файл ключа розпізнавання: + + + 1) Create a key pair on the master computer. + 1) Створіть пару ключів на основному комп'ютері. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Встановіть групу доступу, учасникам якої буде дозволено доступу до інших комп'ютерів. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Експортуйте відкритий ключ і імпортуйте його на всіх клієнтські комп'ютери із однаковою назвою. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Будь ласка, ознайомтеся із <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Підручником із адміністрування Veyon</a>, щоб дізнатися більше. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Пара ключів для розпізнавання складається із поєднаних між собою криптографічних ключів, закритого і відкритого. За допомогою закритого ключа користувачі на основному комп'ютері можуть отримувати доступу до клієнтських комп'ютерів. Важливо, щоб право на читання файла закритого ключа мали лише уповноважені на це користувачі. Відкритий ключ використовується на клієнтських комп'ютерах для розпізнавання вхідних запитів щодо з'єднання. + + + + AuthKeysManager + + Please check your permissions. + Будь ласка, переконайтеся, що маєте належні права доступу. + + + Key name contains invalid characters! + У назві ключа містяться некоректні символи! + + + Invalid key type specified! Please specify "%1" or "%2". + Вказано некоректний тип ключа! Будь ласка, вкажіть «%1» або «%2». + + + Specified key does not exist! Please use the "list" command to list all installed keys. + Вказаного ключа не існує! Будь ласка, скористайтеся командою «list» для перегляду списку усіх встановлених ключів. + + + One or more key files already exist! Please delete them using the "delete" command. + Один або декілька ключів вже існують! Будь ласка, вилучіть їх за допомогою команди «delete». + + + Creating new key pair for "%1" + Створюємо нову пару ключів для «%1» + + + Failed to create public or private key! + Не вдалося створити відкритий або закритий ключ! + + + Newly created key pair has been saved to "%1" and "%2". + Новостворену пару ключів було збережено до «%1» і «%2». + + + Could not remove key file "%1"! + Не вдалося вилучити файл ключа «%1»! + + + Could not remove key file directory "%1"! + Не вдалося вилучити каталог файла ключа «%1»! + + + Failed to create directory for output file. + Не вдалося створити каталог для файла виведення даних. + + + File "%1" already exists. + Файл «%1» вже існує. + + + Failed to write output file. + Не вдалося виконати запис до файла виведення. + + + Key "%1/%2" has been exported to "%3" successfully. + Ключ «%1/%2» було успішно експортовано до «%3». + + + Failed read input file. + Не вдалося прочитати файл вхідних даних. + + + File "%1" does not contain a valid private key! + У файлі «%1» не міститься коректного закритого ключа! + + + File "%1" does not contain a valid public key! + У файлі «%1» не міститься коректного відкритого ключа! + + + Failed to create directory for key file. + Не вдалося створити каталог для файла ключа. + + + Failed to write key file "%1". + Не вдалося записати файл ключа «%1». + + + Failed to set permissions for key file "%1"! + Не вдалося встановити права доступу до файла ключа «%1»! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + Ключ «%1/%2» було успішно імпортовано. Будь ласка, перевірте права доступу до файла «%3», щоб запобігти неуповноваженому доступу. + + + Failed to convert private key to public key + Не вдалося перетворити закритий ключ на відкритий + + + Failed to create directory for private key file "%1". + Не вдалося створити каталог для файла закритого ключа «%1». + + + Failed to save private key in file "%1"! + Не вдалося зберегти закритий ключ до файла «%1»! + + + Failed to set permissions for private key file "%1"! + Не вдалося встановити права доступу до файла закритого ключа «%1»! + + + Failed to create directory for public key file "%1". + Не вдалося створити каталог для файла відкритого ключа «%1». + + + Failed to save public key in file "%1"! + Не вдалося зберегти відкритий ключ до файла «%1»! + + + Failed to set permissions for public key file "%1"! + Не вдалося встановити права доступу до файла відкритого ключа «%1»! + + + Failed to set owner of key file "%1" to "%2". + Не вдалося встановити власника файла ключа «%1» у значення «%2». + + + Failed to set permissions for key file "%1". + Не вдалося встановити права доступу до файла ключа «%1». + + + Key "%1" is now accessible by user group "%2". + Ключ «%1» тепер є доступним для групи користувачів «%2». + + + <N/A> + <н/д> + + + Failed to read key file. + Не вдалося прочитати файл ключа. + + + + AuthKeysPlugin + + Create new authentication key pair + Створити нову пару ключів для розпізнавання + + + Delete authentication key + Вилучити ключ розпізнавання + + + List authentication keys + Вивести список ключів розпізнавання + + + Import public or private key + Імпортувати відкритий або закритий ключ + + + Export public or private key + Експортувати відкритий або закритий ключ + + + Extract public key from existing private key + Видобути відкритий ключ із наявного закритого ключа + + + Set user group allowed to access a key + Встановити групу користувачів, які матимуть доступ до ключа + + + KEY + КЛЮЧ + + + ACCESS GROUP + ГРУПА ДОСТУПУ + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + Ця команда коригує права доступу до файла <КЛЮЧ> так, що право на читання цього файла отримують лише учасники групи <ГРУПА ДОСТУПУ>. + + + NAME + НАЗВА + + + FILE + ФАЙЛ + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Ця команда експортує ключ розпізнавання <КЛЮЧ> до файла <ФАЙЛ>. Якщо файл <ФАЙЛ> не вказано, його назву буде побудовано на основі даних щодо назви і типу ключа <КЛЮЧ>. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Ця команда імпортує ключ розпізнавання <КЛЮЧ> з файла <ФАЙЛ> Якщо файл <ФАЙЛ> не вказано, його назву буде побудовано на основі даних щодо назви і типу ключа <КЛЮЧ>. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + Ця команда виводить список усіх доступних ключів розпізнавання у налаштованому каталозі ключів. Якщо вказано параметр «%1», замість списку буде виведено таблицю із подробицями щодо ключів. Деякі параметри ключа може бути не показано, якщо доступ до ключа обмежено, наприклад через брак прав на читання файла ключа. + + + Please specify the command to display help for! + Будь ласка, вкажіть команду, для якої слід показати довідку! + + + TYPE + ТИП + + + PAIR ID + ІД ПАРИ + + + Command line support for managing authentication keys + Підтримка керування ключами розпізнавання у командному рядку + + + Commands for managing authentication keys + Команди для керування ключами розпізнавання + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + Ця команда створює нову пару ключів для розпізнавання із назвою <НАЗВА> і зберігає закритий і відкритий ключі до налаштованих каталогів ключів. Параметром має бути назва ключа, яка має складатися лише з літер. + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + Ця команда вилучає ключ розпізнавання <КЛЮЧ> із налаштованого каталогу ключів. Будь ласка, зауважте, що після вилучення ключ не можна буде відновити. + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + Ця команда видобуває частину, пов'язану із відкритим ключем, з закритого ключа <КЛЮЧ> і зберігає її до відповідного файла відкритого ключа. Через це при налаштовуванні додаткового основного комп'ютера достатньо перенести на нього лише закритий ключ. Після перенесення відкритий ключ можна просто видобути. + + + + AuthKeysTableModel + + Name + Назва + + + Type + Тип + + + Access group + Група доступу + + + Pair ID + Ід. пари + + + + BuiltinDirectoryConfigurationPage + + Computers + Комп'ютери + + + Name + Назва + + + Host address/IP + Адреса/IP вузла + + + MAC address + MAC-адреса + + + Add new computer + Додати новий комп’ютер + + + Remove selected computer + Вилучити позначений комп’ютер + + + New computer + Новий комп’ютер + + + Builtin directory + Вбудований каталог + + + Locations & computers + Місця і комп'ютери + + + Locations + Місця + + + Add new location + Додати нове місце + + + Remove selected location + Вилучити позначене місце + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + Імпортування файлів CSV можна виконати за допомогою інтерфейсу командного рядка. Докладніший опис можна знайти у <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">інтернет-документації</a>. + + + New location + Нове місце + + + + BuiltinDirectoryPlugin + + Show help for specific command + Показати довідку щодо певної команди + + + Import objects from given file + Імпортувати об'єкти із вказаного файла + + + Export objects to given file + Експортувати об'єкти до вказаного файла + + + Invalid type specified. Valid values are "%1" or "%2". + Вказано некоректний тип. Коректними є значення «%1» і «%2». + + + Type + Тип + + + Name + Назва + + + Host address + Адреса вузла + + + MAC address + MAC-адреса + + + Specified object not found. + Вказаного об'єкта не знайдено. + + + File "%1" does not exist! + Файла «%1» не існує! + + + Can't open file "%1" for reading! + Не вдалося відкрити файл «%1» для читання! + + + Unknown argument "%1". + Невідомий аргумент «%1». + + + Computer "%1" (host address: "%2" MAC address: "%3") + Комп'ютер «%1» (адреса вузла: «%2» MAC-адреса: «%3») + + + Unclassified object "%1" with ID "%2" + Некласифікований об'єкт «%1» із ідентифікатором «%2» + + + None + Немає + + + Computer + Комп'ютер + + + Root + Корінь + + + Invalid + Некоректний + + + Error while parsing line %1. + Помилка під час обробки рядка %1. + + + Network object directory which stores objects in local configuration + Каталог об'єктів мережі, де зберігаються об'єкти у локальній конфігурації + + + Commands for managing the builtin network object directory + Команди для керування каталогом вбудованих об'єктів мережі + + + No format string or regular expression specified! + Немає рядка форматування або не вказано формальний вираз! + + + Can't open file "%1" for writing! + Не вдалося відкрити файл «%1» для запису! + + + No format string specified! + Не вказано рядок формату! + + + Object UUID + UUID об'єкта + + + Parent UUID + Батьківський UUID + + + Add a location or computer + Додати місце або комп'ютер + + + Clear all locations and computers + Вилучити усі дані місць та комп'ютерів + + + Dump all or individual locations and computers + Створити дамп усіх або окремих місць і комп'ютерів + + + List all locations and computers + Вивести список усіх місць і комп'ютерів + + + Remove a location or computer + Вилучити запис місця або комп'ютера + + + Location "%1" + Місце «%1» + + + Builtin (computers and locations in local configuration) + Вбудоване (комп'ютери і місця у локальних налаштуваннях) + + + Location + Місце + + + FILE + ФАЙЛ + + + LOCATION + МІСЦЕ + + + FORMAT-STRING-WITH-PLACEHOLDERS + РЯДОК-ФОРМАТУВАННЯ-ІЗ-ЗАМІННИКАМИ + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + ФОРМАЛЬНИЙ-ВИРАЗ-ІЗ-ЗАМІННИКОМ + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + Імпортує об'єкти із вказаного текстового файла з використанням вказаного рядка форматування або формального виразу, що містять один або декілька замінників. Чинними замінниками є такі: %1 + + + Import simple CSV file to a single room + Імпортувати простий файл CSV до окремого запису класу + + + Import CSV file with location name in first column + Імпортувати файл CSV із назвою місця у першому стовпчику + + + Import text file with with key/value pairs using regular expressions + Імпортувати текстовий файл із парами ключ-значення на основі формальних виразів + + + Import arbitrarily formatted data + Імпортувати довільно форматовані дані + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + Експортує об'єкти до вказаного текстового файла з використанням вказаного рядка форматування, що містять один або декілька замінників. Чинними замінниками є такі: %1 + + + Export all objects to a CSV file + Експортувати усі об'єкти до файла CSV + + + Export all computers in a specific location to a CSV file + Експортувати усі записи комп'ютерів у вказаному місці до файла CSV + + + TYPE + ТИП + + + NAME + НАЗВА + + + PARENT + БАТЬКІВСЬКИЙ ЗАПИС + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + Додає об'єкт, де %1 може бути одним із записів, «%2» або «%3». %4 можна вказати за назвою або UUID. + + + Add a room + Додати клас + + + Add a computer to room %1 + Додати комп'ютер до класу %1 + + + OBJECT + ОБʼЄКТ + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + Вилучає вказаний об'єкт з каталогу. %1 можна вказати за назвою або UUID. Вилучення запису місця призводить до вилучення усіх пов'язаних із ним записів комп'ютерів. + + + Remove a computer by name + Вилучити комп'ютер за назвою + + + Remove an object by UUID + Вилучити об'єкт за UUID + + + "Room 01" + «Клас 01» + + + "Computer 01" + «Комп'ютер 01» + + + HOST ADDRESS + АДРЕСА ВУЗЛА + + + MAC ADDRESS + MAC-АДРЕСА + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + Вбудований сервер VNC (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + Вбудований сервер VNC (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + Вузол/IP-адреса: %1 + + + Active features: %1 + Задіяні можливості: %1 + + + Online and connected + У мережі і з’єднано + + + Establishing connection + Встановлюємо з’єднання + + + Computer offline or switched off + Комп’ютер перебуває поза мережею або його вимкнено + + + Authentication failed or access denied + Не пройдено розпізнавання або заборонено доступ + + + Disconnected + Від’єднано + + + No user logged on + Немає користувачів у системі + + + Logged on user: %1 + Користувач у системі: %1 + + + Location: %1 + Місце: %1 + + + Veyon Server unreachable or not running + Сервер Veyon є недоступним або сервер не запущено + + + [no user] + [немає користувача] + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + Служба %1 %2 о %3:%4 + + + Authentication error + Помилка розпізнавання + + + Remote access + Віддалений доступ + + + User "%1" at host "%2" is now accessing this computer. + Зараз доступ до цього комп'ютера має користувач «%1» на вузлі «%2». + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + Користувач «%1» на вузлі «%2» намагався отримати доступ до цього комп'ютера, але не зміг пройти розпізнавання. + + + Access control error + Помилка керування доступом + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + Користувач «%1» на вузлі «%2» намагався отримати доступ до цього комп'ютера, але його було заблоковано через параметри керування доступом. + + + Active connections: + Активні з'єднання: + + + + ComputerManager + + User + Користувач + + + Missing network object directory plugin + Не вистачає додатка каталогу мережевих об’єктів + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + Не знайдено типового додатка каталогу мережевих об’єктів. Будь ласка, перевірте, чи правильно встановлено програму, або налаштуйте інший модуль каталогу мережевих об’єктів за допомогою засобу налаштовування %1. + + + Location detection failed + Не вдалося визначити місце + + + Computer name;Hostname;User + Назва комп’ютера;Назва вузла;Користувач + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + Не вдалося визначити місце цього комп'ютера. Це означає, що у налаштуваннях системи є проблеми. На панелі вибору комп'ютера буде показано усі місця. + + + + ComputerSelectPanel + + Computer search + Пошук комп’ютера + + + Add location + Додати місце + + + Save computer/user list + Зберегти список комп’ютерів/користувачів + + + Select output filename + Виберіть назву файла для виведених даних + + + CSV files (*.csv) + файли CSV (*.csv) + + + File error + Помилка під час роботи з файлами + + + Could not write the computer and users list to %1! Please check the file access permissions. + Не вдалося записати список комп’ютерів і користувачів до %1! Будь ласка, перевірте, чи є у вас належні права для доступу до цього файла. + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + Будь ласка, вкажіть файл наявних налаштувань для імпортування. + + + Please specify a valid filename for the configuration export. + Будь ласка, вкажіть коректну назву файла для експортування налаштувань. + + + Please specify a valid key. + Будь ласка, вкажіть коректний ключ. + + + Specified key does not exist in current configuration! + Вказаного ключа не існує у поточних налаштуваннях! + + + Please specify a valid value. + Будь ласка, вкажіть коректне значення. + + + Configure Veyon at command line + Налаштувати Veyon з командного рядка + + + Output file is not writable! + Файл для виведення даних непридатний до запису! + + + Output directory is not writable! + Каталог для виведення даних непридатний до запису! + + + Configuration file is not readable! + Не вдалося прочитати файл налаштувань! + + + Clear system-wide Veyon configuration + Вилучити загальносистемні налаштування Veyon + + + List all configuration keys and values + Список усіх ключів і значень налаштувань + + + Import configuration from given file + Імпортувати налаштування із вказаного файла + + + Export configuration to given file + Експортувати налаштування до вказаного файла + + + Read and output configuration value for given key + Прочитати і вивести значення налаштування для вказаного ключа + + + Write given value to given configuration key + Записати вказане значення до вказаного ключа налаштування + + + Unset (remove) given configuration key + Скинути (вилучити) вказаний ключ налаштування + + + Commands for managing the configuration of Veyon + Команди для керування налаштуваннями Veyon + + + Upgrade and save configuration of program and plugins + Оновити і зберегти налаштування програми і додатків + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + Не вдалося змінити параметр автоматичного запуску служби %1. + + + Could not configure the firewall configuration for the %1 Server. + Не вдалося налаштувати брандмауер для сервера %1. + + + Could not configure the firewall configuration for the %1 Worker. + Не вдалося налаштувати брандмауер для робочої станції %1. + + + Configuration is not writable. Please check your permissions! + Файл налаштувань непридатний до запису. Будь ласка, перевірте права доступу до нього! + + + Could not apply platform-specific configuration settings. + Не вдалося застосувати специфічні для платформи параметри налаштувань. + + + + DemoClient + + %1 Demo + Демонстрація %1 + + + + DemoConfigurationPage + + Demo server + Демосервер + + + Tunables + Налаштовувані + + + ms + мс + + + Key frame interval + Інтервал між ключовими кадрами + + + Memory limit + Межа пам’яті + + + MB + МБ + + + Update interval + Інтервал оновлення + + + s + с + + + Slow down thumbnail updates while demo is running + Уповільнити оновлення мініатюр, доки запущено демонстрацію + + + + DemoFeaturePlugin + + Stop demo + Припинити демонстрацію + + + Window demo + Демо у вікні + + + Give a demonstration by screen broadcasting + Виконати демонстрацію трансляцією зображення на екрані + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + У цьому режимі зображення з вашого екрана буде показано у вікні на всіх комп’ютерах. Користувачі, якщо захочуть, матимуть змогу перемикатися на інші вікна. + + + Demo + Демонстрація + + + Share your screen or allow a user to share his screen with other users. + Оприлюдніть зображення з вашого екрана або дозвольте користувачам оприлюднювати зображення з їхнього екрана для інших користувачів. + + + Full screen demo + Повноекранне демо + + + Share your own screen in fullscreen mode + Оприлюднити ваш екран у повноекранному режимі + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + У цьому режимі зображення з екрана вашого комп'ютера демонструватиметься на увесь екран на усіх комп'ютерах, а пристрої введення даних на комп'ютерах буде заблоковано. + + + Share your own screen in a window + Оприлюднити ваш екран у вікні + + + Share selected user's screen in fullscreen mode + Оприлюднити екран вибраного користувача у повноекранному режимі + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + У цьому режимі зображення з екрана комп'ютера вибраного користувача демонструватиметься на увесь екран на усіх комп'ютерах, а пристрої введення даних на комп'ютерах буде заблоковано. + + + Share selected user's screen in a window + Оприлюднити екран вибраного користувача у вікні + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + У цьому режимі зображення з екрана вибраного користувача буде показано у вікні на всіх комп'ютерах. Користувачі, якщо захочуть, матимуть змогу перемикатися на інші вікна. + + + Please select a user screen to share. + Будь ласка, виберіть екран користувача для оприлюднення. + + + Please select only one user screen to share. + Будь ласка, виберіть лише один екран користувача для оприлюднення. + + + All screens + Усі екрани + + + Screen %1 [%2] + Екран %1 [%2] + + + + DesktopAccessDialog + + Desktop access dialog + Вікно доступу до стільниці + + + Confirm desktop access + Підтвердження доступу + + + Never for this session + Ніколи протягом цього сеансу + + + Always for this session + Завжди протягом цього сеансу + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + Користувач %1 з комп’ютера %2 бажає отримати доступ до вашої системи. Надасте йому доступ? + + + + DesktopServicesConfigurationPage + + Programs & websites + Програми і сайти + + + Predefined programs + Попередньо визначені програми + + + Name + Назва + + + Path + Шлях + + + Add new program + Додати нову програму + + + Remove selected program + Вилучити позначену програму + + + Predefined websites + Попередньо визначені сайти + + + Remove selected website + Вилучити позначений сайт + + + URL + Адреса + + + New program + Нова програма + + + New website + Новий сайт + + + + DesktopServicesFeaturePlugin + + Run program + Виконати програму + + + Open website + Відкрити сайт + + + Click this button to open a website on all computers. + Натисніть цю кнопку, щоб відкрити сайт на усіх комп’ютерах. + + + Start programs and services in user desktop + Запустити програми і служби на робочій станції користувача + + + Click this button to run a program on all computers. + Натисніть цю кнопку, щоб запустити програму на усіх комп’ютерах. + + + Run program "%1" + Виконати програму «%1» + + + Custom program + Нетипова програма + + + Open website "%1" + Відкрити сайт «%1» + + + Custom website + Нетиповий сайт + + + + DocumentationFigureCreator + + Teacher + Вчитель + + + Room %1 + Клас %1 + + + Please complete all tasks within the next 5 minutes. + Будь ласка, завершіть усі завдання протягом наступних 5 хвилин. + + + Custom website + Нетиповий сайт + + + Open file manager + Відкрити програму для керування файлами + + + Start learning tool + Запустити інструмент навчання + + + Play tutorial video + Відтворити навчальне відео + + + Custom program + Нетипова програма + + + Handout + Безкоштовний зразок + + + Texts to read + Фрагменти тексту для читання + + + generic-student-user + типовий-користувач-учень + + + + ExternalVncServer + + External VNC server + Зовнішній сервер VNC + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + Налаштування зовнішнього сервера VNC + + + Port: + Порт: + + + Password: + Пароль: + + + + FeatureControl + + Feature control + Керування можливосями + + + + FileTransferConfigurationPage + + File transfer + Передавання файлів + + + Directories + Каталоги + + + Destination directory + Каталог призначення + + + Default source directory + Типовий каталог початкових даних + + + Options + Параметри + + + Remember last source directory + Запам'ятовувати останній каталог початкових даних + + + Create destination directory if it does not exist + Створювати каталог призначення, якщо його не існує + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + Не вдалося відкрити файл «%1» для читання! Будь ласка, перевірте, чи маєте ви достатні права доступу! + + + + FileTransferDialog + + File transfer + Передавання файлів + + + Options + Параметри + + + Transfer only + Лише передавання + + + Transfer and open file(s) with associated program + Передати і відкрити файли за допомогою пов'язаної програми + + + Transfer and open destination folder + Передати і відкрити теку призначення + + + Files + Файли + + + Start + Почати + + + Overwrite existing files + Перезаписати наявні файли + + + + FileTransferPlugin + + File transfer + Передавання файлів + + + Click this button to transfer files from your computer to all computers. + Натисніть цю кнопку, щоб передати файли з вашого комп'ютера на усі комп'ютери. + + + Select one or more files to transfer + Виберіть один або декілька файлів для передавання + + + Transfer files to remote computer + Передати файли на інший комп'ютер + + + Received file "%1". + Отримано файл «%1». + + + Could not receive file "%1" as it already exists. + Не вдалося отримати файл «%1», оскільки такий файл вже існує. + + + Could not receive file "%1" as it could not be opened for writing! + Не вдалося отримати файл «%1», оскільки не вдалося відкрити відповідний файл для запису даних! + + + + GeneralConfigurationPage + + User interface + Інтерфейс користувача + + + Language: + Мова: + + + Use system language setting + Параметри мови системи + + + Veyon + Veyon + + + Logging + Ведення журналу + + + Log file directory + Каталог файла журналу + + + Log level + Рівень журналювання + + + Nothing + Нічого не записувати + + + Only critical messages + Лише критичні повідомлення + + + Errors and critical messages + Помилки і критичні повідомлення + + + Warnings and errors + Попередження і помилки + + + Information, warnings and errors + Сповіщення, попередження і помилки + + + Debug messages and everything else + Діагностичні повідомлення та все інше + + + Limit log file size + Обмежити розмір файла журналу + + + Clear all log files + Спорожнити всі файли журналу + + + Log to standard error output + Виводити повідомлення до стандартного виводу помилок + + + Network object directory + Каталог мережевих об’єктів + + + Backend: + Модуль: + + + Update interval: + Інтервал оновлення: + + + %1 service + Служба %1 + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + Щоб вилучити файли журналів, роботу служби %1 доведеться тимчасово призупинити. Продовжити виконання цього завдання? + + + Log files cleared + Файли журналу вилучено + + + All log files were cleared successfully. + Всі файли журналу було успішно вилучено. + + + Error + Помилка + + + Could not remove all log files. + Не вдалося вилучити всі файли журналу. + + + MB + МБ + + + Rotate log files + Освіжити файли журналу + + + x + x + + + seconds + секунд + + + Write to logging system of operating system + Записувати до журналу операційної системи + + + Authentication + Розпізнавання + + + Method: + Метод: + + + Logon authentication + Розпізнавання під час входу + + + Key file authentication + Розпізнавання за файлами ключів + + + Test + Перевірити + + + Authentication is set up properly on this computer. + На цьому комп'ютері належним чином налаштовано розпізнавання. + + + Authentication keys are not set up properly on this computer. + На цьому комп'ютері ключі розпізнавання налаштовано не так, як слід. + + + Authentication test + Перевірка розпізнавання + + + + HeadlessVncServer + + Headless VNC server + Автоматичний сервер VNC + + + + LdapBrowseDialog + + Browse LDAP + Навігація LDAP + + + + LdapClient + + LDAP error description: %1 + Опис помилки LDAP: %1 + + + + LdapConfigurationPage + + Basic settings + Основні параметри + + + General + Загальні + + + LDAP server and port + Сервер і порт LDAP + + + Bind DN + DN для прив'язки + + + Bind password + Пароль прив’язки + + + Anonymous bind + Анонімна прив’язка + + + Use bind credentials + Реєстраційні дані прив’язки + + + Base DN + Кореневий DN + + + Fixed base DN + Фіксований кореневий DN + + + e.g. dc=example,dc=org + наприклад, dc=example,dc=org + + + Discover base DN by naming context + Визначити кореневий DN за контекстом назв + + + e.g. namingContexts or defaultNamingContext + наприклад, namingContexts або defaultNamingContext + + + Environment settings + Налаштування середовища + + + Object trees + Ієрархії об’єктів + + + Computer tree + Ієрархія комп’ютерів + + + e.g. OU=Groups + наприклад, OU=Groups + + + User tree + Ієрархія користувачів + + + e.g. OU=Users + наприклад, OU=Users + + + e.g. OU=Computers + наприклад, OU=Computers + + + Group tree + Ієрархія груп + + + Perform recursive search operations in object trees + Виконати рекурсивні дії з пошуку у ієрархіях об’єктів + + + Object attributes + Атрибути об’єкта + + + e.g. hwAddress + наприклад, hwAddress + + + e.g. member or memberUid + наприклад, member або memberUid + + + e.g. dNSHostName + наприклад, dNSHostName + + + Computer MAC address attribute + Атрибут MAC-адреси комп’ютера + + + Group member attribute + Атрибут членства у групі + + + e.g. uid or sAMAccountName + наприклад, uid або sAMAccountName + + + Advanced settings + Додаткові параметри + + + Optional object filters + Додаткові фільтри об’єктів + + + Filter for user groups + Фільтрування за групами користувачів + + + Filter for users + Фільтрування за користувачами + + + Filter for computer groups + Фільтрування за групами комп’ютерів + + + Group member identification + Ідентифікація учасників групи + + + Distinguished name (Samba/AD) + Унікальна назва (Samba/AD) + + + List all groups of a user + Список усіх груп користувача + + + List all groups of a computer + Список усіх груп комп’ютера + + + Get computer object by IP address + Отримати об’єкт комп’ютера за IP-адресою + + + LDAP connection failed + Невдала спроба встановити LDAP-з’єднання + + + LDAP bind failed + Помилка прив’язування до LDAP + + + LDAP bind successful + Успішне прив’язування до LDAP + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + Виконано успішне з’єднання із сервером LDAP і прив’язування LDAP. Належним чином налаштовано основні параметри LDAP. + + + LDAP base DN test failed + Не вдалося пройти перевірку кореневого DN LDAP + + + LDAP base DN test successful + Успішна перевірка кореневого DN LDAP + + + LDAP naming context test failed + Не вдалося пройти перевірку контексту іменування LDAP + + + LDAP naming context test successful + Успішно пройдено перевірку контексту іменування LDAP + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + Успішно виконано опитування контексту іменування LDAP. Виявлено такий кореневий DN: +%1 + + + user tree + ієрархія користувачів + + + group tree + ієрархія груп + + + computer tree + ієрархія комп’ютерів + + + Enter username + Введіть ім’я користувача + + + Please enter a user login name (wildcards allowed) which to query: + Вкажіть запис користувача (можна використовувати символи-замінники), дані якого слід отримати: + + + user objects + об’єкти користувачів + + + Enter group name + Введіть назву групи + + + Please enter a group name whose members to query: + Будь ласка, вкажіть назву групи, учасників якої слід визначити: + + + group members + учасники групи + + + Group not found + Групи не знайдено + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + Не вдалося знайти групу із назвою «%1». Будь ласка, перевірте, чи правильно вказано назву групи або параметр ієрархії груп. + + + Enter computer name + Вкажіть назву комп’ютера + + + computer objects + об’єкти комп’ютерів + + + Enter computer DN + Вкажіть DN комп’ютера + + + Please enter the DN of a computer whose MAC address to query: + Будь ласка, вкажіть DN комп’ютера, запит щодо MAC-адреси якого слід надіслати: + + + computer MAC addresses + MAC-адреси комп’ютерів + + + users + користувачі + + + user groups + групи користувачів + + + computer groups + групи комп’ютерів + + + Please enter a user login name whose group memberships to query: + Будь ласка, вкажіть назву запису користувача, для кого слід отримати дані щодо участі у групах: + + + groups of user + групи користувача + + + User not found + Користувача не знайдено + + + groups of computer + групи комп’ютера + + + Computer not found + Комп’ютер не знайдено + + + Enter computer IP address + Вкажіть IP-адресу комп’ютера + + + Please enter a computer IP address which to resolve to an computer object: + Будь ласка, вкажіть IP-адресу комп’ютера, за якою слід визначити об’єкт комп’ютера: + + + computers + комп'ютери + + + LDAP %1 test failed + Помилка тестування LDAP %1 + + + LDAP %1 test successful + Успішне тестування LDAP %1 + + + The %1 has been queried successfully and %2 entries were found. + Успішно опитано %1, виявлено %2 записів. + + + %1 %2 have been queried successfully: + +%3 + %1 %2 успішно опитано: + +%3 + + + LDAP filter test failed + Помилка тестування фільтрування LDAP + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + Не вдалося виконати опитування для жодного %1 з використанням налаштованого фільтрування. Будь ласка, перевірте, чи правильно вказано фільтр LDAP для %1. + +%2 + + + LDAP filter test successful + Успішна перевірка фільтрування LDAP + + + %1 %2 have been queried successfully using the configured filter. + %1 %2 було успішно опитано за допомогою налаштованого фільтра. + + + (only if different from group tree) + (лише якщо відмінне від ієрархії груп) + + + Computer group tree + Ієрархія груп комп’ютерів + + + computer group tree + ієрархії груп комп’ютерів + + + Filter for computers + Фільтр для комп’ютерів + + + e.g. room or computerLab + наприклад, room або computerLab + + + Integration tests + Перевірки інтеграції + + + Computer groups + Групи комп'ютерів + + + e.g. name or description + наприклад назва чи опис + + + Filter for computer containers + Фільтр для контейнерів комп'ютерів + + + Computer containers or OUs + Контейнери комп'ютерів або OU + + + Connection security + Захист з'єднання + + + TLS certificate verification + Перевірка сертифіката TLS + + + System defaults + Типове для системи + + + Never (insecure!) + Ніколи (небезпечно!) + + + Custom CA certificate file + Нетиповий файл служби сертифікації (CA) + + + None + Немає + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + наприклад (objectClass=computer) + + + e.g. (objectClass=group) + наприклад (objectClass=group) + + + e.g. (objectClass=person) + наприклад (objectClass=person) + + + e.g. (objectClass=room) or (objectClass=computerLab) + наприклад (objectClass=room) або (objectClass=computerLab) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + наприклад (objectClass=container) або (objectClass=organizationalUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + Не вдалося опитати налаштований базовий DN. Будь ласка, перевірите, чи правильно вказано параметр базового DN. + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + Успішно опитано базовий DN LDAP. Знайдено такі записи: + +%1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + Не вдалося опитати базовий DN за контекстами іменування. Будь ласка, перевірите, чи правильно вказано параметр атрибута контексту іменування. + +%1 + + + Certificate files (*.pem) + файли сертифікатів (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + Не вдалося з'єднатися із сервером LDAP. Будь ласка, перевірте, чи правильно вказано параметри сервера. + +%1 + + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + Не вдалося прив'язатися до сервера LDAP. Будь ласка, перевірте, чи правильно вказано параметри сервера та реєстраційні дані прив'язки. + +%1 + + + Encryption protocol + Протокол шифрування + + + Computer location attribute + Атрибут місця комп'ютера + + + Computer display name attribute + Атрибут показаної назви комп'ютера + + + Location name attribute + Атрибут назви місця + + + e.g. cn or displayName + наприклад cn або displayName + + + Computer locations identification + Ідентифікація місць комп'ютерів + + + Identify computer locations (e.g. rooms) via: + Ідентифікувати місця комп'ютерів (наприклад класи) на основі: + + + Location attribute in computer objects + Атрибут місця у об'єктах комп'ютерів + + + List all entries of a location + Вивести список усіх записів місця + + + List all locations + Вивести список усіх місць + + + Enter computer display name + Введіть показану назву комп'ютера + + + Please enter a computer display name to query: + Будь ласка, вкажіть показану назву комп’ютера для запиту: + + + Enter computer location name + Введіть назву місця комп'ютера + + + Please enter the name of a computer location (wildcards allowed): + Введіть назву місця комп'ютера (можна використовувати символи-замінники): + + + computer locations + місця комп'ютерів + + + Enter location name + Вкажіть назву місця + + + Please enter the name of a location whose entries to query: + Будь ласка, вкажіть назву місця, запит до учасників якого слід виконати: + + + location entries + записи місць + + + LDAP test failed + Не пройдено перевірку LDAP + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + Не вдалося виконати опитування для жодного %1. Будь ласка, перевірте, чи правильно вказано параметри %2, або вкажіть назву наявного об’єкта. + +%3 + + + and + і + + + LDAP test successful + Перевірку LDAP пройдено + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + Не вдалося опитати жодний із записів у налаштованій %1. Будь ласка, перевірте, чи правильно вказано параметр «%2». + +%3 + + + Browse + Навігація + + + Test + Перевірити + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + Назви вузлів збережено як повні доменні назви (FQDN, наприклад myhost.example.org) + + + Computer hostname attribute + Атрибут назви вузла комп’ютера + + + Please enter a computer hostname to query: + Будь ласка, вкажіть назву вузла комп’ютера для запиту: + + + Invalid hostname + Некоректна назва вузла + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + Ви налаштували програму на зберігання повних доменних назв вузлів комп’ютерів (FQDN), але вказали назву вузла без домену. + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + Ви налаштували програму на зберігання простих назв вузлів комп’ютерів, але вказали назву вузла разом із назвою домену. + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + Не вдалося знайти користувача із іменем «%1». Будь ласка, перевірте, чи правильно вказано ім’я користувача або параметр ієрархії користувачів. + + + Enter hostname + Введіть назву вузла + + + Please enter a computer hostname whose group memberships to query: + Будь ласка, вкажіть назву вузла комп’ютера, для кого слід отримати дані щодо участі у групах: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + Не вдалося знайти комп’ютер із назвою вузла «%1». Будь ласка, перевірте, чи правильно вказано назву вузла або параметр ієрархії комп’ютерів. + + + Hostname lookup failed + Помилка пошуку вузла за назвою + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + Не вдалося виконати пошук назви вузла для IP-адреси %1. Будь ласка, перевірте, чи правильно вказано параметри вашого сервера DNS. + + + User login name attribute + Атрибут імені користувача + + + Configured attribute for user login name or computer hostname (OpenLDAP) + Налаштований атрибут для імені користувача або назви вузла комп’ютера (OpenLDAP) + + + computer containers + контейнери комп'ютерів + + + + LdapPlugin + + Auto-configure the base DN via naming context + Автоматичне налаштовування базового DN за контекстном назви + + + Query objects from LDAP directory + Опитати об’єкти з каталогу LDAP + + + Show help about command + Показати довідку щодо команди + + + Commands for configuring and testing LDAP/AD integration + Команди для налаштовування і тестування інтеграції із LDAP/AD + + + Basic LDAP/AD support for Veyon + Базова підтримка LDAP/AD у Veyon + + + %1 (load computers and locations from LDAP/AD) + %1 (завантажити записи комп'ютерів та місця з LDAP/AD) + + + %1 (load users and groups from LDAP/AD) + %1 (завантажити записи користувачів та груп з LDAP/AD) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + Будь ласка, вкажіть коректну адресу LDAP у такому форматі: "ldap[s]://[користувач[:пароль]@]назва_вузла[:порт]" + + + No naming context attribute name given - falling back to configured value. + Не вказано атрибута контексту іменування — повертаємося до налаштованого значення. + + + Could not query base DN. Please check your LDAP configuration. + Не вдалося обробити запит щодо кореневого DN. Будь ласка, перевірте, чи правильно налаштовано LDAP. + + + Configuring %1 as base DN and disabling naming context queries. + Налаштовуємо %1 як кореневий DN і вимикаємо запити щодо контексту іменування. + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + Нетипова служба PAM для розпізнавання користувачів + + + User authentication + Розпізнавання користувача + + + Session management + Керування сеансами + + + Display manager users + Користувачі керування дисплеєм + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + Додаток, який реалізуває абстрактні функції на платформі Linux + + + + LocationDialog + + Select location + Вибрати місце + + + enter search filter... + введіть фільтр пошуку… + + + + MainToolBar + + Configuration + Налаштування + + + Disable balloon tooltips + Вимкнути панелі підказок + + + Show icons only + Показати лише піктограми + + + + MainWindow + + MainWindow + Головне вікно + + + toolBar + Панель інструментів + + + General + Загальні + + + &File + &Файл + + + &Help + &Довідка + + + &Quit + Ви&йти + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + З&авантажити параметри з файла + + + Ctrl+O + Ctrl+O + + + About Qt + Про Qt + + + Authentication impossible + Розпізнавання неможливе + + + Configuration not writable + Не вдалося записати налаштування + + + Load settings from file + Завантажити параметри з файла + + + Save settings to file + Зберегти параметри до файла + + + Unsaved settings + Незбережені параметри + + + There are unsaved settings. Quit anyway? + Деякі з параметрів не збережено. Завершити роботу попри це? + + + Veyon Configurator + Засіб налаштовування Veyon + + + Service + Служба + + + Master + Основний + + + Access control + Керування доступом + + + About Veyon + Про Veyon + + + Auto + Авто + + + About + Про програму + + + %1 Configurator %2 + Засіб налаштовування %1 %2 + + + JSON files (*.json) + файли JSON (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + Модуль керування локальними налаштуваннями повідомив, що не вдалося виконати запис до файла налаштувань! Будь ласка, запустіть Засіб налаштовування %1 із ширшими правами доступу. + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + Не знайдено файлів ключів розпізнавання або строк дії знайдених ключів вичерпано. Будь ласка, створіть нові файли ключів за допомогою засобу налаштовування %1. Крім того, ви можете налаштувати розпізнавання за іменем користувача під час входу до системи, скориставшись для цього засобом налаштовування %1. Якщо ви не створите ключів і не налаштуєте розпізнавання, ви не зможете отримувати доступ до комп’ютерів за допомогою %1. + + + Access denied + Доступ заборонено + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + Відповідно до локальних налаштувань вам заборонено доступ до комп’ютерів у мережі. Будь ласка, увійдіть до іншого облікового запису або попросіть адміністратора вашої системи змінити локальні налаштування відповідним чином. + + + Screenshots + Знімки вікон + + + Feature active + Задіяно можливість + + + The feature "%1" is still active. Please stop it before closing %2. + Задіяно можливість «%1». Будь ласка, вимкніть її, перш ніж завершувати роботу %2. + + + Reset configuration + Скинути налаштування + + + Do you really want to reset the local configuration and revert all settings to their defaults? + Ви справді хочете скинути локальні налаштування і повернути усі параметри до типових значень? + + + Search users and computers + Пошук користувачів і комп'ютерів + + + Align computers to grid + Вирівняти комп'ютери за ґраткою + + + %1 Configurator + Засіб налаштовування %1 + + + Insufficient privileges + Недостатні права доступу + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + Не вдалося запустити програму із правами доступу адміністратора. Будь ласка, переконайтеся, що у робочому середовищі встановлено sudo-подібну програму! Програму буде запущено із правами доступу звичайного користувача. + + + Only show powered on computers + Показувати лише увімкнені комп'ютери + + + &Save settings to file + З&берегти параметри до файла + + + &View + П&ерегляд + + + &Standard + С&тандартний + + + &Advanced + &Розширений + + + Use custom computer arrangement + Нетипове розташування комп'ютерів + + + Locations && computers + Місця && комп'ютери + + + Slideshow + Показ слайдів + + + Spotlight + Акцент + + + Adjust size of computer icons automatically + Автоматично коригувати розмір піктограм комп'ютерів + + + + MasterConfigurationPage + + Directories + Каталоги + + + User configuration + Налаштування користувачів + + + Feature on computer double click: + Дія у відповідь на подвійне клацання на комп’ютері: + + + Features + Можливості + + + All features + Усі можливості + + + Disabled features + Вимкнені можливості + + + Screenshots + Знімки вікон + + + <no feature> + <no feature> + + + Basic settings + Основні параметри + + + Behaviour + Поведінка + + + Enforce selected mode for client computers + Примусовий вибраний режим для комп'ютерів-клієнтів + + + Hide local computer + Приховати локальний комп'ютер + + + Hide computer filter field + Приховати поле фільтрування комп'ютерів + + + Actions such as rebooting or powering down computers + Дії, зокрема перезавантаження та вимикання комп'ютерів + + + User interface + Інтерфейс користувача + + + Background color + Колір тла + + + Thumbnail update interval + Інтервал оновлення мініатюри + + + ms + мс + + + Program start + Запуск програми + + + Modes and features + Режими і можливості + + + User and computer name + Користувач і назва комп'ютера + + + Only user name + Лише ім'я користувача + + + Only computer name + Лише назва комп'ютера + + + Computer thumbnail caption + Підпис мініатюри комп'ютера + + + Text color + Колір тексту + + + Sort order + Критерій упорядковування + + + Computer and user name + Комп'ютер та ім'я користувача + + + Computer locations + Місця комп'ютерів + + + Show current location only + Показати лише поточне місце + + + Allow adding hidden locations manually + Дозволити додавання прихованих місць вручну + + + Hide empty locations + Приховати порожні місця + + + Show confirmation dialog for potentially unsafe actions + Показувати вікно підтвердження для потенційно небезпечних дій + + + Perform access control + Виконати керування доступом + + + Automatically select current location + Автоматично вибирати поточне місце + + + Automatically open computer select panel + Автоматично відкрити панель вибору комп'ютера + + + Hide local session + Приховати локальний сеанс + + + px + пк + + + Thumbnail spacing + Інтервали між мініатюрами + + + Auto + Авто + + + Thumbnail aspect ratio + Співвідношення розмірів мініатюри + + + Automatically adjust computer icon size + Автоматично коригувати розмір піктограм комп'ютерів + + + Open feature windows on the same screen as the main window + Відкривати вікна додатків на тому самому екрані, що і головне вікно + + + + MonitoringMode + + Monitoring + Спостереження + + + Builtin monitoring mode + Вбудований режим стеження + + + This mode allows you to monitor all computers at one or more locations. + У цьому режимі ви можете спостерігати за усіма комп'ютерами у одному або декількох місцях. + + + + NetworkObjectTreeModel + + Locations/Computers + Місця/Комп'ютери + + + + OpenWebsiteDialog + + Open website + Відкрити сайт + + + e.g. Veyon + наприклад Veyon + + + Remember and add to website menu + Запам'ятати і додати до меню сайтів + + + e.g. www.veyon.io + наприклад www.veyon.io + + + Please enter the URL of the website to open: + Будь ласка, вкажіть адресу сайта, який слід відкрити: + + + Name: + Назва: + + + + PasswordDialog + + Username + Ім’я користувача + + + Password + Пароль + + + Veyon Logon + Вхід до Veyon + + + Authentication error + Помилка розпізнавання + + + Logon failed with given username and password. Please try again! + Не вдалося увійти на основі вказаних імені користувача і пароля. Будь ласка, повторіть спробу! + + + Please enter your username and password in order to access computers. + Будь ласка, вкажіть ваше ім’я користувача та пароль, щоб отримати доступ до комп’ютерів. + + + + PowerControlFeaturePlugin + + Power on + Увімкнути + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + Натисніть цю кнопку, щоб увімкнути усі комп’ютери. Якщо ви нею скористаєтеся, вам не доведеться вмикати кожен комп’ютер вручну. + + + Reboot + Перезавантажити + + + Click this button to reboot all computers. + Натисніть цю кнопку, щоб перезавантажити усі комп’ютери. + + + Power down + Вимкнути + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + Натисніть цю кнопку, щоб вимкнути усі комп’ютери. Якщо ви нею скористаєтеся, вам не доведеться вимикати кожен комп’ютер вручну. + + + Power on/down or reboot a computer + Увімкнути/Вимкнути або перезавантажити комп’ютер + + + Confirm reboot + Підтвердження перезавантаження + + + Confirm power down + Підтвердження вимикання + + + Do you really want to reboot the selected computers? + Ви справді хочете перезавантажити позначені комп'ютери? + + + Do you really want to power down the selected computer? + Ви справді хочете вимкнути позначений комп'ютер? + + + Power on a computer via Wake-on-LAN (WOL) + Увімкнути комп'ютер за допомогою Wake-on-LAN (WOL) + + + MAC ADDRESS + MAC-АДРЕСА + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + Ця команда транслює пакет Wake-on-LAN (WOL) до мережі з метою вмикання живлення на комп'ютері із вказаною MAC-адресою. + + + Please specify the command to display help for! + Будь ласка, вкажіть команду, для якої слід показати довідку! + + + Invalid MAC address specified! + Вказано некоректну MAC-адресу! + + + Commands for controlling power status of computers + Команди для керування станом живлення комп'ютерів + + + Power down now + Вимкнути зараз + + + Install updates and power down + Встановити оновлення і вимкнути + + + Power down after user confirmation + Вимкнути після підтвердження + + + Power down after timeout + Вимкнути по завершенню часу очікування + + + The computer was remotely requested to power down. Do you want to power down the computer now? + Комп'ютер отримав віддалену команду щодо вимикання. Хочете вимкнути комп'ютер зараз? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + Комп'ютер буде вимкнено за %1 хвилин, %2 секунд. + +Будь ласка, збережіть результати вашої роботи і завершіть роботу усіх програм. + + + + PowerDownTimeInputDialog + + Power down + Вимкнути + + + Please specify a timeout for powering down the selected computers: + Будь ласка, вкажіть час очікування на вимикання вибраного комп'ютера. + + + minutes + хвилин + + + seconds + секунд + + + + RemoteAccessFeaturePlugin + + Remote view + Віддалений перегляд + + + Open a remote view for a computer without interaction. + Відкрити панель віддаленого перегляду комп’ютера без втручання. + + + Remote control + Віддалене керування + + + Open a remote control window for a computer. + Відкрити вікно віддаленого керування комп’ютером. + + + Remote access + Віддалений доступ + + + Remote view or control a computer + Віддалений перегляд або керування комп’ютером + + + Please enter the hostname or IP address of the computer to access: + Будь ласка, вкажіть назву вузла або IP-адресу комп’ютера, доступ до якого слід отримати: + + + Show help about command + Показати довідку щодо команди + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 — %2, віддалений доступ + + + %1 - %2 - %3 Remote Access + %1 — %2 — %3, віддалений доступ + + + + RemoteAccessWidgetToolBar + + View only + Лише перегляд + + + Remote control + Віддалене керування + + + Send shortcut + Надіслати скорочення + + + Fullscreen + На весь екран + + + Window + У вікні + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + Menu + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + З’єднання з %1 + + + Connected. + З’єднано. + + + Screenshot + Знімок вікна + + + Exit + Вийти + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + Будь ласка, вкажіть програми або команди, які слід виконати на позначених комп’ютерах. Ви можете вказати декілька програм або команд, записавши їх в окремих рядках. + + + Run programs + Виконати програми + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + наприклад, «C:\Program Files\VideoLAN\VLC\vlc.exe» + + + Name: + Назва: + + + Remember and add to program menu + Запам'ятати і додати до меню програм + + + e.g. VLC + наприклад VLC + + + + ScreenLockFeaturePlugin + + Lock + Заблокувати + + + Unlock + Розблокувати + + + Lock screen and input devices of a computer + Заблокувати екран і пристрої введення на комп’ютері + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + Щоб заволодіти увагою учнів, ви можете заблокувати їхні комп’ютери за допомогою цієї кнопки. У цьому режимі всі пристрої введення даних буде заблоковано, а екрани стануть чорними. + + + Lock input devices + Блокування пристроїв введення + + + Unlock input devices + Розблокувати пристрої введення + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + Щоб заволодіти увагою учнів, ви можете заблокувати їхні комп'ютери за допомогою цієї кнопки. У цьому режимі всі пристрої введення даних буде заблоковано, а зображення на екрані лишиться видимим. + + + + Screenshot + + unknown + невідомий + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + Не вдалося зробити знімок вікна, оскільки теки %1 не існує або її неможливо створити. + + + Screenshot + Знімок вікна + + + Could not open screenshot file %1 for writing. + Не вдалося відкрити файл знімка вікна %1 для запису даних. + + + + ScreenshotFeaturePlugin + + Screenshot + Знімок вікна + + + Use this function to take a screenshot of selected computers. + Скористайтеся цією можливістю для створення знімків екранів позначених комп’ютерів. + + + Screenshots taken + Створені знімки + + + Screenshot of %1 computer have been taken successfully. + Знімок екрана комп’ютера %1 успішно створено. + + + Take screenshots of computers and save them locally. + Створення знімків екранів комп’ютерів і їхнє локальне зберігання. + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + Тут наведено список усіх зроблених вами знімків вікон. Ви моете створювати знімки вікон, вибираючи пункт «Знімок вікна» у меню запису комп’ютера. Керувати знімками вікон можна за допомогою розташованих нижче кнопок. + + + User: + Користувач: + + + Computer: + Комп’ютер: + + + Date: + Дата: + + + Time: + Час: + + + Show + Показати + + + Delete + Вилучити + + + Screenshot + Знімок вікна + + + Do you really want to delete all selected screenshots? + Справді хочете вилучити усі позначені знімки вікон? + + + + ServiceConfigurationPage + + General + Загальні + + + Autostart + Автозапуск + + + Hide tray icon + Сховати піктограму у лотку + + + Start service + Запустити службу + + + Stopped + Зупинено + + + Stop service + Зупинити службу + + + State: + Стан: + + + Enable firewall exception + Увімкнути виключення брандмауера + + + Allow connections from localhost only + Дозволити з’єднання лише з локального вузла + + + VNC server + Сервер VNC + + + Plugin: + Додаток: + + + Restart %1 Service + Перезапустити службу %1 + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + Всі параметри було збережено. З метою набуття чинності нових параметрів слід перезапустити службу %1. Перезапусти службу зараз? + + + Running + Запущено + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + Позначення цього пункту призведе до того, що служба запускатиме процес сервера для кожного інтерактивного сеансу на комп'ютері. +Типово, таке налаштування потрібне для реалізації підтримки термінальних серверів. + + + Show notification on remote connection + Показувати сповіщення щодо віддаленого з'єднання + + + Show notification when an unauthorized access is blocked + Показувати сповіщення, коли програма блокує неуповноважений доступ + + + Sessions + Сеанси + + + Single session mode (create server instance for local/physical session only) + Режим єдиного сеансу (створювати екземпляр сервера лише для локального/фізичного сеансу) + + + Multi session mode (create server instance for each local and remote desktop session) + Режим із багатьма сеансами (створювати екземпляр сервера для кожного локального і віддаленого сеансу) + + + Maximum session count + Максимальна кількість сеансів + + + Network port numbers + Номери портів мережі + + + Veyon server + Сервер Veyon + + + Internal VNC server + Вбудований сервер VNC + + + Feature manager + Керування можливостями + + + Demo server + Демосервер + + + Miscellaneous network settings + Різноманітні параметри мережі + + + + ServiceControl + + Starting service %1 + Запускаємо службу %1 + + + Stopping service %1 + Зупиняємо службу %1 + + + Registering service %1 + Реєструємо службу %1 + + + Unregistering service %1 + Скасовуємо реєстрацію служби %1 + + + Service control + Керування службами + + + + ServiceControlPlugin + + Service is running + Службу запущено + + + Service is not running + Службу не запущено + + + Configure and control Veyon service + Налаштовування і керування службою Veyon + + + Register Veyon Service + Зареєструвати службу Veyon + + + Unregister Veyon Service + Скасувати реєстрацію служби Veyon + + + Start Veyon Service + Запустити службу Veyon + + + Stop Veyon Service + Зупинити службу Veyon + + + Restart Veyon Service + Перезапустити службу Veyon + + + Query status of Veyon Service + Визначити стан служби Veyon + + + Commands for configuring and controlling Veyon Service + Команди для налаштовування і керування службою Veyon + + + + ShellCommandLinePlugin + + Run command file + Виконати файл команди + + + File "%1" does not exist! + Файла «%1» не існує! + + + Interactive shell and script execution for Veyon Control + Інтерактивна оболонка і засіб виконання скриптів для Керування Veyon + + + Commands for shell functionalities + Команди для можливостей оболонки + + + + SlideshowPanel + + Previous + Назад + + + Start/pause + Старт/Пауза + + + Next + Далі + + + Duration: + Тривалість: + + + + SpotlightPanel + + Add selected computers + Додати позначені комп'ютери + + + Remove selected computers + Вилучити позначені комп'ютери + + + Update computers in realtime + Оновлювати комп'ютери у реальному часу + + + Spotlight + Акцент + + + Please select at least one computer to add. + Будь ласка, позначте принаймні один комп'ютер для додавання. + + + Please select at least one computer to remove. + Будь ласка, позначте принаймні один комп'ютер для вилучення. + + + Add computers by clicking with the middle mouse button or clicking the first button below. + Додайте комп'ютери клацанням середньої кнопки миші або натисканням першої кнопки нижче. + + + + SystemTrayIcon + + System tray icon + Піктограма у системному лотку + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + Модуль груп користувачів для груп користувачів системи + + + Default (system user groups) + Типовий (групи користувачів системи) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + Виконати перевірку внутрішніх компонентів і функцій Veyon + + + Commands for testing internal components and functions of Veyon + Команди для перевірки внутрішніх компонентів та функцій Veyon + + + + TextMessageDialog + + Send text message + Надіслати текстове повідомлення + + + Use the field below to type your message which will be sent to all selected users. + Використовуйте це поле, щоб набрати текст повідомлення, яке буде надіслано всім позначеним користувачам. + + + + TextMessageFeaturePlugin + + Text message + Текстове повідомлення + + + Use this function to send a text message to all users e.g. to assign them new tasks. + Скористайтеся цією можливістю для надсилання текстового повідомлення усіх користувачам, наприклад, для визначення нового завдання для користувачів. + + + Message from teacher + Повідомлення від вчителя + + + Send a message to a user + Надіслати користувачу повідомлення + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + Увімкнути захоплення шарів (напівпрозорих) вікон + + + Poll full screen (leave this enabled per default) + Повноекранний режим (залиште це типово увімкненим) + + + Low accuracy (turbo mode) + Низька точність (турбо-режим) + + + Builtin UltraVNC server configuration + Налаштування вбудованого сервера UltraVNC + + + Enable multi monitor support + Увімкнути підтримку декількох моніторів + + + Enable Desktop Duplication Engine on Windows 8 and newer + Увімкнути рушій дублювання стільниці у Windows 8 та новіших версіях + + + Maximum CPU usage + Максимальне використання процесора + + + + UserConfig + + No write access + Немає доступу на запис + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + Не вдалося зберегти ваші особисті параметри! Будь ласка, перевірте, чи правильно вказано шлях до файла налаштувань користувачів за допомогою засобу налаштовування %1. + + + + UserLoginDialog + + User login + Вхід від імені користувача + + + Please enter a username and password for automatic login on all computers. + Будь ласка, вкажіть ім'я користувача і пароль для автоматичного входу до усіх комп'ютерів. + + + Username + Ім’я користувача + + + Password + Пароль + + + + UserSessionControlPlugin + + Log in + Увійти + + + Click this button to log in a specific user on all computers. + Натисніть цю кнопку, щоб увійти від імені вказаного користувача на усі комп'ютери. + + + Log off + Викинути + + + Click this button to log off users from all computers. + Натисніть цю кнопку, щоб виконати примусовий вихід користувачів з систем усіх комп'ютерів. + + + Confirm user logoff + Підтвердження виходу користувачів + + + Do you really want to log off the selected users? + Ви справді хочете виконати вихід із системи для позначених користувачів? + + + User session control + Керування сеансами користувачів + + + + VeyonCore + + [OK] + [Гаразд] + + + [FAIL] + [ПОМИЛКА] + + + Invalid command! + Некоректна команда! + + + Available commands: + Доступні команди: + + + Invalid arguments given + Вказано некоректні аргументи + + + Not enough arguments given - use "%1 help" for more information + Вказано недостатньо аргументів — скористайтеся командою «%1 help», щоб дізнатися більше + + + Unknown result! + Невідомий результат! + + + Available modules: + Доступні модулі: + + + No module specified or module not found - available modules are: + Не вказано модуль або модуль не знайдено. Доступні модулі: + + + Plugin not licensed + Додаток не ліцензовано + + + INFO + ВІДОМОСТІ + + + ERROR + ПОМИЛКА + + + USAGE + КОРИСТУВАННЯ + + + DESCRIPTION + ОПИС + + + EXAMPLES + ПРИКЛАДИ + + + WARNING + ПОПЕРЕДЖЕННЯ + + + + VeyonServiceControl + + Veyon Service + Служба Veyon + + + + VncViewWidget + + Establishing connection to %1 ... + Встановлення зв’язку з %1 ... + + + + WebApiConfigurationPage + + Web API + Програмний вебінтерфейс + + + General + Загальні + + + Network port + Порт мережі + + + Enable WebAPI server + Увімкнути сервер WebAPI + + + Connection settings + Параметри з'єднання + + + Lifetime + Строк служби + + + h + год. + + + s + с + + + Idle timeout + Час очікування бездіяльності + + + Authentication timeout + Час очікування на розпізнавання + + + Maximum number of open connections + Максимальна кількість відкритих з'єднань + + + Connection encryption + Шифрування з'єднання + + + TLS certificate file + Файл сертифіката TLS + + + TLS private key file + Файл закритого ключа TLS + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + Використовувати HTTPS з TLS 1.3 замість HTTP + + + + WebApiPlugin + + Run WebAPI server + Запустити сервер WebAPI + + + Failed to start WebAPI server at port %1 + Не вдалося запустити сервер WebAPI на порту %1 + + + WebAPI server running at port %1 + Сервер WebAPI запущено на порту %1 + + + Provide access to a computer via HTTP + Надати доступ до комп'ютера за допомогою HTTP + + + Commands for running the WebAPI server + Команди для запуску сервера WebAPI + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + Не вдалося змінити параметр для програмного створення SAS. Віддалене надсилання Ctrl+Alt+Del не працюватиме! + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + Загальні + + + Enable SAS generation by software (Ctrl+Alt+Del) + Увімкнути програмне створення SAS (Ctrl+Alt+Del) + + + Screen lock + Блокування екрана + + + Hide taskbar + Приховати панель задач + + + Hide start menu + Приховати меню запуску + + + Hide desktop + Приховати стільницю + + + User authentication + Розпізнавання користувача + + + Use alternative user authentication mechanism + Альтернативний механізм розпізнавання користувачів + + + User login + Вхід від імені користувача + + + Input start delay + Затримка початку введення + + + Simulated key presses interval + Імітований інтервал між натисканнями клавіш + + + Confirm legal notice (message displayed before user logs in) + Підтвердити прочитання правового зауваження (повідомлення, яке показано до входу до системи) + + + Use input device interception driver + Скористатися драйвером-перехоплювачем пристроїв введення + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + Додаток, який реалізуває абстрактні функції на платформі Windows + + + + WindowsServiceControl + + The service "%1" is already installed. + Службу «%1» вже встановлено. + + + The service "%1" could not be installed. + Не вдалося встановити службу «%1». + + + The service "%1" has been installed successfully. + Службу «%1» було успішно встановлено. + + + The service "%1" could not be uninstalled. + Не вдалося вилучити службу «%1». + + + The service "%1" has been uninstalled successfully. + Службу «%1» успішно вилучено. + + + The start type of service "%1" could not be changed. + Не вдалося змінити тип запуску служби «%1». + + + Service "%1" could not be found. + Не вдалося знайти службу «%1». + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + Налаштування вбудованого сервера x11vnc + + + Custom x11vnc parameters: + Нетипові параметри x11vnc: + + + Do not use X Damage extension + Не використовувати розширення X Damage + + + diff --git a/translations/veyon_vi.ts b/translations/veyon_vi.ts new file mode 100644 index 0000000..7caa7b7 --- /dev/null +++ b/translations/veyon_vi.ts @@ -0,0 +1,4059 @@ + + + AboutDialog + + About + Giới thiệu + + + Translation + Dịch thuật + + + License + Giấy phép + + + About Veyon + Về Veyon + + + Contributors + Những người đóng góp + + + Version: + Phiên bản: + + + Website: + Website: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + Ngôn ngữ hiện tại chưa được dịch (hoặc tiếng Anh bản địa). + +Nếu bạn quan tâm đến việc dịch Veyon thành ngôn ngữ bản địa của bạn, ngôn ngữ khác hoặc muốn cải tiến bản dịch đang tồn tại, vui lòng liên hệ với một nhà phát triển Veyon! + + + About %1 %2 + Về %1%2 + + + Support Veyon project with a donation + Hỗ trợ dự án Veyon bằng cách quyên góp + + + + AccessControlPage + + Computer access control + Điều khiển truy cập máy tính + + + Grant access to every authenticated user (default) + Cấp truy cập cho mọi người dùng xác thực (mặc định) + + + Test + Kiểm tra + + + Process access control rules + Xử lý quy tắc điều khiển truy cập + + + User groups authorized for computer access + Nhóm người dùng được cấp quyền truy cập máy tính + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + Vui lòng thêm nhóm mà các thành viên sẽ được cấp quyền truy cập máy tính trong mạng Veyon của bạn. + + + Authorized user groups + Nhóm người dùng được cấp quyền + + + All groups + Tất cả các nhóm + + + Access control rules + Quy tắc điều khiển truy cập + + + Add access control rule + Thêm quy tắc điều khiển truy cập + + + Remove access control rule + Xóa quy tắc điều khiển truy cập + + + Move selected rule down + Đưa quy tắc đã chọn xuống + + + Move selected rule up + Đưa quy tắc đã chọn lên + + + Edit selected rule + Sửa quy tắc được chọn + + + Enter username + Nhập tên người dùng + + + Please enter a user login name whose access permissions to test: + Vui lòng nhập tên đăng nhập của người dùng có quyền truy cập để kiểm tra: + + + Access allowed + Truy cập được cho phép + + + The specified user is allowed to access computers with this configuration. + Người dùng đã chỉ định được phép truy cập máy tính với cấu hình này. + + + Access denied + Truy cập bị từ chối + + + The specified user is not allowed to access computers with this configuration. + Người dùng đã chỉ định không được phép truy cập máy tính với cấu hình này. + + + Enable usage of domain groups + Cho phép sử dụng nhóm miền + + + User groups backend: + Backend nhóm người dùng: + + + Missing user groups backend + Thiếu backend nhóm người dùng + + + No default user groups plugin was found. Please check your installation! + Không tìm thấy plugin nhóm người dùng mặc định. Vui lòng kiểm tra bản cài đặt của bạn! + + + Restrict access to members of specific user groups + + + + + AccessControlRuleEditDialog + + Edit access control rule + Sửa quy tắc điều khiển truy cập + + + General + Chung + + + enter a short name for the rule here + nhập tên ngắn cho quy tắc ở đây + + + Rule name: + Tên quy tắc: + + + enter a description for the rule here + nhập mô tả cho quy tắc ở đây + + + Rule description: + Mô tả quy tắc: + + + Invert all conditions ("is/has" interpreted as "is/has not") + Đảo ngược tất cả các điều kiện (có được hiểu là không) + + + Conditions + Điều kiện + + + is member of group + là thành viên của nhóm + + + Accessing computer is localhost + Máy tính đang truy cập là localhost + + + Accessing user is logged on user + Người dùng đang truy cập là người dùng đã đăng nhập + + + Accessing user is already connected + Người dùng đang truy cập đã được kết nối + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + Nếu có nhiều hơn một điều kiện kích hoạt, mỗi điều kiện phải thỏa mãn để áp dụng quy tắc (AND logic). Nếu chỉ một trong số nhiều điều kiện phải thoải mãn (OR logic) vui lòng tạo nhiều quy tắc điều khiển truy cập. + + + Action + Hành động + + + Allow access + Cho phép truy cập + + + Deny access + Từ chối truy cập + + + Ask logged on user for permission + Đề nghị người dùng đã đăng nhập cấp quyền + + + None (rule disabled) + Không (quy tắc bị vô hiệu lực) + + + Accessing user + Người dùng đang truy cập + + + Accessing computer + Máy tính đang truy cập + + + Local (logged on) user + Người dùng cục bộ (đã đăng nhập) + + + Local computer + Máy tính cục bộ + + + Always process rule and ignore conditions + Luôn xử lý quy tắc và bỏ qua các điều kiện + + + No user logged on + Không có người dùng đã đăng nhập + + + Accessing user has one or more groups in common with local (logged on) user + Người dùng đang truy cập có một hoặc nhiều nhóm chung với người dùng cục bộ (đã đăng nhập) + + + Accessing computer and local computer are at the same location + + + + is located at + + + + + AccessControlRulesTestDialog + + Access control rules test + Kiểm tra quy tắc điều khiển truy cập + + + Accessing user: + Người dùng đang truy cập: + + + Local computer: + Máy tính cục bộ: + + + Accessing computer: + Máy tính đang truy cập: + + + Please enter the following user and computer information in order to test the configured ruleset. + Vui lòng nhập thông tin người dùng và máy tính sau để kiểm tra tập quy tắc đã cấu hình. + + + Local user: + Người dùng cục bộ: + + + Connected users: + Người dùng đã kết nối: + + + The access in the given scenario is allowed. + Truy cập trong tình huống đã cho được cho phép. + + + The access in the given scenario is denied. + Truy cập trong tình huống đã cho bị từ chối. + + + The access in the given scenario needs permission of the logged on user. + Truy cập trong tình huống đã cho cần quyền của người dùng đã đăng nhập. + + + ERROR: Unknown action + LỖI: Hành động không xác định + + + Test result + Kết quả kiểm tra + + + + AuthKeysConfigurationPage + + Authentication keys + Khóa xác thực + + + Introduction + Giới thiệu + + + Key file directories + Thư mục tập tin khóa + + + Public key file base directory + Thư mục cơ sở tập tin khóa công + + + Private key file base directory + Thư mục cơ sở tập tin khóa riêng + + + Available authentication keys + Khóa xác thực khả dụng + + + Create key pair + Tặp cặp khóa + + + Delete key + Xóa khóa + + + Import key + Nhập khóa + + + Export key + Xuất khóa + + + Set access group + Đặt nhóm truy cập + + + Key files (*.pem) + Tập tin khóa (*.pem) + + + Authentication key name + Tên khóa xác thực + + + Please enter the name of the user group or role for which to create an authentication key pair: + Vui lòng nhập tên của nhóm người dùng hoặc vai trò để tạo một cặp khóa xác thực: + + + Do you really want to delete authentication key "%1/%2"? + Bạn có thực sự muốn xóa khóa xác thực "%1/%2" không? + + + Please select a key to delete! + Vui lòng chọn một khóa để xóa! + + + Please enter the name of the user group or role for which to import the authentication key: + Vui lòng nhập tên của nhóm người dùng hoặc vai trò để nhập khóa xác thực: + + + Please select a key to export! + Vui lòng chọn một khóa để xuất! + + + Please select a user group which to grant access to key "%1": + Vui lòng chọn một nhóm người dùng để cấp truy cập cho khóa "%1": + + + Please select a key which to set the access group for! + Vui lòng chọn một khóa để đặt nhóm truy cập! + + + Please perform the following steps to set up key file authentication: + Vui lòng thực hiện các bước sau để thiết lập xác thực tập tin khóa: + + + 1) Create a key pair on the master computer. + 1) Tạo một cặp khóa trên máy tính chủ. + + + 2) Set an access group whose members should be allowed to access other computers. + 2) Thiết lập một nhóm truy cập mà các thành viên sẽ được phép truy cập vào các máy tính khác. + + + 3) Export the public key and import it on all client computers with the same name. + 3) Xuất khóa công và nhập nó trên tất cả các máy tính khách với cùng tên. + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + Vui lòng tham khảo <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Hướng dẫn quản trị Veyon</a>để biết thêm thông tin. + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + Một cặp khóa xác thực bao gồm hai khóa mật mã, một khóa riêng và một khóa công. +Một khóa riêng cho phép người dùng trên máy tính chủ truy cập vào các máy tính khách. +Điều quan trọng đó là chỉ những người dùng được cấp quyền mới có truy cập đọc vào tập tin khóa riêng. +Khóa công được sử dụng trên các máy tính khách để xác thực yêu cầu kết nối đến. + + + + AuthKeysManager + + Please check your permissions. + Vui lòng kiểm tra quyền của bạn. + + + Key name contains invalid characters! + Tên khóa chứa ký tự không hợp lệ! + + + Invalid key type specified! Please specify "%1" or "%2". + Đã chỉ định kiểu khóa không hợp lệ! Vui lòng chỉ định "%1" hoặc "%2". + + + Specified key does not exist! Please use the "list" command to list all installed keys. + Khóa đã chỉ định không tồn tại! Vui lòng sử dụng lệnh "list" để liệt kê tất cả các khóa đã cài đặt. + + + One or more key files already exist! Please delete them using the "delete" command. + Một hoặc nhiều tập tin khóa đã tồn tại! Vui lòng xóa chúng sử dụng lệnh "delete". + + + Creating new key pair for "%1" + Tạo cặp khóa mới cho "%1" + + + Failed to create public or private key! + Thất bại khi tạo khóa công hoặc khóa riêng! + + + Newly created key pair has been saved to "%1" and "%2". + Cặp khóa mới được tạo đã được lưu vào "%1" và "%2". + + + Could not remove key file "%1"! + Không thể xóa tập tin khóa "%1"! + + + Could not remove key file directory "%1"! + Không thể xóa thư mục tập tin khóa "%1"! + + + Failed to create directory for output file. + Thất bại khi tạo thư mục cho tệp ra. + + + File "%1" already exists. + Tập tin "%1" đã tồn tại. + + + Failed to write output file. + Thất bại khi ghi tập tin ra. + + + Key "%1/%2" has been exported to "%3" successfully. + Khóa "%1/%2" đã được xuất ra "%3" thành công. + + + Failed read input file. + Thất bại khi đọc tập tin vào + + + File "%1" does not contain a valid private key! + Tập tin "%1" không chứa khóa riêng hợp lệ! + + + File "%1" does not contain a valid public key! + Tập tin "%1" không chứa khóa công hợp lệ! + + + Failed to create directory for key file. + Thất bại khi tạo thư mục cho tập tin khóa. + + + Failed to write key file "%1". + Thất bại khi ghi tập tin khóa "%1". + + + Failed to set permissions for key file "%1"! + Thất bại khi thiết lập quyền cho tập tin khóa "%1"! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + Khóa "%1/%2" đã được nhập thành công. Vui lòng kiểm tra quyền tập tin của "%3" để ngăn các truy cập trái phép. + + + Failed to convert private key to public key + Thất bại khi chuyển đổi khóa riêng thành khóa công + + + Failed to create directory for private key file "%1". + Thất bại khi tạo thư mục cho tập tin khóa riêng "%1". + + + Failed to save private key in file "%1"! + Thất bại khi lưu khóa riêng trong tập tin "%1"! + + + Failed to set permissions for private key file "%1"! + Thất bại khi thiết lập quyền cho tập tin khóa riêng "%1"! + + + Failed to create directory for public key file "%1". + Thất bại khi tạo thư mục cho tập tin khóa công "%1". + + + Failed to save public key in file "%1"! + Thất bại khi lưu khóa công trong tập tin "%1"! + + + Failed to set permissions for public key file "%1"! + Thất bại khi thiết lập quyền cho tập tin khóa công "%1"! + + + Failed to set owner of key file "%1" to "%2". + Thất bại khi thiết lập người sở hữu của tập tin khóa "%1" thành "%2". + + + Failed to set permissions for key file "%1". + Thất bại khi thiết lập quyền cho tập tin khóa "%1". + + + Key "%1" is now accessible by user group "%2". + Khóa "%1" bây giờ có thể truy cập được bởi nhóm người dùng "%2". + + + <N/A> + <Không> + + + Failed to read key file. + Thất bại khi đọc tập tin khóa. + + + + AuthKeysPlugin + + Create new authentication key pair + Tạo cặp khóa xác thực mới + + + Delete authentication key + Xóa khóa xác thực + + + List authentication keys + Liệt kê các khóa xác thực + + + Import public or private key + Nhập khóa công hoặc riêng + + + Export public or private key + Xuất khóa công hoặc riêng + + + Extract public key from existing private key + Trích xuất khóa công từ khóa riêng đang tồn tại + + + Set user group allowed to access a key + Đặt nhóm người dùng được phép truy cập một khóa + + + KEY + KHÓA + + + ACCESS GROUP + NHÓM TRUY CẬP + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + Lệnh này điều chỉnh quyền truy cập tập tin thành <KEY> để chỉ nhóm người dùng <ACCESS GROUP> có truy cập đọc vào nó. + + + NAME + TÊN + + + FILE + TẬP TIN + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Lệnh này xuất khóa xác thực <KEY> thành <FILE>. Nếu <FILE> không được chỉ định, một tên sẽ được xây dựng từ tên và kiểu của <KEY>. + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + Lệnh này nhập khóa xác thực <KEY> từ <FILE>. Nếu <FILE> không được chỉ định, một tên sẽ được xây dựng từ tên và kiểu của <KEY>. + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + Lệnh này liệt kê tất cả các khóa xác thực khả dụng trong thư mục khóa đã cấu hinh. Nếu tùy chọn "%1" được chỉ định, một bảng với chi tiết khóa sẽ được hiển thị để thay thế. Một số chi tiết có thể bị thiếu nếu một khóa không thể truy cập được ví dụ do thiếu quyền đọc. + + + Please specify the command to display help for! + Vui lòng chỉ định lệnh để hiển thị trợ giúp! + + + TYPE + KIỂU + + + PAIR ID + ID CẶP + + + Command line support for managing authentication keys + Hỗ trợ dòng lệnh để quản lý các khóa xác thực + + + Commands for managing authentication keys + Lệnh để quản lý các khóa xác thực + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + + + + + AuthKeysTableModel + + Name + Tên + + + Type + Kiểu + + + Access group + Nhóm truy cập + + + Pair ID + ID cặp + + + + BuiltinDirectoryConfigurationPage + + Computers + + + + Name + Tên + + + Host address/IP + + + + MAC address + + + + Add new computer + + + + Remove selected computer + + + + New computer + + + + Builtin directory + + + + Locations & computers + + + + Locations + + + + Add new location + + + + Remove selected location + + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + + + + New location + + + + + BuiltinDirectoryPlugin + + Show help for specific command + + + + Import objects from given file + + + + Export objects to given file + + + + Invalid type specified. Valid values are "%1" or "%2". + + + + Type + Kiểu + + + Name + Tên + + + Host address + + + + MAC address + + + + Specified object not found. + + + + File "%1" does not exist! + + + + Can't open file "%1" for reading! + + + + Unknown argument "%1". + + + + Computer "%1" (host address: "%2" MAC address: "%3") + + + + Unclassified object "%1" with ID "%2" + + + + None + + + + Computer + + + + Root + + + + Invalid + + + + Error while parsing line %1. + + + + Network object directory which stores objects in local configuration + + + + Commands for managing the builtin network object directory + + + + No format string or regular expression specified! + + + + Can't open file "%1" for writing! + + + + No format string specified! + + + + Object UUID + + + + Parent UUID + + + + Add a location or computer + + + + Clear all locations and computers + + + + Dump all or individual locations and computers + + + + List all locations and computers + + + + Remove a location or computer + + + + Location "%1" + + + + Builtin (computers and locations in local configuration) + + + + Location + + + + FILE + TẬP TIN + + + LOCATION + + + + FORMAT-STRING-WITH-PLACEHOLDERS + + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + + + + Import simple CSV file to a single room + + + + Import CSV file with location name in first column + + + + Import text file with with key/value pairs using regular expressions + + + + Import arbitrarily formatted data + + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + + + + Export all objects to a CSV file + + + + Export all computers in a specific location to a CSV file + + + + TYPE + KIỂU + + + NAME + TÊN + + + PARENT + + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + + + + Add a room + + + + Add a computer to room %1 + + + + OBJECT + + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + + + + Remove a computer by name + + + + Remove an object by UUID + + + + "Room 01" + + + + "Computer 01" + + + + HOST ADDRESS + + + + MAC ADDRESS + + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + + + + + ComputerControlListModel + + Host/IP address: %1 + + + + Active features: %1 + + + + Online and connected + + + + Establishing connection + + + + Computer offline or switched off + + + + Authentication failed or access denied + + + + Disconnected + + + + No user logged on + Không có người dùng đã đăng nhập + + + Logged on user: %1 + + + + Location: %1 + + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + + + + Authentication error + + + + Remote access + + + + User "%1" at host "%2" is now accessing this computer. + + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + + + + Access control error + + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + + + + Active connections: + + + + + ComputerManager + + User + + + + Missing network object directory plugin + + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + + + + Location detection failed + + + + Computer name;Hostname;User + + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + + + + + ComputerSelectPanel + + Computer search + + + + Add location + + + + Save computer/user list + + + + Select output filename + + + + CSV files (*.csv) + + + + File error + + + + Could not write the computer and users list to %1! Please check the file access permissions. + + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + + + + Please specify a valid filename for the configuration export. + + + + Please specify a valid key. + + + + Specified key does not exist in current configuration! + + + + Please specify a valid value. + + + + Configure Veyon at command line + + + + Output file is not writable! + + + + Output directory is not writable! + + + + Configuration file is not readable! + + + + Clear system-wide Veyon configuration + + + + List all configuration keys and values + + + + Import configuration from given file + + + + Export configuration to given file + + + + Read and output configuration value for given key + + + + Write given value to given configuration key + + + + Unset (remove) given configuration key + + + + Commands for managing the configuration of Veyon + + + + Upgrade and save configuration of program and plugins + + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + + + + Could not configure the firewall configuration for the %1 Server. + + + + Could not configure the firewall configuration for the %1 Worker. + + + + Configuration is not writable. Please check your permissions! + + + + Could not apply platform-specific configuration settings. + + + + + DemoClient + + %1 Demo + + + + + DemoConfigurationPage + + Demo server + + + + Tunables + + + + ms + + + + Key frame interval + + + + Memory limit + + + + MB + + + + Update interval + + + + s + + + + Slow down thumbnail updates while demo is running + + + + + DemoFeaturePlugin + + Stop demo + + + + Window demo + + + + Give a demonstration by screen broadcasting + + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + + + + Confirm desktop access + + + + Never for this session + + + + Always for this session + + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + + + + + DesktopServicesConfigurationPage + + Programs & websites + + + + Predefined programs + + + + Name + Tên + + + Path + + + + Add new program + + + + Remove selected program + + + + Predefined websites + + + + Remove selected website + + + + URL + + + + New program + + + + New website + + + + + DesktopServicesFeaturePlugin + + Run program + + + + Open website + + + + Click this button to open a website on all computers. + + + + Start programs and services in user desktop + + + + Click this button to run a program on all computers. + + + + Run program "%1" + + + + Custom program + + + + Open website "%1" + + + + Custom website + + + + + DocumentationFigureCreator + + Teacher + + + + Room %1 + + + + Please complete all tasks within the next 5 minutes. + + + + Custom website + + + + Open file manager + + + + Start learning tool + + + + Play tutorial video + + + + Custom program + + + + Handout + + + + Texts to read + + + + generic-student-user + + + + + ExternalVncServer + + External VNC server + + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + + + + Port: + + + + Password: + + + + + FeatureControl + + Feature control + + + + + FileTransferConfigurationPage + + File transfer + + + + Directories + + + + Destination directory + + + + Default source directory + + + + Options + + + + Remember last source directory + + + + Create destination directory if it does not exist + + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + + + + + FileTransferDialog + + File transfer + + + + Options + + + + Transfer only + + + + Transfer and open file(s) with associated program + + + + Transfer and open destination folder + + + + Files + + + + Start + + + + Overwrite existing files + + + + + FileTransferPlugin + + File transfer + + + + Click this button to transfer files from your computer to all computers. + + + + Select one or more files to transfer + + + + Transfer files to remote computer + + + + Received file "%1". + + + + Could not receive file "%1" as it already exists. + + + + Could not receive file "%1" as it could not be opened for writing! + + + + + GeneralConfigurationPage + + User interface + + + + Language: + + + + Use system language setting + + + + Veyon + + + + Logging + + + + Log file directory + + + + Log level + + + + Nothing + + + + Only critical messages + + + + Errors and critical messages + + + + Warnings and errors + + + + Information, warnings and errors + + + + Debug messages and everything else + + + + Limit log file size + + + + Clear all log files + + + + Log to standard error output + + + + Network object directory + + + + Backend: + + + + Update interval: + + + + %1 service + + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + + + + Log files cleared + + + + All log files were cleared successfully. + + + + Error + + + + Could not remove all log files. + + + + MB + + + + Rotate log files + + + + x + + + + seconds + + + + Write to logging system of operating system + + + + Authentication + + + + Method: + + + + Logon authentication + + + + Key file authentication + + + + Test + Kiểm tra + + + Authentication is set up properly on this computer. + + + + Authentication keys are not set up properly on this computer. + + + + Authentication test + + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + + + + + LdapClient + + LDAP error description: %1 + + + + + LdapConfigurationPage + + Basic settings + + + + General + Chung + + + LDAP server and port + + + + Bind DN + + + + Bind password + + + + Anonymous bind + + + + Use bind credentials + + + + Base DN + + + + Fixed base DN + + + + e.g. dc=example,dc=org + + + + Discover base DN by naming context + + + + e.g. namingContexts or defaultNamingContext + + + + Environment settings + + + + Object trees + + + + Computer tree + + + + e.g. OU=Groups + + + + User tree + + + + e.g. OU=Users + + + + e.g. OU=Computers + + + + Group tree + + + + Perform recursive search operations in object trees + + + + Object attributes + + + + e.g. hwAddress + + + + e.g. member or memberUid + + + + e.g. dNSHostName + + + + Computer MAC address attribute + + + + Group member attribute + + + + e.g. uid or sAMAccountName + + + + Advanced settings + + + + Optional object filters + + + + Filter for user groups + + + + Filter for users + + + + Filter for computer groups + + + + Group member identification + + + + Distinguished name (Samba/AD) + + + + List all groups of a user + + + + List all groups of a computer + + + + Get computer object by IP address + + + + LDAP connection failed + + + + LDAP bind failed + + + + LDAP bind successful + + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + + + + LDAP base DN test failed + + + + LDAP base DN test successful + + + + LDAP naming context test failed + + + + LDAP naming context test successful + + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + + + + user tree + + + + group tree + + + + computer tree + + + + Enter username + Nhập tên người dùng + + + Please enter a user login name (wildcards allowed) which to query: + + + + user objects + + + + Enter group name + + + + Please enter a group name whose members to query: + + + + group members + + + + Group not found + + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + + + + Enter computer name + + + + computer objects + + + + Enter computer DN + + + + Please enter the DN of a computer whose MAC address to query: + + + + computer MAC addresses + + + + users + + + + user groups + + + + computer groups + + + + Please enter a user login name whose group memberships to query: + + + + groups of user + + + + User not found + + + + groups of computer + + + + Computer not found + + + + Enter computer IP address + + + + Please enter a computer IP address which to resolve to an computer object: + + + + computers + + + + LDAP %1 test failed + + + + LDAP %1 test successful + + + + The %1 has been queried successfully and %2 entries were found. + + + + %1 %2 have been queried successfully: + +%3 + + + + LDAP filter test failed + + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + + + + LDAP filter test successful + + + + %1 %2 have been queried successfully using the configured filter. + + + + (only if different from group tree) + + + + Computer group tree + + + + computer group tree + + + + Filter for computers + + + + e.g. room or computerLab + + + + Integration tests + + + + Computer groups + + + + e.g. name or description + + + + Filter for computer containers + + + + Computer containers or OUs + + + + Connection security + + + + TLS certificate verification + + + + System defaults + + + + Never (insecure!) + + + + Custom CA certificate file + + + + None + + + + TLS + + + + SSL + + + + e.g. (objectClass=computer) + + + + e.g. (objectClass=group) + + + + e.g. (objectClass=person) + + + + e.g. (objectClass=room) or (objectClass=computerLab) + + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + + + + Certificate files (*.pem) + + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + + + + Encryption protocol + + + + Computer location attribute + + + + Computer display name attribute + + + + Location name attribute + + + + e.g. cn or displayName + + + + Computer locations identification + + + + Identify computer locations (e.g. rooms) via: + + + + Location attribute in computer objects + + + + List all entries of a location + + + + List all locations + + + + Enter computer display name + + + + Please enter a computer display name to query: + + + + Enter computer location name + + + + Please enter the name of a computer location (wildcards allowed): + + + + computer locations + + + + Enter location name + + + + Please enter the name of a location whose entries to query: + + + + location entries + + + + LDAP test failed + + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + + + + and + + + + LDAP test successful + + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + + + + Browse + + + + Test + Kiểm tra + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + + + + Computer hostname attribute + + + + Please enter a computer hostname to query: + + + + Invalid hostname + + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + + + + Enter hostname + + + + Please enter a computer hostname whose group memberships to query: + + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + + + + Hostname lookup failed + + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + + + + User login name attribute + + + + Configured attribute for user login name or computer hostname (OpenLDAP) + + + + computer containers + + + + + LdapPlugin + + Auto-configure the base DN via naming context + + + + Query objects from LDAP directory + + + + Show help about command + + + + Commands for configuring and testing LDAP/AD integration + + + + Basic LDAP/AD support for Veyon + + + + %1 (load computers and locations from LDAP/AD) + + + + %1 (load users and groups from LDAP/AD) + + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + + + + No naming context attribute name given - falling back to configured value. + + + + Could not query base DN. Please check your LDAP configuration. + + + + Configuring %1 as base DN and disabling naming context queries. + + + + + LinuxPlatformConfigurationPage + + Linux + + + + Custom PAM service for user authentication + + + + User authentication + + + + Session management + + + + Display manager users + + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + + + + + LocationDialog + + Select location + + + + enter search filter... + + + + + MainToolBar + + Configuration + + + + Disable balloon tooltips + + + + Show icons only + + + + + MainWindow + + MainWindow + + + + toolBar + + + + General + Chung + + + &File + + + + &Help + + + + &Quit + + + + Ctrl+Q + + + + Ctrl+S + + + + L&oad settings from file + + + + Ctrl+O + + + + About Qt + + + + Authentication impossible + + + + Configuration not writable + + + + Load settings from file + + + + Save settings to file + + + + Unsaved settings + + + + There are unsaved settings. Quit anyway? + + + + Veyon Configurator + + + + Service + + + + Master + + + + Access control + + + + About Veyon + Về Veyon + + + Auto + + + + About + Giới thiệu + + + %1 Configurator %2 + + + + JSON files (*.json) + + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + + + + Access denied + Truy cập bị từ chối + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + + + + Screenshots + + + + Feature active + + + + The feature "%1" is still active. Please stop it before closing %2. + + + + Reset configuration + + + + Do you really want to reset the local configuration and revert all settings to their defaults? + + + + Search users and computers + + + + Align computers to grid + + + + %1 Configurator + + + + Insufficient privileges + + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + + + + Only show powered on computers + + + + &Save settings to file + + + + &View + + + + &Standard + + + + &Advanced + + + + Use custom computer arrangement + + + + Locations && computers + + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + + + + User configuration + + + + Feature on computer double click: + + + + Features + + + + All features + + + + Disabled features + + + + Screenshots + + + + <no feature> + + + + Basic settings + + + + Behaviour + + + + Enforce selected mode for client computers + + + + Hide local computer + + + + Hide computer filter field + + + + Actions such as rebooting or powering down computers + + + + User interface + + + + Background color + + + + Thumbnail update interval + + + + ms + + + + Program start + + + + Modes and features + + + + User and computer name + + + + Only user name + + + + Only computer name + + + + Computer thumbnail caption + + + + Text color + + + + Sort order + + + + Computer and user name + + + + Computer locations + + + + Show current location only + + + + Allow adding hidden locations manually + + + + Hide empty locations + + + + Show confirmation dialog for potentially unsafe actions + + + + Perform access control + + + + Automatically select current location + + + + Automatically open computer select panel + + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + + + + Builtin monitoring mode + + + + This mode allows you to monitor all computers at one or more locations. + + + + + NetworkObjectTreeModel + + Locations/Computers + + + + + OpenWebsiteDialog + + Open website + + + + e.g. Veyon + + + + Remember and add to website menu + + + + e.g. www.veyon.io + + + + Please enter the URL of the website to open: + + + + Name: + + + + + PasswordDialog + + Username + + + + Password + + + + Veyon Logon + + + + Authentication error + + + + Logon failed with given username and password. Please try again! + + + + Please enter your username and password in order to access computers. + + + + + PowerControlFeaturePlugin + + Power on + + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + + + + Reboot + + + + Click this button to reboot all computers. + + + + Power down + + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + + + + Power on/down or reboot a computer + + + + Confirm reboot + + + + Confirm power down + + + + Do you really want to reboot the selected computers? + + + + Do you really want to power down the selected computer? + + + + Power on a computer via Wake-on-LAN (WOL) + + + + MAC ADDRESS + + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + + + + Please specify the command to display help for! + Vui lòng chỉ định lệnh để hiển thị trợ giúp! + + + Invalid MAC address specified! + + + + Commands for controlling power status of computers + + + + Power down now + + + + Install updates and power down + + + + Power down after user confirmation + + + + Power down after timeout + + + + The computer was remotely requested to power down. Do you want to power down the computer now? + + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + + + + + PowerDownTimeInputDialog + + Power down + + + + Please specify a timeout for powering down the selected computers: + + + + minutes + + + + seconds + + + + + RemoteAccessFeaturePlugin + + Remote view + + + + Open a remote view for a computer without interaction. + + + + Remote control + + + + Open a remote control window for a computer. + + + + Remote access + + + + Remote view or control a computer + + + + Please enter the hostname or IP address of the computer to access: + + + + Show help about command + + + + + RemoteAccessWidget + + %1 - %2 Remote Access + + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + + + + Remote control + + + + Send shortcut + + + + Fullscreen + + + + Window + + + + Ctrl+Alt+Del + + + + Ctrl+Esc + + + + Alt+Tab + + + + Alt+F4 + + + + Win+Tab + + + + Win + + + + Menu + + + + Alt+Ctrl+F1 + + + + Connecting %1 + + + + Connected. + + + + Screenshot + + + + Exit + + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + + + + Run programs + + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + + Name: + + + + Remember and add to program menu + + + + e.g. VLC + + + + + ScreenLockFeaturePlugin + + Lock + + + + Unlock + + + + Lock screen and input devices of a computer + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + + + + Lock input devices + + + + Unlock input devices + + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + + + + Screenshot + + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + + + + Use this function to take a screenshot of selected computers. + + + + Screenshots taken + + + + Screenshot of %1 computer have been taken successfully. + + + + Take screenshots of computers and save them locally. + + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + + + + User: + + + + Computer: + + + + Date: + + + + Time: + + + + Show + + + + Delete + + + + Screenshot + + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + Chung + + + Autostart + + + + Hide tray icon + + + + Start service + + + + Stopped + + + + Stop service + + + + State: + + + + Enable firewall exception + + + + Allow connections from localhost only + + + + VNC server + + + + Plugin: + + + + Restart %1 Service + + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + + + + Running + + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + + + + Show notification on remote connection + + + + Show notification when an unauthorized access is blocked + + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + + + + Stopping service %1 + + + + Registering service %1 + + + + Unregistering service %1 + + + + Service control + + + + + ServiceControlPlugin + + Service is running + + + + Service is not running + + + + Configure and control Veyon service + + + + Register Veyon Service + + + + Unregister Veyon Service + + + + Start Veyon Service + + + + Stop Veyon Service + + + + Restart Veyon Service + + + + Query status of Veyon Service + + + + Commands for configuring and controlling Veyon Service + + + + + ShellCommandLinePlugin + + Run command file + + + + File "%1" does not exist! + + + + Interactive shell and script execution for Veyon Control + + + + Commands for shell functionalities + + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + + + + Default (system user groups) + + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + + + + Commands for testing internal components and functions of Veyon + + + + + TextMessageDialog + + Send text message + + + + Use the field below to type your message which will be sent to all selected users. + + + + + TextMessageFeaturePlugin + + Text message + + + + Use this function to send a text message to all users e.g. to assign them new tasks. + + + + Message from teacher + + + + Send a message to a user + + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + + + + Poll full screen (leave this enabled per default) + + + + Low accuracy (turbo mode) + + + + Builtin UltraVNC server configuration + + + + Enable multi monitor support + + + + Enable Desktop Duplication Engine on Windows 8 and newer + + + + Maximum CPU usage + + + + + UserConfig + + No write access + + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + + + + + UserLoginDialog + + User login + + + + Please enter a username and password for automatic login on all computers. + + + + Username + + + + Password + + + + + UserSessionControlPlugin + + Log in + + + + Click this button to log in a specific user on all computers. + + + + Log off + + + + Click this button to log off users from all computers. + + + + Confirm user logoff + + + + Do you really want to log off the selected users? + + + + User session control + + + + + VeyonCore + + [OK] + + + + [FAIL] + + + + Invalid command! + + + + Available commands: + + + + Invalid arguments given + + + + Not enough arguments given - use "%1 help" for more information + + + + Unknown result! + + + + Available modules: + + + + No module specified or module not found - available modules are: + + + + Plugin not licensed + + + + INFO + + + + ERROR + + + + USAGE + + + + DESCRIPTION + + + + EXAMPLES + + + + WARNING + + + + + VeyonServiceControl + + Veyon Service + + + + + VncViewWidget + + Establishing connection to %1 ... + + + + + WebApiConfigurationPage + + Web API + + + + General + Chung + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + + + + + WindowsPlatformConfigurationPage + + Windows + + + + General + Chung + + + Enable SAS generation by software (Ctrl+Alt+Del) + + + + Screen lock + + + + Hide taskbar + + + + Hide start menu + + + + Hide desktop + + + + User authentication + + + + Use alternative user authentication mechanism + + + + User login + + + + Input start delay + + + + Simulated key presses interval + + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + + + + + WindowsServiceControl + + The service "%1" is already installed. + + + + The service "%1" could not be installed. + + + + The service "%1" has been installed successfully. + + + + The service "%1" could not be uninstalled. + + + + The service "%1" has been uninstalled successfully. + + + + The start type of service "%1" could not be changed. + + + + Service "%1" could not be found. + + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + + + + Custom x11vnc parameters: + + + + Do not use X Damage extension + + + + \ No newline at end of file diff --git a/translations/veyon_zh_CN.ts b/translations/veyon_zh_CN.ts new file mode 100644 index 0000000..b7fcfe2 --- /dev/null +++ b/translations/veyon_zh_CN.ts @@ -0,0 +1,4082 @@ + + + AboutDialog + + About + 关于 + + + Translation + 翻译 + + + License + 许可 + + + About Veyon + 关于 Veyon + + + Contributors + 贡献者 + + + Version: + 版本: + + + Website: + 主页: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + 当前语言还未翻译(或者是英文母语)。 + +如果您对翻译 Veyon 感兴趣,或者想改进当前的翻译,请联系 Veyon 开发者! + + + About %1 %2 + 关于 %1 %2 + + + Support Veyon project with a donation + 捐赠 Veyon 项目 + + + + AccessControlPage + + Computer access control + 计算机访问控制 + + + Grant access to every authenticated user (default) + 允许任何方式通过验证的用户访问(默认) + + + Test + 测试 + + + Process access control rules + 处理访问控制规则 + + + User groups authorized for computer access + 验证通过允许访问的用户组 + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + 请将允许访问计算机的成员所在的组,添加到 Veyon 网络中 + + + Authorized user groups + 通过验证的用户组 + + + All groups + 所有群组 + + + Access control rules + 访问控制规则 + + + Add access control rule + 添加访问控制规则 + + + Remove access control rule + 远程访问控制规则 + + + Move selected rule down + 下移选中的规则 + + + Move selected rule up + 上移选中的规则 + + + Edit selected rule + 编辑选中的规则 + + + Enter username + 请输入用户名 + + + Please enter a user login name whose access permissions to test: + 请输入一个登陆用户名,用于测试访问权限: + + + Access allowed + 允许访问 + + + The specified user is allowed to access computers with this configuration. + 指定的用户允许使用此配置访问计算机 + + + Access denied + 拒绝访问 + + + The specified user is not allowed to access computers with this configuration. + 指定的用户不允许使用此配置访问计算机。 + + + Enable usage of domain groups + 允许使用域组(domain groups) + + + User groups backend: + 用户组后端: + + + Missing user groups backend + 缺少用户组后端 + + + No default user groups plugin was found. Please check your installation! + 没有找到默认的用户组插件,请检查您的安装程序! + + + Restrict access to members of specific user groups + 限制特定用户组成员的访问权限 + + + + AccessControlRuleEditDialog + + Edit access control rule + 编辑访问控制规则 + + + General + 常规 + + + enter a short name for the rule here + 请为这里的规则输入一个短名称 + + + Rule name: + 规则名称: + + + enter a description for the rule here + 输入规则的描述 + + + Rule description: + 规则描述: + + + Invert all conditions ("is/has" interpreted as "is/has not") + 反转所有条件(“是/有”变成“否/没有”) + + + Conditions + 条件 + + + is member of group + 是组成员 + + + Accessing computer is localhost + 正在访问局域网中的计算机 + + + Accessing user is logged on user + 访问的用户已登录 + + + Accessing user is already connected + 访问的用户已连接 + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + 如果激活多个条件,则必须满足每个条件才能适用规则(逻辑与)。 如果只需满足多个条件中的一个,(逻辑或),请创建多个访问控制规则。 + + + Action + 动作 + + + Allow access + 允许访问 + + + Deny access + 拒绝访问 + + + Ask logged on user for permission + 询问登录用户的权限 + + + None (rule disabled) + 无(规则被禁用) + + + Accessing user + 访问用户 + + + Accessing computer + 访问计算机 + + + Local (logged on) user + 本地(登录)用户 + + + Local computer + 本地计算机 + + + Always process rule and ignore conditions + 总是按规则运行并忽略条件 + + + No user logged on + 没有用户登录 + + + Accessing user has one or more groups in common with local (logged on) user + 访问用户有一个或多个与本地(登录)用户相同的组 + + + Accessing computer and local computer are at the same location + 访问计算机和本地计算机位于同一地点 + + + is located at + 位于 + + + + AccessControlRulesTestDialog + + Access control rules test + 访问控制规则测试 + + + Accessing user: + 访问用户: + + + Local computer: + 本地计算机: + + + Accessing computer: + 访问计算机: + + + Please enter the following user and computer information in order to test the configured ruleset. + 请输入以下用户和计算机信息以测试配置的规则。 + + + Local user: + 本地用户: + + + Connected users: + 连接用户: + + + The access in the given scenario is allowed. + 允许指定场景中的访问。 + + + The access in the given scenario is denied. + 拒绝指定场景中的访问。 + + + The access in the given scenario needs permission of the logged on user. + 指定场景中的访问需要登录用户的权限。 + + + ERROR: Unknown action + 错误:未知的操作 + + + Test result + 测试规则 + + + + AuthKeysConfigurationPage + + Authentication keys + 验证密钥 + + + Introduction + 介绍 + + + Key file directories + 密钥文件目录 + + + Public key file base directory + 公钥文件主目录 + + + Private key file base directory + 私钥文件主目录 + + + Available authentication keys + 可用的验证密钥 + + + Create key pair + 创建密钥对 + + + Delete key + 删除密钥 + + + Import key + 导入密钥 + + + Export key + 导出密钥 + + + Set access group + 设置访问组 + + + Key files (*.pem) + 密钥文件 (*.pem) + + + Authentication key name + 验证密钥文件名 + + + Please enter the name of the user group or role for which to create an authentication key pair: + 请输入要为其创建验证密钥对的用户组或角色的名称: + + + Do you really want to delete authentication key "%1/%2"? + 您真的要删除密钥 "%1/%2"? + + + Please select a key to delete! + 请选择要删除的密钥! + + + Please enter the name of the user group or role for which to import the authentication key: + 请输入要为其导入身份验证密钥的用户组或角色的名称: + + + Please select a key to export! + 请选择要导出的密钥! + + + Please select a user group which to grant access to key "%1": + 请选择一个授权访问密钥 "%1" 的用户组: + + + Please select a key which to set the access group for! + 请选择一个用于访问组的的密钥! + + + Please perform the following steps to set up key file authentication: + 请按以下步骤设置密钥文件验证: + + + 1) Create a key pair on the master computer. + 1)在主计算机上创建密钥对。 + + + 2) Set an access group whose members should be allowed to access other computers. + 2)设置一个访问组,其成员应该被允许访问其他计算机。 + + + 3) Export the public key and import it on all client computers with the same name. + 3)将公钥其导入到具有相同名称的所有客户端计算机上。 + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + 请访问链接 <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon 管理员手册</a> 以获得更多信息。 + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + 密钥包括两部分,私钥和公钥。 +私钥允许主机上的用户访问客户机。 +重要:只有经过验证的用户才可以读取私钥文件。 +公钥存放于客户端电脑,用以验证前来访问的请求。 + + + + + AuthKeysManager + + Please check your permissions. + 请检查您的权限。 + + + Key name contains invalid characters! + 密钥文件名包含非法字符! + + + Invalid key type specified! Please specify "%1" or "%2". + 指定的密钥类型无效! 请指定 "%1" 或者 "%2". + + + Specified key does not exist! Please use the "list" command to list all installed keys. + 指定的密钥不存在! 请使用“list”命令列出所有已安装的密钥。 + + + One or more key files already exist! Please delete them using the "delete" command. + 一个或多个密钥文件已经存在! 请使用“delete”命令删除它们。 + + + Creating new key pair for "%1" + 创建新的密钥对 "%1" + + + Failed to create public or private key! + 创建公钥/私钥失败! + + + Newly created key pair has been saved to "%1" and "%2". + 新建的密钥对已经保存为 "%1" 和 "%2"。 + + + Could not remove key file "%1"! + 不能删除密钥文件 "%1" ! + + + Could not remove key file directory "%1"! + 不能删除密钥文件夹 "%1" ! + + + Failed to create directory for output file. + 创建输出文件夹失败。 + + + File "%1" already exists. + 文件 "%1" 已经存在。 + + + Failed to write output file. + 写入文件失败。 + + + Key "%1/%2" has been exported to "%3" successfully. + 密钥 "%1/%2" 已经成功导出为 "%3" 。 + + + Failed read input file. + 读取输入文件失败。 + + + File "%1" does not contain a valid private key! + "%1" 文件,不包含可用的密钥! + + + File "%1" does not contain a valid public key! + "%1" 文件,不包含可用的公钥! + + + Failed to create directory for key file. + 创建密钥文件夹失败。 + + + Failed to write key file "%1". + 写入密钥文件 "%1" 失败。 + + + Failed to set permissions for key file "%1"! + 设置密钥文件 "%1" 权限失败! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + 密钥 "%1/%2" 已经成功导入,请检查 "%3" 文件权限,以防止未授权的访问。 + + + Failed to convert private key to public key + 转换私钥到公钥失败 + + + Failed to create directory for private key file "%1". + 为 "%1" 私钥文件创建文件夹失败。 + + + Failed to save private key in file "%1"! + 保存 "%1" 私钥文件失败! + + + Failed to set permissions for private key file "%1"! + 设置私钥文件 "%1" 权限失败! + + + Failed to create directory for public key file "%1". + 为公钥文件 "%1" 创建文件夹失败。 + + + Failed to save public key in file "%1"! + 保存公钥文件 "%1" 失败! + + + Failed to set permissions for public key file "%1"! + 为公钥文件 "%1" 设置权限失败! + + + Failed to set owner of key file "%1" to "%2". + 设置密钥文件所有者从 "%1" 到 "%2" 失败。 + + + Failed to set permissions for key file "%1". + 设置密钥文件 "%1" 权限失败。 + + + Key "%1" is now accessible by user group "%2". + 用户组 "%2" 现在可以访问密钥 "%1" 。 + + + <N/A> + <N/A> + + + Failed to read key file. + 读取密钥文件失败。 + + + + AuthKeysPlugin + + Create new authentication key pair + 创建新的密钥对 + + + Delete authentication key + 删除验证密钥 + + + List authentication keys + 列出验证密钥 + + + Import public or private key + 导入公钥或私钥 + + + Export public or private key + 导出公钥或私钥 + + + Extract public key from existing private key + 从现有私钥中提取公钥 + + + Set user group allowed to access a key + 设置用户组允许访问密钥 + + + KEY + 密钥 + + + ACCESS GROUP + 访问用户组 + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + 此命令将文件访问权限调整为 <KEY> 以便只有用户组 <ACCESS GROUP> 能够读取并访问它。 + + + NAME + NAME + + + FILE + FILE + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + 此命令导出验证密钥 <KEY> 到 <FILE> 。如果 <FILE> 没有指定名称,将会从 <KEY> 的名称和类型构建。 + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + 此命令从验证密钥 <FILE> 导入 <KEY> 。如果 <FILE> 没有指定名称,将从 <KEY> 的名称和类型创建。 + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + 此命令列出配置的密钥目录中的所有可用身份验证密钥。 如果指定了选项 "%1" ,则会显示包含密钥详细信息的表格。 如果无法访问密钥,某些细节可能会丢失,例如,没有读取权限。 + + + Please specify the command to display help for! + 请指定需要显示用法命令! + + + TYPE + TYPE + + + PAIR ID + PAIR ID + + + Command line support for managing authentication keys + 用于管理验证密钥的命令行支持 + + + Commands for managing authentication keys + 管理身份验证密钥的命令 + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + 此命令创建一个名为<NAME>的新的验证密钥对,并将密钥保存到已设置的密钥文件夹里。此参数必须是密钥的名称,名称只允许包含英文字母。 + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + 此命令将把验证密钥<KEY>从已设置的密钥文件夹中删除。请注意,密钥一旦删除就无法恢复。 + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + 此命令从私钥<KEY>中抽取公钥部分并将其保存为相应的公钥。因此当设置另一台主计算机时,只传输私钥就可以了。公钥可以被抽取出来。 + + + + AuthKeysTableModel + + Name + 名称 + + + Type + 类型 + + + Access group + 访问组 + + + Pair ID + 配对 ID + + + + BuiltinDirectoryConfigurationPage + + Computers + 计算机 + + + Name + 名称 + + + Host address/IP + 主机 IP 地址 + + + MAC address + MAC 地址 + + + Add new computer + 添加新计算机 + + + Remove selected computer + 删除选中的计算机 + + + New computer + 新计算机 + + + Builtin directory + 内置目录 + + + Locations & computers + 地点及计算机 + + + Locations + 地点 + + + Add new location + 添加新地点 + + + Remove selected location + 移除选中的地点 + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + 通过命令行界面可导入CSV文件。更多信息,请参看<a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">在线文档</a>。 + + + New location + 新地点 + + + + BuiltinDirectoryPlugin + + Show help for specific command + 为指定的命令显示帮助 + + + Import objects from given file + 从给定的文件导入对象 + + + Export objects to given file + 导出对象到指定的文件 + + + Invalid type specified. Valid values are "%1" or "%2". + 指定的类型无效,有效值是 "%1" 或 "%2"。 + + + Type + 类型 + + + Name + 名称 + + + Host address + 主机地址 + + + MAC address + MAC 地址 + + + Specified object not found. + 未找到指定的对象。 + + + File "%1" does not exist! + 文件 "%1" 不存在! + + + Can't open file "%1" for reading! + 无法打开用于读取的文件 "%1" ! + + + Unknown argument "%1". + 未知参数 "%1"。 + + + Computer "%1" (host address: "%2" MAC address: "%3") + 计算机 "%1" (主机地址: "%2" MAC 地址: "%3") + + + Unclassified object "%1" with ID "%2" +  ID 为 "%2" 的未分类对象 "%1" + + + None + 无 + + + Computer + 计算机 + + + Root + Root + + + Invalid + 无效 + + + Error while parsing line %1. + 解析行 %1 时出错。 + + + Network object directory which stores objects in local configuration + 网络对象目录,用于存储本地配置的对象 + + + Commands for managing the builtin network object directory + 管理内置网络对象目录的命令 + + + No format string or regular expression specified! + 没有指定格式字符串或正则表达式! + + + Can't open file "%1" for writing! + 不能打开要写入的文件e "%1"! + + + No format string specified! + 没有指定格式字符串! + + + Object UUID + 对象的 UUID + + + Parent UUID + Parent UUID + + + Add a location or computer + 添加地点或计算机 + + + Clear all locations and computers + 清除所有地点和计算机 + + + Dump all or individual locations and computers + 转储全部和单独的地点和计算机 + + + List all locations and computers + 列出所有地点和计算机 + + + Remove a location or computer + 移除地点或计算机 + + + Location "%1" + 地点 "%1" + + + Builtin (computers and locations in local configuration) + 内置(计算机和地点都在本地配置中) + + + Location + 地点 + + + FILE + FILE + + + LOCATION + 地点 + + + FORMAT-STRING-WITH-PLACEHOLDERS + 使用占位符格式化字符串 + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + 使用占位符的正则表达式 + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + 使用包含一个或多个占位符的给定格式化字符串或正则表达式来从特定文本文件导入对象。合法的占位符有:%1 + + + Import simple CSV file to a single room + 将简单的CSV文件导入到单独教室 + + + Import CSV file with location name in first column + 导入CSV文件,地点名称放在第一列 + + + Import text file with with key/value pairs using regular expressions + 使用正则表达式导入含键值对的文本文件 + + + Import arbitrarily formatted data + 导入任意格式化的数据 + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + 使用包含一个或多个占位符的给定格式化字符串来导出对象到特定的文本文件。合法的占位符有:%1 + + + Export all objects to a CSV file + 导出所有对象到一个CSV文件 + + + Export all computers in a specific location to a CSV file + 导出特定地点中的所有计算机到一个CSV文件 + + + TYPE + TYPE + + + NAME + NAME + + + PARENT + 父母 + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + 添加一个对象,%1可以是 "%2" 或 "%3" 的其中之一。%4 可以通过名称或UUID指定。 + + + Add a room + 添加一个教室 + + + Add a computer to room %1 + 添加一个计算机到教室 %1 + + + OBJECT + 对象 + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + 从目录中特定对象。%1 可以通过名称或UUID指定。移除一个地点也会移除所有相关的计算机。 + + + Remove a computer by name + 按名称移除计算机 + + + Remove an object by UUID + 按UUID移除一个对象 + + + "Room 01" + "教室 01" + + + "Computer 01" + "计算机 01" + + + HOST ADDRESS + 主机地址 + + + MAC ADDRESS + MAC 地址(网卡物理地) + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + 内置 VNC 服务 (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + 内置 VNC 服务 (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + 主机/IP 地址: %1 + + + Active features: %1 + 激活的功能: %1 + + + Online and connected + 在线并已连接 + + + Establishing connection + 正在建立连接 + + + Computer offline or switched off + 计算机离线或者已关机 + + + Authentication failed or access denied + 验证失败或访问被拒绝 + + + Disconnected + 已断开 + + + No user logged on + 没有用户登录 + + + Logged on user: %1 + 登录用户: %1 + + + Location: %1 + 地点:%1 + + + Veyon Server unreachable or not running + + + + [no user] + + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 服务 %2 在 %3:%4 + + + Authentication error + 验证错误 + + + Remote access + 远程访问 + + + User "%1" at host "%2" is now accessing this computer. + "%2" 主机上的用户 "%1" 正在访问这台计算机。 + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + 主机 "%2" 上的用户 "%1" 尝试访问此计算机但没有通过身份验证。 + + + Access control error + 访问控制错误 + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + 主机 "%2" 上的用户 "%1" 尝试访问此计算机但因访问控制设置被阻止。 + + + Active connections: + 活跃连接: + + + + ComputerManager + + User + 用户 + + + Missing network object directory plugin + 缺少网络对象目录插件 + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + 没有找到默认的网络对象目录插件。请检查您的安装或通过 %1 配置器配置不同的网络对象目录后端。 + + + Location detection failed + 地点检测失败 + + + Computer name;Hostname;User + 计算机名称;主机名;用户 + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + 无法确定此计算机的地点。这表明系统配置存在问题。所有地点都将显示在计算机选择面板中。 + + + + ComputerSelectPanel + + Computer search + 搜索计算机 + + + Add location + 添加地点 + + + Save computer/user list + 保存计算机/用户列表 + + + Select output filename + 选择输出文件名 + + + CSV files (*.csv) + CSV 文件 (*.csv) + + + File error + 文件错误 + + + Could not write the computer and users list to %1! Please check the file access permissions. + 无法将计算机和用户列表写入 %1!请检查文件访问权限。 + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + 请指定要导入的配置文件。 + + + Please specify a valid filename for the configuration export. + 请为导出的配置指定一个有效的文件名。 + + + Please specify a valid key. + 请指定一个有效的密钥。 + + + Specified key does not exist in current configuration! + 指定的密钥在当前配置中不存在! + + + Please specify a valid value. + 请指定一个有效的值。 + + + Configure Veyon at command line + 在命令行配置 + + + Output file is not writable! + 输出文件不可写! + + + Output directory is not writable! + 输出目录不可写! + + + Configuration file is not readable! + 配置文件不可读! + + + Clear system-wide Veyon configuration + 清除系统级别 Veyon 配置 + + + List all configuration keys and values + 列出所有配置密钥和值 + + + Import configuration from given file + 从给定的文件导入配置 + + + Export configuration to given file + 导出配置到指定的文件 + + + Read and output configuration value for given key + 读取并输出配置值给指定的密钥 + + + Write given value to given configuration key + 将给定的值写入给定的配置密钥 + + + Unset (remove) given configuration key + 取消设置(移除)给定的配置密钥 + + + Commands for managing the configuration of Veyon + 管理 Veyon 配置的命令 + + + Upgrade and save configuration of program and plugins + 升级并保存程序和插件的配置 + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + 无法修改 %1 服务的自启动属性。 + + + Could not configure the firewall configuration for the %1 Server. + 无法为 %1 服务配置防火墙配置。 + + + Could not configure the firewall configuration for the %1 Worker. + 无法为 %1 程序配置防火墙配置。 + + + Configuration is not writable. Please check your permissions! + 配置不可写,请检查您的权限! + + + Could not apply platform-specific configuration settings. + 无法应用平台相关的配置。 + + + + DemoClient + + %1 Demo + %1 演示 + + + + DemoConfigurationPage + + Demo server + 演示服务 + + + Tunables + 可调参数 + + + ms + 毫秒 + + + Key frame interval + 关键帧间隔 + + + Memory limit + 内存限制 + + + MB + MB + + + Update interval + 更新间隔 + + + s + s + + + Slow down thumbnail updates while demo is running + 当演示正在运行时降低缩略图更新频率 + + + + DemoFeaturePlugin + + Stop demo + 停止演示 + + + Window demo + 窗口演示 + + + Give a demonstration by screen broadcasting + 通过屏幕广播进行演示 + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + 在此模式下,您的屏幕将作为窗口显示在所有计算机中。用户可以根据需要切换到其他窗口。 + + + Demo + + + + Share your screen or allow a user to share his screen with other users. + + + + Full screen demo + + + + Share your own screen in fullscreen mode + + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share your own screen in a window + + + + Share selected user's screen in fullscreen mode + + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + + + + Share selected user's screen in a window + + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + + + + Please select a user screen to share. + + + + Please select only one user screen to share. + + + + All screens + + + + Screen %1 [%2] + + + + + DesktopAccessDialog + + Desktop access dialog + 桌面访问对话框 + + + Confirm desktop access + 确认访问桌面 + + + Never for this session + 从不使用这个会话 + + + Always for this session + 总是使用此会话 + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + 计算机 %2 的用户r %1 想要访问您的桌面,您是否他的允许访问? + + + + DesktopServicesConfigurationPage + + Programs & websites + 程序和网站 + + + Predefined programs + 预定义的程序 + + + Name + 名称 + + + Path + 路径 + + + Add new program + 添加新程序 + + + Remove selected program + 移除选中的程序 + + + Predefined websites + 预定义的网站 + + + Remove selected website + 移除选定的网站 + + + URL + URL + + + New program + 新程序 + + + New website + 新站点 + + + + DesktopServicesFeaturePlugin + + Run program + 运行程序 + + + Open website + 打开网站 + + + Click this button to open a website on all computers. + 点击此按钮,将在所有计算机上打开一个网站 + + + Start programs and services in user desktop + 在用户桌面运行程序和服务 + + + Click this button to run a program on all computers. + 点击此按钮将在所有计算机上运行一个程序。 + + + Run program "%1" + 运行程序 "%1" + + + Custom program + 定制程序 + + + Open website "%1" + 打开站点 "%1" + + + Custom website + 定制站点 + + + + DocumentationFigureCreator + + Teacher + 教师 + + + Room %1 + 教室 %1 + + + Please complete all tasks within the next 5 minutes. + 请在接下来的5分钟内完成所有任务。 + + + Custom website + 定制站点 + + + Open file manager + 打开文件管理器 + + + Start learning tool + 启动学习工具 + + + Play tutorial video + 播放教程视频 + + + Custom program + 定制程序 + + + Handout + 讲义 + + + Texts to read + 要阅读的文本 + + + generic-student-user + 一般学生用户 + + + + ExternalVncServer + + External VNC server + 外部 VNC 服务 + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + 外部 VNC 服务配置 + + + Port: + 端口: + + + Password: + 密码: + + + + FeatureControl + + Feature control + 功能控制 + + + + FileTransferConfigurationPage + + File transfer + 发布文件 + + + Directories + 文件夹 + + + Destination directory + 目标文件夹 + + + Default source directory + 默认源目录 + + + Options + 首选项 + + + Remember last source directory + 记住上次的源目录 + + + Create destination directory if it does not exist + 如果不存在则创建目标目录 + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + 无法打开文件 "%1" 进行阅读! 请检查你的权限! + + + + FileTransferDialog + + File transfer + 发布文件 + + + Options + 首选项 + + + Transfer only + 仅传输 + + + Transfer and open file(s) with associated program + 传输并用关联的程序打开 + + + Transfer and open destination folder + 传输并打开目标文件夹 + + + Files + 文件 + + + Start + 开始 + + + Overwrite existing files + 覆盖已存在的文件 + + + + FileTransferPlugin + + File transfer + 发布文件 + + + Click this button to transfer files from your computer to all computers. + 单击此按钮可将文件从您的计算机发送到所有计算机。 + + + Select one or more files to transfer + 选择一个或多个要发送的文件 + + + Transfer files to remote computer + 发送文件到远程计算机 + + + Received file "%1". + 接收文件 "%1". + + + Could not receive file "%1" as it already exists. + 不能接收文件 "%1" ,因为它已经存在。 + + + Could not receive file "%1" as it could not be opened for writing! + 不能接收文件 "%1" ,因为它无法写入。 + + + + GeneralConfigurationPage + + User interface + 用户界面 + + + Language: + 语言: + + + Use system language setting + 使用系统语言设置 + + + Veyon + Veyon + + + Logging + 正在登录 + + + Log file directory + 日志文件存放目录 + + + Log level + 日志级别 + + + Nothing + 无 + + + Only critical messages + 只记录严重错误信息 + + + Errors and critical messages + 错误和严重错误信息 + + + Warnings and errors + 警告和错误 + + + Information, warnings and errors + 信息、警告和错误 + + + Debug messages and everything else + 调试信息和其他所有信息 + + + Limit log file size + 日志文件限制大小 + + + Clear all log files + 清空所有日志文件 + + + Log to standard error output + 日志显示于标准输出设备(显示器) + + + Network object directory + 网络对象文件夹 + + + Backend: + 后端: + + + Update interval: + 更新频率: + + + %1 service + %1 服务 + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + %1 服务需要停止临时文件夹,以便于移除日志文件,是否继续? + + + Log files cleared + 日志文件已删除 + + + All log files were cleared successfully. + 所有日志文件均已删除成功。 + + + Error + 错误 + + + Could not remove all log files. + 不能删除所有日志文件。 + + + MB + MB + + + Rotate log files + 轮循日志文件 + + + x + x + + + seconds + 秒 + + + Write to logging system of operating system + 写入操作系统的日志记录系统 + + + Authentication + 验证 + + + Method: + 方法: + + + Logon authentication + 登录验证 + + + Key file authentication + 密钥验证 + + + Test + 测试 + + + Authentication is set up properly on this computer. + 身份验证已在此计算机上正确配置。 + + + Authentication keys are not set up properly on this computer. + 验证密钥在此计算机上没有正确配置。 + + + Authentication test + 验证测试 + + + + HeadlessVncServer + + Headless VNC server + + + + + LdapBrowseDialog + + Browse LDAP + 浏览LDAP + + + + LdapClient + + LDAP error description: %1 + LDAP错误描述: %1 + + + + LdapConfigurationPage + + Basic settings + 基本设置 + + + General + 常规 + + + LDAP server and port + LDAP服务器和端口 + + + Bind DN + Bind DN + + + Bind password + Bind 密码 + + + Anonymous bind + 匿名 bind + + + Use bind credentials + 使用绑定的证书 + + + Base DN + Base DN + + + Fixed base DN + 固定 base DN + + + e.g. dc=example,dc=org + 例如: dc=example,dc=org + + + Discover base DN by naming context + 通过命名上下文来发现 base DN + + + e.g. namingContexts or defaultNamingContext + 例如, namingContexts 或 defaultNamingContext + + + Environment settings + 环境设置 + + + Object trees + Object trees + + + Computer tree + Computer tree + + + e.g. OU=Groups + 例如: OU=Groups + + + User tree + User tree + + + e.g. OU=Users + 例如: OU=Users + + + e.g. OU=Computers + 例如: OU=Computers + + + Group tree + Group tree + + + Perform recursive search operations in object trees + 在对象树(object trees)中执行递归搜索操作 + + + Object attributes + 对象属性 + + + e.g. hwAddress + 例如: hwAddress + + + e.g. member or memberUid + 例如 member 或者 memberUid + + + e.g. dNSHostName + 例如: dNSHostName + + + Computer MAC address attribute + 计算机 MAC 地址属性 + + + Group member attribute + 组成员属性 + + + e.g. uid or sAMAccountName + 例如: uid 或者 sAMAccountName + + + Advanced settings + 高级设置 + + + Optional object filters + 可选对象过滤器 + + + Filter for user groups + 用户组过滤器 + + + Filter for users + 用户过滤器 + + + Filter for computer groups + 计算机组过滤器 + + + Group member identification + 组成员标识 + + + Distinguished name (Samba/AD) + 专有名称 (Samba/AD) + + + List all groups of a user + 列出一个用户的所有组 + + + List all groups of a computer + 列出一个计算机的所有组 + + + Get computer object by IP address + 通过 IP 地址获取计算机对象 + + + LDAP connection failed + LDAP 连接失败 + + + LDAP bind failed + LDAP 绑定失败 + + + LDAP bind successful + LDAP 绑定成功 + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + 成功连接到 LDAP 服务器并执行 LDAP 绑定。 基本的 LDAP 设置配置正确。 + + + LDAP base DN test failed + LDAP 基础 DN 测试失败 + + + LDAP base DN test successful + LDAP 基础 DN 测试通过 + + + LDAP naming context test failed + LDAP 命名上下文测试失败 + + + LDAP naming context test successful + LDAP 命名上下文测试通过 + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + LDAP 命名上下文已成功查询。 发现以下基本 DN: +%1 + + + user tree + 用户树 + + + group tree + 组树 + + + computer tree + 计算机树 + + + Enter username + 请输入用户名 + + + Please enter a user login name (wildcards allowed) which to query: + 请输入要查询的用户登录名(允许使用通配符): + + + user objects + 用户对象 + + + Enter group name + 输入组名称 + + + Please enter a group name whose members to query: + 请输入要查询其成员的群组名称: + + + group members + 组成员 + + + Group not found + 未找到组 + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + 找不到名称为 "%1" 的组。 请检查组名称或组树参数。 + + + Enter computer name + 输入计算机名称 + + + computer objects + 计算机对象 + + + Enter computer DN + 输入计算机 + + + Please enter the DN of a computer whose MAC address to query: + 请输入要查询 MAC 地址的计算机的 DN: + + + computer MAC addresses + 计算机 MAC 地址 + + + users + 用户 + + + user groups + 用户组 + + + computer groups + 计算机组 + + + Please enter a user login name whose group memberships to query: + 请输入要查询的组成员资格的用户登录名: + + + groups of user + 用户组 + + + User not found + 未找到用户 + + + groups of computer + 计算机组 + + + Computer not found + 未找到计算机 + + + Enter computer IP address + 输入计算机 IP 地址 + + + Please enter a computer IP address which to resolve to an computer object: + 请输入要解析为计算机对象的计算机 IP 地址: + + + computers + 计算机 + + + LDAP %1 test failed + LDAP %1 测试失败 + + + LDAP %1 test successful + LDAP %1 测试成功 + + + The %1 has been queried successfully and %2 entries were found. + 已成功查询 %1 ,并找到 %2 个条目。 + + + %1 %2 have been queried successfully: + +%3 + %1 %2 查询成功: + +%3 + + + LDAP filter test failed + LDAP 过滤器测试失败 + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + 无法使用配置的过滤器查询任何 %1 。 请检查 LDAP 过滤器的 %1 。 + +%2 + + + LDAP filter test successful + LDAP 过滤器测试成功 + + + %1 %2 have been queried successfully using the configured filter. + %1 %2 使用配置的过滤器成功查询。 + + + (only if different from group tree) + (仅当与组树不同时) + + + Computer group tree + 计算机组树 + + + computer group tree + 计算机组树 + + + Filter for computers + 计算机过滤器 + + + e.g. room or computerLab + 例如:计算机教室或实验室 + + + Integration tests + 集成测试 + + + Computer groups + 计算机组 + + + e.g. name or description + 例如:名称或描述 + + + Filter for computer containers + 用于计算机的过滤器 + + + Computer containers or OUs + 计算机容器或 OU + + + Connection security + 连接安全性 + + + TLS certificate verification + TLS 证书验证 + + + System defaults + 系统默认值 + + + Never (insecure!) + 从不(不安全!) + + + Custom CA certificate file + 自定义 CA 证书文件 + + + None + 无 + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + 例如 (objectClass=computer) + + + e.g. (objectClass=group) + 例如 (objectClass=group) + + + e.g. (objectClass=person) + 例如 (objectClass=person) + + + e.g. (objectClass=room) or (objectClass=computerLab) + 例如 (objectClass=room) 或 (objectClass=computerLab) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + 例如 (objectClass=container) 或 (objectClass=organizationalUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + 无法查询配置的基本 DN。 请检查基本 DN 参数 + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + LDAP 基本 DN 已成功查询。 找到以下条目: + +%1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + 无法通过命名上下文查询基本 DN。 请检查命名上下文属性参数。 + +%1 + + + Certificate files (*.pem) + 证书文件 (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + 无法连接到 LDAP 服务器。 请检查服务器参数。 + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + 无法绑定到 LDAP 服务器。 请检查服务器参数并绑定证书。 + +%1 + + + Encryption protocol + 加密协议 + + + Computer location attribute + 计算机地点属性 + + + Computer display name attribute + 计算机显示名称属性 + + + Location name attribute + 地点名称属性 + + + e.g. cn or displayName + 例如 cn 或者 displayName + + + Computer locations identification + 计算机地点标识 + + + Identify computer locations (e.g. rooms) via: + 通过以下方式标识计算机地点(例如教室): + + + Location attribute in computer objects + 计算机对象中的地点属性 + + + List all entries of a location + 列出一个地点中的所有条目 + + + List all locations + 列出所有地点 + + + Enter computer display name + 输入计算机显示名称 + + + Please enter a computer display name to query: + 请输入要查询的计算机显示名称: + + + Enter computer location name + 输入计算机地点名称 + + + Please enter the name of a computer location (wildcards allowed): + 请输入计算机地点名称(允许使用通配符): + + + computer locations + 计算机地点 + + + Enter location name + 输入地点名称 + + + Please enter the name of a location whose entries to query: + 请输入要查询其条目的地点名称: + + + location entries + 地点条目 + + + LDAP test failed + LDAP测试失败 + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + 无法查询任何 %1。 请检查参数 %2 或输入现有对象的名称。 + +%3 + + + and + 和 + + + LDAP test successful + LDAP测试成功 + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + Could not query any entries in configured %1.无法查询已配置的 %1 中的任何条目。请检查参数 "%2"。 + +%3 + + + Browse + 浏览 + + + Test + 测试 + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + 主机名称存储为完全限定域名(FQDN,例如 myhost.example.org) + + + Computer hostname attribute + 计算机主机名属性 + + + Please enter a computer hostname to query: + 请输入要查询的计算机主机名: + + + Invalid hostname + 无效的主机名 + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + 您将计算机主机名配置为存储为完全限定域名(FQDN),但输入的是没有域名部分的主机名。 + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + 您将计算机主机名配置为存储没有域名的简单主机名,但输入了带有域名部分的主机名。 + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + 无法找到名称为 "%1" 的用户。 请检查用户名或用户树参数。 + + + Enter hostname + 输入主机名 + + + Please enter a computer hostname whose group memberships to query: + 请输入要查询其组成员的计算机主机名: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + 无法找到主机名为 "%1" 的计算机。 请检查主机名或计算机树参数。 + + + Hostname lookup failed + 主机名查询失败 + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + 无法查找IP地址为 %1 的主机名。请检查您的DNS服务器设置。 + + + User login name attribute + 用户登录名属性 + + + Configured attribute for user login name or computer hostname (OpenLDAP) + 用户登录名或计算机主机名的配置属性(OpenLDAP) + + + computer containers + 计算机容器 + + + + LdapPlugin + + Auto-configure the base DN via naming context + 通过命名上下文自动配置基本 DN + + + Query objects from LDAP directory + 从 LDAP 目录查询对象 + + + Show help about command + 显此命令的帮助信息 + + + Commands for configuring and testing LDAP/AD integration + 用于配置和测试 LDAP/AD 集成的命令 + + + Basic LDAP/AD support for Veyon + 对Veyon的基本LDAP/AD支持 + + + %1 (load computers and locations from LDAP/AD) + %1 (从LDAP/AD加载计算机和地点) + + + %1 (load users and groups from LDAP/AD) + %1(从LDAP/AD加载用户和组) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + 请参照此示例指定一个合法的LDAP url "ldap[s]://[user[:password]@]hostname[:port]" + + + No naming context attribute name given - falling back to configured value. + 没有提供命名上下文属性名称 - 回退到已配置的值。 + + + Could not query base DN. Please check your LDAP configuration. + 无法查询base DN。请检查您的LDAP配置。 + + + Configuring %1 as base DN and disabling naming context queries. + 正配置 %1 为base DN,并禁用命名上下文查询。 + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + 用于用户验证的自定义PAM服务 + + + User authentication + 用户验证 + + + Session management + 会话管理 + + + Display manager users + 显示管理员用户 + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + 通过插件实现 Linux 平台的抽象函数 + + + + LocationDialog + + Select location + 选择地点 + + + enter search filter... + 输入搜索管理器... + + + + MainToolBar + + Configuration + 配置 + + + Disable balloon tooltips + 禁用气球浮动提示 + + + Show icons only + 仅显示图标 + + + + MainWindow + + MainWindow + 主窗口 + + + toolBar + 工具栏 + + + General + 常规 + + + &File + 文件(&F) + + + &Help + 帮助(&H) + + + &Quit + 退出(&Q) + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + 打开(&O) + + + Ctrl+O + Ctrl+O + + + About Qt + 关于 Qt + + + Authentication impossible + 无法验证 + + + Configuration not writable + 配置文件不可写 + + + Load settings from file + 打开配置文件 + + + Save settings to file + 保存配置文件 + + + Unsaved settings + 不保存 + + + There are unsaved settings. Quit anyway? + 设置还未保存,确定要退出吗? + + + Veyon Configurator + Veyon 配置器 + + + Service + 服务 + + + Master + 主机 + + + Access control + 访问控制 + + + About Veyon + 关于 Veyon + + + Auto + 自动 + + + About + 关于 + + + %1 Configurator %2 + %1 配置器 %2 + + + JSON files (*.json) + JSON 文件 (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + 本地配置后端报告配置不可写入! 请以更高的权限来运行 %1 配置程序。 + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + 没有找到密钥验证文件或者您当前的文件已过期。 请使用 %1 配置程序创建新的密钥文件。 或者使用 %1 配置器设置登录验证。 否则,您将无法使用 %1 访问计算机。 + + + Access denied + 拒绝访问 + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + 根据本地配置,您不允许访问网络中的计算机。 请使用其他帐户登录或让系统管理员检查本地配置。 + + + Screenshots + 屏幕截图 + + + Feature active + 已激活的功能 + + + The feature "%1" is still active. Please stop it before closing %2. + 功能 "%1" 仍处于活动状态。 请在关闭 "%2" 之前停止它。 + + + Reset configuration + 重置配置 + + + Do you really want to reset the local configuration and revert all settings to their defaults? + 你真的想重置本地配置并将所有设置恢复为默认值吗? + + + Search users and computers + 搜索用户和计算机 + + + Align computers to grid + 将计算机图标与网格对齐 + + + %1 Configurator + %1 配置器 + + + Insufficient privileges + 权限不够 + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + 无法以管理权限运行。 请确保您的桌面环境安装了类似 sudo 的程序!程序将以普通用户权限运行。 + + + Only show powered on computers + 仅显示开机的计算机 + + + &Save settings to file + &S保存设置到文件 + + + &View + &视图 + + + &Standard + &标准 + + + &Advanced + &高级 + + + Use custom computer arrangement + 使用自定义的计算机排序 + + + Locations && computers + 地点及计算机 + + + Slideshow + + + + Spotlight + + + + Adjust size of computer icons automatically + + + + + MasterConfigurationPage + + Directories + 文件夹 + + + User configuration + 用户配置 + + + Feature on computer double click: + 在计算机图标上双击运行的功能: + + + Features + 功能 + + + All features + 所有功能 + + + Disabled features + 禁用的功能 + + + Screenshots + 屏幕截图 + + + <no feature> + <no feature> + + + Basic settings + 基本设置 + + + Behaviour + 行为 + + + Enforce selected mode for client computers + 为客户端计算机强制选择模式 + + + Hide local computer + 隐藏本地计算机 + + + Hide computer filter field + 隐藏计算机过滤器文本框 + + + Actions such as rebooting or powering down computers + 比如重新启动或关闭计算机的操作 + + + User interface + 用户界面 + + + Background color + 背景色 + + + Thumbnail update interval + 缩略图更新时间 + + + ms + 毫秒 + + + Program start + 程序启动 + + + Modes and features + 模式和功能 + + + User and computer name + 用户和计算机名称 + + + Only user name + 仅用户名 + + + Only computer name + 仅计算机名称 + + + Computer thumbnail caption + 计算机缩略图名称 + + + Text color + 文本颜色 + + + Sort order + 排序 + + + Computer and user name + 计算机和用户名 + + + Computer locations + 计算机地点 + + + Show current location only + 只显示当前地点 + + + Allow adding hidden locations manually + 允许手动添加隐藏地点 + + + Hide empty locations + 隐藏空地点 + + + Show confirmation dialog for potentially unsafe actions + 显示潜在危险操作的确认对话框 + + + Perform access control + 执行访问控制 + + + Automatically select current location + 自动选择当前地点 + + + Automatically open computer select panel + 自动打开计算机选择面板 + + + Hide local session + + + + px + + + + Thumbnail spacing + + + + Auto + 自动 + + + Thumbnail aspect ratio + + + + Automatically adjust computer icon size + + + + Open feature windows on the same screen as the main window + + + + + MonitoringMode + + Monitoring + 监控 + + + Builtin monitoring mode + 内置监控模式 + + + This mode allows you to monitor all computers at one or more locations. + 此模式允许您在一个或多个地点监控所有计算机。 + + + + NetworkObjectTreeModel + + Locations/Computers + 地点/计算机 + + + + OpenWebsiteDialog + + Open website + 打开网站 + + + e.g. Veyon + 例如 Veyon + + + Remember and add to website menu + 记住并添加到网站菜单 + + + e.g. www.veyon.io + 例如 www.veyon.io + + + Please enter the URL of the website to open: + 请输入要打开站点的网址: + + + Name: + 名称: + + + + PasswordDialog + + Username + 用户名 + + + Password + 密码 + + + Veyon Logon + Veyon 登录 + + + Authentication error + 验证错误 + + + Logon failed with given username and password. Please try again! + 使用给定的用户名和密码登录失败。 请再试一次! + + + Please enter your username and password in order to access computers. + 请输入您的用户名和密码才能访问计算机。 + + + + PowerControlFeaturePlugin + + Power on + 开机 + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + 点击此按钮开启所有电脑。这样您就不必手动启动每台电脑。 + + + Reboot + 重启 + + + Click this button to reboot all computers. + 点击此按钮重启所有电脑。 + + + Power down + 关机 + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + 点击此按钮关闭所有电脑。这样您就不必手动关闭每台电脑。 + + + Power on/down or reboot a computer + 打开/关闭电源或重新启动计算机 + + + Confirm reboot + 确认重启 + + + Confirm power down + 确认关机 + + + Do you really want to reboot the selected computers? + 你确定要重新启动选定的电脑吗? + + + Do you really want to power down the selected computer? + 你确定要关闭选定的电脑吗? + + + Power on a computer via Wake-on-LAN (WOL) + 通过局域网唤醒(WOL)打开计算机电源 + + + MAC ADDRESS + MAC 地址(网卡物理地) + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + 此命令将网络唤醒(WOL)数据包广播到网络,以便使用给定的MAC地址打开计算机的电源。 + + + Please specify the command to display help for! + 请指定需要显示用法命令! + + + Invalid MAC address specified! + 指定的 MAC 地址无效! + + + Commands for controlling power status of computers + 用于控制计算机电源状态的命令 + + + Power down now + 立即关机 + + + Install updates and power down + 安装更新并关机 + + + Power down after user confirmation + 用户确认后关机 + + + Power down after timeout + 超时后关机 + + + The computer was remotely requested to power down. Do you want to power down the computer now? + 此计算机请求远程关机。您是否需要现在关闭此计算机? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + 此计算机将在 %1 分钟,%2 秒后关闭。 + +请保存您的工作并关闭所有程序。 + + + + PowerDownTimeInputDialog + + Power down + 关机 + + + Please specify a timeout for powering down the selected computers: + 请指定关闭选定计算机的超时时间: + + + minutes + 分钟 + + + seconds + 秒 + + + + RemoteAccessFeaturePlugin + + Remote view + 远程桌面 + + + Open a remote view for a computer without interaction. + 非控制模式打开计算机远程视图。 + + + Remote control + 远程控制 + + + Open a remote control window for a computer. + 打开计算机的远程控制窗口。 + + + Remote access + 远程访问 + + + Remote view or control a computer + 远程查看或控制电脑 + + + Please enter the hostname or IP address of the computer to access: + 请输入要访问的计算机的主机名或IP地址: + + + Show help about command + 显此命令的帮助信息 + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 - %2 远程访问 + + + %1 - %2 - %3 Remote Access + + + + + RemoteAccessWidgetToolBar + + View only + 仅可视 + + + Remote control + 远程控制 + + + Send shortcut + 发送快捷键 + + + Fullscreen + 全屏 + + + Window + 窗口 + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + 菜单键 + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + 正在连接 %1 + + + Connected. + 已连接。 + + + Screenshot + 屏幕截图 + + + Exit + 退出 + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + 请输入要在所选计算机上运行的程序或命令。 您可以按行分隔多个程序/命令。 + + + Run programs + 运行程序 + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + 例如 "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + 名称: + + + Remember and add to program menu + 记住并添加到程序菜单 + + + e.g. VLC + 例如 VLC + + + + ScreenLockFeaturePlugin + + Lock + 锁定 + + + Unlock + 解锁 + + + Lock screen and input devices of a computer + 锁定计算机屏幕和输入设备 + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + 要集中所有用户的注意力,您可以使用此按钮锁定他们的计算机。 在这种模式下,所有输入设备都被锁定,屏幕变黑。 + + + Lock input devices + 锁定输入设备 + + + Unlock input devices + 解锁输入设备 + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + + + + + Screenshot + + unknown + 未知 + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + 无法截取截图,因为目录 %1 不存在且无法创建。 + + + Screenshot + 屏幕截图 + + + Could not open screenshot file %1 for writing. + + + + + ScreenshotFeaturePlugin + + Screenshot + 屏幕截图 + + + Use this function to take a screenshot of selected computers. + 使用此功能截取所选计算机的屏幕截图。 + + + Screenshots taken + 已截图 + + + Screenshot of %1 computer have been taken successfully. + %1 计算机屏幕已成功截图。 + + + Take screenshots of computers and save them locally. + 以计算机屏幕截图并将其保存在本地。 + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + 这里列出了您抓取的所有屏幕截图。 您可以通过单击计算机上下文菜单中的“屏幕截图”菜单来抓取屏幕截图。 截图可以使用下面的按钮进行管理。 + + + User: + 用户: + + + Computer: + 计算机: + + + Date: + 日期: + + + Time: + 时间: + + + Show + 显示 + + + Delete + 删除 + + + Screenshot + 屏幕截图 + + + Do you really want to delete all selected screenshots? + + + + + ServiceConfigurationPage + + General + 常规 + + + Autostart + 自动启动 + + + Hide tray icon + 隐藏托盘图标 + + + Start service + 开启服务 + + + Stopped + 已停止 + + + Stop service + 停止服务 + + + State: + 状态: + + + Enable firewall exception + 开启防火墙例外设定 + + + Allow connections from localhost only + 只允许本地连接 + + + VNC server + VNC 服务 + + + Plugin: + 插件: + + + Restart %1 Service + 重启 %1 服务 + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + 所有设置都已成功保存。为了生效,需要重新启动 %1 服务。现在重新启动它? + + + Running + 正在运行 + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + 启用此选项将启动计算机上每一个交互式会话的服务器进程。 +通常这是支持终端服务器所必需的。 + + + Show notification on remote connection + 在远程连接上显示通知 + + + Show notification when an unauthorized access is blocked + 当未授权访问被阻止时显示通知 + + + Sessions + + + + Single session mode (create server instance for local/physical session only) + + + + Multi session mode (create server instance for each local and remote desktop session) + + + + Maximum session count + + + + Network port numbers + + + + Veyon server + + + + Internal VNC server + + + + Feature manager + + + + Demo server + 演示服务 + + + Miscellaneous network settings + + + + + ServiceControl + + Starting service %1 + 启动服务 %1 + + + Stopping service %1 + 停止服务 %1 + + + Registering service %1 + 注册服务 %1 + + + Unregistering service %1 + 反注册服务 %1 + + + Service control + 服务控制 + + + + ServiceControlPlugin + + Service is running + 服务正在运行 + + + Service is not running + 服务没有运行 + + + Configure and control Veyon service + 配置和控制 Veyon 服务 + + + Register Veyon Service + 注册 Veyon 服务 + + + Unregister Veyon Service + 反注册 Veyon 服务 + + + Start Veyon Service + 启动 Veyon 服务 + + + Stop Veyon Service + 停止 Veyon 服务 + + + Restart Veyon Service + 重启 Veyon 服务 + + + Query status of Veyon Service + 查询 Veyon 服务状态 + + + Commands for configuring and controlling Veyon Service + 用于配置和控制Veyon服务的命令 + + + + ShellCommandLinePlugin + + Run command file + 运行命令文件 + + + File "%1" does not exist! + 文件 "%1" 不存在! + + + Interactive shell and script execution for Veyon Control + Veyon 控制器的交互式 shell 和脚本运行 + + + Commands for shell functionalities + shell功能的命令 + + + + SlideshowPanel + + Previous + + + + Start/pause + + + + Next + + + + Duration: + + + + + SpotlightPanel + + Add selected computers + + + + Remove selected computers + + + + Update computers in realtime + + + + Spotlight + + + + Please select at least one computer to add. + + + + Please select at least one computer to remove. + + + + Add computers by clicking with the middle mouse button or clicking the first button below. + + + + + SystemTrayIcon + + System tray icon + 系统托盘图标 + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + 系统用户组的用户组后端 + + + Default (system user groups) + 默认(系统用户组) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + 测试Veyon内部组件和功能 + + + Commands for testing internal components and functions of Veyon + 测试Veyon内部组件和功能的命令 + + + + TextMessageDialog + + Send text message + 发送消息 + + + Use the field below to type your message which will be sent to all selected users. + 在下面的文本框中输入您要发送给所选用户的消息。 + + + + TextMessageFeaturePlugin + + Text message + 文字消息 + + + Use this function to send a text message to all users e.g. to assign them new tasks. + 使用此功能可向所有用户发送文字信息,例如 为他们分配新任务。 + + + Message from teacher + 来自教师的消息 + + + Send a message to a user + 给一个用户发送消息 + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + 开启多层次(半透明)窗口抓取 + + + Poll full screen (leave this enabled per default) + 抓取全屏幕(默认每次开启) + + + Low accuracy (turbo mode) + 低分辨率(快速模式) + + + Builtin UltraVNC server configuration + 内置 UltraVNC 服务配置 + + + Enable multi monitor support + 启用多显示器支持 + + + Enable Desktop Duplication Engine on Windows 8 and newer + 启用Windows 8及更新操作系统上的桌面拷贝引擎 + + + Maximum CPU usage + + + + + UserConfig + + No write access + 没有写入权限 + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + 无法保存您的个人设置! 请使用 %1 配置器检查用户配置文件路径。 + + + + UserLoginDialog + + User login + 用户登陆 + + + Please enter a username and password for automatic login on all computers. + 请输入用来在所有计算机自动登陆的用户名和密码。 + + + Username + 用户名 + + + Password + 密码 + + + + UserSessionControlPlugin + + Log in + 登陆 + + + Click this button to log in a specific user on all computers. + 点击此按钮以便于在所有计算机上登陆制定账户 + + + Log off + 注销 + + + Click this button to log off users from all computers. + 点击此按钮在所有计算机上执行注销命令。 + + + Confirm user logoff + 确认用户注销 + + + Do you really want to log off the selected users? + 您确定要注销选定的用户吗? + + + User session control + 用户会话控制 + + + + VeyonCore + + [OK] + [OK] + + + [FAIL] + [FAIL] + + + Invalid command! + 无效的命! + + + Available commands: + 可用命令: + + + Invalid arguments given + 给定的参数无效 + + + Not enough arguments given - use "%1 help" for more information + 没有给出足够的参数 - 使用 "%1 help" 获取更多信息 + + + Unknown result! + 未知的结果! + + + Available modules: + 可用的模块: + + + No module specified or module not found - available modules are: + 没有指定的模块或找不到模块 - 可用模块为: + + + Plugin not licensed + 插件没有许可 + + + INFO + INFO + + + ERROR + ERROR + + + USAGE + 使用情况 + + + DESCRIPTION + 描述 + + + EXAMPLES + 示例 + + + WARNING + 警告 + + + + VeyonServiceControl + + Veyon Service + Veyon 服务 + + + + VncViewWidget + + Establishing connection to %1 ... + 正在建立到 %1 的连接... + + + + WebApiConfigurationPage + + Web API + + + + General + 常规 + + + Network port + + + + Enable WebAPI server + + + + Connection settings + + + + Lifetime + + + + h + + + + s + s + + + Idle timeout + + + + Authentication timeout + + + + Maximum number of open connections + + + + Connection encryption + + + + TLS certificate file + + + + TLS private key file + + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + + + + + WebApiPlugin + + Run WebAPI server + + + + Failed to start WebAPI server at port %1 + + + + WebAPI server running at port %1 + + + + Provide access to a computer via HTTP + + + + Commands for running the WebAPI server + + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + 无法通过软件更改 SAS 生成的设置。通过远程控制发送 Ctrl+Alt+Del 将不起作用! + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + 常规 + + + Enable SAS generation by software (Ctrl+Alt+Del) + 通过软件启用 SAS 生成(Ctrl + Alt + Del) + + + Screen lock + 屏幕锁 + + + Hide taskbar + 隐藏任务栏 + + + Hide start menu + 隐藏开始菜单 + + + Hide desktop + 隐藏桌面 + + + User authentication + 用户验证 + + + Use alternative user authentication mechanism + 使用其他用户验证机制 + + + User login + 用户登陆 + + + Input start delay + 输入启动延迟 + + + Simulated key presses interval + 模拟按键间隔 + + + Confirm legal notice (message displayed before user logs in) + + + + Use input device interception driver + + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + 插件实现 Windows 平台的抽象函数 + + + + WindowsServiceControl + + The service "%1" is already installed. + 服务 "%1" 已安装。 + + + The service "%1" could not be installed. + 服务 "%1" 未能安装。 + + + The service "%1" has been installed successfully. + 服务 "%1" 已成功安装。 + + + The service "%1" could not be uninstalled. + 服务 "%1" 未能卸载。 + + + The service "%1" has been uninstalled successfully. + 服务 "%1" 已成功卸载。 + + + The start type of service "%1" could not be changed. + 服务 "%1" 的启动类型无法更改。 + + + Service "%1" could not be found. + 服务 "%1" 未找到。 + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + 内置 x11vnc 服务配置 + + + Custom x11vnc parameters: + 定制 x11vnc 参数: + + + Do not use X Damage extension + 不要使用 X Damage 扩展 + + + \ No newline at end of file diff --git a/translations/veyon_zh_TW.ts b/translations/veyon_zh_TW.ts new file mode 100644 index 0000000..173face --- /dev/null +++ b/translations/veyon_zh_TW.ts @@ -0,0 +1,4081 @@ + + + AboutDialog + + About + 關於 + + + Translation + 翻譯 + + + License + 授權 + + + About Veyon + 關於 Veyon + + + Contributors + 貢獻者 + + + Version: + 版本: + + + Website: + 網站: + + + Current language not translated yet (or native English). + +If you're interested in translating Veyon into your local or another language or want to improve an existing translation, please contact a Veyon developer! + 目前語言尚未翻譯 (或原生英語)。 + +如果您有興趣翻譯 Veyon 為您的本地或另一種語言或想要改善現有的翻譯,請聯絡 Veyon 開發人員! + + + About %1 %2 + 關於 %1 %2 + + + Support Veyon project with a donation + 以捐贈支持 Veyon 專案 + + + + AccessControlPage + + Computer access control + 電腦存取控制 + + + Grant access to every authenticated user (default) + 每個已驗證的使用者授予存取權限 (預設值) + + + Test + 測試 + + + Process access control rules + 處理程序存取控制規則 + + + User groups authorized for computer access + 授權電腦存取的使用者群組 + + + Please add the groups whose members should be authorized to access computers in your Veyon network. + 請加入群組,其成員應授權存取 Veyon 網路中的電腦。 + + + Authorized user groups + 授權的使用者群組 + + + All groups + 所有群組 + + + Access control rules + 存取控制規則 + + + Add access control rule + 加入存取控制規則 + + + Remove access control rule + 移除存取控制規則 + + + Move selected rule down + 向下移動所選的規則 + + + Move selected rule up + 向上移動所選的規則 + + + Edit selected rule + 編輯所選的規則 + + + Enter username + 輸入使用者名稱 + + + Please enter a user login name whose access permissions to test: + 請輸入測試其存取權限的使用者登入名稱: + + + Access allowed + 允許存取 + + + The specified user is allowed to access computers with this configuration. + 允許指定的使用者以這個組態存取電腦。 + + + Access denied + 拒絕存取 + + + The specified user is not allowed to access computers with this configuration. + 不允許指定的使用者以這個組態存取電腦。 + + + Enable usage of domain groups + 啟用網域群組使用 + + + User groups backend: + 使用者群組後端: + + + Missing user groups backend + 缺少使用者群組後端 + + + No default user groups plugin was found. Please check your installation! + 找不到預設的使用者群組外掛件。 請檢查您的安裝! + + + Restrict access to members of specific user groups + 限制對指定使用者群組成員的存取 + + + + AccessControlRuleEditDialog + + Edit access control rule + 編輯存取控制規則 + + + General + 一般 + + + enter a short name for the rule here + 在此輸入規則的簡稱 + + + Rule name: + 規則名稱: + + + enter a description for the rule here + 在此輸入規則的描述 + + + Rule description: + 規則描述: + + + Invert all conditions ("is/has" interpreted as "is/has not") + 反向所有條件 ("是/有" 解釋為 "不是/沒有") + + + Conditions + 條件 + + + is member of group + 是群組的成員 + + + Accessing computer is localhost + 存取電腦是 localhost + + + Accessing user is logged on user + 存取使用者是登入的使用者 + + + Accessing user is already connected + 存取使用者已經連線 + + + If more than one condition is activated each condition has to meet in order to make the rule apply (logical AND). If only one of multiple conditions has to meet (logical OR) please create multiple access control rules. + 如果有啟用一個以上條件,每個條件必須滿足,以便使規則套用 (邏輯 AND)。 如果多個條件只有一個必須滿足 (邏輯 OR),請建立多個存取控制規則。 + + + Action + 動作 + + + Allow access + 允許存取 + + + Deny access + 拒絕存取 + + + Ask logged on user for permission + 詢問登入使用者的權限 + + + None (rule disabled) + 無 (停用規則) + + + Accessing user + 存取使用者 + + + Accessing computer + 存取電腦 + + + Local (logged on) user + 本機 (登入的) 使用者 + + + Local computer + 本機電腦 + + + Always process rule and ignore conditions + 始終處理規則和忽略條件 + + + No user logged on + 沒有使用者登入 + + + Accessing user has one or more groups in common with local (logged on) user + 存取使用者與本機 (登入的) 使用者有一個或數個群組在通用 + + + Accessing computer and local computer are at the same location + 存取電腦和本機電腦位於同一位置 + + + is located at + 位於 + + + + AccessControlRulesTestDialog + + Access control rules test + 存取控制規則測試 + + + Accessing user: + 存取使用者: + + + Local computer: + 本機電腦: + + + Accessing computer: + 存取電腦: + + + Please enter the following user and computer information in order to test the configured ruleset. + 請輸入以下使用者和電腦的資訊,以便測試組態的規則集。 + + + Local user: + 本機使用者: + + + Connected users: + 已連線的使用者: + + + The access in the given scenario is allowed. + 允許在給予情境中存取。 + + + The access in the given scenario is denied. + 拒絕在給予情境中存取。 + + + The access in the given scenario needs permission of the logged on user. + 在給予情境中存取。需要登入使用者的權限。 + + + ERROR: Unknown action + 錯誤: 未知的動作 + + + Test result + 測試結果 + + + + AuthKeysConfigurationPage + + Authentication keys + 身份驗證金鑰 + + + Introduction + 介紹 + + + Key file directories + 金鑰檔目錄 + + + Public key file base directory + 公開金鑰檔基礎目錄 + + + Private key file base directory + 私密金鑰檔基礎目錄 + + + Available authentication keys + 可用身份驗證金鑰 + + + Create key pair + 建立金鑰配對 + + + Delete key + 刪除金鑰 + + + Import key + 匯入金鑰 + + + Export key + 匯出金鑰 + + + Set access group + 設定存取群組 + + + Key files (*.pem) + 金鑰檔 (*.pem) + + + Authentication key name + 身份驗證金鑰名稱 + + + Please enter the name of the user group or role for which to create an authentication key pair: + 請輸入要為其建立身份驗證金鑰配對的使用者群組或角色的名稱: + + + Do you really want to delete authentication key "%1/%2"? + 您真的要刪除身份驗證金鑰 "%1/%2" 嗎? + + + Please select a key to delete! + 請選取要刪除的金鑰! + + + Please enter the name of the user group or role for which to import the authentication key: + 請輸入要為其匯入身份驗證金鑰的使用者群組或角色的名稱: + + + Please select a key to export! + 請選擇要匯出的金鑰! + + + Please select a user group which to grant access to key "%1": + 請選擇要授予對金鑰 "%1" 存取權限的使用者群組: + + + Please select a key which to set the access group for! + 請選擇要為其設定存取群組的金鑰! + + + Please perform the following steps to set up key file authentication: + 請執行以下步驟以設定金鑰檔身份驗證: + + + 1) Create a key pair on the master computer. + 1) 在主電腦上建立金鑰對。 + + + 2) Set an access group whose members should be allowed to access other computers. + 2) 設定一個存取群組,應該允許其成員存取其它電腦。 + + + 3) Export the public key and import it on all client computers with the same name. + 3) 匯出公開金鑰並將其匯入到具有相同名稱的所有用戶端電腦上。 + + + Please refer to the <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon Administrator Manual</a> for more information. + 請參閱 <a href="https://veyon.readthedocs.io/en/latest/admin/index.html">Veyon 管理員手冊</a>,取得更多資訊。 + + + An authentication key pair consist of two coupled cryptographic keys, a private and a public key. +A private key allows users on the master computer to access client computers. +It is important that only authorized users have read access to the private key file. +The public key is used on client computers to authenticate incoming connection request. + 身份驗證金鑰配對由兩個耦合的加密金鑰,一個私有金鑰和一個公開金鑰組成。 +私密金鑰允許主機電腦上的使用者存取用戶端電腦。 +重要的是,只有授權使用者才能對私密金鑰檔進行讀取存取。 +公開金鑰用於用戶端電腦以驗證傳入連線請求。 + + + + AuthKeysManager + + Please check your permissions. + 請檢查您的權限。 + + + Key name contains invalid characters! + 金鑰名稱包含無效字元! + + + Invalid key type specified! Please specify "%1" or "%2". + 指定的金鑰類型無效! 請指定 "%1" 或 "%2"。 + + + Specified key does not exist! Please use the "list" command to list all installed keys. + 指定的金鑰不存在!請使用 "list" 命令以列出所有安裝的金鑰。 + + + One or more key files already exist! Please delete them using the "delete" command. + 一個或多個金鑰檔已經存在! 請使用 "delete" 命令刪除它們。 + + + Creating new key pair for "%1" + 正在建立 "%1" 的金鑰配對 + + + Failed to create public or private key! + 建立公用金鑰或私密金鑰失敗! + + + Newly created key pair has been saved to "%1" and "%2". + 新建立的金鑰配對已儲存到 "%1" 和 "%2"。 + + + Could not remove key file "%1"! + 無法移除金鑰檔 "%1"! + + + Could not remove key file directory "%1"! + 無法移除金鑰檔目錄 "%1"! + + + Failed to create directory for output file. + 建立輸出檔案的目錄失敗。 + + + File "%1" already exists. + 檔案 "%1" 已經存在。 + + + Failed to write output file. + 寫入輸出檔案失敗。 + + + Key "%1/%2" has been exported to "%3" successfully. + 金鑰 "%1/%2" 已匯出到 "%3" 成功。 + + + Failed read input file. + 讀取輸入檔案失敗。 + + + File "%1" does not contain a valid private key! + 檔案 "%1" 不含有效私密金鑰! + + + File "%1" does not contain a valid public key! + 檔案 "%1" 不含有效公開金鑰! + + + Failed to create directory for key file. + 建立金鑰檔的目錄失敗。 + + + Failed to write key file "%1". + 寫入金鑰檔 "%1" 失敗。 + + + Failed to set permissions for key file "%1"! + 設定金鑰檔 "%1" 的權限失敗! + + + Key "%1/%2" has been imported successfully. Please check file permissions of "%3" in order to prevent unauthorized accesses. + 金鑰 "%1/%2" 匯入成功。 請檢查 "%3" 的檔案權限以便防止未經授權的存取。 + + + Failed to convert private key to public key + 將私密金鑰轉換為公開金鑰失敗 + + + Failed to create directory for private key file "%1". + 建立私密金鑰檔 "%1" 的目錄失敗。 + + + Failed to save private key in file "%1"! + 在檔案 "%1" 中儲存私密金鑰失敗! + + + Failed to set permissions for private key file "%1"! + 設定私密金鑰檔 "%1" 的權限失敗! + + + Failed to create directory for public key file "%1". + 建立公開金鑰檔 "%1" 的目錄失敗。 + + + Failed to save public key in file "%1"! + 在檔案 "%1" 中儲存公開金鑰失敗! + + + Failed to set permissions for public key file "%1"! + 設定公開金鑰檔 "%1" 的權限失敗! + + + Failed to set owner of key file "%1" to "%2". + 設定金鑰檔 "%1" 的擁有者為 "%2" 失敗。 + + + Failed to set permissions for key file "%1". + 設定金鑰檔 "%1" 的權限失敗。 + + + Key "%1" is now accessible by user group "%2". + 金鑰 "%1" 現在可由 使用者群組 "%2" 存取。 + + + <N/A> + <不適用> + + + Failed to read key file. + 讀取金鑰檔失敗。 + + + + AuthKeysPlugin + + Create new authentication key pair + 建立新的驗證金鑰配對 + + + Delete authentication key + 刪除驗證金鑰 + + + List authentication keys + 列出驗證金鑰 + + + Import public or private key + 匯入公開或私密金鑰 + + + Export public or private key + 匯出公開或私密金鑰 + + + Extract public key from existing private key + 從現有私密金鑰擷取公開金鑰 + + + Set user group allowed to access a key + 設定允許存取金鑰的使用者群組 + + + KEY + 金鑰 + + + ACCESS GROUP + 存取群組 + + + This command adjusts file access permissions to <KEY> such that only the user group <ACCESS GROUP> has read access to it. + 這個命令將對 <KEY> 的檔案存取權限進行調整,使得只有使用者群組 <ACCESS GROUP> 才具有對其讀取權限。 + + + NAME + 名稱 + + + FILE + 檔案 + + + This command exports the authentication key <KEY> to <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + 這個命令將驗證金鑰 <KEY> 匯出到 <FILE>。 如果沒有指定 <FILE>,將根據 <KEY> 的名稱和類型構造名稱。 + + + This command imports the authentication key <KEY> from <FILE>. If <FILE> is not specified a name will be constructed from name and type of <KEY>. + 這個命令從 <FILE> 匯入身分驗證金鑰 <KEY>。 如果未指定 <FILE>,則名稱將從 <KEY> 的名稱和類型構造。 + + + This command lists all available authentication keys in the configured key directory. If the option "%1" is specified a table with key details will be displayed instead. Some details might be missing if a key is not accessible e.g. due to the lack of read permissions. + 這個命令列出組態的金鑰目錄中所有可用的驗證金鑰。 如果有指定選項「%1」,則會顯示包含金鑰詳細資料的表格。 如果無法存取金鑰,某些詳細資料可能會遺失,例如: 由於缺乏讀取權限。 + + + Please specify the command to display help for! + 請指定顯示說明的命令! + + + TYPE + 類型 + + + PAIR ID + 配對 ID + + + Command line support for managing authentication keys + 管理驗證金鑰的命令列支援 + + + Commands for managing authentication keys + 管理驗證金鑰的命令 + + + This command creates a new authentication key pair with name <NAME> and saves private and public key to the configured key directories. The parameter must be a name for the key, which may only contain letters. + 這個命令建立一個名稱為 <NAME> 的新身份驗證金鑰組,並將私密金鑰和公開金鑰儲存到組態的金鑰目錄。 參數必須是金鑰名稱,金鑰可以只包含字母。 + + + This command deletes the authentication key <KEY> from the configured key directory. Please note that a key can't be recovered once it has been deleted. + 這個命令從組態的金鑰目錄中刪除身份驗證金鑰 <KEY>。 請注意,金鑰刪除後將無法恢復。 + + + This command extracts the public key part from the private key <KEY> and saves it as the corresponding public key. When setting up another master computer, it is therefore sufficient to transfer the private key only. The public key can then be extracted. + 這個命令從私密金鑰 <KEY> 中提取公開金鑰部分,並將其儲存為相應的公開金鑰。 因此,在設定另一部主機電腦時,只需要傳輸私密金鑰。接著可以提取公開金鑰。 + + + + AuthKeysTableModel + + Name + 名稱 + + + Type + 類型 + + + Access group + 存取群組 + + + Pair ID + 配對 ID + + + + BuiltinDirectoryConfigurationPage + + Computers + 電腦 + + + Name + 名稱 + + + Host address/IP + 主機位址/IP + + + MAC address + MAC 位址 + + + Add new computer + 加入新的電腦 + + + Remove selected computer + 移除選取的電腦 + + + New computer + 新電腦 + + + Builtin directory + 內建目錄 + + + Locations & computers + 位置 & 電腦 + + + Locations + 位置 + + + Add new location + 加入新位置 + + + Remove selected location + 移除選取的位置 + + + The import of CSV files is possible through the command line interface. For more information, see the <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">online documentation</a>. + 可以通過命令列介面匯入 CSV 檔。 有關詳細資訊,請參閱 <a href="https://docs.veyon.io/en/latest/admin/cli.html#network-object-directory">線上文件</a>。 + + + New location + 新位置 + + + + BuiltinDirectoryPlugin + + Show help for specific command + 顯示指定命令的說明 + + + Import objects from given file + 從給予的檔案匯入物件 + + + Export objects to given file + 匯出物件到給予的檔案 + + + Invalid type specified. Valid values are "%1" or "%2". + 指定的類型無效。 有效值 "%1" 或 "%2"。 + + + Type + 類型 + + + Name + 名稱 + + + Host address + 主機位址 + + + MAC address + MAC 位址 + + + Specified object not found. + 找不到指定的物件。 + + + File "%1" does not exist! + 檔案 "%1" 不存在! + + + Can't open file "%1" for reading! + 無法開啟檔案 "%1" 來讀取! + + + Unknown argument "%1". + 不明引數 "%1"。 + + + Computer "%1" (host address: "%2" MAC address: "%3") + 電腦 "%1" (主機位址: "%2" MAC 位址: "%3") + + + Unclassified object "%1" with ID "%2" + ID "%2" 的未分類物件 "%1" + + + None + 無 + + + Computer + 電腦 + + + Root + 根 + + + Invalid + 無效 + + + Error while parsing line %1. + 分析行 %1 時錯誤。 + + + Network object directory which stores objects in local configuration + 網路物件目錄期存放物件在本機組態 + + + Commands for managing the builtin network object directory + 管理內建網路物件目錄的命令 + + + No format string or regular expression specified! + 沒有指定格式字串或正則表達式! + + + Can't open file "%1" for writing! + 無法開啟檔案 "%1" 來寫入! + + + No format string specified! + 沒有指定格式字串! + + + Object UUID + 物件 UUID + + + Parent UUID + 上層 UUID + + + Add a location or computer + 加入位置或電腦 + + + Clear all locations and computers + 清除所有位置和電腦 + + + Dump all or individual locations and computers + 傾印所有或個別位置和電腦 + + + List all locations and computers + 列出所有位置和電腦 + + + Remove a location or computer + 移除所有位置和電腦 + + + Location "%1" + 位置 "%1" + + + Builtin (computers and locations in local configuration) + 內建 (本機組態中位置和電腦) + + + Location + 位置 + + + FILE + 檔案 + + + LOCATION + 位置 + + + FORMAT-STRING-WITH-PLACEHOLDERS + 格式字串含預留位置 + + + REGULAR-EXPRESSION-WITH-PLACEHOLDER + 正則運算式含預留位置 + + + Imports objects from the specified text file using the given format string or regular expression containing one or multiple placeholders. Valid placeholders are: %1 + 使用包含一個或多個預留位置的給予格式字串或正則運算式,從指定的文字檔匯入物件。 有效的預留位置為: %1 + + + Import simple CSV file to a single room + 將簡單 CSV 檔匯入到單一教室 + + + Import CSV file with location name in first column + 匯入第一列中含有位置名稱的 CSV 檔 + + + Import text file with with key/value pairs using regular expressions + 使用正則運算式匯入含金鑰/值配對的文字檔 + + + Import arbitrarily formatted data + 匯入任意格式的資料 + + + Exports objects to the specified text file using the given format string containing one or multiple placeholders. Valid placeholders are: %1 + 使用包含一個或多個預留位置的給予格式字串將物件匯出到指定的文字檔。 有效的預留位置為: %1 + + + Export all objects to a CSV file + 將所有物件匯出到 CSV 檔 + + + Export all computers in a specific location to a CSV file + 將指定位置中所有電腦匯出到 CSV 檔 + + + TYPE + 類型 + + + NAME + 名稱 + + + PARENT + 上層 + + + Adds an object where %1 can be one of "%2" or "%3". %4 can be specified by name or UUID. + 加入一個物件,其中 %1 可以是 "%2" 或 "%3" 之一。 %4 可以依名稱或 UUID 指定。 + + + Add a room + 加入教室 + + + Add a computer to room %1 + 將電腦加入到教室 %1 + + + OBJECT + 物件 + + + Removes the specified object from the directory. %1 can be specified by name or UUID. Removing a location will also remove all related computers. + 從目錄中移除指定的物件。 %1 可以依名稱或 UUID 指定。 移除位置也將移除所有相關電腦。 + + + Remove a computer by name + 依名稱移除電腦 + + + Remove an object by UUID + 依 UUID 移除物件 + + + "Room 01" + "教室 01" + + + "Computer 01" + "電腦 01" + + + HOST ADDRESS + 主機位址 + + + MAC ADDRESS + MAC 位址 + + + + BuiltinUltraVncServer + + Builtin VNC server (UltraVNC) + 內建 VNC 伺服器 (UltraVNC) + + + + BuiltinX11VncServer + + Builtin VNC server (x11vnc) + 內建 VNC 伺服器 (x11vnc) + + + + ComputerControlListModel + + Host/IP address: %1 + 主機/IP 位址: %1 + + + Active features: %1 + 可用功能: %1 + + + Online and connected + 線上和已連線 + + + Establishing connection + 建立連線 + + + Computer offline or switched off + 電腦離線或關機 + + + Authentication failed or access denied + 身份驗證失敗或存取拒絕 + + + Disconnected + 中斷連線 + + + No user logged on + 沒有使用者登入 + + + Logged on user: %1 + 登入的使用者: %1 + + + Location: %1 + 位置: %1 + + + Veyon Server unreachable or not running + 無法連線 Veyon 伺服器或未執行 + + + [no user] + [沒有使用者] + + + + ComputerControlServer + + %1 Service %2 at %3:%4 + %1 服務 %2 在 %3:%4 + + + Authentication error + 身份驗證錯誤 + + + Remote access + 遠端存取 + + + User "%1" at host "%2" is now accessing this computer. + 主機 "%2" 的使用者 "%1" 目前正在存取這部電腦。 + + + User "%1" at host "%2" attempted to access this computer but could not authenticate successfully. + 主機 "%2" 的使用者 "%1" 嘗試存取這部電腦,但無法成功進行身份驗證。 + + + Access control error + 存取控制錯誤 + + + User "%1" at host "%2" attempted to access this computer but has been blocked due to access control settings. + 主機 "%2" 的使用者 "%1" 嘗試存取這部電腦,但因為存取控制設定已封鎖。 + + + Active connections: + 使用中連線: + + + + ComputerManager + + User + 使用者 + + + Missing network object directory plugin + 缺少網路物件目錄外掛程式 + + + No default network object directory plugin was found. Please check your installation or configure a different network object directory backend via %1 Configurator. + 找不到預設網路物件目錄外掛程式。 請檢查您的安裝或透過 %1 組態器設定不同的網路物件目錄後端。 + + + Location detection failed + 位置偵測失敗 + + + Computer name;Hostname;User + 電腦名稱;主機名稱,使用者 + + + Could not determine the location of this computer. This indicates a problem with the system configuration. All locations will be shown in the computer select panel instead. + 無法確定這部電腦的位置。 這表示系統組態有問題。 所有位置將顯示在電腦選取面板中。 + + + + ComputerSelectPanel + + Computer search + 電腦搜尋 + + + Add location + 加入位置 + + + Save computer/user list + 儲存電腦/使用者清單 + + + Select output filename + 選擇輸出檔案名稱 + + + CSV files (*.csv) + CSV 檔 (*.csv) + + + File error + 檔案錯誤 + + + Could not write the computer and users list to %1! Please check the file access permissions. + 無法寫入電腦和使用者清單到 %1! 請檢查檔案的存取權限。 + + + + ConfigCommandLinePlugin + + Please specify an existing configuration file to import. + 請指定要匯入的現有組態檔。 + + + Please specify a valid filename for the configuration export. + 請指定一個有效的檔案名稱作為組態匯出。 + + + Please specify a valid key. + 請指定一個有效的金鑰。 + + + Specified key does not exist in current configuration! + 在目前組態中指定的金鑰不存在! + + + Please specify a valid value. + 請指定一個有效的值。 + + + Configure Veyon at command line + 在命令列上組態 Veyon + + + Output file is not writable! + 輸出檔案無法寫入! + + + Output directory is not writable! + 輸出目錄無法寫入! + + + Configuration file is not readable! + 組態檔無法讀取! + + + Clear system-wide Veyon configuration + 清除系統範圍 Veyon 組態 + + + List all configuration keys and values + 列出所有組態金鑰和數值 + + + Import configuration from given file + 從給予的檔案匯入組態 + + + Export configuration to given file + 匯出組態到給予的檔案 + + + Read and output configuration value for given key + 讀取和輸出給予金鑰的組態值 + + + Write given value to given configuration key + 寫入給予的數值到給予的組態金鑰 + + + Unset (remove) given configuration key + 取消設定 (移除) 給予的組態金鑰 + + + Commands for managing the configuration of Veyon + 管理 Veyon 組態的命令 + + + Upgrade and save configuration of program and plugins + 升級並儲存程式及外掛的組態 + + + + ConfigurationManager + + Could not modify the autostart property for the %1 Service. + 無法修改 %1 服務的自動啟動內容。 + + + Could not configure the firewall configuration for the %1 Server. + 無法組態 %1 伺服器的防火牆設定。 + + + Could not configure the firewall configuration for the %1 Worker. + 無法組態 %1 Worker 的防火牆設定。 + + + Configuration is not writable. Please check your permissions! + 組態未寫入,請檢查您的權限! + + + Could not apply platform-specific configuration settings. + 無法套用特定平臺的組態設定。 + + + + DemoClient + + %1 Demo + %1 示範 + + + + DemoConfigurationPage + + Demo server + 示範伺服器 + + + Tunables + 可調整 + + + ms + 毫秒 + + + Key frame interval + 關鍵框架間隔 + + + Memory limit + 記憶體限制 + + + MB + MB + + + Update interval + 更新間隔 + + + s + 秒 + + + Slow down thumbnail updates while demo is running + 示範執行時,縮圖更新速度變慢 + + + + DemoFeaturePlugin + + Stop demo + 停止示範 + + + Window demo + 視窗示範 + + + Give a demonstration by screen broadcasting + 透過螢幕廣播給予示範 + + + In this mode your screen being displayed in a window on all computers. The users are able to switch to other windows as needed. + 在這個模式下,所有電腦上的畫面都以視窗顯示,使用者可以視需要切換到其它視窗。 + + + Demo + 示範 + + + Share your screen or allow a user to share his screen with other users. + 共用螢幕或允許使用者與其他使用者共用其螢幕。 + + + Full screen demo + 全螢幕示範 + + + Share your own screen in fullscreen mode + 以全螢幕模式共用您自己的螢幕 + + + In this mode your screen is being displayed in full screen mode on all computers while the input devices of the users are locked. + 在此模式下,使用者的輸入裝置鎖定時,您的螢幕以全螢幕模式在所有電腦上顯示。 + + + Share your own screen in a window + 在視窗共用您自己的螢幕 + + + Share selected user's screen in fullscreen mode + 以全螢幕模式共用選取使用者的螢幕 + + + In this mode the screen of the selected user is being displayed in full screen mode on all computers while the input devices of the users are locked. + 在此模式下,使用者的輸入裝置鎖定時,選取使用者的螢幕以全螢幕模式在所有電腦上顯示。 + + + Share selected user's screen in a window + 在視窗共用選取使用者的螢幕 + + + In this mode the screen of the selected user being displayed in a window on all computers. The users are able to switch to other windows as needed. + 在此模式下,選取使用者的螢幕顯示在所有電腦的視窗中。 使用者能夠根據需要切換到其它視窗。 + + + Please select a user screen to share. + 請選取要共用的使用者螢幕。 + + + Please select only one user screen to share. + 請只選取一個使用者螢幕進行共用。 + + + All screens + 所有螢幕 + + + Screen %1 [%2] + 螢幕 %1 [%2] + + + + DesktopAccessDialog + + Desktop access dialog + 桌面存取對話方塊 + + + Confirm desktop access + 確認桌面存取 + + + Never for this session + 永不對這個工作階段 + + + Always for this session + 始終對這個工作階段 + + + The user %1 at computer %2 wants to access your desktop. Do you want to grant access? + 在電腦 %2 的使用者 %1 想要存取您的桌面。 您想要授予存取嗎? + + + + DesktopServicesConfigurationPage + + Programs & websites + 程式 & 網站 + + + Predefined programs + 預先定義程式 + + + Name + 名稱 + + + Path + 路徑 + + + Add new program + 加入新的程式 + + + Remove selected program + 移除所選程式 + + + Predefined websites + 預先定義網站 + + + Remove selected website + 移除所選網站 + + + URL + URL + + + New program + 新程式 + + + New website + 新網站 + + + + DesktopServicesFeaturePlugin + + Run program + 執行程式 + + + Open website + 開啟網站 + + + Click this button to open a website on all computers. + 按一下這個按鈕可以在所有電腦上開啟一個網站。 + + + Start programs and services in user desktop + 啟動程式和服務在使用者桌面 + + + Click this button to run a program on all computers. + 按一下這個按鈕可以在所有電腦上執行一個程式。 + + + Run program "%1" + 執行程式 "%1" + + + Custom program + 自訂程式 + + + Open website "%1" + 開啟網站 "%1" + + + Custom website + 自訂網站 + + + + DocumentationFigureCreator + + Teacher + 老師 + + + Room %1 + 教室 %1 + + + Please complete all tasks within the next 5 minutes. + 請在接下來的 5 分鐘內完成所有工作。 + + + Custom website + 自訂網站 + + + Open file manager + 開啟檔案管理員 + + + Start learning tool + 開始學習工具 + + + Play tutorial video + 播放指南視訊 + + + Custom program + 自訂程式 + + + Handout + 講義 + + + Texts to read + 要閱讀的文字 + + + generic-student-user + 通用學生使用者 + + + + ExternalVncServer + + External VNC server + 外部 VNC 伺服器 + + + + ExternalVncServerConfigurationWidget + + External VNC server configuration + 外部 VNC 伺服器組態 + + + Port: + 連接埠: + + + Password: + 密碼: + + + + FeatureControl + + Feature control + 功能控制 + + + + FileTransferConfigurationPage + + File transfer + 檔案傳輸 + + + Directories + 目錄 + + + Destination directory + 目的地目錄 + + + Default source directory + 預設來源目錄 + + + Options + 選項 + + + Remember last source directory + 記住上次來源目錄 + + + Create destination directory if it does not exist + 如果目的地目錄不存在則建立 + + + + FileTransferController + + Could not open file "%1" for reading! Please check your permissions! + 無法開啟檔案 "%1" 來讀取! 請檢查您的權限! + + + + FileTransferDialog + + File transfer + 檔案傳輸 + + + Options + 選項 + + + Transfer only + 只傳輸 + + + Transfer and open file(s) with associated program + 傳輸並使用關聯的程式開啟檔案 + + + Transfer and open destination folder + 傳輸並開啟目的地資料夾 + + + Files + 檔案 + + + Start + 開始 + + + Overwrite existing files + 覆寫現有檔案 + + + + FileTransferPlugin + + File transfer + 檔案傳輸 + + + Click this button to transfer files from your computer to all computers. + 按一下這個按鈕可將檔案從電腦傳輸到所有電腦。 + + + Select one or more files to transfer + 選擇要傳輸的一個或多個檔案 + + + Transfer files to remote computer + 將檔案傳輸到遠端電腦 + + + Received file "%1". + 收到的檔案 "%1"。 + + + Could not receive file "%1" as it already exists. + 無法接收檔案 "%1",因為它已經存在。 + + + Could not receive file "%1" as it could not be opened for writing! + 無法接收檔案 "%1",因為無法開啟來寫入! + + + + GeneralConfigurationPage + + User interface + 使用者介面 + + + Language: + 語言: + + + Use system language setting + 使用系統語言設定 + + + Veyon + Veyon + + + Logging + 紀錄 + + + Log file directory + 紀錄檔目錄 + + + Log level + 紀錄等級 + + + Nothing + 無 + + + Only critical messages + 只紀錄嚴重錯誤的訊息 + + + Errors and critical messages + 錯誤與嚴重錯誤訊息 + + + Warnings and errors + 警告和錯誤 + + + Information, warnings and errors + 資訊,警告和錯誤 + + + Debug messages and everything else + 除錯訊息和所有一切 + + + Limit log file size + 限制紀錄檔大小 + + + Clear all log files + 清除所有的紀錄檔 + + + Log to standard error output + 紀錄到標準錯誤輸出 + + + Network object directory + 網路物件目錄 + + + Backend: + 後端: + + + Update interval: + 更新間隔: + + + %1 service + %1 服務 + + + The %1 service needs to be stopped temporarily in order to remove the log files. Continue? + %1 服務需要暫時停止,以便刪除日誌檔。 繼續? + + + Log files cleared + 紀錄檔已清除 + + + All log files were cleared successfully. + 所有紀錄檔已成功清除。 + + + Error + 錯誤 + + + Could not remove all log files. + 無法移除所有的紀錄檔。 + + + MB + MB + + + Rotate log files + 旋轉日誌檔 + + + x + x + + + seconds + 秒 + + + Write to logging system of operating system + 寫入到作業系統的記錄系統 + + + Authentication + 身份驗證 + + + Method: + 方式: + + + Logon authentication + 登入驗證 + + + Key file authentication + 金鑰檔身份驗證 + + + Test + 測試 + + + Authentication is set up properly on this computer. + 在這部電腦中正確設定身份驗證。 + + + Authentication keys are not set up properly on this computer. + 這部電腦中的身份驗證金鑰設定不正確。 + + + Authentication test + 身份驗證測試 + + + + HeadlessVncServer + + Headless VNC server + 無標題 VNC 伺服器 + + + + LdapBrowseDialog + + Browse LDAP + 瀏覽 LDAP + + + + LdapClient + + LDAP error description: %1 + LDAP 錯誤描述: %1 + + + + LdapConfigurationPage + + Basic settings + 基本設定 + + + General + 一般 + + + LDAP server and port + LDAP 伺服器和埠 + + + Bind DN + 繫結 DN + + + Bind password + 繫結密碼 + + + Anonymous bind + 匿名繫結 + + + Use bind credentials + 使用繫結認證 + + + Base DN + 基本 DN + + + Fixed base DN + 固定的基礎 DN + + + e.g. dc=example,dc=org + 例如: dc=example,dc=org + + + Discover base DN by naming context + 以命名內容探索基礎 DN + + + e.g. namingContexts or defaultNamingContext + 例如: namingCoNtexts 或 defaultNamingCoNtext + + + Environment settings + 環境設定 + + + Object trees + 物件樹 + + + Computer tree + 電腦樹 + + + e.g. OU=Groups + 例如: OU=群組 + + + User tree + 使用者樹 + + + e.g. OU=Users + 例如: OU=使用者 + + + e.g. OU=Computers + 例如: OU=電腦 + + + Group tree + 群組樹 + + + Perform recursive search operations in object trees + 在物件樹中執行遞迴搜尋操作 + + + Object attributes + 物件屬性 + + + e.g. hwAddress + 例如: hwAddress + + + e.g. member or memberUid + 例如: 成員或 memberUid + + + e.g. dNSHostName + 例如: dNSHostName + + + Computer MAC address attribute + 電腦 MAC 位址屬性 + + + Group member attribute + 群組成員屬性 + + + e.g. uid or sAMAccountName + 例如: uid 或 sAMAccountName + + + Advanced settings + 進階設定 + + + Optional object filters + 可選的物件篩選器 + + + Filter for user groups + 使用者群組的篩選器 + + + Filter for users + 使用者的篩選器 + + + Filter for computer groups + 電腦群組的篩選器 + + + Group member identification + 群組成員身份 + + + Distinguished name (Samba/AD) + 可分辨名稱 (Samba/AD) + + + List all groups of a user + 列出所有的使用者群組 + + + List all groups of a computer + 列出所有電腦的群組 + + + Get computer object by IP address + 以 IP 位址取得電腦物件 + + + LDAP connection failed + LDAP 連線失敗 + + + LDAP bind failed + LDAP 繫結失敗 + + + LDAP bind successful + LDAP 繫結成功 + + + Successfully connected to the LDAP server and performed an LDAP bind. The basic LDAP settings are configured correctly. + 成功地連接到 LDAP 伺服器並執行 LDAP 繫結。 基本的 LDAP 設定正確組態。 + + + LDAP base DN test failed + LDAP 基礎 DN 測試失敗 + + + LDAP base DN test successful + LDAP 基礎 DN 測試成功 + + + LDAP naming context test failed + LDAP 命名內容測試失敗 + + + LDAP naming context test successful + LDAP 命名內容測試成功 + + + The LDAP naming context has been queried successfully. The following base DN was found: +%1 + 查詢 LDAP 命名內容成功。 找到以下基礎 DN: +%1 + + + user tree + 使用者樹 + + + group tree + 群組樹 + + + computer tree + 電腦樹 + + + Enter username + 輸入使用者名稱 + + + Please enter a user login name (wildcards allowed) which to query: + 請輸入查詢的使用者登入名稱 (允許萬用字元): + + + user objects + 使用者物件 + + + Enter group name + 輸入群組名稱 + + + Please enter a group name whose members to query: + 請輸入查詢其成員的群組名稱: + + + group members + 群組成員 + + + Group not found + 找不到群組 + + + Could not find a group with the name "%1". Please check the group name or the group tree parameter. + 找不到名稱為「%1」的群組。 請檢查群組名稱或群組樹參數。 + + + Enter computer name + 輸入電腦名稱 + + + computer objects + 電腦物件 + + + Enter computer DN + 輸入電腦 DN + + + Please enter the DN of a computer whose MAC address to query: + 請輸入查詢其 MAC 位址的電腦 DN: + + + computer MAC addresses + 電腦 MAC 位址 + + + users + 使用者 + + + user groups + 使用者群組 + + + computer groups + 電腦群組 + + + Please enter a user login name whose group memberships to query: + 請輸入查詢其群組成員的使用者登入名稱: + + + groups of user + 使用者的群組 + + + User not found + 找不到使用者 + + + groups of computer + 電腦的群組 + + + Computer not found + 找不到電腦 + + + Enter computer IP address + 輸入電腦 IP 位址 + + + Please enter a computer IP address which to resolve to an computer object: + 請輸入電腦的 IP 位址,解析為一個電腦物件: + + + computers + 電腦 + + + LDAP %1 test failed + LDAP %1 測試失敗 + + + LDAP %1 test successful + LDAP %1 測試成功 + + + The %1 has been queried successfully and %2 entries were found. + 查詢 %1 成功和找到 %2 個項目。 + + + %1 %2 have been queried successfully: + +%3 + %1 %2 查詢成功: + +%3 + + + LDAP filter test failed + LDAP 篩選器測試失敗 + + + Could not query any %1 using the configured filter. Please check the LDAP filter for %1. + +%2 + 使用組態的篩選器無法查詢任何 %1。 請檢查 %1 的 LDAP 篩選器。 + +%2 + + + LDAP filter test successful + LDAP 篩選器測試成功 + + + %1 %2 have been queried successfully using the configured filter. + %1 %2 使用組態的篩選器查詢成功。 + + + (only if different from group tree) + (僅從不同群組樹時) + + + Computer group tree + 電腦群組樹 + + + computer group tree + 電腦群組樹 + + + Filter for computers + 電腦的篩選器 + + + e.g. room or computerLab + 例如: room 或 computerLab + + + Integration tests + 整合測試 + + + Computer groups + 電腦群組 + + + e.g. name or description + 例如: 名稱或描述 + + + Filter for computer containers + 電腦容器篩選 + + + Computer containers or OUs + 電腦容器或組織單位 (OU) + + + Connection security + 連線安全性 + + + TLS certificate verification + TLS 憑證驗證 + + + System defaults + 系統預設值 + + + Never (insecure!) + 永不 (不安全) + + + Custom CA certificate file + 自訂 CA 憑證檔 + + + None + 無 + + + TLS + TLS + + + SSL + SSL + + + e.g. (objectClass=computer) + 例如: (objectClass=computer) + + + e.g. (objectClass=group) + 例如: (objectClass=group) + + + e.g. (objectClass=person) + 例如: (objectClass=person) + + + e.g. (objectClass=room) or (objectClass=computerLab) + 例如: (objectClass=room) 或 (objectClass=computerLab) + + + e.g. (objectClass=container) or (objectClass=organizationalUnit) + 例如: (objectClass=container) 或 (objectClass=organizationalUnit) + + + Could not query the configured base DN. Please check the base DN parameter. + +%1 + 無法查詢組態的基礎 DN。 請檢查基礎 DN 參數。 + +%1 + + + The LDAP base DN has been queried successfully. The following entries were found: + +%1 + LDAP 基礎 DN 已成功查詢。 找到以下項目: + +%1 + + + Could not query the base DN via naming contexts. Please check the naming context attribute parameter. + +%1 + 無法透過命名內容查詢基礎 DN。 請檢查命名內容屬性參數。 + +%1 + + + Certificate files (*.pem) + 憑證檔 (*.pem) + + + Could not connect to the LDAP server. Please check the server parameters. + +%1 + 無法連接到 LDAP 伺服器。 請檢查伺服器參數。 + +%1 + + + Could not bind to the LDAP server. Please check the server parameters and bind credentials. + +%1 + 無法繫結到 LDAP 伺服器。 請檢查伺服器參數並繫結認證。 + +%1 + + + Encryption protocol + 加密協定 + + + Computer location attribute + 電腦位置屬性 + + + Computer display name attribute + 電腦顯示名稱屬性 + + + Location name attribute + 位置名稱屬性 + + + e.g. cn or displayName + 例如: cn 或 displayName + + + Computer locations identification + 電腦位置識別 + + + Identify computer locations (e.g. rooms) via: + 透過以下方式識別電腦位置 (例如教室): + + + Location attribute in computer objects + 電腦物件中的位置屬性 + + + List all entries of a location + 列出位置的所有項目 + + + List all locations + 列出所有位置 + + + Enter computer display name + 輸入電腦顯示名稱 + + + Please enter a computer display name to query: + 請輸入查詢的電腦顯示名稱: + + + Enter computer location name + 輸入電腦位置名稱 + + + Please enter the name of a computer location (wildcards allowed): + 請輸入電腦位置的名稱 (允許萬用字元): + + + computer locations + 電腦位置 + + + Enter location name + 輸入位置名稱 + + + Please enter the name of a location whose entries to query: + 請輸入查詢其項目的位置名稱: + + + location entries + 位置項目 + + + LDAP test failed + LDAP 測試失敗 + + + Could not query any %1. Please check the parameter(s) %2 and enter the name of an existing object. + +%3 + 無法查詢任何 %1。 請檢查參數 %2 並輸入現有物件的名稱。 + +%3 + + + and + 和 + + + LDAP test successful + LDAP 測試成功 + + + Could not query any entries in configured %1. Please check the parameter "%2". + +%3 + 無法查詢組態的 %1 中的任何項目。 請檢查參數 "%2"。 + +%3 + + + Browse + 瀏覽 + + + Test + 測試 + + + Hostnames stored as fully qualified domain names (FQDN, e.g. myhost.example.org) + 主機名稱存儲為完全限定網域名稱 (FQDN, 例如: myhost.example.org) + + + Computer hostname attribute + 電腦主機名稱屬性 + + + Please enter a computer hostname to query: + 請輸入查詢的電腦主機名稱: + + + Invalid hostname + 主機名稱無效 + + + You configured computer hostnames to be stored as fully qualified domain names (FQDN) but entered a hostname without domain. + 您將電腦主機名稱組態為以完全限定網域名稱 (FQDN) 存儲,但輸入了不含網域的主機名稱。 + + + You configured computer hostnames to be stored as simple hostnames without a domain name but entered a hostname with a domain name part. + 您將電腦主機名稱組態為以不含域名的簡單主機名稱存儲,但輸入了含有域名部分的主機名稱。 + + + Could not find a user with the name "%1". Please check the username or the user tree parameter. + 找不到名為「%1」的使用者。 請檢查使用者名稱或使用者樹參數。 + + + Enter hostname + 輸入主機名稱 + + + Please enter a computer hostname whose group memberships to query: + 請輸入查詢其群組成員身份的電腦主機名稱: + + + Could not find a computer with the hostname "%1". Please check the hostname or the computer tree parameter. + 找不到主機名稱為「%1」的電腦。 請檢查主機名稱或電腦樹參數。 + + + Hostname lookup failed + 主機名稱尋找失敗 + + + Could not lookup hostname for IP address %1. Please check your DNS server settings. + 無法查找 IP 位址 %1 的主機名稱。 請檢查您的 DNS 伺服器設定。 + + + User login name attribute + 使用者登入名稱屬性 + + + Configured attribute for user login name or computer hostname (OpenLDAP) + 為使用者登入名稱或電腦主機名稱組態的屬性 (OpenLDAP) + + + computer containers + 電腦容器 + + + + LdapPlugin + + Auto-configure the base DN via naming context + 透過命名內文自動組態基礎 DN + + + Query objects from LDAP directory + 從 LDAP 目錄查詢物件 + + + Show help about command + 顯示命令的說明 + + + Commands for configuring and testing LDAP/AD integration + 組態和測試 LDAP/AD 整合的命命 + + + Basic LDAP/AD support for Veyon + Veyon 的基本 LDAP/AD 支援 + + + %1 (load computers and locations from LDAP/AD) + %1 (從 LDAP/AD 載入電腦和位置) + + + %1 (load users and groups from LDAP/AD) + %1 (從 LDAP/AD 載入使用者和群組) + + + Please specify a valid LDAP url following the schema "ldap[s]://[user[:password]@]hostname[:port]" + 請指定一個有效的 LDAP url,其架構如下 "ldap[s]://[user[:password]@]hostname[:port]" + + + No naming context attribute name given - falling back to configured value. + 沒有給予命名內容屬性名稱 - 返回到組態的值。 + + + Could not query base DN. Please check your LDAP configuration. + 無法查詢基本 DN。 請檢查您的 LDAP 組態。 + + + Configuring %1 as base DN and disabling naming context queries. + 將 %1 組態為基本 DN 並停用命名內容查詢。 + + + + LinuxPlatformConfigurationPage + + Linux + Linux + + + Custom PAM service for user authentication + 自訂使用者身份驗證的 PAM 服務 + + + User authentication + 使用者身份驗證 + + + Session management + 工作階段管理 + + + Display manager users + 顯示管理員使用者 + + + + LinuxPlatformPlugin + + Plugin implementing abstract functions for the Linux platform + 外掛程式實現 Linux 平臺的抽象函數 + + + + LocationDialog + + Select location + 選取位置 + + + enter search filter... + 輸入搜尋篩選器... + + + + MainToolBar + + Configuration + 組態 + + + Disable balloon tooltips + 停用氣球工具提示 + + + Show icons only + 只顯示圖示 + + + + MainWindow + + MainWindow + 主視窗 + + + toolBar + 工具列 + + + General + 一般 + + + &File + 檔案(&F) + + + &Help + 說明(&H) + + + &Quit + 離開(&Q) + + + Ctrl+Q + Ctrl+Q + + + Ctrl+S + Ctrl+S + + + L&oad settings from file + 從檔案載入設定(&O) + + + Ctrl+O + Ctrl+O + + + About Qt + 關於 Qt + + + Authentication impossible + 無法驗證 + + + Configuration not writable + 組態無法寫入 + + + Load settings from file + 從檔案載入設定 + + + Save settings to file + 將設定存到檔案 + + + Unsaved settings + 未儲存的設定 + + + There are unsaved settings. Quit anyway? + 有尚未儲存的設定。 仍要離開嗎? + + + Veyon Configurator + Veyon 組態器 + + + Service + 服務 + + + Master + 主要 + + + Access control + 存取控制 + + + About Veyon + 關於 Veyon + + + Auto + 自動 + + + About + 關於 + + + %1 Configurator %2 + %1 組態器 %2 + + + JSON files (*.json) + JSON 檔 (*.json) + + + The local configuration backend reported that the configuration is not writable! Please run the %1 Configurator with higher privileges. + 本機組態後端報告組態不可寫入! 請以較高特權執行 %1 組態器。 + + + No authentication key files were found or your current ones are outdated. Please create new key files using the %1 Configurator. Alternatively set up logon authentication using the %1 Configurator. Otherwise you won't be able to access computers using %1. + 找不到驗證金鑰檔或目前的已過時。 請使用 %1 組態器建立新的金鑰檔。 或者使用 %1 組態器設定登入驗證。 否則您將無法使用 %1 存取電腦。 + + + Access denied + 拒絕存取 + + + According to the local configuration you're not allowed to access computers in the network. Please log in with a different account or let your system administrator check the local configuration. + 根據本機組態您不允許存取網路中的電腦。 請使用不同的帳戶登入,或讓您的系統管理員檢查本機組態。 + + + Screenshots + 螢幕截圖 + + + Feature active + 特色活動 + + + The feature "%1" is still active. Please stop it before closing %2. + 功能 "%1" 仍使用中。 請先停止它再關閉 %2。 + + + Reset configuration + 重設組態 + + + Do you really want to reset the local configuration and revert all settings to their defaults? + 您確定要重設本機組態並恢復所有設定為其預設值嗎? + + + Search users and computers + 搜尋使用者和電腦 + + + Align computers to grid + 將電腦對齊格線 + + + %1 Configurator + %1 組態器 + + + Insufficient privileges + 權限不足 + + + Could not start with administrative privileges. Please make sure a sudo-like program is installed for your desktop environment! The program will be run with normal user privileges. + 無法以管理特權啟動。 請確認為您的桌面環境安裝類似于 sudo 的程式! 程式將以正常使用者特權執行。 + + + Only show powered on computers + 只顯示開機的電腦 + + + &Save settings to file + 將設定儲存到檔案(&S) + + + &View + 檢視(&V) + + + &Standard + 標準(&S) + + + &Advanced + 進階(&A) + + + Use custom computer arrangement + 使用自訂電腦排列 + + + Locations && computers + 地點 && 電腦 + + + Slideshow + 投影片 + + + Spotlight + 聚光燈 + + + Adjust size of computer icons automatically + 自動調整電腦圖示的大小 + + + + MasterConfigurationPage + + Directories + 目錄 + + + User configuration + 使用者組態 + + + Feature on computer double click: + 電腦上按兩下時功能: + + + Features + 功能 + + + All features + 所有功能 + + + Disabled features + 停用的功能 + + + Screenshots + 螢幕截圖 + + + <no feature> + <沒有功能> + + + Basic settings + 基本設定 + + + Behaviour + 行為 + + + Enforce selected mode for client computers + 對用戶端電腦強制選取的模式 + + + Hide local computer + 隱藏本機電腦 + + + Hide computer filter field + 隱藏電腦篩選欄位 + + + Actions such as rebooting or powering down computers + 動作比如重新啟動或關閉電腦 + + + User interface + 使用者介面 + + + Background color + 背景色 + + + Thumbnail update interval + 縮圖更新間隔 + + + ms + 毫秒 + + + Program start + 程式啟動 + + + Modes and features + 模式和功能 + + + User and computer name + 使用者和電腦名稱 + + + Only user name + 僅使用者名稱 + + + Only computer name + 僅電腦名稱 + + + Computer thumbnail caption + 電腦縮圖標題 + + + Text color + 文字色彩 + + + Sort order + 排序 + + + Computer and user name + 電腦和使用者名稱 + + + Computer locations + 電腦位置 + + + Show current location only + 只顯示目前位置 + + + Allow adding hidden locations manually + 允許手動加入隱藏位置 + + + Hide empty locations + 隱藏空位置 + + + Show confirmation dialog for potentially unsafe actions + 顯示潛在不安全動作的確認對話方塊 + + + Perform access control + 執行存取控制 + + + Automatically select current location + 自動選取目前位置 + + + Automatically open computer select panel + 自動開啟電腦選取面板 + + + Hide local session + 隱藏本機工作階段 + + + px + px + + + Thumbnail spacing + 縮圖間距 + + + Auto + 自動 + + + Thumbnail aspect ratio + 縮圖外觀比例 + + + Automatically adjust computer icon size + 自動調整電腦圖示大小 + + + Open feature windows on the same screen as the main window + 在與主視窗同一螢幕上開啟功能視窗 + + + + MonitoringMode + + Monitoring + 監視 + + + Builtin monitoring mode + 內建監視模式 + + + This mode allows you to monitor all computers at one or more locations. + 這個模式允許您監視在一個或數個位置的所有電腦。 + + + + NetworkObjectTreeModel + + Locations/Computers + 位置/電腦 + + + + OpenWebsiteDialog + + Open website + 開啟網站 + + + e.g. Veyon + 例如: Veyon + + + Remember and add to website menu + 記住並加入到網站功能表 + + + e.g. www.veyon.io + 例如: www.veyon.io + + + Please enter the URL of the website to open: + 請輸入開啟網站的 URL: + + + Name: + 名稱: + + + + PasswordDialog + + Username + 使用者名稱 + + + Password + 密碼 + + + Veyon Logon + Veyon 登入 + + + Authentication error + 身份驗證錯誤 + + + Logon failed with given username and password. Please try again! + 以給予的使用者名稱和密碼登入失敗。 請再試一次! + + + Please enter your username and password in order to access computers. + 請輸入您的使用者名稱和密碼才能存取電腦。 + + + + PowerControlFeaturePlugin + + Power on + 開啟電源 + + + Click this button to power on all computers. This way you do not have to power on each computer by hand. + 按一下這個按鈕以開啟所有電腦的電源。 這種方式,您不需要手動開啟每部電腦電源。 + + + Reboot + 重新開機 + + + Click this button to reboot all computers. + 按一下這個按鈕以重新開機所有電腦。 + + + Power down + 關閉電源 + + + Click this button to power down all computers. This way you do not have to power down each computer by hand. + 按一下這個按鈕以關閉所有電腦的電源。 這種方式,您不需要手動關閉每部電腦電源。 + + + Power on/down or reboot a computer + 開啟/關閉電源或重新啟動電腦 + + + Confirm reboot + 確認重新啟動 + + + Confirm power down + 確認關機 + + + Do you really want to reboot the selected computers? + 您真的要重新啟動選取的電腦嗎? + + + Do you really want to power down the selected computer? + 您真的要關閉選取的電腦嗎? + + + Power on a computer via Wake-on-LAN (WOL) + 透過 Wake-on-LAN (WOL) 開啟電腦電源 + + + MAC ADDRESS + MAC 位址 + + + This command broadcasts a Wake-on-LAN (WOL) packet to the network in order to power on the computer with the given MAC address. + 這個命令將 Wake-on-LAN (WOL) 封包廣播到網路,以便使用給予的 MAC 位址開啟電腦電源。 + + + Please specify the command to display help for! + 請指定顯示說明的命令! + + + Invalid MAC address specified! + 指定的 MAC 位址無效! + + + Commands for controlling power status of computers + 用於控制電腦電源狀態的命令 + + + Power down now + 立即關閉電源 + + + Install updates and power down + 安裝更新並關閉電源 + + + Power down after user confirmation + 使用者確認後關閉電源 + + + Power down after timeout + 逾時後關閉電源 + + + The computer was remotely requested to power down. Do you want to power down the computer now? + 遠端請求電腦關閉電源。 您要立即關閉電腦電源嗎? + + + The computer will be powered down in %1 minutes, %2 seconds. + +Please save your work and close all programs. + 電腦將在 %1 分鐘 %2 秒內關閉電源。 + +請儲存您的工作並關閉所有程式。 + + + + PowerDownTimeInputDialog + + Power down + 關閉電源 + + + Please specify a timeout for powering down the selected computers: + 請指定關閉選取電腦電源的逾時: + + + minutes + 分鐘 + + + seconds + 秒 + + + + RemoteAccessFeaturePlugin + + Remote view + 遠端檢視 + + + Open a remote view for a computer without interaction. + 開啟電腦的遠端檢視,而不需互動。 + + + Remote control + 遠端控制 + + + Open a remote control window for a computer. + 開啟電腦的遠端控制視窗。 + + + Remote access + 遠端存取 + + + Remote view or control a computer + 遠端檢視或控制電腦 + + + Please enter the hostname or IP address of the computer to access: + 請輸入主機名稱或電腦的 IP 位址來存取: + + + Show help about command + 顯示命令的說明 + + + + RemoteAccessWidget + + %1 - %2 Remote Access + %1 - %2 遠端存取 + + + %1 - %2 - %3 Remote Access + %1 - %2 - %3 遠端存取 + + + + RemoteAccessWidgetToolBar + + View only + 只有檢視 + + + Remote control + 遠端控制 + + + Send shortcut + 傳送快速鍵 + + + Fullscreen + 全螢幕 + + + Window + 視窗 + + + Ctrl+Alt+Del + Ctrl+Alt+Del + + + Ctrl+Esc + Ctrl+Esc + + + Alt+Tab + Alt+Tab + + + Alt+F4 + Alt+F4 + + + Win+Tab + Win+Tab + + + Win + Win + + + Menu + 功能表 + + + Alt+Ctrl+F1 + Alt+Ctrl+F1 + + + Connecting %1 + 正在連線 %1 + + + Connected. + 已連線。 + + + Screenshot + 螢幕快照 + + + Exit + 結束 + + + + RunProgramDialog + + Please enter the programs or commands to run on the selected computer(s). You can separate multiple programs/commands by line. + 請輸入程式或在選取電腦上執行的命令。 您可以用行分隔多個程式/命令。 + + + Run programs + 執行程式 + + + e.g. "C:\Program Files\VideoLAN\VLC\vlc.exe" + 例如: "C:\Program Files\VideoLAN\VLC\vlc.exe" + + + Name: + 名稱: + + + Remember and add to program menu + 記住並加入到程式功能表 + + + e.g. VLC + 例如: VLC + + + + ScreenLockFeaturePlugin + + Lock + 鎖定 + + + Unlock + 解鎖 + + + Lock screen and input devices of a computer + 鎖定電腦的螢幕和輸入裝置 + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked and the screens are blacked. + 若要收回所有使用者的完全注意,您可以使用此按鈕鎖定其電腦。 在這種模式下,所有的輸入設備鎖定和畫面全黑。 + + + Lock input devices + 鎖定輸入裝置 + + + Unlock input devices + 解鎖輸入裝置 + + + To reclaim all user's full attention you can lock their computers using this button. In this mode all input devices are locked while the desktop is still visible. + 若要收回所有使用者的完全注意,您可以使用此按鈕鎖定其電腦。 在這種模式下,桌面仍可見時輸入設備鎖定。 + + + + Screenshot + + unknown + 未知 + + + Could not take a screenshot as directory %1 doesn't exist and couldn't be created. + 無法取得螢幕快照,因為目錄 %1 不存在且無法建立。 + + + Screenshot + 螢幕快照 + + + Could not open screenshot file %1 for writing. + 無法開啟寫入的螢幕快照檔案 %1。 + + + + ScreenshotFeaturePlugin + + Screenshot + 螢幕快照 + + + Use this function to take a screenshot of selected computers. + 使用此功能來取得選取電腦的螢幕快照。 + + + Screenshots taken + 螢幕快照已取得 + + + Screenshot of %1 computer have been taken successfully. + 取得 %1 電腦的螢幕快照成功。 + + + Take screenshots of computers and save them locally. + 取得電腦螢幕快照並儲存在本機。 + + + + ScreenshotManagementPanel + + All screenshots taken by you are listed here. You can take screenshots by clicking the "Screenshot" item in the context menu of a computer. The screenshots can be managed using the buttons below. + 這裡列出所有您取得的螢幕快照。 您可以在電腦的內容功能表中按一下「螢幕快照」項目來取得螢幕快照。 能使用下面的按鈕來管理螢幕快照。 + + + User: + 使用者: + + + Computer: + 電腦: + + + Date: + 日期: + + + Time: + 時間: + + + Show + 顯示 + + + Delete + 刪除 + + + Screenshot + 螢幕快照 + + + Do you really want to delete all selected screenshots? + 您真的要刪除所有的螢幕快照嗎? + + + + ServiceConfigurationPage + + General + 一般 + + + Autostart + 自動啟動 + + + Hide tray icon + 隱藏通知區域圖示 + + + Start service + 啟動服務 + + + Stopped + 已停止 + + + Stop service + 停止服務 + + + State: + 狀態: + + + Enable firewall exception + 啟用防火牆例外 + + + Allow connections from localhost only + 只允許來自 localhost 的連線 + + + VNC server + VNC 伺服器 + + + Plugin: + 外掛程式: + + + Restart %1 Service + 重新啟動 %1 服務 + + + All settings were saved successfully. In order to take effect the %1 service needs to be restarted. Restart it now? + 已成功儲存所有的設定。 為了生效 %1 服務需要重新啟動。 立即重新啟動它? + + + Running + 執行中 + + + Enabling this option will make the service launch a server process for every interactive session on a computer. +Typically this is required to support terminal servers. + 啟用這個選項將使服務為電腦上的每個互動式工作階段啟動伺服器處理程序。 +通常,這是支援終端機伺服器所必需的。 + + + Show notification on remote connection + 遠端連線時顯示通知 + + + Show notification when an unauthorized access is blocked + 未授權的存取已封鎖時顯示通知 + + + Sessions + 工作階段 + + + Single session mode (create server instance for local/physical session only) + 單一工作階段模式 (只為本地端/實體工作階段建立伺服器實例) + + + Multi session mode (create server instance for each local and remote desktop session) + 多工作階段模式 (為每個本地端與遠端桌面工作階段建立伺服器實例) + + + Maximum session count + 最大工作階段數 + + + Network port numbers + 網路埠號 + + + Veyon server + Veyon 伺服器 + + + Internal VNC server + 內部 VNC 伺服器 + + + Feature manager + 功能管理員 + + + Demo server + 示範伺服器 + + + Miscellaneous network settings + 其它網路設定 + + + + ServiceControl + + Starting service %1 + 正在啟動服務 %1 + + + Stopping service %1 + 正在停止服務 %1 + + + Registering service %1 + 註冊服務 %1 + + + Unregistering service %1 + 取消註冊服務 %1 + + + Service control + 服務控制 + + + + ServiceControlPlugin + + Service is running + 服務正在執行 + + + Service is not running + 服務未執行 + + + Configure and control Veyon service + 組態和控制 Veyon 服務 + + + Register Veyon Service + 註冊 Veyon 服務 + + + Unregister Veyon Service + 取消註冊 Veyon 服務 + + + Start Veyon Service + 啟動 Veyon 服務 + + + Stop Veyon Service + 停止 Veyon 服務 + + + Restart Veyon Service + 重新啟動 Veyon 服務 + + + Query status of Veyon Service + 查詢 Veyon 服務的狀態 + + + Commands for configuring and controlling Veyon Service + 組態和控制 Veyon 服務的命令 + + + + ShellCommandLinePlugin + + Run command file + 執行命令列 + + + File "%1" does not exist! + 檔案 "%1" 不存在! + + + Interactive shell and script execution for Veyon Control + Veyon Control 的交互式殼層和指令碼執行 + + + Commands for shell functionalities + 殼層功能的命令 + + + + SlideshowPanel + + Previous + 上一頁 + + + Start/pause + 開始/暫停 + + + Next + 下一頁 + + + Duration: + 持續時間: + + + + SpotlightPanel + + Add selected computers + 加入選取的電腦 + + + Remove selected computers + 移除選取的電腦 + + + Update computers in realtime + 即時更新電腦 + + + Spotlight + 聚光燈 + + + Please select at least one computer to add. + 請選擇至少一台電腦來加入。 + + + Please select at least one computer to remove. + 請選擇至少一台電腦來移除。 + + + Add computers by clicking with the middle mouse button or clicking the first button below. + 使用滑鼠中鍵按一下或按下以下的第一個按鈕來加入電腦。 + + + + SystemTrayIcon + + System tray icon + 系統通知區域圖示 + + + + SystemUserGroupsPlugin + + User groups backend for system user groups + 系統使用者群組的使用者群組後端 + + + Default (system user groups) + 預設值 (系統使用者群組) + + + + TestingCommandLinePlugin + + Test internal Veyon components and functions + 測試內部 Veyon 元件和功能 + + + Commands for testing internal components and functions of Veyon + 用於測試 Veyon 內部元件和功能的命令 + + + + TextMessageDialog + + Send text message + 傳送文字訊息 + + + Use the field below to type your message which will be sent to all selected users. + 使用下列欄位輸入您要傳送給所有選取使用者的訊息。 + + + + TextMessageFeaturePlugin + + Text message + 文字訊息 + + + Use this function to send a text message to all users e.g. to assign them new tasks. + 使用此函數來向所有使用者傳送文字訊息,例如要向其分配新工作。 + + + Message from teacher + 來自老師的訊息 + + + Send a message to a user + 向使用者傳送訊息 + + + + UltraVncConfigurationWidget + + Enable capturing of layered (semi-transparent) windows + 啟用層次 (半透明) 視窗擷取 + + + Poll full screen (leave this enabled per default) + 輪詢全螢幕 (每個預設值保持這個已啟用) + + + Low accuracy (turbo mode) + 低精確度 (快速模式) + + + Builtin UltraVNC server configuration + 內建 UltraVNC 伺服器組態 + + + Enable multi monitor support + 啟用多部顯示器支援 + + + Enable Desktop Duplication Engine on Windows 8 and newer + 在 Windows 8 和以上啟用「桌面複製引擎」 + + + Maximum CPU usage + 最大 CPU 使用率 + + + + UserConfig + + No write access + 沒有寫入存取 + + + Could not save your personal settings! Please check the user configuration file path using the %1 Configurator. + 無法儲存您的個人設定! 請檢查使用 %1 組態的使用者設定檔路徑。 + + + + UserLoginDialog + + User login + 使用者登入 + + + Please enter a username and password for automatic login on all computers. + 請在所有電腦上輸入自動登入的使用者名稱和密碼。 + + + Username + 使用者名稱 + + + Password + 密碼 + + + + UserSessionControlPlugin + + Log in + 登入 + + + Click this button to log in a specific user on all computers. + 按一下這個按鈕以登入所有電腦中的特定使用者。 + + + Log off + 登出 + + + Click this button to log off users from all computers. + 按一下這個按鈕以登出所有電腦中的使用者。 + + + Confirm user logoff + 確認使用者登出 + + + Do you really want to log off the selected users? + 您真的要登出選取的使用者? + + + User session control + 使用者工作階段控制 + + + + VeyonCore + + [OK] + [正常] + + + [FAIL] + [失敗] + + + Invalid command! + 命令無效! + + + Available commands: + 可用命令: + + + Invalid arguments given + 給予的引數無效 + + + Not enough arguments given - use "%1 help" for more information + 給予的引數不夠 - 使用「%1 說明」取得更多資訊 + + + Unknown result! + 未知結果! + + + Available modules: + 可用模組: + + + No module specified or module not found - available modules are: + 未指定模組或找不到模組 - 可用模組為: + + + Plugin not licensed + 外掛程式未授權 + + + INFO + 資訊 + + + ERROR + 錯誤 + + + USAGE + 用法 + + + DESCRIPTION + 描述 + + + EXAMPLES + 範例 + + + WARNING + 警告 + + + + VeyonServiceControl + + Veyon Service + Veyon 服務 + + + + VncViewWidget + + Establishing connection to %1 ... + 正在建立與 %1 連線... + + + + WebApiConfigurationPage + + Web API + 網路 API + + + General + 一般 + + + Network port + 網路埠 + + + Enable WebAPI server + 啟用 WebAPI 伺服器 + + + Connection settings + 連線設定 + + + Lifetime + 終生 + + + h + h + + + s + 秒 + + + Idle timeout + 閒置逾時 + + + Authentication timeout + 身份驗證逾時 + + + Maximum number of open connections + 開啟連線的最大數量 + + + Connection encryption + 連線加密 + + + TLS certificate file + TLS 憑證檔 + + + TLS private key file + TLS 私密金鑰檔 + + + ... + ... + + + Use HTTPS with TLS 1.3 instead of HTTP + 將 HTTPS 與 TLS 1.3 而不是 HTTP 一起使用 + + + + WebApiPlugin + + Run WebAPI server + 執行 WebAPI 伺服器 + + + Failed to start WebAPI server at port %1 + 在連接埠 %1 啟動 WebAPI 伺服器失敗 + + + WebAPI server running at port %1 + 正在執行 WebAPI 伺服器於連接埠 %1 + + + Provide access to a computer via HTTP + 透過 HTTP 提供對電腦的存取 + + + Commands for running the WebAPI server + 執行 WebAPI 伺服器的命令 + + + + WindowsPlatformConfiguration + + Could not change the setting for SAS generation by software. Sending Ctrl+Alt+Del via remote control will not work! + 軟體無法變更 SAS 產生的設定。 透過遠端控制傳送 Ctrl+Alt+Del 將無法動作! + + + + WindowsPlatformConfigurationPage + + Windows + Windows + + + General + 一般 + + + Enable SAS generation by software (Ctrl+Alt+Del) + 以軟體啟用 SAS 產生 (Ctrl+Alt+Del) + + + Screen lock + 螢幕鎖定 + + + Hide taskbar + 隱藏工作列 + + + Hide start menu + 隱藏 [開始] 功能表 + + + Hide desktop + 隱藏桌面 + + + User authentication + 使用者身份驗證 + + + Use alternative user authentication mechanism + 使用替代使用者身份驗證機制 + + + User login + 使用者登入 + + + Input start delay + 輸入啟動延遲 + + + Simulated key presses interval + 模擬按鍵間隔 + + + Confirm legal notice (message displayed before user logs in) + 確認法律通知 (在使用者登入前顯示訊息) + + + Use input device interception driver + 使用輸入裝置攔截驅動程式 + + + + WindowsPlatformPlugin + + Plugin implementing abstract functions for the Windows platform + 執行 Windows 平臺的抽象函數的外掛程式 + + + + WindowsServiceControl + + The service "%1" is already installed. + 服務「%1」已經安裝。 + + + The service "%1" could not be installed. + 無法安裝服務「%1」。 + + + The service "%1" has been installed successfully. + 服務「%1」安裝成功。 + + + The service "%1" could not be uninstalled. + 無法解除安裝服務「%1」。 + + + The service "%1" has been uninstalled successfully. + 服務「%1」解除安裝成功。 + + + The start type of service "%1" could not be changed. + 無法變更服務「%1」的啟動類型。 + + + Service "%1" could not be found. + 找不到服務「%1」。 + + + + X11VncConfigurationWidget + + Builtin x11vnc server configuration + 內建 x11vnc 伺服器組態 + + + Custom x11vnc parameters: + 自訂 x11vnc 參數: + + + Do not use X Damage extension + 不使用 X Damage 擴充功能 + + + \ No newline at end of file diff --git a/worker/CMakeLists.txt b/worker/CMakeLists.txt new file mode 100644 index 0000000..d97946f --- /dev/null +++ b/worker/CMakeLists.txt @@ -0,0 +1,12 @@ +INCLUDE(BuildVeyonApplication) +INCLUDE(WindowsBuildHelpers) + +FILE(GLOB worker_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h) +FILE(GLOB worker_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src) +build_veyon_application(veyon-worker ${worker_SOURCES} ${worker_INCLUDES}) +TARGET_LINK_LIBRARIES(veyon-worker veyon-core Qt5::Network) + +ADD_WINDOWS_RESOURCE(veyon-worker) +MAKE_GRAPHICAL_APP(veyon-worker) diff --git a/worker/data/veyon-worker.exe.manifest.in b/worker/data/veyon-worker.exe.manifest.in new file mode 100644 index 0000000..aa4fd5a --- /dev/null +++ b/worker/data/veyon-worker.exe.manifest.in @@ -0,0 +1,14 @@ + + + + Veyon Worker + + + + + + + + + + diff --git a/worker/src/FeatureWorkerManagerConnection.cpp b/worker/src/FeatureWorkerManagerConnection.cpp new file mode 100644 index 0000000..f1de66e --- /dev/null +++ b/worker/src/FeatureWorkerManagerConnection.cpp @@ -0,0 +1,103 @@ +/* + * FeatureWorkerManagerConnection.cpp - class which handles communication between service and feature + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "FeatureManager.h" +#include "FeatureWorkerManagerConnection.h" +#include "VeyonConfiguration.h" + + +FeatureWorkerManagerConnection::FeatureWorkerManagerConnection( VeyonWorkerInterface& worker, + FeatureManager& featureManager, + Feature::Uid featureUid, + QObject* parent ) : + QObject( parent ), + m_worker( worker ), + m_featureManager( featureManager ), + m_socket( this ), + m_featureUid( featureUid ) +{ + connect( &m_connectTimer, &QTimer::timeout, this, &FeatureWorkerManagerConnection::tryConnection ); + + connect( &m_socket, &QTcpSocket::connected, + this, &FeatureWorkerManagerConnection::sendInitMessage ); + + connect( &m_socket, &QTcpSocket::disconnected, + QCoreApplication::instance(), &QCoreApplication::quit ); + + connect( &m_socket, &QTcpSocket::readyRead, + this, &FeatureWorkerManagerConnection::receiveMessage ); + + tryConnection(); +} + + + +bool FeatureWorkerManagerConnection::sendMessage( const FeatureMessage& message ) +{ + return message.send( &m_socket ); +} + + + +void FeatureWorkerManagerConnection::tryConnection() +{ + if( m_socket.state() != QTcpSocket::ConnectedState ) + { + const auto port = quint16( VeyonCore::config().featureWorkerManagerPort() + VeyonCore::sessionId() ); + + vDebug() << "connecting to FeatureWorkerManager at port" << port; + + m_socket.connectToHost( QHostAddress::LocalHost, port ); + m_connectTimer.start( ConnectTimeout ); + } +} + + + +void FeatureWorkerManagerConnection::sendInitMessage() +{ + vDebug() << m_featureUid; + + m_connectTimer.stop(); + + FeatureMessage( m_featureUid, FeatureMessage::InitCommand ).send( &m_socket ); +} + + + +void FeatureWorkerManagerConnection::receiveMessage() +{ + FeatureMessage featureMessage; + + while( featureMessage.isReadyForReceive( &m_socket ) ) + { + if( featureMessage.receive( &m_socket ) ) + { + m_featureManager.handleFeatureMessage( m_worker, featureMessage ); + } + } +} diff --git a/worker/src/FeatureWorkerManagerConnection.h b/worker/src/FeatureWorkerManagerConnection.h new file mode 100644 index 0000000..4755fa8 --- /dev/null +++ b/worker/src/FeatureWorkerManagerConnection.h @@ -0,0 +1,61 @@ +/* + * FeatureWorkerManagerConnection.h - class which handles communication between worker manager and worker + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include +#include + +#include "Feature.h" + +class FeatureManager; +class FeatureMessage; +class VeyonWorkerInterface; + +class FeatureWorkerManagerConnection : public QObject +{ + Q_OBJECT +public: + FeatureWorkerManagerConnection( VeyonWorkerInterface& worker, + FeatureManager& featureManager, + Feature::Uid featureUid, + QObject* parent = nullptr ); + + + bool sendMessage( const FeatureMessage& message ); + +private: + static constexpr auto ConnectTimeout = 3000; + + void tryConnection(); + void sendInitMessage(); + void receiveMessage(); + + VeyonWorkerInterface& m_worker; + FeatureManager& m_featureManager; + QTcpSocket m_socket; + Feature::Uid m_featureUid; + QTimer m_connectTimer{this}; + +} ; diff --git a/worker/src/VeyonWorker.cpp b/worker/src/VeyonWorker.cpp new file mode 100644 index 0000000..318c4da --- /dev/null +++ b/worker/src/VeyonWorker.cpp @@ -0,0 +1,70 @@ +/* + * VeyonWorker.cpp - basic implementation of Veyon Worker + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "FeatureWorkerManagerConnection.h" +#include "VeyonConfiguration.h" +#include "VeyonWorker.h" + + +VeyonWorker::VeyonWorker( const QString& featureUid, QObject* parent ) : + QObject( parent ), + m_core( QCoreApplication::instance(), + VeyonCore::Component::Worker, + QStringLiteral( "FeatureWorker-" ) + VeyonCore::formattedUuid( featureUid ) ), + m_featureManager(), + m_workerManagerConnection( nullptr ) +{ + const Feature* workerFeature = nullptr; + + for( const auto& feature : m_featureManager.features() ) + { + if( feature.uid() == featureUid ) + { + workerFeature = &feature; + } + } + + if( workerFeature == nullptr ) + { + qFatal( "Could not find specified feature" ); + } + + if( m_core.config().disabledFeatures().contains( featureUid ) ) + { + qFatal( "Specified feature is disabled by configuration!" ); + } + + m_workerManagerConnection = new FeatureWorkerManagerConnection( *this, m_featureManager, featureUid, this ); + + vInfo() << "Running worker for feature" << workerFeature->name(); +} + + + +bool VeyonWorker::sendFeatureMessageReply( const FeatureMessage& reply ) +{ + return m_workerManagerConnection->sendMessage( reply ); +} diff --git a/worker/src/VeyonWorker.h b/worker/src/VeyonWorker.h new file mode 100644 index 0000000..f105987 --- /dev/null +++ b/worker/src/VeyonWorker.h @@ -0,0 +1,50 @@ +/* + * VeyonWorker.h - basic implementation of Veyon Worker + * + * Copyright (c) 2018-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#pragma once + +#include "FeatureManager.h" +#include "VeyonWorkerInterface.h" + +class FeatureWorkerManagerConnection; + +class VeyonWorker : public QObject, VeyonWorkerInterface +{ + Q_OBJECT +public: + explicit VeyonWorker( const QString& featureUid, QObject* parent = nullptr ); + + bool sendFeatureMessageReply( const FeatureMessage& reply ) override; + + VeyonCore& core() + { + return m_core; + } + +private: + VeyonCore m_core; + FeatureManager m_featureManager; + FeatureWorkerManagerConnection* m_workerManagerConnection; + +} ; diff --git a/worker/src/main.cpp b/worker/src/main.cpp new file mode 100644 index 0000000..6029588 --- /dev/null +++ b/worker/src/main.cpp @@ -0,0 +1,51 @@ +/* + * main.cpp - main file for Veyon Feature Worker + * + * Copyright (c) 2017-2021 Tobias Junghans + * + * This file is part of Veyon - https://veyon.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "VeyonWorker.h" + + +int main( int argc, char **argv ) +{ + QApplication app( argc, argv ); + app.setWindowIcon( QIcon( QStringLiteral(":/core/icon64.png") ) ); + + const auto arguments = app.arguments(); + + if( arguments.count() < 2 ) + { + qFatal( "Not enough arguments (feature)" ); + } + + const auto featureUid = arguments[1]; + if( QUuid( featureUid ).isNull() ) + { + qFatal( "Invalid feature UID given" ); + } + + VeyonWorker worker( featureUid ); + + return worker.core().exec(); +} diff --git a/worker/veyon-worker.1 b/worker/veyon-worker.1 new file mode 100644 index 0000000..b75d4e6 --- /dev/null +++ b/worker/veyon-worker.1 @@ -0,0 +1,45 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH VEYON-WORKER 1 2018-12-05 Veyon +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +veyon-worker \- Veyon Worker +.SH SYNOPSIS +.B veyon-worker +.br +.SH DESCRIPTION +.PP +.\" TeX users may be more comfortable with the \fB\fP and +.\" \fI\fP escape sequences to invode bold face and italics, +.\" respectively. + +\fBVEYON-WORKER\fR is helper program started by the server to provide +specific functions in an isolated environment or in the context of the +user that is currently logged in. Those specific functions include the +demo server for the teacher computer and the demo client on the student +computers. + +.SH SEE ALSO +veyon-server(1), veyon-master(1), veyon-configurator(1), veyon-auth-helper(8) +.PP +https://veyon.io/ + +.SH AUTHOR +Veyon has been written by Tobias Junghans. +.PP +This manual page has been written by Tobias Junghans and Mike Gabriel. It +was originally written for the Debian project (but may be used by +others). diff --git a/worker/veyon-worker.rc.in b/worker/veyon-worker.rc.in new file mode 100644 index 0000000..53c32db --- /dev/null +++ b/worker/veyon-worker.rc.in @@ -0,0 +1,31 @@ +#include + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST data/veyon-worker.exe.manifest + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@,@VERSION_BUILD@ + FILEFLAGSMASK 0x0L + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, charset = Windows, Multilingual + BEGIN + VALUE "Comments", "Virtual Eye On Networks (https://veyon.io)\0" + VALUE "CompanyName", "Veyon Solutions\0" + VALUE "ProductName", "Veyon\0" + VALUE "ProductVersion", "@VERSION_STRING@\0" + VALUE "FileDescription", "Veyon\0" + VALUE "FileVersion", "@VERSION_STRING@\0" + VALUE "LegalCopyright", "Copyright (c) 2017-2021 Veyon Solutions / Tobias Junghans\0" + VALUE "OriginalFilename", "veyon-worker.exe\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04E4 + END +END -- 2.30.2