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/file_path.h" | 18 #include "base/file_path.h" |
18 #include "base/file_util.h" | 19 #include "base/file_util.h" |
19 #include "base/message_loop.h" | 20 #include "base/message_loop.h" |
20 #include "base/path_service.h" | 21 #include "base/path_service.h" |
21 #include "base/process_util.h" | 22 #include "base/process_util.h" |
22 #include "base/scoped_temp_dir.h" | 23 #include "base/scoped_temp_dir.h" |
23 #include "base/string_tokenizer.h" | 24 #include "base/string_tokenizer.h" |
24 #include "base/string_util.h" | 25 #include "base/string_util.h" |
25 #include "base/task.h" | 26 #include "base/task.h" |
26 #include "base/thread.h" | 27 #include "base/thread.h" |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
110 CreateDesktopShortcutTask(const ShellIntegration::ShortcutInfo& shortcut_info) | 111 CreateDesktopShortcutTask(const ShellIntegration::ShortcutInfo& shortcut_info) |
111 : shortcut_info_(shortcut_info) { | 112 : shortcut_info_(shortcut_info) { |
112 } | 113 } |
113 | 114 |
114 virtual void Run() { | 115 virtual void Run() { |
115 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. | 116 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. |
116 std::string template_contents; | 117 std::string template_contents; |
117 if (!GetDesktopShortcutTemplate(&template_contents)) | 118 if (!GetDesktopShortcutTemplate(&template_contents)) |
118 return; | 119 return; |
119 | 120 |
120 std::string contents = ShellIntegration::GetDesktopFileContents( | |
121 template_contents, shortcut_info_.url, shortcut_info_.title); | |
122 | |
123 ScopedTempDir temp_dir; | |
124 if (!temp_dir.CreateUniqueTempDir()) | |
125 return; | |
126 | |
127 FilePath shortcut_filename = | 121 FilePath shortcut_filename = |
128 ShellIntegration::GetDesktopShortcutFilename(shortcut_info_.url); | 122 ShellIntegration::GetDesktopShortcutFilename(shortcut_info_.url); |
129 | 123 |
| 124 std::string contents = ShellIntegration::GetDesktopFileContents( |
| 125 template_contents, shortcut_info_.url, shortcut_info_.title); |
| 126 |
| 127 if (shortcut_info_.create_on_desktop) |
| 128 CreateOnDesktop(shortcut_filename, contents); |
| 129 |
| 130 if (shortcut_info_.create_in_applications_menu) |
| 131 CreateInApplicationsMenu(shortcut_filename, contents); |
| 132 } |
| 133 |
| 134 private: |
| 135 void CreateOnDesktop(const FilePath& shortcut_filename, |
| 136 const std::string& contents) { |
| 137 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. |
| 138 |
| 139 // Make sure that we will later call openat in a secure way. |
| 140 DCHECK_EQ(shortcut_filename.BaseName().value(), shortcut_filename.value()); |
| 141 |
| 142 FilePath desktop_path; |
| 143 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path)) |
| 144 return; |
| 145 |
| 146 int desktop_fd = open(desktop_path.value().c_str(), O_RDONLY | O_DIRECTORY); |
| 147 if (desktop_fd < 0) |
| 148 return; |
| 149 |
| 150 int fd = openat(desktop_fd, shortcut_filename.value().c_str(), |
| 151 O_CREAT | O_EXCL | O_WRONLY, |
| 152 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); |
| 153 if (fd < 0) { |
| 154 HANDLE_EINTR(close(desktop_fd)); |
| 155 return; |
| 156 } |
| 157 |
| 158 ssize_t bytes_written = file_util::WriteFileDescriptor(fd, contents.data(), |
| 159 contents.length()); |
| 160 HANDLE_EINTR(close(fd)); |
| 161 |
| 162 if (bytes_written != static_cast<ssize_t>(contents.length())) { |
| 163 // Delete the file. No shortuct is better than corrupted one. Use unlinkat |
| 164 // to make sure we're deleting the file in the directory we think we are. |
| 165 // Even if an attacker manager to put something other at |
| 166 // |shortcut_filename| we'll just undo his action. |
| 167 unlinkat(desktop_fd, shortcut_filename.value().c_str(), 0); |
| 168 } |
| 169 |
| 170 HANDLE_EINTR(close(desktop_fd)); |
| 171 } |
| 172 |
| 173 void CreateInApplicationsMenu(const FilePath& shortcut_filename, |
| 174 const std::string& contents) { |
| 175 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. |
| 176 ScopedTempDir temp_dir; |
| 177 if (!temp_dir.CreateUniqueTempDir()) |
| 178 return; |
| 179 |
130 FilePath temp_file_path = temp_dir.path().Append(shortcut_filename); | 180 FilePath temp_file_path = temp_dir.path().Append(shortcut_filename); |
131 | 181 |
132 int bytes_written = file_util::WriteFile(temp_file_path, contents.data(), | 182 int bytes_written = file_util::WriteFile(temp_file_path, contents.data(), |
133 contents.length()); | 183 contents.length()); |
134 | 184 |
135 if (bytes_written != static_cast<int>(contents.length())) | 185 if (bytes_written != static_cast<int>(contents.length())) |
136 return; | 186 return; |
137 | 187 |
138 if (shortcut_info_.create_on_desktop) { | 188 std::vector<std::string> argv; |
139 FilePath desktop_path; | 189 argv.push_back("xdg-desktop-menu"); |
140 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path)) | 190 argv.push_back("install"); |
141 return; | |
142 desktop_path = desktop_path.Append(shortcut_filename); | |
143 | 191 |
144 if (!file_util::PathExists(desktop_path)) | 192 // Always install in user mode, even if someone runs the browser as root |
145 file_util::CopyFile(temp_file_path, desktop_path); | 193 // (people do that). |
146 } | 194 argv.push_back("--mode"); |
| 195 argv.push_back("user"); |
147 | 196 |
148 if (shortcut_info_.create_in_applications_menu) { | 197 argv.push_back(temp_file_path.value()); |
149 std::vector<std::string> argv; | 198 LaunchXdgUtility(argv); |
150 argv.push_back("xdg-desktop-menu"); | |
151 argv.push_back("install"); | |
152 | |
153 // Always install in user mode, even if someone runs the browser as root | |
154 // (people do that). | |
155 argv.push_back("--mode"); | |
156 argv.push_back("user"); | |
157 | |
158 argv.push_back(temp_file_path.value()); | |
159 LaunchXdgUtility(argv); | |
160 } | |
161 } | 199 } |
162 | 200 |
163 private: | |
164 const ShellIntegration::ShortcutInfo shortcut_info_; | 201 const ShellIntegration::ShortcutInfo shortcut_info_; |
165 | 202 |
166 DISALLOW_COPY_AND_ASSIGN(CreateDesktopShortcutTask); | 203 DISALLOW_COPY_AND_ASSIGN(CreateDesktopShortcutTask); |
167 }; | 204 }; |
168 | 205 |
169 } // namespace | 206 } // namespace |
170 | 207 |
171 // We delegate the difficulty of setting the default browser in Linux desktop | 208 // We delegate the difficulty of setting the default browser in Linux desktop |
172 // environments to a new xdg utility, xdg-settings. We have to include a copy of | 209 // environments to a new xdg utility, xdg-settings. We have to include a copy of |
173 // it for this to work, obviously, but that's actually the suggested approach | 210 // it for this to work, obviously, but that's actually the suggested approach |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
219 | 256 |
220 // Return BaseName to be absolutely sure we're not vulnerable to a directory | 257 // Return BaseName to be absolutely sure we're not vulnerable to a directory |
221 // traversal attack. | 258 // traversal attack. |
222 return FilePath::FromWStringHack(filename).BaseName(); | 259 return FilePath::FromWStringHack(filename).BaseName(); |
223 } | 260 } |
224 | 261 |
225 std::string ShellIntegration::GetDesktopFileContents( | 262 std::string ShellIntegration::GetDesktopFileContents( |
226 const std::string& template_contents, const GURL& url, | 263 const std::string& template_contents, const GURL& url, |
227 const string16& title) { | 264 const string16& title) { |
228 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ | 265 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ |
229 std::string output_buffer; | 266 // Although not required by the spec, Nautilus on Ubuntu Karmic creates its |
| 267 // launchers with an xdg-open shebang. Follow that convention. |
| 268 std::string output_buffer("#!/usr/bin/env xdg-open\n"); |
230 StringTokenizer tokenizer(template_contents, "\n"); | 269 StringTokenizer tokenizer(template_contents, "\n"); |
231 while (tokenizer.GetNext()) { | 270 while (tokenizer.GetNext()) { |
232 // TODO(phajdan.jr): Add the icon. | 271 // TODO(phajdan.jr): Add the icon. |
233 | 272 |
234 if (tokenizer.token().substr(0, 5) == "Exec=") { | 273 if (tokenizer.token().substr(0, 5) == "Exec=") { |
235 std::string exec_path = tokenizer.token().substr(5); | 274 std::string exec_path = tokenizer.token().substr(5); |
236 StringTokenizer exec_tokenizer(exec_path, " "); | 275 StringTokenizer exec_tokenizer(exec_path, " "); |
237 std::string final_path; | 276 std::string final_path; |
238 while (exec_tokenizer.GetNext()) { | 277 while (exec_tokenizer.GetNext()) { |
239 if (exec_tokenizer.token() != "%U") | 278 if (exec_tokenizer.token() != "%U") |
240 final_path += exec_tokenizer.token() + " "; | 279 final_path += exec_tokenizer.token() + " "; |
241 } | 280 } |
242 std::wstring app_switch_wide(switches::kApp); | 281 std::wstring app_switch_wide(switches::kApp); |
243 std::string app_switch(StringPrintf("\"--%s=%s\"", | 282 std::string app_switch(StringPrintf("\"--%s=%s\"", |
244 WideToUTF8(app_switch_wide).c_str(), | 283 WideToUTF8(app_switch_wide).c_str(), |
245 url.spec().c_str())); | 284 url.spec().c_str())); |
| 285 // Sanitize the command line string. |
246 ReplaceSubstringsAfterOffset(&app_switch, 0, "%", "%%"); | 286 ReplaceSubstringsAfterOffset(&app_switch, 0, "%", "%%"); |
| 287 ReplaceSubstringsAfterOffset(&app_switch, 0, ";", ""); |
| 288 ReplaceSubstringsAfterOffset(&app_switch, 0, "$", ""); |
247 output_buffer += std::string("Exec=") + final_path + app_switch + "\n"; | 289 output_buffer += std::string("Exec=") + final_path + app_switch + "\n"; |
248 } else if (tokenizer.token().substr(0, 5) == "Name=") { | 290 } else if (tokenizer.token().substr(0, 5) == "Name=") { |
249 std::string final_title = UTF16ToUTF8(title); | 291 std::string final_title = UTF16ToUTF8(title); |
250 // Make sure no endline characters can slip in and possibly introduce | 292 // Make sure no endline characters can slip in and possibly introduce |
251 // additional lines (like Exec, which makes it a security risk). Also | 293 // additional lines (like Exec, which makes it a security risk). Also |
252 // use the URL as a default when the title is empty. | 294 // use the URL as a default when the title is empty. |
253 if (final_title.empty() || | 295 if (final_title.empty() || |
254 final_title.find("\n") != std::string::npos || | 296 final_title.find("\n") != std::string::npos || |
255 final_title.find("\r") != std::string::npos) | 297 final_title.find("\r") != std::string::npos) |
256 final_title = url.spec(); | 298 final_title = url.spec(); |
257 output_buffer += StringPrintf("Name=%s\n", final_title.c_str()); | 299 output_buffer += StringPrintf("Name=%s\n", final_title.c_str()); |
258 } else if (tokenizer.token().substr(0, 8) == "Comment=") { | 300 } else if (tokenizer.token().substr(0, 11) == "GenericName" || |
259 // Skip the line. | 301 tokenizer.token().substr(0, 7) == "Comment" || |
| 302 tokenizer.token().substr(0, 1) == "#") { |
| 303 // Skip comment lines. |
260 } else { | 304 } else { |
261 output_buffer += tokenizer.token() + "\n"; | 305 output_buffer += tokenizer.token() + "\n"; |
262 } | 306 } |
263 } | 307 } |
264 return output_buffer; | 308 return output_buffer; |
265 } | 309 } |
266 | 310 |
267 void ShellIntegration::CreateDesktopShortcut( | 311 void ShellIntegration::CreateDesktopShortcut( |
268 const ShortcutInfo& shortcut_info) { | 312 const ShortcutInfo& shortcut_info) { |
269 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, | 313 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, |
270 new CreateDesktopShortcutTask(shortcut_info)); | 314 new CreateDesktopShortcutTask(shortcut_info)); |
271 } | 315 } |
OLD | NEW |