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 |