diff --git a/AutoStart/AutoStart-Linux.cpp b/AutoStart/AutoStart-Linux.cpp index 185d419e1..1a4829e99 100644 --- a/AutoStart/AutoStart-Linux.cpp +++ b/AutoStart/AutoStart-Linux.cpp @@ -149,6 +149,64 @@ std::string AutoStart::GetExePath() | Private Methods | \*---------------------------------------------------------*/ +/*---------------------------------------------------------*\ +| Escape a single argument for use in a .desktop Exec= | +| field, per the Desktop Entry Specification. | +| | +| Reserved characters must be escaped by wrapping the whole | +| argument in double quotes and backslash-escaping the | +| quote/backquote/backslash/dollar characters, and any | +| literal '%' must be doubled to '%%'. | +\*---------------------------------------------------------*/ +static std::string DesktopExecEscape(const std::string& arg) +{ + static const std::string reserved = " \t\n\"'`\\><~|&;$*?#()%"; + + bool needs_quotes = false; + for(char c : arg) + { + if(reserved.find(c) != std::string::npos) + { + needs_quotes = true; + break; + } + } + + std::string escaped; + + if(needs_quotes) + { + escaped += '"'; + } + + for(char c : arg) + { + switch(c) + { + case '"': + case '`': + case '\\': + case '$': + escaped += '\\'; + escaped += c; + break; + case '%': + escaped += "%%"; + break; + default: + escaped += c; + break; + } + } + + if(needs_quotes) + { + escaped += '"'; + } + + return(escaped); +} + std::string AutoStart::GenerateDesktopFile(AutoStartInfo autostart_info) { /*-----------------------------------------------------*\ @@ -169,11 +227,17 @@ std::string AutoStart::GenerateDesktopFile(AutoStartInfo autostart_info) /*-----------------------------------------------------*\ | Add the executable path and arguments | \*-----------------------------------------------------*/ - fileContents << "Exec=" << autostart_info.path; + fileContents << "Exec=" << DesktopExecEscape(autostart_info.path); if (autostart_info.args != "") { - fileContents << " " << autostart_info.args; + std::istringstream arg_parser(autostart_info.args); + std::string arg; + + while(arg_parser >> arg) + { + fileContents << " " << DesktopExecEscape(arg); + } } fileContents << std::endl; diff --git a/AutoStart/AutoStart-MacOS.cpp b/AutoStart/AutoStart-MacOS.cpp index 63b05fe66..a3f30a3b0 100644 --- a/AutoStart/AutoStart-MacOS.cpp +++ b/AutoStart/AutoStart-MacOS.cpp @@ -137,6 +137,43 @@ std::string AutoStart::GetExePath() | Private Methods | \*---------------------------------------------------------*/ +/*---------------------------------------------------------*\ +| Escape a string for embedding inside an XML element | +| (used by the .plist generator) to avoid producing | +| malformed property list files. | +\*---------------------------------------------------------*/ +static std::string XmlEscape(const std::string& input) +{ + std::string escaped; + + for(char c : input) + { + switch(c) + { + case '&': + escaped += "&"; + break; + case '<': + escaped += "<"; + break; + case '>': + escaped += ">"; + break; + case '"': + escaped += """; + break; + case '\'': + escaped += "'"; + break; + default: + escaped += c; + break; + } + } + + return(escaped); +} + std::string AutoStart::GenerateLaunchAgentFile(AutoStartInfo autostart_info) { /*-----------------------------------------------------*\ @@ -153,7 +190,8 @@ std::string AutoStart::GenerateLaunchAgentFile(AutoStartInfo autostart_info) fileContents << " org.openrgb" << std::endl; fileContents << " ProgramArguments" << std::endl; fileContents << " " << std::endl; - fileContents << " " << autostart_info.path << "" << std::endl; + fileContents << " " << XmlEscape(autostart_info.path) << "" + << std::endl; if(autostart_info.args != "") { @@ -162,7 +200,8 @@ std::string AutoStart::GenerateLaunchAgentFile(AutoStartInfo autostart_info) while(arg_parser >> arg) { - fileContents << " " << arg << "" << std::endl; + fileContents << " " << XmlEscape(arg) << "" + << std::endl; } }