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;
}
}