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

Side by Side Diff: net/base/filename_util_internal.cc

Issue 869233006: [net] Cleanup filename_util and make it portable. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@clang-format-filename-tests
Patch Set: Created 5 years, 10 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
« no previous file with comments | « net/base/filename_util_internal.h ('k') | net/base/filename_util_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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 "net/base/filename_util.h" 5 #include "net/base/filename_util.h"
6 6
7 #include "base/files/file_path.h" 7 #include "base/files/file_path.h"
8 #include "base/files/file_util.h" 8 #include "base/files/file_util.h"
9 #include "base/strings/string_util.h" 9 #include "base/strings/string_util.h"
10 #include "base/strings/sys_string_conversions.h" 10 #include "base/strings/sys_string_conversions.h"
11 #include "base/strings/utf_string_conversions.h" 11 #include "base/strings/utf_string_conversions.h"
12 #include "base/threading/thread_restrictions.h" 12 #include "base/threading/thread_restrictions.h"
13 #include "net/base/escape.h" 13 #include "net/base/escape.h"
14 #include "net/base/filename_util_internal.h" 14 #include "net/base/filename_util_internal.h"
15 #include "net/base/mime_util.h" 15 #include "net/base/mime_util.h"
16 #include "net/base/net_string_util.h" 16 #include "net/base/net_string_util.h"
17 #include "net/base/net_util.h"
17 #include "net/http/http_content_disposition.h" 18 #include "net/http/http_content_disposition.h"
18 #include "url/gurl.h" 19 #include "url/gurl.h"
19 20
20 namespace net { 21 namespace net {
21 22
22 void SanitizeGeneratedFileName(base::FilePath::StringType* filename, 23 // Our replacement character is an underscore. It used to be a '-', but that's
23 bool replace_trailing) { 24 // problematic since if we replace a character at the beginning of a string, the
24 const base::FilePath::CharType kReplace[] = FILE_PATH_LITERAL("-"); 25 // resulting filename could look like a command line option when used on the
25 if (filename->empty()) 26 // command line on Posix systems.
26 return; 27 const char kReplacement = '_';
27 if (replace_trailing) {
28 // Handle CreateFile() stripping trailing dots and spaces on filenames
29 // http://support.microsoft.com/kb/115827
30 size_t length = filename->size();
31 size_t pos = filename->find_last_not_of(FILE_PATH_LITERAL(" ."));
32 filename->resize((pos == std::string::npos) ? 0 : (pos + 1));
33 base::TrimWhitespace(*filename, base::TRIM_TRAILING, filename);
34 if (filename->empty())
35 return;
36 size_t trimmed = length - filename->size();
37 if (trimmed)
38 filename->insert(filename->end(), trimmed, kReplace[0]);
39 }
40 base::TrimString(*filename, FILE_PATH_LITERAL("."), filename);
41 if (filename->empty())
42 return;
43 // Replace any path information by changing path separators.
44 ReplaceSubstringsAfterOffset(filename, 0, FILE_PATH_LITERAL("/"), kReplace);
45 ReplaceSubstringsAfterOffset(filename, 0, FILE_PATH_LITERAL("\\"), kReplace);
46 }
47 28
48 // Returns the filename determined from the last component of the path portion 29 namespace {
49 // of the URL. Returns an empty string if the URL doesn't have a path or is
50 // invalid. If the generated filename is not reliable,
51 // |should_overwrite_extension| will be set to true, in which case a better
52 // extension should be determined based on the content type.
53 std::string GetFileNameFromURL(const GURL& url,
54 const std::string& referrer_charset,
55 bool* should_overwrite_extension) {
56 // about: and data: URLs don't have file names, but esp. data: URLs may
57 // contain parts that look like ones (i.e., contain a slash). Therefore we
58 // don't attempt to divine a file name out of them.
59 if (!url.is_valid() || url.SchemeIs("about") || url.SchemeIs("data"))
60 return std::string();
61 30
62 const std::string unescaped_url_filename = UnescapeURLComponent( 31 const base::FilePath::CharType kDefaultExtension[] =
63 url.ExtractFileName(), 32 FILE_PATH_LITERAL("download");
64 UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS);
65
66 // The URL's path should be escaped UTF-8, but may not be.
67 std::string decoded_filename = unescaped_url_filename;
68 if (!base::IsStringUTF8(decoded_filename)) {
69 // TODO(jshin): this is probably not robust enough. To be sure, we need
70 // encoding detection.
71 base::string16 utf16_output;
72 if (!referrer_charset.empty() &&
73 net::ConvertToUTF16(
74 unescaped_url_filename, referrer_charset.c_str(), &utf16_output)) {
75 decoded_filename = base::UTF16ToUTF8(utf16_output);
76 } else {
77 decoded_filename =
78 base::WideToUTF8(base::SysNativeMBToWide(unescaped_url_filename));
79 }
80 }
81 // If the URL contains a (possibly empty) query, assume it is a generator, and
82 // allow the determined extension to be overwritten.
83 *should_overwrite_extension = !decoded_filename.empty() && url.has_query();
84
85 return decoded_filename;
86 }
87 33
88 // Returns whether the specified extension is automatically integrated into the 34 // Returns whether the specified extension is automatically integrated into the
89 // windows shell. 35 // windows shell.
90 bool IsShellIntegratedExtension(const base::FilePath::StringType& extension) { 36 bool IsShellIntegratedExtension(const base::FilePath::StringType& extension) {
91 base::FilePath::StringType extension_lower = 37 base::FilePath::StringType extension_lower =
92 base::StringToLowerASCII(extension); 38 base::StringToLowerASCII(extension);
93 39
94 // http://msdn.microsoft.com/en-us/library/ms811694.aspx 40 // http://msdn.microsoft.com/en-us/library/ms811694.aspx
95 // Right-clicking on shortcuts can be magical. 41 // Right-clicking on shortcuts can be magical.
96 if ((extension_lower == FILE_PATH_LITERAL("local")) || 42 if ((extension_lower == FILE_PATH_LITERAL("local")) ||
97 (extension_lower == FILE_PATH_LITERAL("lnk"))) 43 (extension_lower == FILE_PATH_LITERAL("lnk")))
98 return true; 44 return true;
99 45
100 // http://www.juniper.net/security/auto/vulnerabilities/vuln2612.html 46 // http://www.juniper.net/security/auto/vulnerabilities/vuln2612.html
101 // Files become magical if they end in a CLSID, so block such extensions. 47 // Files become magical if they end in a CLSID, so block such extensions.
102 if (!extension_lower.empty() && 48 if (!extension_lower.empty() &&
103 (extension_lower[0] == FILE_PATH_LITERAL('{')) && 49 (extension_lower[0] == FILE_PATH_LITERAL('{')) &&
104 (extension_lower[extension_lower.length() - 1] == FILE_PATH_LITERAL('}'))) 50 (extension_lower[extension_lower.length() - 1] == FILE_PATH_LITERAL('}')))
105 return true; 51 return true;
106 return false; 52 return false;
107 } 53 }
108 54
109 // Returns whether the specified file name is a reserved name on windows. 55 // Returns whether the specified file name is a reserved name on Windows.
110 // This includes names like "com2.zip" (which correspond to devices) and 56 // This includes names like "com2.zip" (which correspond to devices) and
111 // desktop.ini and thumbs.db which have special meaning to the windows shell. 57 // desktop.ini and thumbs.db which have special meaning to the windows shell.
112 bool IsReservedName(const base::FilePath::StringType& filename) { 58 bool IsReservedName(const base::FilePath::StringType& filename) {
113 // This list is taken from the MSDN article "Naming a file" 59 // This list is taken from the MSDN article "Naming a file"
114 // http://msdn2.microsoft.com/en-us/library/aa365247(VS.85).aspx 60 // http://msdn2.microsoft.com/en-us/library/aa365247(VS.85).aspx
115 // I also added clock$ because GetSaveFileName seems to consider it as a 61 // I also added clock$ because GetSaveFileName seems to consider it as a
116 // reserved name too. 62 // reserved name too.
117 static const char* const known_devices[] = { 63 static const char* const kKnownDeviceNames[] = {
118 "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4", 64 "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4",
119 "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", 65 "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3",
120 "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "clock$"}; 66 "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "clock$"};
121 #if defined(OS_WIN) 67 #if defined(OS_WIN)
122 std::string filename_lower = 68 std::string filename_lower =
123 base::StringToLowerASCII(base::WideToUTF8(filename)); 69 base::StringToLowerASCII(base::WideToUTF8(filename));
124 #elif defined(OS_POSIX) 70 #elif defined(OS_POSIX)
125 std::string filename_lower = base::StringToLowerASCII(filename); 71 std::string filename_lower = base::StringToLowerASCII(filename);
126 #endif 72 #endif
127 73
128 for (size_t i = 0; i < arraysize(known_devices); ++i) { 74 for (const auto& device_name : kKnownDeviceNames) {
129 // Exact match. 75 // Exact match.
130 if (filename_lower == known_devices[i]) 76 if (filename_lower == device_name)
131 return true; 77 return true;
132 // Starts with "DEVICE.". 78 // Starts with "DEVICE.".
133 if (filename_lower.find(std::string(known_devices[i]) + ".") == 0) 79 if (filename_lower.find(std::string(device_name) + ".") == 0)
134 return true; 80 return true;
135 } 81 }
136 82
137 static const char* const magic_names[] = { 83 static const char* const kMagicNames[] = {
138 // These file names are used by the "Customize folder" feature of the shell. 84 // These file names are used by the "Customize folder" feature of the
139 "desktop.ini", 85 // shell.
140 "thumbs.db", 86 "desktop.ini",
87 "thumbs.db",
141 }; 88 };
142 89
143 for (size_t i = 0; i < arraysize(magic_names); ++i) { 90 for (const auto& magic_name : kMagicNames) {
144 if (filename_lower == magic_names[i]) 91 if (filename_lower == magic_name)
145 return true; 92 return true;
146 } 93 }
147 94
148 return false; 95 return false;
149 } 96 }
150 97
151 // Examines the current extension in |file_name| and modifies it if necessary in 98 // Returns the UTF-8 filename determined from the last component of the path
152 // order to ensure the filename is safe. If |file_name| doesn't contain an 99 // portion of the URL. Returns an empty string if the URL doesn't have a path
153 // extension or if |ignore_extension| is true, then a new extension will be 100 // or is invalid. Updates |extension_option| if a filename can be extracted from
154 // constructed based on the |mime_type|. 101 // the URL.
155 // 102 std::string GetFileNameFromURL(const GURL& url,
156 // We're addressing two things here: 103 const std::string& referrer_charset,
157 // 104 ExtensionGenerationOption* extension_option) {
158 // 1) Usability. If there is no reliable file extension, we want to guess a 105 // about: and data: URLs don't have file names, but esp. data: URLs may
159 // reasonable file extension based on the content type. 106 // contain parts that look like ones (i.e., contain a slash). Therefore we
160 // 107 // don't attempt to divine a file name out of them.
161 // 2) Shell integration. Some file extensions automatically integrate with the 108 if (!url.is_valid() || url.SchemeIs("about") || url.SchemeIs("data"))
162 // shell. We block these extensions to prevent a malicious web site from 109 return std::string();
163 // integrating with the user's shell. 110
164 void EnsureSafeExtension(const std::string& mime_type, 111 const std::string unescaped_url_filename = UnescapeURLComponent(
165 bool ignore_extension, 112 url.ExtractFileName(),
166 base::FilePath* file_name) { 113 UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS);
167 // See if our file name already contains an extension. 114
168 base::FilePath::StringType extension = file_name->Extension(); 115 // The URL's path should be escaped UTF-8, but may not be.
169 if (!extension.empty()) 116 std::string decoded_filename = unescaped_url_filename;
170 extension.erase(extension.begin()); // Erase preceding '.'. 117 if (!base::IsStringUTF8(decoded_filename)) {
171 118 // TODO(jshin): this is probably not robust enough. To be sure, we need
172 if ((ignore_extension || extension.empty()) && !mime_type.empty()) { 119 // encoding detection.
173 base::FilePath::StringType preferred_mime_extension; 120 base::string16 utf16_output;
174 std::vector<base::FilePath::StringType> all_mime_extensions; 121 if (!referrer_charset.empty() &&
175 net::GetPreferredExtensionForMimeType(mime_type, &preferred_mime_extension); 122 net::ConvertToUTF16(unescaped_url_filename, referrer_charset.c_str(),
176 net::GetExtensionsForMimeType(mime_type, &all_mime_extensions); 123 &utf16_output)) {
177 // If the existing extension is in the list of valid extensions for the 124 decoded_filename = base::UTF16ToUTF8(utf16_output);
178 // given type, use it. This avoids doing things like pointlessly renaming 125 } else {
179 // "foo.jpg" to "foo.jpeg". 126 decoded_filename =
180 if (std::find(all_mime_extensions.begin(), 127 base::WideToUTF8(base::SysNativeMBToWide(unescaped_url_filename));
181 all_mime_extensions.end(),
182 extension) != all_mime_extensions.end()) {
183 // leave |extension| alone
184 } else if (!preferred_mime_extension.empty()) {
185 extension = preferred_mime_extension;
186 } 128 }
187 } 129 }
188 130 // If the URL contains a (possibly empty) query, assume it is a generator, and
189 #if defined(OS_WIN) 131 // allow the determined extension to be overwritten.
190 static const base::FilePath::CharType default_extension[] = 132 if (!decoded_filename.empty()) {
191 FILE_PATH_LITERAL("download"); 133 *extension_option =
192 134 (url.has_query() ? EXTENSION_OPTION_GENERATE
193 // Rename shell-integrated extensions. 135 : EXTENSION_OPTION_GENERATE_IF_MISSING);
194 // TODO(asanka): Consider stripping out the bad extension and replacing it 136 }
195 // with the preferred extension for the MIME type if one is available. 137
196 if (IsShellIntegratedExtension(extension)) 138 return decoded_filename;
197 extension.assign(default_extension); 139 }
198 #endif 140
199 141 } // namespace
200 *file_name = file_name->ReplaceExtension(extension); 142
201 } 143 // This section intends to implement the algorithm suggested in:
202 144 // https://html.spec.whatwg.org/multipage/semantics.html#as-a-download
203 bool FilePathToString16(const base::FilePath& path, base::string16* converted) { 145 // Any deviations from the spec are explicitly mentioned. The returned filename
204 #if defined(OS_WIN) 146 // is UTF-8 encoded.
205 *converted = path.value(); 147 //
206 return true; 148 // The function is named *Unsafe because the returned name isn't sanitized and
207 #elif defined(OS_POSIX) 149 // may contain illegal or malicious content. It should always be passed through
208 std::string component8 = path.AsUTF8Unsafe(); 150 // EnsureSafePortableFileName() or EnsureSafeFilenameInternal().
209 return !component8.empty() && 151 //
210 base::UTF8ToUTF16(component8.c_str(), component8.size(), converted); 152 // TODO(asanka): Resolve deviations from the spec. The current deviations are
211 #endif 153 // due to document origin and download attribute suggested name not being
212 } 154 // reliably available at this abstraction layer. This should ideally be moved to
213 155 // /content/.
214 base::string16 GetSuggestedFilenameImpl( 156 std::string SelectUnsafeDownloadFilename(
215 const GURL& url, 157 const GURL& url,
216 const std::string& content_disposition, 158 const std::string& content_disposition,
217 const std::string& referrer_charset, 159 const std::string& referrer_charset,
218 const std::string& suggested_name, 160 const std::string& suggested_name,
219 const std::string& mime_type, 161 const std::string& mime_type,
220 const std::string& default_name, 162 const std::string& default_name,
221 ReplaceIllegalCharactersCallback replace_illegal_characters_callback) { 163 ExtensionGenerationOption* extension_option) {
222 // TODO: this function to be updated to match the httpbis recommendations. 164 *extension_option = EXTENSION_OPTION_KEEP_EXISTING;
223 // Talk to abarth for the latest news. 165 // Content-Disposition comes first:
224 166 //
225 // We don't translate this fallback string, "download". If localization is 167 // Deviates from spec: The filename should only be used if the disposition
226 // needed, the caller should provide localized fallback in |default_name|. 168 // type is "attachment" or if this is a trusted download as defined in section
227 static const base::FilePath::CharType kFinalFallbackName[] = 169 // 4.6.4 of the HTML specification linked above. Currently whether this is a
228 FILE_PATH_LITERAL("download"); 170 // trusted download is not known.
229 std::string filename; // In UTF-8
230 bool overwrite_extension = false;
231 bool is_name_from_content_disposition = false;
232 // Try to extract a filename from content-disposition first.
233 if (!content_disposition.empty()) { 171 if (!content_disposition.empty()) {
234 HttpContentDisposition header(content_disposition, referrer_charset); 172 HttpContentDisposition header(content_disposition, referrer_charset);
235 filename = header.filename(); 173 if (!header.filename().empty())
236 if (!filename.empty()) 174 return header.filename();
237 is_name_from_content_disposition = true; 175 }
238 } 176
239 177 // suggested_name is assumed to be the suggested filename from the 'download'
240 // Then try to use the suggested name. 178 // attribute of an anchor element. It should only be non-empty if the download
241 if (filename.empty() && !suggested_name.empty()) 179 // URL is same-origin with the interface.
242 filename = suggested_name; 180 //
243 181 // Deviates from spec: The filename suggested with a download attribute should
244 // Now try extracting the filename from the URL. GetFileNameFromURL() only 182 // also be used if the Content-Disposition specified a disposition of
183 // 'attachment' but didn't specify a filename. Currently this step can't be
184 // implemented due to suggested_name being empty if the download is not
185 // same-origin with the document.
186 if (!suggested_name.empty())
187 return suggested_name;
188
189 // Now try extracting the filename from the URL. GetFileNameFromURL() only
245 // looks at the last component of the URL and doesn't return the hostname as a 190 // looks at the last component of the URL and doesn't return the hostname as a
246 // failover. 191 // failover.
247 if (filename.empty()) 192 //
248 filename = GetFileNameFromURL(url, referrer_charset, &overwrite_extension); 193 // Deviates from spec: Extraction of a filename from the URL should only be
194 // done if the download is trusted.
195 std::string filename =
196 GetFileNameFromURL(url, referrer_charset, extension_option);
197 if (!filename.empty())
198 return filename;
199
200 // If non-empty, we can use the default before trying to extract a filename
201 // from the hostname. The latter is a last resort and is almost never what the
202 // user wanted.
203 if (!default_name.empty()) {
204 *extension_option = EXTENSION_OPTION_GENERATE_IF_MISSING;
205 return default_name;
206 }
249 207
250 // Finally try the URL hostname, but only if there's no default specified in 208 // Finally try the URL hostname, but only if there's no default specified in
251 // |default_name|. Some schemes (e.g.: file:, about:, data:) do not have a 209 // |default_name|. Some schemes (e.g.: file:, about:, data:) do not have a
252 // host name. 210 // host name.
253 if (filename.empty() && default_name.empty() && url.is_valid() && 211 if (url.is_valid() && !url.host().empty()) {
254 !url.host().empty()) { 212 *extension_option = EXTENSION_OPTION_GENERATE_AND_APPEND;
255 // TODO(jungshik) : Decode a 'punycoded' IDN hostname. (bug 1264451) 213 base::string16 filename_string16 = IDNToUnicode(url.host(), std::string());
256 filename = url.host(); 214 return UTF16ToUTF8(filename_string16);
257 } 215 }
258 216
259 bool replace_trailing = false; 217 return std::string();
260 base::FilePath::StringType result_str, default_name_str; 218 }
261 #if defined(OS_WIN) 219
262 replace_trailing = true; 220 // Examines |file_name| and modifies it if necessary in order to ensure the
263 result_str = base::UTF8ToUTF16(filename); 221 // filename is safe. A new extension will be generated if |extension_option|
264 default_name_str = base::UTF8ToUTF16(default_name); 222 // suggests so and |mime_type| is known.
265 #else 223 //
266 result_str = filename; 224 // We're addressing three things here:
267 default_name_str = default_name; 225 //
268 #endif 226 // 1) Usability. If there is no reliable file extension, we want to guess a
269 SanitizeGeneratedFileName(&result_str, replace_trailing); 227 // reasonable file extension based on the content type.
270 if (result_str.find_last_not_of(FILE_PATH_LITERAL("-_")) == 228 //
271 base::FilePath::StringType::npos) { 229 // 2) Shell integration. Some file extensions automatically integrate with the
272 result_str = !default_name_str.empty() 230 // shell. We block these extensions to prevent a malicious web site from
273 ? default_name_str 231 // integrating with the user's shell.
274 : base::FilePath::StringType(kFinalFallbackName); 232 //
275 overwrite_extension = false; 233 // 3) Reserved names. If the resulting filename is considered reserved, then the
276 } 234 // replacement character will be inserted in front so that the file behaves
277 replace_illegal_characters_callback.Run(&result_str, '-'); 235 // as a regualr file.
278 base::FilePath result(result_str); 236 //
279 // extension should not appended to filename derived from 237 // Note that this function does NOT replace illegal characters in the filename.
280 // content-disposition, if it does not have one. 238 // That should be done prior to calling this function.
281 // Hence mimetype and overwrite_extension values are not used. 239 void EnsureSafeFilenameInternal(const std::string& mime_type,
282 if (is_name_from_content_disposition) 240 ExtensionGenerationOption extension_option,
283 GenerateSafeFileName("", false, &result); 241 base::FilePath* file_name) {
284 else 242 base::FilePath::StringType extension = file_name->Extension();
285 GenerateSafeFileName(mime_type, overwrite_extension, &result); 243 if (!extension.empty())
286 244 extension.erase(extension.begin()); // Erase preceding '.'.
287 base::string16 result16; 245
288 if (!FilePathToString16(result, &result16)) { 246 switch (extension_option) {
289 result = base::FilePath(default_name_str); 247 case EXTENSION_OPTION_KEEP_EXISTING:
290 if (!FilePathToString16(result, &result16)) { 248 break;
291 result = base::FilePath(kFinalFallbackName); 249
292 FilePathToString16(result, &result16); 250 case EXTENSION_OPTION_GENERATE_IF_MISSING:
293 } 251 if (!extension.empty())
294 } 252 break;
295 return result16; 253 // Fallthrough
296 } 254
297 255 case EXTENSION_OPTION_GENERATE:
298 base::FilePath GenerateFileNameImpl( 256 case EXTENSION_OPTION_GENERATE_AND_APPEND:
299 const GURL& url, 257 if (mime_type.empty())
300 const std::string& content_disposition, 258 break;
301 const std::string& referrer_charset, 259
302 const std::string& suggested_name, 260 std::vector<base::FilePath::StringType> all_extensions;
303 const std::string& mime_type, 261 GetExtensionsForMimeType(mime_type, &all_extensions);
304 const std::string& default_file_name, 262 if (all_extensions.empty())
305 ReplaceIllegalCharactersCallback replace_illegal_characters_callback) { 263 // We don't expect there to be a preferred MIME extension if
306 base::string16 file_name = 264 // all_extensions was empty. The preferred extension is always expected
307 GetSuggestedFilenameImpl(url, 265 // to be among those returned by GetExtensionsForMimeType().
308 content_disposition, 266 break;
309 referrer_charset, 267
310 suggested_name, 268 if (std::find(all_extensions.begin(), all_extensions.end(), extension) !=
311 mime_type, 269 all_extensions.end())
312 default_file_name, 270 break;
313 replace_illegal_characters_callback); 271 base::FilePath::StringType final_extension = file_name->FinalExtension();
314 272 if (!final_extension.empty()) {
315 #if defined(OS_WIN) 273 final_extension.erase(final_extension.begin());
316 base::FilePath generated_name(file_name); 274 if (std::find(all_extensions.begin(), all_extensions.end(),
317 #else 275 final_extension) != all_extensions.end())
318 base::FilePath generated_name( 276 break;
319 base::SysWideToNativeMB(base::UTF16ToWide(file_name))); 277 }
320 #endif 278
321 279 base::FilePath::StringType new_extension;
322 DCHECK(!generated_name.empty()); 280 GetPreferredExtensionForMimeType(mime_type, &new_extension);
323 281
324 return generated_name; 282 // It is possible for there to be no preferred extension. In this case we
283 // leave the extension as-is. For example: application/octet-stream maps
284 // to 'exe','com' and 'bin', but none of these are preferred if there is
285 // no extension.
286 if (new_extension.empty())
287 break;
288
289 if (extension_option == EXTENSION_OPTION_GENERATE_AND_APPEND &&
290 !extension.empty()) {
291 extension.append(1, base::FilePath::kExtensionSeparator);
292 extension.append(new_extension);
293 } else {
294 extension = new_extension;
295 }
296 }
297
298 if (IsShellIntegratedExtension(extension))
299 extension.assign(kDefaultExtension);
300
301 *file_name = file_name->ReplaceExtension(extension);
302
303 if (IsReservedName(file_name->value())) {
304 base::FilePath::StringType new_filename(file_name->value());
305 new_filename.insert(new_filename.begin(), 1, kReplacement);
306 *file_name = base::FilePath(new_filename);
307 }
325 } 308 }
326 309
327 } // namespace net 310 } // namespace net
OLDNEW
« no previous file with comments | « net/base/filename_util_internal.h ('k') | net/base/filename_util_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698