Compare commits

..

2 Commits

12 changed files with 454 additions and 3925 deletions

View File

@@ -1,72 +0,0 @@
cmake_minimum_required(VERSION 3.5)
project(singlechat.wt LANGUAGES CXX)
add_compile_options(-Wno-deprecated-declarations)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_COMPILER "/usr/bin/g++-12")
# Set default build type if not specified
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# Set compiler flags based on build type
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_options(-g)
else()
add_compile_options(-O2)
endif()
add_executable(${PROJECT_NAME}
src/main.cpp
src/broadcast.cpp
src/app.cpp
)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
find_package(Threads REQUIRED)
target_link_libraries(${PROJECT_NAME}
wt
wthttp
curl
xml2
${CMAKE_THREAD_LIBS_INIT}
)
find_package(Boost COMPONENTS system filesystem REQUIRED)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} GraphicsMagick++ GraphicsMagick)
endif()
if(UNIX)
find_package(PkgConfig)
pkg_check_modules(XML2 libxml-2.0)
if(XML2_FOUND)
target_include_directories(${PROJECT_NAME} PRIVATE ${XML2_INCLUDE_DIRS})
endif()
endif()
if(UNIX)
find_package(PkgConfig)
pkg_check_modules(XML2 libxml-2.0)
if(XML2_FOUND)
target_include_directories(${PROJECT_NAME} PRIVATE ${XML2_INCLUDE_DIRS} /usr/include/GraphicsMagick)
endif()
endif()
include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION /opt/ypchat/bin
)
# Install docroot directory and handle logs setup
install(DIRECTORY docroot/
DESTINATION /opt/ypchat/docroot
)
install(CODE "file(MAKE_DIRECTORY /opt/ypchat/logs)")
install(CODE "execute_process(COMMAND chmod 777 /opt/ypchat/logs)")

View File

@@ -1,71 +0,0 @@
cmake_minimum_required(VERSION 3.5)
project(singlechat.wt LANGUAGES CXX)
add_compile_options(-Wno-deprecated-declarations)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_COMPILER "/usr/bin/g++-12")
set(CMAKE_PREFIX_PATH "/usr")
# Set default build type if not specified
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# Set compiler flags based on build type
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_options(-g)
else()
add_compile_options(-O2)
endif()
add_executable(${PROJECT_NAME}
src/main.cpp
src/broadcast.h src/broadcast.cpp
src/app.h src/app.cpp
docroot/text.xml
docroot/ads.txt
docroot/links.csv
)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
find_package(Threads REQUIRED)
target_link_libraries(${PROJECT_NAME}
wt
wthttp
curl
xml2
${CMAKE_THREAD_LIBS_INIT}
)
find_package(Boost COMPONENTS system filesystem REQUIRED)
if(UNIX)
# Ubuntu-spezifische Include-Verzeichnisse
find_package(PkgConfig)
pkg_check_modules(XML2 libxml-2.0)
if(XML2_FOUND)
target_include_directories(${PROJECT_NAME} PRIVATE /usr/include/GraphicsMagick ${XML2_INCLUDE_DIRS})
endif()
elseif(EXISTS "/etc/os-release" AND ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux"))
# Tumbleweed-spezifische Include-Verzeichnisse
target_include_directories(${PROJECT_NAME} PRIVATE /usr/include/GraphicsMagick)
endif()
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} GraphicsMagick++ GraphicsMagick)
endif()
include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION /opt/ypchat/bin
)
install(DIRECTORY docroot/
DESTINATION /opt/ypchat/docroot
)
install(CODE "file(MAKE_DIRECTORY /opt/ypchat/logs)")
install(CODE "execute_process(COMMAND chmod 777 /opt/ypchat/logs)")

View File

@@ -1,517 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 17.0.1, 2025-11-16T12:30:10. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{551ef6b3-a39b-43e2-9ee3-ad56e19ff4f4}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="qlonglong">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoDetect">true</value>
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.LineEndingBehavior">0</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">2</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<value type="bool" key="AutoTest.ApplyFilter">false</value>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<valuelist type="QVariantList" key="AutoTest.PathFilters"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">8</value>
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="bool" key="HasPerBcDcs">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Importiertes Kit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Importiertes Kit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{c305b734-2e7c-49c6-bd3a-cd5bcb4ae45e}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="CMake.Build.Type">Debug</value>
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
-DCMAKE_GENERATOR:STRING=Ninja Multi-Config
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}</value>
<value type="QString" key="CMake.Source.Directory">/mnt/share/torsten/Programs/SingleChat</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/torsten/Programs/SingleChat/build</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug (importiert)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString"></value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.CMakePackageStep</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="QString" key="ApplicationManagerPlugin.Deploy.InstallPackageStep.Arguments">install-package --acknowledge</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Application Manager-Paket installieren</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.InstallPackageStep</value>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.Configuration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">2</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">singlechat.wt</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">singlechat.wt</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/torsten/Programs/SingleChat/build/Debug</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="QString" key="CMake.Build.Type">Release</value>
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
-DCMAKE_GENERATOR:STRING=Ninja Multi-Config
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}</value>
<value type="QString" key="CMake.Source.Directory">/mnt/share/torsten/Programs/SingleChat</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/torsten/Programs/SingleChat/build</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release (importiert)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">-1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">install</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.CMakePackageStep</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="QString" key="ApplicationManagerPlugin.Deploy.InstallPackageStep.Arguments">install-package --acknowledge</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Application Manager-Paket installieren</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.InstallPackageStep</value>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.Configuration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">2</value>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">0</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="QString" key="CMake.Build.Type">RelWithDebInfo</value>
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
-DCMAKE_GENERATOR:STRING=Ninja Multi-Config
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}</value>
<value type="QString" key="CMake.Source.Directory">/mnt/share/torsten/Programs/SingleChat</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/torsten/Programs/SingleChat/build</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release mit Debuginformationen (importiert)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">-1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">install</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.CMakePackageStep</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="QString" key="ApplicationManagerPlugin.Deploy.InstallPackageStep.Arguments">install-package --acknowledge</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Application Manager-Paket installieren</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.InstallPackageStep</value>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.Configuration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">2</value>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">0</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">3</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString"></value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.CMakePackageStep</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="QString" key="ApplicationManagerPlugin.Deploy.InstallPackageStep.Arguments">install-package --acknowledge</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Application Manager-Paket installieren</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.InstallPackageStep</value>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.Configuration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">2</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">singlechat.wt</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">singlechat.wt</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/torsten/Programs/SingleChat/build/Debug</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="qlonglong">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@@ -1,387 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 9.0.2, 2024-10-21T08:08:04. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{d36652ff-969b-426b-a63f-1edd325096c5}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="qlonglong">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">8</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{15eefee5-3141-47ab-aab0-72db66b5555b}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="CMake.Build.Type">Release</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja Multi-Config
-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable}
-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable}</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/torsten/Programs/SingleChat/build</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="QString" key="CMake.Build.Type">Release</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja Multi-Config
-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable}
-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable}</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/torsten/Programs/SingleChat/../build-SingleChat-Desktop</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="QString" key="CMake.Build.Type">RelWithDebInfo</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja Multi-Config
-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable}
-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable}</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/torsten/Programs/SingleChat/../build-SingleChat-Desktop</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release mit Debuginformationen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.3">
<value type="QString" key="CMake.Build.Type">RelWithDebInfo</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja Multi-Config
-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable}
-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable}</value>
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/torsten/Programs/SingleChat/../build-SingleChat-Desktop</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Profile</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.4">
<value type="QString" key="CMake.Build.Type">MinSizeRel</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja Multi-Config
-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable}
-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable}</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/torsten/Programs/SingleChat/../build-SingleChat-Desktop</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release (kleinstmöglich)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">5</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">/usr/bin/valgrind</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">singlechat.wt</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.singlechat.wt</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">singlechat.wt</value>
<value type="QString" key="RunConfiguration.Arguments">--docroot=&quot;../../docroot&quot; --http-address=0.0.0.0 --http-port=5050</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/torsten/Programs/SingleChat/build/Release</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="qlonglong">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@@ -67,9 +67,12 @@ const smileys = {
};
function sendMessage() {
if (!message.value.trim() || !chatStore.currentConversation) return;
const trimmed = message.value.trim();
if (!trimmed) return;
const isCommand = trimmed.startsWith('/');
if (!isCommand && !chatStore.currentConversation) return;
chatStore.sendMessage(chatStore.currentConversation, message.value.trim());
chatStore.sendMessage(chatStore.currentConversation, trimmed);
message.value = '';
}

View File

