mirror of
https://github.com/wiiu-env/ftpiiu_plugin.git
synced 2024-11-25 22:24:17 +01:00
Add configuration
This commit is contained in:
parent
d9965f33df
commit
3f31371193
@ -21,6 +21,7 @@ AlwaysBreakTemplateDeclarations: true
|
|||||||
BinPackArguments: false
|
BinPackArguments: false
|
||||||
BinPackParameters: false
|
BinPackParameters: false
|
||||||
BraceWrapping:
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: true
|
||||||
AfterClass: true
|
AfterClass: true
|
||||||
AfterControlStatement: true
|
AfterControlStatement: true
|
||||||
AfterEnum: true
|
AfterEnum: true
|
||||||
|
@ -71,7 +71,7 @@ CFLAGS := -g -Wall $(OPTIMIZE) -mword-relocations \
|
|||||||
CFLAGS += $(INCLUDE) -DARM11 -D_3DS \
|
CFLAGS += $(INCLUDE) -DARM11 -D_3DS \
|
||||||
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
||||||
-DIMGUI_DISABLE_INCLUDE_IMCONFIG_H=1 \
|
-DIMGUI_DISABLE_INCLUDE_IMCONFIG_H=1 \
|
||||||
-DNO_IPV6
|
-DNO_IPV6 -DFTPDCONFIG="\"/config/ftpd/ftpd.cfg\""
|
||||||
|
|
||||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
|
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` \
|
`pkg-config --cflags gl glfw3` \
|
||||||
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
||||||
-DIMGUI_DISABLE_INCLUDE_IMCONFIG_H=1 \
|
-DIMGUI_DISABLE_INCLUDE_IMCONFIG_H=1 \
|
||||||
-DIMGUI_IMPL_OPENGL_LOADER_GLAD=1
|
-DIMGUI_IMPL_OPENGL_LOADER_GLAD=1 \
|
||||||
|
-DFTPDCONFIG="\"ftpd.cfg\""
|
||||||
CFLAGS := $(CPPFLAGS)
|
CFLAGS := $(CPPFLAGS)
|
||||||
CXXFLAGS := $(CPPFLAGS) -std=gnu++17
|
CXXFLAGS := $(CPPFLAGS) -std=gnu++17
|
||||||
LDFLAGS := -pthread `pkg-config --libs gl glfw3` -ldl
|
LDFLAGS := -pthread `pkg-config --libs gl glfw3` -ldl
|
||||||
|
@ -47,7 +47,7 @@ CFLAGS := -g -Wall $(OPTIMIZE) \
|
|||||||
$(ARCH) $(INCLUDE) -DARM9 -DNDS \
|
$(ARCH) $(INCLUDE) -DARM9 -DNDS \
|
||||||
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
||||||
-DIMGUI_DISABLE_INCLUDE_IMCONFIG_H=1 \
|
-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
|
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
|
||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) $(OPTIMIZE)
|
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__ \
|
CFLAGS += $(INCLUDE) -D__SWITCH__ \
|
||||||
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
||||||
-DIMGUI_DISABLE_INCLUDE_IMCONFIG_H=1 \
|
-DIMGUI_DISABLE_INCLUDE_IMCONFIG_H=1 \
|
||||||
|
-DFTPDCONFIG="\"/config/ftpd/ftpd.cfg\"" \
|
||||||
`$(PREFIX)pkg-config --cflags libzstd`
|
`$(PREFIX)pkg-config --cflags libzstd`
|
||||||
|
|
||||||
CXXFLAGS := $(CFLAGS) -std=gnu++17 -fno-exceptions -fno-rtti
|
CXXFLAGS := $(CFLAGS) -std=gnu++17 -fno-exceptions -fno-rtti
|
||||||
|
10
README.md
10
README.md
@ -105,6 +105,7 @@ Build `switch/ftpd.nro`:
|
|||||||
- RMD
|
- RMD
|
||||||
- RNFR
|
- RNFR
|
||||||
- RNTO
|
- RNTO
|
||||||
|
- SITE
|
||||||
- SIZE
|
- SIZE
|
||||||
- STAT
|
- STAT
|
||||||
- STOR
|
- STOR
|
||||||
@ -121,3 +122,12 @@ Build `switch/ftpd.nro`:
|
|||||||
## Planned Commands
|
## Planned Commands
|
||||||
|
|
||||||
- STOU
|
- 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 <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
namespace fs
|
namespace fs
|
||||||
{
|
{
|
||||||
@ -82,6 +83,9 @@ public:
|
|||||||
/// \note Can return partial reads
|
/// \note Can return partial reads
|
||||||
ssize_t read (void *data_, std::size_t size_);
|
ssize_t read (void *data_, std::size_t size_);
|
||||||
|
|
||||||
|
/// \brief Read line
|
||||||
|
std::string_view readLine ();
|
||||||
|
|
||||||
/// \brief Read data
|
/// \brief Read data
|
||||||
/// \param data_ Output buffer
|
/// \param data_ Output buffer
|
||||||
/// \param size_ Size to read
|
/// \param size_ Size to read
|
||||||
@ -109,6 +113,12 @@ private:
|
|||||||
|
|
||||||
/// \brief Buffer size
|
/// \brief Buffer size
|
||||||
std::size_t m_bufferSize = 0;
|
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
|
/// 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
|
#pragma once
|
||||||
|
|
||||||
|
#include "ftpConfig.h"
|
||||||
#include "ftpSession.h"
|
#include "ftpSession.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
@ -44,8 +45,7 @@ public:
|
|||||||
void draw ();
|
void draw ();
|
||||||
|
|
||||||
/// \brief Create server
|
/// \brief Create server
|
||||||
/// \param port_ Port to listen on
|
static UniqueFtpServer create ();
|
||||||
static UniqueFtpServer create (std::uint16_t port_);
|
|
||||||
|
|
||||||
/// \brief Get free space
|
/// \brief Get free space
|
||||||
static std::string getFreeSpace ();
|
static std::string getFreeSpace ();
|
||||||
@ -58,8 +58,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/// \brief Paramterized constructor
|
/// \brief Paramterized constructor
|
||||||
/// \param port_ Port to listen on
|
/// \param config_ FTP config
|
||||||
FtpServer (std::uint16_t port_);
|
FtpServer (UniqueFtpConfig config_);
|
||||||
|
|
||||||
/// \brief Handle when network is found
|
/// \brief Handle when network is found
|
||||||
void handleNetworkFound ();
|
void handleNetworkFound ();
|
||||||
@ -67,6 +67,9 @@ private:
|
|||||||
/// \brief Handle when network is lost
|
/// \brief Handle when network is lost
|
||||||
void handleNetworkLost ();
|
void handleNetworkLost ();
|
||||||
|
|
||||||
|
/// \brief Show menu in the current window
|
||||||
|
void showMenu ();
|
||||||
|
|
||||||
/// \brief Server loop
|
/// \brief Server loop
|
||||||
void loop ();
|
void loop ();
|
||||||
|
|
||||||
@ -81,6 +84,9 @@ private:
|
|||||||
platform::Mutex m_lock;
|
platform::Mutex m_lock;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// \brief Config
|
||||||
|
UniqueFtpConfig m_config;
|
||||||
|
|
||||||
/// \brief Listen socket
|
/// \brief Listen socket
|
||||||
UniqueSocket m_socket;
|
UniqueSocket m_socket;
|
||||||
|
|
||||||
@ -90,9 +96,25 @@ private:
|
|||||||
/// \brief Sessions
|
/// \brief Sessions
|
||||||
std::vector<UniqueFtpSession> m_sessions;
|
std::vector<UniqueFtpSession> m_sessions;
|
||||||
|
|
||||||
/// \brief Port to listen on
|
|
||||||
std::uint16_t const m_port;
|
|
||||||
|
|
||||||
/// \brief Whether thread should quit
|
/// \brief Whether thread should quit
|
||||||
std::atomic<bool> m_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
|
#pragma once
|
||||||
|
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
|
#include "ftpConfig.h"
|
||||||
#include "ioBuffer.h"
|
#include "ioBuffer.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
@ -47,8 +48,9 @@ public:
|
|||||||
void draw ();
|
void draw ();
|
||||||
|
|
||||||
/// \brief Create session
|
/// \brief Create session
|
||||||
|
/// \param config_ FTP config
|
||||||
/// \param commandSocket_ Command socket
|
/// \param commandSocket_ Command socket
|
||||||
static UniqueFtpSession create (UniqueSocket commandSocket_);
|
static UniqueFtpSession create (FtpConfig &config_, UniqueSocket commandSocket_);
|
||||||
|
|
||||||
/// \brief Poll for activity
|
/// \brief Poll for activity
|
||||||
/// \param sessions_ Sessions to poll
|
/// \param sessions_ Sessions to poll
|
||||||
@ -122,8 +124,12 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Parameterized constructor
|
/// \brief Parameterized constructor
|
||||||
|
/// \param config_ FTP config
|
||||||
/// \param commandSocket_ Command socket
|
/// \param commandSocket_ Command socket
|
||||||
FtpSession (UniqueSocket commandSocket_);
|
FtpSession (FtpConfig &config_, UniqueSocket commandSocket_);
|
||||||
|
|
||||||
|
/// \brief Whether session is authorized
|
||||||
|
bool authorized () const;
|
||||||
|
|
||||||
/// \brief Set session state
|
/// \brief Set session state
|
||||||
/// \param state_ State to set
|
/// \param state_ State to set
|
||||||
@ -203,6 +209,9 @@ private:
|
|||||||
platform::Mutex m_lock;
|
platform::Mutex m_lock;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// \brief FTP config
|
||||||
|
FtpConfig &m_config;
|
||||||
|
|
||||||
/// \brief Command socket
|
/// \brief Command socket
|
||||||
SharedSocket m_commandSocket;
|
SharedSocket m_commandSocket;
|
||||||
|
|
||||||
@ -278,6 +287,13 @@ private:
|
|||||||
/// \brief Directory transfer mode
|
/// \brief Directory transfer mode
|
||||||
XferDirMode m_xferDirMode;
|
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
|
/// \brief Whether previous command was PASV
|
||||||
bool m_pasv : 1;
|
bool m_pasv : 1;
|
||||||
/// \brief Whether previous command was PORT
|
/// \brief Whether previous command was PORT
|
||||||
@ -409,6 +425,10 @@ private:
|
|||||||
/// \param args_ Command arguments
|
/// \param args_ Command arguments
|
||||||
void RNTO (char const *args_);
|
void RNTO (char const *args_);
|
||||||
|
|
||||||
|
/// \brief Site command
|
||||||
|
/// \param args_ Command arguments
|
||||||
|
void SITE (char const *args_);
|
||||||
|
|
||||||
/// \brief Get file size
|
/// \brief Get file size
|
||||||
/// \param args_ Command arguments
|
/// \param args_ Command arguments
|
||||||
void SIZE (char const *args_);
|
void SIZE (char const *args_);
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
|
||||||
|
#include "../imgui/imgui_internal.h"
|
||||||
|
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
#include "platform.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);
|
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 ()
|
bool imgui::ctru::init ()
|
||||||
@ -180,5 +233,6 @@ void imgui::ctru::newFrame ()
|
|||||||
|
|
||||||
updateTouch (io);
|
updateTouch (io);
|
||||||
updateGamepads (io);
|
updateGamepads (io);
|
||||||
|
updateKeyboard (io);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -23,6 +23,10 @@
|
|||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
|
#if defined(NDS) || defined(_3DS) || defined(__SWITCH__)
|
||||||
|
#define getline __getline
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string fs::printSize (std::uint64_t const size_)
|
std::string fs::printSize (std::uint64_t const size_)
|
||||||
{
|
{
|
||||||
constexpr std::uint64_t const KiB = 1024;
|
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;
|
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 ());
|
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_)
|
bool fs::File::readAll (void *const data_, std::size_t const size_)
|
||||||
{
|
{
|
||||||
auto p = static_cast<char *> (data_);
|
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
|
#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
|
#ifndef NDS
|
||||||
m_thread = platform::Thread (std::bind (&FtpServer::threadFunc, this));
|
m_thread = platform::Thread (std::bind (&FtpServer::threadFunc, this));
|
||||||
@ -172,12 +172,20 @@ void FtpServer::draw ()
|
|||||||
|
|
||||||
ImGui::Begin (title,
|
ImGui::Begin (title,
|
||||||
nullptr,
|
nullptr,
|
||||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
|
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize
|
||||||
|
#ifndef _3DS
|
||||||
|
| ImGuiWindowFlags_MenuBar
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef _3DS
|
||||||
|
showMenu ();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef _3DS
|
#ifndef _3DS
|
||||||
ImGui::BeginChild (
|
ImGui::BeginChild (
|
||||||
"Logs", ImVec2 (0, 0.55f * height), false, ImGuiWindowFlags_HorizontalScrollbar);
|
"Logs", ImVec2 (0, 0.5f * height), false, ImGuiWindowFlags_HorizontalScrollbar);
|
||||||
#endif
|
#endif
|
||||||
drawLog ();
|
drawLog ();
|
||||||
#ifndef _3DS
|
#ifndef _3DS
|
||||||
@ -192,7 +200,10 @@ void FtpServer::draw ()
|
|||||||
ImGui::SetNextWindowPos (ImVec2 (width * 0.1f, height * 0.5f), ImGuiCond_FirstUseEver);
|
ImGui::SetNextWindowPos (ImVec2 (width * 0.1f, height * 0.5f), ImGuiCond_FirstUseEver);
|
||||||
ImGui::Begin ("Sessions",
|
ImGui::Begin ("Sessions",
|
||||||
nullptr,
|
nullptr,
|
||||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
|
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
|
||||||
|
ImGuiWindowFlags_MenuBar);
|
||||||
|
|
||||||
|
showMenu ();
|
||||||
#else
|
#else
|
||||||
ImGui::Separator ();
|
ImGui::Separator ();
|
||||||
#endif
|
#endif
|
||||||
@ -207,10 +218,13 @@ void FtpServer::draw ()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
UniqueFtpServer FtpServer::create (std::uint16_t const port_)
|
UniqueFtpServer FtpServer::create ()
|
||||||
{
|
{
|
||||||
updateFreeSpace ();
|
updateFreeSpace ();
|
||||||
return UniqueFtpServer (new FtpServer (port_));
|
|
||||||
|
auto config = FtpConfig::load (FTPDCONFIG);
|
||||||
|
|
||||||
|
return UniqueFtpServer (new FtpServer (std::move (config)));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FtpServer::getFreeSpace ()
|
std::string FtpServer::getFreeSpace ()
|
||||||
@ -256,13 +270,13 @@ void FtpServer::handleNetworkFound ()
|
|||||||
#else
|
#else
|
||||||
addr.sin_addr.s_addr = INADDR_ANY;
|
addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
#endif
|
#endif
|
||||||
addr.sin_port = htons (m_port);
|
addr.sin_port = htons (m_config->port ());
|
||||||
|
|
||||||
auto socket = Socket::create ();
|
auto socket = Socket::create ();
|
||||||
if (!socket)
|
if (!socket)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_port != 0 && !socket->setReuseAddress (true))
|
if (m_config->port () != 0 && !socket->setReuseAddress (true))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!socket->bind (addr))
|
if (!socket->bind (addr))
|
||||||
@ -292,6 +306,115 @@ void FtpServer::handleNetworkLost ()
|
|||||||
info ("Stopped server at %s\n", m_name.c_str ());
|
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 ()
|
void FtpServer::loop ()
|
||||||
{
|
{
|
||||||
if (!m_socket)
|
if (!m_socket)
|
||||||
@ -309,7 +432,7 @@ void FtpServer::loop ()
|
|||||||
auto socket = m_socket->accept ();
|
auto socket = m_socket->accept ();
|
||||||
if (socket)
|
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)));
|
LOCKED (m_sessions.emplace_back (std::move (session)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -262,11 +262,14 @@ FtpSession::~FtpSession ()
|
|||||||
closeData ();
|
closeData ();
|
||||||
}
|
}
|
||||||
|
|
||||||
FtpSession::FtpSession (UniqueSocket commandSocket_)
|
FtpSession::FtpSession (FtpConfig &config_, UniqueSocket commandSocket_)
|
||||||
: m_commandSocket (std::move (commandSocket_)),
|
: m_config (config_),
|
||||||
|
m_commandSocket (std::move (commandSocket_)),
|
||||||
m_commandBuffer (COMMAND_BUFFERSIZE),
|
m_commandBuffer (COMMAND_BUFFERSIZE),
|
||||||
m_responseBuffer (RESPONSE_BUFFERSIZE),
|
m_responseBuffer (RESPONSE_BUFFERSIZE),
|
||||||
m_xferBuffer (XFER_BUFFERSIZE),
|
m_xferBuffer (XFER_BUFFERSIZE),
|
||||||
|
m_authorizedUser (false),
|
||||||
|
m_authorizedPass (false),
|
||||||
m_pasv (false),
|
m_pasv (false),
|
||||||
m_port (false),
|
m_port (false),
|
||||||
m_recv (false),
|
m_recv (false),
|
||||||
@ -278,6 +281,11 @@ FtpSession::FtpSession (UniqueSocket commandSocket_)
|
|||||||
m_mlstPerm (true),
|
m_mlstPerm (true),
|
||||||
m_mlstUnixMode (false)
|
m_mlstUnixMode (false)
|
||||||
{
|
{
|
||||||
|
if (m_config.user ().empty ())
|
||||||
|
m_authorizedUser = true;
|
||||||
|
if (m_config.pass ().empty ())
|
||||||
|
m_authorizedPass = true;
|
||||||
|
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
std::sprintf (buffer, "Session#%p", this);
|
std::sprintf (buffer, "Session#%p", this);
|
||||||
m_windowName = buffer;
|
m_windowName = buffer;
|
||||||
@ -373,9 +381,9 @@ void FtpSession::draw ()
|
|||||||
#endif
|
#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_)
|
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))
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FtpSession::authorized () const
|
||||||
|
{
|
||||||
|
return m_authorizedUser && m_authorizedPass;
|
||||||
|
}
|
||||||
|
|
||||||
void FtpSession::setState (State const state_, bool const closePasv_, bool const closeData_)
|
void FtpSession::setState (State const state_, bool const closePasv_, bool const closeData_)
|
||||||
{
|
{
|
||||||
m_state = state_;
|
m_state = state_;
|
||||||
@ -989,8 +1005,10 @@ int FtpSession::fillDirent (struct stat const &st_, std::string_view const path_
|
|||||||
if (!tm)
|
if (!tm)
|
||||||
return errno;
|
return errno;
|
||||||
|
|
||||||
auto fmt = "%b %e %H:%M ";
|
auto fmt = "%b %e %Y ";
|
||||||
rc = std::strftime (&buffer[pos], size - pos, fmt, tm);
|
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)
|
if (rc < 0)
|
||||||
return errno;
|
return errno;
|
||||||
if (static_cast<std::size_t> (rc) > size - pos)
|
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';
|
buffer[pos++] = '\n';
|
||||||
|
|
||||||
m_xferBuffer.markUsed (pos);
|
m_xferBuffer.markUsed (pos);
|
||||||
|
LOCKED (m_filePosition += pos);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1145,6 +1164,7 @@ void FtpSession::xferDir (char const *const args_, XferDirMode const mode_, bool
|
|||||||
m_recv = false;
|
m_recv = false;
|
||||||
m_send = true;
|
m_send = true;
|
||||||
|
|
||||||
|
m_filePosition = 0;
|
||||||
m_xferBuffer.clear ();
|
m_xferBuffer.clear ();
|
||||||
|
|
||||||
m_transfer = &FtpSession::listTransfer;
|
m_transfer = &FtpSession::listTransfer;
|
||||||
@ -1233,7 +1253,7 @@ void FtpSession::xferDir (char const *const args_, XferDirMode const mode_, bool
|
|||||||
// everything else uses basename
|
// everything else uses basename
|
||||||
auto const pos = path.find_last_of ('/');
|
auto const pos = path.find_last_of ('/');
|
||||||
assert (pos != std::string::npos);
|
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);
|
auto const rc = fillDirent (st, name);
|
||||||
@ -1420,6 +1440,7 @@ void FtpSession::readCommand (int const events_)
|
|||||||
return ::strcasecmp (lhs_.first.data (), rhs_) < 0;
|
return ::strcasecmp (lhs_.first.data (), rhs_) < 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_timestamp = std::time (nullptr);
|
||||||
if (it == std::end (handlers) || ::strcasecmp (it->first.data (), command) != 0)
|
if (it == std::end (handlers) || ::strcasecmp (it->first.data (), command) != 0)
|
||||||
{
|
{
|
||||||
std::string response = "502 Invalid command \"";
|
std::string response = "502 Invalid command \"";
|
||||||
@ -1585,13 +1606,17 @@ bool FtpSession::listTransfer ()
|
|||||||
if (m_xferDirMode == XferDirMode::NLST)
|
if (m_xferDirMode == XferDirMode::NLST)
|
||||||
{
|
{
|
||||||
// NLST gives the whole path name
|
// 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 ())
|
if (m_xferBuffer.freeSize () < path.size ())
|
||||||
{
|
{
|
||||||
sendResponse ("501 %s\r\n", std::strerror (ENOMEM));
|
sendResponse ("501 %s\r\n", std::strerror (ENOMEM));
|
||||||
setState (State::COMMAND, true, true);
|
setState (State::COMMAND, true, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::memcpy (m_xferBuffer.freeArea (), path.data (), path.size ());
|
||||||
|
m_xferBuffer.markUsed (path.size ());
|
||||||
|
LOCKED (m_filePosition += path.size ());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1629,7 +1654,7 @@ bool FtpSession::listTransfer ()
|
|||||||
else if (m_xferDirMode == XferDirMode::NLST)
|
else if (m_xferDirMode == XferDirMode::NLST)
|
||||||
getmtime = false;
|
getmtime = false;
|
||||||
|
|
||||||
if (getmtime)
|
if (getmtime && m_config.getMTime ())
|
||||||
{
|
{
|
||||||
std::uint64_t mtime = 0;
|
std::uint64_t mtime = 0;
|
||||||
auto const rc = archive_getmtime (fullPath.c_str (), &mtime);
|
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_)
|
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
|
// open the file in append mode
|
||||||
xferFile (args_, XferFileMode::APPE);
|
xferFile (args_, XferFileMode::APPE);
|
||||||
}
|
}
|
||||||
@ -1809,6 +1841,12 @@ void FtpSession::CDUP (char const *args_)
|
|||||||
{
|
{
|
||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
|
if (!authorized ())
|
||||||
|
{
|
||||||
|
sendResponse ("530 Not logged in\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!changeDir (".."))
|
if (!changeDir (".."))
|
||||||
{
|
{
|
||||||
sendResponse ("550 %s\r\n", std::strerror (errno));
|
sendResponse ("550 %s\r\n", std::strerror (errno));
|
||||||
@ -1822,6 +1860,12 @@ void FtpSession::CWD (char const *args_)
|
|||||||
{
|
{
|
||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
|
if (!authorized ())
|
||||||
|
{
|
||||||
|
sendResponse ("530 Not logged in\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!changeDir (args_))
|
if (!changeDir (args_))
|
||||||
{
|
{
|
||||||
sendResponse ("550 %s\r\n", std::strerror (errno));
|
sendResponse ("550 %s\r\n", std::strerror (errno));
|
||||||
@ -1835,6 +1879,12 @@ void FtpSession::DELE (char const *args_)
|
|||||||
{
|
{
|
||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
|
if (!authorized ())
|
||||||
|
{
|
||||||
|
sendResponse ("530 Not logged in\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// build the path to remove
|
// build the path to remove
|
||||||
auto const path = buildResolvedPath (m_cwd, args_);
|
auto const path = buildResolvedPath (m_cwd, args_);
|
||||||
if (path.empty ())
|
if (path.empty ())
|
||||||
@ -1885,6 +1935,13 @@ void FtpSession::HELP (char const *args_)
|
|||||||
|
|
||||||
void FtpSession::LIST (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
|
// open the path in LIST mode
|
||||||
xferDir (args_, XferDirMode::LIST, true);
|
xferDir (args_, XferDirMode::LIST, true);
|
||||||
}
|
}
|
||||||
@ -1892,6 +1949,13 @@ void FtpSession::LIST (char const *args_)
|
|||||||
void FtpSession::MDTM (char const *args_)
|
void FtpSession::MDTM (char const *args_)
|
||||||
{
|
{
|
||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
|
if (!authorized ())
|
||||||
|
{
|
||||||
|
sendResponse ("530 Not logged in\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
sendResponse ("502 Command not implemented\r\n");
|
sendResponse ("502 Command not implemented\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1899,6 +1963,12 @@ void FtpSession::MKD (char const *args_)
|
|||||||
{
|
{
|
||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
|
if (!authorized ())
|
||||||
|
{
|
||||||
|
sendResponse ("530 Not logged in\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// build the path to create
|
// build the path to create
|
||||||
auto const path = buildResolvedPath (m_cwd, args_);
|
auto const path = buildResolvedPath (m_cwd, args_);
|
||||||
if (path.empty ())
|
if (path.empty ())
|
||||||
@ -1920,6 +1990,13 @@ void FtpSession::MKD (char const *args_)
|
|||||||
|
|
||||||
void FtpSession::MLSD (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
|
// open the path in MLSD mode
|
||||||
xferDir (args_, XferDirMode::MLSD, true);
|
xferDir (args_, XferDirMode::MLSD, true);
|
||||||
}
|
}
|
||||||
@ -1977,6 +2054,13 @@ void FtpSession::MODE (char const *args_)
|
|||||||
|
|
||||||
void FtpSession::NLST (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
|
// open the path in NLST mode
|
||||||
xferDir (args_, XferDirMode::NLST, false);
|
xferDir (args_, XferDirMode::NLST, false);
|
||||||
}
|
}
|
||||||
@ -2048,11 +2132,34 @@ void FtpSession::OPTS (char const *args_)
|
|||||||
void FtpSession::PASS (char const *args_)
|
void FtpSession::PASS (char const *args_)
|
||||||
{
|
{
|
||||||
setState (State::COMMAND, false, false);
|
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_)
|
void FtpSession::PASV (char const *args_)
|
||||||
{
|
{
|
||||||
|
if (!authorized ())
|
||||||
|
{
|
||||||
|
setState (State::COMMAND, false, false);
|
||||||
|
sendResponse ("530 Not logged in\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// reset state
|
// reset state
|
||||||
setState (State::COMMAND, true, true);
|
setState (State::COMMAND, true, true);
|
||||||
m_pasv = false;
|
m_pasv = false;
|
||||||
@ -2117,6 +2224,13 @@ void FtpSession::PASV (char const *args_)
|
|||||||
|
|
||||||
void FtpSession::PORT (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
|
// reset state
|
||||||
setState (State::COMMAND, true, true);
|
setState (State::COMMAND, true, true);
|
||||||
m_pasv = false;
|
m_pasv = false;
|
||||||
@ -2203,6 +2317,12 @@ void FtpSession::PWD (char const *args_)
|
|||||||
{
|
{
|
||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
|
if (!authorized ())
|
||||||
|
{
|
||||||
|
sendResponse ("530 Not logged in\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto const path = encodePath (m_cwd);
|
auto const path = encodePath (m_cwd);
|
||||||
|
|
||||||
std::string response = "257 \"";
|
std::string response = "257 \"";
|
||||||
@ -2222,6 +2342,12 @@ void FtpSession::REST (char const *args_)
|
|||||||
{
|
{
|
||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
|
if (!authorized ())
|
||||||
|
{
|
||||||
|
sendResponse ("530 Not logged in\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// parse the offset
|
// parse the offset
|
||||||
std::uint64_t pos = 0;
|
std::uint64_t pos = 0;
|
||||||
for (auto p = args_; *p; ++p)
|
for (auto p = args_; *p; ++p)
|
||||||
@ -2250,6 +2376,13 @@ void FtpSession::REST (char const *args_)
|
|||||||
|
|
||||||
void FtpSession::RETR (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
|
// open the file to retrieve
|
||||||
xferFile (args_, XferFileMode::RETR);
|
xferFile (args_, XferFileMode::RETR);
|
||||||
}
|
}
|
||||||
@ -2258,6 +2391,12 @@ void FtpSession::RMD (char const *args_)
|
|||||||
{
|
{
|
||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
|
if (!authorized ())
|
||||||
|
{
|
||||||
|
sendResponse ("530 Not logged in\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// build the path to remove
|
// build the path to remove
|
||||||
auto const path = buildResolvedPath (m_cwd, args_);
|
auto const path = buildResolvedPath (m_cwd, args_);
|
||||||
if (path.empty ())
|
if (path.empty ())
|
||||||
@ -2281,6 +2420,12 @@ void FtpSession::RNFR (char const *args_)
|
|||||||
{
|
{
|
||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
|
if (!authorized ())
|
||||||
|
{
|
||||||
|
sendResponse ("530 Not logged in\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// build the path to rename from
|
// build the path to rename from
|
||||||
auto const path = buildResolvedPath (m_cwd, args_);
|
auto const path = buildResolvedPath (m_cwd, args_);
|
||||||
if (path.empty ())
|
if (path.empty ())
|
||||||
@ -2306,6 +2451,12 @@ void FtpSession::RNTO (char const *args_)
|
|||||||
{
|
{
|
||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
|
if (!authorized ())
|
||||||
|
{
|
||||||
|
sendResponse ("530 Not logged in\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// make sure the previous command was RNFR
|
// make sure the previous command was RNFR
|
||||||
if (m_rename.empty ())
|
if (m_rename.empty ())
|
||||||
{
|
{
|
||||||
@ -2337,10 +2488,99 @@ void FtpSession::RNTO (char const *args_)
|
|||||||
sendResponse ("250 OK\r\n");
|
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_)
|
void FtpSession::SIZE (char const *args_)
|
||||||
{
|
{
|
||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
|
if (!authorized ())
|
||||||
|
{
|
||||||
|
sendResponse ("530 Not logged in\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// build the path to stat
|
// build the path to stat
|
||||||
auto const path = buildResolvedPath (m_cwd, args_);
|
auto const path = buildResolvedPath (m_cwd, args_);
|
||||||
if (path.empty ())
|
if (path.empty ())
|
||||||
@ -2371,7 +2611,7 @@ void FtpSession::STAT (char const *args_)
|
|||||||
if (m_state == State::DATA_CONNECT)
|
if (m_state == State::DATA_CONNECT)
|
||||||
{
|
{
|
||||||
sendResponse ("211-FTP server status\r\n"
|
sendResponse ("211-FTP server status\r\n"
|
||||||
" Waitin for data connection\r\n"
|
" Waiting for data connection\r\n"
|
||||||
"211 End\r\n");
|
"211 End\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2404,11 +2644,25 @@ void FtpSession::STAT (char const *args_)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!authorized ())
|
||||||
|
{
|
||||||
|
setState (State::COMMAND, false, false);
|
||||||
|
sendResponse ("530 Not logged in\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
xferDir (args_, XferDirMode::STAT, false);
|
xferDir (args_, XferDirMode::STAT, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FtpSession::STOR (char const *args_)
|
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
|
// open the file to store
|
||||||
xferFile (args_, XferFileMode::STOR);
|
xferFile (args_, XferFileMode::STOR);
|
||||||
}
|
}
|
||||||
@ -2450,7 +2704,23 @@ void FtpSession::TYPE (char const *args_)
|
|||||||
void FtpSession::USER (char const *args_)
|
void FtpSession::USER (char const *args_)
|
||||||
{
|
{
|
||||||
setState (State::COMMAND, false, false);
|
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
|
// clang-format off
|
||||||
@ -2484,6 +2754,7 @@ std::vector<std::pair<std::string_view, void (FtpSession::*) (char const *)>> co
|
|||||||
{"RMD", &FtpSession::RMD},
|
{"RMD", &FtpSession::RMD},
|
||||||
{"RNFR", &FtpSession::RNFR},
|
{"RNFR", &FtpSession::RNFR},
|
||||||
{"RNTO", &FtpSession::RNTO},
|
{"RNTO", &FtpSession::RNTO},
|
||||||
|
{"SITE", &FtpSession::SITE},
|
||||||
{"SIZE", &FtpSession::SIZE},
|
{"SIZE", &FtpSession::SIZE},
|
||||||
{"STAT", &FtpSession::STAT},
|
{"STAT", &FtpSession::STAT},
|
||||||
{"STOR", &FtpSession::STOR},
|
{"STOR", &FtpSession::STOR},
|
||||||
|
@ -49,7 +49,7 @@ int main (int argc_, char *argv_[])
|
|||||||
style.WindowRounding = 0.0f;
|
style.WindowRounding = 0.0f;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto server = FtpServer::create (5000);
|
auto server = FtpServer::create ();
|
||||||
|
|
||||||
while (platform::loop ())
|
while (platform::loop ())
|
||||||
{
|
{
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
|
||||||
|
#include "../imgui/imgui_internal.h"
|
||||||
|
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
@ -1376,6 +1378,46 @@ void updateKeyboard (ImGuiIO &io_)
|
|||||||
|
|
||||||
for (int i = 0; i < 256; ++i)
|
for (int i = 0; i < 256; ++i)
|
||||||
io_.KeysDown[i] = hidKeyboardHeld (static_cast<HidKeyboardScancode> (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,
|
.sb_efficiency = 8,
|
||||||
|
|
||||||
.num_bsd_sessions = 1,
|
.num_bsd_sessions = 2,
|
||||||
.bsd_service_type = BsdServiceType_User,
|
.bsd_service_type = BsdServiceType_User,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user