mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-21 07:25:39 -04:00
GameList: Handle non-extension-suffixed urls based on content type
This commit is contained in:
@ -69,7 +69,7 @@ static inline bool FileSystemCharacterIsSane(char32_t c, bool strip_slashes)
|
||||
if (c == '*')
|
||||
return false;
|
||||
|
||||
// macos doesn't allow colons, apparently
|
||||
// macos doesn't allow colons, apparently
|
||||
#ifdef __APPLE__
|
||||
if (c == U':')
|
||||
return false;
|
||||
@ -145,6 +145,37 @@ std::string Path::SanitizeFileName(const std::string_view& str, bool strip_slash
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Path::SanitizeFileName(std::string* str, bool strip_slashes /* = true */)
|
||||
{
|
||||
const size_t len = str->length();
|
||||
|
||||
char small_buf[128];
|
||||
std::unique_ptr<char[]> large_buf;
|
||||
char* str_copy = small_buf;
|
||||
if (len >= std::size(small_buf))
|
||||
{
|
||||
large_buf = std::make_unique<char[]>(len + 1);
|
||||
str_copy = large_buf.get();
|
||||
}
|
||||
std::memcpy(str_copy, str->c_str(), sizeof(char) * (len + 1));
|
||||
str->clear();
|
||||
|
||||
size_t pos = 0;
|
||||
while (pos < len)
|
||||
{
|
||||
char32_t ch;
|
||||
pos += StringUtil::DecodeUTF8(str_copy + pos, pos - len, &ch);
|
||||
ch = FileSystemCharacterIsSane(ch, strip_slashes) ? ch : U'_';
|
||||
StringUtil::EncodeAndAppendUTF8(*str, ch);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows: Can't end filename with a period.
|
||||
if (str->length() > 0 && str->back() == '.')
|
||||
str->back() = '_';
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Path::IsAbsolute(const std::string_view& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "http_downloader.h"
|
||||
#include "assert.h"
|
||||
#include "log.h"
|
||||
#include "string_util.h"
|
||||
#include "timer.h"
|
||||
Log_SetChannel(HTTPDownloader);
|
||||
|
||||
@ -100,7 +101,7 @@ void HTTPDownloader::LockedPollRequests(std::unique_lock<std::mutex>& lock)
|
||||
m_pending_http_requests.erase(m_pending_http_requests.begin() + index);
|
||||
lock.unlock();
|
||||
|
||||
req->callback(-1, Request::Data());
|
||||
req->callback(-1, std::string(), Request::Data());
|
||||
|
||||
CloseRequest(req);
|
||||
|
||||
@ -122,7 +123,7 @@ void HTTPDownloader::LockedPollRequests(std::unique_lock<std::mutex>& lock)
|
||||
|
||||
// run callback with lock unheld
|
||||
lock.unlock();
|
||||
req->callback(req->status_code, req->data);
|
||||
req->callback(req->status_code, std::move(req->content_type), std::move(req->data));
|
||||
CloseRequest(req);
|
||||
lock.lock();
|
||||
}
|
||||
@ -253,4 +254,97 @@ std::string HTTPDownloader::URLDecode(const std::string_view& str)
|
||||
return std::string(str);
|
||||
}
|
||||
|
||||
std::string HTTPDownloader::GetExtensionForContentType(const std::string& content_type)
|
||||
{
|
||||
// Based on https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
||||
static constexpr const char* table[][2] = {
|
||||
{"audio/aac", "aac"},
|
||||
{"application/x-abiword", "abw"},
|
||||
{"application/x-freearc", "arc"},
|
||||
{"image/avif", "avif"},
|
||||
{"video/x-msvideo", "avi"},
|
||||
{"application/vnd.amazon.ebook", "azw"},
|
||||
{"application/octet-stream", "bin"},
|
||||
{"image/bmp", "bmp"},
|
||||
{"application/x-bzip", "bz"},
|
||||
{"application/x-bzip2", "bz2"},
|
||||
{"application/x-cdf", "cda"},
|
||||
{"application/x-csh", "csh"},
|
||||
{"text/css", "css"},
|
||||
{"text/csv", "csv"},
|
||||
{"application/msword", "doc"},
|
||||
{"application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx"},
|
||||
{"application/vnd.ms-fontobject", "eot"},
|
||||
{"application/epub+zip", "epub"},
|
||||
{"application/gzip", "gz"},
|
||||
{"image/gif", "gif"},
|
||||
{"text/html", "htm"},
|
||||
{"image/vnd.microsoft.icon", "ico"},
|
||||
{"text/calendar", "ics"},
|
||||
{"application/java-archive", "jar"},
|
||||
{"image/jpeg", "jpg"},
|
||||
{"text/javascript", "js"},
|
||||
{"application/json", "json"},
|
||||
{"application/ld+json", "jsonld"},
|
||||
{"audio/midi audio/x-midi", "mid"},
|
||||
{"text/javascript", "mjs"},
|
||||
{"audio/mpeg", "mp3"},
|
||||
{"video/mp4", "mp4"},
|
||||
{"video/mpeg", "mpeg"},
|
||||
{"application/vnd.apple.installer+xml", "mpkg"},
|
||||
{"application/vnd.oasis.opendocument.presentation", "odp"},
|
||||
{"application/vnd.oasis.opendocument.spreadsheet", "ods"},
|
||||
{"application/vnd.oasis.opendocument.text", "odt"},
|
||||
{"audio/ogg", "oga"},
|
||||
{"video/ogg", "ogv"},
|
||||
{"application/ogg", "ogx"},
|
||||
{"audio/opus", "opus"},
|
||||
{"font/otf", "otf"},
|
||||
{"image/png", "png"},
|
||||
{"application/pdf", "pdf"},
|
||||
{"application/x-httpd-php", "php"},
|
||||
{"application/vnd.ms-powerpoint", "ppt"},
|
||||
{"application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx"},
|
||||
{"application/vnd.rar", "rar"},
|
||||
{"application/rtf", "rtf"},
|
||||
{"application/x-sh", "sh"},
|
||||
{"image/svg+xml", "svg"},
|
||||
{"application/x-tar", "tar"},
|
||||
{"image/tiff", "tif"},
|
||||
{"video/mp2t", "ts"},
|
||||
{"font/ttf", "ttf"},
|
||||
{"text/plain", "txt"},
|
||||
{"application/vnd.visio", "vsd"},
|
||||
{"audio/wav", "wav"},
|
||||
{"audio/webm", "weba"},
|
||||
{"video/webm", "webm"},
|
||||
{"image/webp", "webp"},
|
||||
{"font/woff", "woff"},
|
||||
{"font/woff2", "woff2"},
|
||||
{"application/xhtml+xml", "xhtml"},
|
||||
{"application/vnd.ms-excel", "xls"},
|
||||
{"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx"},
|
||||
{"application/xml", "xml"},
|
||||
{"text/xml", "xml"},
|
||||
{"application/vnd.mozilla.xul+xml", "xul"},
|
||||
{"application/zip", "zip"},
|
||||
{"video/3gpp", "3gp"},
|
||||
{"audio/3gpp", "3gp"},
|
||||
{"video/3gpp2", "3g2"},
|
||||
{"audio/3gpp2", "3g2"},
|
||||
{"application/x-7z-compressed", "7z"},
|
||||
};
|
||||
|
||||
std::string ret;
|
||||
for (size_t i = 0; i < std::size(table); i++)
|
||||
{
|
||||
if (StringUtil::Strncasecmp(table[i][0], content_type.data(), content_type.length()) == 0)
|
||||
{
|
||||
ret = table[i][1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Common
|
@ -21,7 +21,7 @@ public:
|
||||
struct Request
|
||||
{
|
||||
using Data = std::vector<u8>;
|
||||
using Callback = std::function<void(s32 status_code, const Data& data)>;
|
||||
using Callback = std::function<void(s32 status_code, std::string content_type, Data data)>;
|
||||
|
||||
enum class Type
|
||||
{
|
||||
@ -42,6 +42,7 @@ public:
|
||||
Callback callback;
|
||||
std::string url;
|
||||
std::string post_data;
|
||||
std::string content_type;
|
||||
Data data;
|
||||
u64 start_time;
|
||||
s32 status_code = 0;
|
||||
@ -56,6 +57,7 @@ public:
|
||||
static std::unique_ptr<HTTPDownloader> Create(const char* user_agent = DEFAULT_USER_AGENT);
|
||||
static std::string URLEncode(const std::string_view& str);
|
||||
static std::string URLDecode(const std::string_view& str);
|
||||
static std::string GetExtensionForContentType(const std::string& content_type);
|
||||
|
||||
void SetTimeout(float timeout);
|
||||
void SetMaxActiveRequests(u32 max_active_requests);
|
||||
|
@ -88,6 +88,11 @@ void HTTPDownloaderCurl::ProcessRequest(Request* req)
|
||||
long response_code = 0;
|
||||
curl_easy_getinfo(req->handle, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
req->status_code = static_cast<s32>(response_code);
|
||||
|
||||
char* content_type = nullptr;
|
||||
if (!curl_easy_getinfo(req->handle, CURLINFO_CONTENT_TYPE, &content_type) && content_type)
|
||||
req->content_type = content_type;
|
||||
|
||||
Log_DevPrintf("Request for '%s' returned status code %d and %zu bytes", req->url.c_str(), req->status_code,
|
||||
req->data.size());
|
||||
}
|
||||
@ -159,4 +164,4 @@ void HTTPDownloaderCurl::CloseRequest(HTTPDownloader::Request* request)
|
||||
req->closed.store(true);
|
||||
}
|
||||
|
||||
} // namespace FrontendCommon
|
||||
} // namespace Common
|
||||
|
@ -130,6 +130,20 @@ void CALLBACK HTTPDownloaderWinHttp::HTTPStatusCallback(HINTERNET hRequest, DWOR
|
||||
req->content_length = 0;
|
||||
}
|
||||
|
||||
DWORD content_type_length = 0;
|
||||
if (!WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CONTENT_TYPE, WINHTTP_HEADER_NAME_BY_INDEX,
|
||||
WINHTTP_NO_OUTPUT_BUFFER, &content_type_length, WINHTTP_NO_HEADER_INDEX) &&
|
||||
GetLastError() == ERROR_INSUFFICIENT_BUFFER && content_type_length >= sizeof(content_type_length))
|
||||
{
|
||||
std::wstring content_type_wstring;
|
||||
content_type_wstring.resize((content_type_length / sizeof(wchar_t)) - 1);
|
||||
if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CONTENT_TYPE, WINHTTP_HEADER_NAME_BY_INDEX,
|
||||
content_type_wstring.data(), &content_type_length, WINHTTP_NO_HEADER_INDEX))
|
||||
{
|
||||
req->content_type = StringUtil::WideStringToUTF8String(content_type_wstring);
|
||||
}
|
||||
}
|
||||
|
||||
Log_DevPrintf("Status code %d, content-length is %u", req->status_code, req->content_length);
|
||||
req->data.reserve(req->content_length);
|
||||
req->state = Request::State::Receiving;
|
||||
@ -224,7 +238,7 @@ bool HTTPDownloaderWinHttp::StartRequest(HTTPDownloader::Request* request)
|
||||
if (!WinHttpCrackUrl(url_wide.c_str(), static_cast<DWORD>(url_wide.size()), 0, &uc))
|
||||
{
|
||||
Log_ErrorPrintf("WinHttpCrackUrl() failed: %u", GetLastError());
|
||||
req->callback(-1, req->data);
|
||||
req->callback(-1, std::string(), req->data);
|
||||
delete req;
|
||||
return false;
|
||||
}
|
||||
@ -236,7 +250,7 @@ bool HTTPDownloaderWinHttp::StartRequest(HTTPDownloader::Request* request)
|
||||
if (!req->hConnection)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to start HTTP request for '%s': %u", req->url.c_str(), GetLastError());
|
||||
req->callback(-1, req->data);
|
||||
req->callback(-1, std::string(), req->data);
|
||||
delete req;
|
||||
return false;
|
||||
}
|
||||
@ -297,4 +311,4 @@ void HTTPDownloaderWinHttp::CloseRequest(HTTPDownloader::Request* request)
|
||||
delete req;
|
||||
}
|
||||
|
||||
} // namespace FrontendCommon
|
||||
} // namespace Common
|
@ -23,6 +23,7 @@ void Canonicalize(std::string* path);
|
||||
|
||||
/// Sanitizes a filename for use in a filesystem.
|
||||
std::string SanitizeFileName(const std::string_view& str, bool strip_slashes = true);
|
||||
void SanitizeFileName(std::string* str, bool strip_slashes = true);
|
||||
|
||||
/// Returns true if the specified path is an absolute path (C:\Path on Windows or /path on Unix).
|
||||
bool IsAbsolute(const std::string_view& path);
|
||||
|
Reference in New Issue
Block a user