Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7be30e358a | |||
| d58fc0eac2 | |||
| ed3f8a71c4 | |||
| 13279b6040 | |||
| eb465356b6 | |||
| 8602a5353e | |||
| 467a459665 | |||
| b7fe46cb94 | |||
| 3bcf136d2a | |||
| e5d5ddb8bc |
+2
-1
@@ -28,6 +28,7 @@ add_executable(ezremote_client
|
||||
source/clients/archiveorg.cpp
|
||||
source/clients/ftpclient.cpp
|
||||
source/clients/gdrive.cpp
|
||||
source/clients/github.cpp
|
||||
source/clients/myrient.cpp
|
||||
source/clients/iis.cpp
|
||||
source/clients/nginx.cpp
|
||||
@@ -69,7 +70,7 @@ add_executable(ezremote_client
|
||||
|
||||
add_self(ezremote_client)
|
||||
|
||||
add_pkg(ezremote_client ${CMAKE_SOURCE_DIR}/data "RMTC00001" "ezRemote Client" "01.32" 32 0)
|
||||
add_pkg(ezremote_client ${CMAKE_SOURCE_DIR}/data "RMTC00001" "ezRemote Client" "01.35" 32 0)
|
||||
|
||||
target_link_libraries(ezremote_client
|
||||
c
|
||||
|
||||
@@ -87,7 +87,13 @@ To distinguish between FTP, SMB, NFS, WebDAV or HTTP, the URL must be prefix wit
|
||||
| | | |
|
||||
|----------|-----------|---|
|
||||
|  | |  |
|
||||
|
||||
|
||||
- For Myrient repos, entry **https://myrient.erista.me/files** in the server field.
|
||||

|
||||
|
||||
- Support for browse and download release artifacts from github repos. Under the server just enter the git repo of the homebrew eg https://github.com/cy33hc/ps4-ezremote-client
|
||||

|
||||
|
||||
Tested with following WebDAV server:
|
||||
- **(Recommeded)** [Dufs](https://github.com/sigoden/dufs) - For hosting your own WebDAV server. (Recommended since this allow anonymous access which is required for Remote Package Install)
|
||||
- [SFTPgo](https://github.com/drakkan/sftpgo) - For local hosted WebDAV server. Can also be used as a webdav frontend for Cloud Storage like AWS S3, Azure Blob or Google Storage.
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "clients/webdav.h"
|
||||
#include "clients/apache.h"
|
||||
#include "clients/archiveorg.h"
|
||||
#include "clients/github.h"
|
||||
#include "clients/myrient.h"
|
||||
#include "clients/nginx.h"
|
||||
#include "clients/npxserve.h"
|
||||
@@ -1314,6 +1315,8 @@ namespace Actions
|
||||
remoteclient = new ArchiveOrgClient();
|
||||
else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_MYRIENT) == 0)
|
||||
remoteclient = new MyrientClient();
|
||||
else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_GITHUB) == 0)
|
||||
remoteclient = new GithubClient();
|
||||
}
|
||||
else if (strncmp(remote_settings->server, "webdavs://", 10) == 0 || strncmp(remote_settings->server, "webdav://", 9) == 0)
|
||||
{
|
||||
|
||||
@@ -15,7 +15,119 @@ 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::map<std::string, int> month_map = {{"Jan", 1}, {"Feb", 2}, {"Mar", 3}, {"Apr", 4}, {"May", 5}, {"Jun", 6}, {"Jul", 7}, {"Aug", 8}, {"Sep", 9}, {"Oct", 10}, {"Nov", 11}, {"Dec", 12}};
|
||||
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);
|
||||
else if (Ping())
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DirEntry> ArchiveOrgClient::ListDir(const std::string &path)
|
||||
{
|
||||
@@ -23,9 +135,11 @@ std::vector<DirEntry> ArchiveOrgClient::ListDir(const std::string &path)
|
||||
DirEntry entry;
|
||||
Util::SetupPreviousFolder(path, &entry);
|
||||
out.push_back(entry);
|
||||
Headers headers;
|
||||
SetCookies(headers);
|
||||
|
||||
std::string encoded_path = httplib::detail::encode_url(GetFullPath(path) + "/");
|
||||
if (auto res = client->Get(encoded_path))
|
||||
if (auto res = client->Get(encoded_path, headers))
|
||||
{
|
||||
lxb_status_t status;
|
||||
lxb_dom_attr_t *attr;
|
||||
@@ -130,6 +244,13 @@ std::vector<DirEntry> ArchiveOrgClient::ListDir(const std::string &path)
|
||||
// td0 contains the <a> tag
|
||||
td_element = lxb_dom_collection_element(td_collection, 0);
|
||||
lxb_dom_node_t *a_node = NextChildElement(td_element);
|
||||
// there is no a_node in protected links
|
||||
if (a_node == nullptr)
|
||||
{
|
||||
lxb_dom_collection_destroy(td_collection, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
value = lxb_dom_element_local_name(lxb_dom_interface_element(a_node), &value_len);
|
||||
tmp_string = std::string((const char *)value, value_len);
|
||||
if (tmp_string.compare("a") != 0)
|
||||
@@ -236,4 +357,4 @@ std::vector<DirEntry> ArchiveOrgClient::ListDir(const std::string &path)
|
||||
|
||||
finish:
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,12 @@
|
||||
class ArchiveOrgClient : public BaseClient
|
||||
{
|
||||
public:
|
||||
int Connect(const std::string &url, const std::string &username, const std::string &password);
|
||||
std::vector<DirEntry> ListDir(const std::string &path);
|
||||
|
||||
private:
|
||||
int Login(const std::string &username, const std::string &password);
|
||||
std::string GenerateRandomId(const int len);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -20,6 +20,26 @@ BaseClient::~BaseClient()
|
||||
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;
|
||||
@@ -81,7 +101,10 @@ int BaseClient::Rmdir(const std::string &path, bool recursive)
|
||||
|
||||
int BaseClient::Size(const std::string &path, int64_t *size)
|
||||
{
|
||||
if (auto res = client->Head(GetFullPath(path)))
|
||||
Headers headers;
|
||||
SetCookies(headers);
|
||||
|
||||
if (auto res = client->Head(GetFullPath(path), headers))
|
||||
{
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
@@ -94,6 +117,8 @@ int BaseClient::Size(const std::string &path, int64_t *size)
|
||||
// example: Content-Range: bytes 0-10/4372785
|
||||
{
|
||||
Headers headers = {{"Range", "bytes=0-1"}};
|
||||
SetCookies(headers);
|
||||
|
||||
if (auto range_res = client->Get(GetFullPath(path), headers))
|
||||
{
|
||||
if (HTTP_SUCCESS(range_res->status))
|
||||
@@ -113,7 +138,7 @@ int BaseClient::Size(const std::string &path, int64_t *size)
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(this->response, "%d - %s", res->status, detail::status_message(res->status));
|
||||
sprintf(this->response, "%d - %s", res->status, http_status_message(res->status));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -128,8 +153,10 @@ int BaseClient::Get(const std::string &outputfile, const std::string &path, uint
|
||||
std::ofstream file_stream(outputfile, std::ios::binary);
|
||||
bytes_transfered = 0;
|
||||
sceRtcGetCurrentTick(&prev_tick);
|
||||
Headers headers;
|
||||
SetCookies(headers);
|
||||
|
||||
if (auto res = client->Get(GetFullPath(path),
|
||||
if (auto res = client->Get(GetFullPath(path), headers,
|
||||
[&](const char *data, size_t data_length)
|
||||
{
|
||||
file_stream.write(data, data_length);
|
||||
@@ -149,7 +176,10 @@ int BaseClient::Get(const std::string &outputfile, const std::string &path, uint
|
||||
|
||||
int BaseClient::Get(SplitFile *split_file, const std::string &path, uint64_t offset)
|
||||
{
|
||||
if (auto res = client->Get(GetFullPath(path),
|
||||
Headers headers;
|
||||
SetCookies(headers);
|
||||
|
||||
if (auto res = client->Get(GetFullPath(path), headers,
|
||||
[&](const char *data, size_t data_length)
|
||||
{
|
||||
if (!split_file->IsClosed())
|
||||
@@ -175,6 +205,8 @@ int BaseClient::GetRange(const std::string &path, DataSink &sink, uint64_t size,
|
||||
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)
|
||||
@@ -198,6 +230,8 @@ int BaseClient::GetRange(const std::string &path, void *buffer, uint64_t size, u
|
||||
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;
|
||||
std::vector<char> body;
|
||||
if (auto res = client->Get(GetFullPath(path), headers,
|
||||
@@ -257,6 +291,8 @@ int BaseClient::Head(const std::string &path, void *buffer, uint64_t len)
|
||||
char range_header[64];
|
||||
sprintf(range_header, "bytes=%lu-%lu", 0L, len - 1);
|
||||
Headers headers = {{"Range", range_header}};
|
||||
SetCookies(headers);
|
||||
|
||||
size_t bytes_read = 0;
|
||||
std::vector<char> body;
|
||||
if (auto res = client->Get(GetFullPath(path), headers,
|
||||
|
||||
@@ -49,11 +49,14 @@ public:
|
||||
static std::string UnEscape(const std::string &url);
|
||||
|
||||
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
|
||||
@@ -24,8 +24,6 @@
|
||||
#define FTP_CLIENT_READ 1
|
||||
#define FTP_CLIENT_WRITE 2
|
||||
|
||||
#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
|
||||
|
||||
FtpClient::FtpClient()
|
||||
{
|
||||
mp_ftphandle = static_cast<ftphandle *>(calloc(1, sizeof(ftphandle)));
|
||||
@@ -125,7 +123,7 @@ int FtpClient::Connect(const std::string &url, const std::string &user, const st
|
||||
}
|
||||
mp_ftphandle->handle = sControl;
|
||||
|
||||
if (ReadResponse('2', mp_ftphandle) == 0)
|
||||
if (ReadResponse("2", mp_ftphandle) == 0)
|
||||
{
|
||||
close(mp_ftphandle->handle);
|
||||
mp_ftphandle->handle = 0;
|
||||
@@ -142,7 +140,7 @@ int FtpClient::Connect(const std::string &url, const std::string &user, const st
|
||||
cmd = "USER anonymous";
|
||||
}
|
||||
|
||||
if (!FtpSendCmd(cmd, '3', mp_ftphandle))
|
||||
if (!FtpSendCmd(cmd, "3", mp_ftphandle))
|
||||
{
|
||||
if (mp_ftphandle->ctrl != NULL)
|
||||
return 1;
|
||||
@@ -161,7 +159,7 @@ int FtpClient::Connect(const std::string &url, const std::string &user, const st
|
||||
|
||||
cmd = "PASS " + pass;
|
||||
int ret;
|
||||
if ((ret = FtpSendCmd(cmd, '2', mp_ftphandle)))
|
||||
if ((ret = FtpSendCmd(cmd, "2", mp_ftphandle)))
|
||||
{
|
||||
mp_ftphandle->is_connected = true;
|
||||
}
|
||||
@@ -179,7 +177,7 @@ int FtpClient::Connect(const std::string &url, const std::string &user, const st
|
||||
*
|
||||
* return 1 if proper response received, 0 otherwise
|
||||
*/
|
||||
int FtpClient::FtpSendCmd(const std::string &cmd, char expected_resp, ftphandle *nControl)
|
||||
int FtpClient::FtpSendCmd(const std::string &cmd, const std::string &expected_resp, ftphandle *nControl)
|
||||
{
|
||||
char buf[512];
|
||||
int x;
|
||||
@@ -205,7 +203,7 @@ int FtpClient::FtpSendCmd(const std::string &cmd, char expected_resp, ftphandle
|
||||
* return 0 if first char doesn't match
|
||||
* return 1 if first char matches
|
||||
*/
|
||||
int FtpClient::ReadResponse(char c, ftphandle *nControl)
|
||||
int FtpClient::ReadResponse(const std::string &c, ftphandle *nControl)
|
||||
{
|
||||
char match[5];
|
||||
|
||||
@@ -228,7 +226,7 @@ int FtpClient::ReadResponse(char c, ftphandle *nControl)
|
||||
} while (strncmp(nControl->response, match, 4));
|
||||
}
|
||||
|
||||
if (nControl->response[0] == c)
|
||||
if (c.find(nControl->response[0]) != std::string::npos)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -368,7 +366,7 @@ int FtpClient::FtpAccess(const std::string &path, accesstype type, transfermode
|
||||
return 0;
|
||||
}
|
||||
sprintf(buf, "TYPE %c", mode);
|
||||
if (!FtpSendCmd(buf, '2', nControl))
|
||||
if (!FtpSendCmd(buf, "2", nControl))
|
||||
return 0;
|
||||
|
||||
switch (type)
|
||||
@@ -477,7 +475,7 @@ int FtpClient::FtpAcceptConnection(ftphandle *nData, ftphandle *nControl)
|
||||
{
|
||||
close(nData->handle);
|
||||
nData->handle = 0;
|
||||
ReadResponse('2', nControl);
|
||||
ReadResponse("2", nControl);
|
||||
rv = 0;
|
||||
}
|
||||
|
||||
@@ -521,7 +519,7 @@ int FtpClient::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode
|
||||
|
||||
memset(&sin, 0, l);
|
||||
sin.in.sin_family = AF_INET;
|
||||
if (!FtpSendCmd("PASV", '2', nControl))
|
||||
if (!FtpSendCmd("PASV", "2", nControl))
|
||||
return -1;
|
||||
cp = strchr(nControl->response, '(');
|
||||
if (cp == NULL)
|
||||
@@ -542,7 +540,7 @@ int FtpClient::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode
|
||||
{
|
||||
char buf[512];
|
||||
sprintf(buf, "REST %lld", mp_ftphandle->offset);
|
||||
if (!FtpSendCmd(buf, '3', nControl))
|
||||
if (!FtpSendCmd(buf, "3", nControl))
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -592,7 +590,7 @@ int FtpClient::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ReadResponse('1', nControl))
|
||||
if (!ReadResponse("1", nControl))
|
||||
{
|
||||
close(sData);
|
||||
return -1;
|
||||
@@ -717,7 +715,7 @@ int FtpClient::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode
|
||||
(unsigned char)sin.sa.sa_data[5],
|
||||
(unsigned char)sin.sa.sa_data[0],
|
||||
(unsigned char)sin.sa.sa_data[1]);
|
||||
if (!FtpSendCmd(buf, '2', nControl))
|
||||
if (!FtpSendCmd(buf, "2", nControl))
|
||||
{
|
||||
close(sData);
|
||||
return -1;
|
||||
@@ -727,7 +725,7 @@ int FtpClient::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode
|
||||
{
|
||||
char buf[512];
|
||||
sprintf(buf, "REST %lld", mp_ftphandle->offset);
|
||||
if (!FtpSendCmd(buf, '3', nControl))
|
||||
if (!FtpSendCmd(buf, "3", nControl))
|
||||
{
|
||||
close(sData);
|
||||
return 0;
|
||||
@@ -747,7 +745,7 @@ int FtpClient::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!FtpSendCmd(cmd, '1', nControl))
|
||||
if (!FtpSendCmd(cmd, "1", nControl))
|
||||
{
|
||||
FtpClose(*nData);
|
||||
*nData = NULL;
|
||||
@@ -1045,7 +1043,7 @@ int FtpClient::FtpClose(ftphandle *nData)
|
||||
ctrl = nData->ctrl;
|
||||
free(nData);
|
||||
if (ctrl)
|
||||
return ReadResponse('2', ctrl);
|
||||
return ReadResponse("2", ctrl);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1061,7 +1059,7 @@ int FtpClient::Quit()
|
||||
strcpy(mp_ftphandle->response, "error: no anwser from server\n");
|
||||
return 0;
|
||||
}
|
||||
FtpSendCmd("QUIT", '2', mp_ftphandle);
|
||||
FtpSendCmd("QUIT", "2", mp_ftphandle);
|
||||
shutdown(mp_ftphandle->handle, SHUT_WR);
|
||||
struct linger lng = {1, 0};
|
||||
setsockopt(mp_ftphandle->handle, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng));
|
||||
@@ -1105,7 +1103,7 @@ int FtpClient::RawRead(void *buf, int max, ftphandle *handle)
|
||||
int FtpClient::Site(const std::string &cmd)
|
||||
{
|
||||
std::string tmp = "SITE " + cmd;
|
||||
if (!FtpSendCmd(tmp, '2', mp_ftphandle))
|
||||
if (!FtpSendCmd(tmp, "2", mp_ftphandle))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -1118,7 +1116,7 @@ int FtpClient::Site(const std::string &cmd)
|
||||
|
||||
int FtpClient::Raw(const std::string &cmd)
|
||||
{
|
||||
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
|
||||
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -1137,7 +1135,7 @@ int FtpClient::SysType(char *buf, int max)
|
||||
int l = max;
|
||||
char *b = buf;
|
||||
char *s;
|
||||
if (!FtpSendCmd("SYST", '2', mp_ftphandle))
|
||||
if (!FtpSendCmd("SYST", "2", mp_ftphandle))
|
||||
return 0;
|
||||
s = &mp_ftphandle->response[4];
|
||||
while ((--l) && (*s != ' '))
|
||||
@@ -1154,7 +1152,7 @@ int FtpClient::SysType(char *buf, int max)
|
||||
int FtpClient::Mkdir(const std::string &path)
|
||||
{
|
||||
std::string cmd = "MKD " + path;
|
||||
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
|
||||
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -1167,7 +1165,7 @@ int FtpClient::Mkdir(const std::string &path)
|
||||
int FtpClient::Chdir(const std::string &path)
|
||||
{
|
||||
std::string cmd = "CWD " + path;
|
||||
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
|
||||
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -1179,7 +1177,7 @@ int FtpClient::Chdir(const std::string &path)
|
||||
*/
|
||||
int FtpClient::Cdup()
|
||||
{
|
||||
if (!FtpSendCmd("CDUP", '2', mp_ftphandle))
|
||||
if (!FtpSendCmd("CDUP", "2", mp_ftphandle))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -1191,7 +1189,7 @@ int FtpClient::Cdup()
|
||||
*/
|
||||
bool FtpClient::Noop()
|
||||
{
|
||||
if (!FtpSendCmd("NOOP", '2', mp_ftphandle))
|
||||
if (!FtpSendCmd("NOOP", "25", mp_ftphandle))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -1209,7 +1207,7 @@ bool FtpClient::Ping()
|
||||
int FtpClient::Rmdir(const std::string &path)
|
||||
{
|
||||
std::string cmd = "RMD " + path;
|
||||
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
|
||||
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -1272,11 +1270,11 @@ int FtpClient::Size(const std::string &path, int64_t *size)
|
||||
return 0;
|
||||
|
||||
sprintf(cmd, "TYPE %c", FtpClient::transfermode::image);
|
||||
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
|
||||
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
|
||||
return 0;
|
||||
|
||||
sprintf(cmd, "SIZE %s", path.c_str());
|
||||
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
|
||||
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
|
||||
rv = 0;
|
||||
else
|
||||
{
|
||||
@@ -1408,10 +1406,10 @@ int FtpClient::Put(const std::string &inputfile, const std::string &path, uint64
|
||||
int FtpClient::Rename(const std::string &src, const std::string &dst)
|
||||
{
|
||||
std::string cmd = "RNFR " + src;
|
||||
if (!FtpSendCmd(cmd, '3', mp_ftphandle))
|
||||
if (!FtpSendCmd(cmd, "3", mp_ftphandle))
|
||||
return 0;
|
||||
cmd = "RNTO " + dst;
|
||||
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
|
||||
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
@@ -1420,7 +1418,7 @@ int FtpClient::Rename(const std::string &src, const std::string &dst)
|
||||
int FtpClient::Delete(const std::string &path)
|
||||
{
|
||||
std::string cmd = "DELE " + path;
|
||||
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
|
||||
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -114,12 +114,12 @@ private:
|
||||
char server[128];
|
||||
int server_port;
|
||||
|
||||
int FtpSendCmd(const std::string &cmd, char expected_resp, ftphandle *nControl);
|
||||
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(char c, ftphandle *nControl);
|
||||
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();
|
||||
|
||||
+174
-13
@@ -431,7 +431,6 @@ int GDriveClient::Update(const std::string &inputfile, const std::string &path)
|
||||
sceRtcGetCurrentTick(&prev_tick);
|
||||
|
||||
std::ifstream file_stream(inputfile, std::ios::binary);
|
||||
|
||||
std::string id = GetValue(path_id_map, path);
|
||||
std::string drive_id = GetDriveId(path);
|
||||
|
||||
@@ -442,6 +441,7 @@ int GDriveClient::Update(const std::string &inputfile, const std::string &path)
|
||||
headers.insert(std::make_pair("X-Upload-Content-Type", "application/octet-stream"));
|
||||
headers.insert(std::make_pair("X-Upload-Content-Length", std::to_string(bytes_to_download)));
|
||||
char *buf = new char[GOOGLE_BUF_SIZE];
|
||||
|
||||
if (auto res = client->Patch(url))
|
||||
{
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
@@ -457,8 +457,8 @@ int GDriveClient::Update(const std::string &inputfile, const std::string &path)
|
||||
upload_uri, bytes_to_download,
|
||||
[&file_stream, &buf](size_t offset, size_t length, DataSink &sink)
|
||||
{
|
||||
uint32_t count = 0;
|
||||
uint32_t bytes_to_transfer = MIN(GOOGLE_BUF_SIZE, length - count);
|
||||
uint64_t count = 0;
|
||||
uint64_t bytes_to_transfer = MIN(GOOGLE_BUF_SIZE, length - count);
|
||||
do
|
||||
{
|
||||
file_stream.read(buf, bytes_to_transfer);
|
||||
@@ -471,7 +471,84 @@ int GDriveClient::Update(const std::string &inputfile, const std::string &path)
|
||||
},
|
||||
"application/octet-stream"))
|
||||
{
|
||||
// success
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
delete[] buf;
|
||||
file_stream.close();
|
||||
return 1;
|
||||
}
|
||||
else if (res->status == 503)
|
||||
{
|
||||
// retry interrupted uploads
|
||||
int retries = 5;
|
||||
while (res->status == 503 && retries > 0)
|
||||
{
|
||||
// Send empty PUT request to get resume position
|
||||
Headers headers;
|
||||
headers.insert(std::make_pair("Content-Range", "*/*"));
|
||||
auto resume_res = client->Put(upload_uri, headers, "", 0, "application/octet-stream");
|
||||
retries--;
|
||||
|
||||
if (HTTP_SUCCESS(resume_res->status))
|
||||
{
|
||||
delete[] buf;
|
||||
file_stream.close();
|
||||
return 1;
|
||||
}
|
||||
else if (resume_res->status == 308)
|
||||
{
|
||||
std::string range_val = resume_res->get_header_value("Range");
|
||||
uint64_t resume_offset = std::stol(Util::Split(Util::Split(range_val, "=")[1], "-")[1]);
|
||||
|
||||
Headers headers;
|
||||
headers.insert(std::make_pair("Content-Length", std::to_string(bytes_to_download-resume_offset)));
|
||||
std::string range_value = "bytes " + std::to_string(resume_offset + 1) + "_" + std::to_string(bytes_to_download-1);
|
||||
headers.insert(std::make_pair("Content-Range", range_value));
|
||||
file_stream.seekg(resume_offset+1);
|
||||
|
||||
|
||||
if (res = client->Put(upload_uri, bytes_to_download-resume_offset,
|
||||
[&file_stream, &buf](size_t offset, size_t length, DataSink &sink)
|
||||
{
|
||||
uint64_t count = 0;
|
||||
uint64_t bytes_to_transfer = MIN(GOOGLE_BUF_SIZE, length - count);
|
||||
do
|
||||
{
|
||||
file_stream.read(buf, bytes_to_transfer);
|
||||
if (sink.write(buf, bytes_to_transfer))
|
||||
{
|
||||
count += bytes_to_transfer;
|
||||
bytes_transfered += bytes_to_transfer;
|
||||
bytes_to_transfer = MIN(GOOGLE_BUF_SIZE, length - count);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} while (count < length);
|
||||
return true;
|
||||
},
|
||||
"application/octet-stream"))
|
||||
{
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
delete[] buf;
|
||||
file_stream.close();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete[] buf;
|
||||
file_stream.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// pause 5s before retrying
|
||||
sceKernelUsleep(5000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -505,7 +582,6 @@ int GDriveClient::Put(const std::string &inputfile, const std::string &path, uin
|
||||
sceRtcGetCurrentTick(&prev_tick);
|
||||
|
||||
std::ifstream file_stream(inputfile, std::ios::binary);
|
||||
|
||||
size_t path_pos = path.find_last_of("/");
|
||||
std::string parent_dir;
|
||||
if (path_pos == 0)
|
||||
@@ -528,7 +604,9 @@ int GDriveClient::Put(const std::string &inputfile, const std::string &path, uin
|
||||
headers.insert(std::make_pair("X-Upload-Content-Type", "application/octet-stream"));
|
||||
headers.insert(std::make_pair("X-Upload-Content-Length", std::to_string(bytes_to_download)));
|
||||
char *buf = new char[GOOGLE_BUF_SIZE];
|
||||
if (auto res = client->Post(url, headers, post_data.c_str(), post_data.length(), "application/json"))
|
||||
|
||||
httplib::Result res;
|
||||
if (res = client->Post(url, headers, post_data.c_str(), post_data.length(), "application/json"))
|
||||
{
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
@@ -543,21 +621,104 @@ int GDriveClient::Put(const std::string &inputfile, const std::string &path, uin
|
||||
upload_uri, bytes_to_download,
|
||||
[&file_stream, &buf](size_t offset, size_t length, DataSink &sink)
|
||||
{
|
||||
uint32_t count = 0;
|
||||
uint32_t bytes_to_transfer = MIN(GOOGLE_BUF_SIZE, length - count);
|
||||
uint64_t count = 0;
|
||||
uint64_t bytes_to_transfer = MIN(GOOGLE_BUF_SIZE, length - count);
|
||||
do
|
||||
{
|
||||
file_stream.read(buf, bytes_to_transfer);
|
||||
sink.write(buf, bytes_to_transfer);
|
||||
count += bytes_to_transfer;
|
||||
bytes_transfered += bytes_to_transfer;
|
||||
bytes_to_transfer = MIN(GOOGLE_BUF_SIZE, length - count);
|
||||
if (sink.write(buf, bytes_to_transfer))
|
||||
{
|
||||
count += bytes_to_transfer;
|
||||
bytes_transfered += bytes_to_transfer;
|
||||
bytes_to_transfer = MIN(GOOGLE_BUF_SIZE, length - count);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} while (count < length);
|
||||
return true;
|
||||
},
|
||||
"application/octet-stream"))
|
||||
{
|
||||
// success
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
delete[] buf;
|
||||
file_stream.close();
|
||||
return 1;
|
||||
}
|
||||
else if (res->status == 503)
|
||||
{
|
||||
// retry interrupted uploads
|
||||
int retries = 5;
|
||||
while (res->status == 503)
|
||||
{
|
||||
// Send empty PUT request to get resume position
|
||||
Headers headers;
|
||||
headers.insert(std::make_pair("Content-Range", "*/*"));
|
||||
retries--;
|
||||
|
||||
auto resume_res = client->Put(upload_uri, headers, "", 0, "application/octet-stream");
|
||||
|
||||
if (HTTP_SUCCESS(resume_res->status))
|
||||
{
|
||||
delete[] buf;
|
||||
file_stream.close();
|
||||
return 1;
|
||||
}
|
||||
else if (resume_res->status == 308)
|
||||
{
|
||||
std::string range_val = resume_res->get_header_value("Range");
|
||||
uint64_t resume_offset = std::stol(Util::Split(Util::Split(range_val, "=")[1], "-")[1]);
|
||||
|
||||
Headers headers;
|
||||
headers.insert(std::make_pair("Content-Length", std::to_string(bytes_to_download-resume_offset)));
|
||||
std::string range_value = "bytes " + std::to_string(resume_offset + 1) + "_" + std::to_string(bytes_to_download-1);
|
||||
headers.insert(std::make_pair("Content-Range", range_value));
|
||||
file_stream.seekg(resume_offset+1);
|
||||
|
||||
if (res = client->Put(upload_uri, bytes_to_download-resume_offset,
|
||||
[&file_stream, &buf](size_t offset, size_t length, DataSink &sink)
|
||||
{
|
||||
uint64_t count = 0;
|
||||
uint64_t bytes_to_transfer = MIN(GOOGLE_BUF_SIZE, length - count);
|
||||
do
|
||||
{
|
||||
file_stream.read(buf, bytes_to_transfer);
|
||||
if (sink.write(buf, bytes_to_transfer))
|
||||
{
|
||||
count += bytes_to_transfer;
|
||||
bytes_transfered += bytes_to_transfer;
|
||||
bytes_to_transfer = MIN(GOOGLE_BUF_SIZE, length - count);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} while (count < length);
|
||||
return true;
|
||||
},
|
||||
"application/octet-stream"))
|
||||
{
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
delete[] buf;
|
||||
file_stream.close();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete[] buf;
|
||||
file_stream.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// pause 5s before retrying
|
||||
sceKernelUsleep(5000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
#include <json-c/json.h>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include "common.h"
|
||||
#include "clients/remote_client.h"
|
||||
#include "clients/github.h"
|
||||
#include "lang.h"
|
||||
#include "util.h"
|
||||
#include "windows.h"
|
||||
|
||||
using httplib::Client;
|
||||
using httplib::Headers;
|
||||
using httplib::Result;
|
||||
|
||||
int GithubClient::Connect(const std::string &url, const std::string &username, const std::string &password)
|
||||
{
|
||||
if (url.find("https://github.com") == std::string::npos)
|
||||
return 0;
|
||||
|
||||
this->host_url = "https://api.github.com";
|
||||
this->base_path = "/repos" + url.substr(18);
|
||||
Util::Rtrim(this->base_path, "/");
|
||||
this->base_path += "/releases";
|
||||
|
||||
client = new httplib::Client(this->host_url);
|
||||
if (username.length() > 0)
|
||||
client->set_basic_auth(username, password);
|
||||
client->set_follow_location(true);
|
||||
client->set_connection_timeout(10);
|
||||
client->set_read_timeout(30);
|
||||
client->enable_server_certificate_verification(false);
|
||||
m_client.Connect("https://github.com", username, password);
|
||||
|
||||
if (Ping())
|
||||
this->connected = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<DirEntry> GithubClient::ListDir(const std::string &path)
|
||||
{
|
||||
std::vector<DirEntry> out;
|
||||
DirEntry entry;
|
||||
Util::SetupPreviousFolder(path, &entry);
|
||||
out.push_back(entry);
|
||||
|
||||
if (!ParseReleases())
|
||||
return out;
|
||||
|
||||
if (path.compare("/") == 0) // return releases as folders
|
||||
{
|
||||
for (std::vector<GitRelease>::iterator release = m_releases.begin(); release != m_releases.end();)
|
||||
{
|
||||
DirEntry entry;
|
||||
entry.isDir = true;
|
||||
entry.selectable = true;
|
||||
entry.file_size = 0;
|
||||
snprintf(entry.directory, 512, "%s", "/");
|
||||
snprintf(entry.name, 256, "%s", release->name.c_str());
|
||||
snprintf(entry.path, 768, "/%s", release->name.c_str());
|
||||
snprintf(entry.display_size, 48, "%s", lang_strings[STR_FOLDER]);
|
||||
entry.modified = release->modified;
|
||||
|
||||
out.push_back(entry);
|
||||
release++;
|
||||
}
|
||||
}
|
||||
else // return assets in the releases matching the path
|
||||
{
|
||||
std::string tag_name = path.substr(1);
|
||||
std::map<std::string, GitAsset> assets = m_assets[tag_name];
|
||||
for (std::map<std::string, GitAsset>::iterator asset = assets.begin(); asset != assets.end();)
|
||||
{
|
||||
DirEntry entry;
|
||||
memset(&entry, 0, sizeof(DirEntry));
|
||||
entry.isDir = false;
|
||||
entry.selectable = true;
|
||||
snprintf(entry.directory, 512, "%s", path.c_str());
|
||||
snprintf(entry.name, 256, "%s", asset->second.name.c_str());
|
||||
snprintf(entry.path, 768, "%s/%s", path.c_str(), asset->second.name.c_str());
|
||||
entry.file_size = asset->second.size;
|
||||
entry.modified = asset->second.modified;
|
||||
DirEntry::SetDisplaySize(&entry);
|
||||
|
||||
out.push_back(entry);
|
||||
asset++;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
int GithubClient::Size(const std::string &path, int64_t *size)
|
||||
{
|
||||
if (!ParseReleases())
|
||||
return 0;
|
||||
|
||||
std::vector<std::string> path_parts = Util::Split(path, "/");
|
||||
|
||||
if (path_parts.size() != 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
*size = m_assets[path_parts[0]][path_parts[1]].size;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int GithubClient::Head(const std::string &path, void *buffer, uint64_t len)
|
||||
{
|
||||
if (!ParseReleases())
|
||||
return 0;
|
||||
|
||||
std::vector<std::string> path_parts = Util::Split(path, "/");
|
||||
|
||||
if (path_parts.size() != 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_client.Head(m_assets[path_parts[0]][path_parts[1]].url, buffer, len);
|
||||
}
|
||||
|
||||
int GithubClient::Get(const std::string &outputfile, const std::string &path, uint64_t offset)
|
||||
{
|
||||
if (!ParseReleases())
|
||||
return 0;
|
||||
|
||||
std::vector<std::string> path_parts = Util::Split(path, "/");
|
||||
|
||||
if (path_parts.size() != 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_client.Get(outputfile, m_assets[path_parts[0]][path_parts[1]].url, offset);
|
||||
}
|
||||
|
||||
int GithubClient::Get(SplitFile *split_file, const std::string &path, uint64_t offset)
|
||||
{
|
||||
if (!ParseReleases())
|
||||
return 0;
|
||||
|
||||
std::vector<std::string> path_parts = Util::Split(path, "/");
|
||||
|
||||
if (path_parts.size() != 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_client.Get(split_file, m_assets[path_parts[0]][path_parts[1]].url, offset);
|
||||
}
|
||||
|
||||
int GithubClient::GetRange(const std::string &path, void *buffer, uint64_t size, uint64_t offset)
|
||||
{
|
||||
if (!ParseReleases())
|
||||
return 0;
|
||||
|
||||
std::vector<std::string> path_parts = Util::Split(path, "/");
|
||||
|
||||
if (path_parts.size() != 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_client.GetRange(m_assets[path_parts[0]][path_parts[1]].url, buffer, size, offset);
|
||||
}
|
||||
|
||||
int GithubClient::GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset)
|
||||
{
|
||||
if (!ParseReleases())
|
||||
return 0;
|
||||
|
||||
std::vector<std::string> path_parts = Util::Split(path, "/");
|
||||
|
||||
if (path_parts.size() != 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_client.GetRange(m_assets[path_parts[0]][path_parts[1]].url, sink, size, offset);
|
||||
}
|
||||
|
||||
bool GithubClient::ParseReleases()
|
||||
{
|
||||
if (!releases_parsed)
|
||||
{
|
||||
if (auto res = client->Get(this->base_path + "?per_page=100&page=1"))
|
||||
{
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
json_object *jobj = json_tokener_parse(res->body.c_str());
|
||||
struct array_list *areleases = json_object_get_array(jobj);
|
||||
|
||||
for (size_t release_idx = 0; release_idx < areleases->length; ++release_idx)
|
||||
{
|
||||
GitRelease release_entry;
|
||||
|
||||
json_object *release = (json_object *)array_list_get_idx(areleases, release_idx);
|
||||
release_entry.name = std::string(json_object_get_string(json_object_object_get(release, "tag_name")));
|
||||
std::string date_time = std::string(json_object_get_string(json_object_object_get(release, "published_at")));
|
||||
|
||||
auto date_time_array = Util::Split(date_time, "T");
|
||||
auto date_array = Util::Split(date_time_array[0], "-");
|
||||
auto time_array = Util::Split(date_time_array[1], ":");
|
||||
release_entry.modified.year = std::atoi(date_array[0].c_str());
|
||||
release_entry.modified.month = std::atoi(date_array[1].c_str());
|
||||
release_entry.modified.day = std::atoi(date_array[2].c_str());
|
||||
release_entry.modified.hours = std::atoi(time_array[0].c_str());
|
||||
release_entry.modified.minutes = std::atoi(time_array[1].c_str());
|
||||
release_entry.modified.seconds = std::atoi(time_array[2].substr(0,2).c_str());
|
||||
|
||||
json_object *obj_assets = json_object_object_get(release, "assets");
|
||||
if (json_object_get_type(obj_assets) == json_type_array)
|
||||
{
|
||||
struct array_list *aassets = json_object_get_array(obj_assets);
|
||||
std::map<std::string, GitAsset> assets;
|
||||
|
||||
for (size_t asset_idx = 0; asset_idx < aassets->length; ++asset_idx)
|
||||
{
|
||||
GitAsset asset_entry;
|
||||
|
||||
json_object *asset = (json_object *)array_list_get_idx(aassets, asset_idx);
|
||||
asset_entry.name = std::string(json_object_get_string(json_object_object_get(asset, "name")));
|
||||
asset_entry.size = json_object_get_uint64(json_object_object_get(asset, "size"));
|
||||
std::string date_time = std::string(json_object_get_string(json_object_object_get(asset, "updated_at")));
|
||||
asset_entry.url = std::string(json_object_get_string(json_object_object_get(asset, "browser_download_url")));
|
||||
Util::ReplaceAll(asset_entry.url, "https://github.com", "");
|
||||
|
||||
auto date_time_array = Util::Split(date_time, "T");
|
||||
auto date_array = Util::Split(date_time_array[0], "-");
|
||||
auto time_array = Util::Split(date_time_array[1], ":");
|
||||
asset_entry.modified.year = std::atoi(date_array[0].c_str());
|
||||
asset_entry.modified.month = std::atoi(date_array[1].c_str());
|
||||
asset_entry.modified.day = std::atoi(date_array[2].c_str());
|
||||
asset_entry.modified.hours = std::atoi(time_array[0].c_str());
|
||||
asset_entry.modified.minutes = std::atoi(time_array[1].c_str());
|
||||
asset_entry.modified.seconds = std::atoi(time_array[2].substr(0,2).c_str());
|
||||
|
||||
assets.insert(std::make_pair(asset_entry.name, asset_entry));
|
||||
}
|
||||
|
||||
m_assets.insert(std::make_pair(release_entry.name, assets));
|
||||
}
|
||||
|
||||
m_releases.push_back(release_entry);
|
||||
}
|
||||
|
||||
releases_parsed = true;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef EZ_GITHUB_H
|
||||
#define EZ_GITHUB_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "http/httplib.h"
|
||||
#include "clients/remote_client.h"
|
||||
#include "clients/baseclient.h"
|
||||
#include "common.h"
|
||||
|
||||
class GithubClient : public BaseClient
|
||||
{
|
||||
public:
|
||||
int Connect(const std::string &url, const std::string &username, const std::string &password);
|
||||
std::vector<DirEntry> ListDir(const std::string &path);
|
||||
int Size(const std::string &path, int64_t *size);
|
||||
int Get(const std::string &outputfile, const std::string &path, uint64_t offset=0);
|
||||
int Get(SplitFile *split_file, 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 Head(const std::string &path, void *buffer, uint64_t len);
|
||||
|
||||
private:
|
||||
struct GitAsset
|
||||
{
|
||||
std::string name;
|
||||
std::string url;
|
||||
DateTime modified;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct GitRelease
|
||||
{
|
||||
std::string name;
|
||||
DateTime modified;
|
||||
};
|
||||
|
||||
std::vector<GitRelease> m_releases;
|
||||
std::map<std::string, std::map<std::string, GitAsset>> m_assets;
|
||||
bool releases_parsed = false;
|
||||
BaseClient m_client;
|
||||
|
||||
bool ParseReleases();
|
||||
};
|
||||
|
||||
#endif
|
||||
+3
-2
@@ -156,13 +156,14 @@ namespace CONFIG
|
||||
install_pkg_url.enable_rpi = true;
|
||||
|
||||
sites = {"Site 1", "Site 2", "Site 3", "Site 4", "Site 5", "Site 6", "Site 7", "Site 8", "Site 9", "Site 10",
|
||||
"Site 11", "Site 12", "Site 13", "Site 14", "Site 15", "Site 16", "Site 17", "Site 18", "Site 19", "Site 20"};
|
||||
"Site 11", "Site 12", "Site 13", "Site 14", "Site 15", "Site 16", "Site 17", "Site 18", "Site 19", "Site 20",
|
||||
"Site 21", "Site 22", "Site 23", "Site 24", "Site 25", "Site 26", "Site 27", "Site 28", "Site 29", "Site 30"};
|
||||
|
||||
langs = { "Default", "Arabic", "Catalan", "Croatian", "Dutch", "English", "Euskera", "French", "Galego", "German", "Greek",
|
||||
"Hungarian", "Indonesian", "Italiano", "Japanese", "Korean", "Norwegian", "Polish", "Portuguese_BR", "Russian",
|
||||
"Romanian", "Ryukyuan", "Spanish", "Turkish", "Simplified Chinese", "Traditional Chinese", "Thai", "Ukrainian", "Vietnamese"};
|
||||
|
||||
http_servers = {HTTP_SERVER_APACHE, HTTP_SERVER_MS_IIS, HTTP_SERVER_NGINX, HTTP_SERVER_NPX_SERVE, HTTP_SERVER_RCLONE, HTTP_SERVER_ARCHIVEORG, HTTP_SERVER_MYRIENT};
|
||||
http_servers = {HTTP_SERVER_APACHE, HTTP_SERVER_MS_IIS, HTTP_SERVER_NGINX, HTTP_SERVER_NPX_SERVE, HTTP_SERVER_RCLONE, HTTP_SERVER_ARCHIVEORG, HTTP_SERVER_MYRIENT, HTTP_SERVER_GITHUB};
|
||||
text_file_extensions = { ".txt", ".ini", ".log", ".json", ".xml", ".html", ".xhtml", ".conf", ".config" };
|
||||
image_file_extensions = { ".bmp", ".jpg", ".jpeg", ".png", ".webp" };
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
#define HTTP_SERVER_RCLONE "RClone"
|
||||
#define HTTP_SERVER_ARCHIVEORG "Archive.org"
|
||||
#define HTTP_SERVER_MYRIENT "Myrient"
|
||||
#define HTTP_SERVER_GITHUB "Github"
|
||||
|
||||
#define MAX_EDIT_FILE_SIZE 32768
|
||||
|
||||
|
||||
+980
-480
File diff suppressed because it is too large
Load Diff
+403
-85
File diff suppressed because it is too large
Load Diff
@@ -202,7 +202,8 @@ namespace INSTALLER
|
||||
std::string getRemoteUrl(const std::string path, bool encodeUrl)
|
||||
{
|
||||
if (strlen(remote_settings->username) == 0 && strlen(remote_settings->password) == 0 &&
|
||||
(remoteclient->clientType() == CLIENT_TYPE_WEBDAV || remoteclient->clientType() == CLIENT_TYPE_HTTP_SERVER))
|
||||
(remoteclient->clientType() == CLIENT_TYPE_WEBDAV ||
|
||||
(remoteclient->clientType() == CLIENT_TYPE_HTTP_SERVER && strcmp(remote_settings->http_server_type, HTTP_SERVER_GITHUB) != 0)))
|
||||
{
|
||||
std::string full_url = WebDAVClient::GetHttpUrl(remote_settings->server + path);
|
||||
size_t scheme_pos = full_url.find("://");
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
#include "clients/webdav.h"
|
||||
#include "clients/apache.h"
|
||||
#include "clients/archiveorg.h"
|
||||
#include "clients/github.h"
|
||||
#include "clients/iis.h"
|
||||
#include "clients/myrient.h"
|
||||
#include "clients/nginx.h"
|
||||
#include "clients/npxserve.h"
|
||||
#include "clients/rclone.h"
|
||||
@@ -252,6 +254,10 @@ namespace HttpServer
|
||||
tmp_client = new RCloneClient();
|
||||
else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_ARCHIVEORG) == 0)
|
||||
tmp_client = new ArchiveOrgClient();
|
||||
else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_MYRIENT) == 0)
|
||||
tmp_client = new MyrientClient();
|
||||
else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_GITHUB) == 0)
|
||||
tmp_client = new GithubClient();
|
||||
}
|
||||
|
||||
if (tmp_client->clientType() != CLIENT_TYPE_GOOGLE)
|
||||
|
||||
+42
-33
@@ -439,42 +439,45 @@ namespace Windows
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s:", lang_strings[STR_ENABLE_RPI]);
|
||||
ImGui::SameLine();
|
||||
if (strcmp(remote_settings->http_server_type, HTTP_SERVER_GITHUB) != 0)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s:", lang_strings[STR_ENABLE_RPI]);
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Checkbox("###enable_rpi", &remote_settings->enable_rpi))
|
||||
{
|
||||
CONFIG::SaveConfig();
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(450, 110));
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 440);
|
||||
ImGui::Text("%s", lang_strings[STR_ENABLE_RPI_FTP_SMB_MSG]);
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
if (ImGui::Checkbox("###enable_rpi", &remote_settings->enable_rpi))
|
||||
{
|
||||
CONFIG::SaveConfig();
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(450, 110));
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 440);
|
||||
ImGui::Text("%s", lang_strings[STR_ENABLE_RPI_FTP_SMB_MSG]);
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s:", lang_strings[STR_ENABLE_DISK_CACHE]);
|
||||
ImGui::SameLine();
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s:", lang_strings[STR_ENABLE_DISK_CACHE]);
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Checkbox("###enable_disk_cache", &remote_settings->enable_disk_cache))
|
||||
{
|
||||
CONFIG::SaveConfig();
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(550, 110));
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 540);
|
||||
ImGui::Text("%s", lang_strings[STR_ENABLE_DISC_CACHE_MSG]);
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
if (ImGui::Checkbox("###enable_disk_cache", &remote_settings->enable_disk_cache))
|
||||
{
|
||||
CONFIG::SaveConfig();
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(550, 110));
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 540);
|
||||
ImGui::Text("%s", lang_strings[STR_ENABLE_DISC_CACHE_MSG]);
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
@@ -2647,6 +2650,12 @@ namespace Windows
|
||||
{
|
||||
sprintf(remote_settings->http_server_type, "%s", HTTP_SERVER_MYRIENT);
|
||||
}
|
||||
else if (strncasecmp(remote_settings->server, "https://github.com/", 19) == 0)
|
||||
{
|
||||
snprintf(remote_settings->http_server_type, 24, "%s", HTTP_SERVER_GITHUB);
|
||||
remote_settings->enable_rpi = false;
|
||||
remote_settings->enable_disk_cache = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user