make some web functions work
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
@@ -31,6 +31,19 @@
|
||||
config.set({
|
||||
appName: 'ezRemote Client',
|
||||
listUrl: '/__local__/list',
|
||||
uploadUrl: '/__local__/upload',
|
||||
renameUrl: '/__local__/rename',
|
||||
copyUrl: '/__local__/copy',
|
||||
moveUrl: '/__local__/move',
|
||||
removeUrl: '/__local__/remove',
|
||||
editUrl: '/__local__/edit',
|
||||
getContentUrl: '/__local__/getContent',
|
||||
createFolderUrl: '/__local__/createFolder',
|
||||
downloadFileUrl: '/__local__/downloadFile',
|
||||
downloadMultipleUrl: '/__local__/downloadMultiple',
|
||||
compressUrl: '/__local__/compress',
|
||||
extractUrl: '/__local__/extract',
|
||||
permissionsUrl: '/__local__/permission',
|
||||
pickCallback: function(item) {
|
||||
var msg = 'Picked %s "%s" for external use'
|
||||
.replace('%s', item.type)
|
||||
|
||||
+25
-8
@@ -77,6 +77,15 @@ namespace FS
|
||||
return (stat(path.c_str(), &dir_stat) == 0);
|
||||
}
|
||||
|
||||
int IsFolder(const std::string &path)
|
||||
{
|
||||
struct stat dir_stat = {0};
|
||||
if (stat(path.c_str(), &dir_stat) != 0)
|
||||
return -1;
|
||||
if (S_ISDIR(dir_stat.st_mode))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
void Rename(const std::string &from, const std::string &to)
|
||||
{
|
||||
int res = rename(from.c_str(), to.c_str());
|
||||
@@ -121,7 +130,7 @@ namespace FS
|
||||
|
||||
int Write(FILE *f, const void *buffer, uint32_t size)
|
||||
{
|
||||
int write = fwrite(buffer, size, 1, f);
|
||||
int write = fwrite(buffer, 1, size, f);
|
||||
return write;
|
||||
}
|
||||
|
||||
@@ -136,14 +145,17 @@ namespace FS
|
||||
if (fd == nullptr)
|
||||
return std::vector<char>(0);
|
||||
const auto size = GetSize(path);
|
||||
dbglogger_log("size=%lld", size);
|
||||
std::vector<char> data(size);
|
||||
|
||||
const auto read = fread(data.data(), 1, data.size(), fd);
|
||||
dbglogger_log("read=%lld", size);
|
||||
fclose(fd);
|
||||
if (read < 0)
|
||||
return std::vector<char>(0);
|
||||
|
||||
data.resize(read);
|
||||
data.resize(read+1);
|
||||
data[data.size()-1]=0;
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -209,22 +221,23 @@ namespace FS
|
||||
return true;
|
||||
}
|
||||
|
||||
void Save(const std::string &path, const void *data, uint32_t size)
|
||||
bool Save(const std::string &path, const void *data, uint32_t size)
|
||||
{
|
||||
FILE *fd = fopen(path.c_str(), "w+");
|
||||
if (fd == nullptr)
|
||||
return;
|
||||
return false;
|
||||
|
||||
const char *data8 = static_cast<const char *>(data);
|
||||
while (size != 0)
|
||||
{
|
||||
int written = fwrite(data8, size, 1, fd);
|
||||
int written = fwrite(data8, 1, size, fd);
|
||||
fclose(fd);
|
||||
if (written <= 0)
|
||||
return;
|
||||
return false;
|
||||
data8 += written;
|
||||
size -= written;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<DirEntry> ListDir(const std::string &ppath, int *err)
|
||||
@@ -304,9 +317,7 @@ namespace FS
|
||||
entry.modified.seconds = lt.second;
|
||||
entry.file_size = file_stat.st_size;
|
||||
|
||||
dbglogger_log("%04d-%02d-%02d %02d:%02d:%02d", lt.year, lt.month, lt.day, lt.hour, lt.minute, lt.second);
|
||||
sprintf(entry.display_date, "%04d-%02d-%02d %02d:%02d:%02d", lt.year, lt.month, lt.day, lt.hour, lt.minute, lt.second);
|
||||
dbglogger_log("display_date=%s", entry.display_date);
|
||||
|
||||
if (dirent->d_type & DT_DIR)
|
||||
{
|
||||
@@ -478,6 +489,9 @@ namespace FS
|
||||
bool Copy(const std::string &from, const std::string &to)
|
||||
{
|
||||
MkDirs(to, true);
|
||||
if (from.compare(to) == 0)
|
||||
return true;
|
||||
|
||||
FILE *src = fopen(from.c_str(), "rb");
|
||||
if (!src)
|
||||
{
|
||||
@@ -535,6 +549,9 @@ namespace FS
|
||||
|
||||
bool Move(const std::string &from, const std::string &to)
|
||||
{
|
||||
if (from.compare(to) == 0)
|
||||
return true;
|
||||
|
||||
bool res = Copy(from, to);
|
||||
if (res)
|
||||
Rm(from);
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@ namespace FS
|
||||
|
||||
bool FileExists(const std::string &path);
|
||||
bool FolderExists(const std::string &path);
|
||||
int IsFolder(const std::string &path);
|
||||
|
||||
void Rename(const std::string &from, const std::string &to);
|
||||
|
||||
@@ -54,7 +55,7 @@ namespace FS
|
||||
bool LoadText(std::vector<std::string> *lines, const std::string &path);
|
||||
bool SaveText(std::vector<std::string> *lines, const std::string &path);
|
||||
|
||||
void Save(const std::string &path, const void *data, uint32_t size);
|
||||
bool Save(const std::string &path, const void *data, uint32_t size);
|
||||
|
||||
std::vector<std::string> ListFiles(const std::string &path);
|
||||
std::vector<DirEntry> ListDir(const std::string &path, int *err);
|
||||
|
||||
+516
-168
File diff suppressed because it is too large
Load Diff
+174
-34
@@ -1,14 +1,14 @@
|
||||
//
|
||||
// httplib.h
|
||||
//
|
||||
// Copyright (c) 2022 Yuji Hirose. All rights reserved.
|
||||
// Copyright (c) 2023 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#ifndef CPPHTTPLIB_HTTPLIB_H
|
||||
#define CPPHTTPLIB_HTTPLIB_H
|
||||
|
||||
#define CPPHTTPLIB_VERSION "0.12.0"
|
||||
#define CPPHTTPLIB_VERSION "0.13.1"
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
@@ -87,7 +87,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef CPPHTTPLIB_RECV_BUFSIZ
|
||||
#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
|
||||
#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
|
||||
#endif
|
||||
|
||||
#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
|
||||
@@ -172,9 +172,15 @@ using socket_t = SOCKET;
|
||||
#else // not _WIN32
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#ifndef _AIX
|
||||
#if !defined(_AIX) && !defined(__MVS__)
|
||||
#include <ifaddrs.h>
|
||||
#endif
|
||||
#ifdef __MVS__
|
||||
#include <strings.h>
|
||||
#ifndef NI_MAXHOST
|
||||
#define NI_MAXHOST 1025
|
||||
#endif
|
||||
#endif
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
@@ -223,6 +229,9 @@ using socket_t = int;
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#ifdef _WIN32
|
||||
@@ -239,7 +248,13 @@ using socket_t = int;
|
||||
#pragma comment(lib, "crypt32.lib")
|
||||
#pragma comment(lib, "cryptui.lib")
|
||||
#endif
|
||||
#endif //_WIN32
|
||||
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#if TARGET_OS_OSX
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Security/Security.h>
|
||||
#endif // TARGET_OS_OSX
|
||||
#endif // _WIN32
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
@@ -308,6 +323,34 @@ struct ci {
|
||||
}
|
||||
};
|
||||
|
||||
// This is based on
|
||||
// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".
|
||||
|
||||
struct scope_exit {
|
||||
explicit scope_exit(std::function<void(void)> &&f)
|
||||
: exit_function(std::move(f)), execute_on_destruction{true} {}
|
||||
|
||||
scope_exit(scope_exit &&rhs)
|
||||
: exit_function(std::move(rhs.exit_function)),
|
||||
execute_on_destruction{rhs.execute_on_destruction} {
|
||||
rhs.release();
|
||||
}
|
||||
|
||||
~scope_exit() {
|
||||
if (execute_on_destruction) { this->exit_function(); }
|
||||
}
|
||||
|
||||
void release() { this->execute_on_destruction = false; }
|
||||
|
||||
private:
|
||||
scope_exit(const scope_exit &) = delete;
|
||||
void operator=(const scope_exit &) = delete;
|
||||
scope_exit &operator=(scope_exit &&) = delete;
|
||||
|
||||
std::function<void(void)> exit_function;
|
||||
bool execute_on_destruction;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
using Headers = std::multimap<std::string, std::string, detail::ci>;
|
||||
@@ -340,6 +383,7 @@ public:
|
||||
|
||||
std::function<bool(const char *data, size_t data_len)> write;
|
||||
std::function<void()> done;
|
||||
std::function<void(const Headers &trailer)> done_with_trailer;
|
||||
std::ostream os;
|
||||
|
||||
private:
|
||||
@@ -430,6 +474,7 @@ struct Request {
|
||||
MultipartFormDataMap files;
|
||||
Ranges ranges;
|
||||
Match matches;
|
||||
std::unordered_map<std::string, std::string> path_params;
|
||||
|
||||
// for client
|
||||
ResponseHandler response_handler;
|
||||
@@ -623,6 +668,76 @@ using SocketOptions = std::function<void(socket_t sock)>;
|
||||
|
||||
void default_socket_options(socket_t sock);
|
||||
|
||||
namespace detail {
|
||||
|
||||
class MatcherBase {
|
||||
public:
|
||||
virtual ~MatcherBase() = default;
|
||||
|
||||
// Match request path and populate its matches and
|
||||
virtual bool match(Request &request) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Captures parameters in request path and stores them in Request::path_params
|
||||
*
|
||||
* Capture name is a substring of a pattern from : to /.
|
||||
* The rest of the pattern is matched agains the request path directly
|
||||
* Parameters are captured starting from the next character after
|
||||
* the end of the last matched static pattern fragment until the next /.
|
||||
*
|
||||
* Example pattern:
|
||||
* "/path/fragments/:capture/more/fragments/:second_capture"
|
||||
* Static fragments:
|
||||
* "/path/fragments/", "more/fragments/"
|
||||
*
|
||||
* Given the following request path:
|
||||
* "/path/fragments/:1/more/fragments/:2"
|
||||
* the resulting capture will be
|
||||
* {{"capture", "1"}, {"second_capture", "2"}}
|
||||
*/
|
||||
class PathParamsMatcher : public MatcherBase {
|
||||
public:
|
||||
PathParamsMatcher(const std::string &pattern);
|
||||
|
||||
bool match(Request &request) const override;
|
||||
|
||||
private:
|
||||
static constexpr char marker = ':';
|
||||
// Treat segment separators as the end of path parameter capture
|
||||
// Does not need to handle query parameters as they are parsed before path
|
||||
// matching
|
||||
static constexpr char separator = '/';
|
||||
|
||||
// Contains static path fragments to match against, excluding the '/' after
|
||||
// path params
|
||||
// Fragments are separated by path params
|
||||
std::vector<std::string> static_fragments_;
|
||||
// Stores the names of the path parameters to be used as keys in the
|
||||
// Request::path_params map
|
||||
std::vector<std::string> param_names_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs std::regex_match on request path
|
||||
* and stores the result in Request::matches
|
||||
*
|
||||
* Note that regex match is performed directly on the whole request.
|
||||
* This means that wildcard patterns may match multiple path segments with /:
|
||||
* "/begin/(.*)/end" will match both "/begin/middle/end" and "/begin/1/2/end".
|
||||
*/
|
||||
class RegexMatcher : public MatcherBase {
|
||||
public:
|
||||
RegexMatcher(const std::string &pattern) : regex_(pattern) {}
|
||||
|
||||
bool match(Request &request) const override;
|
||||
|
||||
private:
|
||||
std::regex regex_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class Server {
|
||||
public:
|
||||
using Handler = std::function<void(const Request &, Response &)>;
|
||||
@@ -708,6 +823,7 @@ public:
|
||||
bool listen(const std::string &host, int port, int socket_flags = 0);
|
||||
|
||||
bool is_running() const;
|
||||
void wait_until_ready() const;
|
||||
void stop();
|
||||
|
||||
std::function<TaskQueue *(void)> new_task_queue;
|
||||
@@ -717,7 +833,7 @@ protected:
|
||||
bool &connection_closed,
|
||||
const std::function<void(Request &)> &setup_request);
|
||||
|
||||
std::atomic<socket_t> svr_sock_;
|
||||
std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
|
||||
size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
|
||||
time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
|
||||
time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
|
||||
@@ -729,9 +845,14 @@ protected:
|
||||
size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
|
||||
|
||||
private:
|
||||
using Handlers = std::vector<std::pair<std::regex, Handler>>;
|
||||
using Handlers =
|
||||
std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;
|
||||
using HandlersForContentReader =
|
||||
std::vector<std::pair<std::regex, HandlerWithContentReader>>;
|
||||
std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
|
||||
HandlerWithContentReader>>;
|
||||
|
||||
static std::unique_ptr<detail::MatcherBase>
|
||||
make_matcher(const std::string &pattern);
|
||||
|
||||
socket_t create_server_socket(const std::string &host, int port,
|
||||
int socket_flags,
|
||||
@@ -769,21 +890,23 @@ private:
|
||||
ContentReceiver multipart_receiver);
|
||||
bool read_content_core(Stream &strm, Request &req, Response &res,
|
||||
ContentReceiver receiver,
|
||||
MultipartContentHeader mulitpart_header,
|
||||
MultipartContentHeader multipart_header,
|
||||
ContentReceiver multipart_receiver);
|
||||
|
||||
virtual bool process_and_close_socket(socket_t sock);
|
||||
|
||||
std::atomic<bool> is_running_{false};
|
||||
std::atomic<bool> done_{false};
|
||||
|
||||
struct MountPointEntry {
|
||||
std::string mount_point;
|
||||
std::string base_dir;
|
||||
Headers headers;
|
||||
};
|
||||
std::vector<MountPointEntry> base_dirs_;
|
||||
|
||||
std::atomic<bool> is_running_;
|
||||
std::map<std::string, std::string> file_extension_and_mimetype_map_;
|
||||
Handler file_request_handler_;
|
||||
|
||||
Handlers get_handlers_;
|
||||
Handlers post_handlers_;
|
||||
HandlersForContentReader post_handlers_for_content_reader_;
|
||||
@@ -794,13 +917,15 @@ private:
|
||||
Handlers delete_handlers_;
|
||||
HandlersForContentReader delete_handlers_for_content_reader_;
|
||||
Handlers options_handlers_;
|
||||
|
||||
HandlerWithResponse error_handler_;
|
||||
ExceptionHandler exception_handler_;
|
||||
HandlerWithResponse pre_routing_handler_;
|
||||
Handler post_routing_handler_;
|
||||
Logger logger_;
|
||||
Expect100ContinueHandler expect_100_continue_handler_;
|
||||
|
||||
Logger logger_;
|
||||
|
||||
int address_family_ = AF_UNSPEC;
|
||||
bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
|
||||
SocketOptions socket_options_ = default_socket_options;
|
||||
@@ -823,6 +948,9 @@ enum class Error {
|
||||
UnsupportedMultipartBoundaryChars,
|
||||
Compression,
|
||||
ConnectionTimeout,
|
||||
|
||||
// For internal use only
|
||||
SSLPeerCouldBeClosed_,
|
||||
};
|
||||
|
||||
std::string to_string(const Error error);
|
||||
@@ -831,6 +959,7 @@ std::ostream &operator<<(std::ostream &os, const Error &obj);
|
||||
|
||||
class Result {
|
||||
public:
|
||||
Result() = default;
|
||||
Result(std::unique_ptr<Response> &&res, Error err,
|
||||
Headers &&request_headers = Headers{})
|
||||
: res_(std::move(res)), err_(err),
|
||||
@@ -859,7 +988,7 @@ public:
|
||||
|
||||
private:
|
||||
std::unique_ptr<Response> res_;
|
||||
Error err_;
|
||||
Error err_ = Error::Unknown;
|
||||
Headers request_headers_;
|
||||
};
|
||||
|
||||
@@ -1019,12 +1148,14 @@ public:
|
||||
bool send(Request &req, Response &res, Error &error);
|
||||
Result send(const Request &req);
|
||||
|
||||
size_t is_socket_open() const;
|
||||
|
||||
socket_t socket() const;
|
||||
|
||||
void stop();
|
||||
|
||||
std::string host() const;
|
||||
int port() const;
|
||||
|
||||
size_t is_socket_open() const;
|
||||
socket_t socket() const;
|
||||
|
||||
void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||
|
||||
void set_default_headers(Headers headers);
|
||||
@@ -1077,6 +1208,7 @@ public:
|
||||
void set_ca_cert_path(const std::string &ca_cert_file_path,
|
||||
const std::string &ca_cert_dir_path = std::string());
|
||||
void set_ca_cert_store(X509_STORE *ca_cert_store);
|
||||
X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size);
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -1095,8 +1227,6 @@ protected:
|
||||
bool is_open() const { return sock != INVALID_SOCKET; }
|
||||
};
|
||||
|
||||
Result send_(Request &&req);
|
||||
|
||||
virtual bool create_and_connect_socket(Socket &socket, Error &error);
|
||||
|
||||
// All of:
|
||||
@@ -1118,7 +1248,7 @@ protected:
|
||||
|
||||
void copy_settings(const ClientImpl &rhs);
|
||||
|
||||
// Socket endoint information
|
||||
// Socket endpoint information
|
||||
const std::string host_;
|
||||
const int port_;
|
||||
const std::string host_and_port_;
|
||||
@@ -1197,6 +1327,9 @@ protected:
|
||||
Logger logger_;
|
||||
|
||||
private:
|
||||
bool send_(Request &req, Response &res, Error &error);
|
||||
Result send_(Request &&req);
|
||||
|
||||
socket_t create_client_socket(Error &error) const;
|
||||
bool read_response_line(Stream &strm, const Request &req, Response &res);
|
||||
bool write_request(Stream &strm, Request &req, bool close_connection,
|
||||
@@ -1390,12 +1523,14 @@ public:
|
||||
bool send(Request &req, Response &res, Error &error);
|
||||
Result send(const Request &req);
|
||||
|
||||
size_t is_socket_open() const;
|
||||
|
||||
socket_t socket() const;
|
||||
|
||||
void stop();
|
||||
|
||||
std::string host() const;
|
||||
int port() const;
|
||||
|
||||
size_t is_socket_open() const;
|
||||
socket_t socket() const;
|
||||
|
||||
void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||
|
||||
void set_default_headers(Headers headers);
|
||||
@@ -1456,6 +1591,7 @@ public:
|
||||
const std::string &ca_cert_dir_path = std::string());
|
||||
|
||||
void set_ca_cert_store(X509_STORE *ca_cert_store);
|
||||
void load_ca_cert_store(const char *ca_cert, std::size_t size);
|
||||
|
||||
long get_openssl_verify_result() const;
|
||||
|
||||
@@ -1515,6 +1651,7 @@ public:
|
||||
bool is_valid() const override;
|
||||
|
||||
void set_ca_cert_store(X509_STORE *ca_cert_store);
|
||||
void load_ca_cert_store(const char *ca_cert, std::size_t size);
|
||||
|
||||
long get_openssl_verify_result() const;
|
||||
|
||||
@@ -1624,22 +1761,22 @@ inline ssize_t Stream::write_format(const char *fmt, const Args &...args) {
|
||||
inline void default_socket_options(socket_t sock) {
|
||||
int yes = 1;
|
||||
#ifdef _WIN32
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes),
|
||||
sizeof(yes));
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
|
||||
reinterpret_cast<const char *>(&yes), sizeof(yes));
|
||||
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
||||
reinterpret_cast<char *>(&yes), sizeof(yes));
|
||||
reinterpret_cast<const char *>(&yes), sizeof(yes));
|
||||
#else
|
||||
#ifdef SO_REUSEPORT
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<void *>(&yes),
|
||||
sizeof(yes));
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
|
||||
reinterpret_cast<const void *>(&yes), sizeof(yes));
|
||||
#else
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<void *>(&yes),
|
||||
sizeof(yes));
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
|
||||
reinterpret_cast<const void *>(&yes), sizeof(yes));
|
||||
#endif
|
||||
#endif
|
||||
int const size = 1048576;
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
|
||||
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
|
||||
//int const size = 1048576;
|
||||
//setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
|
||||
//setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
|
||||
}
|
||||
|
||||
template <class Rep, class Period>
|
||||
@@ -1791,6 +1928,9 @@ std::string params_to_query_str(const Params ¶ms);
|
||||
|
||||
void parse_query_text(const std::string &s, Params ¶ms);
|
||||
|
||||
bool parse_multipart_boundary(const std::string &content_type,
|
||||
std::string &boundary);
|
||||
|
||||
bool parse_range_header(const std::string &s, Ranges &ranges);
|
||||
|
||||
int close_socket(socket_t sock);
|
||||
|
||||
@@ -8,10 +8,14 @@
|
||||
#include "windows.h"
|
||||
#include "lang.h"
|
||||
#include "system.h"
|
||||
#include "dbglogger.h"
|
||||
|
||||
#define SERVER_CERT_FILE "/app0/assets/certs/domain.crt"
|
||||
#define SERVER_PRIVATE_KEY_FILE "/app0/assets/certs/domain.key"
|
||||
#define SERVER_PRIVATE_KEY_PASSWORD "12345678"
|
||||
#define SUCCESS_MSG "{ \"result\": { \"success\": true, \"error\": null } }"
|
||||
#define FAILURE_MSG "{ \"result\": { \"success\": false, \"error\": \"%s\" } }"
|
||||
#define SUCCESS_MSG_LEN 48
|
||||
|
||||
using namespace httplib;
|
||||
SSLServer *svr;
|
||||
@@ -76,6 +80,99 @@ namespace HttpServer
|
||||
return s;
|
||||
}
|
||||
|
||||
void failed(Response & res, int status, const std::string &msg)
|
||||
{
|
||||
res.status = status;
|
||||
char response_msg[msg.length()+strlen(FAILURE_MSG)+2];
|
||||
snprintf(response_msg, sizeof(response_msg), "{ \"result\": { \"success\": false, \"error\": \"%s\" } }", msg.c_str());
|
||||
res.set_content(response_msg, strlen(response_msg), "application/json");
|
||||
return;
|
||||
}
|
||||
|
||||
void bad_request(Response & res, const std::string &msg)
|
||||
{
|
||||
failed(res, 200, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
void success(Response & res)
|
||||
{
|
||||
res.status = 200;
|
||||
res.set_content(SUCCESS_MSG, SUCCESS_MSG_LEN, "application/json");
|
||||
return;
|
||||
}
|
||||
|
||||
int CopyOrMove(const DirEntry &src, const char *dest, bool isCopy)
|
||||
{
|
||||
int ret;
|
||||
if (src.isDir)
|
||||
{
|
||||
int err;
|
||||
std::vector<DirEntry> entries = FS::ListDir(src.path, &err);
|
||||
FS::MkDirs(dest);
|
||||
for (int i = 0; i < entries.size(); i++)
|
||||
{
|
||||
int path_length = strlen(dest) + strlen(entries[i].name) + 2;
|
||||
char *new_path = (char *)malloc(path_length);
|
||||
snprintf(new_path, path_length, "%s%s%s", dest, FS::hasEndSlash(dest) ? "" : "/", entries[i].name);
|
||||
|
||||
if (entries[i].isDir)
|
||||
{
|
||||
if (strcmp(entries[i].name, "..") == 0)
|
||||
continue;
|
||||
|
||||
FS::MkDirs(new_path);
|
||||
ret = CopyOrMove(entries[i], new_path, isCopy);
|
||||
if (ret <= 0)
|
||||
{
|
||||
free(new_path);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isCopy)
|
||||
{
|
||||
ret = FS::Copy(entries[i].path, new_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = FS::Move(entries[i].path, new_path);
|
||||
}
|
||||
if (ret <= 0)
|
||||
{
|
||||
free(new_path);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
free(new_path);
|
||||
}
|
||||
if (!isCopy)
|
||||
FS::RmRecursive(src.path);
|
||||
}
|
||||
else
|
||||
{
|
||||
int path_length = strlen(dest) + strlen(src.name) + 2;
|
||||
char *new_path = (char *)malloc(path_length);
|
||||
snprintf(new_path, path_length, "%s%s%s", dest, FS::hasEndSlash(dest) ? "" : "/", src.name);
|
||||
if (isCopy)
|
||||
{
|
||||
ret = FS::Copy(src.path, new_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = FS::Move(src.path, new_path);
|
||||
}
|
||||
if (ret <= 0)
|
||||
{
|
||||
free(new_path);
|
||||
return 0;
|
||||
}
|
||||
free(new_path);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *ServerThread(void *argp)
|
||||
{
|
||||
svr->Get("/", [&](const Request & req, Response & res)
|
||||
@@ -96,11 +193,15 @@ namespace HttpServer
|
||||
onlyFolders = true;
|
||||
if (path == nullptr)
|
||||
{
|
||||
res.status = 400;
|
||||
res.set_content("path parameter is missing", 25, "text/plain");
|
||||
bad_request(res, "Required path parameter missing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bad_request(res, "Invalid payload");
|
||||
return;
|
||||
}
|
||||
|
||||
int err;
|
||||
std::vector<DirEntry> files = FS::ListDir(path, &err);
|
||||
@@ -113,7 +214,7 @@ namespace HttpServer
|
||||
json_object_object_add(new_file, "name", json_object_new_string(it->name));
|
||||
json_object_object_add(new_file, "rights", json_object_new_string(it->isDir ? "drwxrwxrwx" : "rw-rw-rw-"));
|
||||
json_object_object_add(new_file, "date", json_object_new_string(it->display_date));
|
||||
json_object_object_add(new_file, "size", json_object_new_string(it->isDir ? "" : it->display_size));
|
||||
json_object_object_add(new_file, "size", json_object_new_string(it->isDir ? "" : std::to_string(it->file_size).c_str()));
|
||||
json_object_object_add(new_file, "type", json_object_new_string(it->isDir ? "dir" : "file"));
|
||||
json_object_array_add(json_files, new_file);
|
||||
}
|
||||
@@ -126,6 +227,360 @@ namespace HttpServer
|
||||
res.set_content(results_str, strlen(results_str), "application/json");
|
||||
});
|
||||
|
||||
svr->Post("/__local__/rename", [&](const Request & req, Response & res)
|
||||
{
|
||||
const char *item;
|
||||
const char *newItemPath;
|
||||
json_object *jobj = json_tokener_parse(req.body.c_str());
|
||||
if (jobj != nullptr)
|
||||
{
|
||||
item = json_object_get_string(json_object_object_get(jobj, "item"));
|
||||
newItemPath = json_object_get_string(json_object_object_get(jobj, "newItemPath"));
|
||||
if (item == nullptr || newItemPath == nullptr)
|
||||
{
|
||||
bad_request(res, "Required item or newItemPath parameter missing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bad_request(res, "Invalid payload");
|
||||
return;
|
||||
}
|
||||
|
||||
FS::Rename(item, newItemPath);
|
||||
success(res);
|
||||
return;
|
||||
});
|
||||
|
||||
svr->Post("/__local__/move", [&](const Request & req, Response & res)
|
||||
{
|
||||
const json_object *items;
|
||||
const char *newPath;
|
||||
json_object *jobj = json_tokener_parse(req.body.c_str());
|
||||
if (jobj != nullptr)
|
||||
{
|
||||
items = json_object_object_get(jobj, "items");
|
||||
newPath = json_object_get_string(json_object_object_get(jobj, "newPath"));
|
||||
if (items == nullptr || newPath == nullptr)
|
||||
{
|
||||
bad_request(res, "Required items or newPath parameter missing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bad_request(res, "Invalid payload");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string failed_items;
|
||||
size_t len = json_object_array_length(items);
|
||||
for (size_t i=0; i < len; i++)
|
||||
{
|
||||
const char *item = json_object_get_string(json_object_array_get_idx(items, i));
|
||||
DirEntry entry;
|
||||
std::string temp = std::string(item);
|
||||
size_t slash_pos = temp.find_last_of("/");
|
||||
sprintf(entry.name, "%s", temp.substr(slash_pos+1).c_str());
|
||||
sprintf(entry.path, "%s", item);
|
||||
entry.isDir = FS::IsFolder(item);
|
||||
bool ret = CopyOrMove(entry, newPath, false);
|
||||
if (!ret)
|
||||
{
|
||||
failed_items += std::string(item) + ",";
|
||||
}
|
||||
}
|
||||
|
||||
if (failed_items.length() > 0)
|
||||
{
|
||||
std::string error_msg = std::string("One or more file(s) failed to move. ") + failed_items;
|
||||
failed(res, 200, error_msg);
|
||||
}
|
||||
else
|
||||
success(res);
|
||||
});
|
||||
|
||||
svr->Post("/__local__/copy", [&](const Request & req, Response & res)
|
||||
{
|
||||
const json_object *items;
|
||||
const char *newPath;
|
||||
const char *singleFilename;
|
||||
|
||||
json_object *jobj = json_tokener_parse(req.body.c_str());
|
||||
if (jobj != nullptr)
|
||||
{
|
||||
items = json_object_object_get(jobj, "items");
|
||||
newPath = json_object_get_string(json_object_object_get(jobj, "newPath"));
|
||||
singleFilename = json_object_get_string(json_object_object_get(jobj, "singleFilename"));
|
||||
|
||||
if (items == nullptr || newPath == nullptr)
|
||||
{
|
||||
bad_request(res, "Required items or newPath or singleFilename parameter missing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bad_request(res, "Invalid payload");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string failed_items;
|
||||
if (singleFilename != nullptr)
|
||||
{
|
||||
const char *src = json_object_get_string(json_object_array_get_idx(items, 0));
|
||||
std::string dest = std::string(newPath) + "/" + singleFilename;
|
||||
if (dest.compare(src) != 0 && !FS::Copy(src, dest))
|
||||
{
|
||||
failed_items += src;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t len = json_object_array_length(items);
|
||||
for (size_t i=0; i < len; i++)
|
||||
{
|
||||
const char *item = json_object_get_string(json_object_array_get_idx(items, i));
|
||||
DirEntry entry;
|
||||
std::string temp = std::string(item);
|
||||
size_t slash_pos = temp.find_last_of("/");
|
||||
sprintf(entry.name, "%s", temp.substr(slash_pos+1).c_str());
|
||||
sprintf(entry.path, "%s", item);
|
||||
entry.isDir = FS::IsFolder(item);
|
||||
bool ret = CopyOrMove(entry, newPath, true);
|
||||
if (!ret)
|
||||
{
|
||||
failed_items += std::string(item) + ",";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failed_items.length() > 0)
|
||||
{
|
||||
std::string error_msg = std::string("One or more file(s) failed to copy. ") + failed_items;
|
||||
failed(res, 200, error_msg);
|
||||
}
|
||||
else
|
||||
success(res);
|
||||
});
|
||||
|
||||
svr->Post("/__local__/remove", [&](const Request & req, Response & res)
|
||||
{
|
||||
json_object *items;
|
||||
json_object *jobj = json_tokener_parse(req.body.c_str());
|
||||
if (jobj != nullptr)
|
||||
{
|
||||
items = json_object_object_get(jobj, "items");
|
||||
if (items == nullptr)
|
||||
{
|
||||
bad_request(res, "Required items parameter missing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bad_request(res, "Invalid payload");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string failed_items;
|
||||
size_t len = json_object_array_length(items);
|
||||
for (size_t i=0; i < len; i++)
|
||||
{
|
||||
const char *item = json_object_get_string(json_object_array_get_idx(items, i));
|
||||
bool ret = FS::RmRecursive(item);
|
||||
if (!ret)
|
||||
{
|
||||
failed_items += std::string(item) + ",";
|
||||
}
|
||||
}
|
||||
|
||||
if (failed_items.length() > 0)
|
||||
{
|
||||
std::string error_msg = std::string("One or more file(s) failed to delete. ") + failed_items;
|
||||
failed(res, 200, error_msg);
|
||||
}
|
||||
else
|
||||
success(res);
|
||||
});
|
||||
|
||||
svr->Post("/__local__/edit", [&](const Request & req, Response & res)
|
||||
{
|
||||
const char *item;
|
||||
const char *content;
|
||||
size_t content_len;
|
||||
json_object *jobj = json_tokener_parse(req.body.c_str());
|
||||
if (jobj != nullptr)
|
||||
{
|
||||
item = json_object_get_string(json_object_object_get(jobj, "item"));
|
||||
json_object *content_obj = json_object_object_get(jobj, "content");
|
||||
content = json_object_get_string(content_obj);
|
||||
content_len = json_object_get_string_len(content_obj);
|
||||
if (item == nullptr || content == nullptr)
|
||||
{
|
||||
bad_request(res, "Required item or content parameter missing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bad_request(res, "Invalid payload");
|
||||
return;
|
||||
}
|
||||
|
||||
bool ret = FS::Save(item, content, content_len);
|
||||
if (!ret)
|
||||
{
|
||||
failed(res, 200, "Failed to content to file.");
|
||||
return;
|
||||
}
|
||||
|
||||
success(res);
|
||||
});
|
||||
|
||||
svr->Post("/__local__/getContent", [&](const Request & req, Response & res)
|
||||
{
|
||||
const char *item;
|
||||
json_object *jobj = json_tokener_parse(req.body.c_str());
|
||||
if (jobj != nullptr)
|
||||
{
|
||||
item = json_object_get_string(json_object_object_get(jobj, "item"));
|
||||
if (item == nullptr)
|
||||
{
|
||||
bad_request(res, "Required item parameter missing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bad_request(res, "Invalid payload");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<char> content = FS::Load(item);
|
||||
json_object *result = json_object_new_object();
|
||||
json_object_object_add(result, "result", json_object_new_string(content.data()));
|
||||
const char *result_str = json_object_to_json_string(result);
|
||||
res.status = 200;
|
||||
res.set_content(result_str, strlen(result_str), "application/json");
|
||||
});
|
||||
|
||||
svr->Post("/__local__/createFolder", [&](const Request & req, Response & res)
|
||||
{
|
||||
const char *newPath;
|
||||
json_object *jobj = json_tokener_parse(req.body.c_str());
|
||||
if (jobj != nullptr)
|
||||
{
|
||||
newPath = json_object_get_string(json_object_object_get(jobj, "newPath"));
|
||||
if (newPath == nullptr)
|
||||
{
|
||||
bad_request(res, "Required newPath parameter missing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bad_request(res, "Invalid payload");
|
||||
return;
|
||||
}
|
||||
|
||||
FS::MkDirs(newPath);
|
||||
success(res);
|
||||
});
|
||||
|
||||
svr->Post("/__local__/permission", [&](const Request & req, Response & res)
|
||||
{
|
||||
failed(res, 200, "Operation not supported");
|
||||
});
|
||||
|
||||
svr->Post("/__local__/compress", [&](const Request & req, Response & res)
|
||||
{
|
||||
failed(res, 200, "Operation not supported");
|
||||
});
|
||||
|
||||
svr->Post("/__local__/extract", [&](const Request & req, Response & res)
|
||||
{
|
||||
failed(res, 200, "Operation not supported");
|
||||
});
|
||||
|
||||
svr->Post("/__local__/upload", [&](const Request &req, Response &res, const ContentReader &content_reader)
|
||||
{
|
||||
MultipartFormDataItems items;
|
||||
std::string destination;
|
||||
FILE *out = nullptr;
|
||||
std::string new_file;
|
||||
content_reader(
|
||||
[&](const MultipartFormData &item)
|
||||
{
|
||||
items.push_back(item);
|
||||
if (item.name != "destination")
|
||||
{
|
||||
new_file = destination + "/" + item.filename;
|
||||
if (out != nullptr)
|
||||
{
|
||||
FS::Close(out);
|
||||
}
|
||||
out = FS::Create(new_file);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[&](const char *data, size_t data_length)
|
||||
{
|
||||
items.back().content.append(data, data_length);
|
||||
if (items.back().name == "destination")
|
||||
{
|
||||
destination = items.back().content;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbglogger_log("data_length=%lld", data_length);
|
||||
FS::Write(out, data, data_length);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (out != nullptr)
|
||||
{
|
||||
FS::Close(out);
|
||||
}
|
||||
success(res);
|
||||
});
|
||||
|
||||
// Download multiple files as ZIP
|
||||
svr->Get("/__local__/downloadMultiple", [&](const Request & req, Response & res)
|
||||
{
|
||||
const char *path;
|
||||
json_object *jobj = json_tokener_parse(req.body.c_str());
|
||||
if (jobj != nullptr)
|
||||
{
|
||||
path = json_object_get_string(json_object_object_get(jobj, "path"));
|
||||
if (path == nullptr)
|
||||
{
|
||||
failed(res, 200, "One or more file(s) failed to download");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bad_request(res, "Invalid payload");
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// Download single file
|
||||
svr->Get("/__local__/downloadFile", [&](const Request & req, Response & res)
|
||||
{
|
||||
std::string path = req.get_param_value("path", 0);
|
||||
if (path.empty())
|
||||
{
|
||||
bad_request(res, "Failed to download");
|
||||
return;
|
||||
}
|
||||
|
||||
dbglogger_log("path=%s", path.c_str());
|
||||
res.status = 200;
|
||||
});
|
||||
|
||||
svr->Get("/google_auth", [](const Request &req, Response &res)
|
||||
{
|
||||
std::string auth_code = req.get_param_value("code");
|
||||
|
||||
Reference in New Issue
Block a user