| 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 DownloadItem& download_item, | 169 void GenerateFileNameFromRequest(const DownloadItem& download_item, |
| 292 FilePath* generated_name) { | 170 FilePath* generated_name) { |
| 293 GenerateFileNameInternal(download_item.GetURL(), | 171 GenerateFileNameInternal(download_item.GetURL(), |
| 294 download_item.content_disposition(), | 172 download_item.content_disposition(), |
| 295 download_item.referrer_charset(), | 173 download_item.referrer_charset(), |
| 296 download_item.suggested_filename(), | 174 download_item.suggested_filename(), |
| 297 download_item.mime_type(), | 175 download_item.mime_type(), |
| 298 generated_name); | 176 generated_name); |
| 299 } | 177 } |
| 300 | 178 |
| 301 void GenerateFileNameFromSuggestedName(const GURL& url, | 179 void GenerateFileNameFromSuggestedName(const GURL& url, |
| 302 const std::string& suggested_name, | 180 const std::string& suggested_name, |
| 303 const std::string& mime_type, | 181 const std::string& mime_type, |
| 304 FilePath* generated_name) { | 182 FilePath* generated_name) { |
| 183 // TODO(asanka): We should pass in a valid referrer_charset here. |
| 305 GenerateFileNameInternal(url, std::string(), std::string(), | 184 GenerateFileNameInternal(url, std::string(), std::string(), |
| 306 suggested_name, mime_type, generated_name); | 185 suggested_name, mime_type, generated_name); |
| 307 } | 186 } |
| 308 | 187 |
| 309 void GenerateFileName(const GURL& url, | |
| 310 const std::string& content_disposition, | |
| 311 const std::string& referrer_charset, | |
| 312 const std::string& mime_type, | |
| 313 FilePath* generated_name) { | |
| 314 GenerateFileNameInternal(url, content_disposition, referrer_charset, | |
| 315 std::string(), mime_type, generated_name); | |
| 316 } | |
| 317 | |
| 318 void GenerateSafeFileName(const std::string& mime_type, FilePath* file_name) { | |
| 319 // Make sure we get the right file extension | |
| 320 FilePath::StringType extension; | |
| 321 GenerateExtension(*file_name, mime_type, &extension); | |
| 322 *file_name = file_name->ReplaceExtension(extension); | |
| 323 | |
| 324 #if defined(OS_WIN) | |
| 325 // Prepend "_" to the file name if it's a reserved name | |
| 326 FilePath::StringType leaf_name = file_name->BaseName().value(); | |
| 327 DCHECK(!leaf_name.empty()); | |
| 328 if (IsReservedName(leaf_name)) { | |
| 329 leaf_name = FilePath::StringType(FILE_PATH_LITERAL("_")) + leaf_name; | |
| 330 *file_name = file_name->DirName(); | |
| 331 if (file_name->value() == FilePath::kCurrentDirectory) { | |
| 332 *file_name = FilePath(leaf_name); | |
| 333 } else { | |
| 334 *file_name = file_name->Append(leaf_name); | |
| 335 } | |
| 336 } | |
| 337 #endif | |
| 338 } | |
| 339 | |
| 340 void RecordDownloadCount(DownloadCountTypes type) { | 188 void RecordDownloadCount(DownloadCountTypes type) { |
| 341 UMA_HISTOGRAM_ENUMERATION( | 189 UMA_HISTOGRAM_ENUMERATION( |
| 342 "Download.Counts", type, DOWNLOAD_COUNT_TYPES_LAST_ENTRY); | 190 "Download.Counts", type, DOWNLOAD_COUNT_TYPES_LAST_ENTRY); |
| 343 } | 191 } |
| 344 | 192 |
| 345 void RecordDownloadCompleted(const base::TimeTicks& start) { | 193 void RecordDownloadCompleted(const base::TimeTicks& start) { |
| 346 download_util::RecordDownloadCount(download_util::COMPLETED_COUNT); | 194 download_util::RecordDownloadCount(download_util::COMPLETED_COUNT); |
| 347 UMA_HISTOGRAM_LONG_TIMES("Download.Time", (base::TimeTicks::Now() - start)); | 195 UMA_HISTOGRAM_LONG_TIMES("Download.Time", (base::TimeTicks::Now() - start)); |
| 348 } | 196 } |
| 349 | 197 |
| (...skipping 604 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 954 AppendNumberToPath(&new_path, count); | 802 AppendNumberToPath(&new_path, count); |
| 955 | 803 |
| 956 if (!file_util::PathExists(new_path) && | 804 if (!file_util::PathExists(new_path) && |
| 957 !file_util::PathExists(GetCrDownloadPath(new_path))) | 805 !file_util::PathExists(GetCrDownloadPath(new_path))) |
| 958 return count; | 806 return count; |
| 959 } | 807 } |
| 960 | 808 |
| 961 return -1; | 809 return -1; |
| 962 } | 810 } |
| 963 | 811 |
| 964 namespace { | |
| 965 | |
| 966 // NOTE: If index is 0, deletes files that do not have the " (nnn)" appended. | |
| 967 void DeleteUniqueDownloadFile(const FilePath& path, int index) { | |
| 968 FilePath new_path(path); | |
| 969 if (index > 0) | |
| 970 AppendNumberToPath(&new_path, index); | |
| 971 file_util::Delete(new_path, false); | |
| 972 } | |
| 973 | |
| 974 } // namespace | |
| 975 | |
| 976 void EraseUniqueDownloadFiles(const FilePath& path) { | |
| 977 FilePath cr_path = GetCrDownloadPath(path); | |
| 978 | |
| 979 for (int index = 0; index <= kMaxUniqueFiles; ++index) { | |
| 980 DeleteUniqueDownloadFile(path, index); | |
| 981 DeleteUniqueDownloadFile(cr_path, index); | |
| 982 } | |
| 983 } | |
| 984 | |
| 985 FilePath GetCrDownloadPath(const FilePath& suggested_path) { | 812 FilePath GetCrDownloadPath(const FilePath& suggested_path) { |
| 986 FilePath::StringType file_name; | 813 FilePath::StringType file_name; |
| 987 base::SStringPrintf( | 814 base::SStringPrintf( |
| 988 &file_name, | 815 &file_name, |
| 989 PRFilePathLiteral FILE_PATH_LITERAL(".crdownload"), | 816 PRFilePathLiteral FILE_PATH_LITERAL(".crdownload"), |
| 990 suggested_path.value().c_str()); | 817 suggested_path.value().c_str()); |
| 991 return FilePath(file_name); | 818 return FilePath(file_name); |
| 992 } | 819 } |
| 993 | 820 |
| 994 } // namespace download_util | 821 } // namespace download_util |
| OLD | NEW |