initial commit
This commit is contained in:
@@ -10,6 +10,7 @@ compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
CMakeUserPresets.json
|
||||
build
|
||||
|
||||
# CLion
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(ezremote-server)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDONT_HAVE_STRUPR")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive")
|
||||
set(APP_VERSION "1.00")
|
||||
|
||||
add_definitions(-DCPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
add_definitions(-DCPPHTTPLIB_THREAD_POOL_COUNT=64)
|
||||
|
||||
include_directories(
|
||||
source
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME}.elf
|
||||
source/http/httplib.cpp
|
||||
source/server/http_server.cpp
|
||||
source/clients/archiveorg.cpp
|
||||
source/clients/baseclient.cpp
|
||||
source/clients/ftpclient.cpp
|
||||
source/clients/sftpclient.cpp
|
||||
source/clients/nfsclient.cpp
|
||||
source/clients/smbclient.cpp
|
||||
source/clients/webdav.cpp
|
||||
source/config.cpp
|
||||
source/crypt.c
|
||||
source/fs.cpp
|
||||
source/getentropy.c
|
||||
source/orbis_jbc.c
|
||||
source/main.cpp)
|
||||
|
||||
add_self(${PROJECT_NAME}.elf)
|
||||
|
||||
add_pkg(${PROJECT_NAME}.elf ${CMAKE_SOURCE_DIR}/data "EZSR00001" "ezRemote Server" "01.00" 32 0)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME}.elf PRIVATE EZREMOTE_VERSION=${APP_VERSION} CPPHTTPLIB_THREAD_POOL_COUNT=64)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}.elf
|
||||
dbglogger
|
||||
z
|
||||
crypto
|
||||
ssl
|
||||
json-c
|
||||
jbc
|
||||
smb2
|
||||
ssh2
|
||||
nfs
|
||||
kernel_sys
|
||||
SceSysmodule
|
||||
SceNet
|
||||
SceSystemService
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
+110
@@ -0,0 +1,110 @@
|
||||
#ifndef EZ_BASE64_H_
|
||||
#define EZ_BASE64_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
class Base64
|
||||
{
|
||||
public:
|
||||
static int Encode(unsigned char *input, size_t in_len, std::string &out)
|
||||
{
|
||||
static constexpr char sEncodingTable[] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'};
|
||||
|
||||
size_t out_len = 4 * ((in_len + 2) / 3);
|
||||
out.resize(out_len);
|
||||
size_t i;
|
||||
char *p = const_cast<char *>(out.c_str());
|
||||
|
||||
for (i = 0; i < in_len - 2; i += 3)
|
||||
{
|
||||
*p++ = sEncodingTable[(input[i] >> 2) & 0x3F];
|
||||
*p++ = sEncodingTable[((input[i] & 0x3) << 4) | ((int)(input[i + 1] & 0xF0) >> 4)];
|
||||
*p++ = sEncodingTable[((input[i + 1] & 0xF) << 2) | ((int)(input[i + 2] & 0xC0) >> 6)];
|
||||
*p++ = sEncodingTable[input[i + 2] & 0x3F];
|
||||
}
|
||||
if (i < in_len)
|
||||
{
|
||||
*p++ = sEncodingTable[(input[i] >> 2) & 0x3F];
|
||||
if (i == (in_len - 1))
|
||||
{
|
||||
*p++ = sEncodingTable[((input[i] & 0x3) << 4)];
|
||||
*p++ = '=';
|
||||
}
|
||||
else
|
||||
{
|
||||
*p++ = sEncodingTable[((input[i] & 0x3) << 4) | ((int)(input[i + 1] & 0xF0) >> 4)];
|
||||
*p++ = sEncodingTable[((input[i + 1] & 0xF) << 2)];
|
||||
}
|
||||
*p++ = '=';
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Encode(const std::string &input, std::string &out)
|
||||
{
|
||||
return Encode((unsigned char*)input.data(), input.size(), out);
|
||||
}
|
||||
|
||||
static int Decode(const std::string &input, std::string &out)
|
||||
{
|
||||
static constexpr unsigned char kDecodingTable[] = {
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
|
||||
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
|
||||
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64};
|
||||
|
||||
size_t in_len = input.size();
|
||||
if (in_len % 4 != 0)
|
||||
return 0;
|
||||
|
||||
size_t out_len = in_len / 4 * 3;
|
||||
if (input[in_len - 1] == '=')
|
||||
out_len--;
|
||||
if (input[in_len - 2] == '=')
|
||||
out_len--;
|
||||
|
||||
out.resize(out_len);
|
||||
|
||||
for (size_t i = 0, j = 0; i < in_len;)
|
||||
{
|
||||
uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
|
||||
uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
|
||||
uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
|
||||
uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
|
||||
|
||||
uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6);
|
||||
|
||||
if (j < out_len)
|
||||
out[j++] = (triple >> 2 * 8) & 0xFF;
|
||||
if (j < out_len)
|
||||
out[j++] = (triple >> 1 * 8) & 0xFF;
|
||||
if (j < out_len)
|
||||
out[j++] = (triple >> 0 * 8) & 0xFF;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,126 @@
|
||||
#include <lexbor/html/parser.h>
|
||||
#include <lexbor/dom/interfaces/element.h>
|
||||
#include <lexbor/dom/interfaces/node.h>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include "config.h"
|
||||
#include "clients/remote_client.h"
|
||||
#include "clients/archiveorg.h"
|
||||
#include "util.h"
|
||||
|
||||
using httplib::Client;
|
||||
using httplib::Headers;
|
||||
using httplib::Result;
|
||||
|
||||
struct InsensitiveCompare
|
||||
{
|
||||
bool operator()(const std::string &a, const std::string &b) const
|
||||
{
|
||||
return strcasecmp(a.c_str(), b.c_str()) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
static std::set<std::string, InsensitiveCompare> ignore_cookie_keys = {"path", "expires", "max-age", "domain", "secure"};
|
||||
|
||||
std::string ArchiveOrgClient::GenerateRandomId(const int len)
|
||||
{
|
||||
static const char alphanum[] = "0123456789abcdef";
|
||||
std::string tmp_s;
|
||||
tmp_s.reserve(len);
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
tmp_s += alphanum[rand() % (sizeof(alphanum) - 1)];
|
||||
}
|
||||
|
||||
return tmp_s;
|
||||
}
|
||||
|
||||
int ArchiveOrgClient::Connect(const std::string &url, const std::string &username, const std::string &password)
|
||||
{
|
||||
this->host_url = url;
|
||||
size_t scheme_pos = url.find("://");
|
||||
size_t root_pos = url.find("/", scheme_pos + 3);
|
||||
if (root_pos != std::string::npos)
|
||||
{
|
||||
this->host_url = url.substr(0, root_pos);
|
||||
this->base_path = url.substr(root_pos);
|
||||
}
|
||||
client = new httplib::Client(this->host_url);
|
||||
client->set_keep_alive(true);
|
||||
client->set_follow_location(true);
|
||||
client->set_connection_timeout(30);
|
||||
client->set_read_timeout(30);
|
||||
client->enable_server_certificate_verification(false);
|
||||
|
||||
this->cookies = {
|
||||
{"donation-identifier", GenerateRandomId(32)},
|
||||
{"test-cookie", "1"},
|
||||
{"abtest-identifier", GenerateRandomId(32)}
|
||||
};
|
||||
|
||||
if (username.length() > 0)
|
||||
return Login(username, password);
|
||||
|
||||
this->connected = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ArchiveOrgClient::Login(const std::string &username, const std::string &password)
|
||||
{
|
||||
std::string url = std::string("/account/login");
|
||||
Headers headers = {{ "User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0"}};
|
||||
SetCookies(headers);
|
||||
|
||||
MultipartFormDataItems items = {
|
||||
{"username", username, "", ""},
|
||||
{"password", password, "", ""},
|
||||
{"remember", "true", "", ""},
|
||||
{"referer", "https://archive.org/", "", ""},
|
||||
{"login", "true", "", ""},
|
||||
{"submit_by_js", "true", "", ""}};
|
||||
|
||||
if (auto res = client->Post(url, headers, items))
|
||||
{
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
if (res->has_header("Set-Cookie"))
|
||||
{
|
||||
int cookies_count = res->get_header_value_count("Set-Cookie");
|
||||
|
||||
for (int i = 0; i < cookies_count; i++)
|
||||
{
|
||||
std::string cookie_str = res->get_header_value("Set-Cookie", i);
|
||||
|
||||
std::vector<std::string> cookies = Util::Split(cookie_str, ";");
|
||||
for (std::vector<std::string>::iterator it = cookies.begin(); it != cookies.end();)
|
||||
{
|
||||
std::vector<std::string> cookie = Util::Split(*it, "=");
|
||||
std::string key = Util::Trim(cookie[0], " ");
|
||||
if (ignore_cookie_keys.find(key) == ignore_cookie_keys.end())
|
||||
{
|
||||
if (cookie.size() > 1)
|
||||
this->cookies[key] = Util::Trim(cookie[1], " ");
|
||||
else
|
||||
this->cookies[key] = "";
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
this->connected = true;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef EZ_ARCHIVEORG_H
|
||||
#define EZ_ARCHIVEORG_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "clients/remote_client.h"
|
||||
#include "clients/baseclient.h"
|
||||
|
||||
class ArchiveOrgClient : public BaseClient
|
||||
{
|
||||
public:
|
||||
int Connect(const std::string &url, const std::string &username, const std::string &password);
|
||||
|
||||
private:
|
||||
int Login(const std::string &username, const std::string &password);
|
||||
std::string GenerateRandomId(const int len);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,169 @@
|
||||
#include <fstream>
|
||||
#include <curl/curl.h>
|
||||
#include <sys/time.h>
|
||||
#include <orbis/SystemService.h>
|
||||
#include "clients/remote_client.h"
|
||||
#include "clients/baseclient.h"
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
|
||||
using httplib::Client;
|
||||
using httplib::DataSink;
|
||||
using httplib::Headers;
|
||||
using httplib::Result;
|
||||
|
||||
BaseClient::BaseClient(){};
|
||||
|
||||
BaseClient::~BaseClient()
|
||||
{
|
||||
if (client != nullptr)
|
||||
delete client;
|
||||
};
|
||||
|
||||
int BaseClient::SetCookies(Headers &headers)
|
||||
{
|
||||
if (this->cookies.size() > 0)
|
||||
{
|
||||
std::string cookie;
|
||||
for (std::map<std::string, std::string>::iterator it = this->cookies.begin(); it != this->cookies.end();)
|
||||
{
|
||||
cookie.append(it->first).append("=").append(it->second);
|
||||
if (std::next(it, 1) != this->cookies.end())
|
||||
{
|
||||
cookie.append("; ");
|
||||
}
|
||||
++it;
|
||||
}
|
||||
headers.emplace("Cookie", cookie);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int BaseClient::Connect(const std::string &url, const std::string &username, const std::string &password)
|
||||
{
|
||||
this->host_url = url;
|
||||
size_t scheme_pos = url.find("://");
|
||||
size_t root_pos = url.find("/", scheme_pos + 3);
|
||||
if (root_pos != std::string::npos)
|
||||
{
|
||||
this->host_url = url.substr(0, root_pos);
|
||||
this->base_path = url.substr(root_pos);
|
||||
}
|
||||
client = new httplib::Client(this->host_url);
|
||||
if (username.length() > 0)
|
||||
client->set_basic_auth(username, password);
|
||||
client->set_keep_alive(true);
|
||||
client->set_follow_location(true);
|
||||
client->set_connection_timeout(30);
|
||||
client->set_read_timeout(30);
|
||||
client->enable_server_certificate_verification(false);
|
||||
this->connected = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int BaseClient::Get(const std::string &outputfile, const std::string &path, uint64_t offset)
|
||||
{
|
||||
std::ofstream file_stream;
|
||||
if (offset > 0)
|
||||
{
|
||||
file_stream.open(outputfile, std::ofstream::out | std::ofstream::binary | std::ofstream::app);
|
||||
}
|
||||
else
|
||||
{
|
||||
file_stream.open(outputfile, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
|
||||
}
|
||||
|
||||
*g_bytes_transfered = offset;
|
||||
Headers headers;
|
||||
SetCookies(headers);
|
||||
|
||||
if (offset > 0)
|
||||
{
|
||||
char range_header[128];
|
||||
sprintf(range_header, "bytes=%lu-", offset);
|
||||
headers.emplace("Range", range_header);
|
||||
}
|
||||
|
||||
if (auto res = client->Get(GetFullPath(path), headers,
|
||||
[&](const char *data, size_t data_length)
|
||||
{
|
||||
file_stream.write(data, data_length);
|
||||
*g_bytes_transfered = *g_bytes_transfered + data_length;
|
||||
sceSystemServicePowerTick();
|
||||
return true;
|
||||
}))
|
||||
{
|
||||
file_stream.close();
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(this->response, "%s", httplib::to_string(res.error()).c_str());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int BaseClient::GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset)
|
||||
{
|
||||
char range_header[64];
|
||||
sprintf(range_header, "bytes=%lu-%lu", offset, offset + size - 1);
|
||||
Headers headers = {{"Range", range_header}};
|
||||
SetCookies(headers);
|
||||
|
||||
size_t bytes_read = 0;
|
||||
if (auto res = client->Get(GetFullPath(path), headers,
|
||||
[&](const char *data, size_t data_length)
|
||||
{
|
||||
bytes_read += data_length;
|
||||
bool ok = sink.write(data, data_length);
|
||||
sceSystemServicePowerTick();
|
||||
return ok;
|
||||
}))
|
||||
{
|
||||
return bytes_read == size;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(this->response, "%s", httplib::to_string(res.error()).c_str());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string BaseClient::GetPath(std::string ppath1, std::string ppath2)
|
||||
{
|
||||
std::string path1 = ppath1;
|
||||
std::string path2 = ppath2;
|
||||
path1 = Util::Trim(Util::Trim(path1, " "), "/");
|
||||
path2 = Util::Trim(Util::Trim(path2, " "), "/");
|
||||
path1 = this->base_path + ((this->base_path.length() > 0) ? "/" : "") + path1 + "/" + path2;
|
||||
if (path1[0] != '/')
|
||||
path1 = "/" + path1;
|
||||
return path1;
|
||||
}
|
||||
|
||||
std::string BaseClient::GetFullPath(std::string ppath1)
|
||||
{
|
||||
std::string path1 = ppath1;
|
||||
path1 = Util::Trim(Util::Trim(path1, " "), "/");
|
||||
path1 = this->base_path + "/" + path1;
|
||||
Util::ReplaceAll(path1, "//", "/");
|
||||
return path1;
|
||||
}
|
||||
|
||||
const char *BaseClient::LastResponse()
|
||||
{
|
||||
return this->response;
|
||||
}
|
||||
|
||||
int BaseClient::Quit()
|
||||
{
|
||||
if (client != nullptr)
|
||||
{
|
||||
delete client;
|
||||
client = nullptr;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef BASESERVER_H
|
||||
#define BASESERVER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "clients/remote_client.h"
|
||||
|
||||
#define HTTP_SUCCESS(x) (x >= 200 && x < 300)
|
||||
|
||||
class BaseClient : public RemoteClient
|
||||
{
|
||||
public:
|
||||
BaseClient();
|
||||
~BaseClient();
|
||||
int Connect(const std::string &url, const std::string &username, const std::string &password);
|
||||
int Get(const std::string &outputfile, const std::string &path, uint64_t offset=0);
|
||||
int GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset);
|
||||
std::string GetPath(std::string path1, std::string path2);
|
||||
std::string GetFullPath(std::string path1);
|
||||
const char *LastResponse();
|
||||
int Quit();
|
||||
static int DownloadProgressCallback(void* ptr, double dTotalToDownload, double dNowDownloaded, double dTotalToUpload, double dNowUploaded);
|
||||
|
||||
protected:
|
||||
int SetCookies(httplib::Headers &headers);
|
||||
|
||||
httplib::Client *client;
|
||||
std::string base_path;
|
||||
std::string host_url;
|
||||
char response[512];
|
||||
bool connected = false;
|
||||
std::map<std::string, std::string> cookies;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,128 @@
|
||||
#ifndef EZ_FTPCLIENT_H
|
||||
#define EZ_FTPCLIENT_H
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <time.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "clients/remote_client.h"
|
||||
|
||||
#define FTP_CLIENT_MAX_FILENAME_LEN 255
|
||||
|
||||
typedef int (*FtpCallbackXfer)(int64_t xfered, void *arg);
|
||||
|
||||
struct ftphandle
|
||||
{
|
||||
char *cput, *cget;
|
||||
int handle;
|
||||
int cavail, cleft;
|
||||
char *buf;
|
||||
int dir;
|
||||
ftphandle *ctrl;
|
||||
int cmode;
|
||||
int64_t xfered;
|
||||
int64_t xfered1;
|
||||
int64_t cbbytes;
|
||||
char response[512];
|
||||
int64_t offset;
|
||||
bool correctpasv;
|
||||
FtpCallbackXfer xfercb;
|
||||
void *cbarg;
|
||||
bool is_connected;
|
||||
};
|
||||
|
||||
class FtpClient : public RemoteClient
|
||||
{
|
||||
public:
|
||||
enum accesstype
|
||||
{
|
||||
dir = 1,
|
||||
dirverbose,
|
||||
dirmlsd,
|
||||
fileread,
|
||||
filewrite,
|
||||
filereadappend,
|
||||
filewriteappend
|
||||
};
|
||||
|
||||
enum transfermode
|
||||
{
|
||||
ascii = 'A',
|
||||
image = 'I'
|
||||
};
|
||||
|
||||
enum connmode
|
||||
{
|
||||
pasv = 1,
|
||||
port
|
||||
};
|
||||
|
||||
enum attributes
|
||||
{
|
||||
directory = 1,
|
||||
readonly = 2
|
||||
};
|
||||
|
||||
FtpClient();
|
||||
~FtpClient();
|
||||
int Connect(const std::string &url, const std::string &user, const std::string &pass);
|
||||
void SetConnmode(connmode mode);
|
||||
int Site(const std::string &cmd);
|
||||
int Raw(const std::string &cmd);
|
||||
int SysType(char *buf, int max);
|
||||
int Mkdir(const std::string &path);
|
||||
int Chdir(const std::string &path);
|
||||
int Cdup();
|
||||
int Rmdir(const std::string &path);
|
||||
int Size(const std::string &path, uint64_t *size);
|
||||
int Get(const std::string &outputfile, const std::string &path, uint64_t offset = 0);
|
||||
int GetRange(const std::string &path, void *buffer, uint64_t size, uint64_t offset);
|
||||
int GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset);
|
||||
int GetRange(void *fp, void *buffer, uint64_t size, uint64_t offset);
|
||||
int GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset);
|
||||
int Put(const std::string &inputfile, const std::string &path, uint64_t offset = 0);
|
||||
int Rename(const std::string &src, const std::string &dst);
|
||||
int Delete(const std::string &path);
|
||||
int Head(const std::string &path, void *buffer, uint64_t len);
|
||||
void *Open(const std::string &path, int flags);
|
||||
void Close(void *fp);
|
||||
void SetCallbackXferFunction(FtpCallbackXfer pointer);
|
||||
void SetCallbackArg(void *arg);
|
||||
void SetCallbackBytes(int64_t bytes);
|
||||
bool Noop();
|
||||
bool Ping();
|
||||
bool FileExists(const std::string &path);
|
||||
bool IsConnected();
|
||||
char *LastResponse();
|
||||
long GetIdleTime();
|
||||
int Quit();
|
||||
|
||||
private:
|
||||
ftphandle *mp_ftphandle;
|
||||
struct tm cur_time;
|
||||
timeval tick;
|
||||
char server[128];
|
||||
int server_port;
|
||||
|
||||
int FtpSendCmd(const std::string &cmd, const std::string &expected_resp, ftphandle *nControl);
|
||||
ftphandle *RawOpen(const std::string &path, accesstype type, transfermode mode);
|
||||
int RawClose(ftphandle *handle);
|
||||
int RawWrite(void *buf, int len, ftphandle *handle);
|
||||
int RawRead(void *buf, int max, ftphandle *handle);
|
||||
int ReadResponse(const std::string &c, ftphandle *nControl);
|
||||
int Readline(char *buf, int max, ftphandle *nControl);
|
||||
int Writeline(char *buf, int len, ftphandle *nData);
|
||||
void ClearHandle();
|
||||
int FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mode, int dir, std::string &cmd);
|
||||
int FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mode, int dir, std::string &cmd);
|
||||
int FtpAcceptConnection(ftphandle *nData, ftphandle *nControl);
|
||||
int CorrectPasvResponse(int *v);
|
||||
int FtpAccess(const std::string &path, accesstype type, transfermode mode, ftphandle *nControl, ftphandle **nData);
|
||||
int FtpXfer(const std::string &localfile, const std::string &path, ftphandle *nControl, accesstype type, transfermode mode);
|
||||
int FtpWrite(void *buf, int len, ftphandle *nData);
|
||||
int FtpRead(void *buf, int max, ftphandle *nData);
|
||||
int FtpClose(ftphandle *nData);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,201 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <errno.h>
|
||||
#include <orbis/SystemService.h>
|
||||
#include "clients/nfsclient.h"
|
||||
#include "config.h"
|
||||
#include "fs.h"
|
||||
|
||||
#define BUF_SIZE 1048576
|
||||
|
||||
NfsClient::NfsClient()
|
||||
{
|
||||
}
|
||||
|
||||
NfsClient::~NfsClient()
|
||||
{
|
||||
}
|
||||
|
||||
int NfsClient::Connect(const std::string &url, const std::string &user, const std::string &pass)
|
||||
{
|
||||
nfs = nfs_init_context();
|
||||
if (nfs == nullptr)
|
||||
{
|
||||
sprintf(response, "%s", "Failed to init nfs context");
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct nfs_url *nfsurl = nfs_parse_url_full(nfs, url.c_str());
|
||||
if (nfsurl == nullptr) {
|
||||
sprintf(response, "%s", nfs_get_error(nfs));
|
||||
nfs_destroy_context(nfs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string export_path = std::string(nfsurl->path) + nfsurl->file;
|
||||
int ret = nfs_mount(nfs, nfsurl->server, export_path.c_str());
|
||||
if (ret != 0)
|
||||
{
|
||||
sprintf(response, "%s", nfs_get_error(nfs));
|
||||
nfs_destroy_url(nfsurl);
|
||||
nfs_destroy_context(nfs);
|
||||
nfs = nullptr;
|
||||
return 0;
|
||||
}
|
||||
nfs_destroy_url(nfsurl);
|
||||
|
||||
connected = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* LastResponse - return a pointer to the last response received
|
||||
*/
|
||||
const char *NfsClient::LastResponse()
|
||||
{
|
||||
return (const char *)response;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Quit - disconnect from remote
|
||||
*
|
||||
* return 1 if successful, 0 otherwise
|
||||
*/
|
||||
int NfsClient::Quit()
|
||||
{
|
||||
if (nfs != nullptr)
|
||||
{
|
||||
nfs_umount(nfs);
|
||||
nfs_destroy_context(nfs);
|
||||
nfs = nullptr;
|
||||
}
|
||||
connected = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get - issue a GET command and write received data to output
|
||||
*
|
||||
* return 1 if successful, 0 otherwise
|
||||
*/
|
||||
|
||||
int NfsClient::Get(const std::string &outputfile, const std::string &ppath, uint64_t offset)
|
||||
{
|
||||
struct nfsfh *nfsfh = nullptr;
|
||||
int ret = nfs_open(nfs, ppath.c_str(), 0400, &nfsfh);
|
||||
if (ret != 0)
|
||||
{
|
||||
sprintf(response, "%s", nfs_get_error(nfs));
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE* out = NULL;
|
||||
if (offset > 0)
|
||||
{
|
||||
out = FS::Append(outputfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
out = FS::Create(outputfile);
|
||||
}
|
||||
|
||||
|
||||
if (out == NULL)
|
||||
{
|
||||
// sprintf(response, "%s", lang_strings[STR_FAILED]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *buff = malloc(BUF_SIZE);
|
||||
int count = 0;
|
||||
*g_bytes_transfered = offset;
|
||||
if (offset > 0)
|
||||
{
|
||||
nfs_lseek(nfs, nfsfh, offset, SEEK_SET, NULL);
|
||||
}
|
||||
|
||||
while ((count = nfs_read(nfs, nfsfh, BUF_SIZE, buff)) > 0)
|
||||
{
|
||||
if (count < 0)
|
||||
{
|
||||
sprintf(response, "%s", nfs_get_error(nfs));
|
||||
FS::Close(out);
|
||||
nfs_close(nfs, nfsfh);
|
||||
free((void*)buff);
|
||||
return 0;
|
||||
}
|
||||
FS::Write(out, buff, count);
|
||||
*g_bytes_transfered += count;
|
||||
sceSystemServicePowerTick();
|
||||
}
|
||||
|
||||
FS::Close(out);
|
||||
nfs_close(nfs, nfsfh);
|
||||
free((void*)buff);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int NfsClient::GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset)
|
||||
{
|
||||
struct nfsfh *nfsfh = nullptr;
|
||||
int ret = nfs_open(nfs, path.c_str(), 0400, &nfsfh);
|
||||
if (ret != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = this->GetRange((void *)nfsfh, sink, size, offset);
|
||||
nfs_close(nfs, nfsfh);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int NfsClient::GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset)
|
||||
{
|
||||
struct nfsfh *nfsfh = (struct nfsfh *)fp;
|
||||
|
||||
int ret = nfs_lseek(nfs, nfsfh, offset, SEEK_SET, NULL);
|
||||
if (ret != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *buff = malloc(BUF_SIZE);
|
||||
int count = 0;
|
||||
size_t bytes_remaining = size;
|
||||
do
|
||||
{
|
||||
size_t bytes_to_read = std::min<size_t>(BUF_SIZE, bytes_remaining);
|
||||
count = nfs_read(nfs, nfsfh, bytes_to_read, buff);
|
||||
if (count > 0)
|
||||
{
|
||||
bytes_remaining -= count;
|
||||
bool ok = sink.write((char *)buff, count);
|
||||
if (!ok)
|
||||
{
|
||||
free((void *)buff);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
free((void *)buff);
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef NFSCLIENT_H
|
||||
#define NFSCLIENT_H
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <time.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "nfsc/libnfs.h"
|
||||
#include "nfsc/libnfs-raw.h"
|
||||
#include "nfsc/libnfs-raw-mount.h"
|
||||
#include "clients/remote_client.h"
|
||||
|
||||
class NfsClient : public RemoteClient
|
||||
{
|
||||
public:
|
||||
NfsClient();
|
||||
~NfsClient();
|
||||
int Connect(const std::string &url, const std::string &user, const std::string &pass);
|
||||
int Get(const std::string &outputfile, const std::string &path, uint64_t offset=0);
|
||||
int GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset);
|
||||
int GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset);
|
||||
const char *LastResponse();
|
||||
int Quit();
|
||||
|
||||
private:
|
||||
int _Rmdir(const std::string &ppath);
|
||||
struct nfs_context *nfs;
|
||||
char response[1024];
|
||||
bool connected = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef REMOTECLIENT_H
|
||||
#define REMOTECLIENT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "http/httplib.h"
|
||||
|
||||
enum ClientType
|
||||
{
|
||||
CLIENT_TYPE_FTP,
|
||||
CLIENT_TYPE_SFTP,
|
||||
CLIENT_TYPE_SMB,
|
||||
CLIENT_TYPE_WEBDAV,
|
||||
CLIENT_TYPE_HTTP_SERVER,
|
||||
CLIENT_TYPE_NFS,
|
||||
CLIENT_TYPE_FILEHOST,
|
||||
CLINET_TYPE_UNKNOWN
|
||||
};
|
||||
|
||||
using namespace httplib;
|
||||
|
||||
class RemoteClient
|
||||
{
|
||||
public:
|
||||
RemoteClient(){};
|
||||
virtual ~RemoteClient(){};
|
||||
virtual int Connect(const std::string &url, const std::string &username, const std::string &password) = 0;
|
||||
virtual int Get(const std::string &outputfile, const std::string &path, uint64_t offset=0) = 0;
|
||||
virtual int GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset) = 0;
|
||||
virtual const char *LastResponse() = 0;
|
||||
virtual int Quit() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,314 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <time.h>
|
||||
#include <orbis/SystemService.h>
|
||||
#include "clients/remote_client.h"
|
||||
#include "clients/sftpclient.h"
|
||||
#include "fs.h"
|
||||
#include "config.h"
|
||||
|
||||
#define FTP_CLIENT_BUFSIZ 1048576
|
||||
|
||||
SFTPClient::SFTPClient()
|
||||
{
|
||||
session = nullptr;
|
||||
sftp_session = nullptr;
|
||||
sock = 0;
|
||||
};
|
||||
|
||||
SFTPClient::~SFTPClient(){};
|
||||
|
||||
int SFTPClient::Connect(const std::string &url, const std::string &username, const std::string &password)
|
||||
{
|
||||
int port = 22;
|
||||
std::string host = url.substr(7);
|
||||
size_t colon_pos = host.find(":");
|
||||
if (colon_pos != std::string::npos)
|
||||
{
|
||||
port = std::atoi(host.substr(colon_pos + 1).c_str());
|
||||
host = host.substr(0, colon_pos);
|
||||
}
|
||||
|
||||
struct hostent *he;
|
||||
struct in_addr **addr_list;
|
||||
char ip[20];
|
||||
int i;
|
||||
|
||||
if (strcmp(host.c_str(), "localhost") == 0)
|
||||
{
|
||||
sprintf(ip, "%s", "127.0.0.1");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((he = gethostbyname(host.c_str())) == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
addr_list = (struct in_addr **)he->h_addr_list;
|
||||
for (i = 0; addr_list[i] != NULL; i++)
|
||||
{
|
||||
strcpy(ip, inet_ntoa(*addr_list[i]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
in_addr dst_addr;
|
||||
sockaddr_in server_addr;
|
||||
int on = 1;
|
||||
int32_t retval;
|
||||
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
inet_pton(AF_INET, ip, (void *)&dst_addr);
|
||||
server_addr.sin_addr = dst_addr;
|
||||
server_addr.sin_port = htons(port);
|
||||
server_addr.sin_family = AF_INET;
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
retval = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof(on));
|
||||
int const size = FTP_CLIENT_BUFSIZ;
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) == -1)
|
||||
{
|
||||
close(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) == -1)
|
||||
{
|
||||
close(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (connect(sock, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr_in)) != 0)
|
||||
{
|
||||
sprintf(this->response, "%s", "Failed to connect!");
|
||||
return 0;
|
||||
}
|
||||
/* Create a session instance
|
||||
*/
|
||||
session = libssh2_session_init();
|
||||
libssh2_session_set_blocking(session, 1);
|
||||
libssh2_keepalive_config(session, 1, 5);
|
||||
|
||||
if (!session)
|
||||
{
|
||||
sprintf(this->response, "Failed to connect");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ... start it up. This will trade welcome banners, exchange keys,
|
||||
* and setup crypto, compression, and MAC layers
|
||||
*/
|
||||
usleep(100000);
|
||||
int rc = libssh2_session_handshake(session, sock);
|
||||
if (rc)
|
||||
{
|
||||
sprintf(this->response, "Failed SSL handshake %d", rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* At this point we havn't yet authenticated. The first thing to do
|
||||
* is check the hostkey's fingerprint against our known hosts Your app
|
||||
* may have it hard coded, may go to a file, may present it to the
|
||||
* user, that's your call
|
||||
*/
|
||||
const char *fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
|
||||
|
||||
/* check what authentication methods are available */
|
||||
char *userauthlist = libssh2_userauth_list(session, username.c_str(), username.length());
|
||||
|
||||
int auth_pw = 0;
|
||||
if (strstr(userauthlist, "password") != NULL)
|
||||
{
|
||||
auth_pw |= 1;
|
||||
}
|
||||
if (strstr(userauthlist, "keyboard-interactive") != NULL)
|
||||
{
|
||||
auth_pw |= 2;
|
||||
}
|
||||
if (strstr(userauthlist, "publickey") != NULL)
|
||||
{
|
||||
auth_pw |= 4;
|
||||
}
|
||||
|
||||
bool use_identity = password.find("file://") != std::string::npos;
|
||||
if (auth_pw & 1 && !use_identity)
|
||||
{
|
||||
/* We could authenticate via password */
|
||||
if (libssh2_userauth_password(session, username.c_str(), password.c_str()))
|
||||
{
|
||||
sprintf(this->response, "%s", "Authentication by password failed!");
|
||||
goto shutdown;
|
||||
}
|
||||
}
|
||||
else if (auth_pw & 4 && use_identity)
|
||||
{
|
||||
/* Or by public key */
|
||||
std::string publickey = password.substr(7) + "/id_rsa.pub";
|
||||
std::string privatekey = password.substr(7) + "/id_rsa";
|
||||
if (!FS::FileExists(publickey.c_str()))
|
||||
{
|
||||
sprintf(response, "SSH public key %s is not found", publickey.c_str());
|
||||
goto shutdown;
|
||||
}
|
||||
if (!FS::FileExists(privatekey.c_str()))
|
||||
{
|
||||
sprintf(response, "SSH private key %s is not found", privatekey.c_str());
|
||||
goto shutdown;
|
||||
}
|
||||
if (libssh2_userauth_publickey_fromfile(session, username.c_str(), publickey.c_str(), privatekey.c_str(), ""))
|
||||
{
|
||||
sprintf(this->response, "%s", "Authentication by public key failed!");
|
||||
goto shutdown;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(this->response, "%s", "No supported authentication methods found!");
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
sftp_session = libssh2_sftp_init(session);
|
||||
this->connected = true;
|
||||
return 1;
|
||||
|
||||
shutdown:
|
||||
libssh2_session_disconnect(session, "Normal Shutdown");
|
||||
libssh2_session_free(session);
|
||||
close(sock);
|
||||
libssh2_exit();
|
||||
session = nullptr;
|
||||
sock = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SFTPClient::Get(const std::string &outputfile, const std::string &path, uint64_t offset)
|
||||
{
|
||||
LIBSSH2_SFTP_HANDLE *sftp_handle = libssh2_sftp_open(sftp_session, path.c_str(), LIBSSH2_FXF_READ, 0);
|
||||
if (!sftp_handle)
|
||||
{
|
||||
sprintf(response, "Unable to open file with SFTP: %ld", libssh2_sftp_last_error(sftp_session));
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE* out = NULL;
|
||||
if (offset > 0)
|
||||
{
|
||||
out = FS::Append(outputfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
out = FS::Create(outputfile);
|
||||
}
|
||||
|
||||
if (out == NULL)
|
||||
{
|
||||
// sprintf(response, "%s", lang_strings[STR_FAILED]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *buff = (char *)malloc(FTP_CLIENT_BUFSIZ);
|
||||
int rc, count = 0;
|
||||
*g_bytes_transfered = offset;
|
||||
if (offset > 0)
|
||||
{
|
||||
libssh2_sftp_seek64(sftp_handle, offset);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_read(sftp_handle, buff, FTP_CLIENT_BUFSIZ);
|
||||
if (rc > 0)
|
||||
{
|
||||
*g_bytes_transfered += rc;
|
||||
FS::Write(out, buff, rc);
|
||||
sceSystemServicePowerTick();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
free((char *)buff);
|
||||
FS::Close(out);
|
||||
libssh2_sftp_close(sftp_handle);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int SFTPClient::GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset)
|
||||
{
|
||||
LIBSSH2_SFTP_HANDLE *sftp_handle = libssh2_sftp_open(sftp_session, path.c_str(), LIBSSH2_FXF_READ, 0);
|
||||
if (!sftp_handle)
|
||||
{
|
||||
sprintf(response, "Unable to open file with SFTP: %ld", libssh2_sftp_last_error(sftp_session));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ret = this->GetRange((void *)sftp_handle, sink, size, offset);
|
||||
libssh2_sftp_close(sftp_handle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SFTPClient::GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset)
|
||||
{
|
||||
LIBSSH2_SFTP_HANDLE *sftp_handle = (LIBSSH2_SFTP_HANDLE *)fp;
|
||||
|
||||
libssh2_sftp_seek64(sftp_handle, offset);
|
||||
|
||||
char *buff = (char *)malloc(FTP_CLIENT_BUFSIZ);
|
||||
int rc, count = 0;
|
||||
size_t bytes_remaining = size;
|
||||
do
|
||||
{
|
||||
size_t bytes_to_read = std::min<size_t>(FTP_CLIENT_BUFSIZ, bytes_remaining);
|
||||
rc = libssh2_sftp_read(sftp_handle, buff, bytes_to_read);
|
||||
if (rc > 0)
|
||||
{
|
||||
bytes_remaining -= rc;
|
||||
bool ok = sink.write(buff, rc);
|
||||
if (!ok)
|
||||
{
|
||||
free((char *)buff);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
free((char *)buff);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *SFTPClient::LastResponse()
|
||||
{
|
||||
return this->response;
|
||||
}
|
||||
|
||||
int SFTPClient::Quit()
|
||||
{
|
||||
if (sftp_session != nullptr)
|
||||
libssh2_sftp_shutdown(sftp_session);
|
||||
if (session != nullptr)
|
||||
{
|
||||
libssh2_session_disconnect(session, "Normal Shutdown");
|
||||
libssh2_session_free(session);
|
||||
close(sock);
|
||||
libssh2_exit();
|
||||
}
|
||||
session = nullptr;
|
||||
sftp_session = nullptr;
|
||||
sock = 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef EZ_SFTPCLIENT_H
|
||||
#define EZ_SFTPCLIENT_H
|
||||
|
||||
#include <libssh2.h>
|
||||
#include <libssh2_sftp.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "clients/remote_client.h"
|
||||
|
||||
class SFTPClient : public RemoteClient
|
||||
{
|
||||
public:
|
||||
SFTPClient();
|
||||
~SFTPClient();
|
||||
int Connect(const std::string &url, const std::string &username, const std::string &password);
|
||||
int Get(const std::string &outputfile, const std::string &path, uint64_t offset=0);
|
||||
int GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset);
|
||||
int GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset);
|
||||
const char *LastResponse();
|
||||
int Quit();
|
||||
|
||||
protected:
|
||||
LIBSSH2_SESSION *session;
|
||||
LIBSSH2_SFTP *sftp_session;
|
||||
int sock;
|
||||
char response[512];
|
||||
bool connected = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,196 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <errno.h>
|
||||
#include <orbis/SystemService.h>
|
||||
#include "config.h"
|
||||
#include "fs.h"
|
||||
#include "clients/smbclient.h"
|
||||
#include "util.h"
|
||||
|
||||
SmbClient::SmbClient()
|
||||
{
|
||||
}
|
||||
|
||||
SmbClient::~SmbClient()
|
||||
{
|
||||
}
|
||||
|
||||
int SmbClient::Connect(const std::string &url, const std::string &user, const std::string &pass)
|
||||
{
|
||||
struct smb2_url *smb_url;
|
||||
|
||||
smb2 = smb2_init_context();
|
||||
if (smb2 == NULL)
|
||||
{
|
||||
sprintf(response, "Failed to init SMB context");
|
||||
return 0;
|
||||
}
|
||||
|
||||
smb_url = smb2_parse_url(smb2, url.c_str());
|
||||
if (smb_url == NULL || smb_url->share == NULL || strlen(smb_url->share) == 0)
|
||||
{
|
||||
sprintf(response, "Invalid SMB Url");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pass.length() > 0)
|
||||
smb2_set_password(smb2, pass.c_str());
|
||||
smb2_set_security_mode(smb2, SMB2_NEGOTIATE_SIGNING_ENABLED);
|
||||
smb2_set_version(smb2, SMB2_VERSION_ANY);
|
||||
smb2_set_timeout(smb2, 30);
|
||||
|
||||
if (smb2_connect_share(smb2, smb_url->server, smb_url->share, user.c_str()) < 0)
|
||||
{
|
||||
sprintf(response, "%s", smb2_get_error(smb2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
smb2_destroy_url(smb_url);
|
||||
connected = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* SmbLastResponse - return a pointer to the last response received
|
||||
*/
|
||||
const char *SmbClient::LastResponse()
|
||||
{
|
||||
return (const char *)response;
|
||||
}
|
||||
|
||||
/*
|
||||
* SmbQuit - disconnect from remote
|
||||
*
|
||||
* return 1 if successful, 0 otherwise
|
||||
*/
|
||||
int SmbClient::Quit()
|
||||
{
|
||||
smb2_destroy_context(smb2);
|
||||
smb2 = NULL;
|
||||
connected = false;
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* SmbGet - issue a GET command and write received data to output
|
||||
*
|
||||
* return 1 if successful, 0 otherwise
|
||||
*/
|
||||
|
||||
int SmbClient::Get(const std::string &outputfile, const std::string &ppath, uint64_t offset)
|
||||
{
|
||||
std::string path = std::string(ppath);
|
||||
path = Util::Trim(path, "/");
|
||||
|
||||
struct smb2fh* in = smb2_open(smb2, path.c_str(), O_RDONLY);
|
||||
if (in == NULL)
|
||||
{
|
||||
sprintf(response, "%s", smb2_get_error(smb2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE* out = NULL;
|
||||
if (offset > 0)
|
||||
{
|
||||
out = FS::Append(outputfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
out = FS::Create(outputfile);
|
||||
}
|
||||
|
||||
if (out == NULL)
|
||||
{
|
||||
// sprintf(response, "%s", lang_strings[STR_FAILED]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t *buff = (uint8_t*)malloc(max_read_size);
|
||||
int count = 0;
|
||||
*g_bytes_transfered = offset;
|
||||
|
||||
if (offset > 0)
|
||||
{
|
||||
smb2_lseek(smb2, in, offset, SEEK_SET, NULL);
|
||||
}
|
||||
|
||||
while ((count = smb2_read(smb2, in, buff, max_read_size)) > 0)
|
||||
{
|
||||
if (count < 0)
|
||||
{
|
||||
sprintf(response, "%s", smb2_get_error(smb2));
|
||||
FS::Close(out);
|
||||
smb2_close(smb2, in);
|
||||
free((void*)buff);
|
||||
return 0;
|
||||
}
|
||||
FS::Write(out, buff, count);
|
||||
*g_bytes_transfered += count;
|
||||
sceSystemServicePowerTick();
|
||||
}
|
||||
|
||||
FS::Close(out);
|
||||
smb2_close(smb2, in);
|
||||
free((void*)buff);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int SmbClient::GetRange(const std::string &ppath, DataSink &sink, uint64_t size, uint64_t offset)
|
||||
{
|
||||
std::string path = std::string(ppath);
|
||||
path = Util::Trim(path, "/");
|
||||
struct smb2fh *in = smb2_open(smb2, path.c_str(), O_RDONLY);
|
||||
if (in == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ret = this->GetRange((void *)in, sink, size, offset);
|
||||
smb2_close(smb2, in);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SmbClient::GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset)
|
||||
{
|
||||
struct smb2fh *in = (struct smb2fh *)fp;
|
||||
|
||||
smb2_lseek(smb2, in, offset, SEEK_SET, NULL);
|
||||
|
||||
uint8_t *buff = (uint8_t *)malloc(max_read_size);
|
||||
int count = 0;
|
||||
size_t bytes_remaining = size;
|
||||
do
|
||||
{
|
||||
size_t bytes_to_read = std::min<size_t>(max_read_size, bytes_remaining);
|
||||
count = smb2_read(smb2, in, buff, bytes_to_read);
|
||||
if (count > 0)
|
||||
{
|
||||
bytes_remaining -= count;
|
||||
bool ok = sink.write((char *)buff, count);
|
||||
if (!ok)
|
||||
{
|
||||
free((uint8_t *)buff);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
free((char *)buff);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef SMBCLIENT_H
|
||||
#define SMBCLIENT_H
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <time.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <smb2/smb2.h>
|
||||
#include <smb2/libsmb2.h>
|
||||
#include "clients/remote_client.h"
|
||||
|
||||
#define SMB_CLIENT_MAX_FILENAME_LEN 256
|
||||
|
||||
class SmbClient : public RemoteClient
|
||||
{
|
||||
public:
|
||||
SmbClient();
|
||||
~SmbClient();
|
||||
int Connect(const std::string &url, const std::string &user, const std::string &pass);
|
||||
int Get(const std::string &outputfile, const std::string &path, uint64_t offset=0);
|
||||
int GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset);
|
||||
int GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset);
|
||||
const char *LastResponse();
|
||||
int Quit();
|
||||
|
||||
private:
|
||||
struct smb2_context *smb2;
|
||||
char response[1024];
|
||||
bool connected = false;
|
||||
uint32_t max_read_size = 1048576;
|
||||
uint32_t max_write_size = 1048576;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
#include <sys/time.h>
|
||||
#include "clients/remote_client.h"
|
||||
#include "clients/webdav.h"
|
||||
#include "util.h"
|
||||
|
||||
std::string WebDAVClient::GetHttpUrl(std::string url)
|
||||
{
|
||||
std::string http_url = std::regex_replace(url, std::regex("webdav://"), "http://");
|
||||
http_url = std::regex_replace(http_url, std::regex("webdavs://"), "https://");
|
||||
return http_url;
|
||||
}
|
||||
|
||||
int WebDAVClient::Connect(const std::string &host, const std::string &user, const std::string &pass)
|
||||
{
|
||||
std::string url = GetHttpUrl(host);
|
||||
return BaseClient::Connect(url, user, pass);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef WEBDAV_H
|
||||
#define WEBDAV_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "clients/baseclient.h"
|
||||
#include "clients/remote_client.h"
|
||||
|
||||
class WebDAVClient : public BaseClient
|
||||
{
|
||||
public:
|
||||
int Connect(const std::string &url, const std::string &user, const std::string &pass);
|
||||
|
||||
private:
|
||||
static std::string GetHttpUrl(std::string url);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,257 @@
|
||||
// #include <orbis/UserService.h>
|
||||
// #include <orbis/Net.h>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <regex>
|
||||
//#include <shared_mutex>
|
||||
#include <stdlib.h>
|
||||
#include <json-c/json.h>
|
||||
#include "server/http_server.h"
|
||||
#include "config.h"
|
||||
#include "fs.h"
|
||||
#include "crypt.h"
|
||||
#include "base64.h"
|
||||
#include "util.h"
|
||||
|
||||
static std::map<std::string, PackageInstallData> pkg_download_history;
|
||||
std::vector<BgDownloadData> bg_download_list;
|
||||
|
||||
unsigned char cipher_key[32] = {'s', '5', 'v', '8', 'y', '/', 'B', '?', 'E', '(', 'H', '+', 'M', 'b', 'Q', 'e', 'T', 'h', 'W', 'm', 'Z', 'q', '4', 't', '7', 'w', '9', 'z', '$', 'C', '&', 'F'};
|
||||
unsigned char cipher_iv[16] = {'Y', 'p', '3', 's', '6', 'v', '9', 'y', '$', 'B', '&', 'E', ')', 'H', '@', 'M'};
|
||||
|
||||
//std::shared_mutex pkg_mutex_;
|
||||
//std::shared_mutex download_mutex_;
|
||||
uint64_t *g_bytes_transfered;
|
||||
|
||||
namespace CONFIG
|
||||
{
|
||||
int Encrypt(const std::string &text, std::string &encrypt_text)
|
||||
{
|
||||
unsigned char tmp_encrypt_text[text.length() * 2];
|
||||
int encrypt_text_len;
|
||||
memset(tmp_encrypt_text, 0, sizeof(tmp_encrypt_text));
|
||||
int ret = openssl_encrypt((unsigned char *)text.c_str(), text.length(), cipher_key, cipher_iv, tmp_encrypt_text, &encrypt_text_len);
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
return Base64::Encode(std::string((const char *)tmp_encrypt_text, encrypt_text_len), encrypt_text);
|
||||
}
|
||||
|
||||
int Decrypt(const std::string &text, std::string &decrypt_text)
|
||||
{
|
||||
std::string tmp_decode_text;
|
||||
int ret = Base64::Decode(text, tmp_decode_text);
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
|
||||
unsigned char tmp_decrypt_text[tmp_decode_text.length() * 2];
|
||||
int decrypt_text_len;
|
||||
memset(tmp_decrypt_text, 0, sizeof(tmp_decrypt_text));
|
||||
ret = openssl_decrypt((unsigned char *)tmp_decode_text.c_str(), tmp_decode_text.length(), cipher_key, cipher_iv, tmp_decrypt_text, &decrypt_text_len);
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
|
||||
decrypt_text.clear();
|
||||
decrypt_text.append(std::string((const char *)tmp_decrypt_text, decrypt_text_len));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
PackageInstallData* GetPackageInstallHostData(const std::string &hash)
|
||||
{
|
||||
if (pkg_download_history.find(hash) != pkg_download_history.end())
|
||||
return &pkg_download_history[hash];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AddPackageInstallHostData(const std::string &hash, PackageInstallData pkg_data)
|
||||
{
|
||||
//std::unique_lock<std::shared_mutex> lock(pkg_mutex_);
|
||||
std::pair<std::string, PackageInstallData> pair = std::make_pair(hash, pkg_data);
|
||||
pkg_download_history.erase(hash);
|
||||
pkg_download_history.insert(pair);
|
||||
}
|
||||
|
||||
void RemovePackageInstallHostData(const std::string &hash)
|
||||
{
|
||||
//std::unique_lock<std::shared_mutex> lock(pkg_mutex_);
|
||||
pkg_download_history.erase(hash);
|
||||
}
|
||||
|
||||
void LoadPackageInstallHostData()
|
||||
{
|
||||
if (FS::FileExists(PKG_INSTALL_HISTORY_PATH))
|
||||
{
|
||||
json_object *jobj = json_object_from_file(PKG_INSTALL_HISTORY_PATH);
|
||||
struct array_list *history_list = json_object_get_array(jobj);
|
||||
|
||||
for (size_t history_idx = 0; history_idx < history_list->length; ++history_idx)
|
||||
{
|
||||
PackageInstallData history_item;
|
||||
|
||||
json_object *history_item_obj = (json_object *)array_list_get_idx(history_list, history_idx);
|
||||
std::string hash = std::string(json_object_get_string(json_object_object_get(history_item_obj, "hash")));
|
||||
history_item.host_info.url = std::string(json_object_get_string(json_object_object_get(history_item_obj, "url")));
|
||||
history_item.path = std::string(json_object_get_string(json_object_object_get(history_item_obj, "path")));
|
||||
history_item.host_info.username = std::string(json_object_get_string(json_object_object_get(history_item_obj, "username")));
|
||||
std::string encrypted_password = std::string(json_object_get_string(json_object_object_get(history_item_obj, "password")));
|
||||
history_item.host_info.type = json_object_get_int(json_object_object_get(history_item_obj, "type"));
|
||||
history_item.timestamp = json_object_get_uint64(json_object_object_get(history_item_obj, "timestamp"));
|
||||
history_item.client = nullptr;
|
||||
|
||||
if (history_item.host_info.type == CLIENT_TYPE_HTTP_SERVER)
|
||||
{
|
||||
history_item.host_info.http_server_type = std::string(json_object_get_string(json_object_object_get(history_item_obj, "http_server_type")));
|
||||
}
|
||||
|
||||
int ret = Decrypt(encrypted_password, history_item.host_info.password);
|
||||
if (ret == 0)
|
||||
{
|
||||
history_item.host_info.password = encrypted_password;
|
||||
}
|
||||
AddPackageInstallHostData(hash, history_item);
|
||||
}
|
||||
json_object_put(jobj);
|
||||
}
|
||||
}
|
||||
|
||||
void SavePackageInstallHostData()
|
||||
{
|
||||
//std::unique_lock<std::shared_mutex> lock(pkg_mutex_);
|
||||
|
||||
if (!FS::FolderExists(DATA_PATH))
|
||||
{
|
||||
FS::MkDirs(DATA_PATH);
|
||||
}
|
||||
|
||||
json_object *history_list = json_object_new_array();
|
||||
uint64_t current_time = Util::GetTick();
|
||||
|
||||
for (auto it = pkg_download_history.begin(); it != pkg_download_history.end(); ++it)
|
||||
{
|
||||
if (current_time - it->second.timestamp < MAX_PKG_HISTORY_RETENTION)
|
||||
{
|
||||
json_object *history_item_obj = json_object_new_object();
|
||||
json_object_object_add(history_item_obj, "hash", json_object_new_string(it->first.c_str()));
|
||||
json_object_object_add(history_item_obj, "url", json_object_new_string(it->second.host_info.url.c_str()));
|
||||
json_object_object_add(history_item_obj, "path", json_object_new_string(it->second.path.c_str()));
|
||||
json_object_object_add(history_item_obj, "username", json_object_new_string(it->second.host_info.username.c_str()));
|
||||
json_object_object_add(history_item_obj, "type", json_object_new_int(it->second.host_info.type));
|
||||
json_object_object_add(history_item_obj, "timestamp", json_object_new_uint64(it->second.timestamp));
|
||||
if (it->second.host_info.type == CLIENT_TYPE_HTTP_SERVER)
|
||||
{
|
||||
json_object_object_add(history_item_obj, "http_server_type", json_object_new_string(it->second.host_info.http_server_type.c_str()));
|
||||
}
|
||||
|
||||
std::string encrypted_password;
|
||||
if (!it->second.host_info.password.empty())
|
||||
{
|
||||
Encrypt(it->second.host_info.password, encrypted_password);
|
||||
}
|
||||
json_object_object_add(history_item_obj, "password", json_object_new_string(encrypted_password.c_str()));
|
||||
|
||||
json_object_array_add(history_list, history_item_obj);
|
||||
}
|
||||
}
|
||||
|
||||
json_object_to_file(PKG_INSTALL_HISTORY_PATH, history_list);
|
||||
json_object_put(history_list);
|
||||
}
|
||||
|
||||
void AddBgDownloadData(BgDownloadData pkg_data)
|
||||
{
|
||||
//std::unique_lock<std::shared_mutex> lock(download_mutex_);
|
||||
bg_download_list.push_back(pkg_data);
|
||||
}
|
||||
|
||||
void LoadBgDownloadData()
|
||||
{
|
||||
if (FS::FileExists(BG_DOWNLOAD_HISTORY_PATH))
|
||||
{
|
||||
json_object *jobj = json_object_from_file(BG_DOWNLOAD_HISTORY_PATH);
|
||||
struct array_list *history_list = json_object_get_array(jobj);
|
||||
|
||||
for (size_t history_idx = 0; history_idx < history_list->length; ++history_idx)
|
||||
{
|
||||
BgDownloadData history_item;
|
||||
|
||||
json_object *history_item_obj = (json_object *)array_list_get_idx(history_list, history_idx);
|
||||
history_item.host_info.type = json_object_get_int(json_object_object_get(history_item_obj, "type"));
|
||||
history_item.host_info.url = std::string(json_object_get_string(json_object_object_get(history_item_obj, "url")));
|
||||
history_item.host_info.username = std::string(json_object_get_string(json_object_object_get(history_item_obj, "username")));
|
||||
std::string encrypted_password = std::string(json_object_get_string(json_object_object_get(history_item_obj, "password")));
|
||||
|
||||
if (history_item.host_info.type == CLIENT_TYPE_HTTP_SERVER)
|
||||
{
|
||||
history_item.host_info.http_server_type = std::string(json_object_get_string(json_object_object_get(history_item_obj, "http_server_type")));
|
||||
}
|
||||
|
||||
int ret = Decrypt(encrypted_password, history_item.host_info.password);
|
||||
if (ret == 0)
|
||||
{
|
||||
history_item.host_info.password = encrypted_password;
|
||||
}
|
||||
|
||||
history_item.src_path = std::string(json_object_get_string(json_object_object_get(history_item_obj, "src_path")));
|
||||
history_item.dest_path = std::string(json_object_get_string(json_object_object_get(history_item_obj, "dest_path")));
|
||||
history_item.file_size = json_object_get_uint64(json_object_object_get(history_item_obj, "file_size"));
|
||||
history_item.bytes_transfered = json_object_get_uint64(json_object_object_get(history_item_obj, "bytes_transfered"));
|
||||
history_item.state = static_cast<DownloadState>(json_object_get_int(json_object_object_get(history_item_obj, "state")));
|
||||
history_item.id = json_object_get_uint64(json_object_object_get(history_item_obj, "id"));
|
||||
history_item.timestamp = json_object_get_uint64(json_object_object_get(history_item_obj, "timestamp"));
|
||||
|
||||
AddBgDownloadData(history_item);
|
||||
}
|
||||
json_object_put(jobj);
|
||||
}
|
||||
}
|
||||
|
||||
void SaveBgDownloadData()
|
||||
{
|
||||
//std::unique_lock<std::shared_mutex> lock(download_mutex_);
|
||||
|
||||
if (!FS::FolderExists(DATA_PATH))
|
||||
{
|
||||
FS::MkDirs(DATA_PATH);
|
||||
}
|
||||
|
||||
json_object *history_list = json_object_new_array();
|
||||
uint64_t current_time = Util::GetTick();
|
||||
|
||||
for (auto it = bg_download_list.begin(); it != bg_download_list.end(); ++it)
|
||||
{
|
||||
if (current_time - it->timestamp < MAX_PKG_HISTORY_RETENTION)
|
||||
{
|
||||
json_object *history_item_obj = json_object_new_object();
|
||||
json_object_object_add(history_item_obj, "type", json_object_new_int(it->host_info.type));
|
||||
json_object_object_add(history_item_obj, "url", json_object_new_string(it->host_info.url.c_str()));
|
||||
if (it->host_info.type == CLIENT_TYPE_HTTP_SERVER)
|
||||
{
|
||||
json_object_object_add(history_item_obj, "http_server_type", json_object_new_string(it->host_info.http_server_type.c_str()));
|
||||
}
|
||||
|
||||
std::string encrypted_password;
|
||||
if (!it->host_info.password.empty())
|
||||
{
|
||||
Encrypt(it->host_info.password, encrypted_password);
|
||||
}
|
||||
|
||||
json_object_object_add(history_item_obj, "username", json_object_new_string(it->host_info.username.c_str()));
|
||||
json_object_object_add(history_item_obj, "password", json_object_new_string(encrypted_password.c_str()));
|
||||
json_object_object_add(history_item_obj, "src_path", json_object_new_string(it->src_path.c_str()));
|
||||
json_object_object_add(history_item_obj, "dest_path", json_object_new_string(it->dest_path.c_str()));
|
||||
json_object_object_add(history_item_obj, "file_size", json_object_new_uint64(it->file_size));
|
||||
json_object_object_add(history_item_obj, "bytes_transfered", json_object_new_uint64(it->bytes_transfered));
|
||||
json_object_object_add(history_item_obj, "state", json_object_new_int(it->state));
|
||||
json_object_object_add(history_item_obj, "id", json_object_new_uint64(it->id));
|
||||
json_object_object_add(history_item_obj, "timestamp", json_object_new_uint64(it->timestamp));
|
||||
|
||||
json_object_array_add(history_list, history_item_obj);
|
||||
}
|
||||
}
|
||||
|
||||
json_object_to_file(BG_DOWNLOAD_HISTORY_PATH, history_list);
|
||||
json_object_put(history_list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#ifndef EZ_CONFIG_H
|
||||
#define EZ_CONFIG_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "clients/remote_client.h"
|
||||
|
||||
#define APP_ID "ezremote-client"
|
||||
#define DATA_PATH "/data/homebrew/" APP_ID
|
||||
#define PKG_INSTALL_HISTORY_PATH DATA_PATH "/pkg_install_history.json"
|
||||
#define BG_DOWNLOAD_HISTORY_PATH DATA_PATH "/bg_download_history.json"
|
||||
#define DEBUG_SERVER_LOG_PATH DATA_PATH "/ezremote_server.log"
|
||||
#define NOTIFY_ICON_FILE "/user" DATA_PATH "/sce_sys/icon0.png"
|
||||
#define CLIENT_ELF_PATH DATA_PATH "/ezremote_client.elf"
|
||||
|
||||
#define HTTP_SERVER_APACHE "Apache"
|
||||
#define HTTP_SERVER_MS_IIS "Microsoft IIS"
|
||||
#define HTTP_SERVER_NGINX "Nginx"
|
||||
#define HTTP_SERVER_NPX_SERVE "Serve"
|
||||
#define HTTP_SERVER_RCLONE "RClone"
|
||||
#define HTTP_SERVER_ARCHIVEORG "Archive.org"
|
||||
#define HTTP_SERVER_MYRIENT "Myrient"
|
||||
#define HTTP_SERVER_GITHUB "Github"
|
||||
|
||||
#define MAX_PKG_HISTORY_RETENTION 1209600000000L
|
||||
|
||||
enum DownloadState { STATE_PENDING, STATE_DOWNLOADING, STATE_RESUMED, STATE_FAILED, STATE_SUCCESS };
|
||||
|
||||
struct HostInfo
|
||||
{
|
||||
int type;
|
||||
std::string url;
|
||||
std::string http_server_type;
|
||||
std::string username;
|
||||
std::string password;
|
||||
};
|
||||
|
||||
struct PackageInstallData
|
||||
{
|
||||
HostInfo host_info;
|
||||
RemoteClient *client;
|
||||
std::string path;
|
||||
uint64_t timestamp;
|
||||
};
|
||||
|
||||
struct BgDownloadData {
|
||||
HostInfo host_info;
|
||||
std::string src_path;
|
||||
std::string dest_path;
|
||||
uint64_t bytes_transfered;
|
||||
uint64_t file_size;
|
||||
DownloadState state;
|
||||
uint64_t id;
|
||||
uint64_t timestamp;
|
||||
};
|
||||
|
||||
extern uint64_t *g_bytes_transfered;
|
||||
extern std::vector<BgDownloadData> bg_download_list;
|
||||
|
||||
namespace CONFIG
|
||||
{
|
||||
PackageInstallData* GetPackageInstallHostData(const std::string &hash);
|
||||
void AddPackageInstallHostData(const std::string &hash, PackageInstallData pkg_data);
|
||||
void RemovePackageInstallHostData(const std::string &hash);
|
||||
void LoadPackageInstallHostData();
|
||||
void SavePackageInstallHostData();
|
||||
void AddBgDownloadData(BgDownloadData bg_download_data);
|
||||
void LoadBgDownloadData();
|
||||
void SaveBgDownloadData();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "crypt.h"
|
||||
|
||||
int openssl_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,
|
||||
unsigned char *iv, unsigned char *ciphertext, int *ciphertext_len)
|
||||
{
|
||||
EVP_CIPHER_CTX *ctx;
|
||||
|
||||
int len;
|
||||
|
||||
/* Create and initialise the context */
|
||||
if (!(ctx = EVP_CIPHER_CTX_new()))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Initialise the encryption operation. IMPORTANT - ensure you use a key
|
||||
* and IV size appropriate for your cipher
|
||||
* In this example we are using 256 bit AES (i.e. a 256 bit key). The
|
||||
* IV size for *most* modes is the same as the block size. For AES this
|
||||
* is 128 bits
|
||||
*/
|
||||
if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Provide the message to be encrypted, and obtain the encrypted output.
|
||||
* EVP_EncryptUpdate can be called multiple times if necessary
|
||||
*/
|
||||
if (1 != EVP_CipherUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
|
||||
return 0;
|
||||
*ciphertext_len = len;
|
||||
|
||||
/*
|
||||
* Finalise the encryption. Further ciphertext bytes may be written at
|
||||
* this stage.
|
||||
*/
|
||||
if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))
|
||||
return 0;
|
||||
*ciphertext_len += len;
|
||||
|
||||
/* Clean up */
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int openssl_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
|
||||
unsigned char *iv, unsigned char *plaintext, int *plaintext_len)
|
||||
{
|
||||
EVP_CIPHER_CTX *ctx;
|
||||
|
||||
int len;
|
||||
|
||||
/* Create and initialise the context */
|
||||
if (!(ctx = EVP_CIPHER_CTX_new()))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Initialise the decryption operation. IMPORTANT - ensure you use a key
|
||||
* and IV size appropriate for your cipher
|
||||
* In this example we are using 256 bit AES (i.e. a 256 bit key). The
|
||||
* IV size for *most* modes is the same as the block size. For AES this
|
||||
* is 128 bits
|
||||
*/
|
||||
if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Provide the message to be decrypted, and obtain the plaintext output.
|
||||
* EVP_DecryptUpdate can be called multiple times if necessary.
|
||||
*/
|
||||
if (1 != EVP_CipherUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
|
||||
return 0;
|
||||
*plaintext_len = len;
|
||||
|
||||
/*
|
||||
* Finalise the decryption. Further plaintext bytes may be written at
|
||||
* this stage.
|
||||
*/
|
||||
if (1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len))
|
||||
return 0;
|
||||
*plaintext_len += len;
|
||||
|
||||
/* Clean up */
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
int openssl_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,
|
||||
unsigned char *iv, unsigned char *ciphertext, int *ciphertext_len);
|
||||
|
||||
int openssl_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
|
||||
unsigned char *iv, unsigned char *plaintext, int *plaintext_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
*
|
||||
* DBGLOGGER - debug logger library / (c) 2019 El Bucanero <www.bucanero.com.ar>
|
||||
*
|
||||
* Small library for network and local file debug logging in PSL1GHT/Open Orbis SDKs.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
|
||||
#define netConnect connect
|
||||
#define netClose close
|
||||
#define netSend send
|
||||
#define netSocket socket
|
||||
#define netInitialize(...)
|
||||
#define netDeinitialize(...)
|
||||
|
||||
#include "dbglogger.h"
|
||||
|
||||
typedef enum {
|
||||
ENCODE_BASE64,
|
||||
ENCODE_UUENCODE
|
||||
} B64ENC_MODES;
|
||||
|
||||
static int loggerMode = NO_LOGGER;
|
||||
static int socketFD;
|
||||
static char logFile[256];
|
||||
|
||||
#define UDP_INI_STR "udp"
|
||||
#define TCP_INI_STR "tcp"
|
||||
#define FILE_INI_STR "file"
|
||||
#define TTY_INI_STR "tty"
|
||||
#define DEBUG_PORT 18194
|
||||
#define BASE64_LENGTH(X) (4 * ((X + 2) / 3))
|
||||
#define B64_SRC_BUF_SIZE 45 // This *MUST* be a multiple of 3
|
||||
#define B64_DST_BUF_SIZE BASE64_LENGTH(B64_SRC_BUF_SIZE)
|
||||
|
||||
static const char encode_table[2][65] = {
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
||||
"`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`"};
|
||||
|
||||
/*
|
||||
* Copyright (C) 2000 by Glenn McGrath
|
||||
*
|
||||
* based on the function base64_encode from http.c in wget v1.6
|
||||
* Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
|
||||
*
|
||||
* Encode the string S of length LENGTH to base64 format and place it
|
||||
* to STORE. STORE will be 0-terminated, and must point to a writable
|
||||
* buffer of at least 1+BASE64_LENGTH(length) bytes.
|
||||
* where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3))
|
||||
*/
|
||||
static void uuencode(const unsigned char *s, char *store, const int length, const char *tbl)
|
||||
{
|
||||
int i;
|
||||
unsigned char *p = (unsigned char *)store;
|
||||
|
||||
/* Transform the 3x8 bits to 4x6 bits, as required by base64. */
|
||||
for (i = 0; i < length; i += 3) {
|
||||
*p++ = tbl[s[0] >> 2];
|
||||
*p++ = tbl[((s[0] & 0x03) << 4) + (s[1] >> 4)];
|
||||
*p++ = tbl[((s[1] & 0x0f) << 2) + (s[2] >> 6)];
|
||||
*p++ = tbl[s[2] & 0x3f];
|
||||
s += 3;
|
||||
}
|
||||
/* Pad the result if necessary... */
|
||||
if (i == length + 1) {
|
||||
*(p - 1) = tbl[64];
|
||||
}
|
||||
else if (i == length + 2) {
|
||||
*(p - 1) = *(p - 2) = tbl[64];
|
||||
}
|
||||
/* ...and zero-terminate it. */
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
static int dbglogger_base64(const char *filename, const unsigned int encode)
|
||||
{
|
||||
FILE *src_stream;
|
||||
size_t size;
|
||||
unsigned char *src_buf;
|
||||
char *dst_buf;
|
||||
|
||||
src_stream = fopen(filename, "rb");
|
||||
if (!src_stream) {
|
||||
return(0);
|
||||
}
|
||||
src_buf = malloc(B64_SRC_BUF_SIZE + 1);
|
||||
dst_buf = malloc(B64_DST_BUF_SIZE + 1);
|
||||
|
||||
dbglogger_printf("begin%s %o %s", encode == ENCODE_UUENCODE ? "" : "-base64", 0644, strrchr(filename, '/')+1);
|
||||
while ((size = fread(src_buf, 1, B64_SRC_BUF_SIZE, src_stream)) > 0) {
|
||||
if (size != B64_SRC_BUF_SIZE) {
|
||||
/* pad with 0s so we can just encode extra bits */
|
||||
memset(&src_buf[size], 0, B64_SRC_BUF_SIZE - size);
|
||||
}
|
||||
/* Encode the buffer we just read in */
|
||||
uuencode(src_buf, dst_buf, size, encode_table[encode]);
|
||||
|
||||
switch (encode) {
|
||||
case ENCODE_BASE64:
|
||||
dbglogger_printf("\n%s", dst_buf);
|
||||
break;
|
||||
case ENCODE_UUENCODE:
|
||||
dbglogger_printf("\n%c%s", encode_table[encode][size], dst_buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
dbglogger_printf(encode == ENCODE_UUENCODE ? "\n`\nend\n" : "\n====\n");
|
||||
|
||||
free(src_buf);
|
||||
free(dst_buf);
|
||||
fclose(src_stream);
|
||||
return(1);
|
||||
}
|
||||
|
||||
int dbglogger_b64encode(const char *filename)
|
||||
{
|
||||
return dbglogger_base64(filename, ENCODE_BASE64);
|
||||
}
|
||||
|
||||
char* dbg_base64_encode(const unsigned char *data, int data_len)
|
||||
{
|
||||
char* out = malloc(BASE64_LENGTH(data_len) + 1);
|
||||
|
||||
if (!out)
|
||||
return NULL;
|
||||
|
||||
uuencode(data, out, data_len, encode_table[ENCODE_BASE64]);
|
||||
return (out);
|
||||
}
|
||||
|
||||
/*
|
||||
* base64_decode - Base64 decode
|
||||
* @src: Data to be decoded
|
||||
* @out_len: Pointer to output length variable
|
||||
* Returns: Allocated buffer of out_len bytes of decoded data, or NULL on failure
|
||||
*
|
||||
* Caller is responsible for freeing the returned buffer.
|
||||
*/
|
||||
unsigned char * dbg_base64_decode(const char *src, size_t *out_len)
|
||||
{
|
||||
unsigned char dtable[256], *out, *pos, block[4], tmp;
|
||||
size_t i, count, len = strlen(src);
|
||||
int pad = 0;
|
||||
|
||||
memset(dtable, 0x80, 256);
|
||||
for (i = 0; i < sizeof(encode_table[ENCODE_BASE64]) - 1; i++)
|
||||
dtable[(unsigned char)encode_table[ENCODE_BASE64][i]] = (unsigned char) i;
|
||||
dtable['='] = 0;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (dtable[(unsigned char)src[i]] != 0x80)
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0 || count % 4)
|
||||
return NULL;
|
||||
|
||||
pos = out = malloc(count / 4 * 3);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
tmp = dtable[(unsigned char)src[i]];
|
||||
if (tmp == 0x80)
|
||||
continue;
|
||||
|
||||
if (src[i] == '=')
|
||||
pad++;
|
||||
block[count] = tmp;
|
||||
count++;
|
||||
if (count == 4) {
|
||||
*pos++ = (block[0] << 2) | (block[1] >> 4);
|
||||
*pos++ = (block[1] << 4) | (block[2] >> 2);
|
||||
*pos++ = (block[2] << 6) | block[3];
|
||||
count = 0;
|
||||
if (pad) {
|
||||
if (pad == 1)
|
||||
pos--;
|
||||
else if (pad == 2)
|
||||
pos -= 2;
|
||||
else {
|
||||
// Invalid padding
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = pos - out;
|
||||
return out;
|
||||
}
|
||||
|
||||
#ifdef __PPU__
|
||||
// check if we receive a connection and kill the process
|
||||
static void debug_netkill_thread(void *port)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(strtoul(port, NULL, 0));
|
||||
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
int list_s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
|
||||
if ((bind(list_s, (struct sockaddr *)&sa, sizeof(sa)) == -1) || (listen(list_s, 4) == -1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while(accept(list_s, NULL, NULL) <= 0)
|
||||
{
|
||||
usleep(1000*1000);
|
||||
}
|
||||
|
||||
shutdown(list_s, SHUT_RDWR);
|
||||
dbglogger_stop();
|
||||
sysProcessExit(1);
|
||||
}
|
||||
|
||||
// check if the file exists and kill the process
|
||||
static void debug_kill_thread(void* path)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
while ((stat((char*) path, &sb) != 0) || !S_ISREG(sb.st_mode))
|
||||
{
|
||||
usleep(1000*1000);
|
||||
}
|
||||
|
||||
chmod((char*) path, 0777);
|
||||
sysLv2FsUnlink((char*) path);
|
||||
dbglogger_stop();
|
||||
sysProcessExit(1);
|
||||
}
|
||||
|
||||
int dbglogger_failsafe(const char* fpath)
|
||||
{
|
||||
sys_ppu_thread_t tid;
|
||||
|
||||
return sysThreadCreate(&tid, (fpath[0] == '/' ? debug_kill_thread : debug_netkill_thread), (void*) fpath, 1000, 16*1024, THREAD_JOINABLE, "debug_wait");
|
||||
}
|
||||
#endif
|
||||
|
||||
static void networkInit(const char* dbglog_ip, const unsigned short dbglog_port) {
|
||||
struct sockaddr_in stSockAddr;
|
||||
|
||||
memset(&stSockAddr, 0, sizeof(stSockAddr));
|
||||
stSockAddr.sin_family = AF_INET;
|
||||
stSockAddr.sin_port = htons(dbglog_port);
|
||||
#ifdef __PS2__
|
||||
stSockAddr.sin_addr.s_addr = inet_addr(dbglog_ip);
|
||||
#else
|
||||
inet_pton(AF_INET, dbglog_ip, &stSockAddr.sin_addr);
|
||||
#endif
|
||||
netConnect(socketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr));
|
||||
}
|
||||
|
||||
static int logFileInit(const char* file_path, unsigned short overwrite) {
|
||||
snprintf(logFile, sizeof(logFile), "%s", file_path);
|
||||
FILE *fp = fopen(logFile, overwrite ? "w" : "a");
|
||||
|
||||
if (fp) {
|
||||
fclose(fp);
|
||||
} else {
|
||||
loggerMode = NO_LOGGER;
|
||||
}
|
||||
return(loggerMode);
|
||||
}
|
||||
|
||||
static void fileLog(const char* str) {
|
||||
FILE *fp = fopen(logFile, "a");
|
||||
|
||||
if (fp) {
|
||||
fputs(str, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void dbglogger_printf(const char* fmt, ...) {
|
||||
if (loggerMode) {
|
||||
char buffer[0x800];
|
||||
va_list arg;
|
||||
va_start(arg, fmt);
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
switch (loggerMode) {
|
||||
case UDP_LOGGER:
|
||||
case TCP_LOGGER:
|
||||
netSend(socketFD, buffer, strlen(buffer), 0);
|
||||
break;
|
||||
|
||||
case FILE_LOGGER:
|
||||
fileLog(buffer);
|
||||
break;
|
||||
case TTY_LOGGER:
|
||||
printf("%s", buffer); // puts always append newline
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dbglogger_log(const char* fmt, ...) {
|
||||
if (loggerMode) {
|
||||
char buffer[0x800];
|
||||
|
||||
va_list arg;
|
||||
va_start(arg, fmt);
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, arg);
|
||||
va_end(arg);
|
||||
#ifdef __PSP__
|
||||
ScePspDateTime t;
|
||||
sceRtcGetCurrentClockLocalTime(&t);
|
||||
dbglogger_printf("[%d-%02d-%02d %02d:%02d:%02d] %s\n", t.year, t.month, t.day, t.hour, t.minute, t.second, buffer);
|
||||
#else
|
||||
struct tm t = *gmtime(&(time_t){time(NULL)});
|
||||
|
||||
dbglogger_printf("[%d-%02d-%02d %02d:%02d:%02d] %s\n", t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, buffer);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
int dbglogger_init_mode(const unsigned char log_mode, const char* dest, const unsigned short port) {
|
||||
loggerMode = log_mode;
|
||||
switch (log_mode) {
|
||||
case UDP_LOGGER:
|
||||
netInitialize();
|
||||
socketFD = netSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
networkInit(dest, port);
|
||||
dbglogger_log("------ UDP (%s:%d) network debug logger initialized -----", dest, port);
|
||||
break;
|
||||
|
||||
case TCP_LOGGER:
|
||||
netInitialize();
|
||||
socketFD = netSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
networkInit(dest, port);
|
||||
dbglogger_log("------ TCP (%s:%d) network debug logger initialized -----", dest, port);
|
||||
break;
|
||||
|
||||
case FILE_LOGGER:
|
||||
if (logFileInit(dest, port))
|
||||
dbglogger_log("----- File (%s) debug logger initialized -----", dest);
|
||||
break;
|
||||
|
||||
case TTY_LOGGER:
|
||||
dbglogger_log("------ TTY logger initialized ------");
|
||||
break;
|
||||
|
||||
default:
|
||||
loggerMode = NO_LOGGER;
|
||||
// Logging disabled
|
||||
break;
|
||||
}
|
||||
|
||||
return(loggerMode);
|
||||
}
|
||||
|
||||
int dbglogger_init_str(const char* ini_str) {
|
||||
char str[128];
|
||||
strcpy(str, ini_str);
|
||||
|
||||
char *mode = strtok(str, ":");
|
||||
char *data = strtok(NULL, ":");
|
||||
char *tmp = strtok(NULL, ":");
|
||||
unsigned short port = DEBUG_PORT;
|
||||
|
||||
if (tmp)
|
||||
port = strtoul(tmp, NULL, 0);
|
||||
|
||||
if (strcmp(mode, UDP_INI_STR) == 0) {
|
||||
return dbglogger_init_mode(UDP_LOGGER, data, port);
|
||||
} else
|
||||
if (strcmp(mode, TCP_INI_STR) == 0) {
|
||||
return dbglogger_init_mode(TCP_LOGGER, data, port);
|
||||
} else
|
||||
if (strcmp(mode, FILE_INI_STR) == 0) {
|
||||
return dbglogger_init_mode(FILE_LOGGER, data, 0);
|
||||
} else
|
||||
if (strcmp(mode, TTY_INI_STR) == 0) {
|
||||
return dbglogger_init_mode(TTY_LOGGER, NULL, 0);
|
||||
}
|
||||
|
||||
return(NO_LOGGER);
|
||||
}
|
||||
|
||||
int dbglogger_init_file(const char* ini_file) {
|
||||
char str[128];
|
||||
FILE *fp = fopen(ini_file, "r");
|
||||
|
||||
if (fp) {
|
||||
fgets(str, sizeof(str), fp);
|
||||
fclose(fp);
|
||||
return(dbglogger_init_str(str));
|
||||
}
|
||||
return(NO_LOGGER);
|
||||
}
|
||||
|
||||
int dbglogger_init(void) {
|
||||
return(dbglogger_init_str(DEFAULT_LOG_INIT));
|
||||
}
|
||||
|
||||
int dbglogger_stop(void) {
|
||||
switch (loggerMode) {
|
||||
case UDP_LOGGER:
|
||||
case TCP_LOGGER:
|
||||
dbglogger_log("------ network debug logger terminated -----");
|
||||
netClose(socketFD);
|
||||
netDeinitialize();
|
||||
break;
|
||||
|
||||
case FILE_LOGGER:
|
||||
dbglogger_log("------ file debug logger terminated -----");
|
||||
break;
|
||||
|
||||
default:
|
||||
// Logging disabled
|
||||
break;
|
||||
}
|
||||
loggerMode = NO_LOGGER;
|
||||
return(loggerMode);
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
*
|
||||
* DBGLOGGER - debug logger library / (c) 2019 El Bucanero <www.bucanero.com.ar>
|
||||
*
|
||||
|
||||
By default the logger will send debug messages to UDP multicast address 239.255.0.100:30000.
|
||||
To receive them you can use socat on your PC:
|
||||
|
||||
$ socat udp4-recv:30000,ip-add-membership=239.255.0.100:0.0.0.0 -
|
||||
|
||||
TCP/UDP Usage:
|
||||
1. Set the correct IP/port to your computer (default port 18194)
|
||||
2. Execute a local tool to listen to the incoming messages (e.g. netcat, socat)
|
||||
|
||||
Try any of these commands in your terminal:
|
||||
|
||||
UDP
|
||||
$ nc -l -u 18194
|
||||
$ socat udp-recv:18194 stdout
|
||||
|
||||
TCP
|
||||
$ nc -l -k 18194
|
||||
$ socat tcp-listen:18194 stdout
|
||||
|
||||
3. Start the app on your console.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef LIBDEBUGLOG_H
|
||||
#define LIBDEBUGLOG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DEFAULT_LOG_INIT "udp:239.255.0.100:30000"
|
||||
// TCP example string "tcp:192.168.1.123:18194"
|
||||
// File example string "file:/dev_hdd0/temp/app.log"
|
||||
|
||||
|
||||
typedef enum {
|
||||
NO_LOGGER,
|
||||
UDP_LOGGER,
|
||||
TCP_LOGGER,
|
||||
FILE_LOGGER,
|
||||
TTY_LOGGER
|
||||
} LOGGER_MODES;
|
||||
|
||||
typedef struct {
|
||||
char method;
|
||||
char *resource;
|
||||
} dWebRequest_t;
|
||||
|
||||
typedef struct {
|
||||
char type[8];
|
||||
char *data;
|
||||
int64_t size;
|
||||
} dWebResponse_t;
|
||||
|
||||
/*
|
||||
int webReq_GetHandler(dWebRequest_t* request, dWebResponse_t* response, void* usr_data);
|
||||
*/
|
||||
typedef int (*dWebReqHandler_t)(dWebRequest_t*, dWebResponse_t*, void*);
|
||||
|
||||
|
||||
int dbglogger_init(void);
|
||||
int dbglogger_init_str(const char* ini_str);
|
||||
int dbglogger_init_mode(const unsigned char log_mode, const char* dest, const unsigned short port);
|
||||
int dbglogger_init_file(const char* ini_file);
|
||||
|
||||
int dbglogger_stop(void);
|
||||
|
||||
// function to print with format string similar to printf
|
||||
void dbglogger_printf(const char* fmt, ...);
|
||||
|
||||
// function that prints "[timestamp] log \n" similar to printf
|
||||
void dbglogger_log(const char* fmt, ...);
|
||||
|
||||
// starts a thread that terminates the process if the file exists
|
||||
int dbglogger_failsafe(const char* fpath);
|
||||
|
||||
// screenshot method
|
||||
int dbglogger_screenshot(const char* filename, const unsigned char alpha);
|
||||
|
||||
// screenshot will be placed in /dev_hdd0/tmp/screenshot_YYYY_MM_DD_HH_MM_SS.bmp
|
||||
int dbglogger_screenshot_tmp(const unsigned char alpha);
|
||||
|
||||
// base64 file encoding method
|
||||
int dbglogger_b64encode(const char* filename);
|
||||
|
||||
// base64 data encoding method
|
||||
char* dbg_base64_encode(const unsigned char *data, int data_len);
|
||||
|
||||
// base64 data decoding method
|
||||
unsigned char * dbg_base64_decode(const char *src, size_t *out_len);
|
||||
|
||||
// web server methods
|
||||
int dbg_webserver_start(int port, dWebReqHandler_t req, void* usr_data);
|
||||
void dbg_webserver_stop();
|
||||
|
||||
// a simple http server handler that serves the system '/' root folder
|
||||
int dbg_simpleWebServerHandler(dWebRequest_t* req, dWebResponse_t* res, void* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+481
@@ -0,0 +1,481 @@
|
||||
#include "fs.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <dirent.h>
|
||||
#include <sys/time.h>
|
||||
// #include <filesystem>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <sys/stat.h>
|
||||
#include "util.h"
|
||||
|
||||
#if defined(EZREMOTE_ENABLE_UI)
|
||||
#include "windows.h"
|
||||
#endif
|
||||
|
||||
namespace FS
|
||||
{
|
||||
int hasEndSlash(const char *path)
|
||||
{
|
||||
return path[strlen(path) - 1] == '/';
|
||||
}
|
||||
|
||||
void MkDirs(const std::string &ppath, bool prev)
|
||||
{
|
||||
std::string path = ppath;
|
||||
if (!prev)
|
||||
{
|
||||
path.push_back('/');
|
||||
}
|
||||
auto ptr = path.begin();
|
||||
while (true)
|
||||
{
|
||||
ptr = std::find(ptr, path.end(), '/');
|
||||
if (ptr == path.end())
|
||||
break;
|
||||
|
||||
char last = *ptr;
|
||||
*ptr = 0;
|
||||
int err = mkdir(path.c_str(), 0777);
|
||||
*ptr = last;
|
||||
++ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Rm(const std::string &file)
|
||||
{
|
||||
remove(file.c_str());
|
||||
}
|
||||
|
||||
void RmDir(const std::string &path)
|
||||
{
|
||||
rmdir(path.c_str());
|
||||
}
|
||||
|
||||
int64_t GetSize(const std::string &path)
|
||||
{
|
||||
struct stat file_stat = {0};
|
||||
int err = stat(path.c_str(), &file_stat);
|
||||
if (err < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return file_stat.st_size;
|
||||
}
|
||||
|
||||
bool FileExists(const std::string &path)
|
||||
{
|
||||
struct stat file_stat = {0};
|
||||
return (stat(path.c_str(), std::addressof(file_stat)) == 0 && S_ISREG(file_stat.st_mode));
|
||||
}
|
||||
|
||||
bool FolderExists(const std::string &path)
|
||||
{
|
||||
struct stat dir_stat = {0};
|
||||
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());
|
||||
}
|
||||
|
||||
FILE *Create(const std::string &path)
|
||||
{
|
||||
FILE *fd = fopen(path.c_str(), "w");
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
FILE *OpenRW(const std::string &path)
|
||||
{
|
||||
FILE *fd = fopen(path.c_str(), "w+");
|
||||
return fd;
|
||||
}
|
||||
|
||||
FILE *OpenRead(const std::string &path)
|
||||
{
|
||||
FILE *fd = fopen(path.c_str(), "rb");
|
||||
return fd;
|
||||
}
|
||||
|
||||
FILE *Append(const std::string &path)
|
||||
{
|
||||
FILE *fd = fopen(path.c_str(), "ab");
|
||||
return fd;
|
||||
}
|
||||
|
||||
int64_t Seek(FILE *f, uint64_t offset)
|
||||
{
|
||||
auto const pos = fseek(f, offset, SEEK_SET);
|
||||
return pos;
|
||||
}
|
||||
|
||||
int Read(FILE *f, void *buffer, uint32_t size)
|
||||
{
|
||||
const auto read = fread(buffer, 1, size, f);
|
||||
return read;
|
||||
}
|
||||
|
||||
int Write(FILE *f, const void *buffer, uint32_t size)
|
||||
{
|
||||
int write = fwrite(buffer, 1, size, f);
|
||||
return write;
|
||||
}
|
||||
|
||||
void Close(FILE *fd)
|
||||
{
|
||||
int err = fclose(fd);
|
||||
}
|
||||
|
||||
std::vector<char> Load(const std::string &path)
|
||||
{
|
||||
FILE *fd = fopen(path.c_str(), "rb");
|
||||
if (fd == nullptr)
|
||||
return std::vector<char>(0);
|
||||
const auto size = GetSize(path);
|
||||
std::vector<char> data(size);
|
||||
|
||||
const auto read = fread(data.data(), 1, data.size(), fd);
|
||||
fclose(fd);
|
||||
if (read < 0)
|
||||
return std::vector<char>(0);
|
||||
|
||||
data.resize(read+1);
|
||||
data[data.size()-1]=0;
|
||||
return data;
|
||||
}
|
||||
|
||||
bool LoadText(std::vector<std::string> *lines, const std::string &path)
|
||||
{
|
||||
FILE *fd = fopen(path.c_str(), "rb");
|
||||
if (fd == nullptr)
|
||||
return false;
|
||||
|
||||
lines->clear();
|
||||
|
||||
char buffer[1024];
|
||||
short bytes_read;
|
||||
std::vector<char> line = std::vector<char>(0);
|
||||
do
|
||||
{
|
||||
bytes_read = fread(buffer, sizeof(char), 1024, fd);
|
||||
if (bytes_read < 0)
|
||||
{
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (short i = 0; i < bytes_read; i++)
|
||||
{
|
||||
if (buffer[i] != '\r' && buffer[i] != '\n')
|
||||
{
|
||||
line.push_back(buffer[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
lines->push_back(std::string(line.data(), line.size()));
|
||||
line = std::vector<char>(0);
|
||||
if (buffer[i] == '\r' && buffer[i+1] == '\n')
|
||||
i++;
|
||||
}
|
||||
}
|
||||
} while (bytes_read == 1024);
|
||||
if (line.size()>0)
|
||||
lines->push_back(std::string(line.data(), line.size()));
|
||||
|
||||
fclose(fd);
|
||||
if (lines->size() == 0)
|
||||
lines->push_back("");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveText(std::vector<std::string> *lines, const std::string &path)
|
||||
{
|
||||
FILE *fd = OpenRW(path);
|
||||
if (fd == nullptr)
|
||||
return false;
|
||||
|
||||
char nl[1] = {'\n'};
|
||||
for (int i=0; i < lines->size(); i++)
|
||||
{
|
||||
Write(fd, lines->at(i).c_str(), lines->at(i).length());
|
||||
Write(fd, nl, 1);
|
||||
}
|
||||
|
||||
fclose(fd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Save(const std::string &path, const void *data, uint32_t size)
|
||||
{
|
||||
FILE *fd = fopen(path.c_str(), "w+");
|
||||
if (fd == nullptr)
|
||||
return false;
|
||||
|
||||
const char *data8 = static_cast<const char *>(data);
|
||||
while (size != 0)
|
||||
{
|
||||
int written = fwrite(data8, 1, size, fd);
|
||||
fclose(fd);
|
||||
if (written <= 0)
|
||||
return false;
|
||||
data8 += written;
|
||||
size -= written;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> ListFiles(const std::string &path)
|
||||
{
|
||||
DIR *fd = opendir(path.c_str());
|
||||
if (fd == NULL)
|
||||
return std::vector<std::string>(0);
|
||||
|
||||
std::vector<std::string> out;
|
||||
while (true)
|
||||
{
|
||||
struct dirent *dirent;
|
||||
dirent = readdir(fd);
|
||||
if (dirent == NULL)
|
||||
{
|
||||
closedir(fd);
|
||||
return out;
|
||||
}
|
||||
|
||||
if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dirent->d_type & DT_DIR)
|
||||
{
|
||||
std::vector<std::string> files = FS::ListFiles(path + "/" + dirent->d_name);
|
||||
for (std::vector<std::string>::iterator it = files.begin(); it != files.end();)
|
||||
{
|
||||
out.push_back(std::string(dirent->d_name) + "/" + *it);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out.push_back(dirent->d_name);
|
||||
}
|
||||
}
|
||||
closedir(fd);
|
||||
return out;
|
||||
}
|
||||
|
||||
int RmRecursive(const std::string &path)
|
||||
{
|
||||
#if defined(EZREMOTE_ENABLE_UI)
|
||||
if (stop_activity)
|
||||
return 1;
|
||||
#else
|
||||
bool stop_activity = false;
|
||||
#endif
|
||||
|
||||
DIR *dfd = opendir(path.c_str());
|
||||
if (dfd != NULL)
|
||||
{
|
||||
struct dirent *dir = NULL;
|
||||
do
|
||||
{
|
||||
dir = readdir(dfd);
|
||||
if (dir == NULL || strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
char new_path[512];
|
||||
snprintf(new_path, 512, "%s%s%s", path.c_str(), hasEndSlash(path.c_str()) ? "" : "/", dir->d_name);
|
||||
|
||||
if (dir->d_type & DT_DIR)
|
||||
{
|
||||
int ret = RmRecursive(new_path);
|
||||
if (ret <= 0)
|
||||
{
|
||||
#if defined(EZREMOTE_ENABLE_UI)
|
||||
sprintf(status_message, "%s %s", lang_strings[STR_FAIL_DEL_DIR_MSG], new_path);
|
||||
#endif
|
||||
closedir(dfd);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(EZREMOTE_ENABLE_UI)
|
||||
snprintf(activity_message, 1024, "%s %s", lang_strings[STR_DELETING], new_path);
|
||||
#endif
|
||||
int ret = remove(new_path);
|
||||
if (ret < 0)
|
||||
{
|
||||
#if defined(EZREMOTE_ENABLE_UI)
|
||||
sprintf(status_message, "%s %s", lang_strings[STR_FAIL_DEL_FILE_MSG], new_path);
|
||||
#endif
|
||||
closedir(dfd);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
} while (dir != NULL && !stop_activity);
|
||||
|
||||
closedir(dfd);
|
||||
|
||||
if (stop_activity)
|
||||
return 0;
|
||||
|
||||
int ret = rmdir(path.c_str());
|
||||
if (ret < 0)
|
||||
{
|
||||
#if defined(EZREMOTE_ENABLE_UI)
|
||||
sprintf(status_message, "%s %s", lang_strings[STR_FAIL_DEL_DIR_MSG], path.c_str());
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#if defined(EZREMOTE_ENABLE_UI)
|
||||
snprintf(activity_message, 1024, "%s %s", lang_strings[STR_DELETED], path.c_str());
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret = remove(path.c_str());
|
||||
if (ret < 0)
|
||||
{
|
||||
#if defined(EZREMOTE_ENABLE_UI)
|
||||
sprintf(status_message, "%s %s", lang_strings[STR_FAIL_DEL_FILE_MSG], path.c_str());
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#if defined(EZREMOTE_ENABLE_UI)
|
||||
snprintf(activity_message, 1024, "%s %s", lang_strings[STR_DELETED], path.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string GetPath(const std::string &ppath1, const std::string &ppath2)
|
||||
{
|
||||
std::string path1 = ppath1;
|
||||
std::string path2 = ppath2;
|
||||
path2 = Util::Rtrim(Util::Trim(path2, " "), "/");
|
||||
return path1 + "/" + path2;
|
||||
}
|
||||
|
||||
int Head(const std::string &path, void *buffer, uint16_t len)
|
||||
{
|
||||
FILE *file = OpenRead(path);
|
||||
if (file == nullptr)
|
||||
return 0;
|
||||
int ret = Read(file, buffer, len);
|
||||
if (ret != len)
|
||||
{
|
||||
Close(file);
|
||||
return 0;
|
||||
}
|
||||
Close(file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool Copy(const std::string &from, const std::string &to)
|
||||
{
|
||||
#if !defined(EZREMOTE_ENABLE_UI)
|
||||
uint64_t bytes_to_download;
|
||||
uint64_t bytes_transfered;
|
||||
#endif
|
||||
MkDirs(to, true);
|
||||
if (from.compare(to) == 0)
|
||||
return true;
|
||||
|
||||
FILE *src = fopen(from.c_str(), "rb");
|
||||
if (!src)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat file_stat = {0};
|
||||
if (stat(from.c_str(), &file_stat) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes_to_download = file_stat.st_size;
|
||||
|
||||
FILE *dest = fopen(to.c_str(), "wb");
|
||||
if (!dest)
|
||||
{
|
||||
fclose(src);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t bytes_read = 0;
|
||||
bytes_transfered = 0;
|
||||
#if defined(EZREMOTE_ENABLE_UI)
|
||||
prev_tick = Util::GetTick();
|
||||
#endif
|
||||
const size_t buf_size = 0x10000;
|
||||
unsigned char *buf = new unsigned char[buf_size];
|
||||
|
||||
do
|
||||
{
|
||||
bytes_read = fread(buf, sizeof(unsigned char), buf_size, src);
|
||||
if (bytes_read < 0)
|
||||
{
|
||||
delete[] buf;
|
||||
fclose(src);
|
||||
fclose(dest);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t bytes_written = fwrite(buf, sizeof(unsigned char), bytes_read, dest);
|
||||
if (bytes_written != bytes_read)
|
||||
{
|
||||
delete[] buf;
|
||||
fclose(src);
|
||||
fclose(dest);
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes_transfered += bytes_read;
|
||||
} while (bytes_transfered < bytes_to_download);
|
||||
|
||||
delete[] buf;
|
||||
fclose(src);
|
||||
fclose(dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Move(const std::string &from, const std::string &to)
|
||||
{
|
||||
if (from.compare(to) == 0)
|
||||
return true;
|
||||
|
||||
errno = 0;
|
||||
int ret = rename(from.c_str(), to.c_str());
|
||||
if (ret != 0 && (errno == EXDEV || errno == EEXIST))
|
||||
{
|
||||
bool res = Copy(from, to);
|
||||
if (res)
|
||||
Rm(from);
|
||||
else
|
||||
return res;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
#ifndef LAUNCHER_FS_H
|
||||
#define LAUNCHER_FS_H
|
||||
|
||||
#pragma once
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#define MAX_PATH_LENGTH 1024
|
||||
|
||||
namespace FS
|
||||
{
|
||||
std::string GetPath(const std::string &path1, const std::string &path2);
|
||||
|
||||
void MkDirs(const std::string &path, bool prev = false);
|
||||
|
||||
void Rm(const std::string &file);
|
||||
void RmDir(const std::string &path);
|
||||
int RmRecursive(const std::string &path);
|
||||
|
||||
int64_t GetSize(const std::string &path);
|
||||
|
||||
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);
|
||||
|
||||
bool Copy(const std::string &from, const std::string &to);
|
||||
|
||||
bool Move(const std::string &from, const std::string &to);
|
||||
|
||||
// creates file (if it exists, truncates size to 0)
|
||||
FILE *Create(const std::string &path);
|
||||
|
||||
// open existing file in read/write, fails if file does not exist
|
||||
FILE *OpenRW(const std::string &path);
|
||||
|
||||
// open existing file in read/write, fails if file does not exist
|
||||
FILE *OpenRead(const std::string &path);
|
||||
|
||||
// open file for writing, next write will append data to end of it
|
||||
FILE *Append(const std::string &path);
|
||||
|
||||
void Close(FILE *f);
|
||||
|
||||
int64_t Seek(FILE *f, uint64_t offset);
|
||||
int Read(FILE *f, void *buffer, uint32_t size);
|
||||
int Write(FILE *f, const void *buffer, uint32_t size);
|
||||
|
||||
std::vector<char> Load(const std::string &path);
|
||||
bool LoadText(std::vector<std::string> *lines, const std::string &path);
|
||||
bool SaveText(std::vector<std::string> *lines, const std::string &path);
|
||||
|
||||
bool Save(const std::string &path, const void *data, uint32_t size);
|
||||
|
||||
std::vector<std::string> ListFiles(const std::string &path);
|
||||
|
||||
int hasEndSlash(const char *path);
|
||||
|
||||
int Head(const std::string &path, void* buffer, uint16_t len);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/random.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
|
||||
int getentropy(void *buffer, size_t len)
|
||||
{
|
||||
int cs, ret = 0;
|
||||
char *pos = buffer;
|
||||
|
||||
if (len > 256) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
|
||||
|
||||
while (len) {
|
||||
ret = getrandom(pos, len, 0);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
else break;
|
||||
}
|
||||
pos += ret;
|
||||
len -= ret;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
pthread_setcancelstate(cs, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
||||
#undef main
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <orbis/libkernel.h>
|
||||
#include <orbis/Sysmodule.h>
|
||||
#include <orbis/SystemService.h>
|
||||
#include <orbis/Net.h>
|
||||
|
||||
#include "server/http_server.h"
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
#include "dbglogger.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "orbis_jbc.h"
|
||||
}
|
||||
|
||||
static void terminate()
|
||||
{
|
||||
terminate_jbc();
|
||||
sceSystemServiceLoadExec("exit", NULL);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
dbglogger_init();
|
||||
dbglogger_log("If you see this you've set up dbglogger correctly.");
|
||||
|
||||
if (!initialize_jbc())
|
||||
{
|
||||
terminate();
|
||||
}
|
||||
|
||||
atexit(terminate);
|
||||
|
||||
if (sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_SYSTEM_SERVICE) < 0) return 0;
|
||||
if (sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_USER_SERVICE) < 0) return 0;
|
||||
if (sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NET) < 0 || sceNetInit() != 0) return 0;
|
||||
|
||||
CONFIG::LoadPackageInstallHostData();
|
||||
CONFIG::LoadBgDownloadData();
|
||||
|
||||
if (HttpServer::IsStarted())
|
||||
{
|
||||
Util::Notify("ezRemote Server already started");
|
||||
terminate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
dbglogger_log(" Registering Daemon...");
|
||||
sceSystemServiceRegisterDaemon();
|
||||
|
||||
HttpServer::StartDownloadThread();
|
||||
HttpServer::Start();
|
||||
Util::Notify("ezRemote Server stopped.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/uio.h>
|
||||
#include <orbis/libkernel.h>
|
||||
#include <libjbc.h>
|
||||
|
||||
#define SYSCALL(nr, fn) __attribute__((naked)) fn\
|
||||
{\
|
||||
asm volatile("mov $" #nr ", %rax\nmov %rcx, %r10\nsyscall\nret");\
|
||||
}
|
||||
|
||||
SYSCALL(22, static int unmount(const char* path, int flags))
|
||||
SYSCALL(378, static int nmount(struct iovec* iov, unsigned int niov, int flags))
|
||||
|
||||
static void build_iovec(struct iovec** iov, int* iovlen, const char* name, const void* val, size_t len) {
|
||||
int i;
|
||||
|
||||
if (*iovlen < 0)
|
||||
return;
|
||||
|
||||
i = *iovlen;
|
||||
*iov = (struct iovec*)realloc(*iov, sizeof **iov * (i + 2));
|
||||
if (*iov == NULL) {
|
||||
*iovlen = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
(*iov)[i].iov_base = strdup(name);
|
||||
(*iov)[i].iov_len = strlen(name) + 1;
|
||||
++i;
|
||||
|
||||
(*iov)[i].iov_base = (void*)val;
|
||||
if (len == (size_t)-1) {
|
||||
if (val != NULL)
|
||||
len = strlen((const char*)val) + 1;
|
||||
else
|
||||
len = 0;
|
||||
}
|
||||
(*iov)[i].iov_len = (int)len;
|
||||
|
||||
*iovlen = ++i;
|
||||
}
|
||||
|
||||
int mount_large_fs(const char* device, const char* mountpoint, const char* fstype, const char* mode, unsigned int flags)
|
||||
{
|
||||
struct iovec* iov = NULL;
|
||||
int iovlen = 0;
|
||||
|
||||
unmount(mountpoint, 0);
|
||||
|
||||
build_iovec(&iov, &iovlen, "fstype", fstype, -1);
|
||||
build_iovec(&iov, &iovlen, "fspath", mountpoint, -1);
|
||||
build_iovec(&iov, &iovlen, "from", device, -1);
|
||||
build_iovec(&iov, &iovlen, "large", "yes", -1);
|
||||
build_iovec(&iov, &iovlen, "timezone", "static", -1);
|
||||
build_iovec(&iov, &iovlen, "async", "", -1);
|
||||
build_iovec(&iov, &iovlen, "ignoreacl", "", -1);
|
||||
|
||||
if (mode) {
|
||||
build_iovec(&iov, &iovlen, "dirmask", mode, -1);
|
||||
build_iovec(&iov, &iovlen, "mask", mode, -1);
|
||||
}
|
||||
|
||||
return nmount(iov, iovlen, flags);
|
||||
}
|
||||
|
||||
// Variables for (un)jailbreaking
|
||||
jbc_cred g_Cred;
|
||||
jbc_cred g_RootCreds;
|
||||
|
||||
// Verify jailbreak
|
||||
static int is_jailbroken()
|
||||
{
|
||||
FILE *s_FilePointer = fopen("/user/.jailbreak", "w");
|
||||
|
||||
if (!s_FilePointer)
|
||||
return 0;
|
||||
|
||||
fclose(s_FilePointer);
|
||||
remove("/user/.jailbreak");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Jailbreaks creds
|
||||
static int jailbreak()
|
||||
{
|
||||
if (is_jailbroken())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
jbc_get_cred(&g_Cred);
|
||||
g_RootCreds = g_Cred;
|
||||
jbc_jailbreak_cred(&g_RootCreds);
|
||||
jbc_set_cred(&g_RootCreds);
|
||||
|
||||
return (is_jailbroken());
|
||||
}
|
||||
|
||||
// Initialize jailbreak
|
||||
int initialize_jbc()
|
||||
{
|
||||
// Pop notification depending on jailbreak result
|
||||
if (!jailbreak())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Unload libjbc libraries
|
||||
void terminate_jbc()
|
||||
{
|
||||
if (!is_jailbroken())
|
||||
return;
|
||||
|
||||
// Restores original creds
|
||||
jbc_set_cred(&g_Cred);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef __ORBIS_JBC_H__
|
||||
#define __ORBIS_JBC_H__
|
||||
|
||||
#define MNT_UPDATE 0x0000000000010000ULL
|
||||
|
||||
int initialize_jbc();
|
||||
void terminate_jbc();
|
||||
int mount_large_fs(const char* device, const char* mountpoint, const char* fstype, const char* mode, unsigned int flags);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,543 @@
|
||||
#include <string>
|
||||
#include <json-c/json.h>
|
||||
#include "http/httplib.h"
|
||||
#include "server/http_server.h"
|
||||
#include "clients/remote_client.h"
|
||||
#include "clients/archiveorg.h"
|
||||
#include "clients/baseclient.h"
|
||||
#include "clients/ftpclient.h"
|
||||
#include "clients/nfsclient.h"
|
||||
#include "clients/smbclient.h"
|
||||
#include "clients/sftpclient.h"
|
||||
#include "clients/webdav.h"
|
||||
#include "config.h"
|
||||
#include "fs.h"
|
||||
#include "util.h"
|
||||
|
||||
#define SUCCESS_MSG "{ \"result\": { \"success\": true, \"error\": null } }"
|
||||
#define FAILURE_MSG "{ \"result\": { \"success\": false, \"error\": \"%s\" } }"
|
||||
#define SUCCESS_MSG_LEN 48
|
||||
#define PKG_INITIAL_REQUEST_SIZE 8388608ul
|
||||
|
||||
using namespace httplib;
|
||||
|
||||
Server *svr;
|
||||
int http_server_port = 6701;
|
||||
static pthread_t bg_download_thread;
|
||||
static uint64_t g_dl_offset;
|
||||
|
||||
namespace HttpServer
|
||||
{
|
||||
static int FtpCallback(int64_t xfered, void *arg)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int DownloadFtpCallback(int64_t xfered, void *arg)
|
||||
{
|
||||
*g_bytes_transfered = g_dl_offset + xfered;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string dump_headers(const Headers &headers)
|
||||
{
|
||||
std::string s;
|
||||
char buf[BUFSIZ];
|
||||
|
||||
for (auto it = headers.begin(); it != headers.end(); ++it)
|
||||
{
|
||||
const auto &x = *it;
|
||||
snprintf(buf, sizeof(buf), "%s: %s\n", x.first.c_str(), x.second.c_str());
|
||||
s += buf;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string log(const Request &req, const Response &res)
|
||||
{
|
||||
std::string s;
|
||||
char buf[BUFSIZ];
|
||||
|
||||
s += "================================\n";
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s %s %s", req.method.c_str(),
|
||||
req.version.c_str(), req.path.c_str());
|
||||
s += buf;
|
||||
|
||||
std::string query;
|
||||
for (auto it = req.params.begin(); it != req.params.end(); ++it)
|
||||
{
|
||||
const auto &x = *it;
|
||||
snprintf(buf, sizeof(buf), "%c%s=%s",
|
||||
(it == req.params.begin()) ? '?' : '&', x.first.c_str(),
|
||||
x.second.c_str());
|
||||
query += buf;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "%s\n", query.c_str());
|
||||
s += buf;
|
||||
|
||||
s += dump_headers(req.headers);
|
||||
|
||||
s += "--------------------------------\n";
|
||||
|
||||
snprintf(buf, sizeof(buf), "%d %s\n", res.status, res.version.c_str());
|
||||
s += buf;
|
||||
s += dump_headers(res.headers);
|
||||
s += "\n";
|
||||
|
||||
if (!res.body.empty())
|
||||
{
|
||||
s += res.body;
|
||||
}
|
||||
|
||||
s += "\n";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static RemoteClient *GetRemoteClient(HostInfo *host_info)
|
||||
{
|
||||
RemoteClient *tmp_client = nullptr;
|
||||
if (host_info->type == CLIENT_TYPE_HTTP_SERVER)
|
||||
{
|
||||
if (host_info->http_server_type.compare(HTTP_SERVER_ARCHIVEORG))
|
||||
{
|
||||
tmp_client = new ArchiveOrgClient();
|
||||
}
|
||||
else if (host_info->http_server_type.compare(HTTP_SERVER_APACHE))
|
||||
{
|
||||
tmp_client = new BaseClient();
|
||||
}
|
||||
else if (host_info->http_server_type.compare(HTTP_SERVER_MS_IIS))
|
||||
{
|
||||
tmp_client = new BaseClient();
|
||||
}
|
||||
else if (host_info->http_server_type.compare(HTTP_SERVER_NGINX))
|
||||
{
|
||||
tmp_client = new BaseClient();
|
||||
}
|
||||
else if (host_info->http_server_type.compare(HTTP_SERVER_RCLONE))
|
||||
{
|
||||
tmp_client = new BaseClient();
|
||||
}
|
||||
else if (host_info->http_server_type.compare(HTTP_SERVER_NPX_SERVE))
|
||||
{
|
||||
tmp_client = new BaseClient();
|
||||
}
|
||||
}
|
||||
else if (host_info->type == CLIENT_TYPE_WEBDAV)
|
||||
{
|
||||
tmp_client = new WebDAVClient();
|
||||
}
|
||||
else if (host_info->type == CLIENT_TYPE_FILEHOST)
|
||||
{
|
||||
tmp_client = new BaseClient();
|
||||
}
|
||||
else if (host_info->type == CLIENT_TYPE_SMB)
|
||||
{
|
||||
tmp_client = new SmbClient();
|
||||
}
|
||||
else if (host_info->type == CLIENT_TYPE_SFTP)
|
||||
{
|
||||
tmp_client = new SFTPClient();
|
||||
}
|
||||
else if (host_info->type == CLIENT_TYPE_NFS)
|
||||
{
|
||||
tmp_client = new NfsClient();
|
||||
}
|
||||
else if (host_info->type == CLIENT_TYPE_FTP)
|
||||
{
|
||||
tmp_client = new FtpClient();
|
||||
FtpClient *ftp_client = (FtpClient*) tmp_client;
|
||||
ftp_client->SetCallbackXferFunction(FtpCallback);
|
||||
}
|
||||
|
||||
if (tmp_client != nullptr)
|
||||
tmp_client->Connect(host_info->url, host_info->username, host_info->password);
|
||||
|
||||
return tmp_client;
|
||||
}
|
||||
|
||||
static void DeleteRemoteClient(RemoteClient *tmp_client)
|
||||
{
|
||||
if (!dynamic_cast<BaseClient*>(tmp_client))
|
||||
{
|
||||
tmp_client->Quit();
|
||||
delete tmp_client;
|
||||
}
|
||||
}
|
||||
|
||||
void *DownloadFilesThread(void *argp)
|
||||
{
|
||||
char temp_file[2049];
|
||||
uint64_t tmp_file_size;
|
||||
int ret;
|
||||
|
||||
while (true)
|
||||
{
|
||||
for (int i=0; i < bg_download_list.size(); i++)
|
||||
{
|
||||
if (bg_download_list[i].state == STATE_PENDING)
|
||||
{
|
||||
RemoteClient *tmp_client = GetRemoteClient(&(bg_download_list[i].host_info));
|
||||
g_bytes_transfered = &(bg_download_list[i].bytes_transfered);
|
||||
if (bg_download_list[i].host_info.type == CLIENT_TYPE_FTP)
|
||||
{
|
||||
|
||||
FtpClient *ftpclient = (FtpClient*)tmp_client;
|
||||
g_dl_offset = 0;
|
||||
ftpclient->SetCallbackBytes(1);
|
||||
ftpclient->SetCallbackXferFunction(DownloadFtpCallback);
|
||||
}
|
||||
|
||||
bg_download_list[i].state = STATE_DOWNLOADING;
|
||||
CONFIG::SaveBgDownloadData();
|
||||
|
||||
snprintf(temp_file, sizeof(temp_file), "%s.tmp", bg_download_list[i].dest_path.c_str());
|
||||
Util::RichNotify(bg_download_list[i].id, "Started download %s", bg_download_list[i].dest_path.c_str());
|
||||
|
||||
ret = tmp_client->Get(temp_file, bg_download_list[i].src_path);
|
||||
|
||||
FS::Rename(temp_file, bg_download_list[i].dest_path);
|
||||
if (ret == 0)
|
||||
{
|
||||
bg_download_list[i].state = STATE_FAILED;
|
||||
Util::RichNotify(bg_download_list[i].id, "Failed to download %s", bg_download_list[i].dest_path.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
Util::RichNotify(bg_download_list[i].id, "Completed download %s", bg_download_list[i].dest_path.c_str());
|
||||
bg_download_list[i].state = STATE_SUCCESS;
|
||||
}
|
||||
CONFIG::SaveBgDownloadData();
|
||||
|
||||
DeleteRemoteClient(tmp_client);
|
||||
}
|
||||
else if (bg_download_list[i].state == STATE_DOWNLOADING)
|
||||
{
|
||||
// Resume interrupted download
|
||||
RemoteClient *tmp_client = GetRemoteClient(&(bg_download_list[i].host_info));
|
||||
g_bytes_transfered = &(bg_download_list[i].bytes_transfered);
|
||||
if (bg_download_list[i].host_info.type == CLIENT_TYPE_FTP)
|
||||
{
|
||||
|
||||
FtpClient *ftpclient = (FtpClient*)tmp_client;
|
||||
ftpclient->SetCallbackBytes(1);
|
||||
ftpclient->SetCallbackXferFunction(DownloadFtpCallback);
|
||||
}
|
||||
|
||||
bg_download_list[i].state = STATE_RESUMED;
|
||||
|
||||
snprintf(temp_file, sizeof(temp_file), "%s.tmp", bg_download_list[i].dest_path.c_str());
|
||||
// Check if temp file still exists, if exists then resume download
|
||||
Util::RichNotify(bg_download_list[i].id, "Resuming download %s", bg_download_list[i].dest_path.c_str());
|
||||
if (FS::FileExists(temp_file))
|
||||
{
|
||||
tmp_file_size = FS::GetSize(temp_file);
|
||||
g_dl_offset = tmp_file_size;
|
||||
ret = tmp_client->Get(temp_file, bg_download_list[i].src_path, tmp_file_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_dl_offset = 0;
|
||||
ret = tmp_client->Get(temp_file, bg_download_list[i].src_path);
|
||||
}
|
||||
|
||||
FS::Rename(temp_file, bg_download_list[i].dest_path);
|
||||
if (ret == 0)
|
||||
{
|
||||
bg_download_list[i].state = STATE_FAILED;
|
||||
Util::RichNotify(bg_download_list[i].id, "Failed to download %s", bg_download_list[i].dest_path.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
Util::RichNotify(bg_download_list[i].id, "Completed download %s", bg_download_list[i].dest_path.c_str());
|
||||
bg_download_list[i].state = STATE_SUCCESS;
|
||||
}
|
||||
CONFIG::SaveBgDownloadData();
|
||||
|
||||
DeleteRemoteClient(tmp_client);
|
||||
}
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *ServerThread(void *argp)
|
||||
{
|
||||
svr->Get("/", [&](const Request &req, Response &res)
|
||||
{ res.set_redirect("/index.html"); });
|
||||
|
||||
svr->Post("/store_bg_install_data", [&](const Request &req, Response &res)
|
||||
{
|
||||
const char *hash_param;
|
||||
const char *path_param;
|
||||
const char *url_param;
|
||||
const char *username_param;
|
||||
const char *password_param;
|
||||
const char *http_server_type_param;
|
||||
int type_param;
|
||||
|
||||
json_object *jobj = json_tokener_parse(req.body.c_str());
|
||||
if (jobj != nullptr)
|
||||
{
|
||||
hash_param = json_object_get_string(json_object_object_get(jobj, "hash"));
|
||||
url_param = json_object_get_string(json_object_object_get(jobj, "url"));
|
||||
path_param = json_object_get_string(json_object_object_get(jobj, "path"));
|
||||
username_param = json_object_get_string(json_object_object_get(jobj, "username"));
|
||||
password_param = json_object_get_string(json_object_object_get(jobj, "password"));
|
||||
http_server_type_param = json_object_get_string(json_object_object_get(jobj, "http_server_type"));
|
||||
type_param = json_object_get_int(json_object_object_get(jobj, "type"));
|
||||
|
||||
if (url_param == nullptr || hash_param == nullptr)
|
||||
{
|
||||
bad_request(res, "Required url_param or hash parameter missing");
|
||||
return;
|
||||
}
|
||||
|
||||
PackageInstallData pkg_data;
|
||||
pkg_data.host_info.url = url_param;
|
||||
if (username_param != nullptr)
|
||||
pkg_data.host_info.username = username_param;
|
||||
if (password_param != nullptr)
|
||||
pkg_data.host_info.password = password_param;
|
||||
if (path_param != nullptr)
|
||||
pkg_data.path = path_param;
|
||||
if (http_server_type_param != nullptr)
|
||||
pkg_data.host_info.http_server_type = http_server_type_param;
|
||||
pkg_data.timestamp = Util::GetTick();
|
||||
pkg_data.host_info.type = type_param;
|
||||
pkg_data.client = nullptr;
|
||||
|
||||
CONFIG::AddPackageInstallHostData(hash_param, pkg_data);
|
||||
CONFIG::SavePackageInstallHostData();
|
||||
}
|
||||
});
|
||||
|
||||
svr->Get("/bg_install/(.*)", [&](const Request &req, Response &res)
|
||||
{
|
||||
std::string hash = req.matches[1];
|
||||
PackageInstallData* pkg_host_data = CONFIG::GetPackageInstallHostData(hash);
|
||||
RemoteClient *tmp_client;
|
||||
|
||||
if (pkg_host_data == nullptr)
|
||||
{
|
||||
failed(res, 500, "Cannot resume background install of " + hash + ". Host data not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pkg_host_data->host_info.type == CLIENT_TYPE_HTTP_SERVER ||
|
||||
pkg_host_data->host_info.type == CLIENT_TYPE_WEBDAV ||
|
||||
pkg_host_data->host_info.type == CLIENT_TYPE_FILEHOST)
|
||||
{
|
||||
if (pkg_host_data->client == nullptr)
|
||||
{
|
||||
pkg_host_data->client = GetRemoteClient(&(pkg_host_data->host_info));
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp_client = pkg_host_data->client;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp_client = GetRemoteClient(&(pkg_host_data->host_info));
|
||||
}
|
||||
|
||||
if (tmp_client == nullptr)
|
||||
{
|
||||
res.status = 500;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string path = pkg_host_data->path;
|
||||
|
||||
res.status = 206;
|
||||
size_t range_len = (req.ranges[0].second - req.ranges[0].first) + 1;
|
||||
|
||||
std::pair<ssize_t, ssize_t> range = req.ranges[0];
|
||||
res.set_content_provider(
|
||||
range_len, "application/octet-stream",
|
||||
[tmp_client, path, range, range_len](size_t offset, size_t length, DataSink &sink) {
|
||||
int ret;
|
||||
ret = tmp_client->GetRange(path, sink, range_len, range.first);
|
||||
return (ret==1);
|
||||
},
|
||||
[tmp_client](bool success) {
|
||||
DeleteRemoteClient(tmp_client);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
svr->Post("/download_url", [&](const Request &req, Response &res)
|
||||
{
|
||||
int type_param;
|
||||
const char *url_param;
|
||||
const char *username_param;
|
||||
const char *password_param;
|
||||
const char *http_server_type_param;
|
||||
const char *src_path_param;
|
||||
const char *dest_path_param;
|
||||
uint64_t file_size_param;
|
||||
uint64_t id_param;
|
||||
|
||||
json_object *jobj = json_tokener_parse(req.body.c_str());
|
||||
if (jobj != nullptr)
|
||||
{
|
||||
type_param = json_object_get_int(json_object_object_get(jobj, "type"));
|
||||
url_param = json_object_get_string(json_object_object_get(jobj, "url"));
|
||||
username_param = json_object_get_string(json_object_object_get(jobj, "username"));
|
||||
password_param = json_object_get_string(json_object_object_get(jobj, "password"));
|
||||
http_server_type_param = json_object_get_string(json_object_object_get(jobj, "http_server_type"));
|
||||
src_path_param = json_object_get_string(json_object_object_get(jobj, "src_path"));
|
||||
dest_path_param = json_object_get_string(json_object_object_get(jobj, "dest_path"));
|
||||
file_size_param = json_object_get_uint64(json_object_object_get(jobj, "size"));
|
||||
id_param = json_object_get_uint64(json_object_object_get(jobj, "id"));
|
||||
|
||||
if (url_param == nullptr || src_path_param == nullptr || dest_path_param == nullptr)
|
||||
{
|
||||
bad_request(res, "Required parameters are missing");
|
||||
return;
|
||||
}
|
||||
|
||||
BgDownloadData download_data;
|
||||
download_data.host_info.type = type_param;
|
||||
download_data.host_info.url = url_param;
|
||||
download_data.src_path = src_path_param;
|
||||
download_data.dest_path = dest_path_param;
|
||||
download_data.file_size = file_size_param;
|
||||
download_data.state = STATE_PENDING;
|
||||
download_data.id = id_param;
|
||||
download_data.bytes_transfered = 0;
|
||||
download_data.timestamp = Util::GetTick();
|
||||
|
||||
if (username_param != nullptr)
|
||||
download_data.host_info.username = username_param;
|
||||
if (password_param != nullptr)
|
||||
download_data.host_info.password = password_param;
|
||||
if (http_server_type_param != nullptr)
|
||||
download_data.host_info.http_server_type = http_server_type_param;
|
||||
|
||||
CONFIG::AddBgDownloadData(download_data);
|
||||
CONFIG::SaveBgDownloadData();
|
||||
}
|
||||
});
|
||||
|
||||
svr->Get("/get_download_state", [&](const Request &req, Response &res)
|
||||
{
|
||||
json_object *download_list = json_object_new_array();
|
||||
|
||||
for (int i=0; i < bg_download_list.size(); i++)
|
||||
{
|
||||
json_object *download_item_obj = json_object_new_object();
|
||||
json_object_object_add(download_item_obj, "path", json_object_new_string(bg_download_list[i].dest_path.c_str()));
|
||||
json_object_object_add(download_item_obj, "bytes_transfered", json_object_new_uint64(bg_download_list[i].bytes_transfered));
|
||||
json_object_object_add(download_item_obj, "file_size", json_object_new_uint64(bg_download_list[i].file_size));
|
||||
json_object_object_add(download_item_obj, "state", json_object_new_int(bg_download_list[i].state));
|
||||
json_object_object_add(download_item_obj, "timestamp", json_object_new_uint64(bg_download_list[i].timestamp/1000000));
|
||||
json_object_array_add(download_list, download_item_obj);
|
||||
}
|
||||
|
||||
const char *payload_str = json_object_to_json_string(download_list);
|
||||
|
||||
res.status = 200;
|
||||
res.set_content(payload_str, "application/json");
|
||||
});
|
||||
|
||||
svr->Get("/stop", [&](const Request & /*req*/, Response & /*res*/)
|
||||
{
|
||||
svr->stop();
|
||||
});
|
||||
|
||||
svr->Get("/version", [&](const Request & req, Response &res)
|
||||
{
|
||||
res.status = 200;
|
||||
char version[20];
|
||||
sprintf(version, "%.2f", EZREMOTE_VERSION);
|
||||
res.set_content(version, "text/html");
|
||||
});
|
||||
|
||||
svr->set_error_handler([](const Request & /*req*/, Response &res)
|
||||
{
|
||||
const char *fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
|
||||
char buf[BUFSIZ];
|
||||
snprintf(buf, sizeof(buf), fmt, res.status);
|
||||
res.set_content(buf, "text/html");
|
||||
});
|
||||
|
||||
/*
|
||||
svr->set_logger([](const Request &req, const Response &res)
|
||||
{
|
||||
dbglogger_log("%s", log(req, res).c_str());
|
||||
});
|
||||
*/
|
||||
|
||||
svr->set_payload_max_length(1024 * 1024 * 12);
|
||||
svr->set_tcp_nodelay(true);
|
||||
svr->set_mount_point("/", "/");
|
||||
|
||||
svr->listen("0.0.0.0", http_server_port);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (svr == nullptr)
|
||||
svr = new Server();
|
||||
if (!svr->is_valid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Util::Notify("Starting ezRemote Server %.2f on port %d", EZREMOTE_VERSION, http_server_port);
|
||||
ServerThread(nullptr);
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
if (svr != nullptr)
|
||||
svr->stop();
|
||||
}
|
||||
|
||||
void StartDownloadThread()
|
||||
{
|
||||
pthread_create(&bg_download_thread, NULL, DownloadFilesThread, NULL);
|
||||
}
|
||||
|
||||
bool IsStarted()
|
||||
{
|
||||
httplib::Client client = httplib::Client("http://127.0.0.1:6701");
|
||||
if (auto res = client.Get("/version"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef EZ_HTTP_SERVER_H
|
||||
#define EZ_HTTP_SERVER_H
|
||||
|
||||
#include "http/httplib.h"
|
||||
|
||||
using namespace httplib;
|
||||
extern Server *svr;
|
||||
|
||||
extern int http_server_port;
|
||||
|
||||
namespace HttpServer
|
||||
{
|
||||
void *ServerThread(void *argp);
|
||||
void Start();
|
||||
void Stop();
|
||||
void StartDownloadThread();
|
||||
bool IsStarted();
|
||||
}
|
||||
|
||||
#endif
|
||||
+380
@@ -0,0 +1,380 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <stdarg.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include "base64.h"
|
||||
#include "openssl/md5.h"
|
||||
#include "config.h"
|
||||
|
||||
#define SCE_NOTIFICATION_LOCAL_USER_ID_SYSTEM 0xFE
|
||||
|
||||
typedef struct notify_request
|
||||
{
|
||||
char useless1[45];
|
||||
char message[3075];
|
||||
} notify_request_t;
|
||||
|
||||
/*
|
||||
extern "C"
|
||||
{
|
||||
int sceKernelSendNotificationRequest(int, notify_request_t *, size_t, int);
|
||||
int sceNotificationSend(int userId, bool isLogged, const char* payload);
|
||||
}
|
||||
*/
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
static void utf16_to_utf8(const uint16_t *src, uint8_t *dst)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; src[i]; i++)
|
||||
{
|
||||
if ((src[i] & 0xFF80) == 0)
|
||||
{
|
||||
*(dst++) = src[i] & 0xFF;
|
||||
}
|
||||
else if ((src[i] & 0xF800) == 0)
|
||||
{
|
||||
*(dst++) = ((src[i] >> 6) & 0xFF) | 0xC0;
|
||||
*(dst++) = (src[i] & 0x3F) | 0x80;
|
||||
}
|
||||
else if ((src[i] & 0xFC00) == 0xD800 && (src[i + 1] & 0xFC00) == 0xDC00)
|
||||
{
|
||||
*(dst++) = (((src[i] + 64) >> 8) & 0x3) | 0xF0;
|
||||
*(dst++) = (((src[i] >> 2) + 16) & 0x3F) | 0x80;
|
||||
*(dst++) = ((src[i] >> 4) & 0x30) | 0x80 | ((src[i + 1] << 2) & 0xF);
|
||||
*(dst++) = (src[i + 1] & 0x3F) | 0x80;
|
||||
i += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*(dst++) = ((src[i] >> 12) & 0xF) | 0xE0;
|
||||
*(dst++) = ((src[i] >> 6) & 0x3F) | 0x80;
|
||||
*(dst++) = (src[i] & 0x3F) | 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
static void utf8_to_utf16(const uint8_t *src, uint16_t *dst)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; src[i];)
|
||||
{
|
||||
if ((src[i] & 0xE0) == 0xE0)
|
||||
{
|
||||
*(dst++) = ((src[i] & 0x0F) << 12) | ((src[i + 1] & 0x3F) << 6) | (src[i + 2] & 0x3F);
|
||||
i += 3;
|
||||
}
|
||||
else if ((src[i] & 0xC0) == 0xC0)
|
||||
{
|
||||
*(dst++) = ((src[i] & 0x1F) << 6) | (src[i + 1] & 0x3F);
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
*(dst++) = src[i];
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
static std::string &Ltrim(std::string &str, std::string chars)
|
||||
{
|
||||
str.erase(0, str.find_first_not_of(chars));
|
||||
return str;
|
||||
}
|
||||
|
||||
static std::string &Rtrim(std::string &str, std::string chars)
|
||||
{
|
||||
str.erase(str.find_last_not_of(chars) + 1);
|
||||
return str;
|
||||
}
|
||||
|
||||
// trim from both ends (in place)
|
||||
static std::string &Trim(std::string &str, std::string chars)
|
||||
{
|
||||
return Ltrim(Rtrim(str, chars), chars);
|
||||
}
|
||||
|
||||
static void ReplaceAll(std::string &data, std::string toSearch, std::string replaceStr)
|
||||
{
|
||||
size_t pos = data.find(toSearch);
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
data.replace(pos, toSearch.size(), replaceStr);
|
||||
pos = data.find(toSearch, pos + replaceStr.size());
|
||||
}
|
||||
}
|
||||
|
||||
static std::string ToLower(std::string s)
|
||||
{
|
||||
std::transform(s.begin(), s.end(), s.begin(),
|
||||
[](unsigned char c)
|
||||
{ return std::tolower(c); });
|
||||
return s;
|
||||
}
|
||||
|
||||
static bool EndsWith(std::string const &value, std::string const &ending)
|
||||
{
|
||||
if (ending.size() > value.size())
|
||||
return false;
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
||||
static std::vector<std::string> Split(const std::string &str, const std::string &delimiter)
|
||||
{
|
||||
std::string text = std::string(str);
|
||||
std::vector<std::string> tokens;
|
||||
size_t pos = 0;
|
||||
while ((pos = text.find(delimiter)) != std::string::npos)
|
||||
{
|
||||
if (text.substr(0, pos).length() > 0)
|
||||
tokens.push_back(text.substr(0, pos));
|
||||
text.erase(0, pos + delimiter.length());
|
||||
}
|
||||
if (text.length() > 0)
|
||||
{
|
||||
tokens.push_back(text);
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
static std::string ToString(int value)
|
||||
{
|
||||
std::ostringstream myObjectStream;
|
||||
myObjectStream << value;
|
||||
return myObjectStream.str();
|
||||
}
|
||||
|
||||
static std::string UrlHash(const std::string &text)
|
||||
{
|
||||
std::vector<unsigned char> res(16);
|
||||
MD5((const unsigned char *)text.c_str(), text.length(), res.data());
|
||||
|
||||
std::string out;
|
||||
Base64::Encode(res.data(), res.size(), out);
|
||||
Util::ReplaceAll(out, "=", "a");
|
||||
Util::ReplaceAll(out, "+", "b");
|
||||
Util::ReplaceAll(out, "/", "c");
|
||||
out = out + ".pkg";
|
||||
return out;
|
||||
}
|
||||
|
||||
static uint64_t GetTick()
|
||||
{
|
||||
static struct timeval tick;
|
||||
gettimeofday(&tick, NULL);
|
||||
return tick.tv_sec * 1000000 + tick.tv_usec;
|
||||
}
|
||||
|
||||
static void Notify(const char *fmt, ...)
|
||||
{
|
||||
OrbisNotificationRequest request;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsprintf(request.message, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
request.type = OrbisNotificationRequestType::NotificationRequest;
|
||||
request.unk3 = 0;
|
||||
request.useIconImageUri = 0;
|
||||
request.targetId = -1;
|
||||
sceKernelSendNotificationRequest(0, &request, sizeof(request), 0);
|
||||
}
|
||||
|
||||
static void append_json_escaped(char *dst, size_t dst_size, const char *src)
|
||||
{
|
||||
size_t used = strlen(dst);
|
||||
if (used >= dst_size)
|
||||
return;
|
||||
|
||||
for (; *src != '\0' && used + 1 < dst_size; ++src)
|
||||
{
|
||||
const char *escape = NULL;
|
||||
char single[2] = {0};
|
||||
|
||||
switch (*src)
|
||||
{
|
||||
case '\\':
|
||||
escape = "\\\\";
|
||||
break;
|
||||
case '"':
|
||||
escape = "\\\"";
|
||||
break;
|
||||
case '\n':
|
||||
escape = "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
escape = "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
escape = "\\t";
|
||||
break;
|
||||
default:
|
||||
single[0] = *src;
|
||||
escape = single;
|
||||
break;
|
||||
}
|
||||
|
||||
size_t escape_len = strlen(escape);
|
||||
if (used + escape_len >= dst_size)
|
||||
break;
|
||||
memcpy(dst + used, escape, escape_len);
|
||||
used += escape_len;
|
||||
dst[used] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
static bool RichNotify(uint64_t id, const char *fmt, ...)
|
||||
{
|
||||
/*
|
||||
va_list args;
|
||||
char message[3072];
|
||||
char escaped_message[4096];
|
||||
char payload[8192];
|
||||
char created_at[32];
|
||||
char notification_id[32];
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(message, sizeof message, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
escaped_message[0] = '\0';
|
||||
append_json_escaped(escaped_message, sizeof(escaped_message), message);
|
||||
|
||||
struct tm tm_utc;
|
||||
time_t now = time(NULL);
|
||||
gmtime_r(&now, &tm_utc);
|
||||
strftime(created_at, 32, "%Y-%m-%dT%H:%M:%S.000Z", &tm_utc);
|
||||
sprintf(notification_id, "%lu", id);
|
||||
|
||||
int len = snprintf(
|
||||
payload, sizeof(payload),
|
||||
"{\n"
|
||||
" \"rawData\": {\n"
|
||||
" \"viewTemplateType\": \"InteractiveToastTemplateB\",\n"
|
||||
" \"channelType\": \"ServiceFeedback\",\n"
|
||||
" \"bundleName\": \"ezRemoteClientWelcome\",\n"
|
||||
" \"useCaseId\": \"IDC\",\n"
|
||||
" \"soundEffect\": \"none\",\n"
|
||||
" \"toastOverwriteType\": \"InQueue\",\n"
|
||||
" \"isImmediate\": true,\n"
|
||||
" \"priority\": 100,\n"
|
||||
" \"viewData\": {\n"
|
||||
" \"icon\": {\n"
|
||||
" \"type\": \"Url\",\n"
|
||||
" \"parameters\": {\n"
|
||||
" \"url\": \"" NOTIFY_ICON_FILE "\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"message\": {\n"
|
||||
" \"body\": \"%s\"\n"
|
||||
" },\n"
|
||||
" \"subMessage\": {\n"
|
||||
" \"body\": \"ezRemote Client\"\n"
|
||||
" },\n"
|
||||
" \"actions\": [\n"
|
||||
" {\n"
|
||||
" \"actionName\": \"Go to ezRemote Client\",\n"
|
||||
" \"actionType\": \"DeepLink\",\n"
|
||||
" \"defaultFocus\": true,\n"
|
||||
" \"parameters\": {\n"
|
||||
" \"actionUrl\": \"http://localhost:8080/hbldr?path=%s\"\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
" },\n"
|
||||
" \"platformViews\": {\n"
|
||||
" \"previewDisabled\": {\n"
|
||||
" \"viewData\": {\n"
|
||||
" \"icon\": {\n"
|
||||
" \"type\": \"Predefined\",\n"
|
||||
" \"parameters\": {\n"
|
||||
" \"icon\": \"community\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"message\": {\n"
|
||||
" \"body\": \"%s\"\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"createdDateTime\": \"%s\",\n"
|
||||
" \"localNotificationId\": \"%s\"\n"
|
||||
"}",
|
||||
escaped_message, EZREMOTE_VERSION, CLIENT_ELF_PATH, escaped_message, created_at,
|
||||
notification_id);
|
||||
|
||||
if (len < 0 || (size_t)len >= sizeof(payload))
|
||||
return false;
|
||||
|
||||
int rc = sceNotificationSend(SCE_NOTIFICATION_LOCAL_USER_ID_SYSTEM, true,
|
||||
payload);
|
||||
return rc == 0;
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t NthOccurrence(const std::string &str, const std::string &findMe, int nth, size_t start_pos = 0, size_t end_pos = INT_MAX)
|
||||
{
|
||||
size_t prev_pos = std::string::npos;
|
||||
size_t pos = start_pos;
|
||||
int cnt = 0;
|
||||
|
||||
while (cnt != nth)
|
||||
{
|
||||
pos += 1;
|
||||
pos = str.find(findMe, pos);
|
||||
if (pos > end_pos)
|
||||
return prev_pos;
|
||||
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
if (cnt == 0)
|
||||
return std::string::npos;
|
||||
else
|
||||
break;
|
||||
}
|
||||
prev_pos = pos;
|
||||
cnt++;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
static size_t CountOccurrence(const std::string &str, const std::string &findMe, size_t start_pos = 0, size_t end_pos = INT_MAX)
|
||||
{
|
||||
size_t pos = start_pos;
|
||||
int cnt = 0;
|
||||
while (true)
|
||||
{
|
||||
pos += 1;
|
||||
pos = str.find(findMe, pos);
|
||||
if (pos > end_pos)
|
||||
return cnt;
|
||||
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pos += 1;
|
||||
cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user