Compare commits

...

14 Commits

Author SHA1 Message Date
cy33hc ce71f5af9a fix version 2026-05-02 22:24:18 -07:00
cy33hc cd42afcb6b fix random crash when installing with disk cache or pkgs in zip files 2026-05-02 22:22:20 -07:00
cy33hc b6eaef82fd fix edit files from Edit menu 2026-03-29 17:15:02 -07:00
cy33hc 9d0790fccc minor improvement to cut/paste files 2026-03-27 01:27:02 -07:00
Chee Yee bfe2505efc fix installing pkg via RPI 2025-03-17 00:28:16 -07:00
Chee Yee 749a79d8b9 Merge branch 'master' of github.com:cy33hc/ps4-ezremote-client 2025-03-05 11:53:48 -08:00
Chee Yee c03201c040 update version 2025-03-05 11:53:42 -08:00
cy33hc 7be30e358a Update README.md 2025-03-01 18:43:00 -08:00
cy33hc d58fc0eac2 Update README.md 2025-03-01 18:38:37 -08:00
Chee Yee ed3f8a71c4 increase site slots to 30 2025-03-01 03:35:46 -08:00
Chee Yee 13279b6040 disable rpi for github 2025-03-01 03:27:29 -08:00
Chee Yee eb465356b6 refactor parging github releases 2025-03-01 00:37:34 -08:00
Chee Yee 8602a5353e add support for github repos 2025-02-28 15:44:17 -08:00
Chee Yee 467a459665 implement resume upload to gdrive 2025-02-28 15:33:03 -08:00
18 changed files with 1147 additions and 1414 deletions
+2 -1
View File
@@ -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
+7 -1
View File
@@ -87,7 +87,13 @@ To distinguish between FTP, SMB, NFS, WebDAV or HTTP, the URL must be prefix wit
| | | |
|----------|-----------|---|
| ![archive_org_screen1](https://github.com/user-attachments/assets/b129b6cf-b938-4d7c-a3fa-61e1c633c1f6) | ![archive_org_screen2](https://github.com/user-attachments/assets/646106d1-e60b-4b35-b153-3475182df968)| ![image](https://github.com/user-attachments/assets/cad94de8-a694-4ef5-92a8-b87468e30adb) |
- For Myrient repos, entry **https://myrient.erista.me/files** in the server field.
![image](https://github.com/user-attachments/assets/b80e2bec-b8cc-4acc-9ab6-7b0dc4ef20e6)
- 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
![image](https://github.com/user-attachments/assets/f8e931ea-f1d1-4af8-aed5-b0dfe661a230)
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
View File
@@ -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
{
+1 -1
View File
@@ -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
View File
@@ -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
{
+257
View File
@@ -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;
}
+46
View File
@@ -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
+4 -1
View File
@@ -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
View File
@@ -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" };
+1
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+84 -402
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -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("://");
+6
View File
@@ -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
View File
@@ -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
View File
@@ -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
+7
View File
@@ -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);
}
}
}