| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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_linux.h" |
| 6 | 6 |
| 7 #include <fcntl.h> | 7 #include <fcntl.h> |
| 8 #include <glib.h> | 8 #include <glib.h> |
| 9 #include <stdlib.h> | 9 #include <stdlib.h> |
| 10 #include <sys/stat.h> | 10 #include <sys/stat.h> |
| 11 #include <sys/types.h> | 11 #include <sys/types.h> |
| 12 #include <unistd.h> | 12 #include <unistd.h> |
| 13 | 13 |
| 14 #include <string> | 14 #include <string> |
| 15 #include <vector> | 15 #include <vector> |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 106 argv.push_back(base::IntToString(bitmap->width())); | 106 argv.push_back(base::IntToString(bitmap->width())); |
| 107 | 107 |
| 108 argv.push_back(temp_file_path.value()); | 108 argv.push_back(temp_file_path.value()); |
| 109 std::string icon_name = temp_file_path.BaseName().RemoveExtension().value(); | 109 std::string icon_name = temp_file_path.BaseName().RemoveExtension().value(); |
| 110 argv.push_back(icon_name); | 110 argv.push_back(icon_name); |
| 111 int exit_code; | 111 int exit_code; |
| 112 LaunchXdgUtility(argv, &exit_code); | 112 LaunchXdgUtility(argv, &exit_code); |
| 113 return icon_name; | 113 return icon_name; |
| 114 } | 114 } |
| 115 | 115 |
| 116 void CreateShortcutOnDesktop(const FilePath& shortcut_filename, | 116 bool CreateShortcutOnDesktop(const FilePath& shortcut_filename, |
| 117 const std::string& contents) { | 117 const std::string& contents) { |
| 118 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. | |
| 119 | |
| 120 // Make sure that we will later call openat in a secure way. | 118 // Make sure that we will later call openat in a secure way. |
| 121 DCHECK_EQ(shortcut_filename.BaseName().value(), shortcut_filename.value()); | 119 DCHECK_EQ(shortcut_filename.BaseName().value(), shortcut_filename.value()); |
| 122 | 120 |
| 123 FilePath desktop_path; | 121 FilePath desktop_path; |
| 124 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path)) | 122 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path)) |
| 125 return; | 123 return false; |
| 126 | 124 |
| 127 int desktop_fd = open(desktop_path.value().c_str(), O_RDONLY | O_DIRECTORY); | 125 int desktop_fd = open(desktop_path.value().c_str(), O_RDONLY | O_DIRECTORY); |
| 128 if (desktop_fd < 0) | 126 if (desktop_fd < 0) |
| 129 return; | 127 return false; |
| 130 | 128 |
| 131 int fd = openat(desktop_fd, shortcut_filename.value().c_str(), | 129 int fd = openat(desktop_fd, shortcut_filename.value().c_str(), |
| 132 O_CREAT | O_EXCL | O_WRONLY, | 130 O_CREAT | O_EXCL | O_WRONLY, |
| 133 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); | 131 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); |
| 134 if (fd < 0) { | 132 if (fd < 0) { |
| 135 if (HANDLE_EINTR(close(desktop_fd)) < 0) | 133 if (HANDLE_EINTR(close(desktop_fd)) < 0) |
| 136 PLOG(ERROR) << "close"; | 134 PLOG(ERROR) << "close"; |
| 137 return; | 135 return false; |
| 138 } | 136 } |
| 139 | 137 |
| 140 ssize_t bytes_written = file_util::WriteFileDescriptor(fd, contents.data(), | 138 ssize_t bytes_written = file_util::WriteFileDescriptor(fd, contents.data(), |
| 141 contents.length()); | 139 contents.length()); |
| 142 if (HANDLE_EINTR(close(fd)) < 0) | 140 if (HANDLE_EINTR(close(fd)) < 0) |
| 143 PLOG(ERROR) << "close"; | 141 PLOG(ERROR) << "close"; |
| 144 | 142 |
| 145 if (bytes_written != static_cast<ssize_t>(contents.length())) { | 143 if (bytes_written != static_cast<ssize_t>(contents.length())) { |
| 146 // Delete the file. No shortuct is better than corrupted one. Use unlinkat | 144 // Delete the file. No shortuct is better than corrupted one. Use unlinkat |
| 147 // to make sure we're deleting the file in the directory we think we are. | 145 // to make sure we're deleting the file in the directory we think we are. |
| 148 // Even if an attacker manager to put something other at | 146 // Even if an attacker manager to put something other at |
| 149 // |shortcut_filename| we'll just undo his action. | 147 // |shortcut_filename| we'll just undo his action. |
| 150 unlinkat(desktop_fd, shortcut_filename.value().c_str(), 0); | 148 unlinkat(desktop_fd, shortcut_filename.value().c_str(), 0); |
| 151 } | 149 } |
| 152 | 150 |
| 153 if (HANDLE_EINTR(close(desktop_fd)) < 0) | 151 if (HANDLE_EINTR(close(desktop_fd)) < 0) |
| 154 PLOG(ERROR) << "close"; | 152 PLOG(ERROR) << "close"; |
| 153 |
| 154 return true; |
| 155 } | 155 } |
| 156 | 156 |
| 157 void CreateShortcutInApplicationsMenu(const FilePath& shortcut_filename, | 157 bool CreateShortcutInApplicationsMenu(const FilePath& shortcut_filename, |
| 158 const std::string& contents) { | 158 const std::string& contents) { |
| 159 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. | |
| 160 ScopedTempDir temp_dir; | 159 ScopedTempDir temp_dir; |
| 161 if (!temp_dir.CreateUniqueTempDir()) | 160 if (!temp_dir.CreateUniqueTempDir()) |
| 162 return; | 161 return false; |
| 163 | 162 |
| 164 FilePath temp_file_path = temp_dir.path().Append(shortcut_filename); | 163 FilePath temp_file_path = temp_dir.path().Append(shortcut_filename); |
| 165 | 164 |
| 166 int bytes_written = file_util::WriteFile(temp_file_path, contents.data(), | 165 int bytes_written = file_util::WriteFile(temp_file_path, contents.data(), |
| 167 contents.length()); | 166 contents.length()); |
| 168 | 167 |
| 169 if (bytes_written != static_cast<int>(contents.length())) | 168 if (bytes_written != static_cast<int>(contents.length())) |
| 170 return; | 169 return false; |
| 171 | 170 |
| 172 std::vector<std::string> argv; | 171 std::vector<std::string> argv; |
| 173 argv.push_back("xdg-desktop-menu"); | 172 argv.push_back("xdg-desktop-menu"); |
| 174 argv.push_back("install"); | 173 argv.push_back("install"); |
| 175 | 174 |
| 176 // Always install in user mode, even if someone runs the browser as root | 175 // Always install in user mode, even if someone runs the browser as root |
| 177 // (people do that). | 176 // (people do that). |
| 178 argv.push_back("--mode"); | 177 argv.push_back("--mode"); |
| 179 argv.push_back("user"); | 178 argv.push_back("user"); |
| 180 | 179 |
| 181 argv.push_back(temp_file_path.value()); | 180 argv.push_back(temp_file_path.value()); |
| 182 int exit_code; | 181 int exit_code; |
| 183 LaunchXdgUtility(argv, &exit_code); | 182 LaunchXdgUtility(argv, &exit_code); |
| 183 return exit_code == 0; |
| 184 } | 184 } |
| 185 | 185 |
| 186 // Quote a string such that it appears as one verbatim argument for the Exec | 186 // Quote a string such that it appears as one verbatim argument for the Exec |
| 187 // key in a desktop file. | 187 // key in a desktop file. |
| 188 std::string QuoteArgForDesktopFileExec(const std::string& arg) { | 188 std::string QuoteArgForDesktopFileExec(const std::string& arg) { |
| 189 // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html | 189 // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html |
| 190 | 190 |
| 191 // Quoting is only necessary if the argument has a reserved character. | 191 // Quoting is only necessary if the argument has a reserved character. |
| 192 if (arg.find_first_of(" \t\n\"'\\><~|&;$*?#()`") == std::string::npos) | 192 if (arg.find_first_of(" \t\n\"'\\><~|&;$*?#()`") == std::string::npos) |
| 193 return arg; // No quoting necessary. | 193 return arg; // No quoting necessary. |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 470 | 470 |
| 471 return FilePath(); | 471 return FilePath(); |
| 472 } | 472 } |
| 473 | 473 |
| 474 // static | 474 // static |
| 475 std::string ShellIntegration::GetDesktopFileContents( | 475 std::string ShellIntegration::GetDesktopFileContents( |
| 476 const std::string& template_contents, | 476 const std::string& template_contents, |
| 477 const std::string& app_name, | 477 const std::string& app_name, |
| 478 const GURL& url, | 478 const GURL& url, |
| 479 const std::string& extension_id, | 479 const std::string& extension_id, |
| 480 const bool is_platform_app, |
| 481 const FilePath& web_app_path, |
| 482 const FilePath& extension_path, |
| 480 const string16& title, | 483 const string16& title, |
| 481 const std::string& icon_name) { | 484 const std::string& icon_name) { |
| 482 if (template_contents.empty()) | 485 if (template_contents.empty()) |
| 483 return std::string(kXdgOpenShebang) + "\n"; | 486 return std::string(kXdgOpenShebang) + "\n"; |
| 484 | 487 |
| 485 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ | 488 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ |
| 486 // http://developer.gnome.org/glib/unstable/glib-Key-value-file-parser.html | 489 // http://developer.gnome.org/glib/unstable/glib-Key-value-file-parser.html |
| 487 GKeyFile* key_file = g_key_file_new(); | 490 GKeyFile* key_file = g_key_file_new(); |
| 488 GError* err = NULL; | 491 GError* err = NULL; |
| 489 // Loading the data will strip translations and comments from the desktop | 492 // Loading the data will strip translations and comments from the desktop |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 534 std::string exec_string(exec_c_string); | 537 std::string exec_string(exec_c_string); |
| 535 g_free(exec_c_string); | 538 g_free(exec_c_string); |
| 536 StringTokenizer exec_tokenizer(exec_string, " "); | 539 StringTokenizer exec_tokenizer(exec_string, " "); |
| 537 | 540 |
| 538 std::string final_path; | 541 std::string final_path; |
| 539 while (exec_tokenizer.GetNext() && exec_tokenizer.token() != "%U") { | 542 while (exec_tokenizer.GetNext() && exec_tokenizer.token() != "%U") { |
| 540 if (!final_path.empty()) | 543 if (!final_path.empty()) |
| 541 final_path += " "; | 544 final_path += " "; |
| 542 final_path += exec_tokenizer.token(); | 545 final_path += exec_tokenizer.token(); |
| 543 } | 546 } |
| 544 CommandLine cmd_line = | 547 CommandLine cmd_line(CommandLine::NO_PROGRAM); |
| 545 ShellIntegration::CommandLineArgsForLauncher(url, extension_id); | 548 if (is_platform_app) { |
| 549 cmd_line = ShellIntegration::CommandLineArgsForPlatformApp( |
| 550 extension_id, web_app_path, extension_path); |
| 551 } else { |
| 552 cmd_line = ShellIntegration::CommandLineArgsForLauncher( |
| 553 url, extension_id); |
| 554 } |
| 546 const CommandLine::SwitchMap& switch_map = cmd_line.GetSwitches(); | 555 const CommandLine::SwitchMap& switch_map = cmd_line.GetSwitches(); |
| 547 for (CommandLine::SwitchMap::const_iterator i = switch_map.begin(); | 556 for (CommandLine::SwitchMap::const_iterator i = switch_map.begin(); |
| 548 i != switch_map.end(); ++i) { | 557 i != switch_map.end(); ++i) { |
| 549 if (i->second.empty()) { | 558 if (i->second.empty()) { |
| 550 final_path += " --" + i->first; | 559 final_path += " --" + i->first; |
| 551 } else { | 560 } else { |
| 552 final_path += " " + QuoteArgForDesktopFileExec("--" + i->first + | 561 final_path += " " + QuoteArgForDesktopFileExec("--" + i->first + |
| 553 "=" + i->second); | 562 "=" + i->second); |
| 554 } | 563 } |
| 555 } | 564 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 575 if (data_dump) { | 584 if (data_dump) { |
| 576 output_buffer += data_dump; | 585 output_buffer += data_dump; |
| 577 g_free(data_dump); | 586 g_free(data_dump); |
| 578 } | 587 } |
| 579 | 588 |
| 580 g_key_file_free(key_file); | 589 g_key_file_free(key_file); |
| 581 return output_buffer; | 590 return output_buffer; |
| 582 } | 591 } |
| 583 | 592 |
| 584 // static | 593 // static |
| 585 void ShellIntegration::CreateDesktopShortcut( | 594 bool ShellIntegration::CreateDesktopShortcut( |
| 586 const ShortcutInfo& shortcut_info, const std::string& shortcut_template) { | 595 const ShortcutInfo& shortcut_info, |
| 587 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. | 596 const std::string& shortcut_template) { |
| 597 DCHECK(!shortcut_info.is_platform_app); |
| 598 DCHECK(shortcut_info.extension_id.empty()); |
| 588 | 599 |
| 600 return ShellIntegrationLinux::CreateDesktopShortcutForChromeApp( |
| 601 shortcut_info, FilePath(), shortcut_template); |
| 602 } |
| 603 |
| 604 namespace ShellIntegrationLinux { |
| 605 |
| 606 bool CreateDesktopShortcutForChromeApp( |
| 607 const ShellIntegration::ShortcutInfo& shortcut_info, |
| 608 const FilePath& web_app_path, |
| 609 const std::string& shortcut_template) { |
| 589 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 610 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 590 | 611 |
| 591 FilePath shortcut_filename = GetDesktopShortcutFilename(shortcut_info.url); | 612 FilePath shortcut_filename = ShellIntegration::GetDesktopShortcutFilename( |
| 613 shortcut_info.url); |
| 592 if (shortcut_filename.empty()) | 614 if (shortcut_filename.empty()) |
| 593 return; | 615 return false; |
| 594 | 616 |
| 595 std::string icon_name = CreateShortcutIcon(shortcut_info, shortcut_filename); | 617 std::string icon_name = CreateShortcutIcon(shortcut_info, shortcut_filename); |
| 596 | 618 |
| 597 std::string app_name = | 619 std::string app_name = |
| 598 web_app::GenerateApplicationNameFromInfo(shortcut_info); | 620 web_app::GenerateApplicationNameFromInfo(shortcut_info); |
| 599 std::string contents = GetDesktopFileContents( | 621 std::string contents = ShellIntegration::GetDesktopFileContents( |
| 600 shortcut_template, | 622 shortcut_template, |
| 601 app_name, | 623 app_name, |
| 602 shortcut_info.url, | 624 shortcut_info.url, |
| 603 shortcut_info.extension_id, | 625 shortcut_info.extension_id, |
| 626 shortcut_info.is_platform_app, |
| 627 web_app_path, |
| 628 shortcut_info.extension_path, |
| 604 shortcut_info.title, | 629 shortcut_info.title, |
| 605 icon_name); | 630 icon_name); |
| 606 | 631 |
| 632 bool success = true; |
| 633 |
| 607 if (shortcut_info.create_on_desktop) | 634 if (shortcut_info.create_on_desktop) |
| 608 CreateShortcutOnDesktop(shortcut_filename, contents); | 635 success = CreateShortcutOnDesktop(shortcut_filename, contents); |
| 609 | 636 |
| 610 if (shortcut_info.create_in_applications_menu) | 637 if (shortcut_info.create_in_applications_menu) |
| 611 CreateShortcutInApplicationsMenu(shortcut_filename, contents); | 638 success = CreateShortcutInApplicationsMenu(shortcut_filename, contents) && |
| 639 success; |
| 640 |
| 641 return success; |
| 612 } | 642 } |
| 643 |
| 644 } // namespace ShellIntegrationLinux |
| OLD | NEW |