Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ce71f5af9a | |||
| cd42afcb6b | |||
| b6eaef82fd | |||
| 9d0790fccc | |||
| bfe2505efc | |||
| 749a79d8b9 | |||
| c03201c040 | |||
| 7be30e358a | |||
| d58fc0eac2 | |||
| ed3f8a71c4 | |||
| 13279b6040 | |||
| eb465356b6 | |||
| 8602a5353e | |||
| 467a459665 |
+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.34" 32 0)
|
||||
add_pkg(ezremote_client ${CMAKE_SOURCE_DIR}/data "RMTC00001" "ezRemote Client" "01.39" 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.
|
||||
|
||||
+21
-2
@@ -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)
|
||||
{
|
||||
@@ -1467,6 +1470,16 @@ namespace Actions
|
||||
if (src.isDir)
|
||||
{
|
||||
int err;
|
||||
if (!isCopy && !FS::FolderExists(dest))
|
||||
{
|
||||
errno = 0;
|
||||
int ret = rename(src.path, dest);
|
||||
if (ret != 0 && errno != EXDEV && errno != EEXIST)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DirEntry> entries = FS::ListDir(src.path, &err);
|
||||
FS::MkDirs(dest);
|
||||
for (int i = 0; i < entries.size(); i++)
|
||||
@@ -1481,9 +1494,11 @@ namespace Actions
|
||||
if (entries[i].isDir)
|
||||
{
|
||||
if (strcmp(entries[i].name, "..") == 0)
|
||||
{
|
||||
free(new_path);
|
||||
continue;
|
||||
}
|
||||
|
||||
FS::MkDirs(new_path);
|
||||
ret = CopyOrMove(entries[i], new_path, isCopy);
|
||||
if (ret <= 0)
|
||||
{
|
||||
@@ -1507,6 +1522,11 @@ namespace Actions
|
||||
}
|
||||
free(new_path);
|
||||
}
|
||||
|
||||
if (!isCopy && FS::FolderExists(src.path))
|
||||
{
|
||||
FS::RmDir(src.path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1548,7 +1568,6 @@ namespace Actions
|
||||
char new_dir[512];
|
||||
sprintf(new_dir, "%s%s%s", local_directory, FS::hasEndSlash(local_directory) ? "" : "/", it->name);
|
||||
CopyOrMove(*it, new_dir, false);
|
||||
FS::RmRecursive(it->path);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -138,7 +138,7 @@ int BaseClient::Size(const std::string &path, int64_t *size)
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(this->response, "%d - %s", res->status, http_status_message(res->status));
|
||||
sprintf(this->response, "%d - %s", res->status, detail::status_message(res->status));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
+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
|
||||
@@ -223,7 +223,10 @@ std::vector<DirEntry> MyrientClient::ListDir(const std::string &path)
|
||||
}
|
||||
|
||||
lxb_dom_collection_destroy(td_collection, true);
|
||||
out.push_back(entry);
|
||||
if (strcmp(entry.name, "..") != 0 && strcmp(entry.name, ".") != 0)
|
||||
{
|
||||
out.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
lxb_dom_collection_destroy(tr_collection, true);
|
||||
|
||||
+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
|
||||
|
||||
|
||||
+14
-6
@@ -50,7 +50,7 @@ namespace FS
|
||||
|
||||
void RmDir(const std::string &path)
|
||||
{
|
||||
remove(path.c_str());
|
||||
rmdir(path.c_str());
|
||||
}
|
||||
|
||||
int64_t GetSize(const std::string &path)
|
||||
@@ -85,6 +85,7 @@ namespace FS
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Rename(const std::string &from, const std::string &to)
|
||||
{
|
||||
int res = rename(from.c_str(), to.c_str());
|
||||
@@ -269,6 +270,7 @@ namespace FS
|
||||
if (dirent == NULL)
|
||||
{
|
||||
closedir(fd);
|
||||
fd = NULL;
|
||||
return out;
|
||||
}
|
||||
else
|
||||
@@ -344,6 +346,7 @@ namespace FS
|
||||
}
|
||||
}
|
||||
closedir(fd);
|
||||
fd = NULL;
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -549,11 +552,16 @@ namespace FS
|
||||
if (from.compare(to) == 0)
|
||||
return true;
|
||||
|
||||
bool res = Copy(from, to);
|
||||
if (res)
|
||||
Rm(from);
|
||||
else
|
||||
return res;
|
||||
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;
|
||||
}
|
||||
|
||||
+476
-976
File diff suppressed because it is too large
Load Diff
+84
-402
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)
|
||||
|
||||
+38
-6
@@ -56,7 +56,10 @@ size_t SplitFile::Read(char *buf, size_t buf_size, size_t offset)
|
||||
while ((block_num >= this->file_blocks.size() && !this->complete) ||
|
||||
(block_num < this->file_blocks.size() && this->file_blocks[block_num]->status == BLOCK_STATUS_NOT_EXISTS))
|
||||
{
|
||||
sem_wait(&this->block_ready);
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
ts.tv_sec += 2;
|
||||
sem_timedwait(&this->block_ready, &ts);
|
||||
}
|
||||
|
||||
block = this->file_blocks[block_num];
|
||||
@@ -120,7 +123,10 @@ size_t SplitFile::Read(char *buf, size_t buf_size, size_t offset)
|
||||
while ((block_num > this->file_blocks.size() - 1 && !this->complete) ||
|
||||
this->file_blocks[block_num]->status == BLOCK_STATUS_NOT_EXISTS)
|
||||
{
|
||||
sem_wait(&this->block_ready);
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
ts.tv_sec += 2;
|
||||
sem_timedwait(&this->block_ready, &ts);
|
||||
}
|
||||
|
||||
block = this->file_blocks[block_num];
|
||||
@@ -128,7 +134,7 @@ size_t SplitFile::Read(char *buf, size_t buf_size, size_t offset)
|
||||
|
||||
// delete blocks before the first read offset block. Assumuption, that reads are always
|
||||
// forward and won't read previously already read blocks. For safety, keeping only current block and 2 previous blocks
|
||||
for (int j=0; j < first_block_num - 2; j++)
|
||||
for (int j=0; j < first_block_num - 13; j++)
|
||||
{
|
||||
if (this->file_blocks[j]->status == BLOCK_STATUS_CREATED)
|
||||
{
|
||||
@@ -142,12 +148,13 @@ size_t SplitFile::Read(char *buf, size_t buf_size, size_t offset)
|
||||
}
|
||||
}
|
||||
|
||||
this->read_offset = offset + total_bytes_read;
|
||||
return total_bytes_read;
|
||||
}
|
||||
|
||||
size_t SplitFile::Write(char *buf, size_t buf_size)
|
||||
{
|
||||
size_t bytes_written;
|
||||
size_t bytes_written = 0;
|
||||
size_t block_space_remaining;
|
||||
size_t bytes_to_write;
|
||||
|
||||
@@ -155,6 +162,9 @@ size_t SplitFile::Write(char *buf, size_t buf_size)
|
||||
size_t total_bytes_written = 0;
|
||||
size_t remaining_to_write = buf_size;
|
||||
|
||||
if (this->IsClosed())
|
||||
return -1;
|
||||
|
||||
while (remaining_to_write > 0 && !this->complete)
|
||||
{
|
||||
block_space_remaining = this->block_size - block_in_progress->size;
|
||||
@@ -186,6 +196,7 @@ size_t SplitFile::Write(char *buf, size_t buf_size)
|
||||
block_in_progress = NewBlock();
|
||||
}
|
||||
}
|
||||
this->write_offset += total_bytes_written;
|
||||
|
||||
return total_bytes_written;
|
||||
}
|
||||
@@ -195,6 +206,8 @@ int SplitFile::Close()
|
||||
if (this->complete)
|
||||
return 0;
|
||||
|
||||
this->complete = true;
|
||||
|
||||
if (block_in_progress->fd != nullptr)
|
||||
{
|
||||
fflush(block_in_progress->fd);
|
||||
@@ -204,9 +217,28 @@ int SplitFile::Close()
|
||||
block_in_progress->status = BLOCK_STATUS_CREATED;
|
||||
block_in_progress->is_last = true;
|
||||
this->file_blocks.push_back(block_in_progress);
|
||||
this->complete = true;
|
||||
sem_post(&this->block_ready);
|
||||
|
||||
// Wait until file is fully read, if file isn't full read
|
||||
// in 5 mins then go ahead and delete all file chunks
|
||||
int retries = 10;
|
||||
size_t prev_read_offset = 0;
|
||||
while (this->read_offset != this->write_offset && retries > 0)
|
||||
{
|
||||
if (prev_read_offset == this->read_offset)
|
||||
retries--;
|
||||
prev_read_offset = this->read_offset;
|
||||
sceKernelUsleep(1000000);
|
||||
}
|
||||
sceKernelUsleep(5000000);
|
||||
|
||||
for (size_t j = 0; j < this->file_blocks.size(); j++)
|
||||
{
|
||||
if (this->file_blocks[j] != nullptr && this->file_blocks[j]->status == BLOCK_STATUS_CREATED)
|
||||
{
|
||||
remove(this->file_blocks[j]->block_file.c_str());
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -226,4 +258,4 @@ FileBlock *SplitFile::NewBlock()
|
||||
block->fd = fopen(block->block_file.c_str(), "w");
|
||||
|
||||
return block;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -4,6 +4,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <semaphore.h>
|
||||
#include <pthread.h>
|
||||
|
||||
enum FileBlockStatus
|
||||
@@ -35,8 +36,9 @@ public:
|
||||
|
||||
private:
|
||||
std::vector<FileBlock*> file_blocks;
|
||||
size_t write_offset;
|
||||
size_t write_offset = 0;
|
||||
size_t block_size;
|
||||
size_t read_offset;
|
||||
std::string path;
|
||||
int write_error;
|
||||
bool complete;
|
||||
@@ -46,4 +48,4 @@ private:
|
||||
FileBlock *NewBlock();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1618,6 +1618,9 @@ namespace Windows
|
||||
|
||||
void ShowEditorDialog()
|
||||
{
|
||||
if (!paused)
|
||||
saved_selected_browser = selected_browser;
|
||||
|
||||
if (editor_inprogress)
|
||||
{
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
@@ -2647,6 +2650,10 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user