Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d34df2cf94 | |||
| f6cf00974b | |||
| 9788b7e963 | |||
| aa9ef34991 | |||
| fc96b76260 | |||
| c54fc8fcbc | |||
| 5ca56b47df | |||
| 05d253edd8 | |||
| d752dd8fed | |||
| 45320ed2ca | |||
| 8aef5d0233 | |||
| 2c9d31fbcc | |||
| a4510e0b47 | |||
| d6ec9ac1a5 | |||
| 2975f736de | |||
| 9a9308244c | |||
| 649f6daac2 | |||
| cc541c6da5 | |||
| ec7b845bdc | |||
| eed0e5193b | |||
| 9d7e46dcc6 | |||
| 2164443969 | |||
| d3f8cdc774 | |||
| f58e9fdecd | |||
| ea768a3528 | |||
| 6371c6fd29 |
+3
-1
@@ -37,6 +37,7 @@ add_executable(ezremote_client
|
||||
source/clients/npxserve.cpp
|
||||
source/clients/smbclient.cpp
|
||||
source/clients/webdavclient.cpp
|
||||
source/clients/sftpclient.cpp
|
||||
source/server/http_server.cpp
|
||||
source/actions.cpp
|
||||
source/config.cpp
|
||||
@@ -57,7 +58,7 @@ add_executable(ezremote_client
|
||||
|
||||
add_self(ezremote_client)
|
||||
|
||||
add_pkg(ezremote_client ${CMAKE_SOURCE_DIR}/data "RMTC00001" "ezRemote Client" "01.02" 32 0)
|
||||
add_pkg(ezremote_client ${CMAKE_SOURCE_DIR}/data "RMTC00001" "ezRemote Client" "01.05" 32 0)
|
||||
|
||||
target_link_libraries(ezremote_client
|
||||
c
|
||||
@@ -77,6 +78,7 @@ target_link_libraries(ezremote_client
|
||||
un7zip
|
||||
unrar
|
||||
json-c
|
||||
ssh2
|
||||
kernel
|
||||
SceShellCoreUtil
|
||||
SceSysmodule
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
# ezRemote Client
|
||||
|
||||
ezRemote Client is an application that allows you to connect the PS4 to remote FTP, SMB, WebDAV, HTTP servers and Google Drive to transfer files. The interface is inspired by Filezilla client which provides a commander like GUI.
|
||||
ezRemote Client is an application that allows you to connect the PS4 to remote FTP/SFTP, SMB, WebDAV, HTTP servers and Google Drive to transfer files. The interface is inspired by Filezilla client which provides a commander like GUI.
|
||||

|
||||
## Usage
|
||||
To distinguish between FTP, SMB, WebDAV or HTTP, the URL must be prefix with **ftp://**, **smb://**, **webdav://**, **webdavs://**, **http://** and **https://**
|
||||
To distinguish between FTP, SMB, WebDAV or HTTP, the URL must be prefix with **ftp://**, **sftp://**, **smb://**, **webdav://**, **webdavs://**, **http://** and **https://**
|
||||
|
||||
- The url format for FTP is
|
||||
```
|
||||
ftp://hostname[:port]
|
||||
sftp://hostname[:port]
|
||||
|
||||
- hostname can be the textual hostname or an IP address. hostname is required
|
||||
- port is optional and defaults to 21 if not provided
|
||||
- port is optional and defaults to 21(ftp) and 22(sftp) if not provided
|
||||
```
|
||||
For Secure FTP (sftp), use of identity files is possible. Put both the **id_rsa** and **id_rsa.pub** into a folder in the PS4 hard drive. Then in the password field in the UI, instead of putting a password reference the folder where id_rsa and id_rsa.pub is place. Prefix the folder with **"file://"** and **do not** password protect the identity file.
|
||||
```
|
||||
Example: If you had placed the id_rsa and id_rsa.pub files into the folder /data/ezremote-client,
|
||||
then in the password field enter file:///data/ezremote-client
|
||||
```
|
||||
|
||||
- The url format for SMB is
|
||||
@@ -66,7 +72,7 @@ Remote Package Installation only works if the WebDAV server allow anonymous acce
|
||||
- Create Zip files on PS4 local drive or usb drive
|
||||
- Extract from zip, 7zip and rar files
|
||||
- File management function include cut/copy/paste/rename/delete/new folder/file for files on PS4 local drive or usb or WebDAV Server.
|
||||
- Simple Text Editor to make simply changes to config text files.
|
||||
- Simple Text Editor to make simply changes to config text files. Limited to edit files over 32kb and limited to edit lines up to 1023 characters. If you try edit lines longer then 1023 characters, it will be truncated.
|
||||
|
||||
|
||||
## Installation
|
||||
@@ -166,12 +172,16 @@ Build and install libsmb2 - https://github.com/cy33hc/libsmb2/blob/ps4/README_PS
|
||||
|
||||
Build and install lexbor - https://github.com/lexbor/lexbor.git
|
||||
|
||||
Build and install libssh2 - https://www.libssh2.org/
|
||||
|
||||
Build libjbc - https://github.com/cy33hc/ps4-libjbc/blob/master/README_PS4.md
|
||||
|
||||
Build libunrar - https://github.com/cy33hc/libunrar-ps3
|
||||
|
||||
Build libun7zip - https://github.com/cy33hc/libun7zip
|
||||
|
||||
Build libjson-c - https://github.com/json-c/json-c
|
||||
|
||||
I have included the source code from the following 2 projects embedded into this repo.
|
||||
<br/>https://github.com/yhirose/cpp-httplib
|
||||
<br/>https://github.com/CloudPolis/webdav-client-cpp
|
||||
|
||||
Binary file not shown.
@@ -107,9 +107,9 @@ STR_CANT_COPY_TO_SUBDIR_MSG=Cannot copy parent directory to sub subdirectory
|
||||
STR_UNSUPPORTED_OPERATION_MSG=Operation not supported
|
||||
STR_HTTP_PORT=Http Port
|
||||
STR_REINSTALL_CONFIRM_MSG=The content has already been installed. Do you want to continue installing
|
||||
STR_REMOTE_NOT_SUPPORT_MSG=Remote server requires at least a username.
|
||||
STR_REMOTE_NOT_SUPPORT_MSG=Remote package installation is not supported for protected servers.
|
||||
STR_CANNOT_CONNECT_REMOTE_MSG=Remote HTTP Server not reachable.
|
||||
STR_DOWNLOAD_INSTALL_MSG=Remote Package Install not possible. Would you like to download package and install?
|
||||
STR_DOWNLOAD_INSTALL_MSG=Remote Package Install not possible. Would you like to download package and install instead?
|
||||
STR_CHECKING_REMOTE_SERVER_MSG=Checking remote server for Remote Package Install.
|
||||
STR_ENABLE_RPI=RPI
|
||||
STR_ENABLE_RPI_FTP_SMB_MSG=This option enables Remote Package Installation. This requires a HTTP Server setup on the same host sharing the same folder with anonymous access.
|
||||
@@ -120,6 +120,7 @@ STR_SAVE=Save
|
||||
STR_MAX_EDIT_FILE_SIZE_MSG=Cannot edit files bigger than
|
||||
STR_DELETE_LINE=Delete Selected Line
|
||||
STR_INSERT_LINE=Insert Below Selected Line
|
||||
STR_MODIFIED=Modified
|
||||
STR_FAIL_GET_TOKEN_MSG=Failed to obtain an access token from
|
||||
STR_GET_TOKEN_SUCCESS_MSG=Login Success. You may close the browser and return to the application
|
||||
STR_PERM_DRIVE=See, edit, create, and delete all of your Google Drive files
|
||||
@@ -133,4 +134,8 @@ STR_NEW_FILE=New File
|
||||
STR_SETTINGS=Settings
|
||||
STR_CLIENT_ID=Client ID
|
||||
STR_CLIENT_SECRET=Client Secret
|
||||
STR_GLOBAL=Global
|
||||
STR_GLOBAL=Global
|
||||
STR_GOOGLE=Google
|
||||
STR_COPY_LINE=Copy selected line
|
||||
STR_PASTE_LINE=Paste into selected line
|
||||
STR_SHOW_HIDDEN_FILES=Show hidden files
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#define ICON_OF_SQUARE "\xee\x83\x8b" // U+E0CB
|
||||
#define ICON_OF_TRIANGLE "\xee\x83\x9e" // U+E0CB
|
||||
+10
-4
@@ -12,6 +12,7 @@
|
||||
#include "clients/nginx.h"
|
||||
#include "clients/npxserve.h"
|
||||
#include "clients/iis.h"
|
||||
#include "clients/sftpclient.h"
|
||||
#include "common.h"
|
||||
#include "fs.h"
|
||||
#include "config.h"
|
||||
@@ -820,7 +821,7 @@ namespace Actions
|
||||
|
||||
void *ExtractZipThread(void *argp)
|
||||
{
|
||||
FS::MkDirs(extract_zip_folder, true);
|
||||
FS::MkDirs(extract_zip_folder);
|
||||
std::vector<DirEntry> files;
|
||||
if (multi_selected_local_files.size() > 0)
|
||||
std::copy(multi_selected_local_files.begin(), multi_selected_local_files.end(), std::back_inserter(files));
|
||||
@@ -1141,6 +1142,10 @@ namespace Actions
|
||||
client->SetCallbackXferFunction(FtpCallback);
|
||||
remoteclient = client;
|
||||
}
|
||||
else if (strncmp(remote_settings->server, "sftp://", 7) == 0)
|
||||
{
|
||||
remoteclient = new SFTPClient();
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(status_message, "%s", lang_strings[STR_PROTOCOL_NOT_SUPPORTED]);
|
||||
@@ -1180,6 +1185,7 @@ namespace Actions
|
||||
remote_files.clear();
|
||||
sprintf(remote_directory, "%s", "/");
|
||||
sprintf(status_message, "%s", "");
|
||||
delete remoteclient;
|
||||
remoteclient = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -1269,7 +1275,7 @@ namespace Actions
|
||||
{
|
||||
int err;
|
||||
std::vector<DirEntry> entries = FS::ListDir(src.path, &err);
|
||||
FS::MkDirs(dest, true);
|
||||
FS::MkDirs(dest);
|
||||
for (int i = 0; i < entries.size(); i++)
|
||||
{
|
||||
if (stop_activity)
|
||||
@@ -1284,7 +1290,7 @@ namespace Actions
|
||||
if (strcmp(entries[i].name, "..") == 0)
|
||||
continue;
|
||||
|
||||
FS::MkDirs(new_path, true);
|
||||
FS::MkDirs(new_path);
|
||||
ret = CopyOrMove(entries[i], new_path, isCopy);
|
||||
if (ret <= 0)
|
||||
{
|
||||
@@ -1533,7 +1539,7 @@ namespace Actions
|
||||
if (strcmp(entries[i].name, "..") == 0)
|
||||
continue;
|
||||
|
||||
FS::MkDirs(new_path, true);
|
||||
remoteclient->Mkdir(new_path);
|
||||
ret = CopyRemotePath(entries[i], new_path);
|
||||
if (ret <= 0)
|
||||
{
|
||||
|
||||
@@ -81,9 +81,10 @@ std::vector<DirEntry> NginxClient::ListDir(const std::string &path)
|
||||
tmp = std::string((const char *)value, value_len);
|
||||
if (tmp.compare("a") == 0)
|
||||
{
|
||||
value = lxb_dom_node_text_content(node, &value_len);
|
||||
value = lxb_dom_element_get_attribute(lxb_dom_interface_element(node), (const lxb_char_t *)"href", 4, &value_len);
|
||||
tmp = std::string((const char *)value, value_len);
|
||||
tmp = Util::Rtrim(tmp, "/");
|
||||
tmp = BaseClient::DecodeUrl(tmp);
|
||||
if (tmp.compare("..") != 0)
|
||||
{
|
||||
sprintf(entry.directory, "%s", path.c_str());
|
||||
|
||||
@@ -0,0 +1,587 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <time.h>
|
||||
#include "common.h"
|
||||
#include "clients/remote_client.h"
|
||||
#include "clients/sftpclient.h"
|
||||
#include "fs.h"
|
||||
#include "lang.h"
|
||||
#include "util.h"
|
||||
#include "windows.h"
|
||||
#include "system.h"
|
||||
|
||||
#define FTP_CLIENT_BUFSIZ 1048576
|
||||
|
||||
SFTPClient::SFTPClient()
|
||||
{
|
||||
session = nullptr;
|
||||
sftp_session = nullptr;
|
||||
sock = 0;
|
||||
};
|
||||
|
||||
SFTPClient::~SFTPClient(){};
|
||||
|
||||
int SFTPClient::Connect(const std::string &url, const std::string &username, const std::string &password)
|
||||
{
|
||||
int port = 22;
|
||||
std::string host = url.substr(7);
|
||||
size_t colon_pos = host.find(":");
|
||||
if (colon_pos != std::string::npos)
|
||||
{
|
||||
port = std::atoi(host.substr(colon_pos + 1).c_str());
|
||||
host = host.substr(0, colon_pos);
|
||||
}
|
||||
|
||||
struct hostent *he;
|
||||
struct in_addr **addr_list;
|
||||
char ip[20];
|
||||
int i;
|
||||
|
||||
if (strcmp(host.c_str(), "localhost") == 0)
|
||||
{
|
||||
sprintf(ip, "%s", "127.0.0.1");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((he = gethostbyname(host.c_str())) == NULL)
|
||||
{
|
||||
sprintf(this->response, "%s", lang_strings[STR_COULD_NOT_RESOLVE_HOST]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
addr_list = (struct in_addr **)he->h_addr_list;
|
||||
for (i = 0; addr_list[i] != NULL; i++)
|
||||
{
|
||||
strcpy(ip, inet_ntoa(*addr_list[i]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
in_addr dst_addr;
|
||||
sockaddr_in server_addr;
|
||||
int on = 1;
|
||||
int32_t retval;
|
||||
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
inet_pton(AF_INET, ip, (void *)&dst_addr);
|
||||
server_addr.sin_addr = dst_addr;
|
||||
server_addr.sin_port = htons(port);
|
||||
server_addr.sin_family = AF_INET;
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
retval = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof(on));
|
||||
int const size = FTP_CLIENT_BUFSIZ;
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) == -1)
|
||||
{
|
||||
close(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) == -1)
|
||||
{
|
||||
close(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (connect(sock, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr_in)) != 0)
|
||||
{
|
||||
sprintf(this->response, "%s", "Failed to connect!");
|
||||
return 0;
|
||||
}
|
||||
/* Create a session instance
|
||||
*/
|
||||
session = libssh2_session_init();
|
||||
libssh2_session_set_blocking(session, 1);
|
||||
libssh2_keepalive_config(session, 1, 5);
|
||||
|
||||
if (!session)
|
||||
{
|
||||
sprintf(this->response, "%s", lang_strings[STR_FAILED]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ... start it up. This will trade welcome banners, exchange keys,
|
||||
* and setup crypto, compression, and MAC layers
|
||||
*/
|
||||
sceKernelUsleep(100000);
|
||||
int rc = libssh2_session_handshake(session, sock);
|
||||
if (rc)
|
||||
{
|
||||
sprintf(this->response, "Failed SSL handshake %d", rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* At this point we havn't yet authenticated. The first thing to do
|
||||
* is check the hostkey's fingerprint against our known hosts Your app
|
||||
* may have it hard coded, may go to a file, may present it to the
|
||||
* user, that's your call
|
||||
*/
|
||||
const char *fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
|
||||
|
||||
/* check what authentication methods are available */
|
||||
char *userauthlist = libssh2_userauth_list(session, username.c_str(), username.length());
|
||||
|
||||
int auth_pw = 0;
|
||||
if (strstr(userauthlist, "password") != NULL)
|
||||
{
|
||||
auth_pw |= 1;
|
||||
}
|
||||
if (strstr(userauthlist, "keyboard-interactive") != NULL)
|
||||
{
|
||||
auth_pw |= 2;
|
||||
}
|
||||
if (strstr(userauthlist, "publickey") != NULL)
|
||||
{
|
||||
auth_pw |= 4;
|
||||
}
|
||||
|
||||
bool use_identity = password.find("file://") != std::string::npos;
|
||||
if (auth_pw & 1 && !use_identity)
|
||||
{
|
||||
/* We could authenticate via password */
|
||||
if (libssh2_userauth_password(session, username.c_str(), password.c_str()))
|
||||
{
|
||||
sprintf(this->response, "%s", "Authentication by password failed!");
|
||||
goto shutdown;
|
||||
}
|
||||
}
|
||||
else if (auth_pw & 4 && use_identity)
|
||||
{
|
||||
/* Or by public key */
|
||||
std::string publickey = password.substr(7) + "/id_rsa.pub";
|
||||
std::string privatekey = password.substr(7) + "/id_rsa";
|
||||
if (!FS::FileExists(publickey.c_str()))
|
||||
{
|
||||
sprintf(response, "SSH public key %s is not found", publickey.c_str());
|
||||
goto shutdown;
|
||||
}
|
||||
if (!FS::FileExists(privatekey.c_str()))
|
||||
{
|
||||
sprintf(response, "SSH private key %s is not found", privatekey.c_str());
|
||||
goto shutdown;
|
||||
}
|
||||
if (libssh2_userauth_publickey_fromfile(session, username.c_str(), publickey.c_str(), privatekey.c_str(), ""))
|
||||
{
|
||||
sprintf(this->response, "%s", "Authentication by public key failed!");
|
||||
goto shutdown;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(this->response, "%s", "No supported authentication methods found!");
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
sftp_session = libssh2_sftp_init(session);
|
||||
this->connected = true;
|
||||
return 1;
|
||||
|
||||
shutdown:
|
||||
libssh2_session_disconnect(session, "Normal Shutdown");
|
||||
libssh2_session_free(session);
|
||||
close(sock);
|
||||
libssh2_exit();
|
||||
session = nullptr;
|
||||
sock = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SFTPClient::Mkdir(const std::string &path)
|
||||
{
|
||||
int rc = libssh2_sftp_mkdir(sftp_session, path.c_str(),
|
||||
LIBSSH2_SFTP_S_IRWXU | LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IXGRP | LIBSSH2_SFTP_S_IROTH | LIBSSH2_SFTP_S_IXOTH);
|
||||
if (rc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int SFTPClient::Rmdir(const std::string &path, bool recursive)
|
||||
{
|
||||
if (stop_activity)
|
||||
return 1;
|
||||
|
||||
std::vector<DirEntry> list = ListDir(path);
|
||||
int ret;
|
||||
for (int i = 0; i < list.size(); i++)
|
||||
{
|
||||
if (stop_activity)
|
||||
return 1;
|
||||
|
||||
if (list[i].isDir && recursive)
|
||||
{
|
||||
if (strcmp(list[i].name, "..") == 0)
|
||||
continue;
|
||||
ret = Rmdir(list[i].path, recursive);
|
||||
if (ret == 0)
|
||||
{
|
||||
sprintf(status_message, "%s %s", lang_strings[STR_FAIL_DEL_DIR_MSG], list[i].path);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(activity_message, "%s %s\n", lang_strings[STR_DELETING], list[i].path);
|
||||
ret = Delete(list[i].path);
|
||||
if (ret == 0)
|
||||
{
|
||||
sprintf(status_message, "%s %s", lang_strings[STR_FAIL_DEL_FILE_MSG], list[i].path);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = Rmdir(path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int SFTPClient::Rmdir(const std::string &path)
|
||||
{
|
||||
int rc = libssh2_sftp_rmdir(sftp_session, path.c_str());
|
||||
if (rc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int SFTPClient::Size(const std::string &path, int64_t *size)
|
||||
{
|
||||
LIBSSH2_SFTP_ATTRIBUTES attrs;
|
||||
int rc = libssh2_sftp_stat(sftp_session, path.c_str(), &attrs);
|
||||
if (rc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
*size = attrs.filesize;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int SFTPClient::Get(const std::string &outputfile, const std::string &path, uint64_t offset)
|
||||
{
|
||||
if (!Size(path, &bytes_to_download))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIBSSH2_SFTP_HANDLE *sftp_handle = libssh2_sftp_open(sftp_session, path.c_str(), LIBSSH2_FXF_READ, 0);
|
||||
if (!sftp_handle)
|
||||
{
|
||||
sprintf(response, "Unable to open file with SFTP: %ld", libssh2_sftp_last_error(sftp_session));
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE *out = FS::Create(outputfile);
|
||||
if (out == NULL)
|
||||
{
|
||||
sprintf(response, "%s", lang_strings[STR_FAILED]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *buff = (char *)malloc(FTP_CLIENT_BUFSIZ);
|
||||
int rc, count = 0;
|
||||
bytes_transfered = 0;
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_read(sftp_handle, buff, FTP_CLIENT_BUFSIZ);
|
||||
if (rc > 0)
|
||||
{
|
||||
bytes_transfered += rc;
|
||||
FS::Write(out, buff, rc);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
FS::Close(out);
|
||||
libssh2_sftp_close(sftp_handle);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int SFTPClient::Put(const std::string &inputfile, const std::string &path, uint64_t offset)
|
||||
{
|
||||
char *ptr, *buff;
|
||||
int rc;
|
||||
|
||||
bytes_to_download = FS::GetSize(inputfile);
|
||||
if (bytes_to_download < 0)
|
||||
{
|
||||
sprintf(response, "%s", lang_strings[STR_FAILED]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE *in = FS::OpenRead(inputfile);
|
||||
if (in == NULL)
|
||||
{
|
||||
sprintf(response, "%s", lang_strings[STR_FAILED]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIBSSH2_SFTP_HANDLE *sftp_handle = libssh2_sftp_open(sftp_session, path.c_str(),
|
||||
LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC,
|
||||
LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR |
|
||||
LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH);
|
||||
|
||||
if (!sftp_handle)
|
||||
{
|
||||
sprintf(response, "%s", "Unable to open file with SFTP");
|
||||
return 0;
|
||||
}
|
||||
|
||||
buff = (char *)malloc(FTP_CLIENT_BUFSIZ);
|
||||
int nread, count = 0;
|
||||
bytes_transfered = 0;
|
||||
do
|
||||
{
|
||||
nread = FS::Read(in, buff, FTP_CLIENT_BUFSIZ);
|
||||
if (nread <= 0)
|
||||
{
|
||||
/* end of file */
|
||||
break;
|
||||
}
|
||||
ptr = buff;
|
||||
|
||||
do
|
||||
{
|
||||
/* write data in a loop until we block */
|
||||
rc = libssh2_sftp_write(sftp_handle, ptr, nread);
|
||||
|
||||
if (rc < 0)
|
||||
break;
|
||||
ptr += rc;
|
||||
nread -= rc;
|
||||
bytes_transfered += rc;
|
||||
} while (nread);
|
||||
} while (rc > 0);
|
||||
|
||||
libssh2_sftp_close(sftp_handle);
|
||||
FS::Close(in);
|
||||
free(buff);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int SFTPClient::Rename(const std::string &src, const std::string &dst)
|
||||
{
|
||||
int rc = libssh2_sftp_rename_ex(sftp_session, src.c_str(), src.length(),
|
||||
dst.c_str(), dst.length(), LIBSSH2_SFTP_RENAME_ATOMIC | LIBSSH2_SFTP_RENAME_NATIVE);
|
||||
if (rc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int SFTPClient::Delete(const std::string &path)
|
||||
{
|
||||
int rc = libssh2_sftp_unlink(sftp_session, path.c_str());
|
||||
if (rc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int SFTPClient::Copy(const std::string &from, const std::string &to)
|
||||
{
|
||||
sprintf(this->response, "%s", lang_strings[STR_UNSUPPORTED_OPERATION_MSG]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SFTPClient::Move(const std::string &from, const std::string &to)
|
||||
{
|
||||
sprintf(this->response, "%s", lang_strings[STR_UNSUPPORTED_OPERATION_MSG]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SFTPClient::Head(const std::string &path, void *buffer, uint64_t len)
|
||||
{
|
||||
if (!Size(path.c_str(), &bytes_to_download))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIBSSH2_SFTP_HANDLE *sftp_handle = libssh2_sftp_open(sftp_session, path.c_str(), LIBSSH2_FXF_READ, 0);
|
||||
if (!sftp_handle)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = libssh2_sftp_read(sftp_handle, (char *)buffer, len);
|
||||
libssh2_sftp_close(sftp_handle);
|
||||
if (count != len)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool SFTPClient::FileExists(const std::string &path)
|
||||
{
|
||||
int64_t file_size;
|
||||
return Size(path, &file_size);
|
||||
}
|
||||
|
||||
std::vector<DirEntry> SFTPClient::ListDir(const std::string &path)
|
||||
{
|
||||
std::vector<DirEntry> out;
|
||||
DirEntry entry;
|
||||
Util::SetupPreviousFolder(path, &entry);
|
||||
out.push_back(entry);
|
||||
|
||||
/* Request a dir listing via SFTP */
|
||||
LIBSSH2_SFTP_HANDLE *sftp_handle = libssh2_sftp_opendir(sftp_session, path.c_str());
|
||||
if (!sftp_handle)
|
||||
{
|
||||
return out;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
char mem[512];
|
||||
LIBSSH2_SFTP_ATTRIBUTES attrs;
|
||||
DirEntry entry;
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
entry.selectable = true;
|
||||
|
||||
/* loop until we fail */
|
||||
int rc = libssh2_sftp_readdir(sftp_handle, mem, sizeof(mem), &attrs);
|
||||
if (rc > 0)
|
||||
{
|
||||
std::string new_path = std::string(mem, rc);
|
||||
if (new_path.compare(".") == 0 || new_path.compare("..") == 0)
|
||||
continue;
|
||||
;
|
||||
|
||||
if (!show_hidden_files && new_path[0] == '.')
|
||||
continue;
|
||||
|
||||
sprintf(entry.name, "%s", new_path.c_str());
|
||||
sprintf(entry.directory, "%s", path.c_str());
|
||||
if (path.length() > 0 && path[path.length() - 1] == '/')
|
||||
{
|
||||
sprintf(entry.path, "%s%s", path.c_str(), entry.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(entry.path, "%s/%s", path.c_str(), entry.name);
|
||||
}
|
||||
|
||||
if (LIBSSH2_SFTP_S_ISDIR(attrs.permissions))
|
||||
{
|
||||
entry.isDir = true;
|
||||
sprintf(entry.display_size, "%s", lang_strings[STR_FOLDER]);
|
||||
}
|
||||
else if (LIBSSH2_SFTP_S_ISREG(attrs.permissions))
|
||||
{
|
||||
entry.file_size = attrs.filesize;
|
||||
DirEntry::SetDisplaySize(&entry);
|
||||
entry.isDir = false;
|
||||
}
|
||||
else if (LIBSSH2_SFTP_S_ISLNK(attrs.permissions))
|
||||
{
|
||||
entry.file_size = 0;
|
||||
sprintf(entry.display_size, "%s", lang_strings[STR_LINK]);
|
||||
entry.isDir = false;
|
||||
entry.isLink = true;
|
||||
entry.selectable = false;
|
||||
}
|
||||
else // skip any files that aren't regular files/directory
|
||||
{
|
||||
entry.file_size = attrs.filesize;
|
||||
DirEntry::SetDisplaySize(&entry);
|
||||
entry.isDir = false;
|
||||
entry.selectable = false;
|
||||
}
|
||||
|
||||
struct tm tm = *localtime((const time_t *)&attrs.mtime);
|
||||
OrbisDateTime gmt;
|
||||
OrbisDateTime lt;
|
||||
|
||||
gmt.day = tm.tm_mday;
|
||||
gmt.month = tm.tm_mon + 1;
|
||||
gmt.year = tm.tm_year + 1900;
|
||||
gmt.hour = tm.tm_hour;
|
||||
gmt.minute = tm.tm_min;
|
||||
gmt.second = tm.tm_sec;
|
||||
|
||||
convertUtcToLocalTime(&gmt, <);
|
||||
|
||||
entry.modified.day = lt.day;
|
||||
entry.modified.month = lt.month;
|
||||
entry.modified.year = lt.year;
|
||||
entry.modified.hours = lt.hour;
|
||||
entry.modified.minutes = lt.minute;
|
||||
entry.modified.seconds = lt.second;
|
||||
|
||||
out.push_back(entry);
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
} while (1);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string SFTPClient::GetPath(std::string ppath1, std::string ppath2)
|
||||
{
|
||||
std::string path1 = ppath1;
|
||||
std::string path2 = ppath2;
|
||||
path1 = Util::Rtrim(Util::Trim(path1, " "), "/");
|
||||
path2 = Util::Rtrim(Util::Trim(path2, " "), "/");
|
||||
path1 = path1 + "/" + path2;
|
||||
return path1;
|
||||
}
|
||||
|
||||
bool SFTPClient::IsConnected()
|
||||
{
|
||||
return this->connected;
|
||||
}
|
||||
|
||||
bool SFTPClient::Ping()
|
||||
{
|
||||
LIBSSH2_SFTP_ATTRIBUTES attrs;
|
||||
int rc = libssh2_sftp_stat(sftp_session, "/", &attrs);
|
||||
if (rc)
|
||||
{
|
||||
this->connected = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *SFTPClient::LastResponse()
|
||||
{
|
||||
return this->response;
|
||||
}
|
||||
|
||||
int SFTPClient::Quit()
|
||||
{
|
||||
if (sftp_session != nullptr)
|
||||
libssh2_sftp_shutdown(sftp_session);
|
||||
if (session != nullptr)
|
||||
{
|
||||
libssh2_session_disconnect(session, "Normal Shutdown");
|
||||
libssh2_session_free(session);
|
||||
close(sock);
|
||||
libssh2_exit();
|
||||
}
|
||||
session = nullptr;
|
||||
sftp_session = nullptr;
|
||||
sock = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ClientType SFTPClient::clientType()
|
||||
{
|
||||
return CLIENT_TYPE_FTP;
|
||||
}
|
||||
|
||||
uint32_t SFTPClient::SupportedActions()
|
||||
{
|
||||
return REMOTE_ACTION_ALL ^ REMOTE_ACTION_CUT ^ REMOTE_ACTION_COPY ^ REMOTE_ACTION_PASTE;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef SFTPCLIENT_H
|
||||
#define SFTPCLIENT_H
|
||||
|
||||
#include <libssh2.h>
|
||||
#include <libssh2_sftp.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "clients/remote_client.h"
|
||||
#include "common.h"
|
||||
|
||||
class SFTPClient : public RemoteClient
|
||||
{
|
||||
public:
|
||||
SFTPClient();
|
||||
~SFTPClient();
|
||||
int Connect(const std::string &url, const std::string &username, const std::string &password);
|
||||
int Mkdir(const std::string &path);
|
||||
int Rmdir(const std::string &path, bool recursive);
|
||||
int Rmdir(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 Put(const std::string &inputfile, const std::string &path, uint64_t offset=0);
|
||||
int Rename(const std::string &src, const std::string &dst);
|
||||
int Delete(const std::string &path);
|
||||
int Copy(const std::string &from, const std::string &to);
|
||||
int Move(const std::string &from, const std::string &to);
|
||||
int Head(const std::string &path, void *buffer, uint64_t len);
|
||||
bool FileExists(const std::string &path);
|
||||
std::vector<DirEntry> ListDir(const std::string &path);
|
||||
std::string GetPath(std::string path1, std::string path2);
|
||||
bool IsConnected();
|
||||
bool Ping();
|
||||
const char *LastResponse();
|
||||
int Quit();
|
||||
ClientType clientType();
|
||||
uint32_t SupportedActions();
|
||||
|
||||
protected:
|
||||
LIBSSH2_SESSION *session;
|
||||
LIBSSH2_SFTP *sftp_session;
|
||||
int sock;
|
||||
char response[512];
|
||||
bool connected = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
+8
-3
@@ -34,6 +34,7 @@ char favorite_urls[MAX_FAVORITE_URLS][512];
|
||||
bool auto_delete_tmp_pkg;
|
||||
int max_edit_file_size;
|
||||
GoogleAppInfo gg_app;
|
||||
bool show_hidden_files;
|
||||
|
||||
unsigned char cipher_key[32] = {'s', '5', 'v', '8', 'y', '/', 'B', '?', 'E', '(', 'H', '+', 'M', 'b', 'Q', 'e', 'T', 'h', 'W', 'm', 'Z', 'q', '4', 't', '7', 'w', '9', 'z', '$', 'C', '&', 'F'};
|
||||
unsigned char cipher_iv[16] = {'Y', 'p', '3', 's', '6', 'v', '9', 'y', '$', 'B', '&', 'E', ')', 'H', '@', 'M'};
|
||||
@@ -79,7 +80,7 @@ namespace CONFIG
|
||||
{
|
||||
setting->type = CLIENT_TYPE_SMB;
|
||||
}
|
||||
else if (strncmp(setting->server, "ftp://", 6) == 0)
|
||||
else if (strncmp(setting->server, "ftp://", 6) == 0 || strncmp(setting->server, "sftp://", 7) == 0)
|
||||
{
|
||||
setting->type = CLIENT_TYPE_FTP;
|
||||
}
|
||||
@@ -168,6 +169,9 @@ namespace CONFIG
|
||||
max_edit_file_size = ReadInt(CONFIG_GLOBAL, CONFIG_MAX_EDIT_FILE_SIZE, MAX_EDIT_FILE_SIZE);
|
||||
WriteInt(CONFIG_GLOBAL, CONFIG_MAX_EDIT_FILE_SIZE, max_edit_file_size);
|
||||
|
||||
show_hidden_files = ReadBool(CONFIG_GLOBAL, CONFIG_SHOW_HIDDEN_FILES, false);
|
||||
WriteBool(CONFIG_GLOBAL, CONFIG_SHOW_HIDDEN_FILES, show_hidden_files);
|
||||
|
||||
// Load Google Account Info
|
||||
sprintf(gg_app.client_id, "%s", ReadString(CONFIG_GOOGLE, CONFIG_GOOGLE_CLIENT_ID, ""));
|
||||
WriteString(CONFIG_GOOGLE, CONFIG_GOOGLE_CLIENT_ID, gg_app.client_id);
|
||||
@@ -214,7 +218,7 @@ namespace CONFIG
|
||||
sprintf(setting.username, "%s", ReadString(sites[i].c_str(), CONFIG_REMOTE_SERVER_USER, ""));
|
||||
WriteString(sites[i].c_str(), CONFIG_REMOTE_SERVER_USER, setting.username);
|
||||
|
||||
char tmp_password[64];
|
||||
char tmp_password[128];
|
||||
sprintf(tmp_password, "%s", ReadString(sites[i].c_str(), CONFIG_REMOTE_SERVER_PASSWORD, ""));
|
||||
std::string encrypted_password;
|
||||
if (strlen(tmp_password) > 0)
|
||||
@@ -306,7 +310,7 @@ namespace CONFIG
|
||||
WriteBool(last_site, CONFIG_ENABLE_RPI, remote_settings->enable_rpi);
|
||||
WriteString(last_site, CONFIG_REMOTE_HTTP_SERVER_TYPE, remote_settings->http_server_type);
|
||||
WriteString(CONFIG_GLOBAL, CONFIG_LAST_SITE, last_site);
|
||||
WriteBool(CONFIG_GLOBAL, CONFIG_AUTO_DELETE_TMP_PKG, auto_delete_tmp_pkg);
|
||||
|
||||
std::string encrypted_token;
|
||||
if (strlen(remote_settings->gg_account.access_token) > 0)
|
||||
Encrypt(remote_settings->gg_account.access_token, encrypted_token);
|
||||
@@ -338,6 +342,7 @@ namespace CONFIG
|
||||
WriteString(CONFIG_GOOGLE, CONFIG_GOOGLE_CLIENT_ID, gg_app.client_id);
|
||||
WriteString(CONFIG_GOOGLE, CONFIG_GOOGLE_PERMISSIONS, gg_app.permissions);
|
||||
WriteBool(CONFIG_GLOBAL, CONFIG_AUTO_DELETE_TMP_PKG, auto_delete_tmp_pkg);
|
||||
WriteBool(CONFIG_GLOBAL, CONFIG_SHOW_HIDDEN_FILES, show_hidden_files);
|
||||
WriteIniFile(CONFIG_INI_FILE);
|
||||
CloseIniFile();
|
||||
}
|
||||
|
||||
+4
-1
@@ -17,6 +17,8 @@
|
||||
|
||||
#define CONFIG_GLOBAL "Global"
|
||||
|
||||
#define CONFIG_SHOW_HIDDEN_FILES "show_hidden_files"
|
||||
|
||||
#define CONFIG_GOOGLE "Google"
|
||||
#define CONFIG_GOOGLE_CLIENT_ID "google_client_id"
|
||||
#define CONFIG_GOOGLE_CLIENT_SECRET "google_client_secret"
|
||||
@@ -89,7 +91,7 @@ struct RemoteSettings
|
||||
char site_name[32];
|
||||
char server[256];
|
||||
char username[33];
|
||||
char password[64];
|
||||
char password[128];
|
||||
int http_port;
|
||||
ClientType type;
|
||||
bool enable_rpi;
|
||||
@@ -123,6 +125,7 @@ extern int max_edit_file_size;
|
||||
extern unsigned char cipher_key[32];
|
||||
extern unsigned char cipher_iv[16];
|
||||
extern GoogleAppInfo gg_app;
|
||||
extern bool show_hidden_files;
|
||||
|
||||
namespace CONFIG
|
||||
{
|
||||
|
||||
+1
-1
@@ -152,7 +152,7 @@ namespace FS
|
||||
|
||||
bool LoadText(std::vector<std::string> *lines, const std::string &path)
|
||||
{
|
||||
FILE *fd = fopen(path.c_str(), "r");
|
||||
FILE *fd = fopen(path.c_str(), "rb");
|
||||
if (fd == nullptr)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -1200,7 +1200,7 @@ typedef ImBitArray<ImGuiKey_NamedKey_COUNT, -ImGuiKey_NamedKey_BEGIN> ImBitAr
|
||||
#define ImGuiKey_NavGamepadTweakFast ImGuiKey_GamepadR1
|
||||
#define ImGuiKey_NavGamepadActivate ImGuiKey_GamepadFaceDown
|
||||
#define ImGuiKey_NavGamepadCancel ImGuiKey_GamepadFaceRight
|
||||
#define ImGuiKey_NavGamepadMenu ImGuiKey_GamepadFaceLeft
|
||||
#define ImGuiKey_NavGamepadMenu ImGuiKey_Keypad0
|
||||
#define ImGuiKey_NavGamepadInput ImGuiKey_GamepadFaceUp
|
||||
|
||||
enum ImGuiInputEventType
|
||||
|
||||
+17
-10
@@ -97,7 +97,7 @@ namespace INSTALLER
|
||||
s_bgft_initialized = false;
|
||||
}
|
||||
|
||||
std::string getRemoteUrl(const std::string filename)
|
||||
std::string getRemoteUrl(const std::string filename, bool encodeUrl)
|
||||
{
|
||||
if (remoteclient->clientType() == CLIENT_TYPE_WEBDAV || remoteclient->clientType() == CLIENT_TYPE_HTTP_SERVER)
|
||||
{
|
||||
@@ -109,11 +109,14 @@ namespace INSTALLER
|
||||
std::string host = full_url.substr(0, root_pos);
|
||||
std::string path = full_url.substr(root_pos);
|
||||
|
||||
Web::Urn::Path uri(path);
|
||||
CURL *curl = curl_easy_init();
|
||||
path = uri.quote(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
if (encodeUrl)
|
||||
{
|
||||
Web::Urn::Path uri(path);
|
||||
CURL *curl = curl_easy_init();
|
||||
path = uri.quote(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
return host + path;
|
||||
}
|
||||
else
|
||||
@@ -129,9 +132,12 @@ namespace INSTALLER
|
||||
host = host.substr(0, port_pos);
|
||||
|
||||
std::string path = std::string(filename);
|
||||
Web::Urn::Path uri(path);
|
||||
CURL *curl = curl_easy_init();
|
||||
path = uri.quote(curl);
|
||||
if (encodeUrl)
|
||||
{
|
||||
Web::Urn::Path uri(path);
|
||||
CURL *curl = curl_easy_init();
|
||||
path = uri.quote(curl);
|
||||
}
|
||||
return "http://" + host + ":" + std::to_string(remote_settings->http_port) + path;
|
||||
}
|
||||
|
||||
@@ -161,6 +167,7 @@ namespace INSTALLER
|
||||
tmp_client.Connect(host.c_str(), "", "", false);
|
||||
WebDAV::dict_t response_headers{};
|
||||
int ret = tmp_client.GetHeaders(path.c_str(), &response_headers);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
sprintf(confirm_message, "%s %s", lang_strings[STR_CANNOT_CONNECT_REMOTE_MSG], lang_strings[STR_DOWNLOAD_INSTALL_MSG]);
|
||||
@@ -173,7 +180,7 @@ namespace INSTALLER
|
||||
|
||||
int InstallRemotePkg(const std::string &filename, pkg_header *header)
|
||||
{
|
||||
std::string url = getRemoteUrl(filename);
|
||||
std::string url = getRemoteUrl(filename, true);
|
||||
if (url.empty())
|
||||
return 0;
|
||||
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ namespace INSTALLER
|
||||
void Exit(void);
|
||||
|
||||
bool canInstallRemotePkg(const std::string &url);
|
||||
std::string getRemoteUrl(const std::string filename);
|
||||
std::string getRemoteUrl(const std::string filename, bool encodeUrl=false);
|
||||
int InstallRemotePkg(const std::string &filename, pkg_header *header);
|
||||
int InstallLocalPkg(const std::string &filename, pkg_header *header, bool remove_after_install=false);
|
||||
}
|
||||
+4
-1
@@ -119,7 +119,7 @@ char lang_strings[LANG_STRINGS_NUM][LANG_STR_SIZE] = {
|
||||
"Operation not supported", // STR_UNSUPPORTED_OPERATION_MSG
|
||||
"Http Port", // STR_HTTP_PORT
|
||||
"The content has already been installed. Do you want to continue installing", // STR_REINSTALL_CONFIRM_MSG
|
||||
"Remote server requires at least a username.", // STR_REMOTE_NOT_SUPPORT_MSG
|
||||
"Remote package installation is not supported for protected servers.", // STR_REMOTE_NOT_SUPPORT_MSG
|
||||
"Remote HTTP Server not reachable.", // STR_CANNOT_CONNECT_REMOTE_MSG
|
||||
"Remote Package Install not possible. Would you like to download package and install?", // STR_DOWNLOAD_INSTALL_MSG
|
||||
"Checking remote server for Remote Package Install.", // STR_CHECKING_REMOTE_SERVER_MSG
|
||||
@@ -150,6 +150,9 @@ char lang_strings[LANG_STRINGS_NUM][LANG_STR_SIZE] = {
|
||||
"Client Secret", // STR_CLIENT_SECRET
|
||||
"Global", // STR_GLOBAL
|
||||
"Google", // STR_GOOGLE
|
||||
"Copy selected line", // STR_COPY_LINE
|
||||
"Paste into selected line", // STR_PASTE_LINE
|
||||
"Show hidden files", // STR_SHOW_HIDDEN_FILES
|
||||
};
|
||||
|
||||
bool needs_extended_font = false;
|
||||
|
||||
+5
-2
@@ -141,7 +141,10 @@
|
||||
FUNC(STR_CLIENT_ID) \
|
||||
FUNC(STR_CLIENT_SECRET) \
|
||||
FUNC(STR_GLOBAL) \
|
||||
FUNC(STR_GOOGLE)
|
||||
FUNC(STR_GOOGLE) \
|
||||
FUNC(STR_COPY_LINE) \
|
||||
FUNC(STR_PASTE_LINE) \
|
||||
FUNC(STR_SHOW_HIDDEN_FILES)
|
||||
|
||||
#define GET_VALUE(x) x,
|
||||
#define GET_STRING(x) #x,
|
||||
@@ -151,7 +154,7 @@ enum
|
||||
FOREACH_STR(GET_VALUE)
|
||||
};
|
||||
|
||||
#define LANG_STRINGS_NUM 138
|
||||
#define LANG_STRINGS_NUM 141
|
||||
#define LANG_ID_SIZE 64
|
||||
#define LANG_STR_SIZE 384
|
||||
extern char lang_identifiers[LANG_STRINGS_NUM][LANG_ID_SIZE];
|
||||
|
||||
+9
-3
@@ -99,7 +99,7 @@ void InitImgui()
|
||||
0,
|
||||
};
|
||||
|
||||
static const ImWchar icons[] {
|
||||
static const ImWchar fa_icons[] {
|
||||
0xF07B, 0xF07B, // folder
|
||||
0xF65E, 0xF65E, // new folder
|
||||
0xF15B, 0xF15B, // file
|
||||
@@ -127,6 +127,12 @@ void InitImgui()
|
||||
0,
|
||||
};
|
||||
|
||||
static const ImWchar of_icons[] {
|
||||
0xE0CB, 0xE0CB, // square
|
||||
0xE0DE, 0xE0DE, // triangle
|
||||
0,
|
||||
};
|
||||
|
||||
std::string lang = std::string(language);
|
||||
int32_t lang_idx;
|
||||
sceSystemServiceParamGetInt( ORBIS_SYSTEM_SERVICE_PARAM_ID_LANG, &lang_idx );
|
||||
@@ -177,7 +183,8 @@ void InitImgui()
|
||||
ImFontConfig config;
|
||||
config.MergeMode = true;
|
||||
config.GlyphMinAdvanceX = 13.0f; // Use if you want to make the icon monospaced
|
||||
io.Fonts->AddFontFromFileTTF("/app0/assets/fonts/fa-solid-900.ttf", 20.0f, &config, icons);
|
||||
io.Fonts->AddFontFromFileTTF("/app0/assets/fonts/fa-solid-900.ttf", 20.0f, &config, fa_icons);
|
||||
io.Fonts->AddFontFromFileTTF("/app0/assets/fonts/OpenFontIcons.ttf", 20.0f, &config, of_icons);
|
||||
io.Fonts->Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;
|
||||
io.Fonts->Build();
|
||||
|
||||
@@ -307,7 +314,6 @@ int main()
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(window, renderer);
|
||||
ImGui_ImplSDLRenderer_Init(renderer);
|
||||
ImGui_ImplSDLRenderer_CreateFontsTexture();
|
||||
ImGui_ImplSDL2_DisableButton(SDL_CONTROLLER_BUTTON_X, true);
|
||||
|
||||
if (!initialize_jbc())
|
||||
{
|
||||
|
||||
+34
-9
@@ -13,6 +13,7 @@
|
||||
#include "lang.h"
|
||||
#include "ime_dialog.h"
|
||||
#include "IconsFontAwesome6.h"
|
||||
#include "OpenFontIcons.h"
|
||||
#include "server/http_server.h"
|
||||
#include "clients/gdrive.h"
|
||||
|
||||
@@ -77,6 +78,7 @@ char label[256];
|
||||
bool editor_modified = false;
|
||||
char edit_file[256];
|
||||
int edit_line_to_select = -1;
|
||||
std::string copy_text;
|
||||
|
||||
// Overwrite dialog variables
|
||||
bool dont_prompt_overwrite = false;
|
||||
@@ -393,9 +395,9 @@ namespace Windows
|
||||
{
|
||||
ime_single_field = remote_settings->password;
|
||||
ResetImeCallbacks();
|
||||
ime_field_size = 24;
|
||||
ime_field_size = 127;
|
||||
ime_callback = SingleValueImeCallback;
|
||||
Dialog::initImeDialog(lang_strings[STR_PASSWORD], remote_settings->password, 24, ORBIS_TYPE_BASIC_LATIN, pos.x, pos.y);
|
||||
Dialog::initImeDialog(lang_strings[STR_PASSWORD], remote_settings->password, 127, ORBIS_TYPE_BASIC_LATIN, pos.x, pos.y);
|
||||
gui_mode = GUI_MODE_IME;
|
||||
}
|
||||
|
||||
@@ -801,7 +803,7 @@ namespace Windows
|
||||
ImVec4 *colors = style->Colors;
|
||||
int flags;
|
||||
|
||||
if (ImGui::IsKeyDown(ImGuiKey_GamepadFaceUp))
|
||||
if (ImGui::IsKeyDown(ImGuiKey_GamepadFaceUp) && !paused)
|
||||
{
|
||||
if (!paused)
|
||||
saved_selected_browser = selected_browser;
|
||||
@@ -1057,7 +1059,7 @@ namespace Windows
|
||||
ImGui::Separator();
|
||||
|
||||
flags = getSelectableFlag(REMOTE_ACTION_UPLOAD);
|
||||
if (remote_browser_selected && remoteclient != nullptr && !(remoteclient->SupportedActions() & REMOTE_ACTION_UPLOAD))
|
||||
if (local_browser_selected && remoteclient != nullptr && !(remoteclient->SupportedActions() & REMOTE_ACTION_UPLOAD))
|
||||
{
|
||||
flags = ImGuiSelectableFlags_Disabled;
|
||||
}
|
||||
@@ -1519,6 +1521,15 @@ namespace Windows
|
||||
editor_modified = true;
|
||||
edit_line_to_select = j;
|
||||
}
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_GamepadFaceLeft, false))
|
||||
{
|
||||
copy_text = std::string(it->c_str());
|
||||
}
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_GamepadFaceUp, false))
|
||||
{
|
||||
it->clear();
|
||||
it->append(copy_text);
|
||||
}
|
||||
}
|
||||
j++;
|
||||
}
|
||||
@@ -1534,7 +1545,8 @@ namespace Windows
|
||||
|
||||
ImGui::Text("%s%s", (editor_modified ? "**" : ""), edit_file);
|
||||
ImGui::Separator();
|
||||
ImGui::Text("L1 - %s R1 - %s", lang_strings[STR_DELETE_LINE], lang_strings[STR_INSERT_LINE]);
|
||||
ImGui::Text("L1 - %s R1 - %s %s - %s %s - %s", lang_strings[STR_DELETE_LINE], lang_strings[STR_INSERT_LINE],
|
||||
ICON_OF_SQUARE, lang_strings[STR_COPY_LINE], ICON_OF_TRIANGLE, lang_strings[STR_PASTE_LINE]);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
@@ -1564,6 +1576,12 @@ namespace Windows
|
||||
ImGui::SetCursorPosX(705);
|
||||
ImGui::Checkbox("##auto_delete_tmp_pkg", &auto_delete_tmp_pkg);
|
||||
ImGui::Separator();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 15);
|
||||
ImGui::Text("%s", lang_strings[STR_SHOW_HIDDEN_FILES]);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(705);
|
||||
ImGui::Checkbox("##show_hidden_files", &show_hidden_files);
|
||||
ImGui::Separator();
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s", lang_strings[STR_GOOGLE]);
|
||||
ImGui::Separator();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 15);
|
||||
@@ -1571,7 +1589,9 @@ namespace Windows
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(163);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 1.0f));
|
||||
if (ImGui::Button(gg_app.client_id, ImVec2(580, 0)))
|
||||
char id[192];
|
||||
sprintf(id, "%s##client_id_input", gg_app.client_id);
|
||||
if (ImGui::Button(id, ImVec2(580, 0)))
|
||||
{
|
||||
ResetImeCallbacks();
|
||||
ime_single_field = gg_app.client_id;
|
||||
@@ -1584,7 +1604,6 @@ namespace Windows
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 15);
|
||||
ImGui::Text("%s", lang_strings[STR_CLIENT_SECRET]);
|
||||
ImGui::SameLine();
|
||||
char id[128];
|
||||
ImGui::SetCursorPosX(163);
|
||||
if (strlen(gg_app.client_secret) > 0)
|
||||
sprintf(id, "%s", "*********************************************##client_secret_input");
|
||||
@@ -1679,8 +1698,8 @@ namespace Windows
|
||||
ime_after_update = AfterFolderNameCallback;
|
||||
ime_cancelled = CancelActionCallBack;
|
||||
ime_callback = SingleValueImeCallback;
|
||||
ImVec2 pos = selected_action == ACTION_NEW_LOCAL_FOLDER ? ImVec2(410, 350) : ImVec2(1330, 350);
|
||||
Dialog::initImeDialog(lang_strings[STR_NEW_FOLDER], dialog_editor_text, 128, ORBIS_TYPE_BASIC_LATIN, pos.x, pos.y);
|
||||
ImVec2 pos = (selected_action == ACTION_NEW_LOCAL_FOLDER || selected_action == ACTION_NEW_LOCAL_FILE) ? ImVec2(410, 350) : ImVec2(1330, 350);
|
||||
Dialog::initImeDialog((selected_action == ACTION_NEW_LOCAL_FILE || selected_action == ACTION_NEW_REMOTE_FILE)? lang_strings[STR_NEW_FILE]: lang_strings[STR_NEW_FOLDER], dialog_editor_text, 128, ORBIS_TYPE_BASIC_LATIN, pos.x, pos.y);
|
||||
gui_mode = GUI_MODE_IME;
|
||||
}
|
||||
break;
|
||||
@@ -1703,6 +1722,7 @@ namespace Windows
|
||||
if (dont_prompt_overwrite || (!dont_prompt_overwrite && confirm_transfer_state == 1))
|
||||
{
|
||||
activity_inprogess = true;
|
||||
sprintf(activity_message, "%s", "");
|
||||
stop_activity = false;
|
||||
Actions::UploadFiles();
|
||||
confirm_transfer_state = -1;
|
||||
@@ -1714,6 +1734,7 @@ namespace Windows
|
||||
if (dont_prompt_overwrite || (!dont_prompt_overwrite && confirm_transfer_state == 1))
|
||||
{
|
||||
activity_inprogess = true;
|
||||
sprintf(activity_message, "%s", "");
|
||||
stop_activity = false;
|
||||
Actions::DownloadFiles();
|
||||
confirm_transfer_state = -1;
|
||||
@@ -1723,6 +1744,7 @@ namespace Windows
|
||||
case ACTION_EXTRACT_LOCAL_ZIP:
|
||||
sprintf(status_message, "%s", "");
|
||||
activity_inprogess = true;
|
||||
sprintf(activity_message, "%s", "");
|
||||
stop_activity = false;
|
||||
file_transfering = true;
|
||||
selected_action = ACTION_NONE;
|
||||
@@ -1731,6 +1753,7 @@ namespace Windows
|
||||
case ACTION_CREATE_LOCAL_ZIP:
|
||||
sprintf(status_message, "%s", "");
|
||||
activity_inprogess = true;
|
||||
sprintf(activity_message, "%s", "");
|
||||
stop_activity = false;
|
||||
file_transfering = true;
|
||||
selected_action = ACTION_NONE;
|
||||
@@ -1858,6 +1881,7 @@ namespace Windows
|
||||
if (dont_prompt_overwrite || (!dont_prompt_overwrite && confirm_transfer_state == 1))
|
||||
{
|
||||
activity_inprogess = true;
|
||||
sprintf(activity_message, "%s", "");
|
||||
stop_activity = false;
|
||||
confirm_transfer_state = -1;
|
||||
if (paste_action == ACTION_LOCAL_CUT)
|
||||
@@ -1877,6 +1901,7 @@ namespace Windows
|
||||
if (dont_prompt_overwrite || (!dont_prompt_overwrite && confirm_transfer_state == 1))
|
||||
{
|
||||
activity_inprogess = true;
|
||||
sprintf(activity_message, "%s", "");
|
||||
stop_activity = false;
|
||||
confirm_transfer_state = -1;
|
||||
if (paste_action == ACTION_REMOTE_CUT)
|
||||
|
||||
Reference in New Issue
Block a user