#include "net/base/filename_util.h"
#include <set>
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "net/base/filename_util_internal.h"
#include "net/base/net_string_util.h"
#include "net/base/url_util.h"
#include "net/http/http_content_disposition.h"
#include "url/gurl.h"
namespace net {
static const char kFileURLPrefix[] = "file:///";
GURL FilePathToFileURL(const base::FilePath& path) {
#if BUILDFLAG(ARKWEB_ARKWEB_EXTENSIONS)
if (path.IsDataShareUri()) {
return GURL(path.value());
}
#endif
std::string url_string(kFileURLPrefix);
std::string utf8_path = path.AsUTF8Unsafe();
url_string.reserve(url_string.size() + (3 * utf8_path.size()));
for (auto c : utf8_path) {
if (c == '%' || c == ';' || c == '#' || c == '?' ||
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
c == '\\' ||
#endif
c <= ' ') {
url_string += '%';
base::AppendHexEncodedByte(static_cast<uint8_t>(c), url_string);
} else {
url_string += c;
}
}
return GURL(url_string);
}
bool FileURLToFilePath(const GURL& url, base::FilePath* file_path) {
*file_path = base::FilePath();
base::FilePath::StringType& file_path_str =
const_cast<base::FilePath::StringType&>(file_path->value());
file_path_str.clear();
#if BUILDFLAG(ARKWEB_ARKWEB_EXTENSIONS)
if (base::FilePath::IsDataShareUrl(url.possibly_invalid_spec())) {
file_path_str.assign(url.possibly_invalid_spec());
return !file_path_str.empty();
}
#endif
if (!url.is_valid())
return false;
if (!url.SchemeIsFile())
return false;
#if BUILDFLAG(IS_WIN)
std::string path;
std::string host = url.GetHost();
if (host.empty()) {
path = url.GetPath();
size_t first_non_slash = path.find_first_not_of("/\\");
if (first_non_slash != std::string::npos && first_non_slash > 0)
path.erase(0, first_non_slash);
} else {
path = "\\\\";
path.append(host);
path.append(url.GetPath());
}
std::replace(path.begin(), path.end(), '/', '\\');
#else
if (!url.GetHost().empty() && !net::IsLocalhost(url)) {
return false;
}
std::string path = url.GetPath();
#endif
if (path.empty())
return false;
std::set<unsigned char> illegal_encoded_bytes{'/', '\0'};
#if BUILDFLAG(IS_WIN)
illegal_encoded_bytes.insert('\\');
#endif
if (base::ContainsEncodedBytes(path, illegal_encoded_bytes))
return false;
path = base::UnescapeBinaryURLComponent(path);
#if BUILDFLAG(IS_WIN)
if (base::IsStringUTF8(path)) {
file_path_str.assign(base::UTF8ToWide(path));
} else {
file_path_str = base::SysNativeMBToWide(path);
}
#else
std::string new_path;
do {
new_path = path;
base::ReplaceSubstringsAfterOffset(&new_path, 0, "//", "/");
path.swap(new_path);
} while (new_path != path);
file_path_str.assign(path);
#endif
return !file_path_str.empty();
}
void GenerateSafeFileName(const std::string& mime_type,
bool ignore_extension,
base::FilePath* file_path) {
EnsureSafeExtension(mime_type, ignore_extension, file_path);
#if BUILDFLAG(IS_WIN)
base::FilePath::StringType leaf_name = file_path->BaseName().value();
DCHECK(!leaf_name.empty());
if (IsReservedNameOnWindows(leaf_name)) {
leaf_name = base::FilePath::StringType(FILE_PATH_LITERAL("_")) + leaf_name;
*file_path = file_path->DirName();
if (file_path->value() == base::FilePath::kCurrentDirectory) {
*file_path = base::FilePath(leaf_name);
} else {
*file_path = file_path->Append(leaf_name);
}
}
#endif
}
bool IsReservedNameOnWindows(const base::FilePath::StringType& filename) {
static const char* const known_devices[] = {
"con", "prn", "aux", "nul", "com1", "com2", "com3", "com4",
"com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3",
"lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "clock$"};
#if BUILDFLAG(IS_WIN)
std::string filename_lower = base::ToLowerASCII(base::WideToUTF8(filename));
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
std::string filename_lower = base::ToLowerASCII(filename);
#endif
for (const char* const device : known_devices) {
size_t len = strlen(device);
if (filename_lower.starts_with(device) &&
(filename_lower.size() == len || filename_lower[len] == '.')) {
return true;
}
}
static const char* const magic_names[] = {
"desktop.ini",
"thumbs.db",
};
for (const char* const magic_name : magic_names) {
if (filename_lower == magic_name)
return true;
}
return false;
}
}