| Index: chrome/browser/shell_integration_linux.cc
|
| diff --git a/chrome/browser/shell_integration_linux.cc b/chrome/browser/shell_integration_linux.cc
|
| index 78c84433fe24e9427fa7db6fb1673238b8b68f0a..dab372feec86490c34d61be526a3462ad7339e98 100644
|
| --- a/chrome/browser/shell_integration_linux.cc
|
| +++ b/chrome/browser/shell_integration_linux.cc
|
| @@ -14,6 +14,7 @@
|
| #include <vector>
|
|
|
| #include "base/command_line.h"
|
| +#include "base/eintr_wrapper.h"
|
| #include "base/file_path.h"
|
| #include "base/file_util.h"
|
| #include "base/message_loop.h"
|
| @@ -117,16 +118,65 @@ class CreateDesktopShortcutTask : public Task {
|
| if (!GetDesktopShortcutTemplate(&template_contents))
|
| return;
|
|
|
| + FilePath shortcut_filename =
|
| + ShellIntegration::GetDesktopShortcutFilename(shortcut_info_.url);
|
| +
|
| std::string contents = ShellIntegration::GetDesktopFileContents(
|
| template_contents, shortcut_info_.url, shortcut_info_.title);
|
|
|
| + if (shortcut_info_.create_on_desktop)
|
| + CreateOnDesktop(shortcut_filename, contents);
|
| +
|
| + if (shortcut_info_.create_in_applications_menu)
|
| + CreateInApplicationsMenu(shortcut_filename, contents);
|
| + }
|
| +
|
| + private:
|
| + void CreateOnDesktop(const FilePath& shortcut_filename,
|
| + const std::string& contents) {
|
| + // TODO(phajdan.jr): Report errors from this function, possibly as infobars.
|
| +
|
| + // Make sure that we will later call openat in a secure way.
|
| + DCHECK_EQ(shortcut_filename.BaseName().value(), shortcut_filename.value());
|
| +
|
| + FilePath desktop_path;
|
| + if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path))
|
| + return;
|
| +
|
| + int desktop_fd = open(desktop_path.value().c_str(), O_RDONLY | O_DIRECTORY);
|
| + if (desktop_fd < 0)
|
| + return;
|
| +
|
| + int fd = openat(desktop_fd, shortcut_filename.value().c_str(),
|
| + O_CREAT | O_EXCL | O_WRONLY,
|
| + S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
| + if (fd < 0) {
|
| + HANDLE_EINTR(close(desktop_fd));
|
| + return;
|
| + }
|
| +
|
| + ssize_t bytes_written = file_util::WriteFileDescriptor(fd, contents.data(),
|
| + contents.length());
|
| + HANDLE_EINTR(close(fd));
|
| +
|
| + if (bytes_written != static_cast<ssize_t>(contents.length())) {
|
| + // Delete the file. No shortuct is better than corrupted one. Use unlinkat
|
| + // to make sure we're deleting the file in the directory we think we are.
|
| + // Even if an attacker manager to put something other at
|
| + // |shortcut_filename| we'll just undo his action.
|
| + unlinkat(desktop_fd, shortcut_filename.value().c_str(), 0);
|
| + }
|
| +
|
| + HANDLE_EINTR(close(desktop_fd));
|
| + }
|
| +
|
| + void CreateInApplicationsMenu(const FilePath& shortcut_filename,
|
| + const std::string& contents) {
|
| + // TODO(phajdan.jr): Report errors from this function, possibly as infobars.
|
| ScopedTempDir temp_dir;
|
| if (!temp_dir.CreateUniqueTempDir())
|
| return;
|
|
|
| - FilePath shortcut_filename =
|
| - ShellIntegration::GetDesktopShortcutFilename(shortcut_info_.url);
|
| -
|
| FilePath temp_file_path = temp_dir.path().Append(shortcut_filename);
|
|
|
| int bytes_written = file_util::WriteFile(temp_file_path, contents.data(),
|
| @@ -135,32 +185,19 @@ class CreateDesktopShortcutTask : public Task {
|
| if (bytes_written != static_cast<int>(contents.length()))
|
| return;
|
|
|
| - if (shortcut_info_.create_on_desktop) {
|
| - FilePath desktop_path;
|
| - if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path))
|
| - return;
|
| - desktop_path = desktop_path.Append(shortcut_filename);
|
| -
|
| - if (!file_util::PathExists(desktop_path))
|
| - file_util::CopyFile(temp_file_path, desktop_path);
|
| - }
|
| -
|
| - if (shortcut_info_.create_in_applications_menu) {
|
| - std::vector<std::string> argv;
|
| - argv.push_back("xdg-desktop-menu");
|
| - argv.push_back("install");
|
| + std::vector<std::string> argv;
|
| + argv.push_back("xdg-desktop-menu");
|
| + argv.push_back("install");
|
|
|
| - // Always install in user mode, even if someone runs the browser as root
|
| - // (people do that).
|
| - argv.push_back("--mode");
|
| - argv.push_back("user");
|
| + // Always install in user mode, even if someone runs the browser as root
|
| + // (people do that).
|
| + argv.push_back("--mode");
|
| + argv.push_back("user");
|
|
|
| - argv.push_back(temp_file_path.value());
|
| - LaunchXdgUtility(argv);
|
| - }
|
| + argv.push_back(temp_file_path.value());
|
| + LaunchXdgUtility(argv);
|
| }
|
|
|
| - private:
|
| const ShellIntegration::ShortcutInfo shortcut_info_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(CreateDesktopShortcutTask);
|
| @@ -226,7 +263,9 @@ std::string ShellIntegration::GetDesktopFileContents(
|
| const std::string& template_contents, const GURL& url,
|
| const string16& title) {
|
| // See http://standards.freedesktop.org/desktop-entry-spec/latest/
|
| - std::string output_buffer;
|
| + // Although not required by the spec, Nautilus on Ubuntu Karmic creates its
|
| + // launchers with an xdg-open shebang. Follow that convention.
|
| + std::string output_buffer("#!/usr/bin/env xdg-open\n");
|
| StringTokenizer tokenizer(template_contents, "\n");
|
| while (tokenizer.GetNext()) {
|
| // TODO(phajdan.jr): Add the icon.
|
| @@ -243,7 +282,10 @@ std::string ShellIntegration::GetDesktopFileContents(
|
| std::string app_switch(StringPrintf("\"--%s=%s\"",
|
| WideToUTF8(app_switch_wide).c_str(),
|
| url.spec().c_str()));
|
| + // Sanitize the command line string.
|
| ReplaceSubstringsAfterOffset(&app_switch, 0, "%", "%%");
|
| + ReplaceSubstringsAfterOffset(&app_switch, 0, ";", "");
|
| + ReplaceSubstringsAfterOffset(&app_switch, 0, "$", "");
|
| output_buffer += std::string("Exec=") + final_path + app_switch + "\n";
|
| } else if (tokenizer.token().substr(0, 5) == "Name=") {
|
| std::string final_title = UTF16ToUTF8(title);
|
| @@ -255,8 +297,10 @@ std::string ShellIntegration::GetDesktopFileContents(
|
| final_title.find("\r") != std::string::npos)
|
| final_title = url.spec();
|
| output_buffer += StringPrintf("Name=%s\n", final_title.c_str());
|
| - } else if (tokenizer.token().substr(0, 8) == "Comment=") {
|
| - // Skip the line.
|
| + } else if (tokenizer.token().substr(0, 11) == "GenericName" ||
|
| + tokenizer.token().substr(0, 7) == "Comment" ||
|
| + tokenizer.token().substr(0, 1) == "#") {
|
| + // Skip comment lines.
|
| } else {
|
| output_buffer += tokenizer.token() + "\n";
|
| }
|
|
|