mirror of
https://github.com/mtheall/ftpd.git
synced 2025-06-18 19:15:33 -04:00
Fix static analysis reports
This commit is contained in:
parent
9a73b92497
commit
41e467f47b
@ -2,6 +2,18 @@ cmake_minimum_required(VERSION 3.12)
|
|||||||
|
|
||||||
project(ftpd VERSION 3.2.0)
|
project(ftpd VERSION 3.2.0)
|
||||||
|
|
||||||
|
if(CMAKE_EXPORT_COMPILE_COMMANDS)
|
||||||
|
list(APPEND CMAKE_C_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES})
|
||||||
|
list(APPEND CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(gsl
|
||||||
|
GIT_REPOSITORY https://github.com/microsoft/GSL.git
|
||||||
|
GIT_TAG v4.0.0
|
||||||
|
)
|
||||||
|
FetchContent_Populate(gsl)
|
||||||
|
|
||||||
option(FTPD_CLASSIC "Build ${PROJECT_NAME} classic" OFF)
|
option(FTPD_CLASSIC "Build ${PROJECT_NAME} classic" OFF)
|
||||||
|
|
||||||
if(FTPD_CLASSIC AND (NINTENDO_SWITCH OR NINTENDO_3DS))
|
if(FTPD_CLASSIC AND (NINTENDO_SWITCH OR NINTENDO_3DS))
|
||||||
@ -12,6 +24,8 @@ endif()
|
|||||||
|
|
||||||
add_executable(${FTPD_TARGET})
|
add_executable(${FTPD_TARGET})
|
||||||
|
|
||||||
|
target_include_directories(${FTPD_TARGET} PRIVATE ${gsl_SOURCE_DIR}/include)
|
||||||
|
|
||||||
if(NINTENDO_SWITCH OR NINTENDO_3DS OR NINTENDO_DS)
|
if(NINTENDO_SWITCH OR NINTENDO_3DS OR NINTENDO_DS)
|
||||||
dkp_target_generate_symbol_list(${FTPD_TARGET})
|
dkp_target_generate_symbol_list(${FTPD_TARGET})
|
||||||
endif()
|
endif()
|
||||||
|
28
include/fs.h
28
include/fs.h
@ -3,7 +3,7 @@
|
|||||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||||
//
|
//
|
||||||
// Copyright (C) 2020 Michael Theall
|
// Copyright (C) 2024 Michael Theall
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
#include "ioBuffer.h"
|
#include "ioBuffer.h"
|
||||||
|
|
||||||
|
#include <gsl/gsl>
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@ -29,6 +31,7 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace fs
|
namespace fs
|
||||||
{
|
{
|
||||||
@ -69,7 +72,7 @@ public:
|
|||||||
/// \brief Open file
|
/// \brief Open file
|
||||||
/// \param path_ Path to open
|
/// \param path_ Path to open
|
||||||
/// \param mode_ Access mode (\sa std::fopen)
|
/// \param mode_ Access mode (\sa std::fopen)
|
||||||
bool open (char const *path_, char const *mode_ = "rb");
|
bool open (gsl::not_null<gsl::czstring> path_, gsl::not_null<gsl::czstring> mode_ = "rb");
|
||||||
|
|
||||||
/// \brief Close file
|
/// \brief Close file
|
||||||
void close ();
|
void close ();
|
||||||
@ -77,13 +80,13 @@ public:
|
|||||||
/// \brief Seek to file position
|
/// \brief Seek to file position
|
||||||
/// \param pos_ File position
|
/// \param pos_ File position
|
||||||
/// \param origin_ Reference position (\sa std::fseek)
|
/// \param origin_ Reference position (\sa std::fseek)
|
||||||
std::make_signed_t<std::size_t> seek (std::size_t pos_, int origin_);
|
std::make_signed_t<std::size_t> seek (std::make_signed_t<std::size_t> pos_, int origin_);
|
||||||
|
|
||||||
/// \brief Read data
|
/// \brief Read data
|
||||||
/// \param buffer_ Output buffer
|
/// \param buffer_ Output buffer
|
||||||
/// \param size_ Size to read
|
/// \param size_ Size to read
|
||||||
/// \note Can return partial reads
|
/// \note Can return partial reads
|
||||||
std::make_signed_t<std::size_t> read (void *buffer_, std::size_t size_);
|
std::make_signed_t<std::size_t> read (gsl::not_null<void *> buffer_, std::size_t size_);
|
||||||
|
|
||||||
/// \brief Read data
|
/// \brief Read data
|
||||||
/// \param buffer_ Output buffer
|
/// \param buffer_ Output buffer
|
||||||
@ -97,13 +100,13 @@ public:
|
|||||||
/// \param buffer_ Output buffer
|
/// \param buffer_ Output buffer
|
||||||
/// \param size_ Size to read
|
/// \param size_ Size to read
|
||||||
/// \note Fails on partial reads and errors
|
/// \note Fails on partial reads and errors
|
||||||
bool readAll (void *buffer_, std::size_t size_);
|
bool readAll (gsl::not_null<void *> buffer_, std::size_t size_);
|
||||||
|
|
||||||
/// \brief Write data
|
/// \brief Write data
|
||||||
/// \param buffer_ Input data
|
/// \param buffer_ Input data
|
||||||
/// \param size_ Size to write
|
/// \param size_ Size to write
|
||||||
/// \note Can return partial writes
|
/// \note Can return partial writes
|
||||||
std::make_signed_t<std::size_t> write (void const *buffer_, std::size_t size_);
|
std::make_signed_t<std::size_t> write (gsl::not_null<void const *> buffer_, std::size_t size_);
|
||||||
|
|
||||||
/// \brief Write data
|
/// \brief Write data
|
||||||
/// \param buffer_ Input data
|
/// \param buffer_ Input data
|
||||||
@ -114,20 +117,17 @@ public:
|
|||||||
/// \param buffer_ Input data
|
/// \param buffer_ Input data
|
||||||
/// \param size_ Size to write
|
/// \param size_ Size to write
|
||||||
/// \note Fails on partials writes and errors
|
/// \note Fails on partials writes and errors
|
||||||
bool writeAll (void const *buffer_, std::size_t size_);
|
bool writeAll (gsl::not_null<void const *> buffer_, std::size_t size_);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// \brief Underlying std::FILE*
|
/// \brief Underlying std::FILE*
|
||||||
std::unique_ptr<std::FILE, int (*) (std::FILE *)> m_fp{nullptr, nullptr};
|
std::unique_ptr<std::FILE, int (*) (std::FILE *)> m_fp{nullptr, nullptr};
|
||||||
|
|
||||||
/// \brief Buffer
|
/// \brief Buffer
|
||||||
std::unique_ptr<char[]> m_buffer;
|
std::vector<char> m_buffer;
|
||||||
|
|
||||||
/// \brief Buffer size
|
|
||||||
std::size_t m_bufferSize = 0;
|
|
||||||
|
|
||||||
/// \brief Line buffer
|
/// \brief Line buffer
|
||||||
char *m_lineBuffer = nullptr;
|
gsl::owner<char *> m_lineBuffer = nullptr;
|
||||||
|
|
||||||
/// \brief Line buffer size
|
/// \brief Line buffer size
|
||||||
std::size_t m_lineBufferSize = 0;
|
std::size_t m_lineBufferSize = 0;
|
||||||
@ -161,14 +161,14 @@ public:
|
|||||||
|
|
||||||
/// \brief Open directory
|
/// \brief Open directory
|
||||||
/// \param path_ Path to open
|
/// \param path_ Path to open
|
||||||
bool open (char const *const path_);
|
bool open (gsl::not_null<gsl::czstring> path_);
|
||||||
|
|
||||||
/// \brief Close directory
|
/// \brief Close directory
|
||||||
void close ();
|
void close ();
|
||||||
|
|
||||||
/// \brief Read a directory entry
|
/// \brief Read a directory entry
|
||||||
/// \note Returns nullptr on end-of-directory or error; check errno
|
/// \note Returns nullptr on end-of-directory or error; check errno
|
||||||
struct dirent *read ();
|
dirent *read ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// \brief Underlying DIR*
|
/// \brief Underlying DIR*
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||||
//
|
//
|
||||||
// Copyright (C) 2023 Michael Theall
|
// Copyright (C) 2024 Michael Theall
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@ -22,10 +22,13 @@
|
|||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
|
#include <gsl/gsl>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
class FtpConfig;
|
class FtpConfig;
|
||||||
using UniqueFtpConfig = std::unique_ptr<FtpConfig>;
|
using UniqueFtpConfig = std::unique_ptr<FtpConfig>;
|
||||||
@ -41,7 +44,7 @@ public:
|
|||||||
|
|
||||||
/// \brief Load config
|
/// \brief Load config
|
||||||
/// \param path_ Path to config file
|
/// \param path_ Path to config file
|
||||||
static UniqueFtpConfig load (char const *path_);
|
static UniqueFtpConfig load (gsl::not_null<gsl::czstring> path_);
|
||||||
|
|
||||||
#ifndef __NDS__
|
#ifndef __NDS__
|
||||||
std::scoped_lock<platform::Mutex> lockGuard ();
|
std::scoped_lock<platform::Mutex> lockGuard ();
|
||||||
@ -49,7 +52,7 @@ public:
|
|||||||
|
|
||||||
/// \brief Save config
|
/// \brief Save config
|
||||||
/// \param path_ Path to config file
|
/// \param path_ Path to config file
|
||||||
bool save (char const *path_);
|
bool save (gsl::not_null<gsl::czstring> path_);
|
||||||
|
|
||||||
/// \brief Get user
|
/// \brief Get user
|
||||||
std::string const &user () const;
|
std::string const &user () const;
|
||||||
@ -79,15 +82,15 @@ public:
|
|||||||
|
|
||||||
/// \brief Set user
|
/// \brief Set user
|
||||||
/// \param user_ User
|
/// \param user_ User
|
||||||
void setUser (std::string const &user_);
|
void setUser (std::string user_);
|
||||||
|
|
||||||
/// \brief Set password
|
/// \brief Set password
|
||||||
/// \param pass_ Password
|
/// \param pass_ Password
|
||||||
void setPass (std::string const &pass_);
|
void setPass (std::string pass_);
|
||||||
|
|
||||||
/// \brief Set listen port
|
/// \brief Set listen port
|
||||||
/// \param port_ Listen port
|
/// \param port_ Listen port
|
||||||
bool setPort (std::string const &port_);
|
bool setPort (std::string_view port_);
|
||||||
|
|
||||||
/// \brief Set listen port
|
/// \brief Set listen port
|
||||||
/// \param port_ Listen port
|
/// \param port_ Listen port
|
||||||
@ -106,11 +109,11 @@ public:
|
|||||||
|
|
||||||
/// \brief Set access point SSID
|
/// \brief Set access point SSID
|
||||||
/// \param ssid_ SSID
|
/// \param ssid_ SSID
|
||||||
void setSSID (std::string const &ssid_);
|
void setSSID (std::string_view ssid_);
|
||||||
|
|
||||||
/// \brief Set access point passphrase
|
/// \brief Set access point passphrase
|
||||||
/// \param passphrase_ Passphrase
|
/// \param passphrase_ Passphrase
|
||||||
void setPassphrase (std::string const &passphrase_);
|
void setPassphrase (std::string_view passphrase_);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -117,7 +117,7 @@ private:
|
|||||||
std::vector<UniqueFtpSession> m_sessions;
|
std::vector<UniqueFtpSession> m_sessions;
|
||||||
|
|
||||||
/// \brief Whether thread should quit
|
/// \brief Whether thread should quit
|
||||||
std::atomic<bool> m_quit;
|
std::atomic_bool m_quit = false;
|
||||||
|
|
||||||
#ifndef CLASSIC
|
#ifndef CLASSIC
|
||||||
/// \brief Log upload cURL context
|
/// \brief Log upload cURL context
|
||||||
@ -152,7 +152,7 @@ private:
|
|||||||
std::string m_passSetting;
|
std::string m_passSetting;
|
||||||
|
|
||||||
/// \brief Port setting
|
/// \brief Port setting
|
||||||
std::uint16_t m_portSetting;
|
std::uint16_t m_portSetting = 0;
|
||||||
|
|
||||||
#ifdef __3DS__
|
#ifdef __3DS__
|
||||||
/// \brief getMTime setting
|
/// \brief getMTime setting
|
||||||
@ -161,7 +161,7 @@ private:
|
|||||||
|
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
/// \brief Whether an error occurred enabling access point
|
/// \brief Whether an error occurred enabling access point
|
||||||
std::atomic<bool> m_apError = false;
|
std::atomic_bool m_apError = false;
|
||||||
|
|
||||||
/// \brief Enable access point setting
|
/// \brief Enable access point setting
|
||||||
bool m_enableAPSetting;
|
bool m_enableAPSetting;
|
||||||
|
@ -27,10 +27,12 @@
|
|||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
using stat_t = struct stat;
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -165,18 +167,18 @@ private:
|
|||||||
/// \brief Perform stat and apply tz offset to mtime
|
/// \brief Perform stat and apply tz offset to mtime
|
||||||
/// \param path_ Path to stat
|
/// \param path_ Path to stat
|
||||||
/// \param st_ Output stat
|
/// \param st_ Output stat
|
||||||
int tzStat (char const *const path_, struct stat *st_);
|
int tzStat (char const *const path_, stat_t *st_);
|
||||||
|
|
||||||
/// \brief Perform lstat and apply tz offset to mtime
|
/// \brief Perform lstat and apply tz offset to mtime
|
||||||
/// \param path_ Path to lstat
|
/// \param path_ Path to lstat
|
||||||
/// \param st_ Output stat
|
/// \param st_ Output stat
|
||||||
int tzLStat (char const *const path_, struct stat *st_);
|
int tzLStat (char const *const path_, stat_t *st_);
|
||||||
|
|
||||||
/// \brief Fill directory entry
|
/// \brief Fill directory entry
|
||||||
/// \param st_ Entry status
|
/// \param st_ Entry status
|
||||||
/// \param path_ Path name
|
/// \param path_ Path name
|
||||||
/// \param type_ MLST type
|
/// \param type_ MLST type
|
||||||
int fillDirent (struct stat const &st_, std::string_view path_, char const *type_ = nullptr);
|
int fillDirent (stat_t const &st_, std::string_view path_, char const *type_ = nullptr);
|
||||||
|
|
||||||
/// \brief Fill directory entry
|
/// \brief Fill directory entry
|
||||||
/// \param path_ Path name
|
/// \param path_ Path name
|
||||||
|
@ -58,44 +58,46 @@ public:
|
|||||||
SockAddr &operator= (SockAddr &&that_);
|
SockAddr &operator= (SockAddr &&that_);
|
||||||
|
|
||||||
/// \brief Parameterized constructor
|
/// \brief Parameterized constructor
|
||||||
/// \param addr_ Address
|
/// \param addr_ Address (network byte order)
|
||||||
SockAddr (struct sockaddr_in const &addr_);
|
SockAddr (sockaddr_in const &addr_);
|
||||||
|
|
||||||
#ifndef NO_IPV6
|
#ifndef NO_IPV6
|
||||||
/// \brief Parameterized constructor
|
/// \brief Parameterized constructor
|
||||||
/// \param addr_ Address
|
/// \param addr_ Address (network byte order)
|
||||||
SockAddr (struct sockaddr_in6 const &addr_);
|
SockAddr (sockaddr_in6 const &addr_);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// \brief Parameterized constructor
|
/// \brief Parameterized constructor
|
||||||
/// \param addr_ Address
|
/// \param addr_ Address (network byte order)
|
||||||
SockAddr (struct sockaddr_storage const &addr_);
|
SockAddr (sockaddr_storage const &addr_);
|
||||||
|
|
||||||
/// \brief sockaddr_in cast operator
|
/// \brief sockaddr_in cast operator (network byte order)
|
||||||
operator struct sockaddr_in const & () const;
|
operator sockaddr_in const & () const;
|
||||||
|
|
||||||
#ifndef NO_IPV6
|
#ifndef NO_IPV6
|
||||||
/// \brief sockaddr_in6 cast operator
|
/// \brief sockaddr_in6 cast operator (network byte order)
|
||||||
operator struct sockaddr_in6 const & () const;
|
operator sockaddr_in6 const & () const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// \brief sockaddr_storage cast operator
|
/// \brief sockaddr_storage cast operator (network byte order)
|
||||||
operator struct sockaddr_storage const & () const;
|
operator sockaddr_storage const & () const;
|
||||||
|
|
||||||
|
/// \brief sockaddr* cast operator (network byte order)
|
||||||
|
operator sockaddr * ();
|
||||||
|
|
||||||
|
/// \brief sockaddr const* cast operator (network byte order)
|
||||||
|
operator sockaddr const * () const;
|
||||||
|
|
||||||
/// \brief sockaddr* cast operator
|
|
||||||
operator struct sockaddr * ();
|
|
||||||
/// \brief sockaddr const* cast operator
|
|
||||||
operator struct sockaddr const * () const;
|
|
||||||
|
|
||||||
/// \brief sockaddr size
|
/// \brief sockaddr size
|
||||||
socklen_t size () const;
|
socklen_t size () const;
|
||||||
|
|
||||||
/// \brief Address port
|
/// \brief Address port (host byte order)
|
||||||
std::uint16_t port () const;
|
std::uint16_t port () const;
|
||||||
|
|
||||||
/// \brief Set address port
|
/// \brief Set address port
|
||||||
/// \param port_ Port to set
|
/// \param port_ Port to set (host byte order)
|
||||||
bool setPort (std::uint16_t port_);
|
void setPort (std::uint16_t port_);
|
||||||
|
|
||||||
/// \brief Address name
|
/// \brief Address name
|
||||||
/// \param buffer_ Buffer to hold name
|
/// \param buffer_ Buffer to hold name
|
||||||
@ -110,6 +112,6 @@ public:
|
|||||||
char const *name () const;
|
char const *name () const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// \brief Address storage
|
/// \brief Address storage (network byte order)
|
||||||
struct sockaddr_storage m_addr = {};
|
sockaddr_storage m_addr = {};
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||||
//
|
//
|
||||||
// Copyright (C) 2023 Michael Theall
|
// Copyright (C) 2024 Michael Theall
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@ -37,7 +37,7 @@ struct pollfd
|
|||||||
using socklen_t = int;
|
using socklen_t = int;
|
||||||
using nfds_t = unsigned int;
|
using nfds_t = unsigned int;
|
||||||
|
|
||||||
extern "C" int poll (struct pollfd *fds_, nfds_t nfds_, int timeout_);
|
extern "C" int poll (pollfd *fds_, nfds_t nfds_, int timeout_);
|
||||||
|
|
||||||
#define POLLIN (1 << 0)
|
#define POLLIN (1 << 0)
|
||||||
#define POLLPRI (1 << 1)
|
#define POLLPRI (1 << 1)
|
||||||
|
@ -70,7 +70,7 @@ static_assert (SOCU_BUFFERSIZE % SOCU_ALIGN == 0);
|
|||||||
bool s_ndmuLocked = false;
|
bool s_ndmuLocked = false;
|
||||||
|
|
||||||
/// \brief Whether soc:u is active
|
/// \brief Whether soc:u is active
|
||||||
std::atomic<bool> s_socuActive = false;
|
std::atomic_bool s_socuActive = false;
|
||||||
/// \brief soc:u buffer
|
/// \brief soc:u buffer
|
||||||
u32 *s_socuBuffer = nullptr;
|
u32 *s_socuBuffer = nullptr;
|
||||||
/// \brief ac:u fence
|
/// \brief ac:u fence
|
||||||
@ -569,7 +569,7 @@ bool platform::networkVisible ()
|
|||||||
|
|
||||||
bool platform::networkAddress (SockAddr &addr_)
|
bool platform::networkAddress (SockAddr &addr_)
|
||||||
{
|
{
|
||||||
struct sockaddr_in addr;
|
sockaddr_in addr;
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_addr.s_addr = gethostid ();
|
addr.sin_addr.s_addr = gethostid ();
|
||||||
|
|
||||||
|
100
source/fs.cpp
100
source/fs.cpp
@ -3,7 +3,7 @@
|
|||||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||||
//
|
//
|
||||||
// Copyright (C) 2023 Michael Theall
|
// Copyright (C) 2024 Michael Theall
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@ -19,11 +19,24 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
|
#include "ioBuffer.h"
|
||||||
|
|
||||||
|
#include <gsl/pointers>
|
||||||
|
#include <gsl/util>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cerrno>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__)
|
#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__)
|
||||||
#define getline __getline
|
#define getline __getline
|
||||||
@ -38,7 +51,7 @@ std::string fs::printSize (std::uint64_t const size_)
|
|||||||
constexpr std::uint64_t const PiB = 1024 * TiB;
|
constexpr std::uint64_t const PiB = 1024 * TiB;
|
||||||
constexpr std::uint64_t const EiB = 1024 * PiB;
|
constexpr std::uint64_t const EiB = 1024 * PiB;
|
||||||
|
|
||||||
char buffer[64] = {};
|
std::array<char, 64> buffer{};
|
||||||
|
|
||||||
for (auto const &[name, bin] : {
|
for (auto const &[name, bin] : {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@ -56,30 +69,32 @@ std::string fs::printSize (std::uint64_t const size_)
|
|||||||
if (size_ >= 100 * bin)
|
if (size_ >= 100 * bin)
|
||||||
{
|
{
|
||||||
// >= 100, print xxxXiB
|
// >= 100, print xxxXiB
|
||||||
std::sprintf (buffer, "%" PRIu64 "%s", whole, name);
|
std::size_t const size = std::sprintf (buffer.data (), "%" PRIu64 "%s", whole, name);
|
||||||
return buffer;
|
return {buffer.data (), size};
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the fractional portion of the number
|
// get the fractional portion of the number
|
||||||
auto const frac = size_ - whole * bin;
|
auto const frac = size_ - (whole * bin);
|
||||||
if (size_ >= 10 * bin)
|
if (size_ >= 10 * bin)
|
||||||
{
|
{
|
||||||
// >= 10, print xx.xXiB
|
// >= 10, print xx.xXiB
|
||||||
std::sprintf (buffer, "%" PRIu64 ".%" PRIu64 "%s", whole, frac * 10 / bin, name);
|
std::size_t const size = std::sprintf (
|
||||||
return buffer;
|
buffer.data (), "%" PRIu64 ".%" PRIu64 "%s", whole, frac * 10 / bin, name);
|
||||||
|
return {buffer.data (), size};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size_ >= 1000 * (bin / KiB))
|
if (size_ >= 1000 * (bin / KiB))
|
||||||
{
|
{
|
||||||
// >= 1000 of lesser bin, print x.xxXiB
|
// >= 1000 of lesser bin, print x.xxXiB
|
||||||
std::sprintf (buffer, "%" PRIu64 ".%02" PRIu64 "%s", whole, frac * 100 / bin, name);
|
std::size_t const size = std::sprintf (
|
||||||
return buffer;
|
buffer.data (), "%" PRIu64 ".%02" PRIu64 "%s", whole, frac * 100 / bin, name);
|
||||||
|
return {buffer.data (), size};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// < 1KiB, just print the number
|
// < 1KiB, just print the number
|
||||||
std::sprintf (buffer, "%" PRIu64 "B", size_);
|
std::size_t const size = std::sprintf (buffer.data (), "%" PRIu64 "B", size_);
|
||||||
return buffer;
|
return {buffer.data (), size};
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
@ -106,26 +121,24 @@ fs::File::operator FILE * () const
|
|||||||
|
|
||||||
void fs::File::setBufferSize (std::size_t const size_)
|
void fs::File::setBufferSize (std::size_t const size_)
|
||||||
{
|
{
|
||||||
if (m_bufferSize != size_)
|
if (m_buffer.size () != size_)
|
||||||
{
|
m_buffer.resize (size_);
|
||||||
m_buffer = std::make_unique<char[]> (size_);
|
|
||||||
m_bufferSize = size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_fp)
|
if (m_fp)
|
||||||
std::setvbuf (m_fp.get (), m_buffer.get (), _IOFBF, m_bufferSize);
|
(void)std::setvbuf (m_fp.get (), m_buffer.data (), _IOFBF, m_buffer.size ());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fs::File::open (char const *const path_, char const *const mode_)
|
bool fs::File::open (gsl::not_null<char const *> const path_,
|
||||||
|
gsl::not_null<char const *> const mode_)
|
||||||
{
|
{
|
||||||
auto const fp = std::fopen (path_, mode_);
|
gsl::owner<FILE *> fp = std::fopen (path_, mode_);
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_fp = std::unique_ptr<std::FILE, int (*) (std::FILE *)> (fp, &std::fclose);
|
m_fp = std::unique_ptr<std::FILE, int (*) (std::FILE *)> (fp, &std::fclose);
|
||||||
|
|
||||||
if (m_buffer)
|
if (!m_buffer.empty ())
|
||||||
std::setvbuf (m_fp.get (), m_buffer.get (), _IOFBF, m_bufferSize);
|
(void)std::setvbuf (m_fp.get (), m_buffer.data (), _IOFBF, m_buffer.size ());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -135,17 +148,27 @@ void fs::File::close ()
|
|||||||
m_fp.reset ();
|
m_fp.reset ();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::make_signed_t<std::size_t> fs::File::seek (std::size_t const pos_, int const origin_)
|
std::make_signed_t<std::size_t> fs::File::seek (std::make_signed_t<std::size_t> const pos_,
|
||||||
|
int const origin_)
|
||||||
{
|
{
|
||||||
return std::fseek (m_fp.get (), pos_, origin_);
|
return std::fseek (m_fp.get (), pos_, origin_);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::make_signed_t<std::size_t> fs::File::read (void *const buffer_, std::size_t const size_)
|
std::make_signed_t<std::size_t> fs::File::read (gsl::not_null<void *> const buffer_,
|
||||||
|
std::size_t const size_)
|
||||||
{
|
{
|
||||||
assert (buffer_);
|
assert (buffer_);
|
||||||
assert (size_ > 0);
|
assert (size_ > 0);
|
||||||
|
|
||||||
return std::fread (buffer_, 1, size_, m_fp.get ());
|
auto const rc = std::fread (buffer_, 1, size_, m_fp.get ());
|
||||||
|
if (rc == 0)
|
||||||
|
{
|
||||||
|
if (std::feof (m_fp.get ()))
|
||||||
|
return 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gsl::narrow_cast<std::make_signed_t<std::size_t>> (rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::make_signed_t<std::size_t> fs::File::read (IOBuffer &buffer_)
|
std::make_signed_t<std::size_t> fs::File::read (IOBuffer &buffer_)
|
||||||
@ -176,37 +199,41 @@ std::string_view fs::File::readLine ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rc > 0)
|
if (rc > 0)
|
||||||
return std::string_view (m_lineBuffer, rc);
|
return {m_lineBuffer, gsl::narrow_cast<std::size_t> (rc)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fs::File::readAll (void *const buffer_, std::size_t const size_)
|
bool fs::File::readAll (gsl::not_null<void *> const buffer_, std::size_t const size_)
|
||||||
{
|
{
|
||||||
assert (buffer_);
|
assert (buffer_);
|
||||||
assert (size_ > 0);
|
assert (size_ > 0);
|
||||||
|
|
||||||
auto p = static_cast<char *> (buffer_);
|
auto const p = static_cast<char *> (buffer_.get ());
|
||||||
|
|
||||||
std::size_t bytes = 0;
|
std::size_t bytes = 0;
|
||||||
while (bytes < size_)
|
while (bytes < size_)
|
||||||
{
|
{
|
||||||
auto const rc = read (p, size_ - bytes);
|
auto const rc = read (p + bytes, size_ - bytes);
|
||||||
if (rc <= 0)
|
if (rc <= 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
p += rc;
|
|
||||||
bytes += rc;
|
bytes += rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::make_signed_t<std::size_t> fs::File::write (void const *const buffer_, std::size_t const size_)
|
std::make_signed_t<std::size_t> fs::File::write (gsl::not_null<void const *> const buffer_,
|
||||||
|
std::size_t const size_)
|
||||||
{
|
{
|
||||||
assert (buffer_);
|
assert (buffer_);
|
||||||
assert (size_ > 0);
|
assert (size_ > 0);
|
||||||
|
|
||||||
return std::fwrite (buffer_, 1, size_, m_fp.get ());
|
auto const rc = std::fwrite (buffer_, 1, size_, m_fp.get ());
|
||||||
|
if (rc == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return gsl::narrow_cast<std::make_signed_t<std::size_t>> (rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::make_signed_t<std::size_t> fs::File::write (IOBuffer &buffer_)
|
std::make_signed_t<std::size_t> fs::File::write (IOBuffer &buffer_)
|
||||||
@ -220,21 +247,20 @@ std::make_signed_t<std::size_t> fs::File::write (IOBuffer &buffer_)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fs::File::writeAll (void const *const buffer_, std::size_t const size_)
|
bool fs::File::writeAll (gsl::not_null<void const *> const buffer_, std::size_t const size_)
|
||||||
{
|
{
|
||||||
assert (buffer_);
|
assert (buffer_);
|
||||||
assert (size_ > 0);
|
assert (size_ > 0);
|
||||||
|
|
||||||
auto p = static_cast<char const *> (buffer_);
|
auto const p = static_cast<char const *> (buffer_.get ());
|
||||||
|
|
||||||
std::size_t bytes = 0;
|
std::size_t bytes = 0;
|
||||||
while (bytes < size_)
|
while (bytes < size_)
|
||||||
{
|
{
|
||||||
auto const rc = write (p, size_ - bytes);
|
auto const rc = write (p + bytes, size_ - bytes);
|
||||||
if (rc <= 0)
|
if (rc <= 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
p += rc;
|
|
||||||
bytes += rc;
|
bytes += rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +286,7 @@ fs::Dir::operator DIR * () const
|
|||||||
return m_dp.get ();
|
return m_dp.get ();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fs::Dir::open (char const *const path_)
|
bool fs::Dir::open (gsl::not_null<char const *> const path_)
|
||||||
{
|
{
|
||||||
auto const dp = ::opendir (path_);
|
auto const dp = ::opendir (path_);
|
||||||
if (!dp)
|
if (!dp)
|
||||||
@ -275,7 +301,7 @@ void fs::Dir::close ()
|
|||||||
m_dp.reset ();
|
m_dp.reset ();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct dirent *fs::Dir::read ()
|
dirent *fs::Dir::read ()
|
||||||
{
|
{
|
||||||
errno = 0;
|
errno = 0;
|
||||||
return ::readdir (m_dp.get ());
|
return ::readdir (m_dp.get ());
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||||
//
|
//
|
||||||
// Copyright (C) 2023 Michael Theall
|
// Copyright (C) 2024 Michael Theall
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@ -22,24 +22,36 @@
|
|||||||
|
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
#include <gsl/pointers>
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
using stat_t = struct stat;
|
||||||
|
|
||||||
#include <limits>
|
#include <cctype>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <charconv>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <system_error>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
constexpr std::uint16_t DEFAULT_PORT = 5000;
|
constexpr std::uint16_t DEFAULT_PORT = 5000;
|
||||||
|
|
||||||
bool mkdirParent (std::string const &path_)
|
bool mkdirParent (std::string_view const path_)
|
||||||
{
|
{
|
||||||
auto pos = path_.find_first_of ('/');
|
auto pos = path_.find_first_of ('/');
|
||||||
while (pos != std::string::npos)
|
while (pos != std::string::npos)
|
||||||
{
|
{
|
||||||
auto const dir = path_.substr (0, pos);
|
auto const dir = std::string (path_.substr (0, pos));
|
||||||
|
|
||||||
struct stat st;
|
stat_t st{};
|
||||||
auto const rc = ::stat (dir.c_str (), &st);
|
auto const rc = ::stat (dir.c_str (), &st);
|
||||||
if (rc < 0 && errno != ENOENT)
|
if (rc < 0 && errno != ENOENT)
|
||||||
return false;
|
return false;
|
||||||
@ -57,14 +69,13 @@ bool mkdirParent (std::string const &path_)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string strip (std::string const &str_)
|
std::string_view strip (std::string_view const str_)
|
||||||
{
|
{
|
||||||
auto const start = str_.find_first_not_of (" \t");
|
auto const start = str_.find_first_not_of (" \t");
|
||||||
if (start == std::string::npos)
|
if (start == std::string::npos)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
auto const end = str_.find_last_not_of (" \t");
|
auto const end = str_.find_last_not_of (" \t");
|
||||||
|
|
||||||
if (end == std::string::npos)
|
if (end == std::string::npos)
|
||||||
return str_.substr (start);
|
return str_.substr (start);
|
||||||
|
|
||||||
@ -72,37 +83,21 @@ std::string strip (std::string const &str_)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool parseInt (T &out_, std::string const &val_)
|
bool parseInt (T &out_, std::string_view const val_)
|
||||||
{
|
{
|
||||||
T val = 0;
|
auto const rc = std::from_chars (val_.data (), val_.data () + val_.size (), out_);
|
||||||
|
if (rc.ec != std::errc{})
|
||||||
for (auto const &c : val_)
|
|
||||||
{
|
{
|
||||||
if (!std::isdigit (c))
|
errno = static_cast<int> (rc.ec);
|
||||||
{
|
return false;
|
||||||
errno = EINVAL;
|
}
|
||||||
return false;
|
|
||||||
}
|
if (rc.ptr != val_.data () + val_.size ())
|
||||||
|
{
|
||||||
if (std::numeric_limits<T>::max () / 10 < val)
|
errno = EINVAL;
|
||||||
{
|
return false;
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,7 +114,7 @@ UniqueFtpConfig FtpConfig::create ()
|
|||||||
return UniqueFtpConfig (new FtpConfig ());
|
return UniqueFtpConfig (new FtpConfig ());
|
||||||
}
|
}
|
||||||
|
|
||||||
UniqueFtpConfig FtpConfig::load (char const *const path_)
|
UniqueFtpConfig FtpConfig::load (gsl::not_null<gsl::czstring> const path_)
|
||||||
{
|
{
|
||||||
auto config = create ();
|
auto config = create ();
|
||||||
|
|
||||||
@ -139,8 +134,8 @@ UniqueFtpConfig FtpConfig::load (char const *const path_)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const key = strip (line.substr (0, pos));
|
auto const key = strip (std::string_view (line).substr (0, pos));
|
||||||
auto const val = strip (line.substr (pos + 1));
|
auto const val = strip (std::string_view (line).substr (pos + 1));
|
||||||
if (key.empty () || val.empty ())
|
if (key.empty () || val.empty ())
|
||||||
{
|
{
|
||||||
error ("Ignoring '%s'\n", line.c_str ());
|
error ("Ignoring '%s'\n", line.c_str ());
|
||||||
@ -161,7 +156,9 @@ UniqueFtpConfig FtpConfig::load (char const *const path_)
|
|||||||
else if (val == "1")
|
else if (val == "1")
|
||||||
config->m_getMTime = true;
|
config->m_getMTime = true;
|
||||||
else
|
else
|
||||||
error ("Invalid value for mtime: %s\n", val.c_str ());
|
error ("Invalid value for mtime: %.*s\n",
|
||||||
|
gsl::narrow_cast<int> (val.size ()),
|
||||||
|
val.data ());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
@ -172,7 +169,9 @@ UniqueFtpConfig FtpConfig::load (char const *const path_)
|
|||||||
else if (val == "1")
|
else if (val == "1")
|
||||||
config->m_enableAP = true;
|
config->m_enableAP = true;
|
||||||
else
|
else
|
||||||
error ("Invalid value for ap: %s\n", val.c_str ());
|
error ("Invalid value for ap: %.*s\n",
|
||||||
|
gsl::narrow_cast<int> (val.size ()),
|
||||||
|
val.data ());
|
||||||
}
|
}
|
||||||
else if (key == "ssid")
|
else if (key == "ssid")
|
||||||
config->m_ssid = val;
|
config->m_ssid = val;
|
||||||
@ -193,9 +192,9 @@ std::scoped_lock<platform::Mutex> FtpConfig::lockGuard ()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool FtpConfig::save (char const *const path_)
|
bool FtpConfig::save (gsl::not_null<gsl::czstring> const path_)
|
||||||
{
|
{
|
||||||
if (!mkdirParent (path_))
|
if (!mkdirParent (path_.get ()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto fp = fs::File ();
|
auto fp = fs::File ();
|
||||||
@ -203,21 +202,21 @@ bool FtpConfig::save (char const *const path_)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!m_user.empty ())
|
if (!m_user.empty ())
|
||||||
std::fprintf (fp, "user=%s\n", m_user.c_str ());
|
(void)std::fprintf (fp, "user=%s\n", m_user.c_str ());
|
||||||
if (!m_pass.empty ())
|
if (!m_pass.empty ())
|
||||||
std::fprintf (fp, "pass=%s\n", m_pass.c_str ());
|
(void)std::fprintf (fp, "pass=%s\n", m_pass.c_str ());
|
||||||
std::fprintf (fp, "port=%u\n", m_port);
|
(void)std::fprintf (fp, "port=%u\n", m_port);
|
||||||
|
|
||||||
#ifdef __3DS__
|
#ifdef __3DS__
|
||||||
std::fprintf (fp, "mtime=%u\n", m_getMTime);
|
(void)std::fprintf (fp, "mtime=%u\n", m_getMTime);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
std::fprintf (fp, "ap=%u\n", m_enableAP);
|
(void)std::fprintf (fp, "ap=%u\n", m_enableAP);
|
||||||
if (!m_ssid.empty ())
|
if (!m_ssid.empty ())
|
||||||
std::fprintf (fp, "ssid=%s\n", m_ssid.c_str ());
|
(void)std::fprintf (fp, "ssid=%s\n", m_ssid.c_str ());
|
||||||
if (!m_passphrase.empty ())
|
if (!m_passphrase.empty ())
|
||||||
std::fprintf (fp, "passphrase=%s\n", m_passphrase.c_str ());
|
(void)std::fprintf (fp, "passphrase=%s\n", m_passphrase.c_str ());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -262,19 +261,19 @@ std::string const &FtpConfig::passphrase () const
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void FtpConfig::setUser (std::string const &user_)
|
void FtpConfig::setUser (std::string user_)
|
||||||
{
|
{
|
||||||
m_user = user_.substr (0, user_.find_first_of ('\0'));
|
m_user = std::move (user_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FtpConfig::setPass (std::string const &pass_)
|
void FtpConfig::setPass (std::string pass_)
|
||||||
{
|
{
|
||||||
m_pass = pass_.substr (0, pass_.find_first_of ('\0'));
|
m_pass = std::move (pass_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FtpConfig::setPort (std::string const &port_)
|
bool FtpConfig::setPort (std::string_view const port_)
|
||||||
{
|
{
|
||||||
std::uint16_t parsed;
|
std::uint16_t parsed{};
|
||||||
if (!parseInt (parsed, port_))
|
if (!parseInt (parsed, port_))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -317,12 +316,12 @@ void FtpConfig::setEnableAP (bool const enable_)
|
|||||||
m_enableAP = enable_;
|
m_enableAP = enable_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FtpConfig::setSSID (std::string const &ssid_)
|
void FtpConfig::setSSID (std::string_view const ssid_)
|
||||||
{
|
{
|
||||||
m_ssid = ssid_.substr (0, ssid_.find_first_of ('\0'));
|
m_ssid = ssid_.substr (0, ssid_.find_first_of ('\0'));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FtpConfig::setPassphrase (std::string const &passphrase_)
|
void FtpConfig::setPassphrase (std::string_view const passphrase_)
|
||||||
{
|
{
|
||||||
m_passphrase = passphrase_.substr (0, passphrase_.find_first_of ('\0'));
|
m_passphrase = passphrase_.substr (0, passphrase_.find_first_of ('\0'));
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,12 @@
|
|||||||
#include "ftpServer.h"
|
#include "ftpServer.h"
|
||||||
|
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
|
#include "ftpConfig.h"
|
||||||
|
#include "ftpSession.h"
|
||||||
#include "licenses.h"
|
#include "licenses.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
#include "sockAddr.h"
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
@ -32,22 +35,38 @@
|
|||||||
#include <dswifi9.h>
|
#include <dswifi9.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CLASSIC
|
#ifdef __3DS__
|
||||||
#include <jansson.h>
|
#include <citro3d.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLASSIC
|
||||||
|
#include <jansson.h>
|
||||||
|
|
||||||
|
#include <curl/easy.h>
|
||||||
|
#include <curl/multi.h>
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
#include <unistd.h>
|
using statvfs_t = struct statvfs;
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <ctime>
|
||||||
|
#include <functional>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <thread>
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
#ifdef __NDS__
|
#ifdef __NDS__
|
||||||
@ -81,21 +100,33 @@ std::string s_freeSpace;
|
|||||||
|
|
||||||
#ifndef CLASSIC
|
#ifndef CLASSIC
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
std::string printable (char *const data_, std::size_t const size_)
|
std::string printable (std::string_view const data_)
|
||||||
{
|
{
|
||||||
std::string result;
|
unsigned count = 0;
|
||||||
result.reserve (size_);
|
for (auto const &c : data_)
|
||||||
|
|
||||||
for (std::size_t i = 0; i < size_; ++i)
|
|
||||||
{
|
{
|
||||||
if (std::isprint (data_[i]) || std::isspace (data_[i]))
|
if (c != '%' && (std::isprint (c) || std::isspace (c)))
|
||||||
result.push_back (data_[i]);
|
++count;
|
||||||
|
else
|
||||||
|
count += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
result.reserve (count);
|
||||||
|
|
||||||
|
for (auto const &c : data_)
|
||||||
|
{
|
||||||
|
if (c != '%' && (std::isprint (c) || std::isspace (c)))
|
||||||
|
result.push_back (c);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char buffer[5];
|
result.push_back ('%');
|
||||||
std::snprintf (
|
|
||||||
buffer, sizeof (buffer), "%%%02u", static_cast<unsigned char> (data_[i]));
|
auto const upper = (static_cast<unsigned char> (c) >> 4u) & 0xF;
|
||||||
result += buffer;
|
auto const lower = (static_cast<unsigned char> (c) >> 0u) & 0xF;
|
||||||
|
|
||||||
|
result.push_back (gsl::narrow_cast<char> (upper < 10 ? upper + '0' : upper + 'A' - 10));
|
||||||
|
result.push_back (gsl::narrow_cast<char> (lower < 10 ? lower + '0' : lower + 'A' - 10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +142,7 @@ int curlDebug (CURL *const handle_,
|
|||||||
(void)handle_;
|
(void)handle_;
|
||||||
(void)user_;
|
(void)user_;
|
||||||
|
|
||||||
auto const text = printable (data_, size_);
|
auto const text = printable (std::string_view (data_, size_));
|
||||||
|
|
||||||
switch (type_)
|
switch (type_)
|
||||||
{
|
{
|
||||||
@ -190,7 +221,8 @@ FtpServer::~FtpServer ()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
FtpServer::FtpServer (UniqueFtpConfig config_) : m_config (std::move (config_)), m_quit (false)
|
FtpServer::FtpServer (UniqueFtpConfig config_)
|
||||||
|
: m_config (std::move (config_))
|
||||||
{
|
{
|
||||||
#ifndef __NDS__
|
#ifndef __NDS__
|
||||||
m_thread = platform::Thread (std::bind (&FtpServer::threadFunc, this));
|
m_thread = platform::Thread (std::bind (&FtpServer::threadFunc, this));
|
||||||
@ -279,17 +311,17 @@ void FtpServer::draw ()
|
|||||||
ImGui::SetNextWindowSize (ImVec2 (width, height));
|
ImGui::SetNextWindowSize (ImVec2 (width, height));
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
char title[64];
|
std::array<char, 64> title{};
|
||||||
|
|
||||||
{
|
{
|
||||||
auto const serverLock = std::scoped_lock (m_lock);
|
auto const serverLock = std::scoped_lock (m_lock);
|
||||||
std::snprintf (title,
|
std::snprintf (title.data (),
|
||||||
sizeof (title),
|
title.size (),
|
||||||
STATUS_STRING " %s###ftpd",
|
STATUS_STRING " %s###ftpd",
|
||||||
m_socket ? m_name.c_str () : "Waiting for WiFi...");
|
m_socket ? m_name.c_str () : "Waiting for WiFi...");
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Begin (title,
|
ImGui::Begin (title.data (),
|
||||||
nullptr,
|
nullptr,
|
||||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize
|
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize
|
||||||
#ifndef __3DS__
|
#ifndef __3DS__
|
||||||
@ -361,7 +393,7 @@ std::string FtpServer::getFreeSpace ()
|
|||||||
|
|
||||||
void FtpServer::updateFreeSpace ()
|
void FtpServer::updateFreeSpace ()
|
||||||
{
|
{
|
||||||
struct statvfs st;
|
statvfs_t st = {};
|
||||||
#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__)
|
#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__)
|
||||||
if (::statvfs ("sdmc:/", &st) != 0)
|
if (::statvfs ("sdmc:/", &st) != 0)
|
||||||
#else
|
#else
|
||||||
@ -440,8 +472,9 @@ void FtpServer::handleNetworkLost ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// destroy command socket
|
|
||||||
UniqueSocket sock;
|
UniqueSocket sock;
|
||||||
|
|
||||||
|
// destroy command socket
|
||||||
LOCKED (sock = std::move (m_socket));
|
LOCKED (sock = std::move (m_socket));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__)
|
#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__)
|
||||||
@ -60,6 +61,33 @@ namespace
|
|||||||
/// \brief Idle timeout
|
/// \brief Idle timeout
|
||||||
constexpr auto IDLE_TIMEOUT = 60;
|
constexpr auto IDLE_TIMEOUT = 60;
|
||||||
|
|
||||||
|
/// \brief Check if string view is a C string
|
||||||
|
/// \param str_ String to check
|
||||||
|
bool isCString (std::string_view const str_)
|
||||||
|
{
|
||||||
|
return str_.find_first_of ('\0') != std::string_view::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Case-insensitive string compare
|
||||||
|
/// \param lhs_ Left string
|
||||||
|
/// \param rhs_ Right string
|
||||||
|
int compare (std::string_view const lhs_, std::string_view const rhs_)
|
||||||
|
{
|
||||||
|
if (isCString (lhs_) && isCString (rhs_))
|
||||||
|
return ::strcasecmp (lhs_.data (), rhs_.data ());
|
||||||
|
|
||||||
|
auto const maxLen = std::min (lhs_.size (), rhs_.size ());
|
||||||
|
for (unsigned i = 0; i < maxLen; ++i)
|
||||||
|
{
|
||||||
|
auto const l = std::tolower (lhs_[i]);
|
||||||
|
auto const r = std::tolower (rhs_[i]);
|
||||||
|
if (l != r)
|
||||||
|
return l - r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gsl::narrow_cast<int> (lhs_.size ()) - gsl::narrow_cast<int> (rhs_.size ());
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Parse command
|
/// \brief Parse command
|
||||||
/// \param buffer_ Buffer to parse
|
/// \param buffer_ Buffer to parse
|
||||||
/// \param size_ Size of buffer
|
/// \param size_ Size of buffer
|
||||||
@ -171,7 +199,7 @@ std::string resolvePath (std::string_view const path_)
|
|||||||
assert (path_[0] == '/');
|
assert (path_[0] == '/');
|
||||||
|
|
||||||
// make sure parent is a directory
|
// make sure parent is a directory
|
||||||
struct stat st;
|
stat_t st;
|
||||||
if (::stat (dirName (path_).c_str (), &st) != 0)
|
if (::stat (dirName (path_).c_str (), &st) != 0)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
@ -385,7 +413,8 @@ void FtpSession::draw ()
|
|||||||
auto const timeDiff = now - m_filePositionTime;
|
auto const timeDiff = now - m_filePositionTime;
|
||||||
m_filePositionTime = now;
|
m_filePositionTime = now;
|
||||||
|
|
||||||
auto const rate = diff / std::chrono::duration<float> (timeDiff).count ();
|
auto const rate =
|
||||||
|
gsl::narrow_cast<float> (diff) / std::chrono::duration<float> (timeDiff).count ();
|
||||||
auto const alpha = 0.01f;
|
auto const alpha = 0.01f;
|
||||||
m_xferRate = alpha * rate + (1.0f - alpha) * m_xferRate;
|
m_xferRate = alpha * rate + (1.0f - alpha) * m_xferRate;
|
||||||
}
|
}
|
||||||
@ -469,7 +498,7 @@ bool FtpSession::poll (std::vector<UniqueFtpSession> const &sessions_)
|
|||||||
for (auto &pending : session->m_pendingCloseSocket)
|
for (auto &pending : session->m_pendingCloseSocket)
|
||||||
{
|
{
|
||||||
assert (pending.unique ());
|
assert (pending.unique ());
|
||||||
pollInfo.emplace_back (Socket::PollInfo{*pending, POLLIN, 0});
|
pollInfo.emplace_back (*pending, POLLIN, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,8 +542,7 @@ bool FtpSession::poll (std::vector<UniqueFtpSession> const &sessions_)
|
|||||||
{
|
{
|
||||||
if (session->m_commandSocket)
|
if (session->m_commandSocket)
|
||||||
{
|
{
|
||||||
pollInfo.emplace_back (
|
pollInfo.emplace_back (*session->m_commandSocket, POLLIN | POLLPRI, 0);
|
||||||
Socket::PollInfo{*session->m_commandSocket, POLLIN | POLLPRI, 0});
|
|
||||||
if (session->m_responseBuffer.usedSize () != 0)
|
if (session->m_responseBuffer.usedSize () != 0)
|
||||||
pollInfo.back ().events |= POLLOUT;
|
pollInfo.back ().events |= POLLOUT;
|
||||||
}
|
}
|
||||||
@ -530,12 +558,12 @@ bool FtpSession::poll (std::vector<UniqueFtpSession> const &sessions_)
|
|||||||
{
|
{
|
||||||
assert (!session->m_port);
|
assert (!session->m_port);
|
||||||
// we are waiting for a PASV connection
|
// we are waiting for a PASV connection
|
||||||
pollInfo.emplace_back (Socket::PollInfo{*session->m_pasvSocket, POLLIN, 0});
|
pollInfo.emplace_back (*session->m_pasvSocket, POLLIN, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// we are waiting to complete a PORT connection
|
// we are waiting to complete a PORT connection
|
||||||
pollInfo.emplace_back (Socket::PollInfo{*session->m_dataSocket, POLLOUT, 0});
|
pollInfo.emplace_back (*session->m_dataSocket, POLLOUT, 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -544,12 +572,12 @@ bool FtpSession::poll (std::vector<UniqueFtpSession> const &sessions_)
|
|||||||
if (session->m_recv)
|
if (session->m_recv)
|
||||||
{
|
{
|
||||||
assert (!session->m_send);
|
assert (!session->m_send);
|
||||||
pollInfo.emplace_back (Socket::PollInfo{*session->m_dataSocket, POLLIN, 0});
|
pollInfo.emplace_back (*session->m_dataSocket, POLLIN, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
assert (session->m_send);
|
assert (session->m_send);
|
||||||
pollInfo.emplace_back (Socket::PollInfo{*session->m_dataSocket, POLLOUT, 0});
|
pollInfo.emplace_back (*session->m_dataSocket, POLLOUT, 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -751,7 +779,7 @@ bool FtpSession::changeDir (char const *const args_)
|
|||||||
if (path.empty ())
|
if (path.empty ())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
struct stat st;
|
stat_t st;
|
||||||
if (tzStat (path.c_str (), &st) != 0)
|
if (tzStat (path.c_str (), &st) != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -834,7 +862,7 @@ bool FtpSession::dataConnect ()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FtpSession::tzStat (char const *const path_, struct stat *st_)
|
int FtpSession::tzStat (char const *const path_, stat_t *st_)
|
||||||
{
|
{
|
||||||
auto const rc = ::stat (path_, st_);
|
auto const rc = ::stat (path_, st_);
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
@ -855,7 +883,7 @@ int FtpSession::tzStat (char const *const path_, struct stat *st_)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FtpSession::tzLStat (char const *const path_, struct stat *st_)
|
int FtpSession::tzLStat (char const *const path_, stat_t *st_)
|
||||||
{
|
{
|
||||||
auto const rc = ::lstat (path_, st_);
|
auto const rc = ::lstat (path_, st_);
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
@ -876,7 +904,7 @@ int FtpSession::tzLStat (char const *const path_, struct stat *st_)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FtpSession::fillDirent (struct stat const &st_, std::string_view const path_, char const *type_)
|
int FtpSession::fillDirent (stat_t const &st_, std::string_view const path_, char const *type_)
|
||||||
{
|
{
|
||||||
auto const buffer = m_xferBuffer.freeArea ();
|
auto const buffer = m_xferBuffer.freeArea ();
|
||||||
auto const size = m_xferBuffer.freeSize ();
|
auto const size = m_xferBuffer.freeSize ();
|
||||||
@ -1162,7 +1190,7 @@ int FtpSession::fillDirent (struct stat const &st_, std::string_view const path_
|
|||||||
|
|
||||||
int FtpSession::fillDirent (std::string const &path_, char const *type_)
|
int FtpSession::fillDirent (std::string const &path_, char const *type_)
|
||||||
{
|
{
|
||||||
struct stat st;
|
stat_t st;
|
||||||
if (tzStat (path_.c_str (), &st) != 0)
|
if (tzStat (path_.c_str (), &st) != 0)
|
||||||
return errno;
|
return errno;
|
||||||
|
|
||||||
@ -1189,7 +1217,7 @@ void FtpSession::xferFile (char const *const args_, XferFileMode const mode_)
|
|||||||
else if (mode_ == XferFileMode::RETR)
|
else if (mode_ == XferFileMode::RETR)
|
||||||
{
|
{
|
||||||
// stat the file
|
// stat the file
|
||||||
struct stat st;
|
stat_t st;
|
||||||
if (tzStat (path.c_str (), &st) != 0)
|
if (tzStat (path.c_str (), &st) != 0)
|
||||||
{
|
{
|
||||||
sendResponse ("450 %s\r\n", std::strerror (errno));
|
sendResponse ("450 %s\r\n", std::strerror (errno));
|
||||||
@ -1321,7 +1349,7 @@ void FtpSession::xferDir (char const *const args_, XferDirMode const mode_, bool
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct stat st;
|
stat_t st;
|
||||||
if (tzStat (path.c_str (), &st) != 0)
|
if (tzStat (path.c_str (), &st) != 0)
|
||||||
{
|
{
|
||||||
if (needWorkaround)
|
if (needWorkaround)
|
||||||
@ -1600,12 +1628,10 @@ void FtpSession::readCommand (int const events_)
|
|||||||
auto const it = std::lower_bound (std::begin (handlers),
|
auto const it = std::lower_bound (std::begin (handlers),
|
||||||
std::end (handlers),
|
std::end (handlers),
|
||||||
command,
|
command,
|
||||||
[] (auto const &lhs_, auto const &rhs_) {
|
[] (auto const &lhs_, auto const &rhs_) { return compare (lhs_.first, rhs_) < 0; });
|
||||||
return ::strcasecmp (lhs_.first.data (), rhs_) < 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
m_timestamp = std::time (nullptr);
|
m_timestamp = std::time (nullptr);
|
||||||
if (it == std::end (handlers) || ::strcasecmp (it->first.data (), command) != 0)
|
if (it == std::end (handlers) || compare (it->first, command) != 0)
|
||||||
{
|
{
|
||||||
std::string response = "502 Invalid command \"";
|
std::string response = "502 Invalid command \"";
|
||||||
response += encodePath (command);
|
response += encodePath (command);
|
||||||
@ -1623,9 +1649,9 @@ void FtpSession::readCommand (int const events_)
|
|||||||
else if (m_state != State::COMMAND)
|
else if (m_state != State::COMMAND)
|
||||||
{
|
{
|
||||||
// only some commands are available during data transfer
|
// only some commands are available during data transfer
|
||||||
if (::strcasecmp (command, "ABOR") != 0 && ::strcasecmp (command, "NOOP") != 0 &&
|
if (compare (command, "ABOR") != 0 && compare (command, "NOOP") != 0 &&
|
||||||
::strcasecmp (command, "PWD") != 0 && ::strcasecmp (command, "QUIT") != 0 &&
|
compare (command, "PWD") != 0 && compare (command, "QUIT") != 0 &&
|
||||||
::strcasecmp (command, "STAT") != 0 && ::strcasecmp (command, "XPWD") != 0)
|
compare (command, "STAT") != 0 && compare (command, "XPWD") != 0)
|
||||||
{
|
{
|
||||||
sendResponse ("503 Invalid command during transfer\r\n");
|
sendResponse ("503 Invalid command during transfer\r\n");
|
||||||
setState (State::COMMAND, true, true);
|
setState (State::COMMAND, true, true);
|
||||||
@ -1640,7 +1666,7 @@ void FtpSession::readCommand (int const events_)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// clear rename for all commands except RNTO
|
// clear rename for all commands except RNTO
|
||||||
if (::strcasecmp (command, "RNTO") != 0)
|
if (compare (command, "RNTO") != 0)
|
||||||
m_rename.clear ();
|
m_rename.clear ();
|
||||||
|
|
||||||
auto const handler = it->second;
|
auto const handler = it->second;
|
||||||
@ -1791,7 +1817,7 @@ bool FtpSession::listTransfer ()
|
|||||||
{
|
{
|
||||||
// build the path
|
// build the path
|
||||||
auto const fullPath = buildPath (m_lwd, dent->d_name);
|
auto const fullPath = buildPath (m_lwd, dent->d_name);
|
||||||
struct stat st;
|
stat_t st;
|
||||||
|
|
||||||
#ifdef __3DS__
|
#ifdef __3DS__
|
||||||
// the sdmc directory entry already has the type and size, so no need to do a slow stat
|
// the sdmc directory entry already has the type and size, so no need to do a slow stat
|
||||||
@ -2220,7 +2246,7 @@ void FtpSession::MODE (char const *args_)
|
|||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
// we only accept S (stream) mode
|
// we only accept S (stream) mode
|
||||||
if (::strcasecmp (args_, "S") == 0)
|
if (compare (args_, "S") == 0)
|
||||||
{
|
{
|
||||||
sendResponse ("200 OK\r\n");
|
sendResponse ("200 OK\r\n");
|
||||||
return;
|
return;
|
||||||
@ -2254,8 +2280,8 @@ void FtpSession::OPTS (char const *args_)
|
|||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
// check UTF8 options
|
// check UTF8 options
|
||||||
if (::strcasecmp (args_, "UTF8") == 0 || ::strcasecmp (args_, "UTF8 ON") == 0 ||
|
if (compare (args_, "UTF8") == 0 || compare (args_, "UTF8 ON") == 0 ||
|
||||||
::strcasecmp (args_, "UTF8 NLST") == 0)
|
compare (args_, "UTF8 NLST") == 0)
|
||||||
{
|
{
|
||||||
sendResponse ("200 OK\r\n");
|
sendResponse ("200 OK\r\n");
|
||||||
return;
|
return;
|
||||||
@ -2371,7 +2397,7 @@ void FtpSession::PASV (char const *args_)
|
|||||||
m_pasvSocket->setSendBufferSize (SOCK_BUFFERSIZE);
|
m_pasvSocket->setSendBufferSize (SOCK_BUFFERSIZE);
|
||||||
|
|
||||||
// create an address to bind
|
// create an address to bind
|
||||||
struct sockaddr_in addr = m_commandSocket->sockName ();
|
sockaddr_in addr = m_commandSocket->sockName ();
|
||||||
#if defined(__NDS__) || defined(__3DS__)
|
#if defined(__NDS__) || defined(__3DS__)
|
||||||
static std::uint16_t ephemeralPort = 5001;
|
static std::uint16_t ephemeralPort = 5001;
|
||||||
if (ephemeralPort > 10000)
|
if (ephemeralPort > 10000)
|
||||||
@ -2455,7 +2481,7 @@ void FtpSession::PORT (char const *args_)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_in addr = {};
|
sockaddr_in addr{};
|
||||||
|
|
||||||
// parse the address
|
// parse the address
|
||||||
if (!inet_aton (addrString.data (), &addr.sin_addr))
|
if (!inet_aton (addrString.data (), &addr.sin_addr))
|
||||||
@ -2630,7 +2656,7 @@ void FtpSession::RNFR (char const *args_)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// make sure the path exists
|
// make sure the path exists
|
||||||
struct stat st;
|
stat_t st;
|
||||||
if (tzLStat (path.c_str (), &st) != 0)
|
if (tzLStat (path.c_str (), &st) != 0)
|
||||||
{
|
{
|
||||||
sendResponse ("450 %s\r\n", std::strerror (errno));
|
sendResponse ("450 %s\r\n", std::strerror (errno));
|
||||||
@ -2687,13 +2713,13 @@ void FtpSession::SITE (char const *args_)
|
|||||||
{
|
{
|
||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
auto const str = std::string (args_);
|
auto const str = std::string_view (args_);
|
||||||
auto const pos = str.find_first_of (' ');
|
auto const pos = str.find_first_of (' ');
|
||||||
|
|
||||||
auto const command = str.substr (0, pos);
|
auto const command = str.substr (0, pos);
|
||||||
auto const arg = pos == std::string::npos ? std::string () : str.substr (pos + 1);
|
auto const arg = pos == std::string::npos ? std::string_view () : str.substr (pos + 1);
|
||||||
|
|
||||||
if (::strcasecmp (command.c_str (), "HELP") == 0)
|
if (compare (command.data (), "HELP") == 0)
|
||||||
{
|
{
|
||||||
sendResponse ("211-\r\n"
|
sendResponse ("211-\r\n"
|
||||||
" Show this help: SITE HELP\r\n"
|
" Show this help: SITE HELP\r\n"
|
||||||
@ -2714,31 +2740,31 @@ void FtpSession::SITE (char const *args_)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (::strcasecmp (command.c_str (), "USER") == 0)
|
if (compare (command, "USER") == 0)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
#ifndef __NDS__
|
#ifndef __NDS__
|
||||||
auto const lock = m_config.lockGuard ();
|
auto const lock = m_config.lockGuard ();
|
||||||
#endif
|
#endif
|
||||||
m_config.setUser (arg);
|
m_config.setUser (std::string (arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
sendResponse ("200 OK\r\n");
|
sendResponse ("200 OK\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (::strcasecmp (command.c_str (), "PASS") == 0)
|
else if (compare (command, "PASS") == 0)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
#ifndef __NDS__
|
#ifndef __NDS__
|
||||||
auto const lock = m_config.lockGuard ();
|
auto const lock = m_config.lockGuard ();
|
||||||
#endif
|
#endif
|
||||||
m_config.setPass (arg);
|
m_config.setPass (std::string (arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
sendResponse ("200 OK\r\n");
|
sendResponse ("200 OK\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (::strcasecmp (command.c_str (), "PORT") == 0)
|
else if (compare (command, "PORT") == 0)
|
||||||
{
|
{
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
@ -2759,7 +2785,7 @@ void FtpSession::SITE (char const *args_)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#ifdef __3DS__
|
#ifdef __3DS__
|
||||||
else if (::strcasecmp (command.c_str (), "MTIME") == 0)
|
else if (compare (command, "MTIME") == 0)
|
||||||
{
|
{
|
||||||
if (arg == "0")
|
if (arg == "0")
|
||||||
{
|
{
|
||||||
@ -2782,7 +2808,7 @@ void FtpSession::SITE (char const *args_)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else if (::strcasecmp (command.c_str (), "SAVE") == 0)
|
else if (compare (command, "SAVE") == 0)
|
||||||
{
|
{
|
||||||
bool error;
|
bool error;
|
||||||
|
|
||||||
@ -2825,7 +2851,7 @@ void FtpSession::SIZE (char const *args_)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// stat the path
|
// stat the path
|
||||||
struct stat st;
|
stat_t st;
|
||||||
if (tzStat (path.c_str (), &st) != 0)
|
if (tzStat (path.c_str (), &st) != 0)
|
||||||
{
|
{
|
||||||
sendResponse ("550 %s\r\n", std::strerror (errno));
|
sendResponse ("550 %s\r\n", std::strerror (errno));
|
||||||
@ -2915,7 +2941,7 @@ void FtpSession::STRU (char const *args_)
|
|||||||
setState (State::COMMAND, false, false);
|
setState (State::COMMAND, false, false);
|
||||||
|
|
||||||
// we only support F (no structure) mode
|
// we only support F (no structure) mode
|
||||||
if (::strcasecmp (args_, "F") == 0)
|
if (compare (args_, "F") == 0)
|
||||||
{
|
{
|
||||||
sendResponse ("200 OK\r\n");
|
sendResponse ("200 OK\r\n");
|
||||||
return;
|
return;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||||
//
|
//
|
||||||
// Copyright (C) 2023 Michael Theall
|
// Copyright (C) 2024 Michael Theall
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@ -29,7 +29,10 @@
|
|||||||
#include "imgui_impl_glfw.h"
|
#include "imgui_impl_glfw.h"
|
||||||
#include "imgui_impl_opengl3.h"
|
#include "imgui_impl_opengl3.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -193,7 +196,7 @@ bool platform::networkVisible ()
|
|||||||
|
|
||||||
bool platform::networkAddress (SockAddr &addr_)
|
bool platform::networkAddress (SockAddr &addr_)
|
||||||
{
|
{
|
||||||
struct sockaddr_in addr;
|
sockaddr_in addr;
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
|
addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||||
//
|
//
|
||||||
// Copyright (C) 2020 Michael Theall
|
// Copyright (C) 2024 Michael Theall
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@ -40,7 +40,7 @@ PrintConsole g_sessionConsole;
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
/// \brief Host address
|
/// \brief Host address
|
||||||
struct in_addr s_addr = {0};
|
in_addr s_addr = {0};
|
||||||
/// \brief Which side of double-buffer we're on
|
/// \brief Which side of double-buffer we're on
|
||||||
bool s_backBuffer = false;
|
bool s_backBuffer = false;
|
||||||
/// \brief Whether to power backlight
|
/// \brief Whether to power backlight
|
||||||
@ -75,7 +75,7 @@ bool platform::networkVisible ()
|
|||||||
|
|
||||||
bool platform::networkAddress (SockAddr &addr_)
|
bool platform::networkAddress (SockAddr &addr_)
|
||||||
{
|
{
|
||||||
struct sockaddr_in addr;
|
sockaddr_in addr;
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_addr = Wifi_GetIPInfo (nullptr, nullptr, nullptr, nullptr);
|
addr.sin_addr = Wifi_GetIPInfo (nullptr, nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
|
@ -39,83 +39,80 @@ SockAddr &SockAddr::operator= (SockAddr const &that_) = default;
|
|||||||
|
|
||||||
SockAddr &SockAddr::operator= (SockAddr &&that_) = default;
|
SockAddr &SockAddr::operator= (SockAddr &&that_) = default;
|
||||||
|
|
||||||
SockAddr::SockAddr (struct sockaddr_in const &addr_)
|
SockAddr::SockAddr (sockaddr_in const &addr_)
|
||||||
{
|
{
|
||||||
assert (addr_.sin_family == AF_INET);
|
assert (addr_.sin_family == AF_INET);
|
||||||
std::memcpy (&m_addr, &addr_, sizeof (struct sockaddr_in));
|
std::memcpy (&m_addr, &addr_, sizeof (sockaddr_in));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NO_IPV6
|
#ifndef NO_IPV6
|
||||||
SockAddr::SockAddr (struct sockaddr_in6 const &addr_)
|
SockAddr::SockAddr (sockaddr_in6 const &addr_)
|
||||||
{
|
{
|
||||||
assert (addr_.sin6_family == AF_INET6);
|
assert (addr_.sin6_family == AF_INET6);
|
||||||
std::memcpy (&m_addr, &addr_, sizeof (struct sockaddr_in6));
|
std::memcpy (&m_addr, &addr_, sizeof (sockaddr_in6));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SockAddr::SockAddr (struct sockaddr_storage const &addr_)
|
SockAddr::SockAddr (sockaddr_storage const &addr_)
|
||||||
{
|
{
|
||||||
switch (addr_.ss_family)
|
switch (addr_.ss_family)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
assert (addr_.ss_family == AF_INET);
|
std::memcpy (&m_addr, &addr_, sizeof (sockaddr_in));
|
||||||
std::memcpy (&m_addr, &addr_, sizeof (struct sockaddr_in));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#ifndef NO_IPV6
|
#ifndef NO_IPV6
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
assert (addr_.ss_family == AF_INET6);
|
std::memcpy (&m_addr, &addr_, sizeof (sockaddr_in6));
|
||||||
std::memcpy (&m_addr, &addr_, sizeof (struct sockaddr_in6));
|
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
std::abort ();
|
std::abort ();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SockAddr::operator struct sockaddr_in const & () const
|
SockAddr::operator sockaddr_in const & () const
|
||||||
{
|
{
|
||||||
assert (m_addr.ss_family == AF_INET);
|
assert (m_addr.ss_family == AF_INET);
|
||||||
return reinterpret_cast<struct sockaddr_in const &> (m_addr);
|
return reinterpret_cast<sockaddr_in const &> (m_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NO_IPV6
|
#ifndef NO_IPV6
|
||||||
SockAddr::operator struct sockaddr_in6 const & () const
|
SockAddr::operator sockaddr_in6 const & () const
|
||||||
{
|
{
|
||||||
assert (m_addr.ss_family == AF_INET6);
|
assert (m_addr.ss_family == AF_INET6);
|
||||||
return reinterpret_cast<struct sockaddr_in6 const &> (m_addr);
|
return reinterpret_cast<sockaddr_in6 const &> (m_addr);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SockAddr::operator struct sockaddr_storage const & () const
|
SockAddr::operator sockaddr_storage const & () const
|
||||||
{
|
{
|
||||||
return m_addr;
|
return m_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
SockAddr::operator struct sockaddr * ()
|
SockAddr::operator sockaddr * ()
|
||||||
{
|
{
|
||||||
return reinterpret_cast<struct sockaddr *> (&m_addr);
|
return reinterpret_cast<sockaddr *> (&m_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
SockAddr::operator struct sockaddr const * () const
|
SockAddr::operator sockaddr const * () const
|
||||||
{
|
{
|
||||||
return reinterpret_cast<struct sockaddr const *> (&m_addr);
|
return reinterpret_cast<sockaddr const *> (&m_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SockAddr::setPort (std::uint16_t const port_)
|
void SockAddr::setPort (std::uint16_t const port_)
|
||||||
{
|
{
|
||||||
switch (m_addr.ss_family)
|
switch (m_addr.ss_family)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
reinterpret_cast<struct sockaddr_in *> (&m_addr)->sin_port = htons (port_);
|
reinterpret_cast<struct sockaddr_in *> (&m_addr)->sin_port = htons (port_);
|
||||||
return true;
|
break;
|
||||||
|
|
||||||
#ifndef NO_IPV6
|
#ifndef NO_IPV6
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
reinterpret_cast<struct sockaddr_in6 *> (&m_addr)->sin6_port = htons (port_);
|
reinterpret_cast<struct sockaddr_in6 *> (&m_addr)->sin6_port = htons (port_);
|
||||||
return true;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -146,11 +143,11 @@ std::uint16_t SockAddr::port () const
|
|||||||
switch (m_addr.ss_family)
|
switch (m_addr.ss_family)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
return ntohs (reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_port);
|
return ntohs (reinterpret_cast<sockaddr_in const *> (&m_addr)->sin_port);
|
||||||
|
|
||||||
#ifndef NO_IPV6
|
#ifndef NO_IPV6
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
return ntohs (reinterpret_cast<struct sockaddr_in6 const *> (&m_addr)->sin6_port);
|
return ntohs (reinterpret_cast<sockaddr_in6 const *> (&m_addr)->sin6_port);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -167,32 +164,27 @@ char const *SockAddr::name (char *buffer_, std::size_t size_) const
|
|||||||
#ifdef __NDS__
|
#ifdef __NDS__
|
||||||
(void)buffer_;
|
(void)buffer_;
|
||||||
(void)size_;
|
(void)size_;
|
||||||
return inet_ntoa (reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_addr);
|
return inet_ntoa (reinterpret_cast<sockaddr_in const *> (&m_addr)->sin_addr);
|
||||||
#else
|
#else
|
||||||
return inet_ntop (AF_INET,
|
return inet_ntop (
|
||||||
&reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_addr,
|
AF_INET, &reinterpret_cast<sockaddr_in const *> (&m_addr)->sin_addr, buffer_, size_);
|
||||||
buffer_,
|
|
||||||
size_);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NO_IPV6
|
#ifndef NO_IPV6
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
return inet_ntop (AF_INET6,
|
return inet_ntop (
|
||||||
&reinterpret_cast<struct sockaddr_in6 const *> (&m_addr)->sin6_addr,
|
AF_INET6, &reinterpret_cast<sockaddr_in6 const *> (&m_addr)->sin6_addr, buffer_, size_);
|
||||||
buffer_,
|
|
||||||
size_);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
std::abort ();
|
std::abort ();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char const *SockAddr::name () const
|
char const *SockAddr::name () const
|
||||||
{
|
{
|
||||||
#ifdef __NDS__
|
#ifdef __NDS__
|
||||||
return inet_ntoa (reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_addr);
|
return inet_ntoa (reinterpret_cast<sockaddr_in const *> (&m_addr)->sin_addr);
|
||||||
#else
|
#else
|
||||||
#ifdef NO_IPV6
|
#ifdef NO_IPV6
|
||||||
thread_local static char buffer[INET_ADDRSTRLEN];
|
thread_local static char buffer[INET_ADDRSTRLEN];
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@ -65,7 +66,7 @@ Socket::Socket (int const fd_, SockAddr const &sockName_, SockAddr const &peerNa
|
|||||||
UniqueSocket Socket::accept ()
|
UniqueSocket Socket::accept ()
|
||||||
{
|
{
|
||||||
SockAddr addr;
|
SockAddr addr;
|
||||||
socklen_t addrLen = sizeof (struct sockaddr_storage);
|
socklen_t addrLen = sizeof (sockaddr_storage);
|
||||||
|
|
||||||
auto const fd = ::accept (m_fd, addr, &addrLen);
|
auto const fd = ::accept (m_fd, addr, &addrLen);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
@ -104,7 +105,7 @@ bool Socket::bind (SockAddr const &addr_)
|
|||||||
if (addr_.port () == 0)
|
if (addr_.port () == 0)
|
||||||
{
|
{
|
||||||
// get socket name due to request for ephemeral port
|
// get socket name due to request for ephemeral port
|
||||||
socklen_t addrLen = sizeof (struct sockaddr_storage);
|
socklen_t addrLen = sizeof (sockaddr_storage);
|
||||||
if (::getsockname (m_fd, m_sockName, &addrLen) != 0)
|
if (::getsockname (m_fd, m_sockName, &addrLen) != 0)
|
||||||
error ("getsockname: %s\n", std::strerror (errno));
|
error ("getsockname: %s\n", std::strerror (errno));
|
||||||
}
|
}
|
||||||
@ -166,7 +167,7 @@ bool Socket::setLinger (bool const enable_, std::chrono::seconds const time_)
|
|||||||
errno = ENOSYS;
|
errno = ENOSYS;
|
||||||
return -1;
|
return -1;
|
||||||
#else
|
#else
|
||||||
struct linger linger;
|
linger linger;
|
||||||
linger.l_onoff = enable_;
|
linger.l_onoff = enable_;
|
||||||
linger.l_linger = time_.count ();
|
linger.l_linger = time_.count ();
|
||||||
|
|
||||||
@ -330,7 +331,7 @@ int Socket::poll (PollInfo *const info_,
|
|||||||
if (count_ == 0)
|
if (count_ == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
auto const pfd = std::make_unique<struct pollfd[]> (count_);
|
auto const pfd = std::make_unique<pollfd[]> (count_);
|
||||||
for (std::size_t i = 0; i < count_; ++i)
|
for (std::size_t i = 0; i < count_; ++i)
|
||||||
{
|
{
|
||||||
pfd[i].fd = info_[i].socket.get ().m_fd;
|
pfd[i].fd = info_[i].socket.get ().m_fd;
|
||||||
@ -352,7 +353,7 @@ int Socket::poll (PollInfo *const info_,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __NDS__
|
#ifdef __NDS__
|
||||||
extern "C" int poll (struct pollfd *const fds_, nfds_t const nfds_, int const timeout_)
|
extern "C" int poll (pollfd *const fds_, nfds_t const nfds_, int const timeout_)
|
||||||
{
|
{
|
||||||
fd_set readFds;
|
fd_set readFds;
|
||||||
fd_set writeFds;
|
fd_set writeFds;
|
||||||
@ -370,7 +371,7 @@ extern "C" int poll (struct pollfd *const fds_, nfds_t const nfds_, int const ti
|
|||||||
FD_SET (fds_[i].fd, &writeFds);
|
FD_SET (fds_[i].fd, &writeFds);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timeval tv;
|
timeval tv;
|
||||||
tv.tv_sec = timeout_ / 1000;
|
tv.tv_sec = timeout_ / 1000;
|
||||||
tv.tv_usec = (timeout_ % 1000) * 1000;
|
tv.tv_usec = (timeout_ % 1000) * 1000;
|
||||||
auto const rc = ::select (nfds_, &readFds, &writeFds, &exceptFds, &tv);
|
auto const rc = ::select (nfds_, &readFds, &writeFds, &exceptFds, &tv);
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
//
|
//
|
||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (C) 2023 Michael Theall
|
// Copyright (C) 2024 Michael Theall
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
using stat_t = struct stat;
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
@ -123,7 +124,7 @@ void loadShaders (dk::UniqueDevice &device_)
|
|||||||
/// \param path_ Path to file
|
/// \param path_ Path to file
|
||||||
static std::size_t getSize (char const *const path_)
|
static std::size_t getSize (char const *const path_)
|
||||||
{
|
{
|
||||||
struct stat st;
|
stat_t st;
|
||||||
auto const rc = ::stat (path_, &st);
|
auto const rc = ::stat (path_, &st);
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
{
|
{
|
||||||
|
@ -339,7 +339,7 @@ void loadTextures ()
|
|||||||
unsigned imageOffset = 0;
|
unsigned imageOffset = 0;
|
||||||
for (auto const &textureInfo : textureInfos)
|
for (auto const &textureInfo : textureInfos)
|
||||||
{
|
{
|
||||||
struct stat st;
|
stat_t st;
|
||||||
if (::stat (textureInfo.path, &st) != 0)
|
if (::stat (textureInfo.path, &st) != 0)
|
||||||
{
|
{
|
||||||
std::fprintf (stderr, "stat(%s): %s\n", textureInfo.path, std::strerror (errno));
|
std::fprintf (stderr, "stat(%s): %s\n", textureInfo.path, std::strerror (errno));
|
||||||
@ -618,14 +618,14 @@ bool platform::init ()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool platform::enableAP (bool const enableAP_,
|
bool platform::enableAP (bool const enable_,
|
||||||
std::string const &ssid_,
|
std::string const &ssid_,
|
||||||
std::string const &passphrase_)
|
std::string const &passphrase_)
|
||||||
{
|
{
|
||||||
if (s_activeAP == enableAP_)
|
if (s_activeAP == enable_)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (enableAP_)
|
if (enable_)
|
||||||
{
|
{
|
||||||
auto const ssidError = validateSSID (ssid_);
|
auto const ssidError = validateSSID (ssid_);
|
||||||
if (ssidError)
|
if (ssidError)
|
||||||
@ -781,11 +781,11 @@ bool platform::networkAddress (SockAddr &addr_)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
addr_ = *reinterpret_cast<struct sockaddr_in *> (ipConfig.ip_addr);
|
addr_ = *reinterpret_cast<sockaddr_in *> (ipConfig.ip_addr);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_in addr;
|
sockaddr_in addr;
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_addr.s_addr = gethostid ();
|
addr.sin_addr.s_addr = gethostid ();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user