devoptab: add mount creator.
This commit is contained in:
@@ -128,6 +128,7 @@ add_executable(sphaira
|
||||
source/utils/devoptab_bfsar.cpp
|
||||
source/utils/devoptab_vfs.cpp
|
||||
source/utils/devoptab_fatfs.cpp
|
||||
source/utils/devoptab.cpp
|
||||
|
||||
source/usb/base.cpp
|
||||
source/usb/usbds.cpp
|
||||
|
||||
@@ -175,7 +175,10 @@ private:
|
||||
|
||||
class SidebarEntryTextInput final : public SidebarEntryTextBase {
|
||||
public:
|
||||
// uses normal keyboard.
|
||||
explicit SidebarEntryTextInput(const std::string& title, const std::string& value, const std::string& guide = {}, s64 len_min = -1, s64 len_max = PATH_MAX, const std::string& info = "");
|
||||
// uses numpad.
|
||||
explicit SidebarEntryTextInput(const std::string& title, s64 value, const std::string& guide = {}, s64 len_min = -1, s64 len_max = PATH_MAX, const std::string& info = "");
|
||||
|
||||
private:
|
||||
const std::string m_guide;
|
||||
@@ -202,10 +205,8 @@ public:
|
||||
using Items = std::vector<std::unique_ptr<SidebarEntryBase>>;
|
||||
|
||||
public:
|
||||
explicit Sidebar(const std::string& title, Side side, Items&& items);
|
||||
explicit Sidebar(const std::string& title, Side side);
|
||||
explicit Sidebar(const std::string& title, const std::string& sub, Side side, Items&& items);
|
||||
explicit Sidebar(const std::string& title, const std::string& sub, Side side);
|
||||
explicit Sidebar(const std::string& title, Side side, float width = 450.f);
|
||||
explicit Sidebar(const std::string& title, const std::string& sub, Side side, float width = 450.f);
|
||||
|
||||
auto Update(Controller* controller, TouchInfo* touch) -> void override;
|
||||
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
||||
@@ -242,4 +243,11 @@ private:
|
||||
static constexpr Vec2 m_box_size{400.f, 70.f};
|
||||
};
|
||||
|
||||
class FormSidebar : public Sidebar {
|
||||
public:
|
||||
// explicit FormSidebar(const std::string& title) : Sidebar{title, Side::RIGHT, 540.f} {
|
||||
explicit FormSidebar(const std::string& title) : Sidebar{title, Side::RIGHT} {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui
|
||||
|
||||
@@ -36,4 +36,6 @@ void UmountNeworkDevice(const fs::FsPath& mount);
|
||||
// SEE: https://github.com/devkitPro/newlib/issues/35
|
||||
void FixDkpBug();
|
||||
|
||||
void DisplayDevoptabSideBar();
|
||||
|
||||
} // namespace sphaira::devoptab
|
||||
|
||||
@@ -131,7 +131,7 @@ struct MountConfig {
|
||||
std::string user{};
|
||||
std::string pass{};
|
||||
std::string dump_path{};
|
||||
std::optional<long> port{};
|
||||
long port{};
|
||||
long timeout{};
|
||||
bool read_only{};
|
||||
bool no_stat_file{true};
|
||||
@@ -141,6 +141,7 @@ struct MountConfig {
|
||||
|
||||
std::unordered_map<std::string, std::string> extra{};
|
||||
};
|
||||
using MountConfigs = std::vector<MountConfig>;
|
||||
|
||||
struct PullThreadData final : PushPullThreadData {
|
||||
using PushPullThreadData::PushPullThreadData;
|
||||
@@ -214,6 +215,8 @@ private:
|
||||
bool m_mounted{};
|
||||
};
|
||||
|
||||
void LoadConfigsFromIni(const fs::FsPath& path, MountConfigs& out_configs);
|
||||
|
||||
using CreateDeviceCallback = std::function<std::unique_ptr<MountDevice>(const MountConfig& config)>;
|
||||
Result MountNetworkDevice(const CreateDeviceCallback& create_device, size_t file_size, size_t dir_size, const char* name, bool force_read_only = false);
|
||||
|
||||
|
||||
@@ -1953,6 +1953,12 @@ void App::DisplayAdvancedOptions(bool left_side) {
|
||||
}, "When enabled, it replaces /hbmenu.nro with Sphaira, creating a backup of hbmenu to /switch/hbmenu.nro\n\n" \
|
||||
"Disabling will give you the option to restore hbmenu."_i18n);
|
||||
|
||||
options->Add<ui::SidebarEntryCallback>("Add / modify mounts"_i18n, [](){
|
||||
devoptab::DisplayDevoptabSideBar();
|
||||
}, "Create, modify, delete network mounts (HTTP, FTP, SFTP, SMB, NFS).\n"
|
||||
"Mount options only require a URL and Name be set, with other fields being optional, such as port, user, pass etc.\n\n"
|
||||
"Any changes made will require restarting Sphaira to take effect."_i18n);
|
||||
|
||||
options->Add<ui::SidebarEntryBool>("Boost CPU during transfer"_i18n, App::GetApp()->m_progress_boost_mode,
|
||||
"Enables boost mode during transfers which can improve transfer speed. "
|
||||
"This sets the CPU to 1785mhz and lowers the GPU 76mhz"_i18n);
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace {
|
||||
|
||||
using RomDatabaseIndexs = std::vector<size_t>;
|
||||
|
||||
struct ForwarderForm final : public Sidebar {
|
||||
struct ForwarderForm final : public FormSidebar {
|
||||
explicit ForwarderForm(const FileAssocEntry& assoc, const RomDatabaseIndexs& db_indexs, const FileEntry& entry, const fs::FsPath& arg_path);
|
||||
|
||||
private:
|
||||
@@ -288,7 +288,7 @@ auto GetRomIcon(std::string filename, const RomDatabaseIndexs& db_indexs, const
|
||||
}
|
||||
|
||||
ForwarderForm::ForwarderForm(const FileAssocEntry& assoc, const RomDatabaseIndexs& db_indexs, const FileEntry& entry, const fs::FsPath& arg_path)
|
||||
: Sidebar{"Forwarder Creation", Side::RIGHT}
|
||||
: FormSidebar{"Forwarder Creation"}
|
||||
, m_assoc{assoc}
|
||||
, m_db_indexs{db_indexs}
|
||||
, m_arg_path{arg_path} {
|
||||
|
||||
@@ -314,6 +314,16 @@ SidebarEntryTextInput::SidebarEntryTextInput(const std::string& title, const std
|
||||
});
|
||||
}
|
||||
|
||||
SidebarEntryTextInput::SidebarEntryTextInput(const std::string& title, s64 value, const std::string& guide, s64 len_min, s64 len_max, const std::string& info)
|
||||
: SidebarEntryTextInput{title, std::to_string(value), guide, len_min, len_max, info} {
|
||||
SetCallback([this](){
|
||||
s64 out = std::stoul(GetValue());
|
||||
if (R_SUCCEEDED(swkbd::ShowNumPad(out, m_guide.c_str(), GetValue().c_str(), m_len_min, m_len_max))) {
|
||||
SetValue(std::to_string(out));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SidebarEntryFilePicker::SidebarEntryFilePicker(const std::string& title, const std::string& value, const std::vector<std::string>& filter, const std::string& info)
|
||||
: SidebarEntryTextBase{title, value, {}, info}, m_filter{filter} {
|
||||
|
||||
@@ -328,26 +338,21 @@ SidebarEntryFilePicker::SidebarEntryFilePicker(const std::string& title, const s
|
||||
});
|
||||
}
|
||||
|
||||
Sidebar::Sidebar(const std::string& title, Side side, Items&& items)
|
||||
: Sidebar{title, "", side, std::forward<decltype(items)>(items)} {
|
||||
Sidebar::Sidebar(const std::string& title, Side side, float width)
|
||||
: Sidebar{title, "", side, width} {
|
||||
}
|
||||
|
||||
Sidebar::Sidebar(const std::string& title, Side side)
|
||||
: Sidebar{title, "", side, {}} {
|
||||
}
|
||||
|
||||
Sidebar::Sidebar(const std::string& title, const std::string& sub, Side side, Items&& items)
|
||||
Sidebar::Sidebar(const std::string& title, const std::string& sub, Side side, float width)
|
||||
: m_title{title}
|
||||
, m_sub{sub}
|
||||
, m_side{side}
|
||||
, m_items{std::forward<decltype(items)>(items)} {
|
||||
, m_side{side} {
|
||||
switch (m_side) {
|
||||
case Side::LEFT:
|
||||
SetPos(Vec4{0.f, 0.f, 450.f, SCREEN_HEIGHT});
|
||||
SetPos(Vec4{0.f, 0.f, width, SCREEN_HEIGHT});
|
||||
break;
|
||||
|
||||
case Side::RIGHT:
|
||||
SetPos(Vec4{SCREEN_WIDTH - 450.f, 0.f, 450.f, SCREEN_HEIGHT});
|
||||
SetPos(Vec4{SCREEN_WIDTH - width, 0.f, width, SCREEN_HEIGHT});
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -365,11 +370,6 @@ Sidebar::Sidebar(const std::string& title, const std::string& sub, Side side, It
|
||||
m_list->SetScrollBarPos(GetX() + GetW() - 20, m_base_pos.y - 10, pos.h - m_base_pos.y + 48);
|
||||
}
|
||||
|
||||
Sidebar::Sidebar(const std::string& title, const std::string& sub, Side side)
|
||||
: Sidebar{title, sub, side, {}} {
|
||||
}
|
||||
|
||||
|
||||
auto Sidebar::Update(Controller* controller, TouchInfo* touch) -> void {
|
||||
Widget::Update(controller, touch);
|
||||
|
||||
|
||||
294
sphaira/source/utils/devoptab.cpp
Normal file
294
sphaira/source/utils/devoptab.cpp
Normal file
@@ -0,0 +1,294 @@
|
||||
#include "utils/devoptab_common.hpp"
|
||||
#include "utils/thread.hpp"
|
||||
|
||||
#include "ui/sidebar.hpp"
|
||||
#include "ui/popup_list.hpp"
|
||||
#include "ui/option_box.hpp"
|
||||
|
||||
#include "app.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "log.hpp"
|
||||
#include "download.hpp"
|
||||
#include "i18n.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <fcntl.h>
|
||||
#include <minIni.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
namespace sphaira::devoptab {
|
||||
namespace {
|
||||
|
||||
using namespace sphaira::ui;
|
||||
using namespace sphaira::devoptab::common;
|
||||
|
||||
enum class DevoptabType {
|
||||
HTTP,
|
||||
FTP,
|
||||
SFTP,
|
||||
NFS,
|
||||
SMB,
|
||||
WEBDAV,
|
||||
};
|
||||
|
||||
struct TypeEntry {
|
||||
const char* name;
|
||||
DevoptabType type;
|
||||
};
|
||||
|
||||
const TypeEntry TYPE_ENTRIES[] = {
|
||||
{"HTTP", DevoptabType::HTTP},
|
||||
{"FTP", DevoptabType::FTP},
|
||||
{"SFTP", DevoptabType::SFTP},
|
||||
{"NFS", DevoptabType::NFS},
|
||||
{"SMB", DevoptabType::SMB},
|
||||
{"WEBDAV", DevoptabType::WEBDAV},
|
||||
};
|
||||
|
||||
struct TypeConfig {
|
||||
TypeEntry type;
|
||||
MountConfig config;
|
||||
};
|
||||
using TypeConfigs = std::vector<TypeConfig>;
|
||||
|
||||
auto BuildIniPathFromType(DevoptabType type) -> fs::FsPath {
|
||||
switch (type) {
|
||||
case DevoptabType::HTTP: return "/config/sphaira/mount/http.ini";
|
||||
case DevoptabType::FTP: return "/config/sphaira/mount/ftp.ini";
|
||||
case DevoptabType::SFTP: return "/config/sphaira/mount/sftp.ini";
|
||||
case DevoptabType::NFS: return "/config/sphaira/mount/nfs.ini";
|
||||
case DevoptabType::SMB: return "/config/sphaira/mount/smb.ini";
|
||||
case DevoptabType::WEBDAV: return "/config/sphaira/mount/webdav.ini";
|
||||
}
|
||||
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
auto GetTypeName(const TypeConfig& type_config) -> std::string {
|
||||
char name[128]{};
|
||||
std::snprintf(name, sizeof(name), "[%s] %s", type_config.type.name, type_config.config.name.c_str());
|
||||
return name;
|
||||
}
|
||||
|
||||
void LoadAllConfigs(TypeConfigs& out_configs) {
|
||||
out_configs.clear();
|
||||
|
||||
for (const auto& e : TYPE_ENTRIES) {
|
||||
const auto ini_path = BuildIniPathFromType(e.type);
|
||||
|
||||
MountConfigs configs{};
|
||||
LoadConfigsFromIni(ini_path, configs);
|
||||
|
||||
for (const auto& config : configs) {
|
||||
out_configs.emplace_back(e, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DevoptabForm final : public FormSidebar {
|
||||
// create new.
|
||||
explicit DevoptabForm();
|
||||
// modify existing.
|
||||
explicit DevoptabForm(DevoptabType type, const MountConfig& config);
|
||||
|
||||
private:
|
||||
void SetupButtons(bool type_change);
|
||||
|
||||
private:
|
||||
DevoptabType m_type{};
|
||||
MountConfig m_config{};
|
||||
|
||||
SidebarEntryTextInput* m_name{};
|
||||
SidebarEntryTextInput* m_url{};
|
||||
SidebarEntryTextInput* m_port{};
|
||||
// SidebarEntryTextInput* m_timeout{};
|
||||
SidebarEntryTextInput* m_user{};
|
||||
SidebarEntryTextInput* m_pass{};
|
||||
SidebarEntryTextInput* m_dump_path{};
|
||||
};
|
||||
|
||||
DevoptabForm::DevoptabForm(DevoptabType type, const MountConfig& config)
|
||||
: FormSidebar{"Mount Creator"}
|
||||
, m_type{type}
|
||||
, m_config{config} {
|
||||
SetupButtons(false);
|
||||
}
|
||||
|
||||
DevoptabForm::DevoptabForm() : FormSidebar{"Mount Creator"} {
|
||||
SetupButtons(true);
|
||||
}
|
||||
|
||||
void DevoptabForm::SetupButtons(bool type_change) {
|
||||
if (type_change) {
|
||||
SidebarEntryArray::Items items;
|
||||
for (const auto& e : TYPE_ENTRIES) {
|
||||
items.emplace_back(e.name);
|
||||
}
|
||||
|
||||
this->Add<SidebarEntryArray>(
|
||||
"Type", items, [this](s64& index) {
|
||||
m_type = TYPE_ENTRIES[index].type;
|
||||
},
|
||||
(s64)m_type,
|
||||
"Select the type of the forwarder."_i18n
|
||||
);
|
||||
}
|
||||
|
||||
m_name = this->Add<SidebarEntryTextInput>(
|
||||
"Name", m_config.name, "", -1, 32,
|
||||
"Set the name of the application"_i18n
|
||||
);
|
||||
|
||||
m_url = this->Add<SidebarEntryTextInput>(
|
||||
"URL", m_config.url, "", -1, PATH_MAX,
|
||||
"Set the URL of the application"_i18n
|
||||
);
|
||||
|
||||
m_port = this->Add<SidebarEntryTextInput>(
|
||||
"Port", m_config.port, "Port number", 1, 5,
|
||||
"Optional: Set the port of the server. If left empty, the default port for the protocol will be used."_i18n
|
||||
);
|
||||
|
||||
#if 0
|
||||
m_timeout = this->Add<SidebarEntryTextInput>(
|
||||
"Timeout", m_config.timeout, "Timeout in milliseconds", 1, 5,
|
||||
"Optional: Set the timeout in seconds."_i18n
|
||||
);
|
||||
#endif
|
||||
|
||||
m_user = this->Add<SidebarEntryTextInput>(
|
||||
"User", m_config.user, "", -1, PATH_MAX,
|
||||
"Optional: Set the username of the application"_i18n
|
||||
);
|
||||
|
||||
m_pass = this->Add<SidebarEntryTextInput>(
|
||||
"Pass", m_config.pass, "", -1, PATH_MAX,
|
||||
"Optional: Set the password of the application"_i18n
|
||||
);
|
||||
|
||||
m_dump_path = this->Add<SidebarEntryTextInput>(
|
||||
"Dump path", m_config.dump_path, "", -1, PATH_MAX,
|
||||
"Optional: Set the dump path used when exporting games and saves."_i18n
|
||||
);
|
||||
|
||||
this->Add<SidebarEntryBool>(
|
||||
"Read only", m_config.read_only,
|
||||
"Mount the filesystem as read only.\n\n"
|
||||
"Setting this option also hidens the mount from being show as an export option."_i18n
|
||||
);
|
||||
|
||||
this->Add<SidebarEntryBool>(
|
||||
"No stat file", m_config.no_stat_file,
|
||||
"Enabling stops the file browser from checking the file size and timestamp of each file. "
|
||||
"This improves browsing performance."_i18n
|
||||
);
|
||||
|
||||
this->Add<SidebarEntryBool>(
|
||||
"No stat dir", m_config.no_stat_dir,
|
||||
"Enabling stops the file browser from checking how many files and folders are in a folder. "
|
||||
"This improves browsing performance, especially for servers that has slow directory listing."_i18n
|
||||
);
|
||||
|
||||
this->Add<SidebarEntryBool>(
|
||||
"FS hidden", m_config.fs_hidden,
|
||||
"Hide the mount from being visible in the file browser."_i18n
|
||||
);
|
||||
|
||||
this->Add<SidebarEntryBool>(
|
||||
"Export hidden", m_config.dump_hidden,
|
||||
"Hide the mount from being visible as a export option for games and saves."_i18n
|
||||
);
|
||||
|
||||
const auto callback = this->Add<SidebarEntryCallback>("Save", [this](){
|
||||
m_config.name = m_name->GetValue();
|
||||
m_config.url = m_url->GetValue();
|
||||
m_config.user = m_user->GetValue();
|
||||
m_config.pass = m_pass->GetValue();
|
||||
m_config.dump_path = m_dump_path->GetValue();
|
||||
m_config.port = std::stoul(m_port->GetValue());
|
||||
// m_config.timeout = m_timeout->GetValue();
|
||||
|
||||
const auto ini_path = BuildIniPathFromType(m_type);
|
||||
|
||||
ini_puts(m_config.name.c_str(), "url", m_config.url.c_str(), ini_path);
|
||||
ini_puts(m_config.name.c_str(), "user", m_config.user.c_str(), ini_path);
|
||||
ini_puts(m_config.name.c_str(), "pass", m_config.pass.c_str(), ini_path);
|
||||
ini_puts(m_config.name.c_str(), "dump_path", m_config.dump_path.c_str(), ini_path);
|
||||
ini_putl(m_config.name.c_str(), "port", m_config.port, ini_path);
|
||||
ini_putl(m_config.name.c_str(), "timeout", m_config.timeout, ini_path);
|
||||
// todo: update minini to have put_bool.
|
||||
ini_puts(m_config.name.c_str(), "read_only", m_config.read_only ? "true" : "false", ini_path);
|
||||
ini_puts(m_config.name.c_str(), "no_stat_file", m_config.no_stat_file ? "true" : "false", ini_path);
|
||||
ini_puts(m_config.name.c_str(), "no_stat_dir", m_config.no_stat_dir ? "true" : "false", ini_path);
|
||||
ini_puts(m_config.name.c_str(), "fs_hidden", m_config.fs_hidden ? "true" : "false", ini_path);
|
||||
ini_puts(m_config.name.c_str(), "dump_hidden", m_config.dump_hidden ? "true" : "false", ini_path);
|
||||
|
||||
App::Notify("Mount entry saved. Restart Sphaira to apply changes."_i18n);
|
||||
|
||||
this->SetPop();
|
||||
}, "Saves the mount entry.\n\n"
|
||||
"NOTE: You must restart Sphaira for changes to take effect!"_i18n);
|
||||
|
||||
// ensure that all fields are valid.
|
||||
callback->Depends([this](){
|
||||
return
|
||||
!m_name->GetValue().empty() &&
|
||||
!m_url->GetValue().empty();
|
||||
}, "Name and URL must be set!"_i18n);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void DisplayDevoptabSideBar() {
|
||||
auto options = std::make_unique<Sidebar>("Devoptab Options"_i18n, Sidebar::Side::RIGHT);
|
||||
ON_SCOPE_EXIT(App::Push(std::move(options)));
|
||||
|
||||
options->Add<SidebarEntryCallback>("Create New Entry"_i18n, [](){
|
||||
App::Push<DevoptabForm>();
|
||||
}, "Creates a new mount option.\n\n"
|
||||
"NOTE: You must restart Sphaira for changes to take effect!"_i18n);
|
||||
|
||||
options->Add<SidebarEntryCallback>("Modify Existing Entry"_i18n, [](){
|
||||
PopupList::Items items;
|
||||
TypeConfigs configs;
|
||||
LoadAllConfigs(configs);
|
||||
|
||||
for (const auto& e : configs) {
|
||||
items.emplace_back(GetTypeName(e));
|
||||
}
|
||||
|
||||
App::Push<PopupList>("Modify Entry"_i18n, items, [configs](std::optional<s64> index){
|
||||
if (!index.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& entry = configs[index.value()];
|
||||
App::Push<DevoptabForm>(entry.type.type, entry.config);
|
||||
});
|
||||
}, "Modify an existing mount option.\n\n"
|
||||
"NOTE: You must restart Sphaira for changes to take effect!"_i18n);
|
||||
|
||||
options->Add<SidebarEntryCallback>("Delete Existing Entry"_i18n, [](){
|
||||
PopupList::Items items;
|
||||
TypeConfigs configs;
|
||||
LoadAllConfigs(configs);
|
||||
|
||||
for (const auto& e : configs) {
|
||||
items.emplace_back(GetTypeName(e));
|
||||
}
|
||||
|
||||
App::Push<PopupList>("Delete Entry"_i18n, items, [configs](std::optional<s64> index){
|
||||
if (!index.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& entry = configs[index.value()];
|
||||
const auto ini_path = BuildIniPathFromType(entry.type.type);
|
||||
ini_puts(entry.config.name.c_str(), nullptr, nullptr, ini_path);
|
||||
});
|
||||
}, "Delete an existing mount option.\n\n"
|
||||
"NOTE: You must restart Sphaira for changes to take effect!"_i18n);
|
||||
}
|
||||
|
||||
} // namespace sphaira::devoptab
|
||||
@@ -769,6 +769,58 @@ void update_devoptab_for_read_only(devoptab_t* devoptab, bool read_only) {
|
||||
}
|
||||
}
|
||||
|
||||
void LoadConfigsFromIni(const fs::FsPath& path, MountConfigs& out_configs) {
|
||||
static const auto cb = [](const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData) -> int {
|
||||
auto e = static_cast<MountConfigs*>(UserData);
|
||||
if (!Section || !Key || !Value) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// add new entry if use section changed.
|
||||
if (e->empty() || std::strcmp(Section, e->back().name.c_str())) {
|
||||
e->emplace_back(Section);
|
||||
}
|
||||
|
||||
if (!std::strcmp(Key, "url")) {
|
||||
e->back().url = Value;
|
||||
} else if (!std::strcmp(Key, "user")) {
|
||||
e->back().user = Value;
|
||||
} else if (!std::strcmp(Key, "pass")) {
|
||||
e->back().pass = Value;
|
||||
} else if (!std::strcmp(Key, "dump_path")) {
|
||||
e->back().dump_path = Value;
|
||||
} else if (!std::strcmp(Key, "port")) {
|
||||
const auto port = ini_parse_getl(Value, -1);
|
||||
if (port < 0 || port > 65535) {
|
||||
log_write("[DEVOPTAB] INI: invalid port %s\n", Value);
|
||||
} else {
|
||||
e->back().port = port;
|
||||
}
|
||||
} else if (!std::strcmp(Key, "timeout")) {
|
||||
e->back().timeout = ini_parse_getl(Value, e->back().timeout);
|
||||
} else if (!std::strcmp(Key, "read_only")) {
|
||||
e->back().read_only = ini_parse_getbool(Value, e->back().read_only);
|
||||
} else if (!std::strcmp(Key, "no_stat_file")) {
|
||||
e->back().no_stat_file = ini_parse_getbool(Value, e->back().no_stat_file);
|
||||
} else if (!std::strcmp(Key, "no_stat_dir")) {
|
||||
e->back().no_stat_dir = ini_parse_getbool(Value, e->back().no_stat_dir);
|
||||
} else if (!std::strcmp(Key, "fs_hidden")) {
|
||||
e->back().fs_hidden = ini_parse_getbool(Value, e->back().fs_hidden);
|
||||
} else if (!std::strcmp(Key, "dump_hidden")) {
|
||||
e->back().dump_hidden = ini_parse_getbool(Value, e->back().dump_hidden);
|
||||
} else {
|
||||
log_write("[DEVOPTAB] INI: extra key %s=%s\n", Key, Value);
|
||||
e->back().extra.emplace(Key, Value);
|
||||
}
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
out_configs.resize(0);
|
||||
ini_browse(cb, &out_configs, path);
|
||||
log_write("[DEVOPTAB] Found %zu mount configs\n", out_configs.size());
|
||||
}
|
||||
|
||||
bool MountNetworkDevice2(std::unique_ptr<MountDevice>&& device, const MountConfig& config, size_t file_size, size_t dir_size, const char* name, const char* mount_name) {
|
||||
if (!device) {
|
||||
log_write("[DEVOPTAB] No device for %s\n", mount_name);
|
||||
@@ -877,60 +929,11 @@ Result MountNetworkDevice(const CreateDeviceCallback& create_device, size_t file
|
||||
|
||||
SCOPED_RWLOCK(&g_rwlock, true);
|
||||
|
||||
using MountConfigs = std::vector<MountConfig>;
|
||||
|
||||
static const auto cb = [](const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData) -> int {
|
||||
auto e = static_cast<MountConfigs*>(UserData);
|
||||
if (!Section || !Key || !Value) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// add new entry if use section changed.
|
||||
if (e->empty() || std::strcmp(Section, e->back().name.c_str())) {
|
||||
e->emplace_back(Section);
|
||||
}
|
||||
|
||||
if (!std::strcmp(Key, "url")) {
|
||||
e->back().url = Value;
|
||||
} else if (!std::strcmp(Key, "user")) {
|
||||
e->back().user = Value;
|
||||
} else if (!std::strcmp(Key, "pass")) {
|
||||
e->back().pass = Value;
|
||||
} else if (!std::strcmp(Key, "dump_path")) {
|
||||
e->back().dump_path = Value;
|
||||
} else if (!std::strcmp(Key, "port")) {
|
||||
const auto port = ini_parse_getl(Value, -1);
|
||||
if (port < 0 || port > 65535) {
|
||||
log_write("[DEVOPTAB] INI: invalid port %s\n", Value);
|
||||
} else {
|
||||
e->back().port = port;
|
||||
}
|
||||
} else if (!std::strcmp(Key, "timeout")) {
|
||||
e->back().timeout = ini_parse_getl(Value, e->back().timeout);
|
||||
} else if (!std::strcmp(Key, "read_only")) {
|
||||
e->back().read_only = ini_parse_getbool(Value, e->back().read_only);
|
||||
} else if (!std::strcmp(Key, "no_stat_file")) {
|
||||
e->back().no_stat_file = ini_parse_getbool(Value, e->back().no_stat_file);
|
||||
} else if (!std::strcmp(Key, "no_stat_dir")) {
|
||||
e->back().no_stat_dir = ini_parse_getbool(Value, e->back().no_stat_dir);
|
||||
} else if (!std::strcmp(Key, "fs_hidden")) {
|
||||
e->back().fs_hidden = ini_parse_getbool(Value, e->back().fs_hidden);
|
||||
} else if (!std::strcmp(Key, "dump_hidden")) {
|
||||
e->back().dump_hidden = ini_parse_getbool(Value, e->back().dump_hidden);
|
||||
} else {
|
||||
log_write("[DEVOPTAB] INI: extra key %s=%s\n", Key, Value);
|
||||
e->back().extra.emplace(Key, Value);
|
||||
}
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
fs::FsPath config_path{};
|
||||
std::snprintf(config_path, sizeof(config_path), "/config/sphaira/mount/%s.ini", name);
|
||||
|
||||
MountConfigs configs{};
|
||||
ini_browse(cb, &configs, config_path);
|
||||
log_write("[DEVOPTAB] Found %zu mount configs\n", configs.size());
|
||||
LoadConfigsFromIni(config_path, configs);
|
||||
|
||||
for (auto& config : configs) {
|
||||
if (config.name.empty()) {
|
||||
@@ -1192,8 +1195,8 @@ bool MountCurlDevice::Mount() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.port.has_value()) {
|
||||
rc = curl_url_set(curlu, CURLUPART_PORT, std::to_string(config.port.value()).c_str(), flags);
|
||||
if (config.port > 0) {
|
||||
rc = curl_url_set(curlu, CURLUPART_PORT, std::to_string(config.port).c_str(), flags);
|
||||
if (rc != CURLUE_OK) {
|
||||
log_write("[CURL] curl_url_set() port failed: %s\n", curl_url_strerror_wrap(rc));
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ bool Device::Mount() {
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
addrinfo* res{};
|
||||
const auto port = this->config.port.value_or(22);
|
||||
const auto port = this->config.port > 0 ? this->config.port : 22;
|
||||
const auto port_str = std::to_string(port);
|
||||
auto ret = getaddrinfo(this->config.url.c_str(), port_str.c_str(), &hints, &res);
|
||||
if (ret != 0) {
|
||||
|
||||
Reference in New Issue
Block a user