Chromium Code Reviews| Index: chrome/browser/shell_integration_linux.cc |
| diff --git a/chrome/browser/shell_integration_linux.cc b/chrome/browser/shell_integration_linux.cc |
| index fc5e053eb6f0e6f2705922003158bc012556749b..48abd49dd98b374993e0f78077e6261039e6d22c 100644 |
| --- a/chrome/browser/shell_integration_linux.cc |
| +++ b/chrome/browser/shell_integration_linux.cc |
| @@ -12,9 +12,22 @@ |
| #include <vector> |
| +#include "base/file_path.h" |
| +#include "base/file_util.h" |
| +#include "base/message_loop.h" |
| +#include "base/path_service.h" |
| #include "base/process_util.h" |
| +#include "base/string_tokenizer.h" |
| +#include "base/string_util.h" |
| +#include "base/task.h" |
| +#include "base/thread.h" |
| +#include "chrome/browser/browser_process.h" |
| +#include "chrome/common/chrome_paths.h" |
| +#include "googleurl/src/gurl.h" |
| -static const char* GetDesktopName() { |
| +namespace { |
| + |
| +const char* GetDesktopName() { |
| #if defined(GOOGLE_CHROME_BUILD) |
| return "google-chrome.desktop"; |
| #else // CHROMIUM_BUILD |
| @@ -31,6 +44,72 @@ static const char* GetDesktopName() { |
| #endif |
| } |
| +bool GetDesktopShortcutTemplate(std::string* output) { |
| + std::vector<std::string> search_paths; |
| + |
| + const char* xdg_data_home = getenv("XDG_DATA_HOME"); |
|
Lei Zhang
2009/08/14 20:18:33
This will not work because almost nobody has $XDG_
|
| + if (xdg_data_home) |
| + search_paths.push_back(xdg_data_home); |
| + |
| + const char* xdg_data_dirs = getenv("XDG_DATA_DIRS"); |
| + if (xdg_data_dirs) { |
| + StringTokenizer tokenizer(xdg_data_dirs, ":"); |
| + while (tokenizer.GetNext()) { |
| + search_paths.push_back(tokenizer.token()); |
| + } |
| + } |
| + |
| + std::string template_filename(GetDesktopName()); |
| + for (std::vector<std::string>::const_iterator i = search_paths.begin(); |
| + i != search_paths.end(); ++i) { |
| + FilePath path = FilePath(*i).Append(template_filename); |
|
Lei Zhang
2009/08/14 20:18:33
Are you expecting to find /usr/share/google-chrome
Mike Mammarella
2009/08/14 20:56:22
I'm not sure we need to check applnk, but definite
|
| + if (file_util::PathExists(path)) |
| + return file_util::ReadFileToString(path, output); |
| + } |
| + |
| + return false; |
| +} |
| + |
| +class CreateDesktopShortcutTask : public Task { |
| + public: |
| + CreateDesktopShortcutTask(const GURL& url, const string16& title) |
| + : url_(url), |
| + title_(title) { |
| + } |
| + |
| + virtual void Run() { |
| + // TODO(phajdan.jr): Report errors from this function, possibly as infobars. |
| + FilePath desktop_path; |
| + if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path)) |
| + return; |
| + desktop_path = |
| + desktop_path.Append(ShellIntegration::GetDesktopShortcutFilename(url_)); |
| + |
| + if (file_util::PathExists(desktop_path)) |
| + return; |
| + |
| + std::string template_contents; |
| + if (!GetDesktopShortcutTemplate(&template_contents)) |
| + return; |
| + |
| + std::string contents = ShellIntegration::GetDesktopFileContents( |
| + template_contents, url_, title_); |
| + int bytes_written = file_util::WriteFile(desktop_path, contents.data(), |
| + contents.length()); |
| + if (bytes_written != static_cast<int>(contents.length())) { |
| + file_util::Delete(desktop_path, false); |
| + } |
| + } |
| + |
| + private: |
| + const GURL url_; // URL of the web application. |
| + const string16 title_; // Title displayed to the user. |
| + |
| + DISALLOW_COPY_AND_ASSIGN(CreateDesktopShortcutTask); |
| +}; |
| + |
| +} // namespace |
| + |
| // We delegate the difficult of setting the default browser in Linux desktop |
| // environments to a new xdg utility, xdg-settings. We'll have to include a copy |
| // of it for this to work, obviously, but that's actually the suggested approach |
| @@ -98,3 +177,58 @@ bool ShellIntegration::IsFirefoxDefaultBrowser() { |
| base::GetAppOutput(CommandLine(argv), &browser); |
| return browser.find("irefox") != std::string::npos; |
| } |
| + |
| +FilePath ShellIntegration::GetDesktopShortcutFilename(const GURL& url) { |
| + std::wstring filename = UTF8ToWide(url.spec()) + L".desktop"; |
| + file_util::ReplaceIllegalCharacters(&filename, '_'); |
| + |
| + // Return BaseName to be absolutely sure we're not vulnerable to a directory |
| + // traversal attack. |
| + return FilePath::FromWStringHack(filename).BaseName(); |
| +} |
| + |
| +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; |
| + StringTokenizer tokenizer(template_contents, "\n"); |
| + while (tokenizer.GetNext()) { |
| + // TODO(phajdan.jr): Add the icon. |
| + |
| + if (tokenizer.token().substr(0, 5) == "Exec=") { |
| + 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() + " "; |
| + } |
| + std::string app_switch(StringPrintf("\"--app=%s\"", |
|
Mike Mammarella
2009/08/14 20:56:22
This should use switches::kApp.
|
| + url.spec().c_str())); |
|
Evan Martin
2009/08/18 14:43:08
Does this make us vulnerable to some sort of comma
|
| + 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); |
| + // Make sure no endline characters can slip in and possibly introduce |
| + // additional lines (like Exec, which makes it a security risk). Also |
| + // use the URL as a default when the title is empty. |
| + if (final_title.empty() || |
| + final_title.find("\n") != std::string::npos || |
| + 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 { |
| + output_buffer += tokenizer.token() + "\n"; |
| + } |
| + } |
| + return output_buffer; |
| +} |
| + |
| +void ShellIntegration::CreateDesktopShortcut(const GURL& url, |
| + const string16& title) { |
| + g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, |
| + new CreateDesktopShortcutTask(url, title)); |
| +} |