@@ -185,6 +185,10 @@ export const useChatStore = defineStore('chat', () => {
handleWebSocketMessage({ type: 'historyResults', ...data });
});
socketInstance.on('commandResult', (data) => {
handleWebSocketMessage({ type: 'commandResult', ...data });
});
socketInstance.on('unreadChats', (data) => {
handleWebSocketMessage({ type: 'unreadChats', ...data });
});
@@ -287,6 +291,27 @@ export const useChatStore = defineStore('chat', () => {
case 'historyResults':
historyResults.value = data.results;
break;
case 'commandResult': {
const lines = Array.isArray(data.lines) ? data.lines : [];
if (!currentConversation.value) {
errorMessage.value = lines.join(' | ');
setTimeout(() => {
errorMessage.value = null;
}, 5000);
break;
}
const timestamp = new Date().toISOString();
for (const line of lines) {
messages.value.push({
from: 'System',
message: String(line),
timestamp,
self: false,
isImage: false
});
}
break;
}
case 'unreadChats':
unreadChatsCount.value = data.count || 0;
break;
@@ -354,19 +379,23 @@ export const useChatStore = defineStore('chat', () => {
}
const messageId = Date.now().toString();
const trimmed = message.trim();
const isCommand = trimmed.startsWith('/');
socket.value.emit('message', {
toUserName,
message,
message: trimmed,
messageId
});
// Lokal hinzufügen
messages.value.push({
from: userName.value,
message,
timestamp: new Date().toISOString(),
self: true
});
// Lokal hinzufügen (außer bei Commands, die serverseitig beantwortet werden)
if (!isCommand) {
messages.value.push({
from: userName.value,
message: trimmed,
timestamp: new Date().toISOString(),
self: true
});
}
// Timeout zurücksetzen bei Aktivität
resetTimeoutTimer();

View File

@@ -18,6 +18,8 @@ class Client {
this.loginTimeStamp = new Date();
this.blockedUsers = new Set();
this.socket = null; // Socket.IO Socket-Objekt
this.chatAuth = null; // { username, rights: Set<string> }
this.pendingChatLogin = null; // { step: 'username'|'password', username: string }
}
setActivity() {
@@ -50,6 +52,104 @@ class Client {
}
}
const CHAT_USERS_FILE_NAME = 'chat-users.json';
const CHAT_RIGHTS = {
STAT: 'stat',
KICK: 'kick'
};
function getLogsDir(__dirname) {
return join(__dirname, '../logs');
}
function ensureLogsDir(__dirname) {
const logsDir = getLogsDir(__dirname);
if (!existsSync(logsDir)) {
mkdirSync(logsDir, { recursive: true });
}
return logsDir;
}
function getChatUsersPath(__dirname) {
return join(ensureLogsDir(__dirname), CHAT_USERS_FILE_NAME);
}
function sha256(value) {
return crypto.createHash('sha256').update(value).digest('hex');
}
function ensureChatUsersFile(__dirname) {
const usersPath = getChatUsersPath(__dirname);
if (existsSync(usersPath)) {
return;
}
const defaultUsers = [
{
username: 'admin',
passwordHash: `sha256:${sha256('changeme123')}`,
rights: [CHAT_RIGHTS.STAT, CHAT_RIGHTS.KICK]
}
];
writeFileSync(usersPath, JSON.stringify(defaultUsers, null, 2), 'utf-8');
}
function loadChatUsers(__dirname) {
ensureChatUsersFile(__dirname);
const usersPath = getChatUsersPath(__dirname);
const raw = readFileSync(usersPath, 'utf-8').trim();
if (!raw) return [];
let users = [];
try {
users = JSON.parse(raw);
} catch (error) {
throw new Error(`Ungültige ${CHAT_USERS_FILE_NAME}: ${error.message}`);
}
if (!Array.isArray(users)) {
throw new Error(`${CHAT_USERS_FILE_NAME} muss ein Array sein`);
}
return users
.filter((entry) => entry && typeof entry.username === 'string')
.map((entry) => ({
username: entry.username.trim(),
passwordHash: typeof entry.passwordHash === 'string' ? entry.passwordHash.trim() : '',
rights: Array.isArray(entry.rights) ? entry.rights.map((r) => String(r).toLowerCase()) : []
}))
.filter((entry) => entry.username && entry.passwordHash);
}
function parseLoginRecord(line) {
const trimmed = line.trim();
if (!trimmed) return null;
const parts = trimmed.split(',');
if (parts.length < 5) return null;
const timestamp = parts[0].trim();
const userName = parts[1].trim();
const gender = parts[parts.length - 1].trim();
const ageRaw = parts[parts.length - 2].trim();
const country = parts.slice(2, parts.length - 2).join(',').trim();
const age = Number.parseInt(ageRaw, 10);
const date = new Date(timestamp);
if (!timestamp || !userName || !country || Number.isNaN(age) || Number.isNaN(date.getTime())) {
return null;
}
return {
timestamp,
date,
day: timestamp.slice(0, 10),
userName,
country,
age,
gender
};
}
let clients = new Map();
let conversations = new Map(); // Key: "user1:user2" (alphabetisch sortiert)
@@ -223,6 +323,313 @@ export function setupBroadcast(io, __dirname) {
downloadCountries();
setInterval(downloadCountries, 24 * 60 * 60 * 1000); // Täglich aktualisieren
function sendCommandResult(socket, lines) {
const payload = Array.isArray(lines) ? lines : [String(lines)];
socket.emit('commandResult', { lines: payload });
}
function hasRight(client, right) {
return !!client.chatAuth && client.chatAuth.rights instanceof Set && client.chatAuth.rights.has(right);
}
function getLoginsPath() {
return join(ensureLogsDir(__dirname), 'logins.log');
}
function readLoginRecords() {
const logPath = getLoginsPath();
if (!existsSync(logPath)) return [];
const raw = readFileSync(logPath, 'utf-8');
return raw
.split('\n')
.map(parseLoginRecord)
.filter(Boolean);
}
function aggregateTop(items, keySelector, limit = 10) {
const counts = new Map();
for (const item of items) {
const key = keySelector(item);
counts.set(key, (counts.get(key) || 0) + 1);
}
return Array.from(counts.entries())
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
.slice(0, limit);
}
function buildAllStats(records) {
if (records.length === 0) {
return ['Keine Login-Daten vorhanden.'];
}
const today = new Date().toISOString().slice(0, 10);
const todayCount = records.filter((r) => r.day === today).length;
const uniqueNames = new Set(records.map((r) => r.userName)).size;
const ages = records.map((r) => r.age);
const minAge = Math.min(...ages);
const maxAge = Math.max(...ages);
const youngest = records.find((r) => r.age === minAge);
const oldest = records.find((r) => r.age === maxAge);
const topCountries = aggregateTop(records, (r) => r.country, 5)
.map(([name, count]) => `${name}(${count})`)
.join(', ');
return [
`Logins gesamt: ${records.length}`,
`Logins heute (${today}): ${todayCount}`,
`Unterschiedliche Namen: ${uniqueNames}`,
`Jüngster Nutzer: ${youngest.userName} (${youngest.age})`,
`Ältester Nutzer: ${oldest.userName} (${oldest.age})`,
`Top Länder: ${topCountries || 'keine'}`
];
}
function executeStatsCommand(socket, client, parts) {
if (!hasRight(client, CHAT_RIGHTS.STAT)) {
sendCommandResult(socket, 'Keine Berechtigung: Recht "stat" fehlt.');
return;
}
const records = readLoginRecords();
if (records.length === 0) {
sendCommandResult(socket, 'Keine Login-Daten vorhanden.');
return;
}
const sub = (parts[1] || '').toLowerCase();
if (!sub || sub === 'help') {
sendCommandResult(socket, [
'Stat-Befehle:',
'/stat today',
'/stat date YYYY-MM-DD',
'/stat range YYYY-MM-DD YYYY-MM-DD',
'/stat ages',
'/stat names',
'/stat countries',
'/all-stats'
]);
return;
}
if (sub === 'today') {
const day = new Date().toISOString().slice(0, 10);
const dayRecords = records.filter((r) => r.day === day);
sendCommandResult(socket, `Logins heute (${day}): ${dayRecords.length}`);
return;
}
if (sub === 'date') {
const day = parts[2];
if (!day || !/^\d{4}-\d{2}-\d{2}$/.test(day)) {
sendCommandResult(socket, 'Nutzung: /stat date YYYY-MM-DD');
return;
}
const dayRecords = records.filter((r) => r.day === day);
sendCommandResult(socket, `Logins am ${day}: ${dayRecords.length}`);
return;
}
if (sub === 'range') {
const from = parts[2];
const to = parts[3];
if (!from || !to || !/^\d{4}-\d{2}-\d{2}$/.test(from) || !/^\d{4}-\d{2}-\d{2}$/.test(to)) {
sendCommandResult(socket, 'Nutzung: /stat range YYYY-MM-DD YYYY-MM-DD');
return;
}
const filtered = records.filter((r) => r.day >= from && r.day <= to);
const perDay = aggregateTop(filtered, (r) => r.day, 1000)
.sort((a, b) => a[0].localeCompare(b[0]))
.map(([day, count]) => `${day}: ${count}`);
sendCommandResult(socket, [`Logins ${from} bis ${to}: ${filtered.length}`, ...perDay]);
return;
}
if (sub === 'ages') {
const ages = records.map((r) => r.age);
const minAge = Math.min(...ages);
const maxAge = Math.max(...ages);
const youngest = records.find((r) => r.age === minAge);
const oldest = records.find((r) => r.age === maxAge);
sendCommandResult(socket, [
`Jüngster Nutzer: ${youngest.userName} (${youngest.age})`,
`Ältester Nutzer: ${oldest.userName} (${oldest.age})`
]);
return;
}
if (sub === 'names') {
const topNames = aggregateTop(records, (r) => r.userName, 20);
sendCommandResult(socket, [
`Namen gesamt (verschieden): ${new Set(records.map((r) => r.userName)).size}`,
...topNames.map(([name, count]) => `${name}: ${count}`)
]);
return;
}
if (sub === 'countries') {
const topCountries = aggregateTop(records, (r) => r.country, 20);
sendCommandResult(socket, topCountries.map(([country, count]) => `${country}: ${count}`));
return;
}
sendCommandResult(socket, 'Unbekannter /stat-Befehl. Nutze /stat help');
}
function executeAllStatsCommand(socket, client) {
if (!hasRight(client, CHAT_RIGHTS.STAT)) {
sendCommandResult(socket, 'Keine Berechtigung: Recht "stat" fehlt.');
return;
}
sendCommandResult(socket, buildAllStats(readLoginRecords()));
}
function executeKickCommand(socket, client, parts) {
if (!hasRight(client, CHAT_RIGHTS.KICK)) {
sendCommandResult(socket, 'Keine Berechtigung: Recht "kick" fehlt.');
return;
}
const targetName = (parts[1] || '').trim();
if (!targetName) {
sendCommandResult(socket, 'Nutzung: /kick <username>');
return;
}
let targetSessionId = null;
let targetClient = null;
for (const [sid, c] of clients.entries()) {
if (c.userName === targetName && c.socket && c.socket.connected) {
targetSessionId = sid;
targetClient = c;
break;
}
}
if (!targetClient) {
sendCommandResult(socket, `User "${targetName}" ist nicht online.`);
return;
}
if (targetClient.socket) {
targetClient.socket.emit('error', { message: 'Du wurdest vom Chat getrennt (kick).' });
targetClient.socket.disconnect(true);
}
clients.delete(targetSessionId);
broadcastUserList();
sendCommandResult(socket, `User "${targetName}" wurde gekickt.`);
}
function verifyChatUser(username, password) {
const users = loadChatUsers(__dirname);
const user = users.find((u) => u.username.toLowerCase() === username.toLowerCase());
if (!user) return null;
const [algo, hash] = user.passwordHash.split(':');
if (algo !== 'sha256' || !hash) return null;
const inputHash = sha256(password);
if (inputHash !== hash) return null;
return {
username: user.username,
rights: new Set(user.rights)
};
}
function executeCommand(socket, client, rawInput) {
const input = rawInput.trim();
if (client.pendingChatLogin) {
if (client.pendingChatLogin.step === 'username') {
const enteredUser = input;
if (!enteredUser) {
sendCommandResult(socket, 'Username darf nicht leer sein. Bitte Username eingeben:');
return true;
}
client.pendingChatLogin = { step: 'password', username: enteredUser };
sendCommandResult(socket, 'Passwort eingeben:');
return true;
}
if (client.pendingChatLogin.step === 'password') {
const username = client.pendingChatLogin.username;
const auth = verifyChatUser(username, input);
client.pendingChatLogin = null;
if (!auth) {
sendCommandResult(socket, 'Login fehlgeschlagen. Benutzername oder Passwort falsch.');
return true;
}
client.chatAuth = auth;
sendCommandResult(
socket,
`Login erfolgreich als ${auth.username}. Rechte: ${Array.from(auth.rights).join(', ') || 'keine'}`
);
return true;
}
}
if (!input.startsWith('/')) return false;
const parts = input.split(/\s+/);
const command = parts[0].toLowerCase();
if (command === '/login') {
const username = (parts[1] || '').trim();
if (username) {
client.pendingChatLogin = { step: 'password', username };
sendCommandResult(socket, 'Passwort eingeben:');
} else {
client.pendingChatLogin = { step: 'username', username: '' };
sendCommandResult(socket, 'Username eingeben:');
}
return true;
}
if (command === '/logout-admin') {
const wasLoggedIn = !!client.chatAuth;
client.chatAuth = null;
client.pendingChatLogin = null;
sendCommandResult(
socket,
wasLoggedIn
? 'Admin/Command-Login wurde abgemeldet.'
: 'Es war kein Admin/Command-Login aktiv.'
);
return true;
}
if (command === '/whoami-rights') {
if (!client.chatAuth) {
sendCommandResult(socket, 'Nicht per Command-Login angemeldet.');
return true;
}
sendCommandResult(socket, [
`Angemeldet als: ${client.chatAuth.username}`,
`Rechte: ${Array.from(client.chatAuth.rights).join(', ') || 'keine'}`
]);
return true;
}
if (command === '/stat') {
executeStatsCommand(socket, client, parts);
return true;
}
if (command === '/all-stats') {
executeAllStatsCommand(socket, client);
return true;
}
if (command === '/kick') {
executeKickCommand(socket, client, parts);
return true;
}
sendCommandResult(socket, `Unbekannter Befehl: ${command}`);
return true;
}
// Socket.IO-Verbindungshandler
io.on('connection', (socket) => {
const request = socket.handshake;
@@ -599,6 +1006,11 @@ export function setupBroadcast(io, __dirname) {
const { toUserName, message, messageId, isImage, imageType, imageUrl } = data;
// Chat-Befehle werden direkt serverseitig verarbeitet
if (!isImage && typeof message === 'string' && executeCommand(socket, client, message)) {
return;
}
if (!toUserName) {
socket.emit('error', { message: 'Empfänger fehlt' });
return;

File diff suppressed because it is too large Load Diff

229
src/app.h
View File

@@ -1,229 +0,0 @@
#ifndef APP_H
#define APP_H
#include <Wt/WApplication.h>
#include <Wt/WEnvironment.h>
#include "broadcast.h"
#include "curl/curl.h"
#include <libxml2/libxml/tree.h>
#include <libxml2/libxml/xpath.h>
#include <unordered_set>
namespace Magick {
class Image;
class Blob;
}
class App : public Wt::WApplication, public Client {
public:
App(const Wt::WEnvironment& env, Broadcast& server);
~App();
private:
const std::string adminName {"comiciusadmin"};
const std::string adminPassword {"p3Lv9!7?+Qq"};
// Key: short gender code used in the protocol, Value: translation id for display text
std::map<Wt::WString, Wt::WString> genders_ {
{"F", "gender_female"},
{"M", "gender_male"},
{"P", "gender_pair"},
{"TF", "gender_trans_mf"},
{"TM", "gender_trans_fm"}
};
std::vector<std::string> notAllowedNickPhrases_ {
"whore",
"hitler",
"nazi",
"admin"
};
struct Smiley {
std::string code;
std::string tooltip;
Smiley()=default;
Smiley(std::string code_, std::string tooltip_): code(code_), tooltip(tooltip_) {};
};
std::unordered_map<std::string, Smiley> smileys_ {
{":)", Smiley("1F642", "Smile")},
{":D", Smiley("1F600", "Laugh")},
{":(", Smiley("1F641", "Sad")},
{";)", Smiley("1F609", "Twinkle")},
{":p", Smiley("1F60B", "Tongue")},
{";p", Smiley("1F61C", "Twinkle tongue")},
{"O)", Smiley("1F607", "Angel")},
{":*", Smiley("1F617", "Kiss")},
{"(h)", Smiley("1FA77", "Heart")},
{"xD", Smiley("1F602", "Laughing hard")},
{":@", Smiley("1F635", "Confused")},
{":O", Smiley("1F632", "Surprised")},
{":3", Smiley("1F63A", "Cat face")},
{":|", Smiley("1F610", "Neutral")},
{":/", Smiley("1FAE4", "Skeptical")},
{":#", Smiley("1F912", "Sick")},
{"#)", Smiley("1F973", "Partied")},
{"%)", Smiley("1F974", "Drunk")},
{"(t)", Smiley("1F44D", "Thumbs up")},
{":'(", Smiley("1F622", "Cry")}
};
struct Search {
Wt::WContainerWidget *outputContainer = nullptr;
Wt::WString userName{""};
int minAge{18};
int maxAge{150};
std::unordered_set<std::string> gender;
std::unordered_set<std::string> countries;
Search()=default;
Search(Wt::WContainerWidget *outputContainer_):
outputContainer(outputContainer_) {
set = true;
gender.insert("All");
countries.insert("All");
}
bool set = false;
};
Wt::WString smileyPlaceholder_ = "&#x{1};";
const Wt::WEnvironment &env_;
Broadcast &server_;
Wt::WContainerWidget *menuContainer_;
Wt::WContainerWidget *userListContainer_;
Wt::WContainerWidget *contentContainer_;
Wt::JSignal<std::string, std::string> updateLocationSignal_;
std::string responseData_{""};
Wt::WPushButton *inbox_;
std::vector<std::string> searchResults_;
std::string currentConversationWith_{""};
bool inboxOpen_{false};
std::shared_ptr<int> messageCursorPosition_;
std::unique_ptr<Wt::WSound> messageReceived_;
Search searchFields;
Wt::WTimer *loginTimer_;
Wt::WTimer *timeoutRemainingTimer_;
bool isLoggedInAsAdmin {false};
void setMetaTags();
void initLocale();
Wt::WLocale determineLocaleFromBrowser() const;
void initApp();
void reSetUser();
Wt::WVBoxLayout *createVerticalLayout();
Wt::WHBoxLayout *createActionLayout(Wt::WVBoxLayout *verticalContainer);
void createUserListContainer(Wt::WHBoxLayout *layout);
void createContentContainer(Wt::WHBoxLayout *layout);
void createHeadContainer(Wt::WVBoxLayout *layout);
void createMenuContainer(Wt::WVBoxLayout *layout);
void showLogin();
void incomingBroadcast();
void startChat();
void updateLocation();
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, std::string *output) {
size_t totalSize = size * nmemb;
output->append(reinterpret_cast<const char *>(contents), totalSize);
return totalSize;
};
void populateCountryComboBox(Wt::WComboBox *countryWidget);
void handleLogin(Wt::WLineEdit *userName, Wt::WComboBox *countryWidget, Wt::WSpinBox *ageWidget, Wt::WComboBox *genderWidget);
void populateGenderComboBox(Wt::WComboBox *genderWidget);
std::string getGenderShortByGender(std::string gender);
void updateUserlist(Wt::Json::Array userList, int size);
std::vector<Wt::Json::Object> sortUserList(Wt::Json::Array unsortedUserList);
void createMenu();
void addLeaveButton();
void logout();
void addHistoryButton();
void addIdentifier();
void addSearchButton();
void addInboxButton();
void showSearchWindow();
void startSearch();
void showSearch(Wt::Json::Object broadcast);
template<class Class> Class *addSearchItemLine(Wt::WVBoxLayout *layout, std::string label, std::unique_ptr<Wt::WContainerWidget> additionalItem = nullptr);
Wt::WContainerWidget *addSearchItemContainer(Wt::WVBoxLayout *layout, std::string label);
void openInbox();
bool isNickAllowed(const std::string &nick);
bool compareJsonObjects(const Wt::Json::Object &obj1, const Wt::Json::Object &obj2);
void requestConversation(std::string conversationWith);
void showConversation(Wt::Json::Object data);
void sendMessage(Wt::WLineEdit *inputLine);
void renderConversation(Wt::Json::Object conversation);
void showUnreadMessages(Wt::Json::Object data);
void showOpenInbox(Wt::Json::Object data);
void updateUserinfo(Wt::Json::Object data);
std::unique_ptr<Wt::WContainerWidget> createSmileysBar(Wt::WLineEdit *inputLine, std::shared_ptr<int> cursorPosition);
void toggleSmileysBar(Wt::WContainerWidget *smileyBar);
void systemEvent(Wt::Json::Object broadcast);
void addStartChatButton(Wt::WGridLayout *contentGrid, Wt::WLineEdit *userName, Wt::WComboBox *country, Wt::WSpinBox *age, Wt::WComboBox *gender);
Wt::WComboBox *addCountrySelection(Wt::WGridLayout *contentGrid);
Wt::WSpinBox *addAgeInput(Wt::WGridLayout *contentGrid);
Wt::WLineEdit *addUsernameInput(Wt::WGridLayout *contentGrid);
Wt::WComboBox *addGenderSelection(Wt::WGridLayout *contentGrid);
void createLoginContainer();
void connectToServer();
void setUserData(const std::string &nick, Wt::WComboBox *countryWidget, Wt::WSpinBox *ageWidget, Wt::WComboBox *genderWidget);
void validateAge(Wt::WSpinBox *ageWidget);
bool isGenderSelected(Wt::WComboBox *genderWidget);
void validateGender(Wt::WComboBox *genderWidget);
bool isNameAlreadyInUse(const std::string &nick);
bool isInvalidName(const std::string &nick);
void validateName(const std::string &nick);
std::string extractTrimmedUserName(Wt::WLineEdit *userName);
Wt::WPushButton *createSendButton(Wt::WHBoxLayout *inputLayout, Wt::WLineEdit *inputLine);
Wt::WContainerWidget *createSmileyBar(Wt::WContainerWidget *parent, Wt::WLineEdit *inputLine, std::shared_ptr<int> cursorPosition);
Wt::WContainerWidget *createSmileyButton(Wt::WHBoxLayout *inputLayout, Wt::WLineEdit *inputLine, std::shared_ptr<int> cursorPosition);
Wt::WImage *createSendImageButton(Wt::WHBoxLayout *inputLayout);
Wt::WLineEdit *createInputLine(Wt::WHBoxLayout *inputLayout);
Wt::WContainerWidget *createInputContainer(Wt::WVBoxLayout *layout);
std::unique_ptr<Wt::WText> createInfoText(Wt::Json::Object userData);
Wt::WContainerWidget *createInfoWidget(Wt::WVBoxLayout *layout, Wt::Json::Object userData);
void setupConversationUI(Wt::Json::Object userData);
bool shouldShowConversation(Wt::Json::Object userData);
Wt::Json::Object extractUserData(Wt::Json::Object data);
std::unique_ptr<Wt::WPushButton> createBlockButton(Wt::Json::Object userData);
std::string replaceSmileys(std::string outputText);
void renderChatLine(Wt::Json::Object &line, Wt::Json::Object conversation, Wt::WContainerWidget *outputContainer);
std::string getChatLineWriter(Wt::Json::Object &line, Wt::Json::Object conversation);
void renderChatLines(Wt::Json::Object conversation, Wt::WContainerWidget *outputContainer);
void updateOutputContainer(Wt::Json::Object conversation);
void handleException(const std::exception &e);
void processXmlNode(xmlNodePtr node);
void parseXmlDocument(xmlDocPtr doc);
void processCurlResponse();
void performCurlRequest(CURL *curl);
void setCurlOptions(CURL *curl, const std::string &apiUrl);
std::string buildApiUrl(const std::string &userIP);
std::string getUserIP();
Wt::WWebWidget *createImageElement(Wt::Json::Object &line, const std::string &writer, Wt::WContainerWidget *outputContainer, std::string id);
Wt::WWebWidget *createTextElement(const std::string &writer, const std::string &text, Wt::WContainerWidget *outputContainer, std::string id);
void createImprintContainer(Wt::WVBoxLayout *containerLayout);
Wt::WContainerWidget *setupSearchButton(Wt::WVBoxLayout *contentLayout);
void restoreSearchFields(Wt::WContainerWidget *searchResultContainer, Wt::WLineEdit *userNameEdit, Wt::WSpinBox *minAgeEdit, Wt::WSpinBox *maxAgeEdit, Wt::WContainerWidget *countryDropDownContainer, Wt::WContainerWidget *gendersDropDownContainer, Wt::WContainerWidget *countryOpenList, Wt::WContainerWidget *gendersOpenList);
Wt::WLineEdit *setupNameSearchField(Wt::WVBoxLayout *contentLayout);
std::pair<Wt::WContainerWidget *, Wt::WContainerWidget *> setupGendersDropDown(Wt::WVBoxLayout *contentLayout);
std::pair<Wt::WContainerWidget *, Wt::WContainerWidget *> setupCountryDropDown(Wt::WVBoxLayout *contentLayout);
std::pair<Wt::WSpinBox*, Wt::WSpinBox*> setupSearchFields(Wt::WVBoxLayout *contentLayout);
Wt::WVBoxLayout *resetSearchFields();
void itemChanged(Wt::WCheckBox *item, Wt::WContainerWidget *dropDownContainer, Wt::WContainerWidget *openButton, std::unordered_set<std::string> *saveItems);
void addItem(const std::string &country, Wt::WContainerWidget *dropDownContainer, Wt::WContainerWidget *container, std::unordered_set<std::string> *saveItems, bool isSelected = false);
void addUserItemToLayout(Wt::WVBoxLayout *layout, Wt::Json::Object userObject);
std::unordered_set<std::string> gendersListToShortGendersList(std::unordered_set<std::string> gendersList);
std::string genderShortOfGender(const std::string incomingGender);
void extendSearchResultIfNeeded(Wt::Json::Object broadcast);
void removeUserFromSearch(Wt::Json::Object broadcast);
void requestHistory();
void showHistory(Wt::Json::Object broadcast);
void connectionTimedOut();
void addLoginTimeView();
void addTimeoutView();
void showPartnerSites();
void sendImage();
void imageUploaded(Wt::WFileUpload *fileWidget, std::shared_ptr<Magick::Blob> localImage, Wt::WImage *image, Wt::WPushButton *okButton);
std::list<Magick::Image> resizeImages(std::list<Magick::Image>& images, int maxWidth, int maxHeight);
bool isAnimatedGIF(const Magick::Blob &blob);
void showStandardPage();
void onInternalPathChanged(const std::string &path);
void showAdminPage(std::string page);
void showAdminLogin(std::string page);
void showPageNotExists();
void showAdminLogins();
void showAdminStarts();
};
#endif // APP_H

View File

@@ -1,770 +0,0 @@
#include "broadcast.h"
#include <Wt/WApplication.h>
#include <Wt/WAny.h>
#include <Wt/Json/Array.h>
#include <Wt/Json/Serializer.h>
#include <Wt/Json/Parser.h>
#include <Wt/Json/Object.h>
#include <Wt/Auth/HashFunction.h>
#include <Wt/WLocalDateTime.h>
#include <Wt/Utils.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>
#include <curl/curl.h>
#include <boost/algorithm/string.hpp>
#include <Magick++.h>
Broadcast::Broadcast(Wt::WServer *server):
wtServer_(server) {
downloadCountries();
thread_ = std::thread(std::bind(&Broadcast::run, this));
lastTimeoutCheck_ = Wt::WDateTime::currentDateTime();
checkAndLogStart();
}
void Broadcast::checkAndLogStart() {
const std::string logFilePath = "/opt/ypchat/logs/starts.log";
std::ifstream infile(logFilePath);
if (!infile.good()) {
std::ofstream outfile(logFilePath);
outfile.close();
}
infile.close();
Wt::WDateTime now = Wt::WDateTime::currentDateTime();
std::string timestamp = now.toString("yyyy-MM-dd HH:mm:ss").toUTF8();
std::ofstream outfile(logFilePath, std::ios_base::app);
if (outfile.is_open()) {
outfile << timestamp << std::endl;
outfile.close();
} else {
std::cerr << "Fehler beim Öffnen der Datei: " << logFilePath << std::endl;
}
}
Broadcast::~Broadcast() {
stop_ = true;
thread_.join();
}
void Broadcast::connect(Client *client, const std::function<void ()> &fct) {
connections_.push_back(std::make_unique<Connection>(Wt::WApplication::instance()->sessionId(), client, fct));
auto userlistBroadcast = createUserList();
auto newUserBroadcast = Wt::Json::Object{
{"type", "newuser"},
{"data", client->json()},
};
for (auto &connection: connections_) {
connection->addBroadcast(userlistBroadcast);
connection->addBroadcast(newUserBroadcast);
}
logClientLogin(client->json());
}
void Broadcast::logClientLogin(const Wt::Json::Object &clientJson) {
const std::string logFilePath = "../logs/logins.log";
Wt::Json::Array logArray;
std::ifstream infile(logFilePath);
if (infile.is_open()) {
std::stringstream buffer;
buffer << infile.rdbuf();
std::string fileContent = buffer.str();
infile.close();
if (!fileContent.empty()) {
Wt::Json::parse(fileContent, logArray);
}
}
// Zeitstempel hinzufügen
Wt::Json::Object logEntry = clientJson;
Wt::WDateTime now = Wt::WDateTime::currentDateTime();
std::string timestamp = now.toString("yyyy-MM-dd HH:mm:ss").toUTF8();
logEntry["timestamp"] = Wt::Json::Value(timestamp);
logArray.push_back(logEntry);
std::ofstream outfile(logFilePath);
if (outfile.is_open()) {
outfile << Wt::Json::serialize(logArray) << std::endl;
outfile.close();
} else {
std::cerr << "Fehler beim Öffnen der Datei: " << logFilePath << std::endl;
}
}
void Broadcast::disconnect(Client *client) {
std::unique_lock<std::mutex> lock(mutex_);
for (unsigned int i = 0; i < connections_.size(); ++ i) {
if (connections_[i]->client() == client) {
connections_.erase(connections_.begin() + i);
break;
}
}
Wt::Json::Object leftBroadcast{
{"type", "userleft"},
{"data", Wt::Json::Value(client->userName)}
};
auto userlistBroadcast = createUserList();
for (auto &connection: connections_) {
connection->addBroadcast(userlistBroadcast);
connection->addBroadcast(leftBroadcast);
}
auto sessionId = sessionIdForUserName(client->userName);
for (auto it = conversations_.begin(); it != conversations_.end();) {
if (it->first.find(sessionId + "/") == 0 || it->first.rfind("/" + sessionId) == it->first.size() - sessionId.length() - 1) {
it = conversations_.erase(it);
} else {
++it;
}
}
}
void Broadcast::addToDisconnectList(Client *client) {
toDisconnect_.push_back(client);
}
Wt::Json::Object Broadcast::reSetUser(std::string oldSessionId, std::string newSessionId) {
for (auto& connection : connections_) {
if (connection->setSessionId(oldSessionId, newSessionId)) {
Wt::Json::Object userData {
{"gender", Wt::Json::Value(connection->gender())},
{"country", Wt::Json::Value(connection->country())},
{"iso-country-code", Wt::Json::Value(getCountryIsoCodeByCountry(connection->country()))},
{"username", Wt::Json::Value(connection->userName())},
{"age", Wt::Json::Value(connection->age())},
};
reSetSessionIdInMessages(oldSessionId, newSessionId);
return userData;
}
}
return {};
}
int Broadcast::count() const {
std::unique_lock<std::mutex> lock(mutex_);
return connections_.size();
}
std::string Broadcast::userNameForSessionId(std::string sessionId) {
for (const auto &connection: connections_) {
if (sessionId == connection->sessionId()) {
return connection->userName();
}
}
return "";
}
std::string Broadcast::sessionIdForUserName(std::string userName) {
for (const auto &connection: connections_) {
if (userName == connection->userName()) {
return connection->sessionId();
}
}
return "";
}
Wt::Json::Object Broadcast::userForSessionId(std::string sessionId) {
for (const auto &connection: connections_) {
if (sessionId == connection->sessionId()) {
return connection->client()->json();
}
}
return Wt::Json::Object{};
}
Wt::Json::Object Broadcast::userForUserName(std::string userName) {
for (const auto &connection: connections_) {
if (userName == connection->userName()) {
return connection->client()->json();
}
}
return Wt::Json::Object{};
}
void Broadcast::changeClientForSessionId(std::string sessionId, Client *client) {
std::unique_lock<std::mutex> lock(mutex_);
for (auto &connection: connections_) {
if (sessionId == connection->sessionId()) {
connection->setClient(client);
}
}
}
bool Broadcast::nameIsFree(std::string userNameToCheck) {
std::unique_lock<std::mutex> lock(mutex_);
for (auto &connection: connections_) {
if (connection->userName() == userNameToCheck) {
return false;
}
}
return true;
}
void Broadcast::run() {
Wt::Json::Object timedoutBroadcast {
{"type", "timedout"},
{"data", "dummy"}
};
for (;;) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
for (auto clientToDisconnect: toDisconnect_) {
disconnect(clientToDisconnect);
}
toDisconnect_.clear();
if (stop_) {
return;
}
std::unique_lock<std::mutex> lock(mutex_);
for (auto &connection: connections_) {
if (connection->client()->activitiesTimedOut()) {
auto sessionId = connection->sessionId();
toDisconnect_.push_back(connection->client());
addMessageToSessionBroadcast(sessionId, timedoutBroadcast);
}
if (connection->getBroadcasts().size() == 0) {
continue;
}
Wt::WServer::instance()->post(connection->sessionId(), connection->fct());
}
}
}
Wt::Json::Object Broadcast::logoutBroadcast() {
return Wt::Json::Object{
{"type", "logout"}
};
}
void Broadcast::downloadCountries() {
CURL* curl = curl_easy_init();
if (!curl) {
std::cerr << "Failed to initialize CURL." << std::endl;
}
curl_easy_setopt(curl, CURLOPT_URL, countriesDownloadUrl_.toUTF8().c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseData_);
CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res != CURLE_OK) {
std::cerr << "Failed to download data: " << curl_easy_strerror(res) << std::endl;
}
parseCountriesData();
}
std::map<Wt::WString, Wt::WString> Broadcast::countries() {
return countriesMap_;
}
Wt::WString Broadcast::getCountryIsoCodeByCountry(Wt::WString country) {
for (const auto &countryItem: countriesMap_) {
if (countryItem.first == country) {
return countryItem.second;
}
}
return "";
}
std::list<Wt::Json::Object> Broadcast::getBroadcastsForSession(std::string sessionId) {
std::unique_lock<std::mutex> lock(mutex_);
for (auto &connection: connections_) {
if (connection->sessionId() == sessionId) {
return connection->getBroadcasts(true);
}
}
return std::list<Wt::Json::Object>();
}
Wt::Json::Object Broadcast::addMessage(std::string fromSessionId, std::string toUserName, Message message) {
auto toSessionId = sessionIdForUserName(toUserName);
if (toSessionId == "") {
return Wt::Json::Object{
{"type", "system"},
{"data", "User left the chat."}
};
}
auto fromUserName = userNameForSessionId(fromSessionId);
if (blockings_[toUserName].find(fromUserName) != blockings_[toUserName].end()) {
sendBlockedMessage(fromSessionId, toUserName);
}
auto id1 = fromSessionId + "/" + toSessionId;
auto id2 = toSessionId + "/" + fromSessionId;
auto it = conversations_.find(id1);
if (it == conversations_.end()) {
it = conversations_.find(id2);
}
if (it == conversations_.end()) {
MessageQueue messageQueue;
messageQueue.messages.push_back(message);
auto [newIt, success] = conversations_.emplace(id1, MessageQueue{});
it = newIt;
}
it->second.messages.push_back(message);
it->second.user1Read = false;
it->second.user2Read = false;
sendMessageQueueToSession(fromSessionId, userNameForSessionId(fromSessionId), toUserName, it->second.messages);
sendMessageQueueToSession(toSessionId, userNameForSessionId(fromSessionId), toUserName, it->second.messages);
sendMessageCount(toSessionId);
return Wt::Json::Object{
{"type", "dummy"},
{"data", "Message added to users queues."}
};
}
Wt::Json::Object Broadcast::addImage(std::string fromSessionId, std::string toUserName, std::shared_ptr<Magick::Blob> image) {
auto message = Message(fromSessionId, image);
return addMessage(fromSessionId, toUserName, message);
}
void Broadcast::setConversationRead(std::string readingUserSessionId, std::string secondUserName) {
auto toSessionId = sessionIdForUserName(secondUserName);
std::unique_lock<std::mutex> lock(mutex_);
if (toSessionId == "") {
return;
}
auto id1 = readingUserSessionId + "/" + toSessionId;
auto id2 = toSessionId + "/" + readingUserSessionId;
auto it = conversations_.find(id1);
if (it != conversations_.end()) {
it->second.user1Read = true;
} else {
it = conversations_.find(id2);
if (it != conversations_.end()) {
it->second.user2Read = true;
}
}
sendMessageCount(readingUserSessionId);
}
void Broadcast::sendConversation(std::string sessionId1, std::string userName2) {
auto sessionId2 = sessionIdForUserName(userName2);
auto userName1 = userNameForSessionId(sessionId1);
std::unique_lock<std::mutex> lock(mutex_);
auto id1 = sessionId1 + "/" + sessionId2;
auto id2 = sessionId2 + "/" + sessionId1;
auto it = conversations_.find(id1);
if (it == conversations_.end()) {
it = conversations_.find(id2);
if (it == conversations_.end()) {
return;
}
}
sendMessageQueueToSession(sessionId1, userName1, userName2, it->second.messages);
}
void Broadcast::sendOpenConversations(std::string forSessionId) {
std::unique_lock<std::mutex> lock(mutex_);
Wt::Json::Array conversationsWithList;
for (const auto &conversation: conversations_) {
const auto key = conversation.first;
const auto data = conversation.second;
if ((key.compare(0, forSessionId.length() + 1, forSessionId + "/") == 0
&& !data.user1Read)
|| (key.compare(key.length() - forSessionId.length() - 1, forSessionId.length() + 1, "/" + forSessionId) == 0
&& !data.user2Read)) {
std::size_t found = key.find("/");
if (found != std::string::npos) {
std::string firstSessionId = key.substr(0, found);
std::string secondSessionId = key.substr(found + 1);
auto userData = forSessionId == firstSessionId ? userForSessionId(secondSessionId) : userForSessionId(firstSessionId);
conversationsWithList.push_back(userData);
}
}
}
Wt::Json::Object data{
{"type", "openconversations"},
{"data", conversationsWithList}
};
addMessageToSessionBroadcast(forSessionId, data);
}
void Broadcast::sendUserInformation(std::string sendToSessionId, std::string userName, std::string requestingUserName) {
std::unique_lock<std::mutex> lock(mutex_);
auto userData = userForUserName(userName);
if (blockings_.find(userName) == blockings_.end()) {
blockings_[userName] = std::set<std::string>();
}
userData["blocked"] = (blockings_.find(userName) != blockings_.end() && blockings_[userName].find(requestingUserName) != blockings_[userName].end());
Wt::Json::Object broadcast{
{"type", "userinfo"},
{"data", userData}
};
addMessageToSessionBroadcast(sendToSessionId, broadcast);
}
void Broadcast::toggleBlockUser(std::string blockingUserName, std::string blockedUser, std::string blockingUserSessionId) {
std::unique_lock<std::mutex> lock(mutex_);
if (blockings_.find(blockedUser) == blockings_.end()) {
blockings_[blockedUser] = std::set<std::string>();
}
if (blockings_[blockedUser].find(blockingUserName) != blockings_[blockedUser].end()) {
blockings_[blockedUser].erase(blockingUserName);
sendUnblockDone(blockingUserSessionId, blockedUser);
} else {
blockings_[blockedUser].insert(blockingUserName);
sendBlockDone(blockingUserSessionId, blockedUser);
}
}
void Broadcast::requestConversation(std::string sendToSessionId, std::string withUserName, std::string requestingUserName) {
auto userData = userForUserName(withUserName);
std::unique_lock<std::mutex> lock(mutex_);
if (blockings_.find(withUserName) == blockings_.end()) {
blockings_[withUserName] = std::set<std::string>();
}
userData["blocked"] = !(blockings_.find(withUserName) == blockings_.end() || blockings_[withUserName].find(requestingUserName) == blockings_[withUserName].end());
auto sessionId2 = sessionIdForUserName(withUserName);
auto id1 = sendToSessionId + "/" + sessionId2;
auto id2 = sessionId2 + "/" + sendToSessionId;
auto it = conversations_.find(id1);
if (it == conversations_.end()) {
it = conversations_.find(id2);
if (it == conversations_.end()) {
MessageQueue messageQueue;
auto [newIt, success] = conversations_.emplace(id1, MessageQueue{});
it = newIt;
}
}
Wt::Json::Object broadcast{
{"type", "conversation-start"},
{"data", userData},
};
addMessageToSessionBroadcast(sendToSessionId, broadcast);
Wt::Json::Array messagesJson;
for (Broadcast::Message &message: it->second.messages) {
messagesJson.push_back(message.json());
}
broadcast = {
{"type", "messagequeue"},
{"user1", Wt::Json::Value(requestingUserName)},
{"user2", Wt::Json::Value(withUserName)},
{"sessionid1", Wt::Json::Value(sendToSessionId)},
{"sessionid2", Wt::Json::Value(sessionId2)},
{"data", messagesJson}
};
addMessageToSessionBroadcast(sendToSessionId, broadcast);
}
void Broadcast::userSearch(std::string toSession, std::string nameIncludes, int minAge, int maxAge, std::unordered_set<std::string> countries, std::unordered_set<std::string> genders, std::string excludeName) {
Wt::Json::Array searchResult;
for (const auto &user: connections_) {
if (
(nameIncludes == "" || user->userName().find(nameIncludes) != std::string::npos)
&& (minAge <= user->age())
&& (maxAge >= user->age())
&& (countries.contains("All") || countries.contains(user->country()) || countries.size() == 0)
&& (genders.contains("All") || genders.contains(user->gender()) || genders.size() == 0)
&& (user->userName() != excludeName)
) {
searchResult.push_back(user->client()->json());
}
}
Wt::Json::Object broadcast{
{"type", "search-result"},
{"data", searchResult}
};
addMessageToSessionBroadcast(toSession, broadcast);
}
void Broadcast::sendHistory(std::string toSession) {
std::unique_lock<std::mutex> lock(mutex_);
Wt::Json::Array filteredConversationsPartners;
for (const auto& pair : conversations_) {
const std::string& key = pair.first;
if (key.rfind(toSession + "/", 0) == 0 || key.find("/" + toSession) == key.size() - toSession.length() - 1) {
std::string partnerId;
if (key.rfind(toSession + "/", 0) == 0) {
partnerId = key.substr(toSession.size() + 1);
} else {
partnerId = key.substr(0, key.size() - toSession.size() - 1);
}
auto partner = userForSessionId(partnerId);
filteredConversationsPartners.push_back(Wt::Json::Value(partner));
}
}
Wt::Json::Object broadcast{
{"type", "history"},
{"data", filteredConversationsPartners}
};
addMessageToSessionBroadcast(toSession, broadcast);
}
bool Broadcast::parseCountriesData() {
std::istringstream iss(responseData_);
std::string line;
while (std::getline(iss, line)) {
line.erase(std::remove_if(line.begin(), line.end(), [](char c) { return !std::isprint(c) && c != ' '; }), line.end());
std::vector<std::string> tokens;
boost::split(tokens, line, boost::is_any_of(","));
if (tokens.size() == 2) {
auto name = tokens[0];
auto code = tokens[1];
std::transform(code.begin(), code.end(), code.begin(), ::tolower);
countriesMap_[name] = code;
}
}
return true;
}
void Broadcast::reSetSessionIdInMessages(std::string oldSessionId, std::string newSessionId) {
for (auto &conversation: conversations_) {
conversation.second.setNewSesionId(oldSessionId, newSessionId);
}
}
Wt::Json::Object Broadcast::createUserList() {
Wt::Json::Array userList;
for (const auto &connection: connections_) {
Wt::Json::Object user;
user["name"] = Wt::asString(connection->userName());
user["age"] = connection->age();
user["gender"] = Wt::asString(connection->gender());
user["country"] = Wt::asString(connection->country());
user["isoCountryCode"] = getCountryIsoCodeByCountry(connection->country());
userList.push_back(user);
}
Wt::Json::Object data;
data["data"] = userList;
data["type"] = "userlist";
data["count"] = (int)userList.size();
return data;
}
void Broadcast::sendMessageQueueToSession(std::string receiverSessionId, std::string user1, std::string user2, std::vector<Message> messages) {
Wt::Json::Array messagesJson;
for (Broadcast::Message &message: messages) {
messagesJson.push_back(message.json());
}
Wt::Json::Object broadcast{
{"type", "messagequeue"},
{"user1", Wt::Json::Value(user1)},
{"user2", Wt::Json::Value(user2)},
{"sessionid1", Wt::Json::Value(sessionIdForUserName(user1))},
{"sessionid2", Wt::Json::Value(sessionIdForUserName(user2))},
{"data", messagesJson}
};
addMessageToSessionBroadcast(receiverSessionId, broadcast);
}
void Broadcast::addMessageToSessionBroadcast(std::string sessionId, Wt::Json::Object message) {
for (auto &connection: connections_) {
if (connection->sessionId() == sessionId) {
connection->addBroadcast(message);
}
}
}
void Broadcast::sendMessageCount(std::string sessionId) {
int count{0};
for (const auto &conversation: conversations_) {
const auto key = conversation.first;
const auto data = conversation.second;
if ((key.compare(0, sessionId.length() + 1, sessionId + "/") == 0
&& !data.user1Read)
|| (key.compare(key.length() - sessionId.length() - 1, sessionId.length() + 1, "/" + sessionId) == 0
&& !data.user2Read)) {
++count;
}
}
Wt::Json::Object broadcast{
{"type", "unread-chats"},
{"data", count}
};
addMessageToSessionBroadcast(sessionId, broadcast);
}
void Broadcast::sendBlockedMessage(std::string sessionId, std::string toUserName) {
Wt::Json::Object broadcast{
{"type", "system"},
{"related-user", Wt::WString(toUserName)},
{"data", "The user has blocked you."}
};
addMessageToSessionBroadcast(sessionId, broadcast);
}
void Broadcast::sendBlockDone(std::string sessionId, std::string toUserName) {
addMessage(sessionId, toUserName, Message("Conversation is blocked."));
Wt::Json::Object broadcast{
{"type", "system"},
{"related-user", Wt::WString(toUserName)},
{"data", "blocked"}
};
addMessageToSessionBroadcast(sessionId, broadcast);
}
void Broadcast::sendUnblockDone(std::string sessionId, std::string toUserName) {
addMessage(sessionId, toUserName, Message("Conversation is unblocked."));
Wt::Json::Object broadcast{
{"type", "system"},
{"related-user", Wt::WString(toUserName)},
{"data", "unblocked"}
};
addMessageToSessionBroadcast(sessionId, broadcast);
}
size_t Broadcast::WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) {
size_t total_size = size * nmemb;
output->append(reinterpret_cast<char*>(contents), total_size);
return total_size;
}
Broadcast::Connection::Connection(const std::string &id, Client *client, const std::function<void ()> &fct):
sessionId_(id),
client_(client),
fct_(fct) {
}
Client *Broadcast::Connection::client() const {
return client_;
}
std::string Broadcast::Connection::sessionId() const {
return sessionId_;
}
std::string Broadcast::Connection::userName() const {
return client_->userName;
}
std::string Broadcast::Connection::country() const {
return client_->country;
}
int Broadcast::Connection::age() const {
return client_->age;
}
std::string Broadcast::Connection::gender() const {
return client_->gender;
}
void Broadcast::Connection::setClient(Client *client) {
client_ = client;
}
void Broadcast::Connection::addBroadcast(Wt::Json::Object broadcast) {
broadcastFifo.push_back(broadcast);
}
std::list<Wt::Json::Object> Broadcast::Connection::getBroadcasts(bool clear) {
auto broadcastCopy = broadcastFifo;
if (clear) {
broadcastFifo.clear();
}
return broadcastCopy;
}
bool Broadcast::Connection::setSessionId(std::string searchedSessionId, std::string newSessionId) {
if (searchedSessionId == sessionId_) {
sessionId_ = newSessionId;
return true;
}
return false;
}
std::function<void ()> Broadcast::Connection::fct() {
return fct_;
}
void Client::setActivity() {
lastActivity_ = Wt::WDateTime::currentDateTime();
}
bool Client::activitiesTimedOut() {
auto timeDifference = lastActivity_.secsTo(Wt::WDateTime::currentDateTime());
return timeDifference > timeoutSeconds;
}
int Client::currentlyLoggedInSeconds() {
auto currentDateTime = Wt::WDateTime::currentDateTime();
return loginTimeStamp_.secsTo(currentDateTime);
}
Wt::Json::Object Client::json() {
return Wt::Json::Object{
{"name", Wt::WString(userName)},
{"gender", Wt::WString(gender)},
{"isoCountryCode", Wt::WString(isoCountryCode)},
{"country", Wt::WString(country)},
{"age", age}
};
}
int Client::remainingSecondsToTimeout(){
auto timeDifference = lastActivity_.secsTo(Wt::WDateTime::currentDateTime());
return timeoutSeconds - timeDifference;
}
void Client::setLoggedIn() {
loginTimeStamp_ = Wt::WDateTime::currentDateTime();
lastActivity_ = loginTimeStamp_;
}
Broadcast::Message::Message(std::string fromSessionId_, Wt::WString message_):
fromSessionId(fromSessionId_),
sendType("text"),
message(message_) {
auto timestamp = std::time(nullptr);
auto combinedString = std::to_string(timestamp) + fromSessionId_;
Wt::Auth::SHA1HashFunction hashObj;
auto hash = hashObj.compute(combinedString, "salt");
messageId = Wt::WString(hash);
sendTime = Wt::WDateTime::currentDateTime();
}
Broadcast::Message::Message(std::string fromSessionId_, std::shared_ptr<Magick::Blob> imageBlob):
fromSessionId(fromSessionId_),
sendType("image") {
auto timestamp = std::time(nullptr);
auto combinedString = std::to_string(timestamp) + fromSessionId_;
Wt::Auth::SHA1HashFunction hashObj;
auto hash = hashObj.compute(combinedString, "salt");
messageId = Wt::WString(hash);
sendTime = Wt::WDateTime::currentDateTime();
Magick::Image image_;
image_.read(*imageBlob);
std::string imageData(reinterpret_cast<const char*>(imageBlob->data()), imageBlob->length());
std::string imageFormat = image_.magick();
image = {
{"width", Wt::Json::Value((int)image_.columns())},
{"height", Wt::Json::Value((int)image_.rows())},
{"imageblobbase64", Wt::Json::Value(Wt::Utils::base64Encode(imageData))},
{"type", Wt::Json::Value(imageFormat)}
};
}
Broadcast::Message::Message(Wt::WString message_):
systemMessage(true),
sendType("text"),
message (message_) {
auto timestamp = std::time(nullptr);
auto combinedString = std::to_string(timestamp) + "system09715";
Wt::Auth::SHA1HashFunction hashObj;
auto hash = hashObj.compute(combinedString, "salt");
messageId = Wt::WString(hash);
sendTime = Wt::WDateTime::currentDateTime();
}
Wt::Json::Object Broadcast::Message::json() {
auto json = Wt::Json::Object();
json["sender"] = Wt::Json::Value(fromSessionId);
json["type"] = Wt::Json::Value(sendType);
json["string"] = Wt::Json::Value(message);
json["image"] = Wt::Json::Value(image);
json["id"] = Wt::Json::Value(messageId);
json["timestamp"] = sendTime.toString();
return json;
}
void Broadcast::Message::setNewSesionId(std::string oldSessionId, std::string newSessionId) {
if (fromSessionId == oldSessionId) {
fromSessionId = newSessionId;
}
}
void Broadcast::MessageQueue::setNewSesionId(std::string oldSessionId, std::string newSessionId) {
for (auto &message: messages) {
message.setNewSesionId(oldSessionId, newSessionId);
}
}

View File

@@ -1,154 +0,0 @@
#ifndef BROADCAST_H
#define BROADCAST_H
#include <thread>
#include <mutex>
#include <functional>
#include <map>
#include <list>
#include <unordered_map>
#include <unordered_set>
#include <Wt/WServer.h>
#include <Wt/WDate.h>
#include <Wt/Json/Object.h>
namespace Magick {
class Image;
class Blob;
}
struct ConversationItem {
Wt::WDateTime timestamp;
bool selfSend;
Wt::WString text;
};
class Client {
public:
std::string gender;
std::string country;
std::string isoCountryCode;
std::string userName;
int age;
std::unordered_map<std::string, std::vector<ConversationItem> > conversations;
void setActivity();
bool activitiesTimedOut();
int currentlyLoggedInSeconds();
virtual void incomingBroadcast() { std::cout << "incoming" << std::endl;};
Wt::Json::Object json();
int remainingSecondsToTimeout();
protected:
void setLoggedIn();
void setLoggedOut();
private:
const int timeoutSeconds {1800};
Wt::WDateTime lastActivity_;
Wt::WDateTime loginTimeStamp_;
};
class Broadcast {
public:
struct Message {
Message()= default;
Message(std::string fromSessionId_, Wt::WString message_);
Message(std::string fromSessionId_, std::shared_ptr<Magick::Blob> image_);
Message(Wt::WString message_);
bool systemMessage{false};
std::string fromSessionId;
std::string sendType;
Wt::WDateTime sendTime;
Wt::WString message;
Wt::WString messageId;
Wt::Json::Object image;
Wt::Json::Object json();
void setNewSesionId(std::string oldSessionId, std::string newSessionId);
};
struct MessageQueue {
bool user1Read{false};
bool user2Read{false};
std::vector<Message> messages;
void setNewSesionId(std::string oldSessionId, std::string newSessionId);
};
Broadcast(Wt::WServer *server);
~Broadcast();
void connect(Client *client, const std::function<void ()> &fct);
void disconnect(Client *client);
void addToDisconnectList(Client *client);
Wt::Json::Object reSetUser(std::string oldSessionId, std::string newSessionId);
int count() const;
std::string userNameForSessionId(std::string sessionId);
std::string sessionIdForUserName(std::string userName);
Wt::Json::Object userForSessionId(std::string sessionId);
Wt::Json::Object userForUserName(std::string userName);
void changeClientForSessionId(std::string sessionId, Client *client);
bool nameIsFree(std::string userNameToCheck);
void downloadCountries();
std::map<Wt::WString, Wt::WString> countries();
Wt::WString getCountryIsoCodeByCountry(Wt::WString country);
std::list<Wt::Json::Object> getBroadcastsForSession(std::string sessionId);
Wt::Json::Object addMessage(std::string fromSessionId, std::string toUserName, Message message);
Wt::Json::Object addImage(std::string fromSessionId, std::string toUserName, std::shared_ptr<Magick::Blob> image);
void setConversationRead(std::string readingUserSessionId, std::string secondUserName);
void sendConversation(std::string sessionId1, std::string userName2);
void sendOpenConversations(std::string forSessionId);
void sendUserInformation(std::string sendToSessionId, std::string userName, std::string requestingUserName);
void toggleBlockUser(std::string blockingUserName, std::string blockedUser, std::string blockingUserSessionId);
void requestConversation(std::string sendToSessionId, std::string withUserName, std::string requestingUserName);
void userSearch(std::string toSession, std::string nameIncludes, int minAge, int maxAge,
std::unordered_set<std::string> countries, std::unordered_set<std::string> genders, std::string excludeName);
void sendHistory(std::string toSession);
protected:
struct Connection {
Connection(const std::string &id, Client *client, const std::function<void ()> &fct);
public:
Client *client() const;
std::string sessionId() const;
std::function<void ()> fct();
std::string userName() const;
std::string country() const;
int age() const;
std::string gender() const;
void setClient(Client *client);
void addBroadcast(Wt::Json::Object broadcast);
std::list<Wt::Json::Object> getBroadcasts(bool clear = false);
std::unordered_map<std::string, std::vector<std::string> > blockings_;
bool setSessionId(std::string searchedSessionId, std::string newSessionId);
private:
std::string sessionId_;
Client *client_;
std::function<void()> fct_;
int age_;
std::list<Wt::Json::Object> broadcastFifo;
};
private:
Wt::WServer *wtServer_;
bool stop_{false};
mutable std::mutex mutex_;
std::thread thread_;
bool stop;
std::vector<std::unique_ptr<Connection> > connections_;
Wt::WDate lastCountriesDownload_;
Wt::WString countriesDownloadUrl_{"https://pkgstore.datahub.io/core/country-list/data_csv/data/d7c9d7cfb42cb69f4422dec222dbbaa8/data_csv.csv"};
std::map<Wt::WString, Wt::WString> countriesMap_;
std::string responseData_;
Wt::WDateTime lastTimeoutCheck_;
std::unordered_map<std::string, MessageQueue> conversations_;
std::unordered_map<std::string, std::set<std::string> > blockings_;
std::vector<Client *> toDisconnect_;
void run();
Wt::Json::Object logoutBroadcast();
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output);
bool parseCountriesData();
void reSetSessionIdInMessages(std::string oldSessionId, std::string newSessionId);
Wt::Json::Object createUserList();
void sendMessageQueueToSession(std::string receiverSessionId, std::string user1, std::string user2, std::vector<Message> messages);
void addMessageToSessionBroadcast(std::string sessionId, Wt::Json::Object message);
void sendMessageCount(std::string toSessionId);
void sendBlockedMessage(std::string sessionId, std::string toUserName);
void sendBlockDone(std::string sessionId, std::string toUserName);
void sendUnblockDone(std::string sessionId, std::string toUserName);
void checkAndLogStart();
void logClientLogin(const Wt::Json::Object &clientJson);
};
#endif // BROADCAST_H

View File

@@ -1,23 +0,0 @@
#include "broadcast.h"
#include "app.h"
#include <Wt/WServer.h>
#include <Wt/WSound.h>
std::unique_ptr<Wt::WApplication> createApplication(const Wt::WEnvironment& env, Broadcast& server)
{
return std::make_unique<App>(env, server);
}
int main(int argc, char **argv) {
Wt::WServer server(argc, argv, WTHTTP_CONFIGURATION);
Broadcast chatServer(&server);
server.addEntryPoint(Wt::EntryPointType::Application,
std::bind(createApplication, std::placeholders::_1,
std::ref(chatServer)));
if (server.start()) {
int sig = Wt::WServer::waitForShutdown();
std::cerr << "Shutting down: (signal = " << sig << ")" << std::endl;
server.stop();
}
return 0;
}