OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 // Download utility implementation | 5 // Download utility implementation |
6 | 6 |
7 #include "chrome/browser/download/download_util.h" | 7 #include "chrome/browser/download/download_util.h" |
8 | 8 |
9 #if defined(OS_WIN) | 9 #if defined(OS_WIN) |
10 #include <shobjidl.h> | 10 #include <shobjidl.h> |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 | 92 |
93 // The maximum number of 'uniquified' files we will try to create. | 93 // The maximum number of 'uniquified' files we will try to create. |
94 // This is used when the filename we're trying to download is already in use, | 94 // This is used when the filename we're trying to download is already in use, |
95 // so we create a new unique filename by appending " (nnn)" before the | 95 // so we create a new unique filename by appending " (nnn)" before the |
96 // extension, where 1 <= nnn <= kMaxUniqueFiles. | 96 // extension, where 1 <= nnn <= kMaxUniqueFiles. |
97 // Also used by code that cleans up said files. | 97 // Also used by code that cleans up said files. |
98 static const int kMaxUniqueFiles = 100; | 98 static const int kMaxUniqueFiles = 100; |
99 | 99 |
100 namespace { | 100 namespace { |
101 | 101 |
102 #if defined(OS_WIN) | |
103 // Returns whether the specified extension is automatically integrated into the | |
104 // windows shell. | |
105 bool IsShellIntegratedExtension(const string16& extension) { | |
106 string16 extension_lower = StringToLowerASCII(extension); | |
107 | |
108 static const wchar_t* const integrated_extensions[] = { | |
109 // See <http://msdn.microsoft.com/en-us/library/ms811694.aspx>. | |
110 L"local", | |
111 // Right-clicking on shortcuts can be magical. | |
112 L"lnk", | |
113 }; | |
114 | |
115 for (int i = 0; i < arraysize(integrated_extensions); ++i) { | |
116 if (extension_lower == integrated_extensions[i]) | |
117 return true; | |
118 } | |
119 | |
120 // See <http://www.juniper.net/security/auto/vulnerabilities/vuln2612.html>. | |
121 // That vulnerability report is not exactly on point, but files become magical | |
122 // if their end in a CLSID. Here we block extensions that look like CLSIDs. | |
123 if (!extension_lower.empty() && extension_lower[0] == L'{' && | |
124 extension_lower[extension_lower.length() - 1] == L'}') | |
125 return true; | |
126 | |
127 return false; | |
128 } | |
129 | |
130 // Returns whether the specified file name is a reserved name on windows. | |
131 // This includes names like "com2.zip" (which correspond to devices) and | |
132 // desktop.ini and thumbs.db which have special meaning to the windows shell. | |
133 bool IsReservedName(const string16& filename) { | |
134 // This list is taken from the MSDN article "Naming a file" | |
135 // http://msdn2.microsoft.com/en-us/library/aa365247(VS.85).aspx | |
136 // I also added clock$ because GetSaveFileName seems to consider it as a | |
137 // reserved name too. | |
138 static const wchar_t* const known_devices[] = { | |
139 L"con", L"prn", L"aux", L"nul", L"com1", L"com2", L"com3", L"com4", L"com5", | |
140 L"com6", L"com7", L"com8", L"com9", L"lpt1", L"lpt2", L"lpt3", L"lpt4", | |
141 L"lpt5", L"lpt6", L"lpt7", L"lpt8", L"lpt9", L"clock$" | |
142 }; | |
143 string16 filename_lower = StringToLowerASCII(filename); | |
144 | |
145 for (int i = 0; i < arraysize(known_devices); ++i) { | |
146 // Exact match. | |
147 if (filename_lower == known_devices[i]) | |
148 return true; | |
149 // Starts with "DEVICE.". | |
150 if (filename_lower.find(string16(known_devices[i]) + L".") == 0) | |
151 return true; | |
152 } | |
153 | |
154 static const wchar_t* const magic_names[] = { | |
155 // These file names are used by the "Customize folder" feature of the shell. | |
156 L"desktop.ini", | |
157 L"thumbs.db", | |
158 }; | |
159 | |
160 for (int i = 0; i < arraysize(magic_names); ++i) { | |
161 if (filename_lower == magic_names[i]) | |
162 return true; | |
163 } | |
164 | |
165 return false; | |
166 } | |
167 #endif // OS_WIN | |
168 | |
169 void GenerateFileNameInternal(const GURL& url, | 102 void GenerateFileNameInternal(const GURL& url, |
170 const std::string& content_disposition, | 103 const std::string& content_disposition, |
171 const std::string& referrer_charset, | 104 const std::string& referrer_charset, |
172 const std::string& suggested_name, | 105 const std::string& suggested_name, |
173 const std::string& mime_type, | 106 const std::string& mime_type, |
174 FilePath* generated_name) { | 107 FilePath* generated_name) { |
175 | |
176 string16 default_file_name( | 108 string16 default_file_name( |
177 l10n_util::GetStringUTF16(IDS_DEFAULT_DOWNLOAD_FILENAME)); | 109 l10n_util::GetStringUTF16(IDS_DEFAULT_DOWNLOAD_FILENAME)); |
178 | 110 |
179 string16 new_name = net::GetSuggestedFilename(GURL(url), | 111 *generated_name = net::GenerateFileName(url, content_disposition, |
180 content_disposition, | 112 referrer_charset, suggested_name, |
181 referrer_charset, | 113 mime_type, default_file_name); |
182 suggested_name, | |
183 default_file_name); | |
184 | |
185 // TODO(evan): this code is totally wrong -- we should just generate | |
186 // Unicode filenames and do all this encoding switching at the end. | |
187 // However, I'm just shuffling wrong code around, at least not adding | |
188 // to it. | |
189 #if defined(OS_WIN) | |
190 *generated_name = FilePath(new_name); | |
191 #else | |
192 *generated_name = FilePath( | |
193 base::SysWideToNativeMB(UTF16ToWide(new_name))); | |
194 #endif | |
195 | |
196 DCHECK(!generated_name->empty()); | |
197 | |
198 GenerateSafeFileName(mime_type, generated_name); | |
199 } | 114 } |
200 | 115 |
201 // All possible error codes from the network module. Note that the error codes | 116 // All possible error codes from the network module. Note that the error codes |
202 // are all positive (since histograms expect positive sample values). | 117 // are all positive (since histograms expect positive sample values). |
203 const int kAllNetErrorCodes[] = { | 118 const int kAllNetErrorCodes[] = { |
204 #define NET_ERROR(label, value) -(value), | 119 #define NET_ERROR(label, value) -(value), |
205 #include "net/base/net_error_list.h" | 120 #include "net/base/net_error_list.h" |
206 #undef NET_ERROR | 121 #undef NET_ERROR |
207 }; | 122 }; |
208 | 123 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
244 | 159 |
245 bool DownloadPathIsDangerous(const FilePath& download_path) { | 160 bool DownloadPathIsDangerous(const FilePath& download_path) { |
246 FilePath desktop_dir; | 161 FilePath desktop_dir; |
247 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_dir)) { | 162 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_dir)) { |
248 NOTREACHED(); | 163 NOTREACHED(); |
249 return false; | 164 return false; |
250 } | 165 } |
251 return (download_path == desktop_dir); | 166 return (download_path == desktop_dir); |
252 } | 167 } |
253 | 168 |
254 void GenerateExtension(const FilePath& file_name, | |
255 const std::string& mime_type, | |
256 FilePath::StringType* generated_extension) { | |
257 // We're worried about two things here: | |
258 // | |
259 // 1) Usability. If the site fails to provide a file extension, we want to | |
260 // guess a reasonable file extension based on the content type. | |
261 // | |
262 // 2) Shell integration. Some file extensions automatically integrate with | |
263 // the shell. We block these extensions to prevent a malicious web site | |
264 // from integrating with the user's shell. | |
265 | |
266 // See if our file name already contains an extension. | |
267 FilePath::StringType extension = file_name.Extension(); | |
268 if (!extension.empty()) | |
269 extension.erase(extension.begin()); // Erase preceding '.'. | |
270 | |
271 #if defined(OS_WIN) | |
272 static const FilePath::CharType default_extension[] = | |
273 FILE_PATH_LITERAL("download"); | |
274 | |
275 // Rename shell-integrated extensions. | |
276 if (IsShellIntegratedExtension(extension)) | |
277 extension.assign(default_extension); | |
278 #endif | |
279 | |
280 if (extension.empty()) { | |
281 // The GetPreferredExtensionForMimeType call will end up going to disk. Do | |
282 // this on another thread to avoid slowing the IO thread. | |
283 // http://crbug.com/61827 | |
284 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
285 net::GetPreferredExtensionForMimeType(mime_type, &extension); | |
286 } | |
287 | |
288 generated_extension->swap(extension); | |
289 } | |
290 | |
291 void GenerateFileNameFromRequest(const GURL& url, | 169 void GenerateFileNameFromRequest(const GURL& url, |
292 const std::string& content_disposition, | 170 const std::string& content_disposition, |
293 const std::string& referrer_charset, | 171 const std::string& referrer_charset, |
294 const std::string& mime_type, | 172 const std::string& mime_type, |
295 FilePath* generated_name) { | 173 FilePath* generated_name) { |
296 GenerateFileNameInternal(url, | 174 GenerateFileNameInternal(url, |
297 content_disposition, | 175 content_disposition, |
298 referrer_charset, | 176 referrer_charset, |
299 std::string(), | 177 std::string(), |
300 mime_type, | 178 mime_type, |
301 generated_name); | 179 generated_name); |
302 } | 180 } |
303 | 181 |
304 void GenerateFileNameFromSuggestedName(const GURL& url, | 182 void GenerateFileNameFromSuggestedName(const GURL& url, |
305 const std::string& suggested_name, | 183 const std::string& suggested_name, |
306 const std::string& mime_type, | 184 const std::string& mime_type, |
307 FilePath* generated_name) { | 185 FilePath* generated_name) { |
| 186 // TODO(asanka): We should pass in a valid referrer_charset here. |
308 GenerateFileNameInternal(url, std::string(), std::string(), | 187 GenerateFileNameInternal(url, std::string(), std::string(), |
309 suggested_name, mime_type, generated_name); | 188 suggested_name, mime_type, generated_name); |
310 } | 189 } |
311 | 190 |
312 void GenerateFileName(const GURL& url, | |
313 const std::string& content_disposition, | |
314 const std::string& referrer_charset, | |
315 const std::string& mime_type, | |
316 FilePath* generated_name) { | |
317 GenerateFileNameInternal(url, content_disposition, referrer_charset, | |
318 std::string(), mime_type, generated_name); | |
319 } | |
320 | |
321 void GenerateSafeFileName(const std::string& mime_type, FilePath* file_name) { | |
322 // Make sure we get the right file extension | |
323 FilePath::StringType extension; | |
324 GenerateExtension(*file_name, mime_type, &extension); | |
325 *file_name = file_name->ReplaceExtension(extension); | |
326 | |
327 #if defined(OS_WIN) | |
328 // Prepend "_" to the file name if it's a reserved name | |
329 FilePath::StringType leaf_name = file_name->BaseName().value(); | |
330 DCHECK(!leaf_name.empty()); | |
331 if (IsReservedName(leaf_name)) { | |
332 leaf_name = FilePath::StringType(FILE_PATH_LITERAL("_")) + leaf_name; | |
333 *file_name = file_name->DirName(); | |
334 if (file_name->value() == FilePath::kCurrentDirectory) { | |
335 *file_name = FilePath(leaf_name); | |
336 } else { | |
337 *file_name = file_name->Append(leaf_name); | |
338 } | |
339 } | |
340 #endif | |
341 } | |
342 | |
343 void RecordDownloadCount(DownloadCountTypes type) { | 191 void RecordDownloadCount(DownloadCountTypes type) { |
344 UMA_HISTOGRAM_ENUMERATION( | 192 UMA_HISTOGRAM_ENUMERATION( |
345 "Download.Counts", type, DOWNLOAD_COUNT_TYPES_LAST_ENTRY); | 193 "Download.Counts", type, DOWNLOAD_COUNT_TYPES_LAST_ENTRY); |
346 } | 194 } |
347 | 195 |
348 void RecordDownloadCompleted(const base::TimeTicks& start) { | 196 void RecordDownloadCompleted(const base::TimeTicks& start) { |
349 download_util::RecordDownloadCount(download_util::COMPLETED_COUNT); | 197 download_util::RecordDownloadCount(download_util::COMPLETED_COUNT); |
350 UMA_HISTOGRAM_LONG_TIMES("Download.Time", (base::TimeTicks::Now() - start)); | 198 UMA_HISTOGRAM_LONG_TIMES("Download.Time", (base::TimeTicks::Now() - start)); |
351 } | 199 } |
352 | 200 |
(...skipping 528 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
881 AppendNumberToPath(&new_path, count); | 729 AppendNumberToPath(&new_path, count); |
882 | 730 |
883 if (!file_util::PathExists(new_path) && | 731 if (!file_util::PathExists(new_path) && |
884 !file_util::PathExists(GetCrDownloadPath(new_path))) | 732 !file_util::PathExists(GetCrDownloadPath(new_path))) |
885 return count; | 733 return count; |
886 } | 734 } |
887 | 735 |
888 return -1; | 736 return -1; |
889 } | 737 } |
890 | 738 |
891 namespace { | |
892 | |
893 // NOTE: If index is 0, deletes files that do not have the " (nnn)" appended. | |
894 void DeleteUniqueDownloadFile(const FilePath& path, int index) { | |
895 FilePath new_path(path); | |
896 if (index > 0) | |
897 AppendNumberToPath(&new_path, index); | |
898 file_util::Delete(new_path, false); | |
899 } | |
900 | |
901 } // namespace | |
902 | |
903 void EraseUniqueDownloadFiles(const FilePath& path) { | |
904 FilePath cr_path = GetCrDownloadPath(path); | |
905 | |
906 for (int index = 0; index <= kMaxUniqueFiles; ++index) { | |
907 DeleteUniqueDownloadFile(path, index); | |
908 DeleteUniqueDownloadFile(cr_path, index); | |
909 } | |
910 } | |
911 | |
912 FilePath GetCrDownloadPath(const FilePath& suggested_path) { | 739 FilePath GetCrDownloadPath(const FilePath& suggested_path) { |
913 FilePath::StringType file_name; | 740 FilePath::StringType file_name; |
914 base::SStringPrintf( | 741 base::SStringPrintf( |
915 &file_name, | 742 &file_name, |
916 PRFilePathLiteral FILE_PATH_LITERAL(".crdownload"), | 743 PRFilePathLiteral FILE_PATH_LITERAL(".crdownload"), |
917 suggested_path.value().c_str()); | 744 suggested_path.value().c_str()); |
918 return FilePath(file_name); | 745 return FilePath(file_name); |
919 } | 746 } |
920 | 747 |
921 } // namespace download_util | 748 } // namespace download_util |
OLD | NEW |