Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/shell_integration.h" | 5 #include "chrome/browser/shell_integration.h" |
| 6 | 6 |
| 7 #include <fcntl.h> | 7 #include <fcntl.h> |
| 8 #include <stdlib.h> | 8 #include <stdlib.h> |
| 9 #include <sys/stat.h> | 9 #include <sys/stat.h> |
| 10 #include <sys/types.h> | 10 #include <sys/types.h> |
| 11 #include <unistd.h> | 11 #include <unistd.h> |
| 12 | 12 |
| 13 #include <vector> | 13 #include <vector> |
| 14 | 14 |
| 15 #include "base/file_path.h" | |
| 16 #include "base/file_util.h" | |
| 17 #include "base/message_loop.h" | |
| 18 #include "base/path_service.h" | |
| 15 #include "base/process_util.h" | 19 #include "base/process_util.h" |
| 20 #include "base/string_tokenizer.h" | |
| 21 #include "base/string_util.h" | |
| 22 #include "base/task.h" | |
| 23 #include "base/thread.h" | |
| 24 #include "chrome/browser/browser_process.h" | |
| 25 #include "chrome/common/chrome_paths.h" | |
| 26 #include "googleurl/src/gurl.h" | |
| 16 | 27 |
| 17 static const char* GetDesktopName() { | 28 namespace { |
| 29 | |
| 30 const char* GetDesktopName() { | |
| 18 #if defined(GOOGLE_CHROME_BUILD) | 31 #if defined(GOOGLE_CHROME_BUILD) |
| 19 return "google-chrome.desktop"; | 32 return "google-chrome.desktop"; |
| 20 #else // CHROMIUM_BUILD | 33 #else // CHROMIUM_BUILD |
| 21 static const char* name = NULL; | 34 static const char* name = NULL; |
| 22 if (!name) { | 35 if (!name) { |
| 23 // Allow $CHROME_DESKTOP to override the built-in value, so that development | 36 // Allow $CHROME_DESKTOP to override the built-in value, so that development |
| 24 // versions can set themselves as the default without interfering with | 37 // versions can set themselves as the default without interfering with |
| 25 // non-official, packaged versions using the built-in value. | 38 // non-official, packaged versions using the built-in value. |
| 26 name = getenv("CHROME_DESKTOP"); | 39 name = getenv("CHROME_DESKTOP"); |
| 27 if (!name) | 40 if (!name) |
| 28 name = "chromium-browser.desktop"; | 41 name = "chromium-browser.desktop"; |
| 29 } | 42 } |
| 30 return name; | 43 return name; |
| 31 #endif | 44 #endif |
| 32 } | 45 } |
| 33 | 46 |
| 47 bool GetDesktopShortcutTemplate(std::string* output) { | |
| 48 std::vector<std::string> search_paths; | |
| 49 | |
| 50 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_
| |
| 51 if (xdg_data_home) | |
| 52 search_paths.push_back(xdg_data_home); | |
| 53 | |
| 54 const char* xdg_data_dirs = getenv("XDG_DATA_DIRS"); | |
| 55 if (xdg_data_dirs) { | |
| 56 StringTokenizer tokenizer(xdg_data_dirs, ":"); | |
| 57 while (tokenizer.GetNext()) { | |
| 58 search_paths.push_back(tokenizer.token()); | |
| 59 } | |
| 60 } | |
| 61 | |
| 62 std::string template_filename(GetDesktopName()); | |
| 63 for (std::vector<std::string>::const_iterator i = search_paths.begin(); | |
| 64 i != search_paths.end(); ++i) { | |
| 65 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
| |
| 66 if (file_util::PathExists(path)) | |
| 67 return file_util::ReadFileToString(path, output); | |
| 68 } | |
| 69 | |
| 70 return false; | |
| 71 } | |
| 72 | |
| 73 class CreateDesktopShortcutTask : public Task { | |
| 74 public: | |
| 75 CreateDesktopShortcutTask(const GURL& url, const string16& title) | |
| 76 : url_(url), | |
| 77 title_(title) { | |
| 78 } | |
| 79 | |
| 80 virtual void Run() { | |
| 81 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. | |
| 82 FilePath desktop_path; | |
| 83 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path)) | |
| 84 return; | |
| 85 desktop_path = | |
| 86 desktop_path.Append(ShellIntegration::GetDesktopShortcutFilename(url_)); | |
| 87 | |
| 88 if (file_util::PathExists(desktop_path)) | |
| 89 return; | |
| 90 | |
| 91 std::string template_contents; | |
| 92 if (!GetDesktopShortcutTemplate(&template_contents)) | |
| 93 return; | |
| 94 | |
| 95 std::string contents = ShellIntegration::GetDesktopFileContents( | |
| 96 template_contents, url_, title_); | |
| 97 int bytes_written = file_util::WriteFile(desktop_path, contents.data(), | |
| 98 contents.length()); | |
| 99 if (bytes_written != static_cast<int>(contents.length())) { | |
| 100 file_util::Delete(desktop_path, false); | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 private: | |
| 105 const GURL url_; // URL of the web application. | |
| 106 const string16 title_; // Title displayed to the user. | |
| 107 | |
| 108 DISALLOW_COPY_AND_ASSIGN(CreateDesktopShortcutTask); | |
| 109 }; | |
| 110 | |
| 111 } // namespace | |
| 112 | |
| 34 // We delegate the difficult of setting the default browser in Linux desktop | 113 // We delegate the difficult of setting the default browser in Linux desktop |
| 35 // environments to a new xdg utility, xdg-settings. We'll have to include a copy | 114 // environments to a new xdg utility, xdg-settings. We'll have to include a copy |
| 36 // of it for this to work, obviously, but that's actually the suggested approach | 115 // of it for this to work, obviously, but that's actually the suggested approach |
| 37 // for xdg utilities anyway. | 116 // for xdg utilities anyway. |
| 38 | 117 |
| 39 bool ShellIntegration::SetAsDefaultBrowser() { | 118 bool ShellIntegration::SetAsDefaultBrowser() { |
| 40 std::vector<std::string> argv; | 119 std::vector<std::string> argv; |
| 41 argv.push_back("xdg-settings"); | 120 argv.push_back("xdg-settings"); |
| 42 argv.push_back("set"); | 121 argv.push_back("set"); |
| 43 argv.push_back("default-web-browser"); | 122 argv.push_back("default-web-browser"); |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 91 std::vector<std::string> argv; | 170 std::vector<std::string> argv; |
| 92 argv.push_back("xdg-settings"); | 171 argv.push_back("xdg-settings"); |
| 93 argv.push_back("get"); | 172 argv.push_back("get"); |
| 94 argv.push_back("default-web-browser"); | 173 argv.push_back("default-web-browser"); |
| 95 | 174 |
| 96 std::string browser; | 175 std::string browser; |
| 97 // We don't care about the return value here. | 176 // We don't care about the return value here. |
| 98 base::GetAppOutput(CommandLine(argv), &browser); | 177 base::GetAppOutput(CommandLine(argv), &browser); |
| 99 return browser.find("irefox") != std::string::npos; | 178 return browser.find("irefox") != std::string::npos; |
| 100 } | 179 } |
| 180 | |
| 181 FilePath ShellIntegration::GetDesktopShortcutFilename(const GURL& url) { | |
| 182 std::wstring filename = UTF8ToWide(url.spec()) + L".desktop"; | |
| 183 file_util::ReplaceIllegalCharacters(&filename, '_'); | |
| 184 | |
| 185 // Return BaseName to be absolutely sure we're not vulnerable to a directory | |
| 186 // traversal attack. | |
| 187 return FilePath::FromWStringHack(filename).BaseName(); | |
| 188 } | |
| 189 | |
| 190 std::string ShellIntegration::GetDesktopFileContents( | |
| 191 const std::string& template_contents, const GURL& url, | |
| 192 const string16& title) { | |
| 193 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ | |
| 194 std::string output_buffer; | |
| 195 StringTokenizer tokenizer(template_contents, "\n"); | |
| 196 while (tokenizer.GetNext()) { | |
| 197 // TODO(phajdan.jr): Add the icon. | |
| 198 | |
| 199 if (tokenizer.token().substr(0, 5) == "Exec=") { | |
| 200 std::string exec_path = tokenizer.token().substr(5); | |
| 201 StringTokenizer exec_tokenizer(exec_path, " "); | |
| 202 std::string final_path; | |
| 203 while (exec_tokenizer.GetNext()) { | |
| 204 if (exec_tokenizer.token() != "%U") | |
| 205 final_path += exec_tokenizer.token() + " "; | |
| 206 } | |
| 207 std::string app_switch(StringPrintf("\"--app=%s\"", | |
|
Mike Mammarella
2009/08/14 20:56:22
This should use switches::kApp.
| |
| 208 url.spec().c_str())); | |
|
Evan Martin
2009/08/18 14:43:08
Does this make us vulnerable to some sort of comma
| |
| 209 ReplaceSubstringsAfterOffset(&app_switch, 0, "%", "%%"); | |
| 210 output_buffer += std::string("Exec=") + final_path + app_switch + "\n"; | |
| 211 } else if (tokenizer.token().substr(0, 5) == "Name=") { | |
| 212 std::string final_title = UTF16ToUTF8(title); | |
| 213 // Make sure no endline characters can slip in and possibly introduce | |
| 214 // additional lines (like Exec, which makes it a security risk). Also | |
| 215 // use the URL as a default when the title is empty. | |
| 216 if (final_title.empty() || | |
| 217 final_title.find("\n") != std::string::npos || | |
| 218 final_title.find("\r") != std::string::npos) | |
| 219 final_title = url.spec(); | |
| 220 output_buffer += StringPrintf("Name=%s\n", final_title.c_str()); | |
| 221 } else if (tokenizer.token().substr(0, 8) == "Comment=") { | |
| 222 // Skip the line. | |
| 223 } else { | |
| 224 output_buffer += tokenizer.token() + "\n"; | |
| 225 } | |
| 226 } | |
| 227 return output_buffer; | |
| 228 } | |
| 229 | |
| 230 void ShellIntegration::CreateDesktopShortcut(const GURL& url, | |
| 231 const string16& title) { | |
| 232 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, | |
| 233 new CreateDesktopShortcutTask(url, title)); | |
| 234 } | |
| OLD | NEW |