mirror of
https://github.com/wiiu-env/ftpiiu_plugin.git
synced 2024-12-27 04:51:49 +01:00
Add configuration
This commit is contained in:
parent
d9965f33df
commit
3f31371193
@ -21,6 +21,7 @@ AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: true
|
||||
AfterClass: true
|
||||
AfterControlStatement: true
|
||||
AfterEnum: true
|
||||
|
@ -71,7 +71,7 @@ CFLAGS := -g -Wall $(OPTIMIZE) -mword-relocations \
|
||||
CFLAGS += $(INCLUDE) -DARM11 -D_3DS \
|
||||
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
||||
-DIMGUI_DISABLE_INCLUDE_IMCONFIG_H=1 \
|
||||
-DNO_IPV6
|
||||
-DNO_IPV6 -DFTPDCONFIG="\"/config/ftpd/ftpd.cfg\""
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
|
||||
|
||||
|
@ -11,7 +11,8 @@ CPPFLAGS := -g -Wall -pthread -Iinclude -Isource/linux \
|
||||
`pkg-config --cflags gl glfw3` \
|
||||
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
||||
-DIMGUI_DISABLE_INCLUDE_IMCONFIG_H=1 \
|
||||
-DIMGUI_IMPL_OPENGL_LOADER_GLAD=1
|
||||
-DIMGUI_IMPL_OPENGL_LOADER_GLAD=1 \
|
||||
-DFTPDCONFIG="\"ftpd.cfg\""
|
||||
CFLAGS := $(CPPFLAGS)
|
||||
CXXFLAGS := $(CPPFLAGS) -std=gnu++17
|
||||
LDFLAGS := -pthread `pkg-config --libs gl glfw3` -ldl
|
||||
|
@ -47,7 +47,7 @@ CFLAGS := -g -Wall $(OPTIMIZE) \
|
||||
$(ARCH) $(INCLUDE) -DARM9 -DNDS \
|
||||
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
||||
-DIMGUI_DISABLE_INCLUDE_IMCONFIG_H=1 \
|
||||
-DNO_IPV6 -DCLASSIC
|
||||
-DNO_IPV6 -DCLASSIC -DFTPDCONFIG="\"/config/ftpd/ftpd.cfg\""
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) $(OPTIMIZE)
|
||||
|
@ -74,6 +74,7 @@ CFLAGS := -g -Wall -Wno-narrowing $(OPTIMIZE) \
|
||||
CFLAGS += $(INCLUDE) -D__SWITCH__ \
|
||||
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
||||
-DIMGUI_DISABLE_INCLUDE_IMCONFIG_H=1 \
|
||||
-DFTPDCONFIG="\"/config/ftpd/ftpd.cfg\"" \
|
||||
`$(PREFIX)pkg-config --cflags libzstd`
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -std=gnu++17 -fno-exceptions -fno-rtti
|
||||
|
10
README.md
10
README.md
@ -105,6 +105,7 @@ Build `switch/ftpd.nro`:
|
||||
- RMD
|
||||
- RNFR
|
||||
- RNTO
|
||||
- SITE
|
||||
- SIZE
|
||||
- STAT
|
||||
- STOR
|
||||
@ -121,3 +122,12 @@ Build `switch/ftpd.nro`:
|
||||
## Planned Commands
|
||||
|
||||
- STOU
|
||||
|
||||
## SITE commands
|
||||
|
||||
- Show help: SITE HELP
|
||||
- Set username: SITE USER <NAME>
|
||||
- Set password: SITE PASS <PASS>
|
||||
- Set port: SITE PORT <PORT>
|
||||
- Set getMTime: SITE MTIME [0|1]
|
||||
- Save config: SITE SAVE
|
||||
|
10
include/fs.h
10
include/fs.h
@ -26,6 +26,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
namespace fs
|
||||
{
|
||||
@ -82,6 +83,9 @@ public:
|
||||
/// \note Can return partial reads
|
||||
ssize_t read (void *data_, std::size_t size_);
|
||||
|
||||
/// \brief Read line
|
||||
std::string_view readLine ();
|
||||
|
||||
/// \brief Read data
|
||||
/// \param data_ Output buffer
|
||||
/// \param size_ Size to read
|
||||
@ -109,6 +113,12 @@ private:
|
||||
|
||||
/// \brief Buffer size
|
||||
std::size_t m_bufferSize = 0;
|
||||
|
||||
/// \brief Line buffer
|
||||
char *m_lineBuffer = nullptr;
|
||||
|
||||
/// \brief Line buffer size
|
||||
std::size_t m_lineBufferSize = 0;
|
||||
};
|
||||
|
||||
/// Directory object
|
||||
|
100
include/ftpConfig.h
Normal file
100
include/ftpConfig.h
Normal file
@ -0,0 +1,100 @@
|
||||
// ftpd is a server implementation based on the following:
|
||||
// - RFC 959 (https://tools.ietf.org/html/rfc959)
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2020 Michael Theall
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class FtpConfig;
|
||||
using UniqueFtpConfig = std::unique_ptr<FtpConfig>;
|
||||
|
||||
/// \brief FTP config
|
||||
class FtpConfig
|
||||
{
|
||||
public:
|
||||
~FtpConfig ();
|
||||
|
||||
/// \brief Create config
|
||||
static UniqueFtpConfig create ();
|
||||
|
||||
/// \brief Load config
|
||||
/// \param path_ Path to config file
|
||||
static UniqueFtpConfig load (char const *path_);
|
||||
|
||||
/// \brief Save config
|
||||
/// \param path_ Path to config file
|
||||
bool save (char const *path_);
|
||||
|
||||
/// \brief Get user
|
||||
std::string const &user () const;
|
||||
|
||||
/// \brief Get password
|
||||
std::string const &pass () const;
|
||||
|
||||
/// \brief Get port
|
||||
std::uint16_t port () const;
|
||||
|
||||
#ifdef _3DS
|
||||
/// \brief Whether to get mtime
|
||||
/// \note only effective on 3DS
|
||||
bool getMTime () const;
|
||||
#endif
|
||||
|
||||
/// \brief Set user
|
||||
/// \param user_ User
|
||||
void setUser (std::string const &user_);
|
||||
|
||||
/// \brief Set password
|
||||
/// \param pass_ Password
|
||||
void setPass (std::string const &pass_);
|
||||
|
||||
/// \brief Set listen port
|
||||
/// \param port_ Listen port
|
||||
bool setPort (std::string const &port_);
|
||||
|
||||
/// \brief Set listen port
|
||||
/// \param port_ Listen port
|
||||
bool setPort (std::uint16_t port_);
|
||||
|
||||
#ifdef _3DS
|
||||
/// \brief Set whether to get mtime
|
||||
/// \param getMTime_ Whether to get mtime
|
||||
void setGetMTime (bool getMTime_);
|
||||
#endif
|
||||
|
||||
private:
|
||||
FtpConfig ();
|
||||
|
||||
/// \brief Username
|
||||
std::string m_user;
|
||||
|
||||
/// \brief Password
|
||||
std::string m_pass;
|
||||
|
||||
/// \brief Listen port
|
||||
std::uint16_t m_port;
|
||||
|
||||
#ifdef _3DS
|
||||
/// \brief Whether to get mtime
|
||||
bool m_getMTime = true;
|
||||
#endif
|
||||
};
|
@ -20,6 +20,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ftpConfig.h"
|
||||
#include "ftpSession.h"
|
||||
#include "platform.h"
|
||||
#include "socket.h"
|
||||
@ -44,8 +45,7 @@ public:
|
||||
void draw ();
|
||||
|
||||
/// \brief Create server
|
||||
/// \param port_ Port to listen on
|
||||
static UniqueFtpServer create (std::uint16_t port_);
|
||||
static UniqueFtpServer create ();
|
||||
|
||||
/// \brief Get free space
|
||||
static std::string getFreeSpace ();
|
||||
@ -58,8 +58,8 @@ public:
|
||||
|
||||
private:
|
||||
/// \brief Paramterized constructor
|
||||
/// \param port_ Port to listen on
|
||||
FtpServer (std::uint16_t port_);
|
||||
/// \param config_ FTP config
|
||||
FtpServer (UniqueFtpConfig config_);
|
||||
|
||||
/// \brief Handle when network is found
|
||||
void handleNetworkFound ();
|
||||
@ -67,6 +67,9 @@ private:
|
||||
/// \brief Handle when network is lost
|
||||
void handleNetworkLost ();
|
||||
|
||||
/// \brief Show menu in the current window
|
||||
void showMenu ();
|
||||
|
||||
/// \brief Server loop
|
||||
void loop ();
|
||||
|
||||
@ -81,6 +84,9 @@ private:
|
||||
platform::Mutex m_lock;
|
||||
#endif
|
||||
|
||||
/// \brief Config
|
||||
UniqueFtpConfig m_config;
|
||||
|
||||
/// \brief Listen socket
|
||||
UniqueSocket m_socket;
|
||||
|
||||
@ -90,9 +96,25 @@ private:
|
||||
/// \brief Sessions
|
||||
std::vector<UniqueFtpSession> m_sessions;
|
||||
|
||||
/// \brief Port to listen on
|
||||
std::uint16_t const m_port;
|
||||
|
||||
/// \brief Whether thread should quit
|
||||
std::atomic<bool> m_quit;
|
||||
|
||||
#ifndef CLASSIC
|
||||
/// \brief Whether to show settings menu
|
||||
bool m_showSettings = false;
|
||||
|
||||
/// \brief User name setting
|
||||
std::string m_userSetting;
|
||||
|
||||
/// \brief Password setting
|
||||
std::string m_passSetting;
|
||||
|
||||
/// \brief Port setting
|
||||
std::uint16_t m_portSetting;
|
||||
|
||||
#ifdef _3DS
|
||||
/// \brief getMTime setting
|
||||
bool m_getMTimeSetting;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
@ -21,6 +21,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fs.h"
|
||||
#include "ftpConfig.h"
|
||||
#include "ioBuffer.h"
|
||||
#include "platform.h"
|
||||
#include "socket.h"
|
||||
@ -47,8 +48,9 @@ public:
|
||||
void draw ();
|
||||
|
||||
/// \brief Create session
|
||||
/// \param config_ FTP config
|
||||
/// \param commandSocket_ Command socket
|
||||
static UniqueFtpSession create (UniqueSocket commandSocket_);
|
||||
static UniqueFtpSession create (FtpConfig &config_, UniqueSocket commandSocket_);
|
||||
|
||||
/// \brief Poll for activity
|
||||
/// \param sessions_ Sessions to poll
|
||||
@ -122,8 +124,12 @@ private:
|
||||
};
|
||||
|
||||
/// \brief Parameterized constructor
|
||||
/// \param config_ FTP config
|
||||
/// \param commandSocket_ Command socket
|
||||
FtpSession (UniqueSocket commandSocket_);
|
||||
FtpSession (FtpConfig &config_, UniqueSocket commandSocket_);
|
||||
|
||||
/// \brief Whether session is authorized
|
||||
bool authorized () const;
|
||||
|
||||
/// \brief Set session state
|
||||
/// \param state_ State to set
|
||||
@ -203,6 +209,9 @@ private:
|
||||
platform::Mutex m_lock;
|
||||
#endif
|
||||
|
||||
/// \brief FTP config
|
||||
FtpConfig &m_config;
|
||||
|
||||
/// \brief Command socket
|
||||
SharedSocket m_commandSocket;
|
||||
|
||||
@ -278,6 +287,13 @@ private:
|
||||
/// \brief Directory transfer mode
|
||||
XferDirMode m_xferDirMode;
|
||||
|
||||
/// \brief Last command timestamp
|
||||
time_t m_timestamp;
|
||||
|
||||
/// \brief Whether user has been authorized
|
||||
bool m_authorizedUser : 1;
|
||||
/// \brief Whether password has been authorized
|
||||
bool m_authorizedPass : 1;
|
||||
/// \brief Whether previous command was PASV
|
||||
bool m_pasv : 1;
|
||||
/// \brief Whether previous command was PORT
|
||||
@ -409,6 +425,10 @@ private:
|
||||
/// \param args_ Command arguments
|
||||
void RNTO (char const *args_);
|
||||
|
||||
/// \brief Site command
|
||||
/// \param args_ Command arguments
|
||||
void SITE (char const *args_);
|
||||
|
||||
/// \brief Get file size
|
||||
/// \param args_ Command arguments
|
||||
void SIZE (char const *args_);
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#include "../imgui/imgui_internal.h"
|
||||
|
||||
#include "fs.h"
|
||||
#include "platform.h"
|
||||
|
||||
@ -136,6 +138,57 @@ void updateGamepads (ImGuiIO &io_)
|
||||
io_.NavInputs[out] = std::clamp ((value - min) / (max - min), 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Update keyboard inputs
|
||||
/// \param io_ ImGui IO
|
||||
void updateKeyboard (ImGuiIO &io_)
|
||||
{
|
||||
static enum {
|
||||
INACTIVE,
|
||||
KEYBOARD,
|
||||
CLEARED,
|
||||
} state = INACTIVE;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case INACTIVE:
|
||||
{
|
||||
if (!io_.WantTextInput)
|
||||
return;
|
||||
|
||||
auto &textState = ImGui::GetCurrentContext ()->InputTextState;
|
||||
|
||||
SwkbdState kbd;
|
||||
|
||||
swkbdInit (&kbd, SWKBD_TYPE_NORMAL, 2, -1);
|
||||
swkbdSetButton (&kbd, SWKBD_BUTTON_LEFT, "Cancel", false);
|
||||
swkbdSetButton (&kbd, SWKBD_BUTTON_RIGHT, "OK", true);
|
||||
swkbdSetInitialText (
|
||||
&kbd, std::string (textState.InitialTextA.Data, textState.InitialTextA.Size).c_str ());
|
||||
|
||||
if (textState.UserFlags & ImGuiInputTextFlags_Password)
|
||||
swkbdSetPasswordMode (&kbd, SWKBD_PASSWORD_HIDE_DELAY);
|
||||
|
||||
char buffer[32] = {0};
|
||||
auto const button = swkbdInputText (&kbd, buffer, sizeof (buffer));
|
||||
if (button == SWKBD_BUTTON_RIGHT)
|
||||
io_.AddInputCharactersUTF8 (buffer);
|
||||
|
||||
state = KEYBOARD;
|
||||
break;
|
||||
}
|
||||
|
||||
case KEYBOARD:
|
||||
// need to skip a frame for active id to really be cleared
|
||||
ImGui::ClearActiveID ();
|
||||
state = CLEARED;
|
||||
break;
|
||||
|
||||
case CLEARED:
|
||||
state = INACTIVE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool imgui::ctru::init ()
|
||||
@ -180,5 +233,6 @@ void imgui::ctru::newFrame ()
|
||||
|
||||
updateTouch (io);
|
||||
updateGamepads (io);
|
||||
updateKeyboard (io);
|
||||
}
|
||||
#endif
|
||||
|
@ -23,6 +23,10 @@
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
|
||||
#if defined(NDS) || defined(_3DS) || defined(__SWITCH__)
|
||||
#define getline __getline
|
||||
#endif
|
||||
|
||||
std::string fs::printSize (std::uint64_t const size_)
|
||||
{
|
||||
constexpr std::uint64_t const KiB = 1024;
|
||||
@ -77,7 +81,10 @@ std::string fs::printSize (std::uint64_t const size_)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
fs::File::~File () = default;
|
||||
fs::File::~File ()
|
||||
{
|
||||
std::free (m_lineBuffer);
|
||||
}
|
||||
|
||||
fs::File::File () = default;
|
||||
|
||||
@ -136,6 +143,27 @@ ssize_t fs::File::read (void *const data_, std::size_t const size_)
|
||||
return std::fread (data_, 1, size_, m_fp.get ());
|
||||
}
|
||||
|
||||
std::string_view fs::File::readLine ()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
auto rc = ::getline (&m_lineBuffer, &m_lineBufferSize, m_fp.get ());
|
||||
if (rc < 0)
|
||||
return {};
|
||||
|
||||
while (rc > 0)
|
||||
{
|
||||
if (m_lineBuffer[rc - 1] != '\r' && m_lineBuffer[rc - 1] != '\n')
|
||||
break;
|
||||
|
||||
m_lineBuffer[--rc] = 0;
|
||||
}
|
||||
|
||||
if (rc > 0)
|
||||
return std::string_view (m_lineBuffer, rc);
|
||||
}
|
||||
}
|
||||
|
||||
bool fs::File::readAll (void *const data_, std::size_t const size_)
|
||||
{
|
||||
auto p = static_cast<char *> (data_);
|
||||
|
257
source/ftpConfig.cpp
Normal file
257
source/ftpConfig.cpp
Normal file
@ -0,0 +1,257 @@
|
||||
// ftpd is a server implementation based on the following:
|
||||
// - RFC 959 (https://tools.ietf.org/html/rfc959)
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2020 Michael Theall
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "ftpConfig.h"
|
||||
|
||||
#include "fs.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
//#include <algorithm>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr std::uint16_t DEFAULT_PORT = 5000;
|
||||
|
||||
bool mkdirParent (std::string const &path_)
|
||||
{
|
||||
auto pos = path_.find_first_of ('/');
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
auto const dir = path_.substr (0, pos);
|
||||
|
||||
struct stat st;
|
||||
auto const rc = ::stat (dir.c_str (), &st);
|
||||
if (rc < 0 && errno != ENOENT)
|
||||
return false;
|
||||
|
||||
if (rc < 0 && errno == ENOENT)
|
||||
{
|
||||
auto const rc = ::mkdir (dir.c_str (), 0755);
|
||||
if (rc < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
pos = path_.find_first_of ('/', pos + 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string strip (std::string const &str_)
|
||||
{
|
||||
auto const start = str_.find_first_not_of (" \t");
|
||||
if (start == std::string::npos)
|
||||
return {};
|
||||
|
||||
auto const end = str_.find_last_not_of (" \t");
|
||||
|
||||
if (end == std::string::npos)
|
||||
return str_.substr (start);
|
||||
|
||||
return str_.substr (start, end + 1 - start);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool parseInt (T &out_, std::string const &val_)
|
||||
{
|
||||
T val = 0;
|
||||
|
||||
for (auto const &c : val_)
|
||||
{
|
||||
if (!std::isdigit (c))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::numeric_limits<T>::max () / 10 < val)
|
||||
{
|
||||
errno = EOVERFLOW;
|
||||
return false;
|
||||
}
|
||||
|
||||
val *= 10;
|
||||
|
||||
auto const v = c - '0';
|
||||
if (std::numeric_limits<T>::max () - v < val)
|
||||
{
|
||||
errno = EOVERFLOW;
|
||||
return false;
|
||||
}
|
||||
|
||||
val += v;
|
||||
}
|
||||
|
||||
out_ = val;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
FtpConfig::~FtpConfig () = default;
|
||||
|
||||
FtpConfig::FtpConfig () : m_port (DEFAULT_PORT)
|
||||
{
|
||||
}
|
||||
|
||||
UniqueFtpConfig FtpConfig::create ()
|
||||
{
|
||||
return UniqueFtpConfig (new FtpConfig ());
|
||||
}
|
||||
|
||||
UniqueFtpConfig FtpConfig::load (char const *const path_)
|
||||
{
|
||||
auto config = create ();
|
||||
|
||||
auto fp = fs::File ();
|
||||
if (!fp.open (path_))
|
||||
return config;
|
||||
|
||||
std::uint16_t port = DEFAULT_PORT;
|
||||
|
||||
std::string line;
|
||||
while (!(line = fp.readLine ()).empty ())
|
||||
{
|
||||
auto const pos = line.find_first_of ('=');
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
error ("Ignoring '%s'\n", line.c_str ());
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const key = strip (line.substr (0, pos));
|
||||
auto const val = strip (line.substr (pos + 1));
|
||||
if (key.empty () || val.empty ())
|
||||
{
|
||||
error ("Ignoring '%s'\n", line.c_str ());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key == "user")
|
||||
config->m_user = val;
|
||||
else if (key == "pass")
|
||||
config->m_pass = val;
|
||||
else if (key == "port")
|
||||
parseInt (port, val);
|
||||
#ifdef _3DS
|
||||
else if (key == "mtime")
|
||||
{
|
||||
if (val == "0")
|
||||
config->m_getMTime = false;
|
||||
else if (val == "1")
|
||||
config->m_getMTime = true;
|
||||
else
|
||||
error ("Invalid value for mtime: %s\n", val.c_str ());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
config->setPort (port);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
bool FtpConfig::save (char const *const path_)
|
||||
{
|
||||
if (!mkdirParent (path_))
|
||||
return false;
|
||||
|
||||
auto fp = fs::File ();
|
||||
if (!fp.open (path_, "wb"))
|
||||
return false;
|
||||
|
||||
if (!m_user.empty ())
|
||||
std::fprintf (fp, "user=%s\n", m_user.c_str ());
|
||||
if (!m_pass.empty ())
|
||||
std::fprintf (fp, "pass=%s\n", m_pass.c_str ());
|
||||
std::fprintf (fp, "port=%u\n", m_port);
|
||||
|
||||
#ifdef _3DS
|
||||
std::fprintf (fp, "mtime=%u\n", m_getMTime);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string const &FtpConfig::user () const
|
||||
{
|
||||
return m_user;
|
||||
}
|
||||
|
||||
std::string const &FtpConfig::pass () const
|
||||
{
|
||||
return m_pass;
|
||||
}
|
||||
|
||||
std::uint16_t FtpConfig::port () const
|
||||
{
|
||||
return m_port;
|
||||
}
|
||||
|
||||
#ifdef _3DS
|
||||
bool FtpConfig::getMTime () const
|
||||
{
|
||||
return m_getMTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
void FtpConfig::setUser (std::string const &user_)
|
||||
{
|
||||
m_user = user_.substr (0, user_.find_first_of ('\0'));
|
||||
}
|
||||
|
||||
void FtpConfig::setPass (std::string const &pass_)
|
||||
{
|
||||
m_pass = pass_.substr (0, pass_.find_first_of ('\0'));
|
||||
}
|
||||
|
||||
bool FtpConfig::setPort (std::string const &port_)
|
||||
{
|
||||
std::uint16_t parsed;
|
||||
if (!parseInt (parsed, port_))
|
||||
return false;
|
||||
|
||||
return setPort (parsed);
|
||||
}
|
||||
|
||||
bool FtpConfig::setPort (std::uint16_t const port_)
|
||||
{
|
||||
if (port_ < 1024
|
||||
#if !defined(NDS) && !defined(_3DS)
|
||||
&& port_ != 0
|
||||
#endif
|
||||
)
|
||||
{
|
||||
errno = EPERM;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_port = port_;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef _3DS
|
||||
void FtpConfig::setGetMTime (bool const getMTime_)
|
||||
{
|
||||
m_getMTime = getMTime_;
|
||||
}
|
||||
#endif
|
@ -77,7 +77,7 @@ FtpServer::~FtpServer ()
|
||||
#endif
|
||||
}
|
||||
|
||||
FtpServer::FtpServer (std::uint16_t const port_) : m_port (port_), m_quit (false)
|
||||
FtpServer::FtpServer (UniqueFtpConfig config_) : m_config (std::move (config_)), m_quit (false)
|
||||
{
|
||||
#ifndef NDS
|
||||
m_thread = platform::Thread (std::bind (&FtpServer::threadFunc, this));
|
||||
@ -172,12 +172,20 @@ void FtpServer::draw ()
|
||||
|
||||
ImGui::Begin (title,
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize
|
||||
#ifndef _3DS
|
||||
| ImGuiWindowFlags_MenuBar
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
#ifndef _3DS
|
||||
showMenu ();
|
||||
#endif
|
||||
|
||||
#ifndef _3DS
|
||||
ImGui::BeginChild (
|
||||
"Logs", ImVec2 (0, 0.55f * height), false, ImGuiWindowFlags_HorizontalScrollbar);
|
||||
"Logs", ImVec2 (0, 0.5f * height), false, ImGuiWindowFlags_HorizontalScrollbar);
|
||||
#endif
|
||||
drawLog ();
|
||||
#ifndef _3DS
|
||||
@ -192,7 +200,10 @@ void FtpServer::draw ()
|
||||
ImGui::SetNextWindowPos (ImVec2 (width * 0.1f, height * 0.5f), ImGuiCond_FirstUseEver);
|
||||
ImGui::Begin ("Sessions",
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_MenuBar);
|
||||
|
||||
showMenu ();
|
||||
#else
|
||||
ImGui::Separator ();
|
||||
#endif
|
||||
@ -207,10 +218,13 @@ void FtpServer::draw ()
|
||||
#endif
|
||||
}
|
||||
|
||||
UniqueFtpServer FtpServer::create (std::uint16_t const port_)
|
||||
UniqueFtpServer FtpServer::create ()
|
||||
{
|
||||
updateFreeSpace ();
|
||||
return UniqueFtpServer (new FtpServer (port_));
|
||||
|
||||
auto config = FtpConfig::load (FTPDCONFIG);
|
||||
|
||||
return UniqueFtpServer (new FtpServer (std::move (config)));
|
||||
}
|
||||
|
||||
std::string FtpServer::getFreeSpace ()
|
||||
@ -256,13 +270,13 @@ void FtpServer::handleNetworkFound ()
|
||||
#else
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
#endif
|
||||
addr.sin_port = htons (m_port);
|
||||
addr.sin_port = htons (m_config->port ());
|
||||
|
||||
auto socket = Socket::create ();
|
||||
if (!socket)
|
||||
return;
|
||||
|
||||
if (m_port != 0 && !socket->setReuseAddress (true))
|
||||
if (m_config->port () != 0 && !socket->setReuseAddress (true))
|
||||
return;
|
||||
|
||||
if (!socket->bind (addr))
|
||||
@ -292,6 +306,115 @@ void FtpServer::handleNetworkLost ()
|
||||
info ("Stopped server at %s\n", m_name.c_str ());
|
||||
}
|
||||
|
||||
void FtpServer::showMenu ()
|
||||
{
|
||||
#ifndef CLASSIC
|
||||
auto const prevShowSettings = m_showSettings;
|
||||
|
||||
if (ImGui::BeginMenuBar ())
|
||||
{
|
||||
#if defined(_3DS) || defined(__SWITCH__)
|
||||
if (ImGui::BeginMenu (u8"Menu \xee\x80\x83")) // Y Button
|
||||
#else
|
||||
if (ImGui::BeginMenu ("Menu"))
|
||||
#endif
|
||||
{
|
||||
if (ImGui::MenuItem ("Settings"))
|
||||
m_showSettings = true;
|
||||
|
||||
ImGui::EndMenu ();
|
||||
}
|
||||
ImGui::EndMenuBar ();
|
||||
}
|
||||
|
||||
if (!prevShowSettings && m_showSettings)
|
||||
{
|
||||
m_userSetting = m_config->user ();
|
||||
m_userSetting.resize (32);
|
||||
|
||||
m_passSetting = m_config->pass ();
|
||||
m_passSetting.resize (32);
|
||||
|
||||
m_portSetting = m_config->port ();
|
||||
|
||||
#ifdef _3DS
|
||||
m_getMTimeSetting = m_config->getMTime ();
|
||||
#endif
|
||||
|
||||
ImGui::OpenPopup ("Settings");
|
||||
}
|
||||
|
||||
#ifdef _3DS
|
||||
auto const &io = ImGui::GetIO ();
|
||||
auto const width = io.DisplaySize.x;
|
||||
auto const height = io.DisplaySize.y;
|
||||
|
||||
ImGui::SetNextWindowSize (ImVec2 (width * 0.8f, height * 0.5f));
|
||||
ImGui::SetNextWindowPos (ImVec2 (width * 0.1f, height * 0.5f));
|
||||
if (ImGui::BeginPopupModal ("Settings",
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize))
|
||||
#else
|
||||
if (ImGui::BeginPopupModal ("Settings", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
|
||||
#endif
|
||||
{
|
||||
ImGui::InputText (
|
||||
"User", &m_userSetting[0], m_userSetting.size (), ImGuiInputTextFlags_AutoSelectAll);
|
||||
|
||||
ImGui::InputText ("Pass",
|
||||
&m_passSetting[0],
|
||||
m_passSetting.size (),
|
||||
ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_Password);
|
||||
|
||||
ImGui::InputScalar ("Port",
|
||||
ImGuiDataType_U16,
|
||||
&m_portSetting,
|
||||
nullptr,
|
||||
nullptr,
|
||||
"%u",
|
||||
ImGuiInputTextFlags_AutoSelectAll);
|
||||
|
||||
#ifdef _3DS
|
||||
ImGui::Checkbox ("Get mtime", &m_getMTimeSetting);
|
||||
#endif
|
||||
|
||||
auto const apply = ImGui::Button ("Apply", ImVec2 (100, 0));
|
||||
ImGui::SameLine ();
|
||||
auto const save = ImGui::Button ("Save", ImVec2 (100, 0));
|
||||
ImGui::SameLine ();
|
||||
auto const cancel = ImGui::Button ("Cancel", ImVec2 (100, 0));
|
||||
|
||||
if (apply || save)
|
||||
{
|
||||
m_showSettings = false;
|
||||
ImGui::CloseCurrentPopup ();
|
||||
|
||||
m_config->setUser (m_userSetting);
|
||||
m_config->setPass (m_passSetting);
|
||||
m_config->setPort (m_portSetting);
|
||||
|
||||
#ifdef _3DS
|
||||
m_config->setGetMTime (m_getMTimeSetting);
|
||||
#endif
|
||||
|
||||
UniqueSocket socket;
|
||||
LOCKED (socket = std::move (m_socket));
|
||||
}
|
||||
|
||||
if (save && !m_config->save (FTPDCONFIG))
|
||||
error ("Failed to save config\n");
|
||||
|
||||
if (apply || save || cancel)
|
||||
{
|
||||
m_showSettings = false;
|
||||
ImGui::CloseCurrentPopup ();
|
||||
}
|
||||
|
||||
ImGui::EndPopup ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FtpServer::loop ()
|
||||
{
|
||||
if (!m_socket)
|
||||
@ -309,7 +432,7 @@ void FtpServer::loop ()
|
||||
auto socket = m_socket->accept ();
|
||||
if (socket)
|
||||
{
|
||||
auto session = FtpSession::create (std::move (socket));
|
||||
auto session = FtpSession::create (*m_config, std::move (socket));
|
||||
LOCKED (m_sessions.emplace_back (std::move (session)));
|
||||
}
|
||||
else
|
||||
|
@ -262,11 +262,14 @@ FtpSession::~FtpSession ()
|
||||
closeData ();
|
||||
}
|
||||
|
||||
FtpSession::FtpSession (UniqueSocket commandSocket_)
|
||||
: m_commandSocket (std::move (commandSocket_)),
|
||||
FtpSession::FtpSession (FtpConfig &config_, UniqueSocket commandSocket_)
|
||||
: m_config (config_),
|
||||
m_commandSocket (std::move (commandSocket_)),
|
||||
m_commandBuffer (COMMAND_BUFFERSIZE),
|
||||
m_responseBuffer (RESPONSE_BUFFERSIZE),
|
||||
m_xferBuffer (XFER_BUFFERSIZE),
|
||||
m_authorizedUser (false),
|
||||
m_authorizedPass (false),
|
||||
m_pasv (false),
|
||||
m_port (false),
|
||||
m_recv (false),
|
||||
@ -278,6 +281,11 @@ FtpSession::FtpSession (UniqueSocket commandSocket_)
|
||||
m_mlstPerm (true),
|
||||
m_mlstUnixMode (false)
|
||||
{
|
||||
if (m_config.user ().empty ())
|
||||
m_authorizedUser = true;
|
||||
if (m_config.pass ().empty ())
|
||||
m_authorizedPass = true;
|
||||
|
||||
char buffer[32];
|
||||
std::sprintf (buffer, "Session#%p", this);
|
||||
m_windowName = buffer;
|
||||
@ -373,9 +381,9 @@ void FtpSession::draw ()
|
||||
#endif
|
||||
}
|
||||
|
||||
UniqueFtpSession FtpSession::create (UniqueSocket commandSocket_)
|
||||
UniqueFtpSession FtpSession::create (FtpConfig &config_, UniqueSocket commandSocket_)
|
||||
{
|
||||
return UniqueFtpSession (new FtpSession (std::move (commandSocket_)));
|
||||
return UniqueFtpSession (new FtpSession (config_, std::move (commandSocket_)));
|
||||
}
|
||||
|
||||
bool FtpSession::poll (std::vector<UniqueFtpSession> const &sessions_)
|
||||
@ -557,8 +565,11 @@ bool FtpSession::poll (std::vector<UniqueFtpSession> const &sessions_)
|
||||
}
|
||||
else if (i.revents & (POLLIN | POLLOUT))
|
||||
{
|
||||
while (((*session).*(session->m_transfer)) ())
|
||||
;
|
||||
for (unsigned i = 0; i < 10; ++i)
|
||||
{
|
||||
if (!((*session).*(session->m_transfer)) ())
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -568,6 +579,11 @@ bool FtpSession::poll (std::vector<UniqueFtpSession> const &sessions_)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FtpSession::authorized () const
|
||||
{
|
||||
return m_authorizedUser && m_authorizedPass;
|
||||
}
|
||||
|
||||
void FtpSession::setState (State const state_, bool const closePasv_, bool const closeData_)
|
||||
{
|
||||
m_state = state_;
|
||||
@ -989,8 +1005,10 @@ int FtpSession::fillDirent (struct stat const &st_, std::string_view const path_
|
||||
if (!tm)
|
||||
return errno;
|
||||
|
||||
auto fmt = "%b %e %H:%M ";
|
||||
rc = std::strftime (&buffer[pos], size - pos, fmt, tm);
|
||||
auto fmt = "%b %e %Y ";
|
||||
if (m_timestamp > st_.st_mtime && m_timestamp - st_.st_mtime < (60 * 60 * 24 * 365 / 2))
|
||||
fmt = "%b %e %H:%M ";
|
||||
rc = std::strftime (&buffer[pos], size - pos, fmt, tm);
|
||||
if (rc < 0)
|
||||
return errno;
|
||||
if (static_cast<std::size_t> (rc) > size - pos)
|
||||
@ -1009,6 +1027,7 @@ int FtpSession::fillDirent (struct stat const &st_, std::string_view const path_
|
||||
buffer[pos++] = '\n';
|
||||
|
||||
m_xferBuffer.markUsed (pos);
|
||||
LOCKED (m_filePosition += pos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1145,6 +1164,7 @@ void FtpSession::xferDir (char const *const args_, XferDirMode const mode_, bool
|
||||
m_recv = false;
|
||||
m_send = true;
|
||||
|
||||
m_filePosition = 0;
|
||||
m_xferBuffer.clear ();
|
||||
|
||||
m_transfer = &FtpSession::listTransfer;
|
||||
@ -1233,7 +1253,7 @@ void FtpSession::xferDir (char const *const args_, XferDirMode const mode_, bool
|
||||
// everything else uses basename
|
||||
auto const pos = path.find_last_of ('/');
|
||||
assert (pos != std::string::npos);
|
||||
name = encodePath (std::string_view (path).substr (pos));
|
||||
name = encodePath (std::string_view (path).substr (pos + 1));
|
||||
}
|
||||
|
||||
auto const rc = fillDirent (st, name);
|
||||
@ -1420,6 +1440,7 @@ void FtpSession::readCommand (int const events_)
|
||||
return ::strcasecmp (lhs_.first.data (), rhs_) < 0;
|
||||
});
|
||||
|
||||
m_timestamp = std::time (nullptr);
|
||||
if (it == std::end (handlers) || ::strcasecmp (it->first.data (), command) != 0)
|
||||
{
|
||||
std::string response = "502 Invalid command \"";
|
||||
@ -1585,13 +1606,17 @@ bool FtpSession::listTransfer ()
|
||||
if (m_xferDirMode == XferDirMode::NLST)
|
||||
{
|
||||
// NLST gives the whole path name
|
||||
auto const path = encodePath (buildPath (m_lwd, dent->d_name));
|
||||
auto const path = encodePath (buildPath (m_lwd, dent->d_name)) + "\r\n";
|
||||
if (m_xferBuffer.freeSize () < path.size ())
|
||||
{
|
||||
sendResponse ("501 %s\r\n", std::strerror (ENOMEM));
|
||||
setState (State::COMMAND, true, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::memcpy (m_xferBuffer.freeArea (), path.data (), path.size ());
|
||||
m_xferBuffer.markUsed (path.size ());
|
||||
LOCKED (m_filePosition += path.size ());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1629,7 +1654,7 @@ bool FtpSession::listTransfer ()
|
||||
else if (m_xferDirMode == XferDirMode::NLST)
|
||||
getmtime = false;
|
||||
|
||||
if (getmtime)
|
||||
if (getmtime && m_config.getMTime ())
|
||||
{
|
||||
std::uint64_t mtime = 0;
|
||||
auto const rc = archive_getmtime (fullPath.c_str (), &mtime);
|
||||
@ -1801,6 +1826,13 @@ void FtpSession::ALLO (char const *args_)
|
||||
|
||||
void FtpSession::APPE (char const *args_)
|
||||
{
|
||||
if (!authorized ())
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// open the file in append mode
|
||||
xferFile (args_, XferFileMode::APPE);
|
||||
}
|
||||
@ -1809,6 +1841,12 @@ void FtpSession::CDUP (char const *args_)
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
|
||||
if (!authorized ())
|
||||
{
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!changeDir (".."))
|
||||
{
|
||||
sendResponse ("550 %s\r\n", std::strerror (errno));
|
||||
@ -1822,6 +1860,12 @@ void FtpSession::CWD (char const *args_)
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
|
||||
if (!authorized ())
|
||||
{
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!changeDir (args_))
|
||||
{
|
||||
sendResponse ("550 %s\r\n", std::strerror (errno));
|
||||
@ -1835,6 +1879,12 @@ void FtpSession::DELE (char const *args_)
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
|
||||
if (!authorized ())
|
||||
{
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// build the path to remove
|
||||
auto const path = buildResolvedPath (m_cwd, args_);
|
||||
if (path.empty ())
|
||||
@ -1885,6 +1935,13 @@ void FtpSession::HELP (char const *args_)
|
||||
|
||||
void FtpSession::LIST (char const *args_)
|
||||
{
|
||||
if (!authorized ())
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// open the path in LIST mode
|
||||
xferDir (args_, XferDirMode::LIST, true);
|
||||
}
|
||||
@ -1892,6 +1949,13 @@ void FtpSession::LIST (char const *args_)
|
||||
void FtpSession::MDTM (char const *args_)
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
|
||||
if (!authorized ())
|
||||
{
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sendResponse ("502 Command not implemented\r\n");
|
||||
}
|
||||
|
||||
@ -1899,6 +1963,12 @@ void FtpSession::MKD (char const *args_)
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
|
||||
if (!authorized ())
|
||||
{
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// build the path to create
|
||||
auto const path = buildResolvedPath (m_cwd, args_);
|
||||
if (path.empty ())
|
||||
@ -1920,6 +1990,13 @@ void FtpSession::MKD (char const *args_)
|
||||
|
||||
void FtpSession::MLSD (char const *args_)
|
||||
{
|
||||
if (!authorized ())
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// open the path in MLSD mode
|
||||
xferDir (args_, XferDirMode::MLSD, true);
|
||||
}
|
||||
@ -1977,6 +2054,13 @@ void FtpSession::MODE (char const *args_)
|
||||
|
||||
void FtpSession::NLST (char const *args_)
|
||||
{
|
||||
if (!authorized ())
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// open the path in NLST mode
|
||||
xferDir (args_, XferDirMode::NLST, false);
|
||||
}
|
||||
@ -2048,11 +2132,34 @@ void FtpSession::OPTS (char const *args_)
|
||||
void FtpSession::PASS (char const *args_)
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
sendResponse ("230 OK\r\n");
|
||||
|
||||
m_authorizedPass = false;
|
||||
|
||||
if (!m_config.user ().empty () && !m_authorizedUser)
|
||||
{
|
||||
sendResponse ("430 User not authorized\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_config.pass ().empty () || m_config.pass () == args_)
|
||||
{
|
||||
m_authorizedPass = true;
|
||||
sendResponse ("230 OK\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sendResponse ("430 Invalid password\r\n");
|
||||
}
|
||||
|
||||
void FtpSession::PASV (char const *args_)
|
||||
{
|
||||
if (!authorized ())
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// reset state
|
||||
setState (State::COMMAND, true, true);
|
||||
m_pasv = false;
|
||||
@ -2117,6 +2224,13 @@ void FtpSession::PASV (char const *args_)
|
||||
|
||||
void FtpSession::PORT (char const *args_)
|
||||
{
|
||||
if (!authorized ())
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// reset state
|
||||
setState (State::COMMAND, true, true);
|
||||
m_pasv = false;
|
||||
@ -2203,6 +2317,12 @@ void FtpSession::PWD (char const *args_)
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
|
||||
if (!authorized ())
|
||||
{
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto const path = encodePath (m_cwd);
|
||||
|
||||
std::string response = "257 \"";
|
||||
@ -2222,6 +2342,12 @@ void FtpSession::REST (char const *args_)
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
|
||||
if (!authorized ())
|
||||
{
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// parse the offset
|
||||
std::uint64_t pos = 0;
|
||||
for (auto p = args_; *p; ++p)
|
||||
@ -2250,6 +2376,13 @@ void FtpSession::REST (char const *args_)
|
||||
|
||||
void FtpSession::RETR (char const *args_)
|
||||
{
|
||||
if (!authorized ())
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// open the file to retrieve
|
||||
xferFile (args_, XferFileMode::RETR);
|
||||
}
|
||||
@ -2258,6 +2391,12 @@ void FtpSession::RMD (char const *args_)
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
|
||||
if (!authorized ())
|
||||
{
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// build the path to remove
|
||||
auto const path = buildResolvedPath (m_cwd, args_);
|
||||
if (path.empty ())
|
||||
@ -2281,6 +2420,12 @@ void FtpSession::RNFR (char const *args_)
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
|
||||
if (!authorized ())
|
||||
{
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// build the path to rename from
|
||||
auto const path = buildResolvedPath (m_cwd, args_);
|
||||
if (path.empty ())
|
||||
@ -2306,6 +2451,12 @@ void FtpSession::RNTO (char const *args_)
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
|
||||
if (!authorized ())
|
||||
{
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure the previous command was RNFR
|
||||
if (m_rename.empty ())
|
||||
{
|
||||
@ -2337,10 +2488,99 @@ void FtpSession::RNTO (char const *args_)
|
||||
sendResponse ("250 OK\r\n");
|
||||
}
|
||||
|
||||
void FtpSession::SITE (char const *args_)
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
|
||||
auto const str = std::string (args_);
|
||||
auto const pos = str.find_first_of (' ');
|
||||
|
||||
auto const command = str.substr (0, pos);
|
||||
auto const arg = pos == std::string::npos ? std::string () : str.substr (pos + 1);
|
||||
|
||||
if (::strcasecmp (command.c_str (), "HELP") == 0)
|
||||
{
|
||||
sendResponse ("211-\r\n"
|
||||
" Show this help: SITE HELP\r\n"
|
||||
" Set username: SITE USER <NAME>\r\n"
|
||||
" Set password: SITE PASS <PASS>\r\n"
|
||||
" Set port: SITE PORT <PORT>\r\n"
|
||||
#ifdef _3DS
|
||||
" Set getMTime: SITE MTIME [0|1]\r\n"
|
||||
#endif
|
||||
" Save config: SITE SAVE\r\n"
|
||||
"211 End\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!authorized ())
|
||||
{
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (::strcasecmp (command.c_str (), "USER") == 0)
|
||||
{
|
||||
m_config.setUser (arg);
|
||||
sendResponse ("200 OK\r\n");
|
||||
return;
|
||||
}
|
||||
else if (::strcasecmp (command.c_str (), "PASS") == 0)
|
||||
{
|
||||
m_config.setPass (arg);
|
||||
sendResponse ("200 OK\r\n");
|
||||
return;
|
||||
}
|
||||
else if (::strcasecmp (command.c_str (), "PORT") == 0)
|
||||
{
|
||||
if (!m_config.setPort (arg))
|
||||
{
|
||||
sendResponse ("550 %s\r\n", std::strerror (errno));
|
||||
return;
|
||||
}
|
||||
|
||||
sendResponse ("200 OK\r\n");
|
||||
return;
|
||||
}
|
||||
#ifdef _3DS
|
||||
else if (::strcasecmp (command.c_str (), "MTIME") == 0)
|
||||
{
|
||||
if (arg == "0")
|
||||
m_config.setGetMTime (false);
|
||||
else if (arg == "1")
|
||||
m_config.setGetMTime (true);
|
||||
else
|
||||
{
|
||||
sendResponse ("550 %s\r\n", std::strerror (EINVAL));
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (::strcasecmp (command.c_str (), "SAVE") == 0)
|
||||
{
|
||||
if (!m_config.save (FTPDCONFIG))
|
||||
{
|
||||
sendResponse ("550 %s\r\n", std::strerror (errno));
|
||||
return;
|
||||
}
|
||||
|
||||
sendResponse ("200 OK\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sendResponse ("550 Invalid command\r\n");
|
||||
}
|
||||
|
||||
void FtpSession::SIZE (char const *args_)
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
|
||||
if (!authorized ())
|
||||
{
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// build the path to stat
|
||||
auto const path = buildResolvedPath (m_cwd, args_);
|
||||
if (path.empty ())
|
||||
@ -2371,7 +2611,7 @@ void FtpSession::STAT (char const *args_)
|
||||
if (m_state == State::DATA_CONNECT)
|
||||
{
|
||||
sendResponse ("211-FTP server status\r\n"
|
||||
" Waitin for data connection\r\n"
|
||||
" Waiting for data connection\r\n"
|
||||
"211 End\r\n");
|
||||
return;
|
||||
}
|
||||
@ -2404,11 +2644,25 @@ void FtpSession::STAT (char const *args_)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!authorized ())
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
xferDir (args_, XferDirMode::STAT, false);
|
||||
}
|
||||
|
||||
void FtpSession::STOR (char const *args_)
|
||||
{
|
||||
if (!authorized ())
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
sendResponse ("530 Not logged in\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// open the file to store
|
||||
xferFile (args_, XferFileMode::STOR);
|
||||
}
|
||||
@ -2450,7 +2704,23 @@ void FtpSession::TYPE (char const *args_)
|
||||
void FtpSession::USER (char const *args_)
|
||||
{
|
||||
setState (State::COMMAND, false, false);
|
||||
sendResponse ("230 OK\r\n");
|
||||
|
||||
m_authorizedUser = false;
|
||||
|
||||
if (m_config.user ().empty () || m_config.user () == args_)
|
||||
{
|
||||
m_authorizedUser = true;
|
||||
if (m_config.pass ().empty ())
|
||||
{
|
||||
sendResponse ("230 OK\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sendResponse ("331 Need password\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sendResponse ("502 Invalid user\r\n");
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
@ -2484,6 +2754,7 @@ std::vector<std::pair<std::string_view, void (FtpSession::*) (char const *)>> co
|
||||
{"RMD", &FtpSession::RMD},
|
||||
{"RNFR", &FtpSession::RNFR},
|
||||
{"RNTO", &FtpSession::RNTO},
|
||||
{"SITE", &FtpSession::SITE},
|
||||
{"SIZE", &FtpSession::SIZE},
|
||||
{"STAT", &FtpSession::STAT},
|
||||
{"STOR", &FtpSession::STOR},
|
||||
|
@ -49,7 +49,7 @@ int main (int argc_, char *argv_[])
|
||||
style.WindowRounding = 0.0f;
|
||||
#endif
|
||||
|
||||
auto server = FtpServer::create (5000);
|
||||
auto server = FtpServer::create ();
|
||||
|
||||
while (platform::loop ())
|
||||
{
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#include "../imgui/imgui_internal.h"
|
||||
|
||||
#include "fs.h"
|
||||
#include "platform.h"
|
||||
|
||||
@ -1376,6 +1378,46 @@ void updateKeyboard (ImGuiIO &io_)
|
||||
|
||||
for (int i = 0; i < 256; ++i)
|
||||
io_.KeysDown[i] = hidKeyboardHeld (static_cast<HidKeyboardScancode> (i));
|
||||
|
||||
static enum {
|
||||
INACTIVE,
|
||||
KEYBOARD,
|
||||
CLEARED,
|
||||
} state = INACTIVE;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case INACTIVE:
|
||||
{
|
||||
if (!io_.WantTextInput)
|
||||
return;
|
||||
|
||||
auto &textState = ImGui::GetCurrentContext ()->InputTextState;
|
||||
|
||||
SwkbdConfig kbd;
|
||||
swkbdCreate (&kbd, 0);
|
||||
swkbdConfigMakePresetDefault (&kbd);
|
||||
swkbdConfigSetInitialText (
|
||||
&kbd, std::string (textState.InitialTextA.Data, textState.InitialTextA.Size).c_str ());
|
||||
|
||||
char buffer[32];
|
||||
if (R_SUCCEEDED (swkbdShow (&kbd, buffer, sizeof (buffer))))
|
||||
io_.AddInputCharactersUTF8 (buffer);
|
||||
|
||||
state = KEYBOARD;
|
||||
break;
|
||||
}
|
||||
|
||||
case KEYBOARD:
|
||||
// need to skip a frame for active id to really be cleared
|
||||
ImGui::ClearActiveID ();
|
||||
state = CLEARED;
|
||||
break;
|
||||
|
||||
case CLEARED:
|
||||
state = INACTIVE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ static SocketInitConfig const s_socketInitConfig = {
|
||||
|
||||
.sb_efficiency = 8,
|
||||
|
||||
.num_bsd_sessions = 1,
|
||||
.num_bsd_sessions = 2,
|
||||
.bsd_service_type = BsdServiceType_User,
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user