PiPedal 1.2.33 initial commit

This commit is contained in:
Robin Davies
2024-08-08 12:53:02 -04:00
parent 0e378997a5
commit a3847c8184
255 changed files with 22008 additions and 1791 deletions
+1
View File
@@ -31,6 +31,7 @@
#include "JackServerSettings.hpp"
#include <thread>
#include "RtInversionGuard.hpp"
#include "PiPedalException.hpp"
#include "CpuUse.hpp"
+13 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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)
{
-175
View File
@@ -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();
}
-369
View File
@@ -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());
}
}
};
}
-164
View File
@@ -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;
}
-37
View File
@@ -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);
};
}
-261
View File
@@ -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 << "&lt;"; break;
case '>': os << "&gt;"; break;
case '&': os << "&amp;"; break;
default: os << c; break;
}
}
return os.str();
}
-55
View File
@@ -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.
+2 -2
View File
@@ -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
View File
@@ -244,7 +244,7 @@ namespace pipedal
{
if (index == -1)
{
this->bypass = value != 0;
SetBypass(value != 0);
} else {
controlValues[index] = value;
}
+1 -1
View File
@@ -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/"))
-31
View File
@@ -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"
+1
View File
@@ -19,6 +19,7 @@
#pragma once
#include <exception>
#include <string>
namespace pipedal
{
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -727,7 +727,7 @@ namespace pipedal
AutoLilvNode dc__format;
};
LilvUris lilvUris;
LilvUris* lilvUris = nullptr;
private:
bool vst3Enabled = true;
-249
View File
@@ -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;
}
-86
View File
@@ -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; }
};
}
-77
View File
@@ -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);
}
}
-49
View File
@@ -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
View File
@@ -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");
}
}
+4
View File
@@ -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
View File
@@ -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;
}
-31
View File
@@ -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();
}
+12
View File
@@ -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.");
+1
View File
@@ -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;
+139
View File
@@ -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);
}
-637
View File
@@ -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
View File
@@ -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&regDomain = 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&regDb = 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();
}
-144
View File
@@ -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;
}
-49
View File
@@ -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);
};
}
-184
View File
@@ -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;
}
-49
View File
@@ -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);
};
}
-86
View File
@@ -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);
}
}
}
-45
View File
@@ -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);
}
-63
View File
@@ -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
View File
@@ -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 &copyright, 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 &copyright)
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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
-560
View File
@@ -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.
-550
View File
@@ -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}
+2
View File
@@ -0,0 +1,2 @@
[keyfile]
unmanaged-devices=interface-name:${WLAN},interface-name:p2p-dev-${WLAN}
+10
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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();
}
-38
View File
@@ -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;}
}