| 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 <string> | 13 #include <string> |
| 14 #include <vector> | 14 #include <vector> |
| 15 | 15 |
| 16 #include "base/command_line.h" | 16 #include "base/command_line.h" |
| 17 #include "base/eintr_wrapper.h" | 17 #include "base/eintr_wrapper.h" |
| 18 #include "base/file_path.h" | 18 #include "base/file_path.h" |
| 19 #include "base/file_util.h" | 19 #include "base/file_util.h" |
| 20 #include "base/gfx/png_encoder.h" |
| 20 #include "base/message_loop.h" | 21 #include "base/message_loop.h" |
| 21 #include "base/path_service.h" | 22 #include "base/path_service.h" |
| 22 #include "base/process_util.h" | 23 #include "base/process_util.h" |
| 23 #include "base/scoped_temp_dir.h" | 24 #include "base/scoped_temp_dir.h" |
| 24 #include "base/string_tokenizer.h" | 25 #include "base/string_tokenizer.h" |
| 25 #include "base/string_util.h" | 26 #include "base/string_util.h" |
| 26 #include "base/task.h" | 27 #include "base/task.h" |
| 27 #include "base/thread.h" | 28 #include "base/thread.h" |
| 28 #include "chrome/browser/browser_process.h" | 29 #include "chrome/browser/browser_process.h" |
| 29 #include "chrome/common/chrome_constants.h" | 30 #include "chrome/common/chrome_constants.h" |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 114 | 115 |
| 115 virtual void Run() { | 116 virtual void Run() { |
| 116 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. | 117 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. |
| 117 std::string template_contents; | 118 std::string template_contents; |
| 118 if (!GetDesktopShortcutTemplate(&template_contents)) | 119 if (!GetDesktopShortcutTemplate(&template_contents)) |
| 119 return; | 120 return; |
| 120 | 121 |
| 121 FilePath shortcut_filename = | 122 FilePath shortcut_filename = |
| 122 ShellIntegration::GetDesktopShortcutFilename(shortcut_info_.url); | 123 ShellIntegration::GetDesktopShortcutFilename(shortcut_info_.url); |
| 123 | 124 |
| 125 std::string icon_name = CreateIcon(shortcut_filename); |
| 126 |
| 124 std::string contents = ShellIntegration::GetDesktopFileContents( | 127 std::string contents = ShellIntegration::GetDesktopFileContents( |
| 125 template_contents, shortcut_info_.url, shortcut_info_.title); | 128 template_contents, shortcut_info_.url, shortcut_info_.title, |
| 129 icon_name); |
| 126 | 130 |
| 127 if (shortcut_info_.create_on_desktop) | 131 if (shortcut_info_.create_on_desktop) |
| 128 CreateOnDesktop(shortcut_filename, contents); | 132 CreateOnDesktop(shortcut_filename, contents); |
| 129 | 133 |
| 130 if (shortcut_info_.create_in_applications_menu) | 134 if (shortcut_info_.create_in_applications_menu) |
| 131 CreateInApplicationsMenu(shortcut_filename, contents); | 135 CreateInApplicationsMenu(shortcut_filename, contents); |
| 132 } | 136 } |
| 133 | 137 |
| 134 private: | 138 private: |
| 139 std::string CreateIcon(const FilePath& shortcut_filename) { |
| 140 if (shortcut_info_.favicon.isNull()) |
| 141 return std::string(); |
| 142 |
| 143 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. |
| 144 ScopedTempDir temp_dir; |
| 145 if (!temp_dir.CreateUniqueTempDir()) |
| 146 return std::string(); |
| 147 |
| 148 FilePath temp_file_path = temp_dir.path().Append( |
| 149 shortcut_filename.ReplaceExtension("png")); |
| 150 |
| 151 std::vector<unsigned char> png_data; |
| 152 PNGEncoder::EncodeBGRASkBitmap(shortcut_info_.favicon, false, &png_data); |
| 153 int bytes_written = file_util::WriteFile(temp_file_path, |
| 154 reinterpret_cast<char*>(png_data.data()), png_data.size()); |
| 155 |
| 156 if (bytes_written != static_cast<int>(png_data.size())) |
| 157 return std::string(); |
| 158 |
| 159 std::vector<std::string> argv; |
| 160 argv.push_back("xdg-icon-resource"); |
| 161 argv.push_back("install"); |
| 162 |
| 163 // Always install in user mode, even if someone runs the browser as root |
| 164 // (people do that). |
| 165 argv.push_back("--mode"); |
| 166 argv.push_back("user"); |
| 167 |
| 168 argv.push_back("--size"); |
| 169 argv.push_back(IntToString(shortcut_info_.favicon.width())); |
| 170 |
| 171 argv.push_back(temp_file_path.value()); |
| 172 std::string icon_name = temp_file_path.BaseName().RemoveExtension().value(); |
| 173 argv.push_back(icon_name); |
| 174 LaunchXdgUtility(argv); |
| 175 return icon_name; |
| 176 } |
| 177 |
| 135 void CreateOnDesktop(const FilePath& shortcut_filename, | 178 void CreateOnDesktop(const FilePath& shortcut_filename, |
| 136 const std::string& contents) { | 179 const std::string& contents) { |
| 137 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. | 180 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. |
| 138 | 181 |
| 139 // Make sure that we will later call openat in a secure way. | 182 // Make sure that we will later call openat in a secure way. |
| 140 DCHECK_EQ(shortcut_filename.BaseName().value(), shortcut_filename.value()); | 183 DCHECK_EQ(shortcut_filename.BaseName().value(), shortcut_filename.value()); |
| 141 | 184 |
| 142 FilePath desktop_path; | 185 FilePath desktop_path; |
| 143 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path)) | 186 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path)) |
| 144 return; | 187 return; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 ScopedTempDir temp_dir; | 219 ScopedTempDir temp_dir; |
| 177 if (!temp_dir.CreateUniqueTempDir()) | 220 if (!temp_dir.CreateUniqueTempDir()) |
| 178 return; | 221 return; |
| 179 | 222 |
| 180 FilePath temp_file_path = temp_dir.path().Append(shortcut_filename); | 223 FilePath temp_file_path = temp_dir.path().Append(shortcut_filename); |
| 181 | 224 |
| 182 int bytes_written = file_util::WriteFile(temp_file_path, contents.data(), | 225 int bytes_written = file_util::WriteFile(temp_file_path, contents.data(), |
| 183 contents.length()); | 226 contents.length()); |
| 184 | 227 |
| 185 if (bytes_written != static_cast<int>(contents.length())) | 228 if (bytes_written != static_cast<int>(contents.length())) |
| 186 return; | 229 return; |
| 187 | 230 |
| 188 std::vector<std::string> argv; | 231 std::vector<std::string> argv; |
| 189 argv.push_back("xdg-desktop-menu"); | 232 argv.push_back("xdg-desktop-menu"); |
| 190 argv.push_back("install"); | 233 argv.push_back("install"); |
| 191 | 234 |
| 192 // Always install in user mode, even if someone runs the browser as root | 235 // Always install in user mode, even if someone runs the browser as root |
| 193 // (people do that). | 236 // (people do that). |
| 194 argv.push_back("--mode"); | 237 argv.push_back("--mode"); |
| 195 argv.push_back("user"); | 238 argv.push_back("user"); |
| 196 | 239 |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 254 L"-" + UTF8ToWide(url.spec()) + L".desktop"; | 297 L"-" + UTF8ToWide(url.spec()) + L".desktop"; |
| 255 file_util::ReplaceIllegalCharacters(&filename, '_'); | 298 file_util::ReplaceIllegalCharacters(&filename, '_'); |
| 256 | 299 |
| 257 // Return BaseName to be absolutely sure we're not vulnerable to a directory | 300 // Return BaseName to be absolutely sure we're not vulnerable to a directory |
| 258 // traversal attack. | 301 // traversal attack. |
| 259 return FilePath::FromWStringHack(filename).BaseName(); | 302 return FilePath::FromWStringHack(filename).BaseName(); |
| 260 } | 303 } |
| 261 | 304 |
| 262 std::string ShellIntegration::GetDesktopFileContents( | 305 std::string ShellIntegration::GetDesktopFileContents( |
| 263 const std::string& template_contents, const GURL& url, | 306 const std::string& template_contents, const GURL& url, |
| 264 const string16& title) { | 307 const string16& title, const std::string& icon_name) { |
| 265 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ | 308 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ |
| 266 // Although not required by the spec, Nautilus on Ubuntu Karmic creates its | 309 // Although not required by the spec, Nautilus on Ubuntu Karmic creates its |
| 267 // launchers with an xdg-open shebang. Follow that convention. | 310 // launchers with an xdg-open shebang. Follow that convention. |
| 268 std::string output_buffer("#!/usr/bin/env xdg-open\n"); | 311 std::string output_buffer("#!/usr/bin/env xdg-open\n"); |
| 269 StringTokenizer tokenizer(template_contents, "\n"); | 312 StringTokenizer tokenizer(template_contents, "\n"); |
| 270 while (tokenizer.GetNext()) { | 313 while (tokenizer.GetNext()) { |
| 271 // TODO(phajdan.jr): Add the icon. | |
| 272 | |
| 273 if (tokenizer.token().substr(0, 5) == "Exec=") { | 314 if (tokenizer.token().substr(0, 5) == "Exec=") { |
| 274 std::string exec_path = tokenizer.token().substr(5); | 315 std::string exec_path = tokenizer.token().substr(5); |
| 275 StringTokenizer exec_tokenizer(exec_path, " "); | 316 StringTokenizer exec_tokenizer(exec_path, " "); |
| 276 std::string final_path; | 317 std::string final_path; |
| 277 while (exec_tokenizer.GetNext()) { | 318 while (exec_tokenizer.GetNext()) { |
| 278 if (exec_tokenizer.token() != "%U") | 319 if (exec_tokenizer.token() != "%U") |
| 279 final_path += exec_tokenizer.token() + " "; | 320 final_path += exec_tokenizer.token() + " "; |
| 280 } | 321 } |
| 281 std::wstring app_switch_wide(switches::kApp); | 322 std::wstring app_switch_wide(switches::kApp); |
| 282 std::string app_switch(StringPrintf("\"--%s=%s\"", | 323 std::string app_switch(StringPrintf("\"--%s=%s\"", |
| 283 WideToUTF8(app_switch_wide).c_str(), | 324 WideToUTF8(app_switch_wide).c_str(), |
| 284 url.spec().c_str())); | 325 url.spec().c_str())); |
| 285 // Sanitize the command line string. | 326 // Sanitize the command line string. |
| 286 ReplaceSubstringsAfterOffset(&app_switch, 0, "%", "%%"); | 327 ReplaceSubstringsAfterOffset(&app_switch, 0, "%", "%%"); |
| 287 ReplaceSubstringsAfterOffset(&app_switch, 0, ";", ""); | 328 ReplaceSubstringsAfterOffset(&app_switch, 0, ";", ""); |
| 288 ReplaceSubstringsAfterOffset(&app_switch, 0, "$", ""); | 329 ReplaceSubstringsAfterOffset(&app_switch, 0, "$", ""); |
| 289 output_buffer += std::string("Exec=") + final_path + app_switch + "\n"; | 330 output_buffer += std::string("Exec=") + final_path + app_switch + "\n"; |
| 290 } else if (tokenizer.token().substr(0, 5) == "Name=") { | 331 } else if (tokenizer.token().substr(0, 5) == "Name=") { |
| 291 std::string final_title = UTF16ToUTF8(title); | 332 std::string final_title = UTF16ToUTF8(title); |
| 292 // Make sure no endline characters can slip in and possibly introduce | 333 // Make sure no endline characters can slip in and possibly introduce |
| 293 // additional lines (like Exec, which makes it a security risk). Also | 334 // additional lines (like Exec, which makes it a security risk). Also |
| 294 // use the URL as a default when the title is empty. | 335 // use the URL as a default when the title is empty. |
| 295 if (final_title.empty() || | 336 if (final_title.empty() || |
| 296 final_title.find("\n") != std::string::npos || | 337 final_title.find("\n") != std::string::npos || |
| 297 final_title.find("\r") != std::string::npos) | 338 final_title.find("\r") != std::string::npos) { |
| 298 final_title = url.spec(); | 339 final_title = url.spec(); |
| 340 } |
| 299 output_buffer += StringPrintf("Name=%s\n", final_title.c_str()); | 341 output_buffer += StringPrintf("Name=%s\n", final_title.c_str()); |
| 300 } else if (tokenizer.token().substr(0, 11) == "GenericName" || | 342 } else if (tokenizer.token().substr(0, 11) == "GenericName" || |
| 301 tokenizer.token().substr(0, 7) == "Comment" || | 343 tokenizer.token().substr(0, 7) == "Comment" || |
| 302 tokenizer.token().substr(0, 1) == "#") { | 344 tokenizer.token().substr(0, 1) == "#") { |
| 303 // Skip comment lines. | 345 // Skip comment lines. |
| 346 } else if (tokenizer.token().substr(0, 5) == "Icon=" && |
| 347 !icon_name.empty()) { |
| 348 output_buffer += StringPrintf("Icon=%s\n", icon_name.c_str()); |
| 304 } else { | 349 } else { |
| 305 output_buffer += tokenizer.token() + "\n"; | 350 output_buffer += tokenizer.token() + "\n"; |
| 306 } | 351 } |
| 307 } | 352 } |
| 308 return output_buffer; | 353 return output_buffer; |
| 309 } | 354 } |
| 310 | 355 |
| 311 void ShellIntegration::CreateDesktopShortcut( | 356 void ShellIntegration::CreateDesktopShortcut( |
| 312 const ShortcutInfo& shortcut_info) { | 357 const ShortcutInfo& shortcut_info) { |
| 313 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, | 358 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, |
| 314 new CreateDesktopShortcutTask(shortcut_info)); | 359 new CreateDesktopShortcutTask(shortcut_info)); |
| 315 } | 360 } |
| OLD | NEW |