diff options
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/shell_integration.cc | 41 | ||||
-rw-r--r-- | chrome/browser/shell_integration.h | 15 | ||||
-rw-r--r-- | chrome/browser/shell_integration_linux.cc | 70 | ||||
-rw-r--r-- | chrome/browser/shell_integration_unittest.cc | 22 | ||||
-rw-r--r-- | chrome/browser/web_applications/web_app.cc | 19 |
5 files changed, 109 insertions, 58 deletions
diff --git a/chrome/browser/shell_integration.cc b/chrome/browser/shell_integration.cc index cb5fde3..0b66d07 100644 --- a/chrome/browser/shell_integration.cc +++ b/chrome/browser/shell_integration.cc @@ -23,56 +23,41 @@ ShellIntegration::ShortcutInfo::ShortcutInfo() ShellIntegration::ShortcutInfo::~ShortcutInfo() {} -std::string ShellIntegration::GetCommandLineArgumentsCommon( +// static +CommandLine ShellIntegration::CommandLineArgsForLauncher( const GURL& url, const std::string& extension_app_id) { - const CommandLine cmd = *CommandLine::ForCurrentProcess(); - std::wstring arguments_w; + const CommandLine& cmd_line = *CommandLine::ForCurrentProcess(); + CommandLine new_cmd_line(CommandLine::NO_PROGRAM); // Use the same UserDataDir for new launches that we currently have set. - FilePath user_data_dir = cmd.GetSwitchValuePath(switches::kUserDataDir); - if (!user_data_dir.value().empty()) { + FilePath user_data_dir = cmd_line.GetSwitchValuePath(switches::kUserDataDir); + if (!user_data_dir.empty()) { // Make sure user_data_dir is an absolute path. if (file_util::AbsolutePath(&user_data_dir) && file_util::PathExists(user_data_dir)) { - // TODO: This is wrong in pathological quoting scenarios; we shouldn't be - // passing around command lines as strings at all. - arguments_w += std::wstring(L"--") + ASCIIToWide(switches::kUserDataDir) + - L"=\"" + user_data_dir.ToWStringHack() + L"\" "; + new_cmd_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir); } } #if defined(OS_CHROMEOS) - FilePath profile = cmd.GetSwitchValuePath(switches::kLoginProfile); - if (!profile.empty()) { - arguments_w += std::wstring(L"--") + ASCIIToWide(switches::kLoginProfile) + - L"=\"" + profile.ToWStringHack() + L"\" "; - } + FilePath profile = cmd_line.GetSwitchValuePath(switches::kLoginProfile); + if (!profile.empty()) + new_cmd_line.AppendSwitchPath(switches::kLoginProfile, profile); #endif // If |extension_app_id| is present, we use the kAppId switch rather than // the kApp switch (the launch url will be read from the extension app // during launch. if (!extension_app_id.empty()) { - arguments_w += std::wstring(L"--") + ASCIIToWide(switches::kAppId) + - L"=\"" + ASCIIToWide(extension_app_id) + L"\""; + new_cmd_line.AppendSwitchASCII(switches::kAppId, extension_app_id); } else { // Use '--app=url' instead of just 'url' to launch the browser with minimal // chrome. // Note: Do not change this flag! Old Gears shortcuts will break if you do! - std::string url_string = url.spec(); - ReplaceSubstringsAfterOffset(&url_string, 0, "\\", "%5C"); - ReplaceSubstringsAfterOffset(&url_string, 0, "\"", "%22"); - ReplaceSubstringsAfterOffset(&url_string, 0, ";", "%3B"); - ReplaceSubstringsAfterOffset(&url_string, 0, "$", "%24"); -#if defined(OS_WIN) // Windows shortcuts can't escape % so we use \x instead. - ReplaceSubstringsAfterOffset(&url_string, 0, "%", "\\x"); -#endif - std::wstring url_w = UTF8ToWide(url_string); - arguments_w += std::wstring(L"--") + ASCIIToWide(switches::kApp) + - L"=\"" + url_w + L"\""; + new_cmd_line.AppendSwitchASCII(switches::kApp, url.spec()); } - return WideToUTF8(arguments_w); + return new_cmd_line; } /////////////////////////////////////////////////////////////////////////////// diff --git a/chrome/browser/shell_integration.h b/chrome/browser/shell_integration.h index 450c11b..0df0ed7 100644 --- a/chrome/browser/shell_integration.h +++ b/chrome/browser/shell_integration.h @@ -14,6 +14,7 @@ #include "googleurl/src/gurl.h" #include "third_party/skia/include/core/SkBitmap.h" +class CommandLine; class FilePath; class PrefService; @@ -72,14 +73,12 @@ class ShellIntegration { bool create_in_quick_launch_bar; }; - // Re-implementation of chrome_plugin_utill::CPB_GetCommandLineArgumentsCommon - // which is deprecated. If |extension_app_id| is non-empty, an arguments - // string is created using the kAppId=<id> flag. Otherwise, the kApp=<url> is - // used. - // NOTE: This function is dangerous, do not use! You cannot treat - // command lines as plain strings as there are metacharacters. - // TODO(evanm): remove it. - static std::string GetCommandLineArgumentsCommon( + // Set up command line arguments for launching a URL or an app. + // The new command line reuses the current process's user data directory (and + // login profile, for ChromeOS). + // If |extension_app_id| is non-empty, the arguments use kAppId=<id>. + // Otherwise, kApp=<url> is used. + static CommandLine CommandLineArgsForLauncher( const GURL& url, const std::string& extension_app_id); diff --git a/chrome/browser/shell_integration_linux.cc b/chrome/browser/shell_integration_linux.cc index 5e8a3f3..c4fe208 100644 --- a/chrome/browser/shell_integration_linux.cc +++ b/chrome/browser/shell_integration_linux.cc @@ -175,6 +175,52 @@ void CreateShortcutInApplicationsMenu(const FilePath& shortcut_filename, LaunchXdgUtility(argv); } +// Quote a string such that it appears as one verbatim argument for the Exec +// key in a desktop file. +std::string QuoteArgForDesktopFileExec(const std::string& arg) { + // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html + + // Quoting is only necessary if the argument has a reserved character. + if (arg.find_first_of(" \t\n\"'\\><~|&;$*?#()`") == std::string::npos) + return arg; // No quoting necessary. + + std::string quoted = "\""; + for (size_t i = 0; i < arg.size(); ++i) { + // Note that the set of backslashed characters is smaller than the + // set of reserved characters. + switch (arg[i]) { + case '"': + case '`': + case '$': + case '\\': + quoted += '\\'; + break; + } + quoted += arg[i]; + } + quoted += '"'; + + return quoted; +} + +// Escape a string if needed for the right side of a Key=Value +// construct in a desktop file. (Note that for Exec= lines this +// should be used in conjunction with QuoteArgForDesktopFileExec, +// possibly escaping a backslash twice.) +std::string EscapeStringForDesktopFile(const std::string& arg) { + // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s03.html + if (arg.find('\\') == std::string::npos) + return arg; + + std::string escaped; + for (size_t i = 0; i < arg.size(); ++i) { + if (arg[i] == '\\') + escaped += '\\'; + escaped += arg[i]; + } + return escaped; +} + } // namespace // static @@ -328,13 +374,25 @@ std::string ShellIntegration::GetDesktopFileContents( std::string exec_path = tokenizer.token().substr(5); StringTokenizer exec_tokenizer(exec_path, " "); std::string final_path; - while (exec_tokenizer.GetNext()) { - if (exec_tokenizer.token() != "%U") - final_path += exec_tokenizer.token() + " "; + while (exec_tokenizer.GetNext() && exec_tokenizer.token() != "%U") { + if (!final_path.empty()) + final_path += " "; + final_path += exec_tokenizer.token(); + } + CommandLine cmd_line = + ShellIntegration::CommandLineArgsForLauncher(url, extension_id); + const CommandLine::SwitchMap& switch_map = cmd_line.GetSwitches(); + for (CommandLine::SwitchMap::const_iterator i = switch_map.begin(); + i != switch_map.end(); ++i) { + if (i->second.empty()) { + final_path += " --" + i->first; + } else { + final_path += " " + QuoteArgForDesktopFileExec("--" + i->first + + "=" + i->second); + } } - std::string switches = - ShellIntegration::GetCommandLineArgumentsCommon(url, extension_id); - output_buffer += std::string("Exec=") + final_path + switches + "\n"; + output_buffer += std::string("Exec=") + + EscapeStringForDesktopFile(final_path) + "\n"; } else if (tokenizer.token().substr(0, 5) == "Name=") { std::string final_title = UTF16ToUTF8(title); // Make sure no endline characters can slip in and possibly introduce diff --git a/chrome/browser/shell_integration_unittest.cc b/chrome/browser/shell_integration_unittest.cc index 9a86b58..1a6b9795 100644 --- a/chrome/browser/shell_integration_unittest.cc +++ b/chrome/browser/shell_integration_unittest.cc @@ -191,7 +191,7 @@ TEST(ShellIntegrationTest, GetDesktopFileContents) { "Version=1.0\n" "Encoding=UTF-8\n" "Name=GMail\n" - "Exec=/opt/google/chrome/google-chrome --app=\"http://gmail.com/\"\n" + "Exec=/opt/google/chrome/google-chrome --app=http://gmail.com/\n" "Terminal=false\n" "Icon=chrome-http__gmail.com\n" "Type=Application\n" @@ -209,7 +209,7 @@ TEST(ShellIntegrationTest, GetDesktopFileContents) { "#!/usr/bin/env xdg-open\n" "Name=GMail\n" - "Exec=/opt/google/chrome/google-chrome --app=\"http://gmail.com/\"\n" + "Exec=/opt/google/chrome/google-chrome --app=http://gmail.com/\n" }, // Make sure i18n-ed comments are removed. @@ -223,7 +223,7 @@ TEST(ShellIntegrationTest, GetDesktopFileContents) { "#!/usr/bin/env xdg-open\n" "Name=GMail\n" - "Exec=/opt/google/chrome/google-chrome --app=\"http://gmail.com/\"\n" + "Exec=/opt/google/chrome/google-chrome --app=http://gmail.com/\n" }, // Make sure that empty icons are replaced by the chrome icon. @@ -238,7 +238,7 @@ TEST(ShellIntegrationTest, GetDesktopFileContents) { "#!/usr/bin/env xdg-open\n" "Name=GMail\n" - "Exec=/opt/google/chrome/google-chrome --app=\"http://gmail.com/\"\n" + "Exec=/opt/google/chrome/google-chrome --app=http://gmail.com/\n" "Icon=/opt/google/chrome/product_logo_48.png\n" }, @@ -253,7 +253,7 @@ TEST(ShellIntegrationTest, GetDesktopFileContents) { "#!/usr/bin/env xdg-open\n" "Name=http://evil.com/evil%20--join-the-b0tnet\n" "Exec=/opt/google/chrome/google-chrome " - "--app=\"http://evil.com/evil%20--join-the-b0tnet\"\n" + "--app=http://evil.com/evil%20--join-the-b0tnet\n" }, { "http://evil.com/evil; rm -rf /; \"; rm -rf $HOME >ownz0red", "Innocent Title", @@ -265,8 +265,11 @@ TEST(ShellIntegrationTest, GetDesktopFileContents) { "#!/usr/bin/env xdg-open\n" "Name=Innocent Title\n" "Exec=/opt/google/chrome/google-chrome " - "--app=\"http://evil.com/evil%3B%20rm%20-rf%20/%3B%20%22%3B%20rm%20" - "-rf%20%24HOME%20%3Eownz0red\"\n" + "\"--app=http://evil.com/evil;%20rm%20-rf%20/;%20%22;%20rm%20" + // Note: $ is escaped as \$ within an arg to Exec, and then + // the \ is escaped as \\ as all strings in a Desktop file should + // be; finally, \\ becomes \\\\ when represented in a C++ string! + "-rf%20\\\\$HOME%20%3Eownz0red\"\n" }, { "http://evil.com/evil | cat `echo ownz0red` >/dev/null", "Innocent Title", @@ -278,11 +281,12 @@ TEST(ShellIntegrationTest, GetDesktopFileContents) { "#!/usr/bin/env xdg-open\n" "Name=Innocent Title\n" "Exec=/opt/google/chrome/google-chrome " - "--app=\"http://evil.com/evil%20%7C%20cat%20%60echo%20ownz0red" - "%60%20%3E/dev/null\"\n" + "--app=http://evil.com/evil%20%7C%20cat%20%60echo%20ownz0red" + "%60%20%3E/dev/null\n" }, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); i++) { + SCOPED_TRACE(i); EXPECT_EQ(test_cases[i].expected_output, ShellIntegration::GetDesktopFileContents( test_cases[i].template_contents, diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc index 2e77928..8cd0028 100644 --- a/chrome/browser/web_applications/web_app.cc +++ b/chrome/browser/web_applications/web_app.cc @@ -14,6 +14,7 @@ #include <vector> #include "base/callback.h" +#include "base/command_line.h" #include "base/file_util.h" #include "base/md5.h" #include "base/message_loop.h" @@ -93,8 +94,8 @@ FilePath GetSanitizedFileName(const string16& name) { // Returns relative directory of given web app url. FilePath GetWebAppDir(const ShellIntegration::ShortcutInfo& info) { if (!info.extension_id.empty()) { - std::string app_name = web_app::GenerateApplicationNameFromExtensionId( - info.extension_id); + std::string app_name = + web_app::GenerateApplicationNameFromExtensionId(info.extension_id); #if defined(OS_WIN) return FilePath(UTF8ToWide(app_name)); #elif defined(OS_POSIX) @@ -378,10 +379,14 @@ bool CreateShortcutTask::CreateShortcut() { // Working directory. FilePath chrome_folder = chrome_exe.DirName(); - std::string switches = - ShellIntegration::GetCommandLineArgumentsCommon(shortcut_info_.url, - shortcut_info_.extension_id); - std::wstring wide_switchs(UTF8ToWide(switches)); + CommandLine cmd_line = + ShellIntegration::CommandLineArgsForLauncher(shortcut_info_.url, + shortcut_info_.extension_id); + // TODO(evan): we rely on the fact that command_line_string() is + // properly quoted for a Windows command line. The method on + // CommandLine should probably be renamed to better reflect that + // fact. + std::wstring wide_switches(cmd_line.command_line_string()); // Sanitize description if (shortcut_info_.description.length() >= MAX_PATH) @@ -417,7 +422,7 @@ bool CreateShortcutTask::CreateShortcut() { success &= file_util::CreateShortcutLink(chrome_exe.value().c_str(), shortcut_file.value().c_str(), chrome_folder.value().c_str(), - wide_switchs.c_str(), + wide_switches.c_str(), shortcut_info_.description.c_str(), icon_file.value().c_str(), 0, |