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

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: Comments Created 9 years, 5 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 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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698