PiPedal 1.2.33 initial commit
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
#include "JackServerSettings.hpp"
|
||||
#include <thread>
|
||||
#include "RtInversionGuard.hpp"
|
||||
#include "PiPedalException.hpp"
|
||||
|
||||
#include "CpuUse.hpp"
|
||||
|
||||
|
||||
+13
-1
@@ -33,6 +33,10 @@ namespace pipedal
|
||||
{
|
||||
|
||||
public:
|
||||
AutoLilvNode(const AutoLilvNode&) = delete;
|
||||
AutoLilvNode&operator=(const AutoLilvNode&) = delete;
|
||||
AutoLilvNode(AutoLilvNode&&) = default;
|
||||
|
||||
AutoLilvNode()
|
||||
{
|
||||
}
|
||||
@@ -89,6 +93,7 @@ namespace pipedal
|
||||
AutoLilvNode &operator=(AutoLilvNode &&other)
|
||||
{
|
||||
std::swap(this->node, other.node);
|
||||
std::swap(this->isConst,other.isConst);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -101,7 +106,14 @@ namespace pipedal
|
||||
void Free()
|
||||
{
|
||||
if (node != nullptr && !isConst)
|
||||
lilv_node_free(node);
|
||||
{
|
||||
try {
|
||||
lilv_node_free(node);
|
||||
} catch (const std::exception&)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
node = nullptr;
|
||||
}
|
||||
|
||||
|
||||
+35
-84
@@ -46,22 +46,6 @@ endif()
|
||||
|
||||
|
||||
|
||||
# Can't get the pkg_check to work.
|
||||
# pkg_check_modules(LIBNL3 "nl-genl-3")
|
||||
# if(!LIBNL3_FOUND)
|
||||
# message(ERROR "libnl-genl-3-dev package not installed.")
|
||||
# else()
|
||||
# message(STATUS "LIBNL3 libraries: ${LIBNL3_LIBRARIES}")
|
||||
# message(STATUS "LBNL3 includes: ${LIBNL3_INCLUDE_DIRS}")
|
||||
# endif()
|
||||
|
||||
# nlgenl-3 library.
|
||||
execute_process(COMMAND ls /usr/include/libnl3/netlink/netlink.h RESULT_VARIABLE LNL3_MISSING OUTPUT_QUIET ERROR_QUIET)
|
||||
if(LNL3_MISSING)
|
||||
message(ERROR " Need to: sudo apt install libnl-3-dev libnl-genl-3-dev ")
|
||||
endif()
|
||||
set(LIBNL3_INCLUDE_DIRS /usr/include/libnl3)
|
||||
set(LIBNL3_LIBRARIES nl-3 nl-genl-3)
|
||||
|
||||
|
||||
pkg_check_modules(SYSTEMD "systemd")
|
||||
@@ -81,8 +65,14 @@ endif()
|
||||
|
||||
# Use LV2 headers from the /usr/lib directory.
|
||||
#set (LV2DEV_INCLUDE_DIRS /usr/lib)
|
||||
set (LV2DEV_INCLUDE_DIRS )
|
||||
set (LV2DEV_INCLUDE_DIRS ) # use lvt headers fro /usr/include
|
||||
|
||||
|
||||
# Use submodules/websocketpp heders taken from websocketpp/develop branch, because debian-provided
|
||||
# version of websocketpp cannot compile under GCC 12.x.
|
||||
|
||||
set (WEBSOCKETPP_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/modules/websocketpp)
|
||||
message(STATUS "WEBSOCKETPP_INCLUDE_DIRS ${WEBSOCKETPP_INCLUDE_DIRS}" )
|
||||
pkg_check_modules(JACK "jack")
|
||||
if(!JACK_FOUND)
|
||||
message(ERROR "jack package not installed.")
|
||||
@@ -148,8 +138,8 @@ set (PIPEDAL_SOURCES
|
||||
inverting_mutex.hpp
|
||||
DbDezipper.hpp DbDezipper.cpp
|
||||
WebServerLog.hpp
|
||||
WifiChannelSelectors.cpp WifiChannelSelectors.hpp
|
||||
|
||||
util.hpp util.cpp
|
||||
PiPedalUI.hpp
|
||||
PiPedalUI.cpp
|
||||
MapPathFeature.hpp
|
||||
@@ -165,25 +155,15 @@ set (PIPEDAL_SOURCES
|
||||
AutoLilvNode.cpp
|
||||
RtInversionGuard.hpp
|
||||
CpuUse.hpp CpuUse.cpp
|
||||
P2pConfigFiles.hpp
|
||||
AvahiService.cpp AvahiService.hpp
|
||||
ServiceConfiguration.cpp ServiceConfiguration.hpp
|
||||
WriteTemplateFile.cpp WriteTemplateFile.hpp
|
||||
ConfigUtil.hpp ConfigUtil.cpp
|
||||
ConfigSerializer.h ConfigSerializer.cpp
|
||||
StdErrorCapture.hpp StdErrorCapture.cpp
|
||||
Ipv6Helpers.cpp Ipv6Helpers.hpp
|
||||
PluginPreset.cpp PluginPreset.hpp
|
||||
CpuGovernor.cpp CpuGovernor.hpp
|
||||
GovernorSettings.cpp GovernorSettings.hpp
|
||||
SysExec.cpp SysExec.hpp
|
||||
WebServer.cpp WebServer.hpp HtmlHelper.cpp HtmlHelper.hpp pch.h Uri.cpp Uri.hpp
|
||||
WifiConfigSettings.hpp WifiConfigSettings.cpp
|
||||
WifiDirectConfigSettings.hpp WifiDirectConfigSettings.cpp
|
||||
ConfigUtil.hpp ConfigUtil.cpp
|
||||
WebServer.cpp WebServer.hpp pch.h Uri.cpp Uri.hpp
|
||||
|
||||
RequestHandler.hpp json.cpp json.hpp
|
||||
json_variant.hpp json_variant.cpp
|
||||
RequestHandler.hpp
|
||||
Scratch.cpp PluginHost.hpp PluginHost.cpp
|
||||
PluginType.hpp PluginType.cpp
|
||||
Lv2Log.hpp Lv2Log.cpp
|
||||
@@ -220,9 +200,6 @@ set (PIPEDAL_SOURCES
|
||||
Locale.hpp Locale.cpp
|
||||
Lv2EventBufferWriter.hpp Lv2EventBufferWriter.cpp
|
||||
IpSubnet.hpp
|
||||
WifiChannels.hpp
|
||||
WifiChannels.cpp
|
||||
RegDb.cpp RegDb.hpp
|
||||
PiPedalAlsa.hpp PiPedalAlsa.cpp
|
||||
InheritPriorityMutex.hpp InheritPriorityMutex.cpp
|
||||
UnixSocket.cpp UnixSocket.hpp
|
||||
@@ -237,12 +214,16 @@ set (PIPEDAL_SOURCES
|
||||
|
||||
|
||||
configure_file(config.hpp.in config.hpp)
|
||||
include_directories( ${pipedald_SOURCE_DIR}/. ../build/src)
|
||||
include_directories(
|
||||
${pipedald_SOURCE_DIR}/.
|
||||
../build/src
|
||||
)
|
||||
|
||||
set (PIPEDAL_INCLUDES
|
||||
${LV2DEV_INCLUDE_DIRS}
|
||||
${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS}
|
||||
${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS}
|
||||
${VST3_INCLUDES}
|
||||
${WEBSOCKETPP_INCLUDE_DIRS}
|
||||
.
|
||||
)
|
||||
|
||||
@@ -251,7 +232,6 @@ set(PIPEDAL_LIBS libpipedald
|
||||
${VST3_LIBRARIES}
|
||||
${LILV_0_LIBRARIES}
|
||||
# ${JACK_LIBRARIES} - pending delete for JACK support.
|
||||
${LIBNL3_LIBRARIES}
|
||||
)
|
||||
|
||||
|
||||
@@ -263,6 +243,8 @@ add_library(libpipedald STATIC ${PIPEDAL_SOURCES})
|
||||
target_include_directories(libpipedald PRIVATE ${PIPEDAL_INCLUDES}
|
||||
)
|
||||
|
||||
target_link_libraries(libpipedald PUBLIC PiPedalCommon)
|
||||
|
||||
if(${USE_PCH})
|
||||
target_precompile_headers(libpipedald PRIVATE pch.h)
|
||||
endif()
|
||||
@@ -275,7 +257,7 @@ add_executable(pipedald
|
||||
)
|
||||
target_include_directories(pipedald PRIVATE ${PIPEDAL_INCLUDES})
|
||||
|
||||
target_link_libraries(pipedald PRIVATE
|
||||
target_link_libraries(pipedald PRIVATE PiPedalCommon
|
||||
|
||||
${PIPEDAL_LIBS}
|
||||
)
|
||||
@@ -285,12 +267,8 @@ add_executable(pipedaltest testMain.cpp
|
||||
|
||||
InvertingMutexTest.cpp
|
||||
jsonTest.cpp
|
||||
json_variant.cpp
|
||||
json_variant.hpp
|
||||
|
||||
utilTest.cpp
|
||||
util.cpp
|
||||
util.hpp
|
||||
|
||||
AlsaDriverTest.cpp
|
||||
AvahiServiceTest.cpp
|
||||
@@ -307,21 +285,15 @@ add_executable(pipedaltest testMain.cpp
|
||||
MemDebug.cpp
|
||||
MemDebug.hpp
|
||||
)
|
||||
|
||||
target_link_libraries(pipedaltest PRIVATE PiPedalCommon)
|
||||
target_include_directories(pipedaltest PRIVATE ${PIPEDAL_INCLUDES}
|
||||
)
|
||||
|
||||
add_executable(jsonTest
|
||||
testMain.cpp
|
||||
jsonTest.cpp
|
||||
json.hpp
|
||||
json.cpp
|
||||
json_variant.cpp
|
||||
json_variant.hpp
|
||||
MapFeature.cpp
|
||||
MapFeature.hpp
|
||||
HtmlHelper.cpp
|
||||
HtmlHelper.hpp
|
||||
AtomConverter.hpp
|
||||
AtomConverter.cpp
|
||||
AtomConverterTest.cpp
|
||||
@@ -329,6 +301,7 @@ add_executable(jsonTest
|
||||
Promise.hpp
|
||||
PromiseTest.cpp
|
||||
)
|
||||
target_link_libraries(jsonTest PRIVATE PiPedalCommon)
|
||||
target_include_directories(jsonTest PRIVATE ${PIPEDAL_INCLUDES}
|
||||
)
|
||||
|
||||
@@ -575,40 +548,26 @@ add_test(NAME DevTest COMMAND pipedaltest "[Dev]")
|
||||
|
||||
add_executable(pipedalconfig
|
||||
PrettyPrinter.hpp
|
||||
ServiceConfiguration.cpp ServiceConfiguration.hpp
|
||||
json.cpp json.hpp
|
||||
HtmlHelper.cpp HtmlHelper.hpp
|
||||
SetWifiConfig.cpp SetWifiConfig.hpp
|
||||
CommandLineParser.hpp
|
||||
WriteTemplateFile.hpp WriteTemplateFile.cpp
|
||||
PiPedalException.hpp
|
||||
ConfigSerializer.h ConfigSerializer.cpp
|
||||
ConfigMain.cpp
|
||||
WifiConfigSettings.cpp WifiConfigSettings.hpp
|
||||
WifiDirectConfigSettings.cpp WifiDirectConfigSettings.hpp
|
||||
ConfigUtil.hpp ConfigUtil.cpp
|
||||
JackServerSettings.hpp JackServerSettings.cpp
|
||||
SetWifiConfig.hpp SetWifiConfig.cpp
|
||||
SystemConfigFile.hpp SystemConfigFile.cpp
|
||||
WriteTemplateFile.hpp WriteTemplateFile.cpp
|
||||
SysExec.hpp SysExec.cpp
|
||||
WifiChannelSelectors.cpp WifiChannelSelectors.hpp
|
||||
asan_options.cpp
|
||||
Lv2Log.cpp Lv2Log.hpp
|
||||
P2pConfigFiles.hpp
|
||||
|
||||
)
|
||||
|
||||
target_link_libraries(pipedalconfig PRIVATE pthread atomic uuid stdc++fs
|
||||
target_link_libraries(pipedalconfig PRIVATE PiPedalCommon pthread atomic uuid stdc++fs
|
||||
)
|
||||
|
||||
add_executable(pipedal_latency_test
|
||||
util.hpp
|
||||
util.cpp
|
||||
PrettyPrinter.hpp
|
||||
CommandLineParser.hpp
|
||||
PiLatencyMain.cpp
|
||||
PiPedalAlsa.hpp PiPedalAlsa.cpp
|
||||
json.hpp json.cpp
|
||||
HtmlHelper.cpp HtmlHelper.hpp
|
||||
asan_options.cpp
|
||||
Lv2Log.cpp Lv2Log.hpp
|
||||
AlsaDriver.cpp AlsaDriver.hpp
|
||||
@@ -616,11 +575,9 @@ add_executable(pipedal_latency_test
|
||||
JackServerSettings.hpp JackServerSettings.cpp
|
||||
CpuUse.hpp
|
||||
CpuUse.cpp
|
||||
|
||||
|
||||
)
|
||||
|
||||
target_link_libraries(pipedal_latency_test PRIVATE pthread asound
|
||||
target_link_libraries(pipedal_latency_test PRIVATE pthread asound PiPedalCommon
|
||||
)
|
||||
|
||||
|
||||
@@ -639,34 +596,25 @@ target_link_libraries(capturepresets ${PIPEDAL_LIBS})
|
||||
|
||||
add_executable(pipedaladmind AdminMain.cpp CommandLineParser.hpp
|
||||
UnixSocket.cpp UnixSocket.hpp
|
||||
ServiceConfiguration.cpp ServiceConfiguration.hpp
|
||||
ConfigSerializer.h ConfigSerializer.cpp
|
||||
autoptr_vector.h
|
||||
|
||||
SetWifiConfig.cpp SetWifiConfig.hpp
|
||||
|
||||
JackServerSettings.hpp JackServerSettings.cpp
|
||||
json.hpp json.cpp HtmlHelper.hpp HtmlHelper.cpp Lv2Log.hpp Lv2Log.cpp
|
||||
Lv2SystemdLogger.hpp Lv2SystemdLogger.cpp
|
||||
WifiConfigSettings.cpp WifiConfigSettings.hpp
|
||||
WriteTemplateFile.cpp WriteTemplateFile.hpp
|
||||
WifiDirectConfigSettings.cpp WifiDirectConfigSettings.hpp
|
||||
ConfigUtil.hpp ConfigUtil.cpp
|
||||
|
||||
SetWifiConfig.hpp SetWifiConfig.cpp
|
||||
Lv2Log.cpp Lv2Log.hpp
|
||||
SystemConfigFile.hpp SystemConfigFile.cpp
|
||||
SysExec.hpp SysExec.cpp
|
||||
CpuGovernor.cpp CpuGovernor.hpp
|
||||
asan_options.cpp
|
||||
P2pConfigFiles.hpp
|
||||
|
||||
)
|
||||
target_link_libraries(pipedaladmind PRIVATE pthread atomic stdc++fs systemd)
|
||||
target_link_libraries(pipedaladmind PRIVATE PiPedalCommon pthread atomic stdc++fs systemd )
|
||||
|
||||
add_executable(processcopyrights copyrightMain.cpp
|
||||
CommandLineParser.hpp
|
||||
PiPedalException.hpp
|
||||
HtmlHelper.hpp HtmlHelper.cpp
|
||||
asan_options.cpp )
|
||||
target_link_libraries(processcopyrights PRIVATE stdc++fs)
|
||||
target_link_libraries(processcopyrights PRIVATE PiPedalCommon stdc++fs)
|
||||
|
||||
set (REACT_BUILD_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../build/react/build/)
|
||||
set (REACT_NOTICES_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../build/src/notices.txt)
|
||||
@@ -690,7 +638,7 @@ add_custom_command(OUTPUT ${REACT_NOTICES_FILE}
|
||||
COMMAND "$<TARGET_FILE:processcopyrights>"
|
||||
--output ${REACT_NOTICES_FILE}
|
||||
--projectCopyright ${DEBIAN_COPYRIGHT_FILE}
|
||||
liblilv-0-0 ${BOOST_COPYRIGHT_DIR} libnl-3-dev libnl-3-dev lv2-dev
|
||||
liblilv-0-0 ${BOOST_COPYRIGHT_DIR} lv2-dev libsdbus-c++-dev librsvg2-2 libpango-1.0-0 libx11-6 libxrandr2
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${DEBIAN_COPYRIGHT_FILE} "$<TARGET_FILE:processcopyrights>"
|
||||
COMMENT "Updating copyright notices."
|
||||
@@ -702,7 +650,10 @@ add_custom_target (
|
||||
)
|
||||
|
||||
|
||||
install (TARGETS pipedalconfig pipedald pipedaladmind pipedal_latency_test DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
|
||||
install (TARGETS pipedalconfig pipedal_latency_test DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
|
||||
EXPORT pipedalTargets)
|
||||
|
||||
install (TARGETS pipedald pipedaladmind DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin
|
||||
EXPORT pipedalSbinTargets)
|
||||
|
||||
|
||||
|
||||
+359
-261
@@ -21,7 +21,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include "CommandLineParser.hpp"
|
||||
#include "PiPedalException.hpp"
|
||||
#include <filesystem>
|
||||
#include <stdlib.h>
|
||||
#include "WriteTemplateFile.hpp"
|
||||
@@ -37,7 +36,7 @@
|
||||
#include <uuid/uuid.h>
|
||||
#include <random>
|
||||
#include "AudioConfig.hpp"
|
||||
|
||||
#include "WifiChannelSelectors.hpp"
|
||||
|
||||
#if JACK_HOST
|
||||
#define INSTALL_JACK_SERVICE 1
|
||||
@@ -47,7 +46,6 @@
|
||||
|
||||
#define UNINSTALL_JACK_SERVICE 1
|
||||
|
||||
|
||||
#define SS(x) (((std::stringstream &)(std::stringstream() << x)).str())
|
||||
|
||||
#define TEMPLATE_PATH(filename) ("/etc/pipedal/config/templates/" filename ".service")
|
||||
@@ -70,9 +68,11 @@ using namespace pipedal;
|
||||
#define CHMOD_BIN "/usr/bin/chmod"
|
||||
|
||||
#define SERVICE_PATH "/usr/lib/systemd/system"
|
||||
#define NATIVE_SERVICE "pipedald"
|
||||
#define PIPEDALD_SERVICE "pipedald"
|
||||
#define ADMIN_SERVICE "pipedaladmind"
|
||||
#define PIPEDAL_P2PD_SERVICE "pipedal_p2pd"
|
||||
#define PIPEDAL_NM_P2PD_SERVICE "pipedal_nm_p2pd"
|
||||
#define NETWORK_MANAGER_SERIVCE "NetworkManager"
|
||||
#define JACK_SERVICE "jack"
|
||||
|
||||
#define REMOVE_OLD_SERVICE 0 // Grandfathering: whether to remove the old shutdown service (now pipedaladmind)
|
||||
@@ -105,14 +105,14 @@ std::filesystem::path findOnPath(const std::string &command)
|
||||
}
|
||||
std::stringstream s;
|
||||
s << "'" << command << "' is not installed.";
|
||||
throw PiPedalException(s.str());
|
||||
throw std::runtime_error(s.str());
|
||||
}
|
||||
|
||||
void EnableService()
|
||||
{
|
||||
if (sysExec(SYSTEMCTL_BIN " enable " NATIVE_SERVICE ".service") != EXIT_SUCCESS)
|
||||
if (sysExec(SYSTEMCTL_BIN " enable " PIPEDALD_SERVICE ".service") != EXIT_SUCCESS)
|
||||
{
|
||||
cout << "Error: Failed to enable the " NATIVE_SERVICE " service.\n";
|
||||
cout << "Error: Failed to enable the " PIPEDALD_SERVICE " service.\n";
|
||||
}
|
||||
if (sysExec(SYSTEMCTL_BIN " enable " ADMIN_SERVICE ".service") != EXIT_SUCCESS)
|
||||
{
|
||||
@@ -121,17 +121,17 @@ void EnableService()
|
||||
}
|
||||
void DisableService()
|
||||
{
|
||||
#if INSTALL_JACK_SERVICE || UNINSTALL_JACK_SERVICE
|
||||
#if INSTALL_JACK_SERVICE || UNINSTALL_JACK_SERVICE
|
||||
if (silentSysExec(SYSTEMCTL_BIN " disable " JACK_SERVICE ".service") != EXIT_SUCCESS)
|
||||
{
|
||||
#if INSTALL_JACK_SERVICE
|
||||
// cout << "Error: Failed to disable the " JACK_SERVICE " service.";
|
||||
#endif
|
||||
#if INSTALL_JACK_SERVICE
|
||||
// cout << "Error: Failed to disable the " JACK_SERVICE " service.";
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (sysExec(SYSTEMCTL_BIN " disable " NATIVE_SERVICE ".service") != EXIT_SUCCESS)
|
||||
#endif
|
||||
if (sysExec(SYSTEMCTL_BIN " disable " PIPEDALD_SERVICE ".service") != EXIT_SUCCESS)
|
||||
{
|
||||
cout << "Error: Failed to disable the " NATIVE_SERVICE " service.\n";
|
||||
cout << "Error: Failed to disable the " PIPEDALD_SERVICE " service.\n";
|
||||
}
|
||||
if (sysExec(SYSTEMCTL_BIN " disable " ADMIN_SERVICE ".service") != EXIT_SUCCESS)
|
||||
{
|
||||
@@ -147,9 +147,9 @@ void DisableService()
|
||||
|
||||
void StopService(bool excludeShutdownService = false)
|
||||
{
|
||||
if (sysExec(SYSTEMCTL_BIN " stop " NATIVE_SERVICE ".service") != EXIT_SUCCESS)
|
||||
if (sysExec(SYSTEMCTL_BIN " stop " PIPEDALD_SERVICE ".service") != EXIT_SUCCESS)
|
||||
{
|
||||
cout << "Error: Failed to stop the " NATIVE_SERVICE " service.\n";
|
||||
cout << "Error: Failed to stop the " PIPEDALD_SERVICE " service.\n";
|
||||
}
|
||||
if (!excludeShutdownService)
|
||||
{
|
||||
@@ -167,9 +167,9 @@ void StopService(bool excludeShutdownService = false)
|
||||
#if INSTALL_JACK_SERVICE || UNINSTALL_JACK_SERVICE
|
||||
if (silentSysExec(SYSTEMCTL_BIN " stop " JACK_SERVICE ".service") != EXIT_SUCCESS)
|
||||
{
|
||||
#if INSTALL_JACK_SERVICE
|
||||
// cout << PiPedalException("Failed to stop the " JACK_SERVICE " service.");
|
||||
#endif
|
||||
#if INSTALL_JACK_SERVICE
|
||||
// cout << std::runtime_error("Failed to stop the " JACK_SERVICE " service.");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -182,12 +182,12 @@ void StartService(bool excludeShutdownService = false)
|
||||
{
|
||||
if (sysExec(SYSTEMCTL_BIN " start " ADMIN_SERVICE ".service") != EXIT_SUCCESS)
|
||||
{
|
||||
throw PiPedalException("Failed to start the " ADMIN_SERVICE " service.");
|
||||
throw std::runtime_error("Failed to start the " ADMIN_SERVICE " service.");
|
||||
}
|
||||
}
|
||||
if (sysExec(SYSTEMCTL_BIN " start " NATIVE_SERVICE ".service") != EXIT_SUCCESS)
|
||||
if (sysExec(SYSTEMCTL_BIN " start " PIPEDALD_SERVICE ".service") != EXIT_SUCCESS)
|
||||
{
|
||||
throw PiPedalException("Failed to start the " NATIVE_SERVICE " service.");
|
||||
throw std::runtime_error("Failed to start the " PIPEDALD_SERVICE " service.");
|
||||
}
|
||||
#if INSTALL_JACK_SERVICE
|
||||
JackServerSettings serverSettings;
|
||||
@@ -196,7 +196,7 @@ void StartService(bool excludeShutdownService = false)
|
||||
{
|
||||
if (sysExec(SYSTEMCTL_BIN " start " JACK_SERVICE ".service") != EXIT_SUCCESS)
|
||||
{
|
||||
throw PiPedalException("Failed to start the " JACK_SERVICE " service.");
|
||||
throw std::runtime_error("Failed to start the " JACK_SERVICE " service.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -246,7 +246,7 @@ static void RemoveLine(const std::string &path, const std::string lineToRemove)
|
||||
ifstream f(path);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw PiPedalException(SS("Can't open " << path));
|
||||
throw std::runtime_error(SS("Can't open " << path));
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
@@ -265,7 +265,7 @@ static void RemoveLine(const std::string &path, const std::string lineToRemove)
|
||||
std::ofstream f(path);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw PiPedalException(SS("Can't write to " << path));
|
||||
throw std::runtime_error(SS("Can't write to " << path));
|
||||
}
|
||||
for (auto &line : lines)
|
||||
{
|
||||
@@ -303,7 +303,7 @@ void InstallPamEnv()
|
||||
ifstream f(path);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw PiPedalException(SS("Can't open " << path));
|
||||
throw std::runtime_error(SS("Can't open " << path));
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
@@ -328,7 +328,7 @@ void InstallPamEnv()
|
||||
std::ofstream f(path);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw PiPedalException(SS("Can't write to " << path));
|
||||
throw std::runtime_error(SS("Can't write to " << path));
|
||||
}
|
||||
for (auto &line : lines)
|
||||
{
|
||||
@@ -348,7 +348,7 @@ void InstallLimits()
|
||||
{
|
||||
if (sysExec(GROUPADD_BIN " -f " AUDIO_SERVICE_GROUP_NAME) != EXIT_SUCCESS)
|
||||
{
|
||||
throw PiPedalException("Failed to create audio service group.");
|
||||
throw std::runtime_error("Failed to create audio service group.");
|
||||
}
|
||||
|
||||
std::filesystem::path limitsConfig = "/etc/security/limits.d/audio.conf";
|
||||
@@ -365,7 +365,6 @@ void InstallLimits()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if JACK_HOST
|
||||
void MaybeStartJackService()
|
||||
{
|
||||
@@ -390,13 +389,13 @@ void InstallAudioService()
|
||||
#if INSTALL_JACK_SERVICE
|
||||
if (sysExec(GROUPADD_BIN " -f " JACK_SERVICE_GROUP_NAME) != EXIT_SUCCESS)
|
||||
{
|
||||
throw PiPedalException("Failed to create jack service group.");
|
||||
throw std::runtime_error("Failed to create jack service group.");
|
||||
}
|
||||
if (!userExists(JACK_SERVICE_ACCOUNT_NAME))
|
||||
{
|
||||
if (sysExec(USERADD_BIN " " JACK_SERVICE_ACCOUNT_NAME " -g " JACK_SERVICE_GROUP_NAME " -m -N -r") != EXIT_SUCCESS)
|
||||
{
|
||||
// throw PiPedalException("Failed to create service account.");
|
||||
// throw std::runtime_error("Failed to create service account.");
|
||||
}
|
||||
// lock account for login.
|
||||
silentSysExec("passwd -l " JACK_SERVICE_ACCOUNT_NAME);
|
||||
@@ -417,18 +416,32 @@ void InstallAudioService()
|
||||
|
||||
MaybeStartJackService();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int SudoExec(char **argv)
|
||||
int SudoExec(int argc, char**argv)
|
||||
{
|
||||
// re-execute with SUDO in order to prompt for SUDO credentials once only.
|
||||
std::vector<char *> args;
|
||||
std::string pkexec = "/usr/bin/sudo"; // staged because "ISO C++ forbids converting a string constant to std::vector<char*>::value_type"(!)
|
||||
args.push_back((char *)(pkexec.c_str()));
|
||||
|
||||
std::string sPath = getSelfExePath();
|
||||
args.push_back(const_cast<char *>(sPath.c_str()));
|
||||
for (int arg = 1; arg < argc; ++arg)
|
||||
{
|
||||
args.push_back(const_cast<char *>(argv[arg]));
|
||||
}
|
||||
|
||||
args.push_back(nullptr);
|
||||
|
||||
char **newArgs = &args[0];
|
||||
|
||||
int pbPid;
|
||||
int returnValue = 0;
|
||||
|
||||
if ((pbPid = fork()) == 0)
|
||||
{
|
||||
execv(argv[0], argv);
|
||||
exit(-1);
|
||||
return execv(newArgs[0], newArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -445,38 +458,36 @@ static bool IsP2pServiceEnabled()
|
||||
return settings.enable_;
|
||||
}
|
||||
|
||||
static void RestartP2pdService()
|
||||
{
|
||||
if (IsP2pServiceEnabled())
|
||||
{
|
||||
silentSysExec(SYSTEMCTL_BIN " restart " PIPEDAL_P2PD_SERVICE);
|
||||
}
|
||||
}
|
||||
|
||||
static void UninstallP2pdService()
|
||||
{
|
||||
silentSysExec("systemctl stop " PIPEDAL_P2PD_SERVICE);
|
||||
silentSysExec("systemctl disable " PIPEDAL_P2PD_SERVICE);
|
||||
}
|
||||
|
||||
|
||||
void Uninstall()
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsP2pServiceEnabled())
|
||||
{
|
||||
WifiDirectConfigSettings settings;
|
||||
settings.Load();
|
||||
settings.enable_ = false;
|
||||
settings.Save();
|
||||
SetWifiDirectConfig(settings);
|
||||
}
|
||||
}
|
||||
catch (const std::exception e)
|
||||
{
|
||||
std::cout << "Error: Unable to disable the Wiifi P2P service. " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
OnWifiUninstall();
|
||||
|
||||
StopService();
|
||||
DisableService();
|
||||
UninstallP2pdService();
|
||||
|
||||
#if UNINSTALL_JACK_SERVICE
|
||||
silentSysExec(SYSTEMCTL_BIN " stop jack");
|
||||
silentSysExec(SYSTEMCTL_BIN " disable jack");
|
||||
#endif
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
std::filesystem::remove("/usr/bin/systemd/system/" OLD_SHUTDOWN_SERVICE ".service");
|
||||
@@ -493,6 +504,13 @@ void Uninstall()
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
std::filesystem::remove("/usr/bin/systemd/system/" PIPEDAL_NM_P2PD_SERVICE ".service");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
std::filesystem::remove("/usr/bin/systemd/system/" PIPEDAL_P2PD_SERVICE ".service");
|
||||
}
|
||||
@@ -502,7 +520,7 @@ void Uninstall()
|
||||
|
||||
try
|
||||
{
|
||||
std::filesystem::remove("/usr/bin/systemd/system/" NATIVE_SERVICE ".service");
|
||||
std::filesystem::remove("/usr/bin/systemd/system/" PIPEDALD_SERVICE ".service");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -521,12 +539,12 @@ void Uninstall()
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
// We should NEVER get here. But the consequences of failure are high (causes permanent apt installs/uninstall problems), so be safe.
|
||||
// We should NEVER get here. But the consequences of failure are high (causes permanent apt installs/uninstall problems), so be safe.
|
||||
cout << "ERROR: Unexpected error while uninstalling pipedal. (" << e.what() << ")" << endl;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// We should NEVER get here. But the consequences of failure are high (causes permanent apt installs/uninstall problems), so be safe.
|
||||
// We should NEVER get here. But the consequences of failure are high (causes permanent apt installs/uninstall problems), so be safe.
|
||||
cout << "ERROR: Unexpected error while uninstalling pipedal." << endl;
|
||||
}
|
||||
}
|
||||
@@ -577,15 +595,14 @@ static std::string RandomChars(int nchars)
|
||||
static void PrepareServiceConfigurationFile(uint16_t portNumber)
|
||||
{
|
||||
ServiceConfiguration serviceConfiguration;
|
||||
try {
|
||||
serviceConfiguration.Load();
|
||||
} catch (const std::exception &)
|
||||
try
|
||||
{
|
||||
|
||||
serviceConfiguration.Load();
|
||||
}
|
||||
if (serviceConfiguration.deviceName == "" || serviceConfiguration.uuid == ""
|
||||
|| portNumber != serviceConfiguration.server_port
|
||||
)
|
||||
catch (const std::exception &)
|
||||
{
|
||||
}
|
||||
if (serviceConfiguration.deviceName == "" || serviceConfiguration.uuid == "" || portNumber != serviceConfiguration.server_port)
|
||||
{
|
||||
if (serviceConfiguration.deviceName == "")
|
||||
{
|
||||
@@ -600,190 +617,230 @@ static void PrepareServiceConfigurationFile(uint16_t portNumber)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CopyWpaSupplicantConfigFile()
|
||||
{
|
||||
try {
|
||||
const char NM_SUPPLICANT_CONFIG_PATH[] = "/etc/pipedal/config/wpa_supplicant/wpa_supplicant-pipedal.conf";
|
||||
const char NM_SUPPLICANT_CONFIG_SOURCE_PATH[] = "/etc/pipedal/config/templates/wpa_supplicant-pipedal.conf";
|
||||
std::filesystem::path destinationPath = NM_SUPPLICANT_CONFIG_PATH;
|
||||
std::filesystem::path sourcePath = NM_SUPPLICANT_CONFIG_SOURCE_PATH;
|
||||
|
||||
std::filesystem::create_directories(destinationPath.parent_path());
|
||||
std::filesystem::copy_file(sourcePath,destinationPath,std::filesystem::copy_options::overwrite_existing);
|
||||
} catch (const std::exception&e)
|
||||
{
|
||||
cout << "ERROR: " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
void Install(const std::filesystem::path &programPrefix, const std::string endpointAddress)
|
||||
{
|
||||
if (sysExec(GROUPADD_BIN " -f " AUDIO_SERVICE_GROUP_NAME) != EXIT_SUCCESS)
|
||||
{
|
||||
throw PiPedalException("Failed to create audio service group.");
|
||||
}
|
||||
if (sysExec(GROUPADD_BIN " -f " SERVICE_GROUP_NAME) != EXIT_SUCCESS)
|
||||
{
|
||||
throw PiPedalException("Failed to create pipedald service group.");
|
||||
}
|
||||
|
||||
InstallAudioService();
|
||||
auto endpos = endpointAddress.find_last_of(':');
|
||||
if (endpos == string::npos)
|
||||
{
|
||||
throw PiPedalException("Invalid endpoint address: " + endpointAddress);
|
||||
}
|
||||
uint16_t port;
|
||||
auto strPort = endpointAddress.substr(endpos + 1);
|
||||
cout << "Configuring pipedal" << endl;
|
||||
try
|
||||
{
|
||||
auto lport = std::stoul(strPort);
|
||||
if (lport == 0 || lport >= std::numeric_limits<uint16_t>::max())
|
||||
if (sysExec(GROUPADD_BIN " -f " AUDIO_SERVICE_GROUP_NAME) != EXIT_SUCCESS)
|
||||
{
|
||||
throw PiPedalException("out of range.");
|
||||
throw std::runtime_error("Failed to create audio service group.");
|
||||
}
|
||||
port = (uint16_t)lport;
|
||||
std::stringstream s;
|
||||
s << port;
|
||||
strPort = s.str(); // normalized.
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "Invalid port number: " << strPort;
|
||||
throw PiPedalException(s.str());
|
||||
}
|
||||
PrepareServiceConfigurationFile(port);
|
||||
|
||||
|
||||
|
||||
bool authBindRequired = port < 512;
|
||||
|
||||
// Create and configure service account.
|
||||
|
||||
if (sysExec(GROUPADD_BIN " -f " SERVICE_GROUP_NAME) != EXIT_SUCCESS)
|
||||
{
|
||||
throw PiPedalException("Failed to create service group.");
|
||||
}
|
||||
|
||||
if (!userExists(SERVICE_ACCOUNT_NAME))
|
||||
{
|
||||
if (sysExec(USERADD_BIN " " SERVICE_ACCOUNT_NAME " -g " SERVICE_GROUP_NAME " -m --home /var/pipedal/home -N -r") != EXIT_SUCCESS)
|
||||
if (sysExec(GROUPADD_BIN " -f " SERVICE_GROUP_NAME) != EXIT_SUCCESS)
|
||||
{
|
||||
// throw PiPedalException("Failed to create service account.");
|
||||
throw std::runtime_error("Failed to create pipedald service group.");
|
||||
}
|
||||
// lock account for login.
|
||||
silentSysExec("passwd -l " SERVICE_ACCOUNT_NAME);
|
||||
}
|
||||
|
||||
// Add to audio groups.
|
||||
sysExec(USERMOD_BIN " -a -G " AUDIO_SERVICE_GROUP_NAME " " SERVICE_ACCOUNT_NAME);
|
||||
|
||||
// create and configure /var directory.
|
||||
|
||||
std::filesystem::path varDirectory("/var/pipedal");
|
||||
std::filesystem::create_directory(varDirectory);
|
||||
|
||||
{
|
||||
std::stringstream s;
|
||||
s << CHGRP_BIN " " SERVICE_GROUP_NAME " " << varDirectory.c_str();
|
||||
sysExec(s.str().c_str());
|
||||
}
|
||||
{
|
||||
std::stringstream s;
|
||||
s << CHOWN_BIN << " " << SERVICE_ACCOUNT_NAME << " " << varDirectory.c_str();
|
||||
sysExec(s.str().c_str());
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream s;
|
||||
s << CHMOD_BIN << " 775 " << varDirectory.c_str();
|
||||
sysExec(s.str().c_str());
|
||||
}
|
||||
{
|
||||
std::stringstream s;
|
||||
s << CHMOD_BIN << " g+s " << varDirectory.c_str(); // child files/directories inherit ownership.
|
||||
sysExec(s.str().c_str());
|
||||
}
|
||||
|
||||
// authbind port.
|
||||
|
||||
if (authBindRequired)
|
||||
{
|
||||
std::filesystem::create_directories("/etc/authbind/byport");
|
||||
std::filesystem::path portAuthFile = std::filesystem::path("/etc/authbind/byport") / strPort;
|
||||
|
||||
// defensively disable wifi p2p if some leftover config file left it enabled.
|
||||
try
|
||||
{
|
||||
// create it.
|
||||
std::ofstream f(portAuthFile);
|
||||
if (!f.is_open())
|
||||
if (IsP2pServiceEnabled())
|
||||
{
|
||||
throw PiPedalException("Failed to create " + portAuthFile.string());
|
||||
WifiDirectConfigSettings settings;
|
||||
settings.Load();
|
||||
settings.enable_ = false;
|
||||
settings.Save();
|
||||
}
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
// own it.
|
||||
std::stringstream s;
|
||||
s << CHOWN_BIN << " " SERVICE_ACCOUNT_NAME " " << portAuthFile.c_str();
|
||||
sysExec(s.str().c_str());
|
||||
}
|
||||
|
||||
InstallAudioService();
|
||||
auto endpos = endpointAddress.find_last_of(':');
|
||||
if (endpos == string::npos)
|
||||
{
|
||||
// group own it.
|
||||
throw std::runtime_error("Invalid endpoint address: " + endpointAddress);
|
||||
}
|
||||
uint16_t port;
|
||||
auto strPort = endpointAddress.substr(endpos + 1);
|
||||
try
|
||||
{
|
||||
auto lport = std::stoul(strPort);
|
||||
if (lport == 0 || lport >= std::numeric_limits<uint16_t>::max())
|
||||
{
|
||||
throw std::runtime_error("out of range.");
|
||||
}
|
||||
port = (uint16_t)lport;
|
||||
std::stringstream s;
|
||||
s << CHGRP_BIN << " " SERVICE_GROUP_NAME " " << portAuthFile.c_str();
|
||||
s << port;
|
||||
strPort = s.str(); // normalized.
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "Invalid port number: " << strPort;
|
||||
throw std::runtime_error(s.str());
|
||||
}
|
||||
PrepareServiceConfigurationFile(port);
|
||||
|
||||
bool authBindRequired = port < 512;
|
||||
|
||||
// Create and configure service account.
|
||||
|
||||
if (sysExec(GROUPADD_BIN " -f " SERVICE_GROUP_NAME) != EXIT_SUCCESS)
|
||||
{
|
||||
throw std::runtime_error("Failed to create service group.");
|
||||
}
|
||||
|
||||
if (!userExists(SERVICE_ACCOUNT_NAME))
|
||||
{
|
||||
if (sysExec(USERADD_BIN " " SERVICE_ACCOUNT_NAME " -g " SERVICE_GROUP_NAME " -m --home /var/pipedal/home -N -r") != EXIT_SUCCESS)
|
||||
{
|
||||
// throw std::runtime_error("Failed to create service account.");
|
||||
}
|
||||
// lock account for login.
|
||||
silentSysExec("passwd -l " SERVICE_ACCOUNT_NAME);
|
||||
}
|
||||
|
||||
// Add to audio groups.
|
||||
sysExec(USERMOD_BIN " -a -G " AUDIO_SERVICE_GROUP_NAME " " SERVICE_ACCOUNT_NAME);
|
||||
|
||||
// create and configure /var directory.
|
||||
|
||||
std::filesystem::path varDirectory("/var/pipedal");
|
||||
std::filesystem::create_directory(varDirectory);
|
||||
|
||||
{
|
||||
std::stringstream s;
|
||||
s << CHGRP_BIN " " SERVICE_GROUP_NAME " " << varDirectory.c_str();
|
||||
sysExec(s.str().c_str());
|
||||
}
|
||||
{
|
||||
std::stringstream s;
|
||||
s << CHMOD_BIN << " 770 " << portAuthFile.c_str();
|
||||
s << CHOWN_BIN << " " << SERVICE_ACCOUNT_NAME << " " << varDirectory.c_str();
|
||||
sysExec(s.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
cout << "Creating Systemd file."
|
||||
<< "\n";
|
||||
{
|
||||
std::stringstream s;
|
||||
s << CHMOD_BIN << " 775 " << varDirectory.c_str();
|
||||
sysExec(s.str().c_str());
|
||||
}
|
||||
{
|
||||
std::stringstream s;
|
||||
s << CHMOD_BIN << " g+s " << varDirectory.c_str(); // child files/directories inherit ownership.
|
||||
sysExec(s.str().c_str());
|
||||
}
|
||||
|
||||
std::map<string, string> map;
|
||||
|
||||
int shutdownPort = 3147;
|
||||
map["DESCRIPTION"] = "PiPedal Web Service";
|
||||
{
|
||||
std::stringstream s;
|
||||
// authbind port.
|
||||
|
||||
if (authBindRequired)
|
||||
{
|
||||
s << findOnPath("authbind").string() << " --deep ";
|
||||
std::filesystem::create_directories("/etc/authbind/byport");
|
||||
std::filesystem::path portAuthFile = std::filesystem::path("/etc/authbind/byport") / strPort;
|
||||
|
||||
{
|
||||
// create it.
|
||||
std::ofstream f(portAuthFile);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw std::runtime_error("Failed to create " + portAuthFile.string());
|
||||
}
|
||||
}
|
||||
{
|
||||
// own it.
|
||||
std::stringstream s;
|
||||
s << CHOWN_BIN << " " SERVICE_ACCOUNT_NAME " " << portAuthFile.c_str();
|
||||
sysExec(s.str().c_str());
|
||||
}
|
||||
{
|
||||
// group own it.
|
||||
std::stringstream s;
|
||||
s << CHGRP_BIN << " " SERVICE_GROUP_NAME " " << portAuthFile.c_str();
|
||||
sysExec(s.str().c_str());
|
||||
}
|
||||
{
|
||||
std::stringstream s;
|
||||
s << CHMOD_BIN << " 770 " << portAuthFile.c_str();
|
||||
sysExec(s.str().c_str());
|
||||
}
|
||||
}
|
||||
s
|
||||
<< (programPrefix / "bin" / NATIVE_SERVICE).string()
|
||||
<< " /etc/pipedal/config /etc/pipedal/react -port " << endpointAddress << " -systemd";
|
||||
|
||||
map["COMMAND"] = s.str();
|
||||
std::map<string, string> map;
|
||||
|
||||
int shutdownPort = 3147;
|
||||
map["DESCRIPTION"] = "PiPedal Web Service";
|
||||
{
|
||||
std::stringstream s;
|
||||
|
||||
if (authBindRequired)
|
||||
{
|
||||
s << findOnPath("authbind").string() << " --deep ";
|
||||
}
|
||||
s
|
||||
<< (programPrefix / "sbin" / PIPEDALD_SERVICE).string()
|
||||
<< " /etc/pipedal/config /etc/pipedal/react -port " << endpointAddress << " -systemd";
|
||||
|
||||
map["COMMAND"] = s.str();
|
||||
}
|
||||
WriteTemplateFile(map, GetServiceFileName(PIPEDALD_SERVICE));
|
||||
|
||||
map["DESCRIPTION"] = "PiPedal Admin Service";
|
||||
{
|
||||
std::stringstream s;
|
||||
|
||||
s
|
||||
<< (programPrefix / "sbin" / ADMIN_SERVICE).string()
|
||||
<< " -port " << shutdownPort;
|
||||
|
||||
map["COMMAND"] = s.str();
|
||||
}
|
||||
WriteTemplateFile(map, GetServiceFileName(ADMIN_SERVICE));
|
||||
|
||||
// /usr/bin/pipedal_p2pd --config-file /etc/pipedal/config/template_p2pd.conf
|
||||
|
||||
if (UsingNetworkManager())
|
||||
{
|
||||
std::string pipedal_nm_p2pd_cmd = SS(
|
||||
(programPrefix / "sbin" / "pipedal_nm_p2pd").string()
|
||||
//<< " --config-file " << PIPEDAL_P2PD_CONF_PATH
|
||||
);
|
||||
map["COMMAND"] = pipedal_nm_p2pd_cmd;
|
||||
|
||||
WriteTemplateFile(map, GetServiceFileName(PIPEDAL_NM_P2PD_SERVICE));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string pipedal_p2pd_cmd = SS(
|
||||
(programPrefix / "sbin" / "pipedal_p2pd").string()
|
||||
<< " --config-file " << PIPEDAL_P2PD_CONF_PATH);
|
||||
map["COMMAND"] = pipedal_p2pd_cmd;
|
||||
|
||||
WriteTemplateFile(map, GetServiceFileName(PIPEDAL_P2PD_SERVICE));
|
||||
}
|
||||
CopyWpaSupplicantConfigFile();
|
||||
|
||||
sysExec(SYSTEMCTL_BIN " daemon-reload");
|
||||
|
||||
RestartService(false);
|
||||
EnableService();
|
||||
}
|
||||
WriteTemplateFile(map, GetServiceFileName(NATIVE_SERVICE));
|
||||
|
||||
map["DESCRIPTION"] = "PiPedal Admin Service";
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::stringstream s;
|
||||
|
||||
s
|
||||
<< (programPrefix / "bin" / ADMIN_SERVICE).string()
|
||||
<< " -port " << shutdownPort;
|
||||
|
||||
map["COMMAND"] = s.str();
|
||||
// don't allow abnormal termination, which leaves the package in a state that's
|
||||
// difficult to uninstall.
|
||||
cout << "Error: " << e.what();
|
||||
}
|
||||
WriteTemplateFile(map, GetServiceFileName(ADMIN_SERVICE));
|
||||
|
||||
// /usr/bin/pipedal_p2pd --config-file /etc/pipedal/config/template_p2pd.conf
|
||||
|
||||
std::string pipedal_p2pd_cmd = SS(
|
||||
(programPrefix / "bin" / "pipedal_p2pd").string()
|
||||
<< " --config-file " << PIPEDAL_P2PD_CONF_PATH);
|
||||
map["COMMAND"] = pipedal_p2pd_cmd;
|
||||
|
||||
WriteTemplateFile(map, GetServiceFileName(PIPEDAL_P2PD_SERVICE));
|
||||
|
||||
sysExec(SYSTEMCTL_BIN " daemon-reload");
|
||||
|
||||
cout << "Starting services"
|
||||
<< "\n";
|
||||
RestartService(false);
|
||||
EnableService();
|
||||
|
||||
RestartP2pdService();
|
||||
|
||||
cout << "Complete"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
static std::string GetCurrentWebServicePort()
|
||||
{
|
||||
std::filesystem::path servicePath = GetServiceFileName(NATIVE_SERVICE);
|
||||
std::filesystem::path servicePath = GetServiceFileName(PIPEDALD_SERVICE);
|
||||
try
|
||||
{
|
||||
if (std::filesystem::exists(servicePath))
|
||||
@@ -792,7 +849,7 @@ static std::string GetCurrentWebServicePort()
|
||||
ifstream f(servicePath);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw PiPedalException(SS("Can't open " << servicePath));
|
||||
throw std::runtime_error(SS("Can't open " << servicePath));
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
@@ -805,11 +862,11 @@ static std::string GetCurrentWebServicePort()
|
||||
auto startPos = line.find("-port ");
|
||||
if (startPos != std::string::npos)
|
||||
{
|
||||
startPos = startPos+6;
|
||||
startPos = startPos + 6;
|
||||
auto endPos = line.find(" -systemd");
|
||||
if (endPos != std::string::npos)
|
||||
{
|
||||
std::string result = line.substr(startPos,endPos-startPos);
|
||||
std::string result = line.substr(startPos, endPos - startPos);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -818,7 +875,7 @@ static std::string GetCurrentWebServicePort()
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception &/*ignored*/)
|
||||
catch (const std::exception & /*ignored*/)
|
||||
{
|
||||
}
|
||||
return "";
|
||||
@@ -909,21 +966,25 @@ static void PrintHelp()
|
||||
<< "Channel number defaults to 1."
|
||||
<< "\n\n"
|
||||
|
||||
<< HangingIndent() << " --disable-p2p\tDisabled Wi-Fi Direct access."
|
||||
<< HangingIndent() << " --disable-p2p\tDisable Wi-Fi Direct access."
|
||||
<< "\n\n"
|
||||
|
||||
<< HangingIndent() << " --enable-legacy-ap\t <country_code> <ssid> <wep_password> <channel>\tEnable a legacy Wi-Fi access point."
|
||||
<< "\n\n"
|
||||
<< "Enable a legacy Wi-Fi access point. \n\n"
|
||||
<< "country_code is the 2-letter ISO-3166 country code for "
|
||||
<< "the country you are in. see below for further notes."
|
||||
<< "\n\n"
|
||||
<< "See below for an explanation of when you might want to use a legacy Wi-Fi access point instead of Wifi-Direct access."
|
||||
<< "an explanation of when you might want to use a legacy Access Point instead of "
|
||||
<< "a P2P (Wi-Fi Direct) connection. Generally, you should prefer a P2p connection "
|
||||
<< "to an ordinary Hotspot connection."
|
||||
<< HangingIndent() << " --list-p2p-channels [<country_code>] \tList valid p2p channels for the current/specified country."
|
||||
<< "\n\n"
|
||||
|
||||
|
||||
// << HangingIndent() << " --enable-legacy-ap\t <country_code> <ssid> <wep_password> <channel>\tEnable a legacy Wi-Fi access point."
|
||||
// << "\n\n"
|
||||
// << "Enable a legacy Wi-Fi access point. \n\n"
|
||||
// << "country_code is the 2-letter ISO-3166 country code for "
|
||||
// << "the country you are in. see below for further notes."
|
||||
// << "\n\n"
|
||||
// << "See below for an explanation of when you might want to use a legacy Wi-Fi access point instead of Wifi-Direct access."
|
||||
// << "an explanation of when you might want to use a legacy Access Point instead of "
|
||||
// << "a P2P (Wi-Fi Direct) connection. Generally, you should prefer a P2p connection "
|
||||
// << "to an ordinary Hotspot connection."
|
||||
// << "\n\n"
|
||||
|
||||
<< HangingIndent() << " --disable-legacy-ap\tDisabled the legacy Wi-Fi access point."
|
||||
<< "\n\n"
|
||||
|
||||
@@ -962,6 +1023,40 @@ static void PrintHelp()
|
||||
"therefore preferrable under almost all circumstances.\n\n";
|
||||
}
|
||||
|
||||
static int ListP2PChannels(const std::vector<std::string>&arguments)
|
||||
{
|
||||
try {
|
||||
std::string country;
|
||||
if (arguments.size() >= 2)
|
||||
{
|
||||
throw std::runtime_error("Invalid arguments.");
|
||||
} if (arguments.size() == 1)
|
||||
{
|
||||
country = arguments[0];
|
||||
} else {
|
||||
// use the currently selected country by default.
|
||||
WifiDirectConfigSettings settings;
|
||||
settings.Load();
|
||||
if (settings.countryCode_.empty())
|
||||
{
|
||||
throw std::runtime_error("A country has not yet been selected. Please provde a country code.");
|
||||
}
|
||||
country = settings.countryCode_;
|
||||
}
|
||||
auto channelSelectors = getWifiChannelSelectors(country.c_str(),true);
|
||||
for (const auto &channelSelector: channelSelectors)
|
||||
{
|
||||
cout << channelSelector.channelName_ << endl;
|
||||
}
|
||||
} catch (const std::exception &e)
|
||||
{
|
||||
cout << "ERROR: " << e.what() << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CommandLineParser parser;
|
||||
@@ -973,13 +1068,14 @@ int main(int argc, char **argv)
|
||||
bool enable = false, disable = false, restart = false;
|
||||
bool enable_ap = false, disable_ap = false;
|
||||
bool enable_p2p = false, disable_p2p = false;
|
||||
bool list_p2p_channels = false;
|
||||
bool get_current_port = false;
|
||||
bool nopkexec = false;
|
||||
bool nosudo = false;
|
||||
bool excludeShutdownService = false;
|
||||
std::string prefixOption;
|
||||
std::string portOption;
|
||||
|
||||
parser.AddOption("--nopkexec", &nopkexec); // strictly a debugging aid.
|
||||
parser.AddOption("--nosudo", &nosudo); // strictly a debugging aid. Run without sudo, until we fail because of permissions.
|
||||
parser.AddOption("--install", &install);
|
||||
parser.AddOption("--uninstall", &uninstall);
|
||||
parser.AddOption("--stop", &stop);
|
||||
@@ -991,10 +1087,12 @@ int main(int argc, char **argv)
|
||||
parser.AddOption("--help", &help);
|
||||
parser.AddOption("--prefix", &prefixOption);
|
||||
parser.AddOption("--port", &portOption);
|
||||
parser.AddOption("--enable-legacy-ap", &enable_ap);
|
||||
//parser.AddOption("--enable-legacy-ap", &enable_ap);
|
||||
parser.AddOption("--disable-ap", &disable_ap);
|
||||
parser.AddOption("--enable-p2p", &enable_p2p);
|
||||
parser.AddOption("--disable-p2p", &disable_p2p);
|
||||
parser.AddOption("--list-p2p-channels", &list_p2p_channels);
|
||||
|
||||
|
||||
parser.AddOption("--get-current-port", &get_current_port); // private. For debug use only.
|
||||
|
||||
@@ -1003,20 +1101,25 @@ int main(int argc, char **argv)
|
||||
{
|
||||
parser.Parse(argc, (const char **)argv);
|
||||
|
||||
int actionCount = get_current_port + install + uninstall + stop + start + enable + disable + enable_ap + disable_ap + restart + enable_p2p + disable_p2p;
|
||||
int actionCount =
|
||||
help + get_current_port + install + uninstall + stop + start + enable + disable
|
||||
+ enable_ap + disable_ap + restart + enable_p2p + disable_p2p
|
||||
+ list_p2p_channels
|
||||
;
|
||||
if (actionCount > 1)
|
||||
{
|
||||
throw PiPedalException("Please provide only one action.");
|
||||
throw std::runtime_error("Please provide only one action.");
|
||||
}
|
||||
if (argc == 1 )
|
||||
if (argc == 1)
|
||||
{
|
||||
help = true;
|
||||
} else if (actionCount == 0)
|
||||
}
|
||||
else if (actionCount == 0)
|
||||
{
|
||||
|
||||
throw PiPedalException("No action provided.");
|
||||
throw std::runtime_error("No action provided.");
|
||||
}
|
||||
if ((!enable_p2p) && (!enable_ap))
|
||||
if ((!enable_p2p) && (!enable_ap) && (!list_p2p_channels))
|
||||
{
|
||||
if (parser.Arguments().size() != 0)
|
||||
{
|
||||
@@ -1045,7 +1148,11 @@ int main(int argc, char **argv)
|
||||
if (get_current_port)
|
||||
{
|
||||
std::cout << "current port: " << GetCurrentWebServicePort() << std::endl;
|
||||
return 0;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
if (list_p2p_channels)
|
||||
{
|
||||
return ListP2PChannels(parser.Arguments());
|
||||
}
|
||||
if (portOption.size() != 0 && !install)
|
||||
{
|
||||
@@ -1055,24 +1162,9 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
auto uid = getuid();
|
||||
if (uid != 0 && !nopkexec)
|
||||
if (uid != 0 && !nosudo)
|
||||
{
|
||||
// re-execute with PKEXEC in order to prompt for SUDO credentials once only.
|
||||
std::vector<char *> args;
|
||||
std::string pkexec = "/usr/bin/pkexec"; // staged because "ISO C++ forbids converting a string constant to std::vector<char*>::value_type"(!)
|
||||
args.push_back((char *)(pkexec.c_str()));
|
||||
|
||||
std::string sPath = getSelfExePath();
|
||||
args.push_back(const_cast<char *>(sPath.c_str()));
|
||||
for (int arg = 1; arg < argc; ++arg)
|
||||
{
|
||||
args.push_back(const_cast<char *>(argv[arg]));
|
||||
}
|
||||
|
||||
args.push_back(nullptr);
|
||||
|
||||
char **rawArgv = &args[0];
|
||||
return SudoExec(rawArgv);
|
||||
return SudoExec(argc,argv);
|
||||
}
|
||||
|
||||
try
|
||||
@@ -1088,12 +1180,12 @@ int main(int argc, char **argv)
|
||||
{
|
||||
prefix = std::filesystem::path(argv[0]).parent_path().parent_path();
|
||||
|
||||
std::filesystem::path pipedalPath = prefix / "bin/pipedald";
|
||||
std::filesystem::path pipedalPath = prefix / "sbin" / "pipedald";
|
||||
if (!std::filesystem::exists(pipedalPath))
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "Can't find pipedald executable at " << pipedalPath << ". Try again using the -prefix option.";
|
||||
throw PiPedalException(s.str());
|
||||
throw std::runtime_error(s.str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1137,13 +1229,19 @@ int main(int argc, char **argv)
|
||||
}
|
||||
else if (enable_p2p)
|
||||
{
|
||||
auto argv = parser.Arguments();
|
||||
WifiDirectConfigSettings settings;
|
||||
settings.ParseArguments(argv);
|
||||
settings.valid_ = true;
|
||||
settings.enable_ = true;
|
||||
SetWifiDirectConfig(settings);
|
||||
RestartService(true); // also have to retart web service so that it gets the correct device name.
|
||||
try {
|
||||
auto argv = parser.Arguments();
|
||||
WifiDirectConfigSettings settings;
|
||||
settings.ParseArguments(argv);
|
||||
settings.valid_ = true;
|
||||
settings.enable_ = true;
|
||||
SetWifiDirectConfig(settings);
|
||||
RestartService(true); // also have to retart web service so that it gets the correct device name.
|
||||
} catch (const std::exception&e)
|
||||
{
|
||||
cout << "ERROR: " << e.what() << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else if (disable_p2p)
|
||||
{
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "ConfigSerializer.h"
|
||||
|
||||
|
||||
using namespace config_serializer;
|
||||
using namespace config_serializer::detail;
|
||||
using namespace std;
|
||||
|
||||
std::string config_serializer::detail::trim(const std::string &v)
|
||||
{
|
||||
size_t start = v.find_first_not_of(' ');
|
||||
size_t end = v.find_last_not_of(' ');
|
||||
if (start >= end+1)
|
||||
return "";
|
||||
return v.substr(start, end+1 - start);
|
||||
}
|
||||
|
||||
std::vector<std::string> config_serializer::detail::split(const std::string &value, char delimiter)
|
||||
{
|
||||
size_t start = 0;
|
||||
std::vector<std::string> result;
|
||||
while (start < value.length())
|
||||
{
|
||||
size_t pos = value.find_first_of(delimiter, start);
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
result.push_back(value.substr(start));
|
||||
break;
|
||||
}
|
||||
result.push_back(value.substr(start, pos - start));
|
||||
start = pos + 1;
|
||||
if (start == value.length())
|
||||
{
|
||||
// ends with delimieter? Then there's an empty-length value at the end.
|
||||
result.push_back("");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string config_serializer::detail::EncodeString(const std::string &s)
|
||||
{
|
||||
bool requiresEncoding = false;
|
||||
|
||||
for (char c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
break;
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\\':
|
||||
case ' ':
|
||||
case '\"':
|
||||
requiresEncoding = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!requiresEncoding)
|
||||
return s;
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
ss << '"';
|
||||
for (char c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
// discard.
|
||||
break;
|
||||
case '\r':
|
||||
ss << "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
ss << "\\t";
|
||||
break;
|
||||
case '\\':
|
||||
ss << "\\\\";
|
||||
break;
|
||||
case '\"':
|
||||
ss << "\\\"";
|
||||
break;
|
||||
default:
|
||||
ss << c;
|
||||
}
|
||||
}
|
||||
ss << '"';
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string config_serializer::detail::DecodeString(const std::string &value)
|
||||
{
|
||||
std::stringstream s;
|
||||
const char *p = value.c_str();
|
||||
char quoteChar;
|
||||
if (*p == '\'' || *p == '\"')
|
||||
{
|
||||
quoteChar = *p;
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
if (*p != quoteChar)
|
||||
return value;
|
||||
if (value.at(value.length()-1) != quoteChar)
|
||||
{
|
||||
throw std::invalid_argument("Invalid quoted string.");
|
||||
}
|
||||
++p;
|
||||
while (*p != 0 && *p != quoteChar)
|
||||
{
|
||||
char c = *p++;
|
||||
if (c == '\\')
|
||||
{
|
||||
c = *p++;
|
||||
switch (c)
|
||||
{
|
||||
case 'r':
|
||||
s << '\r';
|
||||
break;
|
||||
case 't':
|
||||
s << '\t';
|
||||
break;
|
||||
case 'n':
|
||||
s << '\n';
|
||||
break;
|
||||
case '\0':
|
||||
throw std::invalid_argument("Invalid quoted string.");
|
||||
break;
|
||||
default:
|
||||
s << c;
|
||||
}
|
||||
if (c != 0)
|
||||
{
|
||||
s << c;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s << c;
|
||||
}
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
|
||||
@@ -1,369 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include "autoptr_vector.h"
|
||||
#include <unordered_map>
|
||||
#include <fstream>
|
||||
|
||||
namespace config_serializer
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
std::string trim(const std::string &v);
|
||||
|
||||
std::vector<std::string> split(const std::string &v, char seperator);
|
||||
|
||||
/**
|
||||
* @brief Convert string to config file format.
|
||||
*
|
||||
* Adds quotes and escapes, but only if neccessary.
|
||||
*
|
||||
* Source text is assumed to be UTF-8. Escapes for the
|
||||
* following values only: \r \n \t \" \\.
|
||||
*
|
||||
*
|
||||
* @param s
|
||||
* @return std::string Encoded string.
|
||||
*/
|
||||
std::string EncodeString(const std::string &s);
|
||||
|
||||
/**
|
||||
* @brief Config file format to string.
|
||||
*
|
||||
* Decodes quotes and escapes, but only if neccessary.
|
||||
*
|
||||
* Source text is assumed to be UTF-8. Escapes for the
|
||||
* following values only: \r \n \t \" \\.
|
||||
*
|
||||
* @param s
|
||||
* @return std::string
|
||||
*/
|
||||
std::string DecodeString(const std::string &s);
|
||||
|
||||
/**
|
||||
* @brief Convert a string to an integer.
|
||||
*
|
||||
* @tparam T A signed or unsigned integral type.
|
||||
* @param value The string to parse.
|
||||
* @return T The parsed result.
|
||||
* @throws std::invalid_argument if the supplied string is not a valid integer.
|
||||
*/
|
||||
template <class T>
|
||||
T ToInt(const std::string &value)
|
||||
{
|
||||
std::stringstream s(value);
|
||||
T result;
|
||||
s >> result;
|
||||
if (s.fail())
|
||||
{
|
||||
throw std::invalid_argument("Invalid integer value.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using namespace detail;
|
||||
|
||||
template <class OBJ>
|
||||
class ConfigSerializerBase
|
||||
{
|
||||
protected:
|
||||
ConfigSerializerBase(const std::string &name, const std::string &comment)
|
||||
: name(name),
|
||||
comment(comment)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
using Obj = OBJ;
|
||||
virtual ~ConfigSerializerBase() {}
|
||||
|
||||
const std::string &GetName() const { return name; }
|
||||
const std::string &GetComment() const { return comment; }
|
||||
virtual void SetValue(Obj &configurable, const std::string &value) const = 0;
|
||||
virtual const std::string GetValue(Obj &configurable) const = 0;
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
std::string comment;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class ConfigConverter
|
||||
{
|
||||
public:
|
||||
static T FromString(const std::string &value)
|
||||
{
|
||||
T result;
|
||||
std::stringstream s(value);
|
||||
s >> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string ToString(const T &value)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << value;
|
||||
return s.str();
|
||||
}
|
||||
};
|
||||
|
||||
// string specializations.
|
||||
template <>
|
||||
inline std::string ConfigConverter<std::string>::FromString(const std::string &value)
|
||||
{
|
||||
return DecodeString(value);
|
||||
}
|
||||
template <>
|
||||
inline std::string ConfigConverter<std::string>::ToString(const std::string &config)
|
||||
{
|
||||
return EncodeString(config);
|
||||
}
|
||||
// bool specializations.
|
||||
template <>
|
||||
inline bool ConfigConverter<bool>::FromString(const std::string &value)
|
||||
{
|
||||
bool result = false;
|
||||
if (value == "")
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
else if (value == "true")
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
else if (value == "false")
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
int t = ToInt<int>(value);
|
||||
result = (t != 0);
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
throw std::invalid_argument("Expecting true or false.");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
template <>
|
||||
inline std::string ConfigConverter<bool>::ToString(const bool &value)
|
||||
{
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
template <class OBJ, class T>
|
||||
class ConfigSerializer : public ConfigSerializerBase<OBJ>
|
||||
{
|
||||
public:
|
||||
using Obj = OBJ;
|
||||
using pointer_type = T Obj::*;
|
||||
using base = ConfigSerializerBase<Obj>;
|
||||
using converter = ConfigConverter<T>;
|
||||
|
||||
ConfigSerializer(const std::string &name,
|
||||
pointer_type pMember,
|
||||
const std::string &comment)
|
||||
: base(name, comment),
|
||||
pMember(pMember)
|
||||
{
|
||||
}
|
||||
virtual void SetValue(Obj &configurable, const std::string &value) const
|
||||
{
|
||||
configurable.*pMember = ConfigConverter<T>::FromString(value);
|
||||
}
|
||||
virtual const std::string GetValue(Obj &configurable) const
|
||||
{
|
||||
return ConfigConverter<T>::ToString(configurable.*pMember);
|
||||
}
|
||||
|
||||
private:
|
||||
pointer_type pMember;
|
||||
};
|
||||
|
||||
template <class OBJ>
|
||||
class ConfigSerializable
|
||||
{
|
||||
private:
|
||||
using Obj = OBJ;
|
||||
|
||||
using serializer_t = ConfigSerializerBase<OBJ>;
|
||||
using serializers_t = p2p::autoptr_vector<serializer_t>;
|
||||
|
||||
const serializers_t &serializers;
|
||||
|
||||
protected:
|
||||
ConfigSerializable(const serializers_t &serializers)
|
||||
: serializers(serializers)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void Save(std::ostream &f)
|
||||
{
|
||||
bool firstLine = true;
|
||||
for (const serializer_t *serializer : serializers)
|
||||
{
|
||||
if (serializer->GetComment().length() != 0)
|
||||
{
|
||||
if (!firstLine)
|
||||
{
|
||||
f << std::endl;
|
||||
}
|
||||
firstLine = false;
|
||||
std::vector<std::string> comments = split(serializer->GetComment(), '\n');
|
||||
|
||||
if (comments.size() != 0)
|
||||
for (const std::string &comment : comments)
|
||||
{
|
||||
f << "# " << comment << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
f << serializer->GetName() << '=' << serializer->GetValue((Obj &)*this) << std::endl;
|
||||
}
|
||||
}
|
||||
void Save(const std::string &path)
|
||||
{
|
||||
std::ofstream f;
|
||||
f.open(path);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw std::invalid_argument("Can't write to file " + path);
|
||||
}
|
||||
try
|
||||
{
|
||||
Save(f);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
throw std::invalid_argument("Error writing to '" + path + "'. " + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Load(std::istream &f)
|
||||
{
|
||||
std::unordered_map<std::string, serializer_t *> index;
|
||||
|
||||
for (serializer_t *serializer : serializers)
|
||||
{
|
||||
index[serializer->GetName()] = serializer;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
int nLine = 0;
|
||||
while (true)
|
||||
{
|
||||
if (f.eof())
|
||||
break;
|
||||
getline(f, line);
|
||||
++nLine;
|
||||
|
||||
// remove comments.
|
||||
auto npos = line.find_first_of('#');
|
||||
if (npos != std::string::npos)
|
||||
{
|
||||
line = line.substr(0, npos);
|
||||
}
|
||||
size_t start = 0;
|
||||
while (start < line.size() && line[start] == ' ')
|
||||
{
|
||||
++start;
|
||||
}
|
||||
if (start == line.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto pos = line.find_first_of('=', start);
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
std::stringstream msg;
|
||||
|
||||
msg << "(" << nLine << "," << (start + 1) << "): Syntax error. Expecting '='.";
|
||||
throw std::logic_error(msg.str());
|
||||
}
|
||||
std::string label = trim(line.substr(start, pos - start));
|
||||
std::string value = trim(line.substr(pos + 1));
|
||||
|
||||
if (index.contains(label))
|
||||
{
|
||||
serializer_t *serializer = index[label];
|
||||
try
|
||||
{
|
||||
serializer->SetValue((Obj &)*this, value);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "(" << nLine << ',' << (pos + 1) << "): " << e.what();
|
||||
throw std::logic_error(msg.str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "(" << nLine << ',' << (start) << "): "
|
||||
<< "Invalid property: " + label;
|
||||
throw std::logic_error(
|
||||
msg.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Load(const std::string &path, bool throwIfNotFound = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::ifstream f;
|
||||
f.open(path);
|
||||
if (!f.is_open())
|
||||
{
|
||||
if (throwIfNotFound)
|
||||
{
|
||||
throw std::logic_error("Can't open file.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
Load(f);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "Error: " << path << " " << e.what();
|
||||
throw std::logic_error(msg.str());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "ConfigUtil.hpp"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
using namespace pipedal;
|
||||
using namespace std;
|
||||
|
||||
static std::string getKey(const std::string &line, size_t length)
|
||||
{
|
||||
std::stringstream s;
|
||||
size_t start = 0;
|
||||
size_t end = length;
|
||||
char c;
|
||||
while (start < end && (line[start] == ' ' || line[start] == '\t'))
|
||||
++start;
|
||||
|
||||
while (end > start && (line[end - 1] == ' ' || line[end - 1] == '\t'))
|
||||
--end;
|
||||
|
||||
return line.substr(start, end - start);
|
||||
}
|
||||
static std::string trim(const std::string &value)
|
||||
{
|
||||
size_t start = 0;
|
||||
size_t end = value.length();
|
||||
|
||||
while (start < end && (value[start] == ' ' || value[start] == '\t'))
|
||||
++start;
|
||||
|
||||
while (end > start && (value[end - 1] == ' ' || value[end - 1] == '\t'))
|
||||
--end;
|
||||
|
||||
return value.substr(start, end - start);
|
||||
}
|
||||
static std::string unquote(const std::string &value)
|
||||
{
|
||||
if (value.length() > 2)
|
||||
{
|
||||
if (
|
||||
(value[0] == '\'' && value[value.length() - 1] == '\'') || (value[0] == '\"' && value[value.length() - 1] == '\"'))
|
||||
{
|
||||
char quotChar = value[0];
|
||||
|
||||
std::stringstream ss;
|
||||
for (size_t i = 1; i < value.length() - 1; ++i)
|
||||
{
|
||||
char c = value[i];
|
||||
if (c == '\\')
|
||||
{
|
||||
++i;
|
||||
c = value[i];
|
||||
switch (c)
|
||||
{
|
||||
case 'r':
|
||||
c = '\r';
|
||||
break;
|
||||
case 'n':
|
||||
c = '\n';
|
||||
break;
|
||||
case 't':
|
||||
c = '\t';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ss << c;
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << c;
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string ConfigUtil::QuoteString(const std::string &value)
|
||||
{
|
||||
std::stringstream s;
|
||||
|
||||
s << '"';
|
||||
for (char c : value)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
break;
|
||||
case '"':
|
||||
s << "\\\"";
|
||||
break;
|
||||
case '\\':
|
||||
s << "\\\\";
|
||||
break;
|
||||
default:
|
||||
s << c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
s << '"';
|
||||
return s.str();
|
||||
}
|
||||
bool ConfigUtil::GetConfigLine(const std::string &filePath, const std::string &key, std::string *pValue)
|
||||
{
|
||||
|
||||
ifstream f;
|
||||
f.open(filePath);
|
||||
if (f.is_open())
|
||||
{
|
||||
std::string line;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (f.eof() || f.fail())
|
||||
{
|
||||
break;
|
||||
}
|
||||
getline(f, line);
|
||||
auto pos = line.find_first_of('#');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
line = line.substr(0, pos);
|
||||
}
|
||||
pos = line.find_first_of('=');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
std::string thisKey = getKey(line, pos);
|
||||
if (thisKey == key)
|
||||
{
|
||||
std::string value = line.substr(pos + 1);
|
||||
|
||||
*pValue = unquote(trim(value));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace pipedal {
|
||||
class ConfigUtil {
|
||||
public:
|
||||
static bool GetConfigLine(const std::string & filePath,const std::string & key, std::string *pValue);
|
||||
|
||||
static std::string QuoteString(const std::string &value);
|
||||
|
||||
};
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "pch.h"
|
||||
#include "HtmlHelper.hpp"
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
|
||||
// non-localizable. RFC 7231 Date Format
|
||||
;
|
||||
static const char*INVARIANT_WEEK_DAYS[] =
|
||||
{ "Sun","Mon", "Tue", "Wed","Thu", "Fri","Sat"};
|
||||
// non-localizable. RFC 7231 Date Format
|
||||
static const char*INVARIANT_MONTHS[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep","Oct","Nov", "Dec"};
|
||||
// non-localizable. RFC 7231 Date Format
|
||||
static const char*FORMAT = "%s, %02d %s %04d %02d:%02d:%02d GMT";
|
||||
|
||||
std::string HtmlHelper::timeToHttpDate()
|
||||
{
|
||||
return timeToHttpDate(time(nullptr));
|
||||
}
|
||||
|
||||
std::string HtmlHelper::timeToHttpDate(time_t time)
|
||||
{
|
||||
// RFC 7231, IMF-fixdate.
|
||||
|
||||
struct tm gmTm;
|
||||
|
||||
if (gmtime_r(&time,&gmTm) == nullptr)
|
||||
{
|
||||
return "Mon, 01 Jan 1970 00:00:00 GMT";
|
||||
}
|
||||
char szBuffer[sizeof("Mon, 01 Jan 1970 00:00:00 GMT+")];
|
||||
snprintf(szBuffer,sizeof(szBuffer), FORMAT,
|
||||
INVARIANT_WEEK_DAYS[gmTm.tm_wday],
|
||||
gmTm.tm_mday,
|
||||
INVARIANT_MONTHS[gmTm.tm_mon],
|
||||
gmTm.tm_year+1900,
|
||||
gmTm.tm_hour,
|
||||
gmTm.tm_min,
|
||||
gmTm.tm_sec);
|
||||
return szBuffer;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int hexN(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') return c-'0';
|
||||
if (c >= 'a' && c <= 'f') return 10+c-'a';
|
||||
if (c >= 'A' && c <= 'F') return 10+c-'A';
|
||||
throw std::invalid_argument("Malformed URL.");
|
||||
}
|
||||
static char hexChar(char c1, char c2)
|
||||
{
|
||||
return (char)((hexN(c1) << 4) + hexN(c2));
|
||||
}
|
||||
|
||||
class SafeCharacterMap {
|
||||
bool safeCharacter[256];
|
||||
public:
|
||||
SafeCharacterMap()
|
||||
{
|
||||
for (int i = 0; i < 0x20; ++i) {
|
||||
safeCharacter[i] = false;
|
||||
}
|
||||
for (int i = 0x20; i < 0x80; ++i) {
|
||||
safeCharacter[i] = true;
|
||||
}
|
||||
for (int i = 0x80; i < 256; ++i)
|
||||
{
|
||||
safeCharacter[i] = false;
|
||||
}
|
||||
const char *unsafeCharacters = ":/+?=#%@ ";
|
||||
for (const char*p = unsafeCharacters; *p != 0; ++p)
|
||||
{
|
||||
safeCharacter[(uint8_t)*p] = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool isSafeCharacter(char c) { return safeCharacter[(uint8_t)c];}
|
||||
} g_safeCharacterMap;
|
||||
|
||||
|
||||
std::string HtmlHelper::encode_url_segment(const char*pStart, const char*pEnd, bool isQuerySegment )
|
||||
{
|
||||
std::stringstream s;
|
||||
encode_url_segment(s,pStart,pEnd,isQuerySegment);
|
||||
return s.str();
|
||||
}
|
||||
|
||||
static char hexChars[] = "0123456789ABCDEF";
|
||||
|
||||
void HtmlHelper::encode_url_segment(std::ostream&os, const char*pStart, const char *pEnd, bool isQuerySegment)
|
||||
{
|
||||
while (pStart != pEnd)
|
||||
{
|
||||
char c = *pStart++;
|
||||
if (g_safeCharacterMap.isSafeCharacter(c))
|
||||
{
|
||||
os << c;
|
||||
} else if (c == ' ' && isQuerySegment)
|
||||
{
|
||||
os << '+';
|
||||
|
||||
os << c;
|
||||
} else {
|
||||
os << '%' << hexChars[(c >> 4) & 0x0f] << hexChars[c & 0x0F];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::string HtmlHelper::decode_url_segment(const char*pStart, const char*pEnd, bool isQuery)
|
||||
{
|
||||
std::stringstream s;
|
||||
auto p = pStart;
|
||||
while (p != pEnd)
|
||||
{
|
||||
char c = *p;
|
||||
++p;
|
||||
if (c == '+')
|
||||
{
|
||||
s << ' ';
|
||||
} else if (c != '%')
|
||||
{
|
||||
s << c;
|
||||
} else {
|
||||
if (pStart+2 < pEnd)
|
||||
{
|
||||
char c1 = *p; ++p;
|
||||
char c2 = *p; ++p;
|
||||
s << hexChar(c1,c2);
|
||||
} else {
|
||||
throw std::invalid_argument("Malformed URL.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
|
||||
std::string HtmlHelper::decode_url_segment(const char*text, bool isQuery)
|
||||
{
|
||||
return decode_url_segment(text, text + strlen(text),isQuery);
|
||||
}
|
||||
|
||||
void HtmlHelper::utf32_to_utf8_stream(std::ostream &s, uint32_t uc)
|
||||
{
|
||||
if (uc < 0x80u)
|
||||
{
|
||||
s << (char)uc;
|
||||
} else if (uc < 0x800u) {
|
||||
s << (char)(0xC0 + (uc >> 6));
|
||||
s << (char)(0x80 + (uc & 0x3F));
|
||||
|
||||
} else if (uc < 0x10000u) {
|
||||
s << (char)(0xE0 + (uc >> 12));
|
||||
s << (char)(0x80 + ((uc >> 6) & 0x3F));
|
||||
s << (char)(0x80 + (uc & 0x3F));
|
||||
} else if (uc < 0x0110000) {
|
||||
s << (char)(0xF0 + (uc >> 18));
|
||||
s << (char)(0x80 + ((uc >> 12) & 0x3F));
|
||||
s << (char)(0x80 + ((uc >> 6) & 0x3F));
|
||||
s << (char)(0x80 + (uc & 0x3F));
|
||||
} else {
|
||||
throw std::range_error("Illegal UTF-32 character.");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string ESPECIALS = "()<>@,;:\"/[]?.=";
|
||||
|
||||
#define MAX_FILENAME_LENGTH 96
|
||||
#define MAX_SEGMENT_LENGTH 74 // 75 per RFC2047.2. But leave space for a ';' to allow easier breaking of lines.
|
||||
|
||||
std::string HtmlHelper::Rfc5987EncodeFileName(const std::string&name)
|
||||
{
|
||||
// Encode all characters. Let the browser handle safe-ifying the actual name, since that's os dependent.
|
||||
std::stringstream s;
|
||||
std::string prefix = "UTF-8''";
|
||||
std::string postfix = "";
|
||||
s << prefix;
|
||||
for (char c: name)
|
||||
{
|
||||
uint8_t uc = (uint8_t)c;
|
||||
if (uc < 0x20 || uc >= 0x80 || ESPECIALS.find(c) != std::string::npos)
|
||||
{
|
||||
s << '%' << hexChars[uc >> 4] << hexChars[uc & 0x0F];
|
||||
} else {
|
||||
s << c;
|
||||
}
|
||||
}
|
||||
s << postfix;
|
||||
return s.str();
|
||||
}
|
||||
|
||||
|
||||
#define MAX_FILE_NAME_LENGHT 96
|
||||
|
||||
const std::string SF_SPECIALS = " <>@;:\"\'/[]?=";
|
||||
|
||||
std::string HtmlHelper::SafeFileName(const std::string &name)
|
||||
{
|
||||
std::stringstream s;
|
||||
for (char c : name)
|
||||
{
|
||||
uint8_t uc = (uint8_t)c;
|
||||
if (uc < 0x20 || uc >= 0x80 || SF_SPECIALS.find(c) != std::string::npos)
|
||||
{
|
||||
s << '_';
|
||||
}
|
||||
else
|
||||
{
|
||||
s << c;
|
||||
}
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
|
||||
std::string HtmlHelper::HtmlEncode(const std::string& text)
|
||||
{
|
||||
std::stringstream os;
|
||||
for (char c: text)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case '<': os << "<"; break;
|
||||
case '>': os << ">"; break;
|
||||
case '&': os << "&"; break;
|
||||
default: os << c; break;
|
||||
|
||||
}
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace pipedal {
|
||||
|
||||
class HtmlHelper {
|
||||
|
||||
public:
|
||||
static std::string timeToHttpDate();
|
||||
static std::string timeToHttpDate(time_t time);
|
||||
|
||||
|
||||
static std::string encode_url_segment(const char*pStart, const char*pEnd, bool isQuerySegment = false);
|
||||
static void encode_url_segment(std::ostream&os, const char*pStart, const char *pEnd, bool isQuerySegment = false);
|
||||
static void encode_url_segment(std::ostream&os, const std::string&segment, bool isQuerySegment = false)
|
||||
{
|
||||
const char*p = segment.c_str();
|
||||
encode_url_segment(os,p, p + segment.length(),isQuerySegment);
|
||||
}
|
||||
|
||||
|
||||
static std::string decode_url_segment(const char*pStart, const char*pEnd, bool isQuerySegment = false);
|
||||
|
||||
static std::string decode_url_segment(const char*text, bool isQuerySegment = false);
|
||||
|
||||
static void utf32_to_utf8_stream(std::ostream &s, uint32_t uc);
|
||||
|
||||
static std::string Rfc5987EncodeFileName(const std::string&name);
|
||||
|
||||
static std::string SafeFileName(const std::string &name);
|
||||
static std::string HtmlEncode(const std::string& text);
|
||||
|
||||
};
|
||||
|
||||
} // namespace.
|
||||
@@ -33,8 +33,8 @@ namespace pipedal
|
||||
bool rebootRequired_ = false;
|
||||
std::string alsaDevice_;
|
||||
uint64_t sampleRate_ = 0;
|
||||
uint32_t bufferSize_ = 0;
|
||||
uint32_t numberOfBuffers_ = 0;
|
||||
uint32_t bufferSize_ = 64;
|
||||
uint32_t numberOfBuffers_ = 2;
|
||||
|
||||
public:
|
||||
JackServerSettings();
|
||||
|
||||
+1
-1
@@ -244,7 +244,7 @@ namespace pipedal
|
||||
{
|
||||
if (index == -1)
|
||||
{
|
||||
this->bypass = value != 0;
|
||||
SetBypass(value != 0);
|
||||
} else {
|
||||
controlValues[index] = value;
|
||||
}
|
||||
|
||||
+1
-1
@@ -136,7 +136,7 @@ static std::string toLower(const std::string&value)
|
||||
|
||||
void MimeTypes::AddMimeType(const std::string&extension_, const std::string&mimeType)
|
||||
{
|
||||
std::string extension = "." + toLower(extension_);
|
||||
std::string extension = std::string(".") + toLower(extension_);
|
||||
mimeTypeToExtension[mimeType] = extension;
|
||||
extensionToMimeType[extension] = mimeType;
|
||||
if (mimeType.starts_with("audio/"))
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#define DEVICE_GUID_FILE "/etc/pipedal/config/device_uuid"
|
||||
|
||||
#define PIPEDAL_P2PD_CONF_PATH "/etc/pipedal/config/pipedal_p2pd.conf"
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#include "AdminClient.hpp"
|
||||
#include "WifiConfigSettings.hpp"
|
||||
#include "WifiDirectConfigSettings.hpp"
|
||||
#include "WifiChannels.hpp"
|
||||
#include "WifiChannelSelectors.hpp"
|
||||
#include "SysExec.hpp"
|
||||
#include "PiPedalAlsa.hpp"
|
||||
#include <filesystem>
|
||||
@@ -973,7 +973,7 @@ public:
|
||||
{
|
||||
std::string country;
|
||||
pReader->read(&country);
|
||||
std::vector<WifiChannel> channels = pipedal::getWifiChannels(country.c_str());
|
||||
std::vector<WifiChannelSelector> channels = pipedal::getWifiChannelSelectors(country.c_str());
|
||||
this->Reply(replyTo, "getWifiChannels", channels);
|
||||
}
|
||||
else if (message == "getPluginPresets")
|
||||
|
||||
+26
-26
@@ -35,7 +35,7 @@ PiPedalUI::PiPedalUI(PluginHost *pHost, const LilvNode *uiNode, const std::files
|
||||
{
|
||||
auto pWorld = pHost->getWorld();
|
||||
|
||||
AutoLilvNodes fileNodes = lilv_world_find_nodes(pWorld, uiNode, pHost->lilvUris.pipedalUI__fileProperties, nullptr);
|
||||
AutoLilvNodes fileNodes = lilv_world_find_nodes(pWorld, uiNode, pHost->lilvUris->pipedalUI__fileProperties, nullptr);
|
||||
LILV_FOREACH(nodes, i, fileNodes)
|
||||
{
|
||||
const LilvNode *fileNode = lilv_nodes_get(fileNodes, i);
|
||||
@@ -49,7 +49,7 @@ PiPedalUI::PiPedalUI(PluginHost *pHost, const LilvNode *uiNode, const std::files
|
||||
pHost->LogWarning(SS("Failed to read pipedalui::fileProperties. " << e.what()));
|
||||
}
|
||||
}
|
||||
AutoLilvNodes frequencyPlotNodes = lilv_world_find_nodes(pWorld, uiNode, pHost->lilvUris.pipedalUI__frequencyPlot, nullptr);
|
||||
AutoLilvNodes frequencyPlotNodes = lilv_world_find_nodes(pWorld, uiNode, pHost->lilvUris->pipedalUI__frequencyPlot, nullptr);
|
||||
LILV_FOREACH(nodes, i, frequencyPlotNodes)
|
||||
{
|
||||
const LilvNode *frequencyPlotNode = lilv_nodes_get(frequencyPlotNodes, i);
|
||||
@@ -65,7 +65,7 @@ PiPedalUI::PiPedalUI(PluginHost *pHost, const LilvNode *uiNode, const std::files
|
||||
}
|
||||
}
|
||||
|
||||
AutoLilvNodes portNotifications = lilv_world_find_nodes(pWorld, uiNode, pHost->lilvUris.ui__portNotification, nullptr);
|
||||
AutoLilvNodes portNotifications = lilv_world_find_nodes(pWorld, uiNode, pHost->lilvUris->ui__portNotification, nullptr);
|
||||
LILV_FOREACH(nodes, i, portNotifications)
|
||||
{
|
||||
const LilvNode *portNotificationNode = lilv_nodes_get(portNotifications, i);
|
||||
@@ -88,7 +88,7 @@ UiFileType::UiFileType(PluginHost *pHost, const LilvNode *node)
|
||||
AutoLilvNode label = lilv_world_get(
|
||||
pWorld,
|
||||
node,
|
||||
pHost->lilvUris.rdfs__label,
|
||||
pHost->lilvUris->rdfs__label,
|
||||
nullptr);
|
||||
if (label)
|
||||
{
|
||||
@@ -101,7 +101,7 @@ UiFileType::UiFileType(PluginHost *pHost, const LilvNode *node)
|
||||
AutoLilvNode fileExtension = lilv_world_get(
|
||||
pWorld,
|
||||
node,
|
||||
pHost->lilvUris.pipedalUI__fileExtension,
|
||||
pHost->lilvUris->pipedalUI__fileExtension,
|
||||
nullptr);
|
||||
if (fileExtension)
|
||||
{
|
||||
@@ -114,7 +114,7 @@ UiFileType::UiFileType(PluginHost *pHost, const LilvNode *node)
|
||||
AutoLilvNode mimeType = lilv_world_get(
|
||||
pWorld,
|
||||
node,
|
||||
pHost->lilvUris.pipedalUI__mimeType,
|
||||
pHost->lilvUris->pipedalUI__mimeType,
|
||||
nullptr);
|
||||
if (mimeType)
|
||||
{
|
||||
@@ -140,7 +140,7 @@ UiFileProperty::UiFileProperty(PluginHost *pHost, const LilvNode *node, const st
|
||||
AutoLilvNode label = lilv_world_get(
|
||||
pWorld,
|
||||
node,
|
||||
pHost->lilvUris.rdfs__label,
|
||||
pHost->lilvUris->rdfs__label,
|
||||
nullptr);
|
||||
if (label)
|
||||
{
|
||||
@@ -153,7 +153,7 @@ UiFileProperty::UiFileProperty(PluginHost *pHost, const LilvNode *node, const st
|
||||
AutoLilvNode index = lilv_world_get(
|
||||
pWorld,
|
||||
node,
|
||||
pHost->lilvUris.lv2core__index,
|
||||
pHost->lilvUris->lv2core__index,
|
||||
nullptr);
|
||||
if (index)
|
||||
{
|
||||
@@ -167,7 +167,7 @@ UiFileProperty::UiFileProperty(PluginHost *pHost, const LilvNode *node, const st
|
||||
AutoLilvNode directory = lilv_world_get(
|
||||
pWorld,
|
||||
node,
|
||||
pHost->lilvUris.pipedalUI__directory,
|
||||
pHost->lilvUris->pipedalUI__directory,
|
||||
nullptr);
|
||||
if (directory)
|
||||
{
|
||||
@@ -185,7 +185,7 @@ UiFileProperty::UiFileProperty(PluginHost *pHost, const LilvNode *node, const st
|
||||
AutoLilvNode patchProperty = lilv_world_get(
|
||||
pWorld,
|
||||
node,
|
||||
pHost->lilvUris.pipedalUI__patchProperty,
|
||||
pHost->lilvUris->pipedalUI__patchProperty,
|
||||
nullptr);
|
||||
if (patchProperty)
|
||||
{
|
||||
@@ -196,13 +196,13 @@ UiFileProperty::UiFileProperty(PluginHost *pHost, const LilvNode *node, const st
|
||||
throw std::logic_error("PiPedal FileProperty is missing pipedalui:patchProperty value.");
|
||||
}
|
||||
|
||||
AutoLilvNode portGroup = lilv_world_get(pWorld,node,pHost->lilvUris.portgroups__group,nullptr);
|
||||
AutoLilvNode portGroup = lilv_world_get(pWorld,node,pHost->lilvUris->portgroups__group,nullptr);
|
||||
if (portGroup)
|
||||
{
|
||||
this->portGroup_ = portGroup.AsUri();
|
||||
}
|
||||
|
||||
this->fileTypes_ = UiFileType::GetArray(pHost, node, pHost->lilvUris.pipedalUI__fileTypes);
|
||||
this->fileTypes_ = UiFileType::GetArray(pHost, node, pHost->lilvUris->pipedalUI__fileTypes);
|
||||
}
|
||||
|
||||
std::vector<UiFileType> UiFileType::GetArray(PluginHost *pHost, const LilvNode *node, const LilvNode *uri)
|
||||
@@ -210,7 +210,7 @@ std::vector<UiFileType> UiFileType::GetArray(PluginHost *pHost, const LilvNode *
|
||||
std::vector<UiFileType> result;
|
||||
LilvWorld *pWorld = pHost->getWorld();
|
||||
|
||||
LilvNodes *fileTypeNodes = lilv_world_find_nodes(pWorld, node, pHost->lilvUris.pipedalUI__fileTypes, nullptr);
|
||||
LilvNodes *fileTypeNodes = lilv_world_find_nodes(pWorld, node, pHost->lilvUris->pipedalUI__fileTypes, nullptr);
|
||||
LILV_FOREACH(nodes, i, fileTypeNodes)
|
||||
{
|
||||
const LilvNode *fileTypeNode = lilv_nodes_get(fileTypeNodes, i);
|
||||
@@ -297,7 +297,7 @@ UiPortNotification::UiPortNotification(PluginHost *pHost, const LilvNode *node)
|
||||
// ]
|
||||
LilvWorld *pWorld = pHost->getWorld();
|
||||
|
||||
AutoLilvNode portIndex = lilv_world_get(pWorld, node, pHost->lilvUris.ui__portIndex, nullptr);
|
||||
AutoLilvNode portIndex = lilv_world_get(pWorld, node, pHost->lilvUris->ui__portIndex, nullptr);
|
||||
if (!portIndex)
|
||||
{
|
||||
this->portIndex_ = -1;
|
||||
@@ -306,7 +306,7 @@ UiPortNotification::UiPortNotification(PluginHost *pHost, const LilvNode *node)
|
||||
{
|
||||
this->portIndex_ = (uint32_t)lilv_node_as_int(portIndex);
|
||||
}
|
||||
AutoLilvNode symbol = lilv_world_get(pWorld, node, pHost->lilvUris.lv2__symbol, nullptr);
|
||||
AutoLilvNode symbol = lilv_world_get(pWorld, node, pHost->lilvUris->lv2__symbol, nullptr);
|
||||
if (!symbol)
|
||||
{
|
||||
this->symbol_ = "";
|
||||
@@ -315,7 +315,7 @@ UiPortNotification::UiPortNotification(PluginHost *pHost, const LilvNode *node)
|
||||
{
|
||||
this->symbol_ = symbol.AsString();
|
||||
}
|
||||
AutoLilvNode plugin = lilv_world_get(pWorld, node, pHost->lilvUris.ui__plugin, nullptr);
|
||||
AutoLilvNode plugin = lilv_world_get(pWorld, node, pHost->lilvUris->ui__plugin, nullptr);
|
||||
if (!plugin)
|
||||
{
|
||||
this->plugin_ = "";
|
||||
@@ -324,7 +324,7 @@ UiPortNotification::UiPortNotification(PluginHost *pHost, const LilvNode *node)
|
||||
{
|
||||
this->plugin_ = plugin.AsUri();
|
||||
}
|
||||
AutoLilvNode protocol = lilv_world_get(pWorld, node, pHost->lilvUris.ui__protocol, nullptr);
|
||||
AutoLilvNode protocol = lilv_world_get(pWorld, node, pHost->lilvUris->ui__protocol, nullptr);
|
||||
if (!protocol)
|
||||
{
|
||||
this->protocol_ = "";
|
||||
@@ -399,7 +399,7 @@ UiFrequencyPlot::UiFrequencyPlot(PluginHost*pHost, const LilvNode*node,
|
||||
AutoLilvNode patchProperty = lilv_world_get(
|
||||
pWorld,
|
||||
node,
|
||||
pHost->lilvUris.pipedalUI__patchProperty,
|
||||
pHost->lilvUris->pipedalUI__patchProperty,
|
||||
nullptr);
|
||||
if (patchProperty)
|
||||
{
|
||||
@@ -414,7 +414,7 @@ UiFrequencyPlot::UiFrequencyPlot(PluginHost*pHost, const LilvNode*node,
|
||||
AutoLilvNode index = lilv_world_get(
|
||||
pWorld,
|
||||
node,
|
||||
pHost->lilvUris.lv2core__index,
|
||||
pHost->lilvUris->lv2core__index,
|
||||
nullptr);
|
||||
if (index)
|
||||
{
|
||||
@@ -425,17 +425,17 @@ UiFrequencyPlot::UiFrequencyPlot(PluginHost*pHost, const LilvNode*node,
|
||||
this->index_ = -1;
|
||||
}
|
||||
|
||||
AutoLilvNode portGroup = lilv_world_get(pWorld,node,pHost->lilvUris.portgroups__group,nullptr);
|
||||
AutoLilvNode portGroup = lilv_world_get(pWorld,node,pHost->lilvUris->portgroups__group,nullptr);
|
||||
if (portGroup)
|
||||
{
|
||||
this->portGroup_ = portGroup.AsUri();
|
||||
}
|
||||
this->xLeft_ = GetFloat(pWorld,node,pHost->lilvUris.pipedalUI__xLeft,100);
|
||||
this->xRight_ = GetFloat(pWorld,node,pHost->lilvUris.pipedalUI__xRight,22000);
|
||||
this->yTop_ = GetFloat(pWorld,node,pHost->lilvUris.pipedalUI__yTop,5);
|
||||
this->yBottom_ = GetFloat(pWorld,node,pHost->lilvUris.pipedalUI__yBottom,-35);
|
||||
this->xLog_ = GetFloat(pWorld,node,pHost->lilvUris.pipedalUI__xLog,-35);
|
||||
this->width_ = GetFloat(pWorld,node,pHost->lilvUris.pipedalUI__width,60);
|
||||
this->xLeft_ = GetFloat(pWorld,node,pHost->lilvUris->pipedalUI__xLeft,100);
|
||||
this->xRight_ = GetFloat(pWorld,node,pHost->lilvUris->pipedalUI__xRight,22000);
|
||||
this->yTop_ = GetFloat(pWorld,node,pHost->lilvUris->pipedalUI__yTop,5);
|
||||
this->yBottom_ = GetFloat(pWorld,node,pHost->lilvUris->pipedalUI__yBottom,-35);
|
||||
this->xLog_ = GetFloat(pWorld,node,pHost->lilvUris->pipedalUI__xLog,-35);
|
||||
this->width_ = GetFloat(pWorld,node,pHost->lilvUris->pipedalUI__width,60);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+37
-34
@@ -281,9 +281,11 @@ void PluginHost::OnConfigurationChanged(const JackConfiguration &configuration,
|
||||
|
||||
PluginHost::~PluginHost()
|
||||
{
|
||||
lilvUris.Free();
|
||||
free_world();
|
||||
delete lilvUris;
|
||||
lilvUris = nullptr;
|
||||
delete urids;
|
||||
urids = nullptr;
|
||||
free_world();
|
||||
}
|
||||
|
||||
void PluginHost::free_world()
|
||||
@@ -408,7 +410,8 @@ void PluginHost::Load(const char *lv2Path)
|
||||
// LilvNode*lv2_path = lilv_new_file_uri(pWorld,NULL,lv2Path);
|
||||
// lilv_world_set_option(world,LILV_OPTION_LV2_PATH,lv)
|
||||
|
||||
lilvUris.Initialize(pWorld);
|
||||
lilvUris = new LilvUris();
|
||||
lilvUris->Initialize(pWorld);
|
||||
|
||||
const LilvPlugins *plugins = lilv_world_get_all_plugins(pWorld);
|
||||
|
||||
@@ -549,7 +552,7 @@ const char *PluginHost::RDFS__range = "http://www.w3.org/2000/01/rdf-schema#"
|
||||
LilvNode *PluginHost::get_comment(const std::string &uri)
|
||||
{
|
||||
AutoLilvNode uriNode = lilv_new_uri(pWorld, uri.c_str());
|
||||
LilvNode *result = lilv_world_get(pWorld, uriNode, lilvUris.rdfs__Comment, nullptr);
|
||||
LilvNode *result = lilv_world_get(pWorld, uriNode, lilvUris->rdfs__Comment, nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -560,7 +563,7 @@ static bool ports_sort_compare(std::shared_ptr<Lv2PortInfo> &p1, const std::shar
|
||||
|
||||
bool Lv2PluginInfo::HasFactoryPresets(PluginHost *lv2Host, const LilvPlugin *plugin)
|
||||
{
|
||||
AutoLilvNodes nodes = lilv_plugin_get_related(plugin, lv2Host->lilvUris.presets__preset);
|
||||
AutoLilvNodes nodes = lilv_plugin_get_related(plugin, lv2Host->lilvUris->presets__preset);
|
||||
bool result = false;
|
||||
LILV_FOREACH(nodes, iNode, nodes)
|
||||
{
|
||||
@@ -583,7 +586,7 @@ std::shared_ptr<PiPedalUI> Lv2PluginInfo::FindWritablePathProperties(PluginHost
|
||||
|
||||
LilvWorld *pWorld = lv2Host->getWorld();
|
||||
AutoLilvNode pluginUri = lilv_plugin_get_uri(pPlugin);
|
||||
AutoLilvNodes patchWritables = lilv_world_find_nodes(pWorld, pluginUri, lv2Host->lilvUris.patch__writable, nullptr);
|
||||
AutoLilvNodes patchWritables = lilv_world_find_nodes(pWorld, pluginUri, lv2Host->lilvUris->patch__writable, nullptr);
|
||||
|
||||
std::vector<UiFileProperty::ptr> fileProperties;
|
||||
|
||||
@@ -593,12 +596,12 @@ std::shared_ptr<PiPedalUI> Lv2PluginInfo::FindWritablePathProperties(PluginHost
|
||||
if (propertyUri)
|
||||
{
|
||||
// a lv2:Parameter?
|
||||
if (lilv_world_ask(pWorld, propertyUri, lv2Host->lilvUris.isA, lv2Host->lilvUris.lv2core__Parameter))
|
||||
if (lilv_world_ask(pWorld, propertyUri, lv2Host->lilvUris->isA, lv2Host->lilvUris->lv2core__Parameter))
|
||||
{
|
||||
// rfs:range atom:Path?
|
||||
if (lilv_world_ask(pWorld, propertyUri, lv2Host->lilvUris.rdfs__range, lv2Host->lilvUris.atom__Path))
|
||||
if (lilv_world_ask(pWorld, propertyUri, lv2Host->lilvUris->rdfs__range, lv2Host->lilvUris->atom__Path))
|
||||
{
|
||||
AutoLilvNode label = lilv_world_get(pWorld, propertyUri, lv2Host->lilvUris.rdfs__label, nullptr);
|
||||
AutoLilvNode label = lilv_world_get(pWorld, propertyUri, lv2Host->lilvUris->rdfs__label, nullptr);
|
||||
std::string strLabel = label.AsString();
|
||||
if (strLabel.length() != 0)
|
||||
{
|
||||
@@ -611,7 +614,7 @@ std::shared_ptr<PiPedalUI> Lv2PluginInfo::FindWritablePathProperties(PluginHost
|
||||
std::make_shared<UiFileProperty>(
|
||||
strLabel, propertyUri.AsUri(), lv2DirectoryName);
|
||||
|
||||
AutoLilvNodes dc_types = lilv_world_find_nodes(pWorld, propertyUri, lv2Host->lilvUris.dc__format, nullptr);
|
||||
AutoLilvNodes dc_types = lilv_world_find_nodes(pWorld, propertyUri, lv2Host->lilvUris->dc__format, nullptr);
|
||||
LILV_FOREACH(nodes, i, dc_types)
|
||||
{
|
||||
AutoLilvNode dc_type = lilv_nodes_get(dc_types, i);
|
||||
@@ -657,10 +660,10 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP
|
||||
AutoLilvNode name = (lilv_plugin_get_name(pPlugin));
|
||||
this->name_ = nodeAsString(name);
|
||||
|
||||
AutoLilvNode brand = lilv_world_get(pWorld,plugUri,lv2Host->lilvUris.mod__brand, nullptr);
|
||||
AutoLilvNode brand = lilv_world_get(pWorld,plugUri,lv2Host->lilvUris->mod__brand, nullptr);
|
||||
this->brand_ = nodeAsString(brand);
|
||||
|
||||
AutoLilvNode label = lilv_world_get(pWorld,plugUri,lv2Host->lilvUris.mod__label, nullptr);
|
||||
AutoLilvNode label = lilv_world_get(pWorld,plugUri,lv2Host->lilvUris->mod__label, nullptr);
|
||||
this->label_ = nodeAsString(label);
|
||||
if (label_.length() == 0)
|
||||
{
|
||||
@@ -738,7 +741,7 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP
|
||||
AutoLilvNode pipedalUINode = lilv_world_get(
|
||||
pWorld,
|
||||
lilv_plugin_get_uri(pPlugin),
|
||||
lv2Host->lilvUris.pipedalUI__ui,
|
||||
lv2Host->lilvUris->pipedalUI__ui,
|
||||
nullptr);
|
||||
if (pipedalUINode)
|
||||
{
|
||||
@@ -872,12 +875,12 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP
|
||||
if (default_value_ < min_value_)
|
||||
default_value_ = min_value_;
|
||||
|
||||
this->is_logarithmic_ = lilv_port_has_property(plugin, pPort, host->lilvUris.port_logarithmic);
|
||||
this->is_logarithmic_ = lilv_port_has_property(plugin, pPort, host->lilvUris->port_logarithmic);
|
||||
|
||||
// typo in invada plugins.
|
||||
this->is_logarithmic_ |= lilv_port_has_property(plugin, pPort, host->lilvUris.invada_portprops__logarithmic);
|
||||
this->is_logarithmic_ |= lilv_port_has_property(plugin, pPort, host->lilvUris->invada_portprops__logarithmic);
|
||||
|
||||
AutoLilvNodes priority_nodes = lilv_port_get_value(plugin, pPort, host->lilvUris.port__display_priority);
|
||||
AutoLilvNodes priority_nodes = lilv_port_get_value(plugin, pPort, host->lilvUris->port__display_priority);
|
||||
|
||||
this->display_priority_ = -1;
|
||||
if (priority_nodes)
|
||||
@@ -889,7 +892,7 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP
|
||||
}
|
||||
}
|
||||
|
||||
AutoLilvNodes range_steps_nodes = lilv_port_get_value(plugin, pPort, host->lilvUris.port_range_steps);
|
||||
AutoLilvNodes range_steps_nodes = lilv_port_get_value(plugin, pPort, host->lilvUris->port_range_steps);
|
||||
this->range_steps_ = 0;
|
||||
if (range_steps_nodes)
|
||||
{
|
||||
@@ -899,13 +902,13 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP
|
||||
this->range_steps_ = lilv_node_as_int(range_steps_node);
|
||||
}
|
||||
}
|
||||
this->integer_property_ = lilv_port_has_property(plugin, pPort, host->lilvUris.integer_property_uri);
|
||||
this->integer_property_ = lilv_port_has_property(plugin, pPort, host->lilvUris->integer_property_uri);
|
||||
|
||||
this->enumeration_property_ = lilv_port_has_property(plugin, pPort, host->lilvUris.enumeration_property_uri);
|
||||
this->enumeration_property_ = lilv_port_has_property(plugin, pPort, host->lilvUris->enumeration_property_uri);
|
||||
|
||||
this->toggled_property_ = lilv_port_has_property(plugin, pPort, host->lilvUris.core__toggled);
|
||||
this->not_on_gui_ = lilv_port_has_property(plugin, pPort, host->lilvUris.portprops__not_on_gui_property_uri);
|
||||
this->connection_optional_ = lilv_port_has_property(plugin, pPort, host->lilvUris.core__connectionOptional);
|
||||
this->toggled_property_ = lilv_port_has_property(plugin, pPort, host->lilvUris->core__toggled);
|
||||
this->not_on_gui_ = lilv_port_has_property(plugin, pPort, host->lilvUris->portprops__not_on_gui_property_uri);
|
||||
this->connection_optional_ = lilv_port_has_property(plugin, pPort, host->lilvUris->core__connectionOptional);
|
||||
|
||||
LilvScalePoints *pScalePoints = lilv_port_get_scale_points(plugin, pPort);
|
||||
LILV_FOREACH(scale_points, iSP, pScalePoints)
|
||||
@@ -929,16 +932,16 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP
|
||||
is_atom_port_ = is_a(host, LV2_ATOM__AtomPort);
|
||||
is_cv_port_ = is_a(host, LV2_CORE__CVPort);
|
||||
|
||||
supports_midi_ = lilv_port_supports_event(plugin, pPort, host->lilvUris.midi__event);
|
||||
supports_time_position_ = lilv_port_supports_event(plugin, pPort, host->lilvUris.time_Position);
|
||||
supports_midi_ = lilv_port_supports_event(plugin, pPort, host->lilvUris->midi__event);
|
||||
supports_time_position_ = lilv_port_supports_event(plugin, pPort, host->lilvUris->time_Position);
|
||||
|
||||
AutoLilvNode designationValue = lilv_port_get(plugin, pPort, host->lilvUris.core__designation);
|
||||
AutoLilvNode designationValue = lilv_port_get(plugin, pPort, host->lilvUris->core__designation);
|
||||
designation_ = nodeAsString(designationValue);
|
||||
|
||||
AutoLilvNode portGroup_value = lilv_port_get(plugin, pPort, host->lilvUris.portgroups__group);
|
||||
AutoLilvNode portGroup_value = lilv_port_get(plugin, pPort, host->lilvUris->portgroups__group);
|
||||
port_group_ = nodeAsString(portGroup_value);
|
||||
|
||||
AutoLilvNode unitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris.units__unit);
|
||||
AutoLilvNode unitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris->units__unit);
|
||||
if (unitsValueUri)
|
||||
{
|
||||
this->units_ = UriToUnits(nodeAsString(unitsValueUri));
|
||||
@@ -946,7 +949,7 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP
|
||||
else
|
||||
{
|
||||
// invada plugins use the wrong URI.
|
||||
AutoLilvNode invadaUnitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris.invada_units__unit);
|
||||
AutoLilvNode invadaUnitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris->invada_units__unit);
|
||||
if (invadaUnitsValueUri)
|
||||
{
|
||||
std::string uri = nodeAsString(invadaUnitsValueUri);
|
||||
@@ -964,10 +967,10 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP
|
||||
}
|
||||
}
|
||||
|
||||
AutoLilvNode commentNode = lilv_port_get(plugin, pPort, host->lilvUris.rdfs__Comment);
|
||||
AutoLilvNode commentNode = lilv_port_get(plugin, pPort, host->lilvUris->rdfs__Comment);
|
||||
this->comment_ = nodeAsString(commentNode);
|
||||
|
||||
AutoLilvNode bufferType = lilv_port_get(plugin, pPort, host->lilvUris.atom__bufferType);
|
||||
AutoLilvNode bufferType = lilv_port_get(plugin, pPort, host->lilvUris->atom__bufferType);
|
||||
|
||||
this->buffer_type_ = "";
|
||||
if (bufferType)
|
||||
@@ -1186,7 +1189,7 @@ std::vector<ControlValue> PluginHost::LoadFactoryPluginPreset(
|
||||
throw PiPedalStateException("No such plugin.");
|
||||
}
|
||||
|
||||
LilvNodes *presets = lilv_plugin_get_related(plugin, lilvUris.presets__preset);
|
||||
LilvNodes *presets = lilv_plugin_get_related(plugin, lilvUris->presets__preset);
|
||||
LILV_FOREACH(nodes, i, presets)
|
||||
{
|
||||
const LilvNode *preset = lilv_nodes_get(presets, i);
|
||||
@@ -1268,7 +1271,7 @@ PluginPresets PluginHost::GetFactoryPluginPresets(const std::string &pluginUri)
|
||||
PluginPresets result;
|
||||
result.pluginUri_ = pluginUri;
|
||||
|
||||
LilvNodes *presets = lilv_plugin_get_related(plugin, lilvUris.presets__preset);
|
||||
LilvNodes *presets = lilv_plugin_get_related(plugin, lilvUris->presets__preset);
|
||||
LILV_FOREACH(nodes, i, presets)
|
||||
{
|
||||
const LilvNode *preset = lilv_nodes_get(presets, i);
|
||||
@@ -1344,9 +1347,9 @@ Lv2PortGroup::Lv2PortGroup(PluginHost *lv2Host, const std::string &groupUri)
|
||||
|
||||
this->uri_ = groupUri;
|
||||
AutoLilvNode uri = lilv_new_uri(pWorld, groupUri.c_str());
|
||||
LilvNode *symbolNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris.lv2core__symbol, nullptr);
|
||||
LilvNode *symbolNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris->lv2core__symbol, nullptr);
|
||||
symbol_ = nodeAsString(symbolNode);
|
||||
LilvNode *nameNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris.lv2core__name, nullptr);
|
||||
LilvNode *nameNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris->lv2core__name, nullptr);
|
||||
name_ = nodeAsString(nameNode);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -727,7 +727,7 @@ namespace pipedal
|
||||
|
||||
AutoLilvNode dc__format;
|
||||
};
|
||||
LilvUris lilvUris;
|
||||
LilvUris* lilvUris = nullptr;
|
||||
|
||||
private:
|
||||
bool vst3Enabled = true;
|
||||
|
||||
-249
@@ -1,249 +0,0 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "pch.h"
|
||||
#include "RegDb.hpp"
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <unordered_set>
|
||||
#include <arpa/inet.h>
|
||||
#include "PiPedalException.hpp"
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define REGDB_MAGIC 0x52474442
|
||||
#define REGDB_VERSION 19
|
||||
|
||||
|
||||
using namespace pipedal;
|
||||
using namespace std;
|
||||
|
||||
struct RegDbFileHeader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t countryOffset;
|
||||
uint32_t countryCount;
|
||||
uint32_t signatureLength;
|
||||
|
||||
void toNs() {
|
||||
this->magic = htonl(this->magic);
|
||||
this->version = htonl(this->version);
|
||||
this->countryOffset = htonl(this->countryOffset);
|
||||
this->countryCount = htonl(this->countryCount);
|
||||
this->signatureLength = htonl(this->signatureLength);
|
||||
}
|
||||
};
|
||||
|
||||
struct CountryHeader {
|
||||
char alpha2[2];
|
||||
uint8_t pad;
|
||||
uint8_t dfsRegion; // first 2 bits only.
|
||||
uint32_t rulesOffset;
|
||||
|
||||
void toNs() {
|
||||
this->rulesOffset = htonl(rulesOffset);
|
||||
}
|
||||
|
||||
};
|
||||
struct RulesCollection {
|
||||
uint32_t ruleCount;
|
||||
uint32_t ruleOffsets[1];
|
||||
|
||||
void toNs() {
|
||||
this->ruleCount = htonl(this->ruleCount);
|
||||
uint32_t *p = ruleOffsets;
|
||||
for (uint32_t i = 0; i < this->ruleCount; ++i)
|
||||
{
|
||||
*p = htonl(*p);
|
||||
++p;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Rule {
|
||||
uint32_t frequencyRangeOffset;
|
||||
uint32_t powerRuleOffset;
|
||||
uint32_t flags;
|
||||
|
||||
void toNs() {
|
||||
frequencyRangeOffset = htonl(frequencyRangeOffset);
|
||||
powerRuleOffset = htonl(powerRuleOffset);
|
||||
flags = htonl(flags);
|
||||
}
|
||||
};
|
||||
|
||||
struct FrequencyRange {
|
||||
uint32_t startFrequency; // in khz.
|
||||
uint32_t endFrequency; // in khz.
|
||||
uint32_t maxBandwidth; // in khz.
|
||||
void toNs() {
|
||||
startFrequency = htonl(startFrequency);
|
||||
endFrequency = htonl(endFrequency);
|
||||
maxBandwidth = htonl(maxBandwidth);
|
||||
}
|
||||
};
|
||||
struct PowerRule {
|
||||
uint32_t maximumAntennaGain;
|
||||
uint32_t maximumEirp;
|
||||
void toNs() {
|
||||
maximumAntennaGain = htonl(maximumAntennaGain);
|
||||
maximumEirp = htonl(maximumEirp);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
const char*filePaths[] =
|
||||
{
|
||||
"/usr/lib/crda/regulatory.bin"
|
||||
};
|
||||
|
||||
static std::filesystem::path getFilePath()
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(filePaths)/sizeof(filePaths[0]); ++i)
|
||||
{
|
||||
std::filesystem::path path(filePaths[i]);
|
||||
if (std::filesystem::exists(path)) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
throw PiPedalException("Could not find 'regulatory.bin' file.");
|
||||
}
|
||||
|
||||
RegDb::~RegDb()
|
||||
{
|
||||
delete[] pData;
|
||||
}
|
||||
|
||||
RegDb::RegDb()
|
||||
:pData(nullptr)
|
||||
{
|
||||
RegDbFileHeader header;
|
||||
std::filesystem::path path = getFilePath();
|
||||
ifstream f(path);
|
||||
if (!f.is_open()) {
|
||||
stringstream s;
|
||||
s << "Can't read " << path;
|
||||
throw PiPedalException(s.str());
|
||||
}
|
||||
size_t fileSize = std::filesystem::file_size(path);
|
||||
this->pData = new uint8_t[fileSize];
|
||||
f.read((char*)this->pData,fileSize);
|
||||
|
||||
// convert from big-endian to local byte order;
|
||||
RegDbFileHeader *fileHeader = (RegDbFileHeader*)pData;
|
||||
fileHeader->toNs();
|
||||
if (fileHeader->magic != REGDB_MAGIC)
|
||||
{
|
||||
throw PiPedalException("Invalid file format.");
|
||||
}
|
||||
std::unordered_set<uint32_t> visited;
|
||||
|
||||
CountryHeader*countryHeader = (CountryHeader*)(pData + fileHeader->countryOffset);
|
||||
for (uint32_t i = 0; i < fileHeader->countryCount; ++i)
|
||||
{
|
||||
countryHeader->toNs();
|
||||
if (visited.count(countryHeader->rulesOffset) == 0)
|
||||
{
|
||||
visited.insert(countryHeader->rulesOffset);
|
||||
RulesCollection*pRules = (RulesCollection*)(pData + countryHeader->rulesOffset);
|
||||
pRules->toNs();
|
||||
for (uint32_t nRule = 0; nRule < pRules->ruleCount; ++nRule)
|
||||
{
|
||||
uint32_t ruleOffset = pRules->ruleOffsets[nRule];
|
||||
if (visited.count(ruleOffset) == 0)
|
||||
{
|
||||
visited.insert(ruleOffset);
|
||||
Rule *pRule = (Rule*)(pData + ruleOffset);
|
||||
pRule->toNs();
|
||||
|
||||
if (visited.count(pRule->frequencyRangeOffset) == 0)
|
||||
{
|
||||
visited.insert(pRule->frequencyRangeOffset);
|
||||
|
||||
FrequencyRange*pf = (FrequencyRange*)(pData + pRule->frequencyRangeOffset);
|
||||
pf->toNs();
|
||||
}
|
||||
if (visited.count(pRule->powerRuleOffset) == 0)
|
||||
{
|
||||
visited.insert(pRule->powerRuleOffset);
|
||||
|
||||
PowerRule *pr = (PowerRule*)(pData + pRule->powerRuleOffset);
|
||||
pr->toNs();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
++countryHeader;
|
||||
}
|
||||
|
||||
}
|
||||
bool CrdbRules::GetFrequencyRule(uint32_t frequencyRuleMHz, FrequencyRule*pResult)
|
||||
{
|
||||
memset(pResult,0,sizeof(FrequencyRule));
|
||||
RulesCollection*pRules = (RulesCollection*)(regDb->pData + countryOffset);
|
||||
uint32_t frequencyRuleKHz = frequencyRuleMHz*1000;
|
||||
for (uint32_t i = 0; i < pRules->ruleCount; ++i)
|
||||
{
|
||||
Rule *pRule = (Rule*)(regDb->pData + pRules->ruleOffsets[i]);
|
||||
FrequencyRange*pf = (FrequencyRange*)(regDb->pData + pRule->frequencyRangeOffset);
|
||||
if (frequencyRuleKHz >= pf->startFrequency && frequencyRuleKHz < pf->endFrequency)
|
||||
{
|
||||
PowerRule *pr = (PowerRule*)(regDb->pData + pRule->powerRuleOffset);
|
||||
pResult->flags = (RegRuleFlags)(pRule->flags);
|
||||
pResult->maximumAntennaGain = pr->maximumAntennaGain;
|
||||
pResult->maximumEirp = pr->maximumEirp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
CrdbRules*RegDb::GetCrdbRules(const char*countryCodeIso633)
|
||||
{
|
||||
const char c0 = countryCodeIso633[0];
|
||||
if (c0 == '\0') return nullptr;
|
||||
const char c1 = countryCodeIso633[1];
|
||||
|
||||
RegDbFileHeader *fileHeader = (RegDbFileHeader*)pData;
|
||||
CountryHeader*countryHeader = (CountryHeader*)(pData + fileHeader->countryOffset);
|
||||
for (uint32_t i = 0; i < fileHeader->countryCount; ++i)
|
||||
{
|
||||
if (countryHeader->alpha2[0] == c0 && countryHeader->alpha2[1] == c1) {
|
||||
return new CrdbRules(
|
||||
this,
|
||||
countryHeader->rulesOffset,
|
||||
(DfsRegion)(countryHeader->dfsRegion & 0x03)
|
||||
);
|
||||
}
|
||||
++countryHeader;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CrdbRules::CrdbRules(RegDb *regDb,uint32_t countryOffset, DfsRegion dfsRegion )
|
||||
: regDb(regDb),
|
||||
countryOffset(countryOffset),
|
||||
dfsRegion(dfsRegion)
|
||||
{
|
||||
}
|
||||
|
||||
DfsRegion CrdbRules::GetDfsRegion() const {
|
||||
return this->dfsRegion;
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace pipedal {
|
||||
|
||||
// sync with <linux/nl80211.h
|
||||
enum class RegRuleFlags {
|
||||
NO_OFDM = 1<<0,
|
||||
NO_CCK = 1<<1,
|
||||
NO_INDOOR = 1<<2,
|
||||
NO_OUTDOOR = 1<<3,
|
||||
DFS = 1<<4,
|
||||
PTP_ONLY = 1<<5,
|
||||
PTMP_ONLY = 1<<6,
|
||||
NO_IR = 1<<7,
|
||||
__NO_IBSS = 1<<8,
|
||||
AUTO_BW = 1<<11,
|
||||
IR_CONCURRENT = 1<<12,
|
||||
NO_HT40MINUS = 1<<13,
|
||||
NO_HT40PLUS = 1<<14,
|
||||
NO_80MHZ = 1<<15,
|
||||
NO_160MHZ = 1<<16,
|
||||
NO_HE = 1<<17,
|
||||
};
|
||||
|
||||
enum class DfsRegion {
|
||||
Unset = 0,
|
||||
Fcc = 1,
|
||||
Etsi = 2,
|
||||
Japan = 3
|
||||
};
|
||||
|
||||
struct FrequencyRule {
|
||||
uint32_t maximumAntennaGain; // in mBiu (100*dBi)
|
||||
uint32_t maximumEirp; // in mBm (100*dBm)
|
||||
RegRuleFlags flags;
|
||||
|
||||
bool hasFlag(RegRuleFlags flag) const { return (((int)flag) & (int)this->flags) != 0; }
|
||||
};
|
||||
|
||||
class RegDb;
|
||||
|
||||
class CrdbRules {
|
||||
friend class RegDb;
|
||||
RegDb *regDb = nullptr;
|
||||
uint32_t countryOffset;
|
||||
DfsRegion dfsRegion;
|
||||
CrdbRules(RegDb *regDb,uint32_t countryOffset, DfsRegion dfsRegion );
|
||||
public:
|
||||
virtual DfsRegion GetDfsRegion() const;
|
||||
virtual bool GetFrequencyRule(uint32_t frequencyMHz,FrequencyRule*pResult);
|
||||
};
|
||||
|
||||
class RegDb {
|
||||
private:
|
||||
friend class CrdbRules;
|
||||
uint8_t*pData;
|
||||
|
||||
public:
|
||||
RegDb();
|
||||
~RegDb();
|
||||
CrdbRules *GetCrdbRules(const char*countryCodeIso6333);
|
||||
bool isValid() const { return pData != nullptr; }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <grp.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <tuple>
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
using namespace std;
|
||||
using namespace config_serializer;
|
||||
|
||||
|
||||
const char ServiceConfiguration::DEVICEID_FILE_NAME[] = "/etc/pipedal/config/service.conf";
|
||||
|
||||
#define SERIALIZER_ENTRY(MEMBER_NAME) \
|
||||
new ConfigSerializer<ServiceConfiguration,decltype(ServiceConfiguration::MEMBER_NAME)>(#MEMBER_NAME, &ServiceConfiguration::MEMBER_NAME, "")
|
||||
|
||||
|
||||
static p2p::autoptr_vector<ConfigSerializerBase<ServiceConfiguration>>
|
||||
deviceIdSerializers
|
||||
{
|
||||
SERIALIZER_ENTRY(uuid),
|
||||
SERIALIZER_ENTRY(deviceName),
|
||||
SERIALIZER_ENTRY(server_port)
|
||||
};
|
||||
|
||||
|
||||
ServiceConfiguration::ServiceConfiguration()
|
||||
: base(deviceIdSerializers)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ServiceConfiguration::Load()
|
||||
{
|
||||
base::Load(DEVICEID_FILE_NAME,false);
|
||||
}
|
||||
void ServiceConfiguration::Save()
|
||||
{
|
||||
base::Save(DEVICEID_FILE_NAME);
|
||||
{
|
||||
struct group *group_;
|
||||
group_ = getgrnam("pipedal_d");
|
||||
if (group_ == nullptr)
|
||||
{
|
||||
throw logic_error("Group not found: pipedal_d");
|
||||
}
|
||||
std::ignore = chown(DEVICEID_FILE_NAME,-1,group_->gr_gid);
|
||||
std::ignore = chmod(DEVICEID_FILE_NAME, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "ConfigSerializer.h"
|
||||
|
||||
namespace pipedal {
|
||||
using namespace config_serializer;
|
||||
|
||||
class ServiceConfiguration: protected ConfigSerializable<ServiceConfiguration> {
|
||||
public:
|
||||
using base = ConfigSerializable<ServiceConfiguration>;
|
||||
|
||||
ServiceConfiguration();
|
||||
|
||||
static const char DEVICEID_FILE_NAME[];
|
||||
|
||||
void Load();
|
||||
void Save();
|
||||
|
||||
|
||||
std::string uuid;
|
||||
std::string deviceName = "PiPedal";
|
||||
uint32_t server_port = 80;
|
||||
};
|
||||
}
|
||||
+269
-112
@@ -17,7 +17,6 @@
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ss.hpp"
|
||||
|
||||
#include "P2pConfigFiles.hpp"
|
||||
@@ -44,17 +43,46 @@ using namespace std;
|
||||
static char DNSMASQ_APD_PATH[] = "/etc/dnsmasq.d/30-pipedal-apd.conf";
|
||||
|
||||
static char DNSMASQ_P2P_SOURCE_PATH[] = "/etc/pipedal/config/templates/30-pipedal-p2p.conf";
|
||||
|
||||
static char DNSMASQ_P2P_PATH[] = "/etc/dnsmasq.d/30-pipedal-p2p.conf";
|
||||
|
||||
static char NETWORK_MANAGER_DNSMASQ_P2P_SOURCE_PATH[] = "/etc/pipedal/config/templates/30-pipedal-p2p-dnsmasq-nm.conf";
|
||||
static char NETWORK_MANAGER_DNSMASQ_P2P_PATH[] = "/etc/dnsmasq.d/30-pipedal-p2p-dnsmasq-nm.conf";
|
||||
|
||||
static char NETWORK_MANAGER_SERVICE_PATH[] = "/usr/lib/systemd/system/NetworkManager.service";
|
||||
static char NETWORK_MANAGER_CONFIG_PATH[] = "/etc/NetworkManager/conf.d/50-pipedal-nm.conf";
|
||||
static char NETWORK_MANAGER_CONFIG_SOURCE_PATH[] = "/etc/pipedal/config/templates/50-pipedal-nm.conf";
|
||||
|
||||
//forward declarations
|
||||
static void ConfigDhcpcdForP2p();
|
||||
static void applyNetworkManagerP2pConfig();
|
||||
static void applyDnsmasqP2pConfig();
|
||||
|
||||
|
||||
static std::string gWlanAddress = "wlan0";
|
||||
|
||||
void pipedal::SetWifiConfigWLanAddress(const std::string &wLanAddress)
|
||||
{
|
||||
gWlanAddress = wLanAddress;
|
||||
}
|
||||
const std::string &pipedal::GetWifiConfigWlanAddress()
|
||||
{
|
||||
return gWlanAddress;
|
||||
}
|
||||
|
||||
bool pipedal::UsingNetworkManager()
|
||||
{
|
||||
// does the neworkManager service path exists?
|
||||
return std::filesystem::exists(NETWORK_MANAGER_SERVICE_PATH);
|
||||
}
|
||||
|
||||
static bool IsApdInstalled()
|
||||
{
|
||||
|
||||
|
||||
return std::filesystem::exists(DNSMASQ_APD_PATH);
|
||||
}
|
||||
static bool IsP2pInstalled()
|
||||
{
|
||||
return std::filesystem::exists(DNSMASQ_P2P_PATH);
|
||||
return std::filesystem::exists(DNSMASQ_P2P_PATH) | std::filesystem::exists(NETWORK_MANAGER_DNSMASQ_P2P_PATH);
|
||||
}
|
||||
|
||||
static void restoreApdDhcpdConfFile()
|
||||
@@ -112,7 +140,7 @@ static void restoreP2pDhcpdConfFile()
|
||||
dhcpcd.SetLineValue(line, hostNameLine);
|
||||
}
|
||||
|
||||
line = dhcpcd.GetLineNumber("interface p2p-wlan0-0");
|
||||
line = dhcpcd.GetLineNumber("interface p2p-wlan0-0"); // pre bookworm.
|
||||
if (line != -1)
|
||||
{
|
||||
dhcpcd.EraseLine(line);
|
||||
@@ -129,6 +157,14 @@ static void restoreP2pDhcpdConfFile()
|
||||
}
|
||||
}
|
||||
}
|
||||
line = dhcpcd.GetLineNumberStartingWith("allowinterfaces p2p-"); // bookworm
|
||||
if (line != -1)
|
||||
{
|
||||
while (line < dhcpcd.GetLineCount())
|
||||
{
|
||||
dhcpcd.EraseLine(line);
|
||||
}
|
||||
}
|
||||
dhcpcd.Save(dhcpcdConfig);
|
||||
}
|
||||
}
|
||||
@@ -143,10 +179,19 @@ static void restoreApdDnsmasqConfFile()
|
||||
}
|
||||
static void restoreP2pDnsmasqConfFile()
|
||||
{
|
||||
std::filesystem::path path(DNSMASQ_P2P_PATH);
|
||||
if (std::filesystem::exists(path))
|
||||
{
|
||||
std::filesystem::remove(path);
|
||||
std::filesystem::path path(DNSMASQ_P2P_PATH);
|
||||
if (std::filesystem::exists(path))
|
||||
{
|
||||
std::filesystem::remove(path);
|
||||
}
|
||||
}
|
||||
{
|
||||
std::filesystem::path path(NETWORK_MANAGER_DNSMASQ_P2P_PATH);
|
||||
if (std::filesystem::exists(path))
|
||||
{
|
||||
std::filesystem::remove(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +213,89 @@ static void UninstallHostApd()
|
||||
}
|
||||
}
|
||||
|
||||
static void InstallP2p(const WifiDirectConfigSettings &settings)
|
||||
{
|
||||
cout << "Enabling P2P" << endl;
|
||||
|
||||
settings.Save();
|
||||
// ******************* device_uuid
|
||||
|
||||
ServiceConfiguration deviceIdFile;
|
||||
deviceIdFile.Load();
|
||||
deviceIdFile.deviceName = settings.hotspotName_;
|
||||
deviceIdFile.Save();
|
||||
|
||||
// ******************** dsnmasq ******
|
||||
|
||||
std::filesystem::remove(DNSMASQ_P2P_PATH);
|
||||
std::filesystem::remove(NETWORK_MANAGER_DNSMASQ_P2P_PATH);
|
||||
|
||||
// dnsmasq
|
||||
if (UsingNetworkManager())
|
||||
{
|
||||
applyDnsmasqP2pConfig();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::filesystem::copy_file(DNSMASQ_P2P_SOURCE_PATH, DNSMASQ_P2P_PATH);
|
||||
}
|
||||
|
||||
// ****** dhcpd.conf ***/
|
||||
std::filesystem::path dhcpcdConfig("/etc/dhcpcd.conf");
|
||||
if (std::filesystem::exists(dhcpcdConfig))
|
||||
{
|
||||
ConfigDhcpcdForP2p();
|
||||
}
|
||||
|
||||
/* Network manager */
|
||||
if (UsingNetworkManager())
|
||||
{
|
||||
applyNetworkManagerP2pConfig();
|
||||
}
|
||||
|
||||
// **************** start services ************
|
||||
|
||||
cout << "Starting P2P services." << endl;
|
||||
sysExec("rfkill unblock wlan");
|
||||
|
||||
sysExec(SYSTEMCTL_BIN " daemon-reload");
|
||||
sysExec(SYSTEMCTL_BIN " stop pipedal_nm_p2pd");
|
||||
sysExec(SYSTEMCTL_BIN " stop pipedal_p2pd");
|
||||
sysExec(SYSTEMCTL_BIN " stop dhcpcd");
|
||||
if (UsingNetworkManager())
|
||||
{
|
||||
sysExec(SYSTEMCTL_BIN " restart NetworkManager");
|
||||
// NetworkManager takes care of resetting wpa_supplicant.
|
||||
}
|
||||
else
|
||||
{
|
||||
sysExec(SYSTEMCTL_BIN " restart wpa_supplicant");
|
||||
}
|
||||
sysExec(SYSTEMCTL_BIN " start dhcpcd");
|
||||
|
||||
sysExec(SYSTEMCTL_BIN " enable wpa_supplicant"); // ?? Not sure that's still right. :-/
|
||||
sysExec(SYSTEMCTL_BIN " start dnsmasq");
|
||||
sysExec(SYSTEMCTL_BIN " enable dnsmasq");
|
||||
if (UsingNetworkManager())
|
||||
{
|
||||
if (sysExec(SYSTEMCTL_BIN " start pipedal_nm_p2pd") != EXIT_SUCCESS)
|
||||
{
|
||||
throw PiPedalException("Unable to start the Wi-Fi Direct access point service (pipedal_nm_p2pd).");
|
||||
}
|
||||
sysExec(SYSTEMCTL_BIN " enable pipedal_nm_p2pd");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sysExec(SYSTEMCTL_BIN " start pipedal_p2pd") != EXIT_SUCCESS)
|
||||
{
|
||||
throw PiPedalException("Unable to start the Wi-Fi Direct access point service (pipedal_p2pd).");
|
||||
}
|
||||
sysExec(SYSTEMCTL_BIN " enable pipedal_p2pd");
|
||||
}
|
||||
|
||||
sysExec(SYSTEMCTL_BIN " restart pipedald");
|
||||
}
|
||||
|
||||
static void UninstallP2p();
|
||||
|
||||
void pipedal::SetWifiConfig(const WifiConfigSettings &settings)
|
||||
@@ -371,18 +499,53 @@ interface p2p-wlan0-0
|
||||
Watch resolv.conf to make sure we don't lose DNS servers.
|
||||
************************************************************************************/
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
static void restoreNetworkManagerP2pConfig()
|
||||
{
|
||||
std::filesystem::path path(NETWORK_MANAGER_CONFIG_PATH);
|
||||
if (std::filesystem::exists(path))
|
||||
{
|
||||
std::filesystem::remove(path);
|
||||
}
|
||||
}
|
||||
static void applyNetworkManagerP2pConfig()
|
||||
{
|
||||
std::filesystem::path path(NETWORK_MANAGER_CONFIG_PATH);
|
||||
|
||||
std::map<std::string, std::string> map;
|
||||
map["WLAN"] = GetWifiConfigWlanAddress();
|
||||
WriteTemplateFile(map, NETWORK_MANAGER_CONFIG_SOURCE_PATH, NETWORK_MANAGER_CONFIG_PATH);
|
||||
}
|
||||
|
||||
static void applyDnsmasqP2pConfig()
|
||||
{
|
||||
std::map<std::string, std::string> map;
|
||||
map["WLAN"] = GetWifiConfigWlanAddress();
|
||||
WriteTemplateFile(map, NETWORK_MANAGER_DNSMASQ_P2P_SOURCE_PATH, NETWORK_MANAGER_DNSMASQ_P2P_PATH);
|
||||
}
|
||||
|
||||
void UninstallP2p()
|
||||
{
|
||||
if (IsP2pInstalled())
|
||||
{
|
||||
sysExec(SYSTEMCTL_BIN " stop pipedal_p2pd");
|
||||
sysExec(SYSTEMCTL_BIN " disable pipedal_p2pd");
|
||||
if (UsingNetworkManager())
|
||||
{
|
||||
sysExec(SYSTEMCTL_BIN " stop pipedal_nm_p2pd");
|
||||
sysExec(SYSTEMCTL_BIN " disable pipedal_nm_p2pd");
|
||||
}
|
||||
else
|
||||
{
|
||||
sysExec(SYSTEMCTL_BIN " stop pipedal_p2pd");
|
||||
sysExec(SYSTEMCTL_BIN " disable pipedal_p2pd");
|
||||
}
|
||||
|
||||
sysExec(SYSTEMCTL_BIN " stop dnsmasq");
|
||||
sysExec(SYSTEMCTL_BIN " disable dnsmasq");
|
||||
|
||||
restoreP2pDhcpdConfFile();
|
||||
restoreP2pDnsmasqConfFile();
|
||||
restoreNetworkManagerP2pConfig();
|
||||
|
||||
sysExec(SYSTEMCTL_BIN " restart dhcpcd");
|
||||
|
||||
@@ -393,6 +556,91 @@ void UninstallP2p()
|
||||
}
|
||||
}
|
||||
|
||||
static void RemoveDhcpcdConfig()
|
||||
{
|
||||
std::filesystem::path dhcpcdConfig("/etc/dhcpcd.conf");
|
||||
SystemConfigFile dhcpcd(dhcpcdConfig);
|
||||
|
||||
int line = dhcpcd.GetLineNumber("interface p2p-wlan0-0");
|
||||
if (line != -1)
|
||||
{
|
||||
dhcpcd.EraseLine(line);
|
||||
while (line < dhcpcd.GetLineCount())
|
||||
{
|
||||
const std::string &lineValue = dhcpcd.GetLineValue(line);
|
||||
if (lineValue.length() > 0 && (lineValue[0] == ' ' || lineValue[0] == '\t'))
|
||||
{
|
||||
dhcpcd.EraseLine(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
line = dhcpcd.GetLineNumberStartingWith("allowinterfaces p2p-");
|
||||
if (line != -1)
|
||||
{
|
||||
dhcpcd.EraseLine(line);
|
||||
while (line < dhcpcd.GetLineCount())
|
||||
{
|
||||
dhcpcd.EraseLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
static void ConfigDhcpcdForP2p()
|
||||
{
|
||||
RemoveDhcpcdConfig();
|
||||
|
||||
std::filesystem::path dhcpcdConfig("/etc/dhcpcd.conf");
|
||||
cout << "Updating /etc/dhcpcd.conf" << endl;
|
||||
SystemConfigFile dhcpcd(dhcpcdConfig);
|
||||
|
||||
// dhcpcd.conf:
|
||||
// ======
|
||||
// interface p2p-wlan0-0
|
||||
// static ip_address=172.24.0.1/24
|
||||
// domain_name_server=172.24.0.1
|
||||
|
||||
int line = dhcpcd.GetLineThatStartsWith("hostname");
|
||||
std::string hostNameLine;
|
||||
hostNameLine = "hostname"; // the default value.
|
||||
if (line != -1)
|
||||
{
|
||||
dhcpcd.SetLineValue(line, hostNameLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
dhcpcd.AppendLine(hostNameLine);
|
||||
}
|
||||
|
||||
if (!UsingNetworkManager())
|
||||
{
|
||||
line = dhcpcd.GetLineCount();
|
||||
// interface p2p-wlan0-0
|
||||
// static ip_address=172.24.0.1/16 (trying int on .1.)
|
||||
// domain_name_server=172.24.0.1
|
||||
|
||||
dhcpcd.InsertLine(line++, "interface p2p-wlan0-0");
|
||||
dhcpcd.InsertLine(line++, " static ip_address=" P2P_INTERFACE_ADDR);
|
||||
dhcpcd.InsertLine(line++, " nohook wpa_supplicant");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (line == -1)
|
||||
{
|
||||
line = dhcpcd.GetLineCount();
|
||||
}
|
||||
// interface p2p-wlan0-0
|
||||
// static ip_address=172.24.0.1/16 (trying int on .1.)
|
||||
// domain_name_server=172.24.0.1
|
||||
|
||||
dhcpcd.InsertLine(line++, SS("allowinterfaces p2p-" << GetWifiConfigWlanAddress() << "-*").c_str());
|
||||
dhcpcd.InsertLine(line++, "static ip_address=" P2P_INTERFACE_ADDR);
|
||||
dhcpcd.InsertLine(line++, "nohook wpa_supplicant");
|
||||
}
|
||||
dhcpcd.Save(dhcpcdConfig);
|
||||
}
|
||||
void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
|
||||
{
|
||||
|
||||
@@ -414,107 +662,7 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Enabling P2P" << endl;
|
||||
|
||||
settings.Save();
|
||||
// ******************* device_uuid
|
||||
|
||||
ServiceConfiguration deviceIdFile;
|
||||
deviceIdFile.Load();
|
||||
deviceIdFile.deviceName = settings.hotspotName_;
|
||||
deviceIdFile.Save();
|
||||
|
||||
// ******************** dsnmasq ******
|
||||
|
||||
std::filesystem::remove(DNSMASQ_P2P_PATH);
|
||||
std::filesystem::copy_file(DNSMASQ_P2P_SOURCE_PATH, DNSMASQ_P2P_PATH);
|
||||
|
||||
// ****** dhcpd.conf ***/
|
||||
std::filesystem::path dhcpcdConfig("/etc/dhcpcd.conf");
|
||||
if (std::filesystem::exists(dhcpcdConfig))
|
||||
{
|
||||
cout << "Updating /etc/dhcpcd.conf" << endl;
|
||||
SystemConfigFile dhcpcd(dhcpcdConfig);
|
||||
|
||||
// dhcpcd.conf:
|
||||
// ======
|
||||
// interface p2p-wlan0-0
|
||||
// static ip_address=172.24.0.1/24
|
||||
// domain_name_server=172.24.0.1
|
||||
|
||||
int line = dhcpcd.GetLineThatStartsWith("hostname");
|
||||
std::string hostNameLine;
|
||||
if (false)
|
||||
{
|
||||
// hostNameLine = "hostname " + settings.mdnsName_;
|
||||
}
|
||||
else
|
||||
{
|
||||
hostNameLine = "hostname"; // the default value.
|
||||
}
|
||||
if (line != -1)
|
||||
{
|
||||
dhcpcd.SetLineValue(line, hostNameLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
dhcpcd.AppendLine(hostNameLine);
|
||||
}
|
||||
|
||||
// erase any existing interface for p2p-wlan0-0
|
||||
line = dhcpcd.GetLineNumber("interface p2p-wlan0-0");
|
||||
if (line != -1)
|
||||
{
|
||||
dhcpcd.EraseLine(line);
|
||||
while (line < dhcpcd.GetLineCount())
|
||||
{
|
||||
const std::string &lineValue = dhcpcd.GetLineValue(line);
|
||||
if (lineValue.length() > 0 && (lineValue[0] == ' ' || lineValue[0] == '\t'))
|
||||
{
|
||||
dhcpcd.EraseLine(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (line == -1)
|
||||
{
|
||||
dhcpcd.AppendLine("");
|
||||
line = dhcpcd.GetLineCount();
|
||||
}
|
||||
// interface p2p-wlan0-0
|
||||
// static ip_address=172.24.0.1/16 (trying int on .1.)
|
||||
// domain_name_server=172.24.0.1
|
||||
|
||||
dhcpcd.InsertLine(line++, "interface p2p-wlan0-0");
|
||||
dhcpcd.InsertLine(line++, " static ip_address=" P2P_INTERFACE_ADDR);
|
||||
dhcpcd.InsertLine(line++, " nohook wpa_supplicant");
|
||||
// dhcpcd.InsertLine(line++, " domain_name_servers=" P2P_IP_ADDRESS);
|
||||
dhcpcd.Save(dhcpcdConfig);
|
||||
}
|
||||
|
||||
// **************** start services ************
|
||||
|
||||
cout << "Starting P2P services." << endl;
|
||||
sysExec("rfkill unblock wlan");
|
||||
|
||||
sysExec(SYSTEMCTL_BIN " daemon-reload");
|
||||
|
||||
sysExec(SYSTEMCTL_BIN " stop pipedal_p2pd");
|
||||
sysExec(SYSTEMCTL_BIN " restart dhcpcd");
|
||||
sysExec(SYSTEMCTL_BIN " start wpa_supplicant");
|
||||
sysExec(SYSTEMCTL_BIN " enable wpa_supplicant"); // ?? Not sure that's still right. :-/
|
||||
sysExec(SYSTEMCTL_BIN " start dnsmasq");
|
||||
sysExec(SYSTEMCTL_BIN " enable dnsmasq");
|
||||
sysExec(SYSTEMCTL_BIN " start pipedal_p2pd");
|
||||
if (sysExec(SYSTEMCTL_BIN " start pipedal_p2pd") != 0)
|
||||
{
|
||||
throw PiPedalException("Unable to start the Wi-Fi Direct access point.");
|
||||
}
|
||||
|
||||
sysExec(SYSTEMCTL_BIN " enable pipedal_p2pd");
|
||||
InstallP2p(settings);
|
||||
sysExec(SYSTEMCTL_BIN " restart pipedald");
|
||||
}
|
||||
}
|
||||
@@ -531,7 +679,7 @@ void pipedal::OnWifiUninstall()
|
||||
{
|
||||
UninstallHostApd();
|
||||
}
|
||||
else if (IsP2pInstalled())
|
||||
if (IsP2pInstalled())
|
||||
{
|
||||
UninstallP2p();
|
||||
}
|
||||
@@ -548,3 +696,12 @@ void pipedal::OnWifiInstallComplete()
|
||||
// sysExec(SYSTEMCTL_BIN " daemon-reload");
|
||||
// }
|
||||
}
|
||||
|
||||
static void restartNetworkManagerService()
|
||||
{
|
||||
if (UsingNetworkManager())
|
||||
{
|
||||
sysExec(SYSTEMCTL_BIN " restart NetworkManager");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#include "WifiDirectConfigSettings.hpp"
|
||||
|
||||
namespace pipedal {
|
||||
void SetWifiConfigWLanAddress(const std::string&wLanAddress);
|
||||
const std::string &GetWifiConfigWlanAddress();
|
||||
|
||||
|
||||
void SetWifiConfig(const WifiConfigSettings&settings);
|
||||
void SetWifiDirectConfig(const WifiDirectConfigSettings&settings);
|
||||
@@ -29,4 +32,5 @@ namespace pipedal {
|
||||
void OnWifiUninstall();
|
||||
void OnWifiInstallComplete();
|
||||
|
||||
bool UsingNetworkManager();
|
||||
} // namespace.
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "pch.h"
|
||||
#include "SysExec.hpp"
|
||||
#include <sys/wait.h>
|
||||
#include <filesystem>
|
||||
#include "PiPedalException.hpp"
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/limits.h> // for PATH_MAX
|
||||
|
||||
using namespace pipedal;
|
||||
using namespace std;
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wunused-result"
|
||||
|
||||
// find on path, but ONLY /usr/bin and /usr/sbin
|
||||
|
||||
|
||||
static std::filesystem::path findOnSystemPath(const std::string &command)
|
||||
{
|
||||
if (command.length() != 0 && command[0] == '/')
|
||||
{
|
||||
return command;
|
||||
}
|
||||
std::string path = "/usr/bin:/usr/sbin";
|
||||
std::vector<std::string> paths;
|
||||
size_t t = 0;
|
||||
while (t < path.length())
|
||||
{
|
||||
size_t pos = path.find(':', t);
|
||||
if (pos == string::npos)
|
||||
{
|
||||
pos = path.length();
|
||||
}
|
||||
std::string thisPath = path.substr(t, pos - t);
|
||||
std::filesystem::path path = std::filesystem::path(thisPath) / command;
|
||||
if (std::filesystem::exists(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
t = pos + 1;
|
||||
}
|
||||
std::stringstream s;
|
||||
s << "'" << command << "' is not installed.";
|
||||
throw PiPedalException(s.str());
|
||||
}
|
||||
|
||||
int pipedal::silentSysExec(const char *szCommand)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << szCommand << " 2>&1";
|
||||
|
||||
FILE *output = popen(s.str().c_str(), "r");
|
||||
char buffer[512];
|
||||
if (output)
|
||||
{
|
||||
while (!feof(output))
|
||||
{
|
||||
fgets(buffer, sizeof(buffer), output);
|
||||
}
|
||||
return pclose(output);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
int pipedal::sysExec(const char *szCommand)
|
||||
{
|
||||
char *args = strdup(szCommand);
|
||||
int argc;
|
||||
std::vector<char *> argv;
|
||||
|
||||
char *p = args;
|
||||
while (*p)
|
||||
{
|
||||
argv.push_back(p);
|
||||
|
||||
while (*p && *p != ' ')
|
||||
{
|
||||
++p;
|
||||
}
|
||||
if (*p)
|
||||
{
|
||||
*p++ = '\0';
|
||||
while (*p && *p == ' ')
|
||||
{
|
||||
++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
argv.push_back(nullptr);
|
||||
|
||||
std::filesystem::path execPath = argv[0];
|
||||
if (execPath.is_relative())
|
||||
{
|
||||
execPath = findOnSystemPath(execPath);
|
||||
}
|
||||
if (!std::filesystem::exists(execPath))
|
||||
{
|
||||
throw PiPedalException(SS("Path does not exist: " << execPath << "."));
|
||||
}
|
||||
argv[0] = (char *)(execPath.c_str());
|
||||
|
||||
char **rawArgv = &argv[0];
|
||||
int pbPid;
|
||||
int returnValue = 0;
|
||||
|
||||
if ((pbPid = fork()) == 0)
|
||||
{
|
||||
execv(argv[0], rawArgv);
|
||||
exit(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
free((void *)args);
|
||||
waitpid(pbPid, &returnValue, 0);
|
||||
int exitStatus = WEXITSTATUS(returnValue);
|
||||
return exitStatus;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string pipedal::getSelfExePath()
|
||||
{
|
||||
char result[PATH_MAX+1];
|
||||
ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
|
||||
if (count < 0) throw PiPedalException("Can't find EXE path.");
|
||||
result[count] = 0;
|
||||
return result;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace pipedal {
|
||||
// exec a command, returning the actual exit code (unlike execXX() or system() )
|
||||
int sysExec(const char*szCommand);
|
||||
|
||||
// execute a command, suppressing output.
|
||||
int silentSysExec(const char *szCommand);
|
||||
|
||||
std::string getSelfExePath();
|
||||
}
|
||||
@@ -297,8 +297,20 @@ int SystemConfigFile::GetLineNumber(const std::string &line) const {
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int SystemConfigFile::GetLineNumberStartingWith(const std::string &line) const {
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
if (lines[i].starts_with(line))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
void SystemConfigFile::EraseLine(int i)
|
||||
{
|
||||
if (i < 0 || i >= lines.size()) throw PiPedalArgumentException("Range error.");
|
||||
|
||||
@@ -56,6 +56,7 @@ public:
|
||||
int64_t Insert(int64_t position, const std::string&key, const std::string&value);
|
||||
int64_t Insert(int64_t position, const std::string&line);
|
||||
int GetLineNumber(const std::string&line) const;
|
||||
int GetLineNumberStartingWith(const std::string&line) const;
|
||||
bool HasLine(const std::string&line) const { return GetLineNumber(line) != -1; }
|
||||
void EraseLine(int i);
|
||||
int GetLineThatStartsWith(const std::string&text) const;
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
// #include <unistd.h>
|
||||
// #include <fcntl.h>
|
||||
// #include <string>
|
||||
// #include <net/if.h>
|
||||
// #include <netlink/netlink.h>
|
||||
// #include <netlink/genl/genl.h>
|
||||
// #include <netlink/genl/family.h>
|
||||
// #include <netlink/genl/ctrl.h>
|
||||
// #include <netlink/msg.h>
|
||||
// #include <netlink/attr.h>
|
||||
// #include <linux/nl80211.h>
|
||||
|
||||
// #include <linux/nl80211.h>
|
||||
#include "PiPedalException.hpp"
|
||||
#include "WifiChannelSelectors.hpp"
|
||||
#include "Lv2Log.hpp"
|
||||
#include <set>
|
||||
|
||||
#include "RegDb.hpp"
|
||||
|
||||
#include "ChannelInfo.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
|
||||
|
||||
// static bool firstTime = true;
|
||||
// static std::unique_ptr<RegDb> g_regDb;
|
||||
|
||||
std::vector<WifiChannelSelector> pipedal::getWifiChannelSelectors(const char*countryIso3661, bool forCommandline)
|
||||
{
|
||||
// Use the wifi physical device to get a list of channels
|
||||
// that the hardware supports.
|
||||
// The override the hardware's regulation flags using regulation.bin
|
||||
// database info for the selected country code.
|
||||
// If the regulation.bin database doesn't have a matching record,
|
||||
// just pass the data that the wifi reported.
|
||||
|
||||
// regdb is broken on bookworm.
|
||||
// if (firstTime) {
|
||||
// firstTime = false;
|
||||
// try {
|
||||
// g_regDb = std::make_unique<RegDb>();
|
||||
// } catch (const std::exception& e)
|
||||
// {
|
||||
// std::stringstream s;
|
||||
// s << "Failed to open the Wi-Fi regulatory.bin database. " << e.what();
|
||||
// Lv2Log::error(s.str());
|
||||
// }
|
||||
// }
|
||||
std::vector<WifiChannelSelector> result;
|
||||
|
||||
|
||||
if (forCommandline) {
|
||||
WifiChannelSelector autoSelect;
|
||||
autoSelect.channelId_ = "0";
|
||||
autoSelect.channelName_ = "0 Select automatically";
|
||||
result.push_back(autoSelect);
|
||||
} else {
|
||||
WifiChannelSelector autoSelect;
|
||||
autoSelect.channelId_ = "0";
|
||||
autoSelect.channelName_ = "Select automatically";
|
||||
result.push_back(autoSelect);
|
||||
}
|
||||
WifiInfo wifiInfo = getWifiInfo(countryIso3661);
|
||||
|
||||
std::set<int32_t> visitedChannels;
|
||||
for (auto &channel: wifiInfo.channels) {
|
||||
if ((!channel.disabled) && (!channel.radarDetection))
|
||||
{
|
||||
if (channel.no20MHz)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// ignore duplicates
|
||||
if (visitedChannels.contains(channel.channelNumber))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
visitedChannels.insert(channel.channelNumber);
|
||||
WifiChannelSelector ch;
|
||||
std::stringstream s;
|
||||
s << channel.channelNumber;
|
||||
ch.channelId_ = s.str();
|
||||
|
||||
std::stringstream t;
|
||||
t << channel.channelNumber;
|
||||
switch (channel.hardwareMode)
|
||||
{
|
||||
case WifiMode::IEEE80211G:
|
||||
t << " (2.4GHz)";
|
||||
break;
|
||||
case WifiMode::IEEE80211A:
|
||||
case WifiMode::IEEE80211B:
|
||||
t << " (5GHz)";
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
if (channel.indoorOnly) {
|
||||
t << " Indoors only";
|
||||
}
|
||||
if (channel.outdoorsOnly) {
|
||||
t << " Outdoors only";
|
||||
}
|
||||
ch.channelName_ = t.str();
|
||||
result.push_back(ch);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
JSON_MAP_BEGIN(WifiChannelSelector)
|
||||
JSON_MAP_REFERENCE(WifiChannelSelector,channelId)
|
||||
JSON_MAP_REFERENCE(WifiChannelSelector,channelName)
|
||||
JSON_MAP_END()
|
||||
|
||||
|
||||
@@ -17,19 +17,21 @@
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#pragma once
|
||||
#include "json.hpp"
|
||||
namespace pipedal {
|
||||
namespace pipedal
|
||||
{
|
||||
|
||||
class WifiChannel {
|
||||
public:
|
||||
std::string channelId_;
|
||||
std::string channelName_;
|
||||
class WifiChannelSelector
|
||||
{
|
||||
public:
|
||||
std::string channelId_;
|
||||
std::string channelName_;
|
||||
|
||||
DECLARE_JSON_MAP(WifiChannel);
|
||||
};
|
||||
|
||||
std::vector<WifiChannel> getWifiChannels(const char*countryIso3661);
|
||||
DECLARE_JSON_MAP(WifiChannelSelector);
|
||||
};
|
||||
|
||||
std::vector<WifiChannelSelector> getWifiChannelSelectors(const char *countryIso3661, bool forCommandline = false);
|
||||
|
||||
}
|
||||
@@ -1,637 +0,0 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
#include "pch.h"
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string>
|
||||
#include <net/if.h>
|
||||
#include <netlink/netlink.h>
|
||||
#include <netlink/genl/genl.h>
|
||||
#include <netlink/genl/family.h>
|
||||
#include <netlink/genl/ctrl.h>
|
||||
#include <netlink/msg.h>
|
||||
#include <netlink/attr.h>
|
||||
#include <linux/nl80211.h>
|
||||
|
||||
#include <linux/nl80211.h>
|
||||
#include "PiPedalException.hpp"
|
||||
#include "WifiChannels.hpp"
|
||||
#include "Lv2Log.hpp"
|
||||
|
||||
#include "RegDb.hpp"
|
||||
|
||||
/*
|
||||
Heavily based on code from iw (1) command.
|
||||
https://kernel.googlesource.com/pub/scm/linux/kernel/git/jberg/iw/+/v0.9/COPYING
|
||||
|
||||
Copyright (c) 2007, 2008 Johannes Berg
|
||||
Copyright (c) 2007 Andy Lutomirski
|
||||
Copyright (c) 2007 Mike Kershaw
|
||||
Copyright (c) 2008 Luis R. Rodriguez
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
struct MHzToChannel {
|
||||
char band;
|
||||
int channel;
|
||||
int mhz;
|
||||
};
|
||||
|
||||
MHzToChannel mhzToChannels[] = {
|
||||
{'g', 1, 2412},
|
||||
{'g',2, 2417},
|
||||
{'g',3, 2422},
|
||||
{'g',4, 2427},
|
||||
{'g',5, 2432},
|
||||
{'g',6, 2437},
|
||||
{'g',7, 2442},
|
||||
{'g',8, 2447},
|
||||
{'g',9, 2452},
|
||||
{'g',10, 2457},
|
||||
{'g',11, 2462},
|
||||
{'g',12, 2467},
|
||||
{'g',13, 2472},
|
||||
{'g',14, 2484},
|
||||
{'a',7, 5035},
|
||||
{'a',8, 5040},
|
||||
{'a',9, 5045},
|
||||
{'a',11, 5055},
|
||||
{'a',12, 5060},
|
||||
{'a',16, 5080},
|
||||
{'a',32, 5160},
|
||||
{'a',34, 5170},
|
||||
{'a',36, 5180},
|
||||
{'a',38, 5190},
|
||||
{'a',40, 5200},
|
||||
{'a',42, 5210},
|
||||
{'a',44, 5220},
|
||||
{'a',46, 5230},
|
||||
{'a',48, 5240},
|
||||
{'a',50, 5250},
|
||||
{'a',52, 5260},
|
||||
{'a',54, 5270},
|
||||
{'a',56, 5280},
|
||||
{'a',58, 5290},
|
||||
{'a',60, 5300},
|
||||
{'a',62, 5310},
|
||||
{'a',64, 5320},
|
||||
{'a',68, 5340},
|
||||
{'a',96, 5480},
|
||||
{'a',100, 5500},
|
||||
{'a',102, 5510},
|
||||
{'a',104, 5520},
|
||||
{'a',106, 5530},
|
||||
{'a',108, 5540},
|
||||
{'a',110, 5550},
|
||||
{'a',112, 5560},
|
||||
{'a',114, 5570},
|
||||
{'a',116, 5580},
|
||||
{'a',118, 5590},
|
||||
{'a',120, 5600},
|
||||
{'a',122, 5610},
|
||||
{'a',124, 5620},
|
||||
{'a',126, 5630},
|
||||
{'a',128, 5640},
|
||||
{'a',132, 5660},
|
||||
{'a',134, 5670},
|
||||
{'a',136, 5680},
|
||||
{'a',138, 5690},
|
||||
{'a',140, 5700},
|
||||
{'a',142, 5710},
|
||||
{'a',144, 5720},
|
||||
{'a',149, 5745},
|
||||
{'a',151, 5755},
|
||||
{'a',153, 5765},
|
||||
{'a',155, 5775},
|
||||
{'a',157, 5785},
|
||||
{'a',159, 5795},
|
||||
{'a',161, 5805},
|
||||
{'a',163, 5815},
|
||||
{'a',165, 5825},
|
||||
{'a',167, 5835},
|
||||
{'a',169, 5845},
|
||||
{'a',171, 5855},
|
||||
{'a',173, 5865},
|
||||
{'a',175, 5875},
|
||||
{'a',177, 5885},
|
||||
{'a',180, 5900},
|
||||
{'a',182, 5910},
|
||||
{'a',18, 5915},
|
||||
{'a',184, 5920},
|
||||
{'a',187, 5935},
|
||||
{'a',188, 5940},
|
||||
{'a',189, 5945},
|
||||
{'a',192, 5960},
|
||||
{'a',196, 5980},
|
||||
};
|
||||
|
||||
|
||||
bool MHzToChannelNumber(int mhz, char*pBand, int*pChannel)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(mhzToChannels)/sizeof(mhzToChannels[0]); ++i)
|
||||
{
|
||||
if (std::abs(mhz-mhzToChannels[i].mhz) <= 1) {
|
||||
*pBand = mhzToChannels[i].band;
|
||||
*pChannel = mhzToChannels[i].channel;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static nla_policy *makeFreqPolicy()
|
||||
{
|
||||
nla_policy* result = new nla_policy[NL80211_FREQUENCY_ATTR_MAX + 1];
|
||||
memset(result,0,sizeof(nla_policy[NL80211_FREQUENCY_ATTR_MAX + 1]));
|
||||
|
||||
result[NL80211_FREQUENCY_ATTR_FREQ] = {NLA_U32,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_DISABLED] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_NO_IR] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_RADAR] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_INDOOR_ONLY] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_INDOOR_ONLY] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_NO_20MHZ] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_NO_20MHZ] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_NO_80MHZ] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_NO_160MHZ] = {NLA_FLAG,0,0};
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct nla_policy* freq_policy = makeFreqPolicy();
|
||||
|
||||
|
||||
static nla_policy *makeRatePolicy() {
|
||||
nla_policy* result = new nla_policy[NL80211_BITRATE_ATTR_MAX + 1];
|
||||
memset(result,0,sizeof(nla_policy[NL80211_BITRATE_ATTR_MAX + 1]));
|
||||
result[NL80211_BITRATE_ATTR_RATE] = {.type = NLA_U32};
|
||||
result[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = {.type = NLA_FLAG};
|
||||
return result;
|
||||
};
|
||||
|
||||
static struct nla_policy* rate_policy = makeRatePolicy();
|
||||
|
||||
struct IfType {
|
||||
enum nl80211_iftype iftype;
|
||||
const char*name;
|
||||
};
|
||||
static struct IfType ifTypes[] = {
|
||||
{NL80211_IFTYPE_UNSPECIFIED,"Unspecified"},
|
||||
{NL80211_IFTYPE_ADHOC,"Adhoc"},
|
||||
{NL80211_IFTYPE_STATION,"Station"},
|
||||
{NL80211_IFTYPE_AP,"AP"},
|
||||
{NL80211_IFTYPE_AP_VLAN,"VLAN"},
|
||||
{NL80211_IFTYPE_WDS,"WDS"},
|
||||
{NL80211_IFTYPE_MONITOR,"Monitor"},
|
||||
{NL80211_IFTYPE_MESH_POINT,"Mesh point"},
|
||||
{NL80211_IFTYPE_P2P_CLIENT,"Client"},
|
||||
{NL80211_IFTYPE_P2P_GO,"P2P Go"},
|
||||
{NL80211_IFTYPE_P2P_DEVICE,"P2P"},
|
||||
{NL80211_IFTYPE_OCB,"OCB"},
|
||||
{NL80211_IFTYPE_NAN,"NAN"},
|
||||
|
||||
};
|
||||
const char *iftype_name(enum nl80211_iftype iftype)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(ifTypes)/sizeof(ifTypes[0]); ++i)
|
||||
{
|
||||
if (ifTypes[i].iftype == iftype)
|
||||
{
|
||||
return ifTypes[i].name;
|
||||
}
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
static int phy_lookup(const char *name)
|
||||
{
|
||||
char buf[200];
|
||||
int fd, pos;
|
||||
snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name);
|
||||
fd = open(buf, O_RDONLY);
|
||||
pos = read(fd, buf, sizeof(buf) - 1);
|
||||
if (pos < 0)
|
||||
return -1;
|
||||
buf[pos] = '\0';
|
||||
return atoi(buf);
|
||||
}
|
||||
|
||||
static int getWifiName_callback(struct nl_msg *msg, void *arg);
|
||||
static int getWifiInfo_callback(struct nl_msg *msg, void *arg);
|
||||
|
||||
struct WifiChannelInfo
|
||||
{
|
||||
int channelNumber = -1;
|
||||
char band = 'g'; // 'g' or 'a'
|
||||
int mhz = 0;
|
||||
bool disabled = false;
|
||||
bool ir = true;
|
||||
bool radarDetection = false;
|
||||
std::vector<float> bitrates;
|
||||
bool indoorOnly = false;
|
||||
bool noHt40Minus = false;
|
||||
bool noHt40Plus = false;
|
||||
bool no10MHz = false;
|
||||
bool no20MHz = false;
|
||||
bool no80MHz = false;
|
||||
bool no160MHz = false;
|
||||
};
|
||||
|
||||
struct WifiBand
|
||||
{
|
||||
int index;
|
||||
std::vector<WifiChannelInfo> channels;
|
||||
};
|
||||
|
||||
struct WifiInfo
|
||||
{
|
||||
std::string name;
|
||||
std::vector<WifiBand> bands;
|
||||
std::vector<std::string> supportedIfTypes;
|
||||
};
|
||||
class NetLink
|
||||
{
|
||||
private:
|
||||
|
||||
std::vector<WifiBand> result;
|
||||
nl_sock*socket = nullptr;
|
||||
nl_cache*cache = nullptr;
|
||||
genl_family*nl80211 = nullptr;
|
||||
|
||||
int errorCode = -1;
|
||||
|
||||
static int finish_handler(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
NetLink*this_ = (NetLink*)arg;
|
||||
this_->errorCode = 0;
|
||||
return NL_SKIP;
|
||||
}
|
||||
static int ack_handler(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
NetLink*this_ = (NetLink*)arg;
|
||||
this_->errorCode = 0;
|
||||
return NL_STOP;
|
||||
}
|
||||
|
||||
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
|
||||
void *arg)
|
||||
{
|
||||
NetLink*this_ = (NetLink*)arg;
|
||||
this_->errorCode = err->error;
|
||||
return NL_STOP;
|
||||
}
|
||||
|
||||
|
||||
static int info_phy_handler(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
return ((NetLink *)arg)->InfoPhyHandler(msg, (WifiInfo *)arg);
|
||||
}
|
||||
int InfoPhyHandler(struct nl_msg *msg, WifiInfo *result)
|
||||
{
|
||||
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
|
||||
struct genlmsghdr *gnlh = (genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
|
||||
struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
|
||||
struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
|
||||
struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
|
||||
struct nlattr *nl_band;
|
||||
struct nlattr *nl_freq;
|
||||
struct nlattr *nl_rate;
|
||||
struct nlattr *nl_mode;
|
||||
int bandidx = 1;
|
||||
int rem_band, rem_freq, rem_rate, rem_mode;
|
||||
int open;
|
||||
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
||||
genlmsg_attrlen(gnlh, 0), NULL);
|
||||
if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
|
||||
return NL_SKIP;
|
||||
if (tb_msg[NL80211_ATTR_WIPHY_NAME])
|
||||
result->name = nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME]);
|
||||
|
||||
nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band)
|
||||
{
|
||||
WifiBand band;
|
||||
band.index = bandidx;
|
||||
|
||||
bandidx++;
|
||||
nla_parse(tb_band, NL80211_BAND_ATTR_MAX, (nlattr *)nla_data(nl_band),
|
||||
nla_len(nl_band), NULL);
|
||||
|
||||
nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq)
|
||||
{
|
||||
nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, (nlattr *)nla_data(nl_freq),
|
||||
nla_len(nl_freq), freq_policy);
|
||||
if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
|
||||
continue;
|
||||
|
||||
WifiChannelInfo channel;
|
||||
|
||||
channel.mhz = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
|
||||
open = 0;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
|
||||
channel.disabled = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR])
|
||||
channel.ir = false;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
|
||||
channel.radarDetection = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_10MHZ])
|
||||
channel.no10MHz = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_INDOOR_ONLY])
|
||||
channel.indoorOnly = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ])
|
||||
channel.no20MHz = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS])
|
||||
channel.noHt40Minus = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS])
|
||||
channel.noHt40Plus = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ])
|
||||
channel.no80MHz = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
|
||||
channel.no160MHz = true;
|
||||
|
||||
if (MHzToChannelNumber(channel.mhz,&channel.band, &channel.channelNumber))
|
||||
{
|
||||
band.channels.push_back(std::move(channel));
|
||||
} else {
|
||||
// throw PiPedalException("Frequency not recognized.");
|
||||
}
|
||||
}
|
||||
nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate)
|
||||
{
|
||||
nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, (nlattr *)nla_data(nl_rate),
|
||||
nla_len(nl_rate), rate_policy);
|
||||
if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
|
||||
continue;
|
||||
//channel.bitRates.push_back(0.1f * nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]);
|
||||
}
|
||||
result->bands.push_back(std::move(band));
|
||||
}
|
||||
if (!tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES])
|
||||
return NL_SKIP;
|
||||
|
||||
nla_for_each_nested(nl_mode, tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES], rem_mode)
|
||||
result->supportedIfTypes.push_back(iftype_name((nl80211_iftype)(nl_mode->nla_type)));
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
public:
|
||||
~NetLink()
|
||||
{
|
||||
if (this->cache)
|
||||
{
|
||||
nl_cache_free(cache);
|
||||
cache = nullptr;
|
||||
}
|
||||
if (nl80211 != nullptr)
|
||||
{
|
||||
genl_family_put(nl80211); // (release reference)
|
||||
nl80211 = nullptr;
|
||||
}
|
||||
if (socket)
|
||||
{
|
||||
nl_close(socket);
|
||||
nl_socket_free(socket);
|
||||
socket = nullptr;
|
||||
}
|
||||
}
|
||||
NetLink()
|
||||
{
|
||||
socket = nl_socket_alloc();
|
||||
if (!socket)
|
||||
{
|
||||
throw PiPedalException("Can't allocate netlink socket.");
|
||||
}
|
||||
nl_socket_set_buffer_size(socket, 8192, 8192);
|
||||
|
||||
if (genl_connect(socket))
|
||||
{
|
||||
throw PiPedalException("Failed to connect to netlink socket.");
|
||||
}
|
||||
if (genl_ctrl_alloc_cache(this->socket,&this->cache) != 0) {
|
||||
throw PiPedalException("Failed to allocate generic netlink cache.");
|
||||
}
|
||||
this->nl80211 = genl_ctrl_search_by_name(this->cache, "nl80211");
|
||||
if (!this->nl80211) {
|
||||
throw PiPedalException("nl80211 family not found.");
|
||||
}
|
||||
}
|
||||
void GetAvailableChannels(const char *phyName, WifiInfo *result)
|
||||
{
|
||||
|
||||
struct nl_msg *msg = nullptr;
|
||||
struct nl_cb*cb = nullptr;
|
||||
int devidx = 0;
|
||||
int err;
|
||||
|
||||
devidx = phy_lookup(phyName);
|
||||
try {
|
||||
|
||||
|
||||
msg = nlmsg_alloc();
|
||||
if (!msg)
|
||||
{
|
||||
throw PiPedalException("failed to allocate netlink message");
|
||||
}
|
||||
cb = nl_cb_alloc(NL_CB_DEFAULT);
|
||||
if (!cb)
|
||||
{
|
||||
throw PiPedalException("failed to allocate netlink callbacks");
|
||||
}
|
||||
genlmsg_put(msg, 0, 0, genl_family_get_id(this->nl80211), 0,
|
||||
0, NL80211_CMD_GET_WIPHY, 0);
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, devidx);
|
||||
|
||||
this->errorCode = 0;
|
||||
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, info_phy_handler, result);
|
||||
|
||||
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, this);
|
||||
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, this);
|
||||
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, this);
|
||||
|
||||
err = nl_send_auto_complete(this->socket, msg);
|
||||
|
||||
if (err < 0)
|
||||
throw PiPedalException("Command failed.");
|
||||
nl_recvmsgs(this->socket, cb);
|
||||
nlmsg_free(msg);
|
||||
if (this->errorCode != 0)
|
||||
{
|
||||
throw PiPedalException("Command failed.");
|
||||
}
|
||||
nl_cb_put(cb);
|
||||
return;
|
||||
nla_put_failure:
|
||||
throw PiPedalException("NLA Put failure.");
|
||||
} catch (const std::exception&e)
|
||||
{
|
||||
if (cb != nullptr)
|
||||
{
|
||||
nl_cb_put(cb);
|
||||
cb = nullptr;
|
||||
}
|
||||
if (msg != nullptr)
|
||||
{
|
||||
nlmsg_free(msg);
|
||||
msg = nullptr;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WifiInfo getWifiInfo(const char *phyName)
|
||||
{
|
||||
int index = phy_lookup(phyName);
|
||||
if (index == -1)
|
||||
{
|
||||
throw PiPedalArgumentException("No such device.");
|
||||
}
|
||||
|
||||
NetLink netLink;
|
||||
WifiInfo result;
|
||||
netLink.GetAvailableChannels(phyName,&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
const char*allowOutdoorsLocales[] = {
|
||||
"US","TW","SG",
|
||||
|
||||
};
|
||||
|
||||
static bool allowOutdoors(const char*countryCode)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(allowOutdoorsLocales)/sizeof(allowOutdoorsLocales[0]); ++i)
|
||||
{
|
||||
if (strcmp(allowOutdoorsLocales[i],countryCode) == 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool firstTime = true;
|
||||
static std::unique_ptr<RegDb> g_regDb;
|
||||
std::vector<WifiChannel> pipedal::getWifiChannels(const char*countryIso3661)
|
||||
{
|
||||
// Use the wifi physical device to get a list of channels
|
||||
// that the hardware supports.
|
||||
// The override the hardware's regulation flags using regulation.bin
|
||||
// database info for the selected country code.
|
||||
// If the regulation.bin database doesn't have a matching record,
|
||||
// just pass the data that the wifi reported.
|
||||
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
try {
|
||||
g_regDb = std::make_unique<RegDb>();
|
||||
} catch (const std::exception& e)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "Failed to open the Wi-Fi regulations.bin database. " << e.what();
|
||||
Lv2Log::error(s.str());
|
||||
}
|
||||
}
|
||||
std::vector<WifiChannel> result;
|
||||
|
||||
WifiInfo wifiInfo = getWifiInfo("phy0");
|
||||
std::unique_ptr<CrdbRules> crdbRules = nullptr;
|
||||
if (g_regDb) {
|
||||
crdbRules = std::unique_ptr<CrdbRules>(g_regDb->GetCrdbRules(countryIso3661));
|
||||
if (!crdbRules) {
|
||||
crdbRules = std::unique_ptr<CrdbRules>(g_regDb->GetCrdbRules("00")); // unset default.
|
||||
}
|
||||
}
|
||||
for (auto &band : wifiInfo.bands)
|
||||
{
|
||||
for (auto &channel: band.channels) {
|
||||
if (crdbRules)
|
||||
{
|
||||
FrequencyRule crdbRule;
|
||||
if (crdbRules->GetFrequencyRule(channel.mhz,&crdbRule))
|
||||
{
|
||||
channel.disabled = false;
|
||||
channel.radarDetection = crdbRule.hasFlag(RegRuleFlags::DFS);
|
||||
channel.ir = !crdbRule.hasFlag(RegRuleFlags::NO_IR);
|
||||
channel.indoorOnly = crdbRule.hasFlag(RegRuleFlags::NO_OUTDOOR);
|
||||
if (crdbRule.hasFlag(RegRuleFlags::PTP_ONLY)
|
||||
|| crdbRule.hasFlag(RegRuleFlags::PTMP_ONLY))
|
||||
{
|
||||
channel.disabled = true;
|
||||
|
||||
}
|
||||
} else {
|
||||
channel.disabled = true;
|
||||
}
|
||||
}
|
||||
if ((!channel.disabled) && (!channel.radarDetection) && channel.ir)
|
||||
{
|
||||
WifiChannel ch;
|
||||
std::stringstream s;
|
||||
s << channel.band << channel.channelNumber;
|
||||
ch.channelId_ = s.str();
|
||||
|
||||
std::stringstream t;
|
||||
t << channel.channelNumber;
|
||||
if (channel.band == 'a')
|
||||
t << " (5GHz)";
|
||||
else
|
||||
t << " (2.4GHz)";
|
||||
|
||||
if (channel.indoorOnly) {
|
||||
t << " Indoors only";
|
||||
}
|
||||
ch.channelName_ = t.str();
|
||||
result.push_back(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
JSON_MAP_BEGIN(WifiChannel)
|
||||
JSON_MAP_REFERENCE(WifiChannel,channelId)
|
||||
JSON_MAP_REFERENCE(WifiChannel,channelName)
|
||||
JSON_MAP_END()
|
||||
|
||||
|
||||
+197
-14
@@ -23,36 +23,219 @@
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "WifiChannels.hpp"
|
||||
|
||||
|
||||
#include "WifiChannelSelectors.hpp"
|
||||
#include "ChannelInfo.hpp"
|
||||
#include "RegDb.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
static WifiChannel GetChannel(std::vector<WifiChannel> channels, const std::string&channelId)
|
||||
static WifiChannelSelector GetChannel(const std::vector<WifiChannelSelector> &channels, const std::string &channelId)
|
||||
{
|
||||
for (auto &channel: channels)
|
||||
for (auto &channel : channels)
|
||||
{
|
||||
if (channel.channelId_ == channelId)
|
||||
{
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
throw PiPedalException("Channel not found.");;
|
||||
throw std::runtime_error("Channel not found.");
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST_CASE( "Wifi Channel Test", "[wifi_channels_test]" ) {
|
||||
std::vector<WifiChannel> result = pipedal::getWifiChannels("CA");
|
||||
void TestWifiChannels()
|
||||
{
|
||||
using namespace std;
|
||||
cout << " ==== CA Channels ====" << endl;
|
||||
std::vector<WifiChannelSelector> result = pipedal::getWifiChannelSelectors("CA");
|
||||
for (const auto &channel : result)
|
||||
{
|
||||
std::cout << " " << channel.channelName_ << std::endl;
|
||||
}
|
||||
|
||||
// channel 36 is indoors only (CA)
|
||||
WifiChannel channel = GetChannel(result,"a36");
|
||||
WifiChannelSelector channel = GetChannel(result, "36");
|
||||
|
||||
|
||||
// present before bookworm. no longer.
|
||||
REQUIRE(channel.channelName_.find("Indoors") != std::string::npos);
|
||||
|
||||
|
||||
for (const auto &channel : result)
|
||||
{
|
||||
std::cout << " " << channel.channelName_ << std::endl;
|
||||
}
|
||||
|
||||
// channel 36 is not indoors only (US)
|
||||
result = pipedal::getWifiChannels("US");
|
||||
channel = GetChannel(result,"a36");
|
||||
cout << " ==== US Channels ====" << endl;
|
||||
|
||||
result = pipedal::getWifiChannelSelectors("US");
|
||||
for (const auto &channel : result)
|
||||
{
|
||||
std::cout << " " << channel.channelName_ << std::endl;
|
||||
}
|
||||
channel = GetChannel(result, "36");
|
||||
REQUIRE(channel.channelName_.find("Indoors") == std::string::npos);
|
||||
}
|
||||
|
||||
struct RegulatoryDomain {
|
||||
RegulatoryDomain(int32_t domain) : domain(domain) { }
|
||||
RegulatoryDomain(int32_t domain,const std::vector<int32_t>&channels) : domain(domain),channels(channels) { }
|
||||
void AddChannel(int32_t channel)
|
||||
{
|
||||
std::vector<int32_t>::iterator i;
|
||||
for (i = channels.begin(); i != channels.end(); ++i)
|
||||
{
|
||||
if ((*i) == channel) return;
|
||||
if (*i > channel) break;
|
||||
}
|
||||
channels.insert(i,channel);
|
||||
}
|
||||
bool operator==(const RegulatoryDomain&other) const
|
||||
{
|
||||
if (this->domain != other.domain) return false;
|
||||
return std::equal(channels.begin(),channels.end(),other.channels.begin(),other.channels.end());
|
||||
}
|
||||
int32_t domain;
|
||||
std::vector<int32_t> channels;
|
||||
};
|
||||
|
||||
class ChannelsByRegDomain {
|
||||
public:
|
||||
ChannelsByRegDomain() { }
|
||||
ChannelsByRegDomain(const std::vector<RegulatoryDomain>&domains): regulations(domains) { }
|
||||
void AddChannel(int32_t domain, int32_t channel)
|
||||
{
|
||||
RegulatoryDomain®Domain = GetDomain(domain);
|
||||
regDomain.AddChannel(channel);
|
||||
}
|
||||
std::string toString() const {
|
||||
std::stringstream s;
|
||||
bool first = true;
|
||||
for (const auto&domain: regulations)
|
||||
{
|
||||
if (!first) s << " ";
|
||||
first = false;
|
||||
s << domain.domain << ":";
|
||||
bool firstChan = true;
|
||||
for (const auto c : domain.channels)
|
||||
{
|
||||
if(!firstChan) s << ",";
|
||||
firstChan = false;
|
||||
|
||||
s << c;
|
||||
}
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
bool operator==(const ChannelsByRegDomain&other) const
|
||||
{
|
||||
return std::equal(regulations.begin(),regulations.end(),other.regulations.begin(),other.regulations.end());
|
||||
}
|
||||
private:
|
||||
RegulatoryDomain&GetDomain(int32_t domain) {
|
||||
|
||||
std::vector<RegulatoryDomain>::iterator i;
|
||||
for (i = regulations.begin(); i != regulations.end(); i++)
|
||||
{
|
||||
if (i->domain == domain) return *i;
|
||||
if (i->domain > domain) break;
|
||||
}
|
||||
i = regulations.insert(i,RegulatoryDomain(domain));
|
||||
return *i;
|
||||
|
||||
}
|
||||
std::vector<RegulatoryDomain> regulations;
|
||||
};
|
||||
|
||||
void DisplayP2pChannels(const char*isoCountry, const ChannelsByRegDomain&expected)
|
||||
{
|
||||
std::cout << " " << isoCountry <<":";
|
||||
|
||||
ChannelsByRegDomain result;
|
||||
auto wifiInfo = pipedal::getWifiInfo("CA");
|
||||
for (const auto&channel: wifiInfo.channels)
|
||||
{
|
||||
result.AddChannel(channel.regDomain,channel.channelNumber);
|
||||
}
|
||||
std::cout << result.toString() << std::endl;
|
||||
if (result != expected)
|
||||
{
|
||||
throw std::runtime_error("Incorrect results..");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TestBookwormRegDbLoad()
|
||||
{
|
||||
std::filesystem::path dbPath = "test_data/debian_bookworm_regulatory.db";
|
||||
RegDb regdb{dbPath};
|
||||
REQUIRE(regdb.IsValid());
|
||||
}
|
||||
void TestP2pChannels()
|
||||
{
|
||||
std::cout << "---- TestP2PChannels ---" << std::endl;
|
||||
|
||||
// expecting (for CA)
|
||||
//81:1,2,3,4,5,6,7,8,9,10,11 115:36,40,44,48 116:36,44 117:40,48 124:149,153,157,161 125:149,153,157,161,165 126:149,157
|
||||
//81:1,2,3,4,5,6,7,8,9,10,11 115:36,40,44,48 116:36,44 117:40,48 124:149,153,157,161 125:149,153,157,161,165 126:149,157 127:153,161
|
||||
// 127:153,161 128:36,40,44,48,149,153,157,161 130:36,40,44,48,149,153,157,161,165
|
||||
// 127:153,161
|
||||
|
||||
|
||||
ChannelsByRegDomain expectedCaDomains =
|
||||
{
|
||||
std::vector<RegulatoryDomain>{
|
||||
{81, { 1,2,3,4,5,6,7,8,9,10,11}},
|
||||
{ 115, {36,40,44,48}},
|
||||
{ 116, {36,44}},
|
||||
{ 117, { 40,48}},
|
||||
{ 124, {149,153,157,161}},
|
||||
{ 125, {149,153,157,161,165}},
|
||||
{ 126, {149,157 }},
|
||||
{ 127, {153,161}},
|
||||
// ignore 80ghz ac channels (which pi supports, but we don't).
|
||||
// { 128, {36,40,44,48,149,153,157,161}},
|
||||
// { 130, {36,40,44,48,149,153,157,161,165}}
|
||||
}
|
||||
};
|
||||
DisplayP2pChannels("CA",expectedCaDomains);
|
||||
// DisplayP2pChannels("US");
|
||||
// DisplayP2pChannels("JP");
|
||||
// DisplayP2pChannels("00");
|
||||
|
||||
}
|
||||
|
||||
static void DisplayRegulations(const std::string& country)
|
||||
{
|
||||
using namespace std;
|
||||
RegDb®Db = RegDb::GetInstance();
|
||||
|
||||
cout << " ---- " << country << " regulations --- " << endl;
|
||||
const auto& regulations = regDb.getWifiRegulations(country);
|
||||
for (const auto&rule: regulations.rules)
|
||||
{
|
||||
cout << rule.start_freq_khz/1000 << "-" << rule.end_freq_khz/1000 << " (" << rule.max_bandwidth_khz/1000 << ")";
|
||||
cout << (rule.HasFlag(RegRuleFlags::NO_INDOOR) ? " NO_INDOORS" : "")
|
||||
<< (rule.HasFlag(RegRuleFlags::NO_OUTDOOR) ? " NO_OUTDOOR" : "")
|
||||
<< (rule.HasFlag(RegRuleFlags::DFS) ? " RADAR" : "")
|
||||
<< (rule.HasFlag(RegRuleFlags::NO_IR) ? " NO_IR" : "")
|
||||
<< (rule.HasFlag(RegRuleFlags::NO_OFDM) ? " NO_OFDM" : "")
|
||||
<< (rule.HasFlag(RegRuleFlags::NO_HE) ? " NO_HE" : "")
|
||||
<< (rule.HasFlag(RegRuleFlags::NO_HT40MINUS) ? " NO_HT40MINUS" : "")
|
||||
<< (rule.HasFlag(RegRuleFlags::NO_HT40PLUS) ? " NO_HT40PLUS" : "")
|
||||
<< (rule.HasFlag(RegRuleFlags::AUTO_BW) ? " AUTO_BW" : "")
|
||||
<< (rule.HasFlag(RegRuleFlags::PTP_ONLY) ? " PTP_ONLY" : "")
|
||||
<< (rule.HasFlag(RegRuleFlags::PTMP_ONLY) ? " PTMP_ONLY" : "")
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Wifi Channel Test", "[wifi_channels_test]")
|
||||
{
|
||||
DisplayRegulations("CA");
|
||||
DisplayRegulations("US");
|
||||
DisplayRegulations("JP");
|
||||
TestBookwormRegDbLoad();
|
||||
TestWifiChannels();
|
||||
TestP2pChannels();
|
||||
}
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "pch.h"
|
||||
#include "WifiConfigSettings.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace pipedal;
|
||||
using namespace std;
|
||||
|
||||
JSON_MAP_BEGIN(WifiConfigSettings)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,valid)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,wifiWarningGiven)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,rebootRequired)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,enable)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,hotspotName)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,mdnsName)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,hasPassword)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,password)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,countryCode)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,channel)
|
||||
JSON_MAP_END()
|
||||
|
||||
bool WifiConfigSettings::ValidateCountryCode(const std::string&value)
|
||||
{
|
||||
if (value.size() < 2 || value.size() > 3) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
uint32_t pipedal::ChannelToWifiFrequency(const std::string &channel)
|
||||
{
|
||||
std::string t = channel;
|
||||
// remove dprecated band specs.
|
||||
if (t.size() > 1 && t[0] == 'a' || t[0] == 'g')
|
||||
{
|
||||
t = t.substr(1);
|
||||
}
|
||||
size_t size = t.length();
|
||||
|
||||
unsigned long long lChannel = std::stoull(t,&size);
|
||||
if (size != t.length())
|
||||
{
|
||||
throw invalid_argument("Expecting a number: '" + t + "'.");
|
||||
}
|
||||
return ChannelToWifiFrequency((uint32_t)lChannel);
|
||||
}
|
||||
|
||||
uint32_t pipedal::ChannelToWifiFrequency(uint32_t channel)
|
||||
{
|
||||
if (channel > 1000) // must be a frequency.
|
||||
{
|
||||
return channel;
|
||||
}
|
||||
// 2.4GHz.
|
||||
if (channel >= 1 && channel <= 13)
|
||||
{
|
||||
return 2412 + 5*(channel-1);
|
||||
}
|
||||
if (channel == 14)
|
||||
{
|
||||
return 2484;
|
||||
}
|
||||
// 802.11y
|
||||
if (channel >= 131 && channel < 137)
|
||||
{
|
||||
return 3660 + (channel-131)*5;
|
||||
}
|
||||
if (channel >= 32 && channel <= 68 && (channel & 1) == 0)
|
||||
{
|
||||
return 5160 + (channel-32)/2*10;
|
||||
}
|
||||
if (channel == 96) return 5480;
|
||||
|
||||
if (channel >= 100 && channel <= 196)
|
||||
{
|
||||
return 5500 + (channel-100)/5;
|
||||
}
|
||||
throw invalid_argument(SS("Invalid channel: " << channel));
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool WifiConfigSettings::ValidateChannel(const std::string&value)
|
||||
{
|
||||
// 1) frequency in khz.
|
||||
// 2) unadorned channel number 1, 2,3 &c.
|
||||
// 3) With band annotated: g1, a51.
|
||||
try {
|
||||
ChannelToWifiFrequency(value);
|
||||
return true;
|
||||
} catch (const std::string&/**/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// come back to this.
|
||||
return true;
|
||||
}
|
||||
|
||||
void WifiConfigSettings::ParseArguments(const std::vector<std::string> &argv)
|
||||
{
|
||||
this->valid_ = false;
|
||||
if (argv.size() != 4) {
|
||||
throw invalid_argument("Invalid number of arguments.");
|
||||
}
|
||||
this->enable_ = true;
|
||||
this->countryCode_ = argv[0];
|
||||
this->hotspotName_ = argv[1];
|
||||
this->mdnsName_ = this->hotspotName_;
|
||||
this->password_ = argv[2];
|
||||
this->channel_ = argv[3];
|
||||
this->hasPassword_ = this->password_.length() != 0;
|
||||
|
||||
if (!ValidateCountryCode(this->countryCode_))
|
||||
{
|
||||
throw invalid_argument("Invalid country code.");
|
||||
}
|
||||
if (this->hotspotName_.length() > 31) throw invalid_argument("Hotspot name is too long.");
|
||||
if (this->hotspotName_.length() < 1) throw invalid_argument("Hotspot name is too short.");
|
||||
if (this->password_.size() != 0 && this->password_.size() < 8) throw invalid_argument("Passphrase must be at least 8 characters long.");
|
||||
|
||||
if (!ValidateChannel(this->channel_))
|
||||
{
|
||||
throw invalid_argument("Channel is not valid.");
|
||||
}
|
||||
this->valid_ = true;
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
namespace pipedal {
|
||||
|
||||
|
||||
uint32_t ChannelToWifiFrequency(const std::string &channel);
|
||||
uint32_t ChannelToWifiFrequency(uint32_t channel);
|
||||
|
||||
class WifiConfigSettings {
|
||||
public:
|
||||
bool valid_ = false;
|
||||
bool wifiWarningGiven_ = false;
|
||||
bool rebootRequired_ = false;
|
||||
bool enable_ = false;
|
||||
std::string countryCode_ = "US"; // iso 3661
|
||||
std::string hotspotName_ = "pipedal";
|
||||
std::string mdnsName_ = "pipedal";
|
||||
bool hasPassword_ = false;
|
||||
std::string password_;
|
||||
std::string channel_ = "g6";
|
||||
|
||||
void ParseArguments(const std::vector<std::string> &arguments);
|
||||
static bool ValidateChannel(const std::string&value);
|
||||
static bool ValidateCountryCode(const std::string&value);
|
||||
public:
|
||||
DECLARE_JSON_MAP(WifiConfigSettings);
|
||||
};
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
// #include "pch.h"
|
||||
#include "WifiDirectConfigSettings.hpp"
|
||||
#include "P2pConfigFiles.hpp"
|
||||
|
||||
#include "WifiConfigSettings.hpp"
|
||||
#include <stdexcept>
|
||||
#include <random>
|
||||
#include "ConfigUtil.hpp"
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include "WriteTemplateFile.hpp"
|
||||
#include "ss.hpp"
|
||||
|
||||
|
||||
using namespace pipedal;
|
||||
using namespace std;
|
||||
|
||||
JSON_MAP_BEGIN(WifiDirectConfigSettings)
|
||||
JSON_MAP_REFERENCE(WifiDirectConfigSettings,valid)
|
||||
JSON_MAP_REFERENCE(WifiDirectConfigSettings,rebootRequired)
|
||||
JSON_MAP_REFERENCE(WifiDirectConfigSettings,enable)
|
||||
JSON_MAP_REFERENCE(WifiDirectConfigSettings,hotspotName)
|
||||
JSON_MAP_REFERENCE(WifiDirectConfigSettings,pinChanged)
|
||||
JSON_MAP_REFERENCE(WifiDirectConfigSettings,pin)
|
||||
JSON_MAP_REFERENCE(WifiDirectConfigSettings,countryCode)
|
||||
JSON_MAP_REFERENCE(WifiDirectConfigSettings,channel)
|
||||
JSON_MAP_END()
|
||||
|
||||
static std::string MakePin()
|
||||
{
|
||||
std::random_device rand_dev;
|
||||
|
||||
std::mt19937 gen(rand_dev());
|
||||
std::uniform_int_distribution<int> dist { 0,9};
|
||||
|
||||
std::stringstream s;
|
||||
for (size_t i = 0; i < 8; ++i)
|
||||
{
|
||||
int r = dist(gen);
|
||||
s << (char)('0' + r);
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
static void ValidatePin(const std::string&pin)
|
||||
{
|
||||
for (char c : pin)
|
||||
{
|
||||
if (c < '0' || c > '9')
|
||||
{
|
||||
throw invalid_argument("Pin must consist of digits only.");
|
||||
}
|
||||
}
|
||||
if (pin.length() != 8)
|
||||
{
|
||||
throw invalid_argument("Pin must have exctly 8 digits.");
|
||||
}
|
||||
}
|
||||
|
||||
void WifiDirectConfigSettings::ParseArguments(const std::vector<std::string> &arguments)
|
||||
{
|
||||
this->valid_ = false;
|
||||
|
||||
this->enable_ = true;
|
||||
|
||||
if (arguments.size() == 0)
|
||||
{
|
||||
Load();
|
||||
this->enable_ = true;
|
||||
} else {
|
||||
if (arguments.size() < 2 || arguments.size() > 4)
|
||||
{
|
||||
throw invalid_argument("Incorrect number of arguments supplied");
|
||||
}
|
||||
this->countryCode_ = arguments[0];
|
||||
this->hotspotName_ = arguments[1];
|
||||
this->pin_ = arguments.size() >= 3 ? arguments[2] : "";
|
||||
this->channel_ = arguments.size() >= 4? arguments[3]: "";
|
||||
}
|
||||
if (this->channel_ == "") {
|
||||
this->channel_ = "1";
|
||||
}
|
||||
size_t maxLength = 32-string("DIRECT-XX-").length();
|
||||
if (hotspotName_.size() >= maxLength) { throw invalid_argument("Device name is too long."); }
|
||||
if (hotspotName_.size() < 1) { throw invalid_argument("Device name is too short."); }
|
||||
if (!WifiConfigSettings::ValidateCountryCode(countryCode_))
|
||||
{
|
||||
throw invalid_argument("Invalid country code.");
|
||||
}
|
||||
if (pin_.size() == 0)
|
||||
{
|
||||
pin_ = MakePin();
|
||||
}
|
||||
ValidatePin(pin_); // throws.
|
||||
if (!WifiConfigSettings::ValidateChannel(channel_))
|
||||
{
|
||||
throw invalid_argument("Invalid channel.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WifiDirectConfigSettings::Save() const
|
||||
{
|
||||
// ******************* pipedal_p2pd ******
|
||||
{
|
||||
// ${COUNTRY_CODE}
|
||||
//${PIN}
|
||||
// ${DEVICE_NAME}
|
||||
// ${WIFI_GROUP_FREQUENCY}
|
||||
// ${INSTANCE_ID_FILE} /var/pipedal/instance_id
|
||||
|
||||
std::map<string, string> map;
|
||||
map["COUNTRY_CODE"] = this->countryCode_;
|
||||
map["PIN"] = this->pin_;
|
||||
map["DEVICE_NAME"] = ConfigUtil::QuoteString(this->hotspotName_);
|
||||
map["WIFI_CHANNEL"] = SS(this->channel_);
|
||||
map["ENABLED"] = this->enable_ ? "true": "false";
|
||||
map["WIFI_GROUP_FREQUENCY"] = SS(ChannelToWifiFrequency(this->channel_));
|
||||
map["INSTANCE_ID_FILE"] = DEVICE_GUID_FILE;
|
||||
|
||||
|
||||
WriteTemplateFile(map, PIPEDAL_P2PD_CONF_PATH);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
void WifiDirectConfigSettings::Load()
|
||||
{
|
||||
this->enable_ = false;
|
||||
std::string strEnable;
|
||||
if (ConfigUtil::GetConfigLine("/etc/pipedal/config/pipedal_p2pd.conf","enabled",&strEnable))
|
||||
{
|
||||
this->enable_ = (strEnable == "true" || strEnable == "1");
|
||||
}
|
||||
|
||||
|
||||
if (!ConfigUtil::GetConfigLine("/etc/pipedal/config/pipedal_p2pd.conf","p2p_device_name",&this->hotspotName_))
|
||||
{
|
||||
this->hotspotName_ = "PiPedal";
|
||||
}
|
||||
|
||||
if (!ConfigUtil::GetConfigLine("/etc/pipedal/config/pipedal_p2pd.conf","country_code",&this->countryCode_))
|
||||
{
|
||||
this->countryCode_ = "US";
|
||||
}
|
||||
if (!ConfigUtil::GetConfigLine("/etc/pipedal/config/pipedal_p2pd.conf","p2p_pin",&this->pin_))
|
||||
{
|
||||
this->pin_ = "12345678";
|
||||
}
|
||||
if (!ConfigUtil::GetConfigLine("/etc/pipedal/config/pipedal_p2pd.conf","wifiChannel",&this->channel_))
|
||||
{
|
||||
this->channel_ = "6";
|
||||
}
|
||||
try {
|
||||
ServiceConfiguration deviceIdFile;
|
||||
deviceIdFile.Load();
|
||||
if (deviceIdFile.deviceName == "")
|
||||
{
|
||||
|
||||
}
|
||||
this->hotspotName_ = deviceIdFile.deviceName;
|
||||
} catch (const std::exception &e)
|
||||
{
|
||||
}
|
||||
this->valid_ = true;
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
namespace pipedal {
|
||||
|
||||
class WifiDirectConfigSettings {
|
||||
public:
|
||||
bool valid_ = false;
|
||||
bool rebootRequired_ = false;
|
||||
bool enable_ = false;
|
||||
std::string countryCode_ = "US"; // iso 3661
|
||||
std::string hotspotName_ = "pipedal";
|
||||
bool pinChanged_ = false;
|
||||
std::string pin_;
|
||||
std::string channel_ = "6";
|
||||
|
||||
void ParseArguments(const std::vector<std::string> &arguments);
|
||||
void Save() const;
|
||||
void Load();
|
||||
|
||||
public:
|
||||
DECLARE_JSON_MAP(WifiDirectConfigSettings);
|
||||
};
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#include "pch.h"
|
||||
|
||||
#include "WriteTemplateFile.hpp"
|
||||
#include <fstream>
|
||||
#include "PiPedalException.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
void pipedal::WriteTemplateFile(
|
||||
const std::map<std::string, std::string> &map,
|
||||
const std::filesystem::path &outputFile)
|
||||
{
|
||||
std::string inputFile = "/etc/pipedal/config/templates/" + outputFile.filename().string() + ".template";
|
||||
WriteTemplateFile(map, inputFile,outputFile);
|
||||
}
|
||||
|
||||
void pipedal::WriteTemplateFile(
|
||||
const std::map<std::string, std::string> &map,
|
||||
const std::filesystem::path &inputFile,
|
||||
const std::filesystem::path &outputFile)
|
||||
{
|
||||
std::ofstream out(outputFile);
|
||||
std::ifstream in(inputFile);
|
||||
|
||||
if (!in.is_open())
|
||||
{
|
||||
throw PiPedalArgumentException("File not found: " + inputFile.string());
|
||||
}
|
||||
|
||||
while (in.peek() != -1)
|
||||
{
|
||||
char c = in.get();
|
||||
if (c == '$')
|
||||
{
|
||||
if (in.peek() == '{')
|
||||
{
|
||||
in.get();
|
||||
|
||||
std::stringstream sName;
|
||||
try
|
||||
{
|
||||
while ((c = in.get()) != '}')
|
||||
{
|
||||
sName.put(c);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
throw PiPedalException("Unexpected end of file. Expecting '}'.");
|
||||
}
|
||||
std::string s = map.at(sName.str());
|
||||
out << s;
|
||||
}
|
||||
else if (in.peek() == '$')
|
||||
{
|
||||
out.put(in.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
out.put(c);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out.put(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <filesystem>
|
||||
|
||||
|
||||
namespace pipedal {
|
||||
void WriteTemplateFile(
|
||||
const std::map<std::string,std::string> &map,
|
||||
const std::filesystem::path& inputFile,
|
||||
const std::filesystem::path& outputFile);
|
||||
|
||||
/**
|
||||
* @brief Write template file.
|
||||
*
|
||||
* The input file is
|
||||
*
|
||||
* /etc/pipedal/config/template/<filename>.template
|
||||
*
|
||||
* @param map Dictionary of values to substitute.
|
||||
* @param outputFile The full path of the output file.
|
||||
*/
|
||||
void WriteTemplateFile(
|
||||
const std::map<std::string,std::string> &map,
|
||||
const std::filesystem::path &outputFile);
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
namespace p2p {
|
||||
|
||||
template <typename T> class autoptr_vector
|
||||
{
|
||||
std::vector<T*> v;
|
||||
private:
|
||||
// no copy no move.
|
||||
autoptr_vector(const autoptr_vector<T> &) = delete;
|
||||
autoptr_vector(autoptr_vector<T> && ) = delete;
|
||||
public:
|
||||
using iterator = std::vector<T*>::const_iterator;
|
||||
|
||||
autoptr_vector() { }
|
||||
autoptr_vector(std::initializer_list<T*> l)
|
||||
:v(l)
|
||||
{
|
||||
|
||||
}
|
||||
~autoptr_vector() {
|
||||
for (T *i: v)
|
||||
{
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
void push_back(T*value) { v.push_back(value);}
|
||||
|
||||
iterator begin() const { return v.begin(); }
|
||||
iterator end() const { return v.end(); }
|
||||
|
||||
T*operator[](size_t index) const { return v[index]; }
|
||||
|
||||
size_t size() const { return v.size(); }
|
||||
};
|
||||
}
|
||||
+11
-2
@@ -19,6 +19,10 @@ std::vector<std::string> unknownLicense ={
|
||||
"These copyright notices were found, but we were unable to automatically identify an associated license."
|
||||
};
|
||||
|
||||
bool ContainsName(const std::string ©right, const char*name)
|
||||
{
|
||||
return copyright.find(name) != -1;
|
||||
}
|
||||
|
||||
class Copyrights {
|
||||
struct License {
|
||||
@@ -56,7 +60,7 @@ class Copyrights {
|
||||
|
||||
}
|
||||
|
||||
void addCopyright(std::string license,const std::string ©right)
|
||||
void addCopyright(std::string license,std::string copyright)
|
||||
{
|
||||
if (copyright.length() == 0) return;
|
||||
if (copyright == "no-info-found") return;
|
||||
@@ -65,6 +69,11 @@ class Copyrights {
|
||||
{
|
||||
license = UNKNOWN_LICENSE;
|
||||
}
|
||||
const static std::string UNSPECIFIED = "(unspecified) ";
|
||||
if (copyright.starts_with(UNSPECIFIED))
|
||||
{
|
||||
copyright = copyright.substr(UNSPECIFIED.length());
|
||||
}
|
||||
auto currentLicense = licenseMap.find(license);
|
||||
if (currentLicense == licenseMap.end())
|
||||
{
|
||||
@@ -342,7 +351,7 @@ int main(int argc, const char*argv[])
|
||||
{
|
||||
cerr << endl;
|
||||
}
|
||||
cout << "Syntax: processcopyrights --projectCopyright debian/copyright -output <oututfile> [<dependents>]*" << endl;
|
||||
cout << "Syntax: processcopyrights --projectCopyright debian/copyright -output <outputfile> [<dependents>]*" << endl;
|
||||
return helpError? EXIT_FAILURE: EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
-641
@@ -1,641 +0,0 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "pch.h"
|
||||
#include "json.hpp"
|
||||
#include <string_view>
|
||||
#include <cctype>
|
||||
#include "PiPedalException.hpp"
|
||||
#include "json_variant.hpp"
|
||||
#include "util.hpp"
|
||||
using namespace pipedal;
|
||||
|
||||
|
||||
|
||||
|
||||
uint32_t json_writer::continuation_byte(std::string_view::iterator &p, std::string_view::const_iterator end)
|
||||
{
|
||||
if (p == end)
|
||||
throw_encoding_error();
|
||||
uint8_t c = *p++;
|
||||
if ((c & UTF8_CONTINUATION_MASK) != UTF8_CONTINUATION_BITS)
|
||||
throw_encoding_error();
|
||||
return c & 0x3F;
|
||||
}
|
||||
static char hex(int v)
|
||||
{
|
||||
if (v < 10)
|
||||
return (char)('0' + v);
|
||||
return (char)('A' + v - 10);
|
||||
}
|
||||
|
||||
void json_writer::throw_encoding_error()
|
||||
{
|
||||
throw std::invalid_argument("Invalid UTF-8 character sequence");
|
||||
}
|
||||
void json_writer::write_utf16_char(uint16_t uc)
|
||||
{
|
||||
os << "\\u"
|
||||
<< hex((int)((uc >> 12) & 0x0F))
|
||||
<< hex((int)((uc >> 8) & 0x0F))
|
||||
<< hex((int)((uc >> 4) & 0x0F))
|
||||
<< hex((int)((uc)&0x0F));
|
||||
}
|
||||
|
||||
void json_writer::write(string_view v,bool enforceValidUtf8Encoding)
|
||||
{
|
||||
// convert to utf-32.
|
||||
// convert utf-32 to normalized utf-16.
|
||||
// write non-7-bit and unsafe characters as \uHHHH.
|
||||
|
||||
auto p = v.begin();
|
||||
os << '"';
|
||||
while (p != v.end())
|
||||
{
|
||||
uint32_t uc;
|
||||
uint8_t c = (uint8_t)*p++;
|
||||
if ((c & UTF8_ONE_BYTE_MASK) == UTF8_ONE_BYTE_BITS)
|
||||
{
|
||||
uc = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t c2 = continuation_byte(p, v.end());
|
||||
|
||||
if ((c & UTF8_TWO_BYTES_MASK) == UTF8_TWO_BYTES_BITS)
|
||||
{
|
||||
uint32_t c1 = c & (uint32_t)(~UTF8_TWO_BYTES_MASK);
|
||||
if (c1 <= 1 && enforceValidUtf8Encoding)
|
||||
{
|
||||
// overlong encoding.
|
||||
throw_encoding_error();
|
||||
}
|
||||
uc = (c1 << 6) | c2;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t c3 = continuation_byte(p, v.end());
|
||||
|
||||
if ((c & UTF8_THREE_BYTES_MASK) == UTF8_THREE_BYTES_BITS)
|
||||
{
|
||||
uint32_t c1 = c & (uint32_t)~UTF8_THREE_BYTES_MASK;
|
||||
if (c1 == 0 && c2 < 0x20 && enforceValidUtf8Encoding)
|
||||
{
|
||||
// overlong encoding.
|
||||
throw_encoding_error();
|
||||
}
|
||||
|
||||
uc = (c1) << 12 | (c2 << 6) | c3;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t c4 = continuation_byte(p, v.end());
|
||||
if ((c & UTF8_FOUR_BYTES_MASK) == UTF8_FOUR_BYTES_BITS)
|
||||
{
|
||||
uint32_t c1 = c & (uint32_t)~UTF8_FOUR_BYTES_MASK;
|
||||
if (c1 == 0 && c2 < 0x10 && enforceValidUtf8Encoding)
|
||||
{
|
||||
// overlong encoding.
|
||||
throw_encoding_error();
|
||||
}
|
||||
uc = (c1 << 18) | (c2 << 12) | (c3 << 6) | c4;
|
||||
} else {
|
||||
// outside legal UCS range.
|
||||
throw_encoding_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((uc >= UTF16_SURROGATE_1_BASE && uc <= UTF16_SURROGATE_1_BASE + UTF16_SURROGATE_MASK) || (uc >= UTF16_SURROGATE_2_BASE && uc <= UTF16_SURROGATE_2_BASE + UTF16_SURROGATE_MASK))
|
||||
{
|
||||
// MUST not encode UTF16 surrogates in UTF8.
|
||||
throw_encoding_error();
|
||||
}
|
||||
|
||||
if (uc == '"' || uc == '\\')
|
||||
{
|
||||
os << (char)'\\';
|
||||
os << (char)uc;
|
||||
}
|
||||
else if (uc >= 0x20 && uc < 0x80)
|
||||
{
|
||||
os << (char)uc;
|
||||
}
|
||||
else if (uc == '\r')
|
||||
{
|
||||
os << "\\r";
|
||||
}
|
||||
else if (uc == '\n')
|
||||
{
|
||||
os << "\\n";
|
||||
}
|
||||
else if (uc == '\t')
|
||||
{
|
||||
os << "\\t";
|
||||
}
|
||||
else if (uc < 0x10000ul)
|
||||
{
|
||||
write_utf16_char((uint16_t)uc);
|
||||
}
|
||||
else
|
||||
{
|
||||
// write UTF-16 surrogate pair.
|
||||
uc -= 0x10000;
|
||||
|
||||
uint16_t s1 = (uint16_t)(UTF16_SURROGATE_1_BASE + ((uc >> 10) & 0x3FFu));
|
||||
uint16_t s2 = (uint16_t)(UTF16_SURROGATE_2_BASE + (uc & 0x03FFu));
|
||||
// surrogate pair.
|
||||
write_utf16_char(s1);
|
||||
write_utf16_char(s2);
|
||||
}
|
||||
}
|
||||
os << '"';
|
||||
}
|
||||
|
||||
void json_writer::indent()
|
||||
{
|
||||
if (!compressed)
|
||||
{
|
||||
for (int i = 0; i < indent_level; ++i)
|
||||
{
|
||||
os << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void json_writer::start_object()
|
||||
{
|
||||
os << "{" << CRLF;
|
||||
indent_level += TAB_SIZE;
|
||||
}
|
||||
void json_writer::end_object()
|
||||
{
|
||||
indent_level -= TAB_SIZE;
|
||||
os << CRLF;
|
||||
indent();
|
||||
os << "}";
|
||||
}
|
||||
|
||||
void json_writer::start_array()
|
||||
{
|
||||
indent();
|
||||
os << "[" << CRLF;
|
||||
indent_level += TAB_SIZE;
|
||||
}
|
||||
void json_writer::end_array()
|
||||
{
|
||||
indent_level -= TAB_SIZE;
|
||||
indent();
|
||||
os << "]" << CRLF;
|
||||
}
|
||||
|
||||
|
||||
void json_reader::skip_whitespace()
|
||||
{
|
||||
char c;
|
||||
while (true)
|
||||
{
|
||||
int ic = is_.peek();
|
||||
if (ic == -1)
|
||||
break;
|
||||
if (is_whitespace((char)ic))
|
||||
{
|
||||
c = get();
|
||||
}
|
||||
else if (ic == '/')
|
||||
{
|
||||
get();
|
||||
int c2 = is_.peek();
|
||||
if (c2 == '/') {
|
||||
// skip to end of line.
|
||||
get();
|
||||
while (true) {
|
||||
c2 = is_.peek();
|
||||
if (c2 == '\r' || c2 == '\n')
|
||||
{
|
||||
get(); // and continue.
|
||||
break;
|
||||
}
|
||||
if (c2 == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
get();
|
||||
}
|
||||
} else if (c2 == '*') {
|
||||
get();
|
||||
int level = 1;
|
||||
while (true)
|
||||
{
|
||||
c = get();
|
||||
if (c == '*' && is_.peek() == '/')
|
||||
{
|
||||
get();
|
||||
if (--level == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c == '/' && is_.peek() == '*')
|
||||
{
|
||||
get();
|
||||
++level;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
throw_format_error();
|
||||
}
|
||||
} else
|
||||
{
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string json_reader::read_string()
|
||||
{
|
||||
// To completely normalize UTF-32 values we must covert to UTF-16, resolve surrogate pairs, and then convert UTF-32 to UTF-8.
|
||||
|
||||
|
||||
skip_whitespace();
|
||||
char c;
|
||||
char startingCharacter;
|
||||
startingCharacter = get();
|
||||
if (startingCharacter != '\'' && startingCharacter != '\"')
|
||||
{
|
||||
throw_format_error();
|
||||
}
|
||||
std::stringstream s;
|
||||
|
||||
while (true)
|
||||
{
|
||||
c = get();
|
||||
if (c == startingCharacter)
|
||||
{
|
||||
if (is_.peek() == startingCharacter) // "" -> "
|
||||
{
|
||||
get();
|
||||
s.put(c);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c != '\\')
|
||||
{
|
||||
s << (char)c;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = get();
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
default:
|
||||
s << c;
|
||||
break;
|
||||
case 'r':
|
||||
s << '\r';
|
||||
break;
|
||||
case 'b':
|
||||
s << '\b';
|
||||
break;
|
||||
case 'f':
|
||||
s << '\f';
|
||||
break;
|
||||
case 'n':
|
||||
s << '\n';
|
||||
break;
|
||||
case 't':
|
||||
s << '\t';
|
||||
break;
|
||||
case 'u':
|
||||
{
|
||||
uint32_t uc = read_u_escape();
|
||||
if (uc >= UTF16_SURROGATE_1_BASE && uc <= UTF16_SURROGATE_1_BASE + UTF16_SURROGATE_MASK)
|
||||
{
|
||||
// MUST be a UTF16_SURROGATE 2 to be legal.
|
||||
c = get();
|
||||
if (c != '\\')
|
||||
throw_format_error("Invalid UTF16 surrogate pair");
|
||||
c = get();
|
||||
if (c != 'u')
|
||||
throw_format_error("Invalid UTF16 surrogate pair");
|
||||
uint16_t uc2 = read_u_escape();
|
||||
if (uc2 < UTF16_SURROGATE_2_BASE || uc2 > UTF16_SURROGATE_2_BASE + UTF16_SURROGATE_MASK)
|
||||
{
|
||||
throw_format_error("Invalid UTF16 surrogate pair");
|
||||
}
|
||||
uc = ((uc & UTF16_SURROGATE_MASK) << 10) + (uc2 & UTF16_SURROGATE_MASK) + 0x10000U;
|
||||
}
|
||||
HtmlHelper::utf32_to_utf8_stream(s, uc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
uint16_t json_reader::read_hex()
|
||||
{
|
||||
char c;
|
||||
c = get();
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
throw_format_error("Invalid \\u escape character");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t json_reader::read_u_escape()
|
||||
{
|
||||
uint16_t c1 = read_hex();
|
||||
uint16_t c2 = read_hex();
|
||||
uint16_t c3 = read_hex();
|
||||
uint16_t c4 = read_hex();
|
||||
|
||||
return (c1 << 12) + (c2 << 8) + (c3 << 4) + c4;
|
||||
}
|
||||
|
||||
bool json_reader::is_complete()
|
||||
{
|
||||
skip_whitespace();
|
||||
return is_.peek() == -1;
|
||||
}
|
||||
|
||||
void json_reader::consumeToken(const char*expectedToken, const char*errorMessage)
|
||||
{
|
||||
skip_whitespace();
|
||||
|
||||
const char*p = expectedToken;
|
||||
while (*p != '\0')
|
||||
{
|
||||
char expectedChar = *p++;
|
||||
int c = is_.get();
|
||||
if (expectedChar != c) {
|
||||
this->throw_format_error(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void json_reader::consume(char expected)
|
||||
{
|
||||
skip_whitespace();
|
||||
char c;
|
||||
c = get();
|
||||
if (c != expected)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "Expecting '" << expected << "'";
|
||||
throw_format_error(s.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void json_reader::skip_property()
|
||||
{
|
||||
skip_whitespace();
|
||||
int c = is_.peek();
|
||||
switch (c)
|
||||
{
|
||||
case -1:
|
||||
throw_format_error("Premature end of file.");
|
||||
break;
|
||||
case '[':
|
||||
skip_array();
|
||||
break;
|
||||
case '{':
|
||||
skip_object();
|
||||
break;
|
||||
case '\"':
|
||||
skip_string();
|
||||
break;
|
||||
case 't':
|
||||
case 'f':
|
||||
read_boolean();
|
||||
break;
|
||||
case 'n':
|
||||
read_null();
|
||||
break;
|
||||
default:
|
||||
skip_number();
|
||||
break;
|
||||
}
|
||||
}
|
||||
void json_reader::skip_string()
|
||||
{
|
||||
consume('"');
|
||||
while (true)
|
||||
{
|
||||
int c;
|
||||
c = get();
|
||||
if (c == -1) throw_format_error("Premature end of file.");
|
||||
|
||||
if (c == '\"')
|
||||
{
|
||||
if (peek() == '\"')
|
||||
{
|
||||
get();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c == '\\')
|
||||
{
|
||||
c = get(); // all of standard escapes, enough to get past \u
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void json_reader::skip_number()
|
||||
{
|
||||
skip_whitespace();
|
||||
int c;
|
||||
if (is_.peek() == '-')
|
||||
{
|
||||
get();
|
||||
}
|
||||
if (!std::isdigit(is_.peek()))
|
||||
{
|
||||
throw_format_error("Expecting a number.");
|
||||
}
|
||||
while (std::isdigit(is_.peek()))
|
||||
{
|
||||
get();
|
||||
}
|
||||
if (is_.peek() == '.')
|
||||
{
|
||||
get();
|
||||
}
|
||||
while (std::isdigit(is_.peek()))
|
||||
{
|
||||
get();
|
||||
}
|
||||
c = is_.peek();
|
||||
if (c == 'e' || c == 'E')
|
||||
{
|
||||
get();
|
||||
c = is_.peek();
|
||||
if (c == '+' || c == 'i')
|
||||
{
|
||||
get();
|
||||
}
|
||||
while (std::isdigit(is_.peek()))
|
||||
{
|
||||
get();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
void json_reader::skip_array()
|
||||
{
|
||||
int c;
|
||||
consume('[');
|
||||
|
||||
while (true)
|
||||
{
|
||||
c = peek();
|
||||
if (c == -1) throw_format_error("Premature end of file.");
|
||||
|
||||
if (c == ']')
|
||||
{
|
||||
c = get();
|
||||
break;
|
||||
}
|
||||
skip_property();
|
||||
skip_whitespace();
|
||||
if (is_.peek() == ',')
|
||||
{
|
||||
c = get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void json_reader::skip_object()
|
||||
{
|
||||
int c;
|
||||
consume('{');
|
||||
while (true)
|
||||
{
|
||||
c = peek();
|
||||
if (c == -1) throw_format_error("Premature end of file.");
|
||||
if (c == '}')
|
||||
{
|
||||
c = get();
|
||||
break;
|
||||
}
|
||||
skip_string(); // name.
|
||||
consume(':');
|
||||
skip_object();
|
||||
if (peek() == ',')
|
||||
{
|
||||
consume(',');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void json_reader::read_object_start()
|
||||
{
|
||||
skip_whitespace();
|
||||
|
||||
char c;
|
||||
c = get();
|
||||
if (c != '{')
|
||||
throw_format_error();
|
||||
}
|
||||
|
||||
std::string json_reader::readToken()
|
||||
{
|
||||
skip_whitespace();
|
||||
|
||||
std::stringstream s;
|
||||
while (true)
|
||||
{
|
||||
int ic = is_.peek();
|
||||
if (ic == -1)
|
||||
break;
|
||||
char c = (char)ic;
|
||||
if (!std::isalpha(c))
|
||||
break;
|
||||
is_.get();
|
||||
s << c;
|
||||
}
|
||||
return s.str();
|
||||
|
||||
}
|
||||
|
||||
void json_reader::read_null()
|
||||
{
|
||||
std::string s = readToken();
|
||||
if (s != "null")
|
||||
{
|
||||
throw_format_error("Format error. Expecting 'null'.");
|
||||
|
||||
}
|
||||
}
|
||||
bool json_reader::read_boolean()
|
||||
{
|
||||
std::string ss = readToken();
|
||||
if (ss == "true")
|
||||
return true;
|
||||
if (ss == "false")
|
||||
return false;
|
||||
|
||||
throw_format_error("Format error. Expectiong 'true' or 'false'");
|
||||
return false;
|
||||
}
|
||||
|
||||
void json_reader::throw_format_error(const char*error)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << error;
|
||||
s << ", near: '";
|
||||
skip_whitespace();
|
||||
if (is_.peek() == -1) {
|
||||
s << "<eof>";
|
||||
} else {
|
||||
for (int i = 0; i < 40; ++i)
|
||||
{
|
||||
int c = get();
|
||||
if (c == -1) break;
|
||||
if (c == '\r') {
|
||||
s << "\\r";
|
||||
} else if (c == '\n')
|
||||
{
|
||||
s << "\\n";
|
||||
} else {
|
||||
s << (char)c;
|
||||
}
|
||||
}
|
||||
}
|
||||
s << "'.";
|
||||
std::string message = s.str();
|
||||
throw PiPedalException(message);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// void json_writer::write(const json_variant &value)
|
||||
// {
|
||||
// ((JsonSerializable *)&value)->write_json(*this);
|
||||
// }
|
||||
-1078
File diff suppressed because it is too large
Load Diff
@@ -1,560 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "json_variant.hpp"
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
|
||||
json_variant::~json_variant()
|
||||
{
|
||||
free();
|
||||
}
|
||||
|
||||
void json_variant::free()
|
||||
{
|
||||
// in-place deletion.
|
||||
switch (content_type)
|
||||
{
|
||||
case ContentType::String:
|
||||
memString().std::string::~string();
|
||||
break;
|
||||
case ContentType::Object:
|
||||
memObject().std::shared_ptr<json_object>::~shared_ptr();
|
||||
break;
|
||||
case ContentType::Array:
|
||||
memArray().std::shared_ptr<json_array>::~shared_ptr();
|
||||
break;
|
||||
}
|
||||
content_type = ContentType::Null;
|
||||
}
|
||||
|
||||
|
||||
json_variant::json_variant(json_variant &&other)
|
||||
{
|
||||
this->content_type = ContentType::Null;
|
||||
switch (other.content_type)
|
||||
{
|
||||
case ContentType::Null:
|
||||
break;
|
||||
case ContentType::Bool:
|
||||
this->content.bool_value = other.content.bool_value;
|
||||
break;
|
||||
case ContentType::Number:
|
||||
this->content.double_value = other.content.double_value;
|
||||
break;
|
||||
case ContentType::String:
|
||||
new (content.mem) std::string(std::move(other.memString()));
|
||||
this->content_type = ContentType::String;
|
||||
return;
|
||||
case ContentType::Object:
|
||||
new (content.mem) std::shared_ptr<json_object>{std::move(other.memObject())};
|
||||
this->content_type = ContentType::Object;
|
||||
return;
|
||||
case ContentType::Array:
|
||||
new (content.mem) std::shared_ptr<json_array>{std::move(other.memArray())};
|
||||
this->content_type = ContentType::Array;
|
||||
return;
|
||||
}
|
||||
this->content_type = other.content_type;
|
||||
other.content_type = ContentType::Null;
|
||||
}
|
||||
|
||||
json_variant &json_variant::operator=(const json_variant &other)
|
||||
{
|
||||
free();
|
||||
switch (other.content_type)
|
||||
{
|
||||
case ContentType::Null:
|
||||
break;
|
||||
case ContentType::Bool:
|
||||
this->content.bool_value = other.content.bool_value;
|
||||
break;
|
||||
case ContentType::Number:
|
||||
this->content.double_value = other.content.double_value;
|
||||
break;
|
||||
case ContentType::String:
|
||||
new (content.mem) std::string(other.memString());
|
||||
break;
|
||||
case ContentType::Object:
|
||||
new (content.mem) std::shared_ptr<json_object>{other.memObject()};
|
||||
break;
|
||||
case ContentType::Array:
|
||||
new (content.mem) std::shared_ptr<json_array>{other.memArray()};
|
||||
this->content_type = ContentType::Array;
|
||||
break;
|
||||
}
|
||||
this->content_type = other.content_type;
|
||||
return *this;
|
||||
|
||||
}
|
||||
|
||||
json_variant::json_variant(const json_variant &other)
|
||||
{
|
||||
this->content_type = ContentType::Null;
|
||||
switch (other.content_type)
|
||||
{
|
||||
case ContentType::Null:
|
||||
break;
|
||||
case ContentType::Bool:
|
||||
this->content.bool_value = other.content.bool_value;
|
||||
break;
|
||||
case ContentType::Number:
|
||||
this->content.double_value = other.content.double_value;
|
||||
break;
|
||||
case ContentType::String:
|
||||
new (content.mem) std::string(other.memString());
|
||||
this->content_type = ContentType::String;
|
||||
return;
|
||||
case ContentType::Object:
|
||||
new (content.mem) std::shared_ptr<json_object>{other.memObject()};
|
||||
this->content_type = ContentType::Object;
|
||||
return;
|
||||
case ContentType::Array:
|
||||
new (content.mem) std::shared_ptr<json_array>{other.memArray()};
|
||||
this->content_type = ContentType::Array;
|
||||
return;
|
||||
}
|
||||
this->content_type = other.content_type;
|
||||
}
|
||||
|
||||
void json_variant::write_float_value(json_writer &writer, double value) const
|
||||
{
|
||||
writer.write(value);
|
||||
}
|
||||
|
||||
void json_variant::write_double_value(json_writer &writer, double value) const
|
||||
{
|
||||
writer.write(value);
|
||||
}
|
||||
|
||||
void json_variant::read_json(json_reader &reader)
|
||||
{
|
||||
int v = reader.peek();
|
||||
if (v == '[')
|
||||
{
|
||||
json_array array;
|
||||
reader.read(&array);
|
||||
(*this) = std::move(array);
|
||||
}
|
||||
else if (v == '{')
|
||||
{
|
||||
json_object object;
|
||||
reader.read(&object);
|
||||
(*this) = std::move(object);
|
||||
}
|
||||
else if (v == '\"')
|
||||
{
|
||||
std::string s;
|
||||
reader.read(&s);
|
||||
(*this) = std::move(s);
|
||||
}
|
||||
else if (v == 'n')
|
||||
{
|
||||
reader.read_null();
|
||||
(*this) = json_null();
|
||||
}
|
||||
else if (v == 't' || v == 'f')
|
||||
{
|
||||
bool b;
|
||||
reader.read(&b);
|
||||
(*this) = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
// it's a number.
|
||||
double v;
|
||||
reader.read(&v);
|
||||
(*this) = v;
|
||||
}
|
||||
}
|
||||
void json_variant::write_json(json_writer &writer) const
|
||||
{
|
||||
switch (content_type)
|
||||
{
|
||||
case ContentType::Null:
|
||||
writer.write_raw("null");
|
||||
break;
|
||||
case ContentType::Bool:
|
||||
writer.write(as_bool());
|
||||
break;
|
||||
case ContentType::Number:
|
||||
write_double_value(writer, as_number());
|
||||
break;
|
||||
case ContentType::String:
|
||||
writer.write(as_string());
|
||||
break;
|
||||
case ContentType::Object:
|
||||
writer.write(as_object());
|
||||
break;
|
||||
case ContentType::Array:
|
||||
writer.write(as_array());
|
||||
break;
|
||||
default:
|
||||
throw std::logic_error("Invalid variant type");
|
||||
}
|
||||
}
|
||||
|
||||
bool json_array::operator==(const json_array &other) const
|
||||
{
|
||||
if (!(this->size() == other.size()))
|
||||
return false;
|
||||
for (size_t i = 0; i < this->size(); ++i)
|
||||
{
|
||||
if (!((*this)[i] == other[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void json_array::read_json(json_reader &reader)
|
||||
{
|
||||
reader.read(&(this->values));
|
||||
}
|
||||
|
||||
void json_array::write_json(json_writer &writer) const
|
||||
{
|
||||
writer.start_array();
|
||||
bool first = true;
|
||||
for (auto &value : values)
|
||||
{
|
||||
if (!first)
|
||||
writer.write_raw(",");
|
||||
first = false;
|
||||
writer.write(value);
|
||||
}
|
||||
writer.end_array();
|
||||
}
|
||||
|
||||
|
||||
json_object::iterator json_object::find(const std::string& key)
|
||||
{
|
||||
for (auto i = begin(); i != end(); ++i)
|
||||
{
|
||||
if (i->first == key)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
json_object::const_iterator json_object::find(const std::string&key) const
|
||||
{
|
||||
for (auto i = begin(); i != end(); ++i)
|
||||
{
|
||||
if (i->first == key)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool json_object::operator==(const json_object &other) const
|
||||
{
|
||||
|
||||
for (const auto &pair : this->values)
|
||||
{
|
||||
auto index = other.find(pair.first);
|
||||
if (index == other.end())
|
||||
return false;
|
||||
if (!(index->second == pair.second))
|
||||
return false;
|
||||
}
|
||||
for (const auto &pair : other.values)
|
||||
{
|
||||
auto index = this->find(pair.first);
|
||||
if (index == this->end())
|
||||
return false;
|
||||
if (!(index->second == pair.second))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void json_object::read_json(json_reader &reader)
|
||||
{
|
||||
reader.start_object();
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (reader.peek() == '}') break;
|
||||
|
||||
std::string key;
|
||||
json_variant value;
|
||||
reader.read(&key);
|
||||
reader.consume(':');
|
||||
reader.read(&value);
|
||||
|
||||
(*this)[key] = value;
|
||||
if (reader.peek() == ',')
|
||||
{
|
||||
reader.consume(',');
|
||||
}
|
||||
}
|
||||
|
||||
reader.end_object();
|
||||
}
|
||||
void json_object::write_json(json_writer &writer) const
|
||||
{
|
||||
writer.start_object();
|
||||
bool first = true;
|
||||
for (auto &value : values)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
writer.write_raw(",");
|
||||
}
|
||||
first = false;
|
||||
writer.write(value.first);
|
||||
writer.write_raw(": ");
|
||||
writer.writeRawWritable(value.second);
|
||||
}
|
||||
writer.end_object();
|
||||
}
|
||||
|
||||
json_variant &json_array::at(size_t index)
|
||||
{
|
||||
check_index(index);
|
||||
return values.at(index);
|
||||
}
|
||||
const json_variant &json_array::at(size_t index) const
|
||||
{
|
||||
check_index(index);
|
||||
return values.at(index);
|
||||
}
|
||||
|
||||
size_t json_variant::size() const
|
||||
{
|
||||
if (content_type == ContentType::Array)
|
||||
return as_array()->size();
|
||||
if (content_type == ContentType::Object)
|
||||
{
|
||||
return as_object()->size();
|
||||
}
|
||||
throw std::logic_error("Not supported.");
|
||||
}
|
||||
|
||||
json_variant &json_object::at(const std::string &index) {
|
||||
for (auto&entry: values)
|
||||
{
|
||||
if (entry.first == index)
|
||||
{
|
||||
return entry.second;
|
||||
}
|
||||
}
|
||||
throw std::logic_error("Not found.");
|
||||
values.push_back(std::pair(index,json_variant()));
|
||||
return values[values.size()-1].second;
|
||||
}
|
||||
const json_variant &json_object::at(const std::string &index) const {
|
||||
for (const auto&entry: values)
|
||||
{
|
||||
if (entry.first == index)
|
||||
{
|
||||
return entry.second;
|
||||
}
|
||||
}
|
||||
throw std::logic_error("Not found.");
|
||||
}
|
||||
|
||||
json_variant &json_object::operator[](const std::string &index)
|
||||
{
|
||||
for (auto&entry: values)
|
||||
{
|
||||
if (entry.first == index)
|
||||
{
|
||||
return entry.second;
|
||||
}
|
||||
}
|
||||
values.push_back(std::pair(index,json_variant()));
|
||||
return values[values.size()-1].second;
|
||||
}
|
||||
const json_variant &json_object::operator[](const std::string &index) const
|
||||
{
|
||||
return at(index);
|
||||
}
|
||||
bool json_object::contains(const std::string &index) const
|
||||
{
|
||||
return find(index) != end();
|
||||
}
|
||||
|
||||
json_variant &json_variant::operator=(bool value)
|
||||
{
|
||||
free();
|
||||
this->content_type = ContentType::Bool;
|
||||
this->content.bool_value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
json_variant &json_variant::operator=(double value)
|
||||
{
|
||||
free();
|
||||
this->content_type = ContentType::Number;
|
||||
this->content.double_value = value;
|
||||
return *this;
|
||||
}
|
||||
json_variant &json_variant::operator=(const std::string &value)
|
||||
{
|
||||
free();
|
||||
this->content_type = ContentType::String;
|
||||
new (this->content.mem) std::string(value); // in-place constructor.
|
||||
return *this;
|
||||
}
|
||||
json_variant &json_variant::operator=(std::string &&value)
|
||||
{
|
||||
free();
|
||||
this->content_type = ContentType::String;
|
||||
new (this->content.mem) std::string(std::move(value)); // in-place constructor.
|
||||
return *this;
|
||||
}
|
||||
json_variant &json_variant::operator=(json_object &&value)
|
||||
{
|
||||
free();
|
||||
new (this->content.mem) std::shared_ptr<json_object>{new json_object(std::move(value))};
|
||||
this->content_type = ContentType::Object;
|
||||
return *this;
|
||||
}
|
||||
json_variant &json_variant::operator=(json_array &&value)
|
||||
{
|
||||
free();
|
||||
this->content_type = ContentType::Array;
|
||||
new (this->content.mem) std::shared_ptr<json_array>{new json_array(std::move(value))};
|
||||
return *this;
|
||||
}
|
||||
json_variant &json_variant::operator=(json_variant &&value)
|
||||
{
|
||||
if (this->content_type == value.content_type)
|
||||
{
|
||||
switch (this->content_type)
|
||||
{
|
||||
case ContentType::String:
|
||||
std::swap(this->memString(), value.memString());
|
||||
return *this;
|
||||
case ContentType::Array:
|
||||
std::swap(this->memArray(), value.memArray());
|
||||
return *this;
|
||||
case ContentType::Object:
|
||||
std::swap(this->memObject(), value.memObject());
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
free();
|
||||
switch (value.content_type)
|
||||
{
|
||||
case ContentType::String:
|
||||
new (content.mem) std::string(std::move(value.memString()));
|
||||
value.memString().std::string::~string();
|
||||
break;
|
||||
case ContentType::Object:
|
||||
new (content.mem) std::shared_ptr<json_object>(std::move(value.memObject()));
|
||||
value.memObject().std::shared_ptr<json_object>::~shared_ptr();
|
||||
break;
|
||||
case ContentType::Array:
|
||||
new (content.mem) std::shared_ptr<json_array>(std::move(value.memArray()));
|
||||
value.memArray().std::shared_ptr<json_array>::~shared_ptr();
|
||||
break;
|
||||
default:
|
||||
// undifferentiated copy of POD types.
|
||||
*(uint64_t *)(this->content.mem) = *(uint64_t *)(value.content.mem);
|
||||
break;
|
||||
}
|
||||
this->content_type = value.content_type;
|
||||
value.content_type = ContentType::Null;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool json_variant::operator==(const json_variant &other) const
|
||||
{
|
||||
if (this->content_type != other.content_type)
|
||||
return false;
|
||||
|
||||
switch (this->content_type)
|
||||
{
|
||||
case ContentType::Null:
|
||||
return true;
|
||||
case ContentType::Bool:
|
||||
return as_bool() == other.as_bool();
|
||||
break;
|
||||
case ContentType::Number:
|
||||
return as_number() == other.as_number();
|
||||
case ContentType::String:
|
||||
return as_string() == other.as_string();
|
||||
case ContentType::Array:
|
||||
return *(as_array().get()) == *(other.as_array().get());
|
||||
case ContentType::Object:
|
||||
return *(as_object().get()) == *(other.as_object().get());;
|
||||
default:
|
||||
throw std::logic_error("Invalid content_type.");
|
||||
}
|
||||
}
|
||||
json_array::json_array(json_array&&other)
|
||||
: values(std::move(other.values))
|
||||
{
|
||||
++allocation_count_;
|
||||
}
|
||||
json_object::json_object(json_object&&other)
|
||||
:values(std::move(other.values))
|
||||
{
|
||||
++allocation_count_;
|
||||
}
|
||||
|
||||
bool json_variant::contains(const std::string &index) const
|
||||
{
|
||||
if (is_object())
|
||||
{
|
||||
return as_object()->contains(index);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string json_variant::to_string() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
json_writer writer(ss);
|
||||
writer.write(*this);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
json_variant::json_variant(const char*sz)
|
||||
: json_variant(std::string(sz))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*static*/ json_null json_null::instance;
|
||||
/*static*/ int64_t json_array::allocation_count_ = 0; // strictly for testing purposes. not thread safe.
|
||||
/*static*/ int64_t json_object::allocation_count_ = 0; // strictly for testing purposes. not thread safe.
|
||||
|
||||
@@ -1,550 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <map>
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include "json.hpp"
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
class json_null
|
||||
{
|
||||
public:
|
||||
static json_null instance;
|
||||
bool operator==(const json_null &other) const { return true; }
|
||||
bool operator!=(const json_null &other) const { return (!((*this) == other)); }
|
||||
|
||||
private:
|
||||
int value = 0;
|
||||
};
|
||||
|
||||
class json_object;
|
||||
class json_array;
|
||||
|
||||
class json_variant
|
||||
: public JsonSerializable
|
||||
{
|
||||
public:
|
||||
enum class ContentType
|
||||
{
|
||||
Null,
|
||||
Bool,
|
||||
Number,
|
||||
String,
|
||||
Object,
|
||||
Array
|
||||
};
|
||||
using object_ptr = std::shared_ptr<json_object>;
|
||||
using array_ptr = std::shared_ptr<json_array>;
|
||||
|
||||
private:
|
||||
public:
|
||||
~json_variant();
|
||||
json_variant();
|
||||
json_variant(json_variant &&);
|
||||
json_variant(const json_variant &);
|
||||
|
||||
json_variant(json_null value);
|
||||
json_variant(bool value);
|
||||
json_variant(double value);
|
||||
json_variant(const std::string &value);
|
||||
json_variant(std::shared_ptr<json_object> &&value);
|
||||
json_variant(const std::shared_ptr<json_object> &value);
|
||||
json_variant(std::shared_ptr<json_array> &&value);
|
||||
json_variant(const std::shared_ptr<json_array> &value);
|
||||
json_variant(json_array &&array);
|
||||
json_variant(json_object &&object);
|
||||
json_variant(const char*sz);
|
||||
|
||||
json_variant(const void*) = delete; // do NOT allow implicit conversion of pointers to bool
|
||||
|
||||
json_variant &operator=(json_variant &&value);
|
||||
json_variant &operator=(const json_variant &value);
|
||||
json_variant &operator=(bool value);
|
||||
json_variant &operator=(double value);
|
||||
json_variant &operator=(const std::string &value);
|
||||
json_variant &operator=(std::string &&value);
|
||||
json_variant &operator=(json_object &&value);
|
||||
json_variant &operator=(json_array &&value);
|
||||
|
||||
json_variant &operator=(const char*sz) { return (*this) = std::string(sz); }
|
||||
|
||||
json_variant &operator=(void*) = delete; // do NOT allow implicit conversion of pointers to bool
|
||||
|
||||
void require_type(ContentType content_type) const
|
||||
{
|
||||
if (this->content_type != content_type)
|
||||
{
|
||||
throw std::logic_error("Content type is not valid.");
|
||||
}
|
||||
}
|
||||
bool is_null() const { return content_type == ContentType::Null; }
|
||||
bool is_bool() const { return content_type == ContentType::Bool; }
|
||||
bool is_number() const { return content_type == ContentType::Number; }
|
||||
bool is_string() const { return content_type == ContentType::String; }
|
||||
bool is_object() const { return content_type == ContentType::Object; }
|
||||
bool is_array() const { return content_type == ContentType::Array; }
|
||||
|
||||
const json_null &as_null() const
|
||||
{
|
||||
require_type(ContentType::Bool);
|
||||
return json_null::instance;
|
||||
}
|
||||
json_null &as_null()
|
||||
{
|
||||
require_type(ContentType::Null);
|
||||
return json_null::instance;
|
||||
}
|
||||
|
||||
bool as_bool() const
|
||||
{
|
||||
require_type(ContentType::Bool);
|
||||
return content.bool_value;
|
||||
}
|
||||
bool &as_bool()
|
||||
{
|
||||
require_type(ContentType::Bool);
|
||||
return content.bool_value;
|
||||
}
|
||||
|
||||
double as_number() const
|
||||
{
|
||||
require_type(ContentType::Number);
|
||||
return content.double_value;
|
||||
}
|
||||
double &as_number()
|
||||
{
|
||||
require_type(ContentType::Number);
|
||||
return content.double_value;
|
||||
}
|
||||
|
||||
const std::string &as_string() const;
|
||||
std::string &as_string();
|
||||
|
||||
const std::shared_ptr<json_object> &as_object() const;
|
||||
std::shared_ptr<json_object> &as_object();
|
||||
|
||||
const std::shared_ptr<json_array> &as_array() const;
|
||||
std::shared_ptr<json_array> &as_array();
|
||||
|
||||
template <typename U>
|
||||
U &as() { static_assert("Invalid type."); }
|
||||
|
||||
// convenience methods for object and array manipulation.
|
||||
static json_variant make_object();
|
||||
static json_variant make_array();
|
||||
|
||||
void resize(size_t size);
|
||||
size_t size() const;
|
||||
|
||||
bool contains(const std::string &index) const;
|
||||
|
||||
json_variant &at(size_t index);
|
||||
const json_variant &at(size_t index) const;
|
||||
|
||||
json_variant &operator[](size_t index);
|
||||
const json_variant &operator[](size_t index) const;
|
||||
|
||||
json_variant &operator[](const std::string &index);
|
||||
const json_variant &operator[](const std::string &index) const;
|
||||
|
||||
bool operator==(const json_variant &other) const;
|
||||
bool operator!=(const json_variant &other) const;
|
||||
|
||||
std::string to_string() const;
|
||||
private:
|
||||
void free();
|
||||
void write_double_value(json_writer &writer, double value) const;
|
||||
void write_float_value(json_writer &writer, double value) const;
|
||||
virtual void read_json(json_reader &reader);
|
||||
virtual void write_json(json_writer &writer) const;
|
||||
|
||||
static constexpr size_t stringSize = sizeof(std::string);
|
||||
static constexpr size_t objectSize = sizeof(std::shared_ptr<json_variant>);
|
||||
static constexpr size_t memSize = stringSize > objectSize ? stringSize : objectSize;
|
||||
union Content
|
||||
{
|
||||
bool bool_value;
|
||||
double double_value;
|
||||
float float_value;
|
||||
int32_t int32_value;
|
||||
uint8_t mem[memSize];
|
||||
};
|
||||
|
||||
ContentType content_type = ContentType::Null;
|
||||
Content content;
|
||||
|
||||
std::string &memString();
|
||||
object_ptr &memObject();
|
||||
array_ptr &memArray();
|
||||
|
||||
const std::string &memString() const;
|
||||
const object_ptr &memObject() const;
|
||||
const array_ptr &memArray() const;
|
||||
};
|
||||
class json_array : public JsonSerializable
|
||||
{
|
||||
private:
|
||||
json_array(const json_array&) { } // deleted.
|
||||
public:
|
||||
using ptr = std::shared_ptr<json_array>;
|
||||
|
||||
json_array() { ++allocation_count_; }
|
||||
json_array(json_array&&other);
|
||||
~json_array() { --allocation_count_; }
|
||||
|
||||
json_variant &at(size_t index);
|
||||
const json_variant &at(size_t index) const;
|
||||
|
||||
json_variant &operator[](size_t index);
|
||||
const json_variant &operator[](size_t &index) const;
|
||||
|
||||
void resize(size_t size) { values.resize(size); }
|
||||
size_t size() const { return values.size(); }
|
||||
void push_back(json_variant &&value) { values.push_back(std::move(value)); }
|
||||
template <typename U>
|
||||
void push_back(U &&value) { values.push_back(value); }
|
||||
void push_back(double value) { values.push_back(json_variant{value}); }
|
||||
void push_back(const std::string &value) { values.push_back(json_variant{value}); }
|
||||
void push_back(bool value) { values.push_back(json_variant{value}); }
|
||||
void push_back(const std::shared_ptr<json_array> &value) { values.push_back(json_variant(value)); }
|
||||
void push_back(const std::shared_ptr<json_object> &value) { values.push_back(json_variant(value)); }
|
||||
|
||||
bool operator==(const json_array &other) const;
|
||||
bool operator!=(const json_array &other) const { return (!((*this) == other)); }
|
||||
|
||||
// Strictly for testing purposes. Not thread-safe.
|
||||
static int64_t allocation_count()
|
||||
{
|
||||
return allocation_count_;
|
||||
}
|
||||
using iterator = std::vector<json_variant>::iterator;
|
||||
using const_iterator = std::vector<json_variant>::const_iterator;
|
||||
|
||||
iterator begin() { return values.begin(); }
|
||||
iterator end() { return values.end(); }
|
||||
const_iterator begin() const { return values.begin(); }
|
||||
const_iterator end() const { return values.end(); }
|
||||
|
||||
private:
|
||||
static int64_t allocation_count_; // strictly for testing purposes. not thread safe.
|
||||
|
||||
virtual void read_json(json_reader &reader);
|
||||
virtual void write_json(json_writer &writer) const;
|
||||
|
||||
void check_index(size_t size) const;
|
||||
|
||||
std::vector<json_variant> values;
|
||||
};
|
||||
class json_object : public JsonSerializable
|
||||
{
|
||||
private:
|
||||
json_object(const json_object&) { } // deleted.
|
||||
public:
|
||||
using ptr = std::shared_ptr<json_object>;
|
||||
|
||||
json_object() { ++ allocation_count_;}
|
||||
json_object(json_object&&other);
|
||||
~json_object() { --allocation_count_;}
|
||||
|
||||
size_t size() const { return values.size(); }
|
||||
json_variant &at(const std::string &index);
|
||||
const json_variant &at(const std::string &index) const;
|
||||
|
||||
json_variant &operator[](const std::string &index);
|
||||
const json_variant &operator[](const std::string &index) const;
|
||||
|
||||
bool operator==(const json_object &other) const;
|
||||
bool operator!=(const json_object &other) const { return (!((*this) == other)); }
|
||||
bool contains(const std::string &index) const;
|
||||
|
||||
|
||||
using values_t = std::vector< std::pair<std::string, json_variant> >;
|
||||
using iterator = values_t::iterator;
|
||||
using const_iterator = values_t::const_iterator;
|
||||
|
||||
iterator begin() { return values.begin(); }
|
||||
iterator end() { return values.end(); }
|
||||
const_iterator begin() const { return values.begin(); }
|
||||
const_iterator end() const { return values.end(); }
|
||||
|
||||
iterator find(const std::string& key);
|
||||
const_iterator find(const std::string& key) const;
|
||||
|
||||
|
||||
// strictly for testing purposes. Not thread-safe.
|
||||
static int64_t allocation_count()
|
||||
{
|
||||
return allocation_count_;
|
||||
}
|
||||
private:
|
||||
virtual void read_json(json_reader &reader);
|
||||
virtual void write_json(json_writer &writer) const;
|
||||
|
||||
static int64_t allocation_count_;
|
||||
values_t values;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////
|
||||
|
||||
inline std::string &json_variant::memString() { return *(std::string *)content.mem; }
|
||||
inline const std::string &json_variant::memString() const { return *(const std::string *)content.mem; }
|
||||
|
||||
template <>
|
||||
inline json_null &json_variant::as<json_null>() { return as_null(); }
|
||||
|
||||
template <>
|
||||
inline bool &json_variant::as<bool>() { return as_bool(); }
|
||||
|
||||
template <>
|
||||
inline double &json_variant::as<double>() { return as_number(); }
|
||||
|
||||
template <>
|
||||
inline std::string &json_variant::as<std::string>() { return as_string(); }
|
||||
|
||||
template <>
|
||||
inline std::shared_ptr<json_object> &json_variant::as<std::shared_ptr<json_object>>() { return as_object(); }
|
||||
|
||||
template <>
|
||||
inline std::shared_ptr<json_array> &json_variant::as<std::shared_ptr<json_array>>() { return as_array(); }
|
||||
|
||||
template <>
|
||||
inline json_variant &json_variant::as<json_variant>() { return *this; }
|
||||
|
||||
inline json_variant::object_ptr &json_variant::memObject()
|
||||
{
|
||||
return *(object_ptr *)content.mem;
|
||||
}
|
||||
inline json_variant::array_ptr &json_variant::memArray()
|
||||
{
|
||||
return *(array_ptr *)content.mem;
|
||||
}
|
||||
|
||||
inline const json_variant::object_ptr &json_variant::memObject() const
|
||||
{
|
||||
return *(const object_ptr *)content.mem;
|
||||
}
|
||||
inline const json_variant::array_ptr &json_variant::memArray() const
|
||||
{
|
||||
return *(const array_ptr *)content.mem;
|
||||
}
|
||||
|
||||
inline json_variant::json_variant(json_array &&array)
|
||||
{
|
||||
this->content_type = ContentType::Null;
|
||||
new (content.mem) std::shared_ptr<json_array>{new json_array(std::move(array))};
|
||||
this->content_type = ContentType::Array;
|
||||
}
|
||||
inline json_variant::json_variant(json_object &&object)
|
||||
{
|
||||
this->content_type = ContentType::Null;
|
||||
new (content.mem) std::shared_ptr<json_object>{new json_object(std::move(object))};
|
||||
this->content_type = ContentType::Object;
|
||||
}
|
||||
|
||||
inline json_variant::json_variant(const std::string &value)
|
||||
{
|
||||
this->content_type = ContentType::Null;
|
||||
new (content.mem) std::string(value); // placement new.
|
||||
content_type = ContentType::String;
|
||||
}
|
||||
|
||||
inline json_variant::json_variant(std::shared_ptr<json_object> &&value)
|
||||
{
|
||||
this->content_type = ContentType::Null;
|
||||
new (content.mem) std::shared_ptr<json_object>(std::move(value)); // placement new.
|
||||
content_type = ContentType::Object;
|
||||
}
|
||||
inline json_variant::json_variant(const std::shared_ptr<json_object> &value)
|
||||
{
|
||||
// don't deep copy!
|
||||
std::shared_ptr<json_object> t = const_cast<std::shared_ptr<json_object> &>(value);
|
||||
this->content_type = ContentType::Null;
|
||||
new (content.mem) std::shared_ptr<json_object>(t); // placement new.
|
||||
content_type = ContentType::Object;
|
||||
}
|
||||
|
||||
inline json_variant::json_variant(const std::shared_ptr<json_array> &value)
|
||||
{
|
||||
// Make sure we don't deep copy!
|
||||
std::shared_ptr<json_array> t = const_cast<std::shared_ptr<json_array> &>(value);
|
||||
this->content_type = ContentType::Null;
|
||||
new (content.mem) std::shared_ptr<json_array>(t); // placement new.
|
||||
content_type = ContentType::Array;
|
||||
}
|
||||
inline json_variant::json_variant(array_ptr &&value)
|
||||
{
|
||||
this->content_type = ContentType::Null;
|
||||
new (content.mem) std::shared_ptr<json_array>(std::move(value)); // placement new.
|
||||
content_type = ContentType::Array;
|
||||
}
|
||||
inline json_variant::json_variant()
|
||||
{
|
||||
content_type = ContentType::Null;
|
||||
}
|
||||
inline json_variant::json_variant(json_null value)
|
||||
{
|
||||
content_type = ContentType::Null;
|
||||
}
|
||||
inline json_variant::json_variant(bool value)
|
||||
{
|
||||
content_type = ContentType::Bool;
|
||||
content.bool_value = value;
|
||||
}
|
||||
|
||||
inline json_variant::json_variant(double value)
|
||||
{
|
||||
content_type = ContentType::Number;
|
||||
content.double_value = value;
|
||||
}
|
||||
inline std::string &json_variant::as_string()
|
||||
{
|
||||
require_type(ContentType::String);
|
||||
return memString();
|
||||
}
|
||||
|
||||
inline const std::string &json_variant::as_string() const
|
||||
{
|
||||
require_type(ContentType::String);
|
||||
return memString();
|
||||
}
|
||||
|
||||
inline const json_variant::object_ptr &json_variant::as_object() const
|
||||
{
|
||||
require_type(ContentType::Object);
|
||||
return memObject();
|
||||
}
|
||||
|
||||
inline json_variant::object_ptr &json_variant::as_object()
|
||||
{
|
||||
require_type(ContentType::Object);
|
||||
return memObject();
|
||||
}
|
||||
|
||||
inline const json_variant::array_ptr &json_variant::as_array() const
|
||||
{
|
||||
require_type(ContentType::Array);
|
||||
return memArray();
|
||||
}
|
||||
|
||||
inline json_variant::array_ptr &json_variant::as_array()
|
||||
{
|
||||
require_type(ContentType::Array);
|
||||
return memArray();
|
||||
}
|
||||
|
||||
inline /*static*/ json_variant json_variant::make_object()
|
||||
{
|
||||
return json_variant{std::make_shared<json_object>()};
|
||||
};
|
||||
inline /*static */ json_variant json_variant::make_array()
|
||||
{
|
||||
return json_variant{std::make_shared<json_array>()};
|
||||
};
|
||||
|
||||
inline void json_variant::resize(size_t size)
|
||||
{
|
||||
as_array()->resize(size);
|
||||
}
|
||||
|
||||
inline json_variant &json_variant::at(size_t index)
|
||||
{
|
||||
return as_array()->at(index);
|
||||
}
|
||||
inline const json_variant &json_variant::at(size_t index) const
|
||||
{
|
||||
return as_array()->at(index);
|
||||
}
|
||||
|
||||
inline json_variant &json_variant::operator[](size_t index)
|
||||
{
|
||||
return as_array()->at(index);
|
||||
}
|
||||
inline const json_variant &json_variant::operator[](size_t index) const
|
||||
{
|
||||
return as_array()->at(index);
|
||||
}
|
||||
|
||||
inline const json_variant &json_variant::operator[](const std::string &index) const
|
||||
{
|
||||
return (*as_object())[index];
|
||||
}
|
||||
inline json_variant &json_variant::operator[](const std::string &index)
|
||||
{
|
||||
return (*as_object())[index];
|
||||
}
|
||||
|
||||
inline const json_variant &json_array::operator[](size_t &index) const
|
||||
{
|
||||
check_index(index);
|
||||
return values[index];
|
||||
}
|
||||
|
||||
inline json_variant &json_array::operator[](size_t index)
|
||||
{
|
||||
return at(index);
|
||||
}
|
||||
|
||||
inline void json_array::check_index(size_t size) const
|
||||
{
|
||||
if (size >= values.size())
|
||||
{
|
||||
throw std::out_of_range("index out of range.");
|
||||
}
|
||||
}
|
||||
|
||||
inline bool json_variant::operator!=(const json_variant &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
|
||||
// Holds a string but is json_read and json_written as an unqoted json object.
|
||||
class raw_json_string: public JsonSerializable {
|
||||
public:
|
||||
raw_json_string() { }
|
||||
raw_json_string(const std::string &value) : value(value) {}
|
||||
const std::string& as_string() const { return value; }
|
||||
|
||||
void Set(const std::string&value) { this->value = value; }
|
||||
|
||||
private:
|
||||
|
||||
virtual void write_json(json_writer &writer) const {
|
||||
writer.write_raw(value.c_str());
|
||||
}
|
||||
virtual void read_json(json_reader &reader) {
|
||||
throw std::logic_error("Not implemented.");
|
||||
}
|
||||
|
||||
std::string value;
|
||||
};
|
||||
|
||||
} // namespace pipedal
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
|
||||
interface=p2p-${WLAN}-*
|
||||
#disable dns, dhcp only.a"
|
||||
port=0
|
||||
dhcp-range=172.23.0.20,172.23.0.127,15m
|
||||
domain=local
|
||||
address=/pipedal-p2p.local/172.23.0.2
|
||||
|
||||
except-interface=eth0
|
||||
except-interface=${WLAN}
|
||||
@@ -0,0 +1,2 @@
|
||||
[keyfile]
|
||||
unmanaged-devices=interface-name:${WLAN},interface-name:p2p-dev-${WLAN}
|
||||
@@ -0,0 +1,10 @@
|
||||
# PiPedal dnsmasq settings for p2p
|
||||
#disable dns, dhcp only."
|
||||
port=0
|
||||
dhcp-range=172.23.0.3,172.23.0.127,15m
|
||||
domain=local
|
||||
address=/pipedal-p2p.local/172.23.0.2
|
||||
|
||||
# defensive. Not actually required.
|
||||
except-interface=eth0
|
||||
except-interface=wlan0
|
||||
@@ -0,0 +1,20 @@
|
||||
[Unit]
|
||||
Description=PiPedal P2P Session Manager
|
||||
After=network.target
|
||||
After=nss-lookup.target
|
||||
After=wpa_supplicant.service
|
||||
After=NetworkManager.service
|
||||
After=dhcpcd.service
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=8
|
||||
|
||||
[Service]
|
||||
ExecStart=${COMMAND} --systemd --loglevel info
|
||||
Type=notify
|
||||
WorkingDirectory=/var/pipedal
|
||||
Restart=on-failure
|
||||
TimeoutStopSec=5
|
||||
RestartSec=5s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -8,7 +8,7 @@ StartLimitIntervalSec=300
|
||||
StartLimitBurst=8
|
||||
|
||||
[Service]
|
||||
ExecStart=${COMMAND} --systemd --log-level debug
|
||||
ExecStart=${COMMAND} --systemd --log-level info
|
||||
Type=notify
|
||||
WorkingDirectory=/var/pipedal
|
||||
TimeoutStopSec=5
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
ctrl_interface=DIR=/run/wpa_supplicant GROUP=netdev
|
||||
update_config=1
|
||||
driver_param=p2p_device=6
|
||||
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Robin E. R. Davies
|
||||
* All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "util.hpp"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <thread>
|
||||
|
||||
#include <unistd.h> // for gettid()
|
||||
#include <codecvt>
|
||||
#include <sstream>
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
|
||||
void pipedal::SetThreadName(const std::string &name)
|
||||
{
|
||||
std::string threadName = "ppdl_" + name;
|
||||
if (threadName.length () > 15)
|
||||
{
|
||||
threadName = threadName.substr(0,15);
|
||||
}
|
||||
pthread_t pid = pthread_self();
|
||||
pthread_setname_np(pid,threadName.c_str());
|
||||
}
|
||||
|
||||
|
||||
static const uint8_t utf8extraBytes[256] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
|
||||
};
|
||||
|
||||
constexpr char32_t ILLEGAL_CHAR32 = U'⊗';
|
||||
static const uint8_t utf8Offset[] = { 0,0b11000000, 0b11100000,0b11110000,0b11111000,0b11111100};
|
||||
|
||||
std::u32string pipedal::ToUtf32(const std::string &s)
|
||||
{
|
||||
std::basic_stringstream<char32_t> result;
|
||||
|
||||
auto p = s.begin();
|
||||
auto end = s.end();
|
||||
|
||||
while (p != end)
|
||||
{
|
||||
uint8_t c = (uint8_t)(*p++);
|
||||
if (c < 0x80)
|
||||
{
|
||||
result << (char32_t)c;
|
||||
} else {
|
||||
auto extraBytes = utf8extraBytes[c];
|
||||
if (extraBytes == 0)
|
||||
{
|
||||
result << ILLEGAL_CHAR32;
|
||||
} else if (p+extraBytes > end)
|
||||
{
|
||||
result << ILLEGAL_CHAR32;
|
||||
break;
|
||||
} else {
|
||||
char32_t cResult = c -= utf8Offset[extraBytes];
|
||||
while (extraBytes != 0)
|
||||
{
|
||||
c = *p++;
|
||||
--extraBytes;
|
||||
if (c < 0x80 || c >= 0xC0)
|
||||
{
|
||||
result << ILLEGAL_CHAR32;
|
||||
p += extraBytes;
|
||||
break;
|
||||
}
|
||||
cResult = (cResult << 6) + (c & 0x3F);
|
||||
}
|
||||
result << cResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.str();
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Robin E. R. Davies
|
||||
* All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
namespace pipedal {
|
||||
|
||||
inline bool endsWith(const std::string& str, const std::string& suffix)
|
||||
{
|
||||
return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix);
|
||||
}
|
||||
void SetThreadName(const std::string &name);
|
||||
|
||||
std::u32string ToUtf32(const std::string &s);
|
||||
|
||||
inline bool isPathSeperator(char c) { return c == '/' || c == std::filesystem::path::preferred_separator;}
|
||||
}
|
||||
Reference in New Issue
Block a user