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 |