Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(285)

Side by Side Diff: chrome/browser/download/download_util.cc

Issue 7300005: Move filename determination to net_util (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: " Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
OLDNEW
« no previous file with comments | « chrome/browser/download/download_util.h ('k') | chrome/browser/download/download_util_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698