From ca5ea827b25da2c7196320f676cf8c3d48b5926a Mon Sep 17 00:00:00 2001 From: ITotalJustice <47043333+ITotalJustice@users.noreply.github.com> Date: Fri, 3 Oct 2025 07:31:10 +0100 Subject: [PATCH] devoptab: fix nginx listing, fix modifying entry overriding the scheme, fix smb failing to parse url if path isn't set. ftpsrv: workaround clients sending PASS for anon. --- sphaira/CMakeLists.txt | 2 +- sphaira/source/utils/devoptab.cpp | 6 +++-- sphaira/source/utils/devoptab_http.cpp | 37 +++++++++++++++++++++----- sphaira/source/utils/devoptab_smb2.cpp | 8 +++++- 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/sphaira/CMakeLists.txt b/sphaira/CMakeLists.txt index 8211d66..0bda3b4 100644 --- a/sphaira/CMakeLists.txt +++ b/sphaira/CMakeLists.txt @@ -341,7 +341,7 @@ endif() if (ENABLE_FTPSRV) FetchContent_Declare(ftpsrv GIT_REPOSITORY https://github.com/ITotalJustice/ftpsrv.git - GIT_TAG 7770dab + GIT_TAG 7c82402 SOURCE_SUBDIR NONE ) diff --git a/sphaira/source/utils/devoptab.cpp b/sphaira/source/utils/devoptab.cpp index 252ced0..82c4257 100644 --- a/sphaira/source/utils/devoptab.cpp +++ b/sphaira/source/utils/devoptab.cpp @@ -224,8 +224,10 @@ void DevoptabForm::SetupButtons(bool type_change) { "Hide the mount from being visible as a export option for games and saves."_i18n ); - // set default scheme if needed. - UpdateSchemeURL(); + // set default scheme when creating a new entry. + if (type_change) { + UpdateSchemeURL(); + } const auto callback = this->Add("Save", [this](){ m_config.name = m_name->GetValue(); diff --git a/sphaira/source/utils/devoptab_http.cpp b/sphaira/source/utils/devoptab_http.cpp index f1b13f1..bc09d7d 100644 --- a/sphaira/source/utils/devoptab_http.cpp +++ b/sphaira/source/utils/devoptab_http.cpp @@ -20,7 +20,9 @@ namespace sphaira::devoptab { namespace { struct DirEntry { - std::string name{}; + // deprecated because the names can be truncated and really set to anything. + std::string name_deprecated{}; + // url decoded href. std::string href{}; bool is_dir{}; }; @@ -113,14 +115,30 @@ int Device::http_dirlist(const std::string& path, DirEntries& out) { // todo: if i ever add an xml parser to sphaira, use that instead. // todo: for the above, benchmark the parser to ensure its faster than the my code. std::string_view chunk_view{chunk.data(), chunk.size()}; + + const auto body_start = chunk_view.find(""); const auto table_start = chunk_view.find(""); + std::string_view table_view{}; + + // try and find the body, if this doesn't exist, fallback it's not a valid html page. + if (body_start != std::string_view::npos && body_end != std::string_view::npos && body_end > body_start) { + table_view = chunk_view.substr(body_start, body_end - body_start); + } + + // try and find the table, massively speeds up parsing if it exists. + // todo: this may cause issues with some web servers that don't use a table for listings. + // todo: if table fails to fine anything, fallback to body_view. if (table_start != std::string_view::npos && table_end != std::string_view::npos && table_end > table_start) { + table_view = chunk_view.substr(table_start, table_end - table_start); + } + + if (!table_view.empty()) { const std::string_view href_tag_start = ""; const std::string_view anchor_tag_end = ""; - const auto table_view = chunk_view.substr(table_start, table_end - table_start); size_t pos = 0; out.reserve(10000); @@ -138,6 +156,11 @@ int Device::http_dirlist(const std::string& path, DirEntries& out) { break; // no more href. } + const auto href_name_end = table_view.find('"', href_begin); + if (href_name_end == std::string_view::npos || href_name_end < href_begin || href_name_end > href_end) { + break; // invalid href. + } + const auto name_begin = href_end + href_tag_end.length(); const auto name_end = table_view.find(anchor_tag_end, name_begin); if (name_end == std::string_view::npos) { @@ -145,7 +168,7 @@ int Device::http_dirlist(const std::string& path, DirEntries& out) { } pos = name_end + anchor_tag_end.length(); - const auto href = url_decode(std::string{table_view.substr(href_begin, href_end - href_begin)}); + auto href = url_decode(std::string{table_view.substr(href_begin, href_name_end - href_begin)}); auto name = url_decode(std::string{table_view.substr(name_begin, name_end - name_begin)}); // skip empty names/links, root dir entry and links that are not actual files/dirs (e.g. sorting/filter controls). @@ -158,9 +181,9 @@ int Device::http_dirlist(const std::string& path, DirEntries& out) { continue; } - const auto is_dir = name.ends_with('/'); + const auto is_dir = href.ends_with('/'); if (is_dir) { - name.pop_back(); // remove the trailing '/' + href.pop_back(); // remove the trailing '/' } out.emplace_back(name, href, is_dir); @@ -375,8 +398,10 @@ int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) { filestat->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; } + // Compass_2.0.7.1-Release_ScVi3.0.1-Standalone-21..> filestat->st_nlink = 1; - std::strcpy(filename, entry.name.c_str()); + // std::strcpy(filename, entry.name.c_str()); + std::strcpy(filename, entry.href.c_str()); dir->index++; return 0; diff --git a/sphaira/source/utils/devoptab_smb2.cpp b/sphaira/source/utils/devoptab_smb2.cpp index 7a06b0b..e767c40 100644 --- a/sphaira/source/utils/devoptab_smb2.cpp +++ b/sphaira/source/utils/devoptab_smb2.cpp @@ -123,7 +123,13 @@ bool Device::Mount() { } } - auto smb2_url = smb2_parse_url(this->smb2, this->config.url.c_str()); + // due to a bug in old sphira, i incorrectly prepended the url with smb:// rather than smb2:// + auto url = this->config.url; + if (!url.ends_with('/')) { + url += '/'; + } + + auto smb2_url = smb2_parse_url(this->smb2, url.c_str()); if (!smb2_url) { log_write("[SMB2] smb2_parse_url() failed: %s\n", smb2_get_error(this->smb2)); return false;