Compare commits

..

88 Commits

Author SHA1 Message Date
cy33hc 28c046d5a5 disable debug 2026-05-31 11:26:09 -07:00
cy33hc 99099a6b82 update readme 2026-05-31 10:09:58 -07:00
cy33hc c30a8ab70e update message 2026-05-31 02:30:43 -07:00
cy33hc 046083ccf6 bump version 2026-05-31 02:25:54 -07:00
cy33hc 6d36073413 handle proper loading of ezremote server payload 2026-05-31 02:24:31 -07:00
cy33hc b831dd7db4 update code to handle background install and download of file from file hosts 2026-05-30 21:34:56 -07:00
cy33hc 3ba286a016 add ability to use R2 button to refresh files 2026-05-30 19:34:11 -07:00
cy33hc 7395056a75 add bg download function 2026-05-30 13:49:31 -07:00
cy33hc 81a5143f2f first working bg install 2026-05-30 04:08:00 -07:00
cy33hc 432c0020be commit what I have so far 2026-05-19 23:07:59 -07:00
cy33hc fd1b9e5d96 add windows changes 2026-05-18 20:08:44 -07:00
cy33hc a3741a0a6c initial bg install 2026-05-18 20:03:50 -07:00
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
Chee Yee b7fe46cb94 support archive.org protected urls 2025-01-11 21:25:55 -08:00
Chee Yee 3bcf136d2a support archive.org protected urls 2025-01-10 21:06:19 -08:00
Chee Yee e5d5ddb8bc support for servers that does not permit NOOP 2025-01-10 21:05:16 -08:00
Chee Yee 3e4b5e3ea2 update to handle Norwegian lang 2024-11-08 22:55:32 -08:00
cy33hc c4889ec160 Merge pull request #32 from xdpirate/master
Add Norwegian translation
2024-11-08 22:33:05 -08:00
xdpirate 3076f4b179 Add Norwegian translation 2024-11-08 11:24:14 +01:00
Chee Yee 93fb338ce0 failed install to display server error message 2024-11-08 00:38:31 -08:00
Chee Yee 785a073bce fixed issue parse apache2 httpd directory listing using docker image 2024-10-26 15:31:01 -07:00
Chee Yee a71bd1eb28 enchancements to install from url 2024-10-08 20:50:42 -07:00
Chee Yee 0a1420dd96 add support for myrient.erista.me website 2024-07-20 02:45:52 -07:00
Chee Yee dcdc1fc54b use spaces for separator, seems less overcrowded 2024-07-16 21:27:12 -07:00
Chee Yee 5c254589e6 Merge branch 'master' of github.com:cy33hc/ps4-ezremote-client 2024-07-16 20:59:32 -07:00
Chee Yee 2b25272379 display server url in the sites drop down to easily identify site 2024-07-16 20:59:23 -07:00
cy33hc 6b66d39eba Update README.md 2024-07-16 10:42:18 -07:00
Chee Yee b143c3e3c5 add archive org screenshot 2024-07-15 19:15:25 -07:00
Chee Yee c3cc6d5954 add screenshots for archive org retrieve download URL 2024-07-15 15:02:55 -07:00
Chee Yee cfcb420b9d add support for archive.org 2024-07-14 01:54:54 -07:00
Chee Yee 294455c735 add ability to use L2 button to navigate back to previous directory 2024-07-13 19:40:20 -07:00
Chee Yee 4ff119b34d fix crash on failed install split pkg 2024-07-03 09:12:49 -07:00
Chee Yee 114c1974e1 enable disk caching for all remote types 2024-06-28 02:26:03 -07:00
Chee Yee a99bf163d8 implement download to split_file for all remote clients 2024-06-28 00:35:02 -07:00
Chee Yee b9ab71577f fix crash installing from real-debrid webdav 2024-06-27 22:04:06 -07:00
Chee Yee f80b3112c6 increase archive read buffer size to improve extraction speed 2024-06-27 11:11:33 -07:00
Chee Yee e0c72c85e5 fix crash while error reading from archive 2024-06-27 11:10:56 -07:00
Chee Yee 01f568feac update javascript 2024-06-27 00:55:57 -07:00
Chee Yee 2046a0e096 add disk cache option for installing from WebUi 2024-06-26 21:33:47 -07:00
Chee Yee 49f535dfde set the proper start time when overwrite prompt 2024-06-16 21:19:02 -07:00
Chee Yee 5a250a4182 display transfer speed 2024-06-13 23:44:19 -07:00
Chee Yee 89fa9aba69 fix install for large patch pkg 2024-06-13 18:38:18 -07:00
Chee Yee 0feff205d2 fix extracting from webdav 2024-04-29 00:13:07 -07:00
Chee Yee 5df5447eb0 improve speed of install pkg in zip on smb, nfs and sftp 2024-04-28 23:59:01 -07:00
Chee Yee b87dc91c6f fix install from url 2024-04-28 23:19:59 -07:00
Chee Yee 14b32504e0 more improvements to install speed for nfs, smb ans sftp 2024-04-28 18:11:22 -07:00
Chee Yee d35345a270 fix install from gdrive after refactor 2024-04-28 14:16:08 -07:00
Chee Yee f041172768 improve speed of installing from smb, nfs, sftp 2024-04-28 13:49:06 -07:00
Chee Yee 232a6233ca Merge branch 'master' of github.com:cy33hc/ps4-ezremote-client 2024-04-21 21:16:19 -07:00
Chee Yee c620478691 fix extraction of 7zip 2024-04-21 21:16:12 -07:00
cy33hc 2615372288 Update README.md 2024-04-04 01:21:34 -07:00
cy33hc 0889b0923d Update README.md 2024-04-04 01:21:03 -07:00
cy33hc a2b26102de Update README.md 2024-04-04 01:20:38 -07:00
cy33hc dff469a9d7 Update README.md 2024-04-04 01:20:16 -07:00
cy33hc 37074f0102 Update README.md 2024-04-04 01:19:56 -07:00
Chee Yee eb4184d488 fixed RPI for all remotes which was broken since 1.18 2024-03-17 15:26:26 -07:00
Chee Yee c328b26480 fix google login 2024-02-26 00:25:41 -08:00
Chee Yee 1e702aa0e2 refactor webdav client 2024-02-22 20:30:18 -08:00
Chee Yee d35b311946 make settings dialog bigger to fix content 2024-02-17 18:40:57 -08:00
Chee Yee f02944e596 dont need WebUI install dialog to wait 2024-02-16 19:26:16 -08:00
Chee Yee 338eacfd3f more places where modal mode needs to be disabled 2024-02-16 14:08:45 -08:00
Chee Yee 0bd0a0f273 disable modal dialog mode 2024-02-16 02:19:45 -08:00
Chee Yee bf3f4330c0 add progress meter for file extraction and pkg install 2024-02-16 02:11:05 -08:00
Chee Yee f16850fed9 more bug fixes for install patch/dlc from remote 2024-02-16 01:19:27 -08:00
cy33hc a30a20f6db Update README.md 2024-02-15 21:47:57 -08:00
cy33hc 54b9a80410 Update README.md 2024-02-15 10:53:22 -08:00
Chee Yee fe55ddeb8a fix install of some patch/dlc pkg 2024-02-15 04:01:42 -08:00
Chee Yee a5a5f8d611 fix 2024-02-12 16:03:21 -08:00
Chee Yee 9854437c94 add support for realdebrid 2024-02-10 23:57:52 -08:00
Chee Yee 7a908ebf1b improve speed of installing from compress file by increase read buffer 2024-02-10 00:27:18 -08:00
Chee Yee f83629d107 add ability to install pkg in compress file on public shares like mediafire, gdrive, pixeldrain and alldebrid 2024-02-09 21:31:59 -08:00
Chee Yee f0c0213940 fix file list navigration jumping to top of list when last item is selected 2024-02-07 23:54:25 -08:00
Chee Yee 9baf6c18fa enable extracting compress files from http servers 2024-02-07 07:38:54 -08:00
Chee Yee 263822ef66 update to install all pkgs inside compress file 2024-02-07 02:28:44 -08:00
Chee Yee 487e288635 fix rclone filesize display 2024-02-06 23:40:17 -08:00
107 changed files with 5994 additions and 3941 deletions
+1
View File
@@ -11,3 +11,4 @@ CTestTestfile.cmake
_deps
.vscode
build
data/daemon
+3
View File
@@ -0,0 +1,3 @@
[submodule "ps4-ezremote-server"]
path = ps4-ezremote-server
url = git@github.com:cy33hc/ps4-ezremote-server.git
+19 -13
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.5)
project(ezremote_client)
@@ -22,26 +22,24 @@ add_executable(ezremote_client
source/imgui/imgui_widgets.cpp
source/imgui/imgui.cpp
source/pugixml/pugixml.cpp
source/web/callback.cpp
source/web/fsinfo.cpp
source/web/header.cpp
source/web/request.cpp
source/web/urn.cpp
source/webdav/client.cpp
source/http/httplib.cpp
source/clients/baseclient.cpp
source/clients/apache.cpp
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
source/clients/npxserve.cpp
source/clients/nfsclient.cpp
source/clients/smbclient.cpp
source/clients/webdavclient.cpp
source/clients/sftpclient.cpp
source/clients/rclone.cpp
source/clients/webdav.cpp
source/filehost/alldebrid.cpp
source/filehost/realdebrid.cpp
source/filehost/directhost.cpp
source/filehost/gdrive.cpp
source/filehost/filehost.cpp
@@ -67,13 +65,11 @@ add_executable(ezremote_client
source/windows.cpp
source/zip_util.cpp
source/split_file.cpp
source/mem_file.cpp
)
add_self(ezremote_client)
add_pkg(ezremote_client ${CMAKE_SOURCE_DIR}/data "RMTC00001" "ezRemote Client" "01.14" 32 0)
target_link_libraries(ezremote_client
dbglogger
c
c++
png
@@ -111,9 +107,19 @@ target_link_libraries(ezremote_client
SceUserService
ScePad
SceAudioOut
SceSysUtil
SceImeDialog
SceNet
SceBgft
SceAppInstUtil
SceLncUtil
)
add_self(ezremote_client)
add_pkg(ezremote_client ${CMAKE_SOURCE_DIR}/data "RMTC00001" "ezRemote Client" "02.00" 32 0)
add_custom_target(package
COMMAND mkdir -p ${PROJECT_SOURCE_DIR}/data/daemon
COMMAND cp ${PROJECT_SOURCE_DIR}/ps4-ezremote-server/data/eboot.bin ${PROJECT_SOURCE_DIR}/data/daemon/daemon.self
COMMAND cp ${PROJECT_SOURCE_DIR}/ps4-ezremote-server/data/eboot.md5 ${PROJECT_SOURCE_DIR}/data/daemon/daemon.md5
COMMAND cp ${PROJECT_SOURCE_DIR}/ps4-ezremote-server/data/sce_sys/param.sfo ${PROJECT_SOURCE_DIR}/data/daemon/param
)
+39 -4
View File
@@ -1,6 +1,28 @@
# ezRemote Client
ezRemote Client is an application that allows you to connect the PS4 to remote FTP/SFTP, SMB, NFS, 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(Windows Share), NFS, WebDAV, HTTP servers and Google Drive to transfer files. The interface is inspired by Filezilla client which provides a commander like GUI.
**NEW: As of version 2.00, You can download large file in the background.**
- You can enable/disable background download in the Global settings
- You can set the minimum file size where background download will use. Default is 1GB
- In Global Settings, you can show the background download progress of all requested downloads
- Does not support rest mode. Background downloads will stop in rest mode, but will be resumed when ezRemote Server is restarted.
- If PS4 is restarted, background downloads will resume when ezRemote Server is restarted.
- Updated the Web UI, so you can download files of File shares like mediafire, google shared link, pixeldrain, real-debrid and all-debrid or any direct links
**NEW: As of version 2.00, PS4 pkgs can be installed in the background. Use ezRemote client to connect to remote server and select pkg to install. You can then close ezRemote Client app and the package will continue installing in background. This is archieved with a new payload called [ezRemote Server](https://github.com/cy33hc/ps4-ezremote-server) that is packaged together with exRemote Client. ezRemote Server runs in the background and acts as a proxy to the remote server. ezRemote Server must be started to enable backgroud installs.**
- support background install from all remote servers that ezremote client supports
- support background install from file host like mediafire, google shared link, pixeldrain, real-debrid and all-debrid
- does not support background install using the options "Disk Cache" or PKGs inside zip files
- You can pause and resume installs
- After restarting PS4, you can resume installs by first reloading ezRemote Server
- From testing, background install continues even in rest mode (This isn't granteed). You may need to restart ezRemote Server payload before you can resume install
- ezRemote Server payload can be stop/restarted from ezRemote Client settngs dialog.
- Do not submit to many background installs, it can crash ezRemote Server.
**New: As of version 1.19**
- Install PKG inside Zip files from both local hdd and remote servers.
- Extact zip files directly from remote servers.
**New:** As of version 1.0.8, ezRemote Client has a Web Interface that can be access from any modern browser to manage the PS4 files.
@@ -27,7 +49,7 @@ To distinguish between FTP, SMB, NFS, WebDAV or HTTP, the URL must be prefix wit
then in the password field enter file:///data/ezremote-client
```
- The url format for SMB is
- The url format for SMB(Windows Share) is
```
smb://hostname[:port]/sharename
@@ -77,7 +99,19 @@ To distinguish between FTP, SMB, NFS, WebDAV or HTTP, the URL must be prefix wit
```
- For Google Drive use the following URL for the server **https://drive.google.com**
<br />[Go to the following wiki for instructions on how to setup the app to connect to Google Drive]( https://github.com/cy33hc/ps4-ezremote-client/wiki/Setup-App-for-use-with-Google-Drive)
- For Internet Archive repos download URLs
- Only supports parsing of the download URL (ie the URL where you see a list of files). Example
| | | |
|----------|-----------|---|
| ![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.
@@ -114,7 +148,7 @@ Remote Package Installation with all Remote Server, even if they are password pr
- Upload files to the PS4
- Download files from the PS4
- Install packages on the PS4
- Install packages from shared links from mediafire, google and pixeldrain. For other filehost, they can installed via AllDebrid (See AllDebrid website for supported filehost).
- Install packages from shared links from mediafire, google and pixeldrain. For other filehost, they can installed via AllDebrid/RealDebrid (See respective websites for supported filehost).
## How to access the Web Interface ##
You need to launch the "ezRemote Client" app on the PS4. Then on any device(laptop, tablet, phone etc..) with web browser goto to http://<ip_address_of_ps4>:8080 . That's all.
@@ -132,6 +166,7 @@ Circle - Un-Select the file list to navigate to other widgets or Close Dialog wi
Square - Mark file(s)/folder(s) for Delete/Rename/Upload/Download
R1 - Navigate to the Remote list of files
L1 - Navigate to the Local list of files
L2 - To go up a directory from current directory
TouchPad Button - Exit Application (versions prior to 1.06)
Options Button - Exit Application (versions 1.06 and above)
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

+2 -1
View File
@@ -42,6 +42,7 @@
installUrl: '/__local__/install',
uploadResumeSizeUrl: '__local__/uploadResumeSize',
installUrlUrl: '/__local__/install_url',
downloadUrlUrl: '/__local__/download_url',
multipleDownloadFileName: 'ezremote-client.zip',
isExtractableFilePattern: /\.(7z|rar|zip)$/i,
pickCallback: function (item) {
@@ -63,4 +64,4 @@
<angular-filemanager></angular-filemanager>
</body>
</html>
</html>
+1 -1
View File
@@ -113,7 +113,7 @@ STR_DOWNLOAD_INSTALL_MSG=تثبيت الحزمة عن بعد غير ممكن. ه
STR_CHECKING_REMOTE_SERVER_MSG=التحقق من الخادم البعيد لتثبيت الحزمة عن بعد.
STR_ENABLE_RPI=RPI
STR_ENABLE_RPI_FTP_SMB_MSG=يتيح هذا الخيار تثبيت الحزمة عن بُعد عبر خادم الويب المضمن.
STR_ENABLE_RPI_WEBDAV_MSG==يتيح هذا الخيار تثبيت الحزمة عن بُعد عبر خادم الويب المضمن.
STR_ENABLE_RPI_WEBDAV_MSG=يتيح هذا الخيار تثبيت الحزمة عن بُعد عبر خادم الويب المضمن.
STR_FILES=الملفات
STR_EDITOR=المحرر
STR_SAVE=حفظ
+9 -3
View File
@@ -112,8 +112,8 @@ 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 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 via embedded Web Server.
STR_ENABLE_RPI_WEBDAV_MSG==This option enables Remote Package Installation via embedded Web Server.
STR_ENABLE_RPI_FTP_SMB_MSG=This option enables Remote Package Installation via embedded Web Server proxy.
STR_ENABLE_RPI_WEBDAV_MSG=This option enables Remote Package Installation via embedded Web Server proxy.
STR_FILES=Files
STR_EDITOR=Editor
STR_SAVE=Save
@@ -157,4 +157,10 @@ STR_FAIL_INSTALL_FROM_URL_MSG=Failed to install from URL
STR_INVALID_URL=InValid URL
STR_ALLDEBRID_API_KEY_MISSING_MSG=To use this function, an API Key needs to be configured in the ezRemote Client settings
STR_LANGUAGE=Language
STR_TEMP_DIRECTORY=Temp Directory
STR_TEMP_DIRECTORY=Temp Directory
STR_REALDEBRID=Real-Debrid
STR_BACKGROUND_INSTALL_INPROGRESS=Package install is running in the background. Don't close the app while install is in progress
STR_ENABLE_DISC_CACHE_MSG=Enable disk caching. Can improve Remote Package Install speed in cases where connection to remote is slow, but breaks resuming of install
STR_ENABLE_ALLDEBRID_MSG=Install Via AllDebrid
STR_ENABLE_REALDEBRID_MSG=Install Via RealDebrid
STR_ENABLE_DISKCACHE_DESC=Enable Disk Cache
+74 -2
View File
@@ -25,7 +25,7 @@ STR_FILE=파일
STR_TYPE=종류
STR_NAME=이름
STR_SIZE=크기
STR_DATE=일시
STR_DATE=날짜
STR_NEW_FOLDER=새 폴더
STR_RENAME=이름 변경
STR_DELETE=삭제
@@ -39,7 +39,7 @@ STR_OVERWRITE=덮어쓰기
STR_DONT_OVERWRITE=덮어쓰기 금지
STR_ASK_FOR_CONFIRM=수락여부를 질문
STR_DONT_ASK_CONFIRM=수락여부를 묻지 않음
STR_ALLWAYS_USE_OPTION=다시 묻지 않고 항상 이 설정을 사용
STR_ALLWAYS_USE_OPTION=다시 묻지 않고 항상 이 설정을 사용
STR_ACTIONS=실행
STR_CONFIRM=확인
STR_OVERWRITE_OPTIONS=덮어쓰기 옵션
@@ -59,3 +59,75 @@ STR_FAIL_DEL_DIR_MSG=디렉토리 삭제에 실패하였습니다.
STR_DELETING=삭제 중
STR_FAIL_DEL_FILE_MSG=파일 삭제에 실패하였습니다.
STR_DELETED=삭제됨
STR_LINK=링크
STR_SHARE=공유
STR_FAILED=310 실패함
STR_FAIL_CREATE_LOCAL_FILE_MSG=310 로컬에서 파일을 만들기 실패함
STR_INSTALL=설치
STR_INSTALLING=설치 중
STR_INSTALL_SUCCESS=성공
STR_INSTALL_FAILED=실패함
STR_INSTALL_SKIPPED=건너뜀
STR_CHECK_HTTP_MSG=원격 HTTP 서버에 대한 연결 확인
STR_FAILED_HTTP_CHECK=HTTP 서버에 연결에 실패함
STR_REMOTE_NOT_HTTP=원격이 HTTP 서버가 아님
STR_INSTALL_FROM_DATA_MSG=패키지가 /data 또는 /mnt/usbX 폴더에 없음
STR_ALREADY_INSTALLED_MSG=패키지가 이미 설치되었음
STR_INSTALL_FROM_URL=URL에서 설치
STR_CANNOT_READ_PKG_HDR_MSG=패키지 헤더 정보를 읽을 수 없음
STR_FAVORITE_URLS=즐겨찾기 URL
STR_SLOT=슬롯
STR_EDIT=편집
STR_ONETIME_URL=한 번만 Url
STR_NOT_A_VALID_PACKAGE=유효하지 않은 패키지
STR_WAIT_FOR_INSTALL_MSG=패키지 설치가 완료되기를 기다리는 중
STR_FAIL_INSTALL_TMP_PKG_MSG=pkg 파일을 설치하지 못했습니다. tmp pkg를 수동으로 삭제하세요.
STR_AUTO_DELETE_TMP_PKG=설치 후 임시로 다운로드한 pkg 파일을 자동으로 삭제
STR_PROTOCOL_NOT_SUPPORTED=지원되지 않는 프로토콜
STR_COULD_NOT_RESOLVE_HOST=호스트 이름을 확인할 수 없음
STR_EXTRACT=압축 해제
STR_EXTRACTING=압축 해제 중
STR_FAILED_TO_EXTRACT=압축 해제 실패함
STR_EXTRACT_LOCATION=압축 해제 위치
STR_COMPRESS=압축
STR_ZIP_FILE_PATH=Zip 파일 이름
STR_COMPRESSING=압축 중
STR_ERROR_CREATE_ZIP=zip 파일을 만드는 동안 오류 발생
STR_UNSUPPORTED_FILE_FORMAT=지원되지 않는 압축 파일 형식
STR_CUT=잘라내기
STR_COPY=복사
STR_PASTE=붙여넣기
STR_MOVING=이동 중
STR_COPYING=복사 중
STR_FAIL_MOVE_MSG=파일 이동 실패함
STR_FAIL_COPY_MSG=파일 복사 실패함
STR_CANT_MOVE_TO_SUBDIR_MSG=상위 디렉토리를 하위 디렉토리로 이동할 수 없음
STR_CANT_COPY_TO_SUBDIR_MSG=상위 디렉토리를 하위 디렉토리로 복사할 수 없음
STR_UNSUPPORTED_OPERATION_MSG=지원되지 않는 작업
STR_HTTP_PORT=Http 포트
STR_REINSTALL_CONFIRM_MSG=콘텐츠가 이미 설치되었습니다. 설치를 계속할까요?
STR_REMOTE_NOT_SUPPORT_MSG=보호된 서버에서는 원격 패키지 설치가 지원되지 않습니다.
STR_CANNOT_CONNECT_REMOTE_MSG=원격 HTTP 서버에 연결할 수 없습니다.
STR_DOWNLOAD_INSTALL_MSG=원격 패키지 설치가 불가능합니다. 패키지를 다운로드하여 설치할까요?
STR_CHECKING_REMOTE_SERVER_MSG=원격 패키지 설치를 위해 원격 서버를 확인하고 있습니다.
STR_FILES=파일
STR_EDITOR=편집기
STR_SAVE=저장
STR_MAX_EDIT_FILE_SIZE_MSG=다음보다 큰 파일을 편집할 수 없음
STR_DELETE_LINE=선택한 줄 삭제
STR_INSERT_LINE=선택한 줄 아래에 삽입
STR_MODIFIED=수정됨
STR_FAIL_GET_TOKEN_MSG=다음에서 접속 토큰을 얻는데 실패함
STR_GET_TOKEN_SUCCESS_MSG=로그인에 성공했습니다. 브라우저를 닫고 애플리케이션으로 돌아갈 수 있음
STR_NEW_FILE=새 파일
STR_SETTINGS=설정
STR_GLOBAL=글로벌
STR_COPY_LINE=선택한 줄 복사
STR_PASTE_LINE=선택한 줄에 붙여넣기
STR_SHOW_HIDDEN_FILES=숨겨진 파일 표시
STR_SET_DEFAULT_DIRECTORY=기본 폴더 설정
STR_SET_DEFAULT_DIRECTORY_MSG=기본 디렉터리로 설정됨
STR_NFS_EXP_PATH_MISSING_MSG=URL에 NFS 내보내기 경로 없음
STR_FAIL_INIT_NFS_CONTEXT=NFS 컨텍스트를 초기화 실패함
STR_FAIL_MOUNT_NFS_MSG=NFS 공유를 마운트 실패함
STR_VIEW_IMAGE=이미지 보기
+166
View File
@@ -0,0 +1,166 @@
STR_CONNECTION_SETTINGS=Tilkoblingsinnstillinger
STR_SITE=Side
STR_LOCAL=Lokal
STR_REMOTE=Ekstern
STR_MESSAGES=Meldinger
STR_UPDATE_SOFTWARE=Oppdater programvare
STR_CONNECT=Koble til
STR_DISCONNECT=Koble fra
STR_SEARCH=Søk
STR_REFRESH=Oppdater
STR_SERVER=Tjener
STR_USERNAME=Brukernavn
STR_PASSWORD=Passord
STR_PORT=Port
STR_PASV=Pasv
STR_DIRECTORY=Mappe
STR_FILTER=Filter
STR_YES=Ja
STR_NO=Nei
STR_CANCEL=Avbryt
STR_CONTINUE=Fortsett
STR_CLOSE=Lukk
STR_FOLDER=Mappe
STR_FILE=Fil
STR_TYPE=Type
STR_NAME=Navn
STR_SIZE=Størrelse
STR_DATE=Dato
STR_NEW_FOLDER=Ny mappe
STR_RENAME=Gi nytt navn
STR_DELETE=Slett
STR_UPLOAD=Last opp
STR_DOWNLOAD=Last ned
STR_SELECT_ALL=Marker alle
STR_CLEAR_ALL=Fjern alle
STR_UPLOADING=Laster opp
STR_DOWNLOADING=Laster ned
STR_OVERWRITE=Skriv over
STR_DONT_OVERWRITE=Ikke skriv over
STR_ASK_FOR_CONFIRM=Spør om bekreftelse
STR_DONT_ASK_CONFIRM=Ikke spør om bekreftelse
STR_ALLWAYS_USE_OPTION=Bruk alltid dette valget og ikke spør igjen
STR_ACTIONS=Handlinger
STR_CONFIRM=Bekreft
STR_OVERWRITE_OPTIONS=Overskriv innstillinger
STR_PROPERTIES=Egenskaper
STR_PROGRESS=Framdrift
STR_UPDATES=Oppdateringer
STR_DEL_CONFIRM_MSG=Er du sikker på at du vil slette filen(e)/mappen(e)?
STR_CANCEL_ACTION_MSG=Avbryter. Venter på at siste handling skal fullføres
STR_FAIL_UPLOAD_MSG=Opplasting av fil mislykkes
STR_FAIL_DOWNLOAD_MSG=Nedlasting av fil mislykkes
STR_FAIL_READ_LOCAL_DIR_MSG=Lesing av mappe mislykkes, eller mappen finnes ikke.
STR_CONNECTION_CLOSE_ERR_MSG=426 Tilkobling Lukket.
STR_REMOTE_TERM_CONN_MSG=426 Ekstern Tjener har lukket tilkoblingen.
STR_FAIL_LOGIN_MSG=300 Innlogging Mislykkes. Vennligst sjekk ditt brukernavn og passord.
STR_FAIL_TIMEOUT_MSG=426 Mislykkes. Tilkoblingen timet ut.
STR_FAIL_DEL_DIR_MSG=Sletting av mappe mislykkes
STR_DELETING=Sletter
STR_FAIL_DEL_FILE_MSG=Sletting av fil mislykkes
STR_DELETED=Slettet
STR_LINK=Lenke
STR_SHARE=Del
STR_FAILED=310 Mislykket
STR_FAIL_CREATE_LOCAL_FILE_MSG=310 Oppretting av lokal fil mislykkes
STR_INSTALL=Installér
STR_INSTALLING=Installerer
STR_INSTALL_SUCCESS=Vellykket
STR_INSTALL_FAILED=Mislykket
STR_INSTALL_SKIPPED=Hoppet over
STR_CHECK_HTTP_MSG=Sjekker tilkobling til ekstern HTTP-tjener
STR_FAILED_HTTP_CHECK=Mislykket tilkobling til HTTP-tjener
STR_REMOTE_NOT_HTTP=Ekstern tjener er ikke en HTTP-tjener
STR_INSTALL_FROM_DATA_MSG=Pakke finnes ikke i mappene /data eller /mnt/usbX
STR_ALREADY_INSTALLED_MSG=Pakken er allerede installert
STR_INSTALL_FROM_URL=Installer fra URL
STR_CANNOT_READ_PKG_HDR_MSG=Kunne ikke lese pakkens header
STR_FAVORITE_URLS=Favoritt-URLer
STR_SLOT=Spor
STR_EDIT=Redigér
STR_ONETIME_URL=Engangs-URL
STR_NOT_A_VALID_PACKAGE=Ikke en gyldig pakke
STR_WAIT_FOR_INSTALL_MSG=Venter på at pakke skal bli ferdig å installere
STR_FAIL_INSTALL_TMP_PKG_MSG=Installasjon av PKG-fil mislykkes. Vennligst slett den midlertidlige PKG-filen manuelt
STR_FAIL_TO_OBTAIN_GG_DL_MSG=Kunne ikke hente nedlastings-URL
STR_AUTO_DELETE_TMP_PKG=Automatisk slett midlertidig nedlastet PKG-fil etter installasjon
STR_PROTOCOL_NOT_SUPPORTED=Protokollen er ikke støttet
STR_COULD_NOT_RESOLVE_HOST=Kunne ikke koble til tjenernavnet
STR_EXTRACT=Pakk ut
STR_EXTRACTING=Pakker ut
STR_FAILED_TO_EXTRACT=Utpakking mislykkes
STR_EXTRACT_LOCATION=Utpakkingsbane
STR_COMPRESS=Komprimér
STR_ZIP_FILE_PATH=ZIP-filnavn
STR_COMPRESSING=Komprimerer
STR_ERROR_CREATE_ZIP=Oppretting av ZIP mislykket
STR_UNSUPPORTED_FILE_FORMAT=Arkivformatet er ikke støttet
STR_CUT=Klipp ut
STR_COPY=Kopiér
STR_PASTE=Lim inn
STR_MOVING=Flytter
STR_COPYING=Kopierer
STR_FAIL_MOVE_MSG=Flytting av fil mislykkes
STR_FAIL_COPY_MSG=Kopiering av fil mislykkes
STR_CANT_MOVE_TO_SUBDIR_MSG=Kan ikke flytte overordnet mappe til undermappe
STR_CANT_COPY_TO_SUBDIR_MSG=Kan ikke kopiere overordnet mappe til undermappe
STR_UNSUPPORTED_OPERATION_MSG=Handling ikke støttet
STR_HTTP_PORT=HTTP-port
STR_REINSTALL_CONFIRM_MSG=Innholdet er allerede installert. Vil du fortsette installasjonen
STR_REMOTE_NOT_SUPPORT_MSG=Fjerninstallasjon av pakker er ikke støttet for beskyttede tjenere.
STR_CANNOT_CONNECT_REMOTE_MSG=Ekstern HTTP-tjener kan ikke nås.
STR_DOWNLOAD_INSTALL_MSG=Fjerninstallasjon er ikke mulig. Vil du laste ned pakken og installere den istedet?
STR_CHECKING_REMOTE_SERVER_MSG=Sjekker tjener for fjerninstallasjon av pakke.
STR_ENABLE_RPI=FIP
STR_ENABLE_RPI_FTP_SMB_MSG=Dette valget skrur på fjerninstallasjon av pakke via innebygd Web-tjenerproxy.
STR_ENABLE_RPI_WEBDAV_MSG=Dette valget skrur på fjerninstallasjon av pakke via innebygd Web-tjenerproxy.
STR_FILES=Filer
STR_EDITOR=Redigeringsprogram
STR_SAVE=Lagre
STR_MAX_EDIT_FILE_SIZE_MSG=Kan ikke redigere filer større enn
STR_DELETE_LINE=Slett markert linje
STR_INSERT_LINE=Sett inn under markert linje
STR_MODIFIED=Endret
STR_FAIL_GET_TOKEN_MSG=Kunne ikke hente en tilgangstoken fra
STR_GET_TOKEN_SUCCESS_MSG=Innlogging vellykket. Du kan lukke nettleseren og gå tilbake til applikasjonen
STR_PERM_DRIVE=Se, endre, opprette og slette dine Google Drive-filer
STR_PERM_DRIVE_APPDATA=Se, opprette og slette dens egen konfigurasjonsdata i din Google Drive
STR_PERM_DRIVE_FILE=Se, endre, opprette og slette kun de spesifikke Google Drive-filene du bruker med denne appen
STR_PERM_DRIVE_METADATA=Vise og behandle metadataen til filer i din Google Drive
STR_PERM_DRIVE_METADATA_RO=Se informasjon om dine Google Drive-filer
STR_GOOGLE_LOGIN_FAIL_MSG=Google-innlogging mislykkes
STR_GOOGLE_LOGIN_TIMEOUT_MSG=Google-innlogging timet ut
STR_NEW_FILE=Ny Fil
STR_SETTINGS=Innstillinger
STR_CLIENT_ID=Klient-ID
STR_CLIENT_SECRET=Klienthemmelighet (Client Secret)
STR_GLOBAL=Global
STR_GOOGLE=Google
STR_COPY_LINE=Kopier markert linje
STR_PASTE_LINE=Lim inn i markert linje
STR_SHOW_HIDDEN_FILES=Vis skjulte filer
STR_SET_DEFAULT_DIRECTORY=Sett oppstartsmappe
STR_SET_DEFAULT_DIRECTORY_MSG=har blitt satt som oppstartsmappe
STR_VIEW_IMAGE=Vis bilde
STR_VIEW_PKG_INFO=Pakkeinformasjon
STR_NFS_EXP_PATH_MISSING_MSG=NFS-eksportbane mangler fra URL
STR_FAIL_INIT_NFS_CONTEXT=Initialisering av NFS-kontekst mislykkes
STR_FAIL_MOUNT_NFS_MSG=Montering av NFS-share mislykkes
STR_WEB_SERVER=Web-tjener
STR_ENABLE=Skru på
STR_COMPRESSED_FILE_PATH=Lokasjon for komprimerte filer
STR_COMPRESSED_FILE_PATH_MSG=Lokasjon for komprimerte filer på Web-tjener
STR_ALLDEBRID=AllDebrid
STR_API_KEY=API-nøkkel
STR_CANT_EXTRACT_URL_MSG=Kunne ikke utvinne nedlastings-URL
STR_FAIL_INSTALL_FROM_URL_MSG=Installasjon fra URL mislykkes
STR_INVALID_URL=Ugyldig URL
STR_ALLDEBRID_API_KEY_MISSING_MSG=For å bruke denne funksjonen, må du konfigurere en API-nøkkel i instillingene for ezRemote Client.
STR_LANGUAGE=Språk
STR_TEMP_DIRECTORY=Midlertidig mappe
STR_REALDEBRID=Real-Debrid
STR_BACKGROUND_INSTALL_INPROGRESS=Pakkeinstallasjon kjører i bakrunnen. Ikke lykk denne appen mens installasjonen pågår
STR_ENABLE_DISC_CACHE_MSG=Skru på disk-caching. Kan gjøre fjerninstallasjon av pakker kjappere når tilkoblingen er treg, men lar deg ikke fortsette en avbrutt installasjon
STR_ENABLE_ALLDEBRID_MSG=Installér via AllDebrid
STR_ENABLE_REALDEBRID_MSG=Installér via RealDebrid
STR_ENABLE_DISKCACHE_DESC=Skru på disk-cache
+98 -26
View File
@@ -1,8 +1,8 @@
STR_CONNECTION_SETTINGS=连接设置
STR_SITE=
STR_LOCAL=当地的
STR_REMOTE=偏僻的
STR_MESSAGES=留言
STR_SITE=
STR_LOCAL=本地
STR_REMOTE=远程路径
STR_MESSAGES=信息
STR_UPDATE_SOFTWARE=更新软件
STR_CONNECT=连接
STR_DISCONNECT=断开
@@ -11,51 +11,123 @@ STR_REFRESH=刷新
STR_SERVER=服务器
STR_USERNAME=用户名
STR_PASSWORD=密码
STR_PORT=
STR_PASV=帕夫
STR_PORT=
STR_PASV=被动模式
STR_DIRECTORY=目录
STR_FILTER=筛选
STR_YES=
STR_NO=
STR_FILTER=过滤器
STR_YES=
STR_NO=
STR_CANCEL=取消
STR_CONTINUE=继续
STR_CLOSE=关闭
STR_FOLDER=文件夹
STR_FILE=文件
STR_TYPE=类型
STR_NAME=
STR_SIZE=尺寸
STR_NAME=
STR_SIZE=大小
STR_DATE=日期
STR_NEW_FOLDER=新建文件夹
STR_RENAME=
STR_RENAME=重命
STR_DELETE=删除
STR_UPLOAD=上传
STR_DOWNLOAD=下载
STR_SELECT_ALL=全选
STR_CLEAR_ALL=全部清除
STR_UPLOADING=上传
STR_DOWNLOADING=下载
STR_CLEAR_ALL=取消全选
STR_UPLOADING=上传
STR_DOWNLOADING=下载
STR_OVERWRITE=覆盖
STR_DONT_OVERWRITE=覆盖
STR_ASK_FOR_CONFIRM=要求确认
STR_DONT_ASK_CONFIRM=要要求确认
STR_DONT_OVERWRITE=不覆盖
STR_ASK_FOR_CONFIRM=确认请求
STR_DONT_ASK_CONFIRM=不确认请求
STR_ALLWAYS_USE_OPTION=始终使用此选项,不再询问
STR_ACTIONS=行动
STR_CONFIRM=确认
STR_OVERWRITE_OPTIONS=覆盖选项
STR_PROPERTIES=
STR_PROGRESS=
STR_PROPERTIES=
STR_PROGRESS=
STR_UPDATES=更新
STR_DEL_CONFIRM_MSG=您确定要删除此文件/文件夹
STR_CANCEL_ACTION_MSG=取消等待最后一个动作完成
STR_DEL_CONFIRM_MSG=您确定要删除此文件/文件夹?
STR_CANCEL_ACTION_MSG=取消中。请等待最后一个操作结束
STR_FAIL_UPLOAD_MSG=上传文件失败
STR_FAIL_DOWNLOAD_MSG=下载文件失败
STR_FAIL_READ_LOCAL_DIR_MSG=读取目录内容失败或文件夹不存在。
STR_CONNECTION_CLOSE_ERR_MSG=426 连接已关闭。
STR_REMOTE_TERM_CONN_MSG=426 远程服务器已终止连接。
STR_FAIL_LOGIN_MSG=300 登录失败。 请检查您的用户名或密码。
STR_FAIL_TIMEOUT_MSG=426 失败。 连接超时。
STR_FAIL_LOGIN_MSG=300 登录失败。请检查您的用户名或密码。
STR_FAIL_TIMEOUT_MSG=426 失败。连接超时。
STR_FAIL_DEL_DIR_MSG=删除目录失败
STR_DELETING=删除
STR_DELETING=删除
STR_FAIL_DEL_FILE_MSG=删除文件失败
STR_DELETED=删除
STR_DELETED=删除完毕
STR_LINK=链接
STR_SHARE=分享
STR_FAILED=310 失败
STR_FAIL_CREATE_LOCAL_FILE_MSG=310 无法本地创建文件
STR_INSTALL=安装
STR_INSTALLING=正在安装中
STR_INSTALL_SUCCESS=成功
STR_INSTALL_FAILED=失败
STR_INSTALL_SKIPPED=已忽略
STR_CHECK_HTTP_MSG=正在检查与远程HTTP服务器的连接
STR_FAILED_HTTP_CHECK=连接到HTTP服务器失败
STR_REMOTE_NOT_HTTP=远程不是HTTP服务器
STR_INSTALL_FROM_DATA_MSG=文件包不在/data或/mnt/usbX文件夹中
STR_ALREADY_INSTALLED_MSG=文件包已经安装
STR_INSTALL_FROM_URL=从网址安装
STR_CANNOT_READ_PKG_HDR_MSG=无法读取包文件头信息
STR_FAVORITE_URLS=收藏网址
STR_SLOT=槽位
STR_EDIT=编辑
STR_ONETIME_URL=一次性网址
STR_NOT_A_VALID_PACKAGE=不是有效的程序包
STR_WAIT_FOR_INSTALL_MSG=正在等待包完成安装
STR_FAIL_INSTALL_TMP_PKG_MSG=未能安装pkg文件。请手动删除tmp pkg
STR_AUTO_DELETE_TMP_PKG=安装后自动删除临时下载的pkg文件
STR_PROTOCOL_NOT_SUPPORTED=不支持该协议
STR_COULD_NOT_RESOLVE_HOST=无法解析主机名
STR_EXTRACT=提取
STR_EXTRACTING=提取中
STR_FAILED_TO_EXTRACT=提取失败
STR_EXTRACT_LOCATION=提取位置
STR_COMPRESS=压缩
STR_ZIP_FILE_PATH=Zip 文件名
STR_COMPRESSING=压缩中
STR_ERROR_CREATE_ZIP=正在创建zip文件时出现错误
STR_UNSUPPORTED_FILE_FORMAT=不支持的压缩文件格式
STR_CUT=剪切
STR_COPY=复制
STR_PASTE=粘贴
STR_MOVING=移动中
STR_COPYING=复制中
STR_FAIL_MOVE_MSG=移动文件失败
STR_FAIL_COPY_MSG=复制文件事变
STR_CANT_MOVE_TO_SUBDIR_MSG=无法将父目录移动到子目录
STR_CANT_COPY_TO_SUBDIR_MSG=无法将父目录复制到子目录
STR_UNSUPPORTED_OPERATION_MSG=不支持操作
STR_HTTP_PORT=Http 端口
STR_REINSTALL_CONFIRM_MSG=内容已安装。是否要继续安装
STR_REMOTE_NOT_SUPPORT_MSG=受保护的服务器不支持远程程序包安装。
STR_CANNOT_CONNECT_REMOTE_MSG=无法访问远程HTTP服务器。
STR_DOWNLOAD_INSTALL_MSG=无法安装远程程序包。是否下载软件包并进行安装?
STR_CHECKING_REMOTE_SERVER_MSG=正在检查远程服务器的远程包安装。
STR_FILES=文件
STR_EDITOR=编辑器
STR_SAVE=保存
STR_MAX_EDIT_FILE_SIZE_MSG=无法编辑大于全部的文件。
STR_DELETE_LINE=删除所选行
STR_INSERT_LINE=在所选行下方插入
STR_MODIFIED=已修改
STR_FAIL_GET_TOKEN_MSG=无法从获取访问令牌
STR_GET_TOKEN_SUCCESS_MSG=登录成功。您可以关闭浏览器并返回应用程序
STR_NEW_FILE=新文件
STR_SETTINGS=设置
STR_GLOBAL=全局
STR_COPY_LINE=复制所选行
STR_PASTE_LINE=粘贴到所选行
STR_SHOW_HIDDEN_FILES=显示隐藏文件
STR_SET_DEFAULT_DIRECTORY=设置默认文件夹
STR_SET_DEFAULT_DIRECTORY_MSG=已设置为默认目录
STR_NFS_EXP_PATH_MISSING_MSG=URL中缺少NFS导出路径
STR_FAIL_INIT_NFS_CONTEXT=无法初始化NFS上下文
STR_FAIL_MOUNT_NFS_MSG=挂在 NFS 共享失败
STR_VIEW_IMAGE=查看图片
+94 -36
View File
@@ -1,75 +1,133 @@
STR_CONNECTION_SETTINGS=Bağlantı Ayarları
STR_SITE=Site
STR_LOCAL=Yerel
STR_REMOTE=Uzaktan
STR_REMOTE=Uzak
STR_MESSAGES=Mesajlar
STR_UPDATE_SOFTWARE=Yazılımı Güncelle
STR_CONNECT=Bağlan
STR_DISCONNECT=Bağlantıyı kes
STR_SEARCH=Ara
STR_DISCONNECT=Bağlantıyı Kes
STR_SEARCH=Arama
STR_REFRESH=Yenile
STR_SERVER=Sunucu
STR_USERNAME=Kullanıcı Adı
STR_PASSWORD=Şifre
STR_PASSWORD=Parola
STR_PORT=Port
STR_PASV=Pasv
STR_PASV=Pasif
STR_DIRECTORY=Dizin
STR_FILTER=Filtre
STR_YES=Evet
STR_NO=Hayır
STR_CANCEL=İptal
STR_CONTINUE=Devam
STR_CONTINUE=Devam Et
STR_CLOSE=Kapat
STR_FOLDER=Klasör
STR_FILE=Dosya
STR_TYPE=Tip
STR_TYPE=Tür
STR_NAME=Ad
STR_SIZE=Boyut
STR_DATE=Tarih
STR_NEW_FOLDER=Yeni Klasör
STR_RENAME=Yeniden Adlandır
STR_DELETE=Sil
STR_UPLOAD=Karşıya Yükle
STR_UPLOAD=Yükle
STR_DOWNLOAD=İndir
STR_SELECT_ALL=Tümünü Seç
STR_CLEAR_ALL=Tümünü Temizle
STR_UPLOADING=Karşıya Yükleniyor
STR_SELECT_ALL=Hepsini Seç
STR_CLEAR_ALL=Hepsini Temizle
STR_UPLOADING=Yükleniyor
STR_DOWNLOADING=İndiriliyor
STR_OVERWRITE=Üzerine yaz
STR_DONT_OVERWRITE=Üzerine yazma
STR_ASK_FOR_CONFIRM=Onaylamak için sor
STR_DONT_ASK_CONFIRM=Onaylamak için sorma
STR_ALLWAYS_USE_OPTION=Daima bu seçeneği kullan ve bir daha sorma
STR_ACTIONS=Eylemler
STR_OVERWRITE=Üzerine Yaz
STR_DONT_OVERWRITE=Üzerine Yazma
STR_ASK_FOR_CONFIRM=Onay İste
STR_DONT_ASK_CONFIRM=Onay İsteme
STR_ALLWAYS_USE_OPTION=Bu seçeneği her zaman kullan ve bir daha sorma
STR_ACTIONS=İşlemler
STR_CONFIRM=Onayla
STR_OVERWRITE_OPTIONS=Ayarların üzerine yaz
STR_OVERWRITE_OPTIONS=Üzerine Yazma Seçenekleri
STR_PROPERTIES=Özellikler
STR_PROGRESS=Durum
STR_PROGRESS=İlerleme
STR_UPDATES=Güncellemeler
STR_DEL_CONFIRM_MSG=Bu dosya(ları)/klasör(leri) silmek istediğinizden emin misiniz?
STR_CANCEL_ACTION_MSG=İptal ediliyor. Son lemin tamamlanması bekleniyor
STR_FAIL_UPLOAD_MSG=Dosya karşıya yükleme başarısız
STR_FAIL_DOWNLOAD_MSG=Dosya indirme başarısız
STR_FAIL_READ_LOCAL_DIR_MSG=Dizindeki içerikler okunamadı ya da öyle bir klasör yok
STR_CONNECTION_CLOSE_ERR_MSG=426 Bağlantı kesildi.
STR_REMOTE_TERM_CONN_MSG=426 Uzaktan Sunucu bağlantıyı kesti.
STR_FAIL_LOGIN_MSG=300 Giriş başarısız. Lütfen kullanıcı adınızı ve şifrenizi kontrol edin.
STR_CANCEL_ACTION_MSG=İptal ediliyor. Son eylemin tamamlanması bekleniyor
STR_FAIL_UPLOAD_MSG=Dosya yüklenemedi
STR_FAIL_DOWNLOAD_MSG=Dosya indirilemedi
STR_FAIL_READ_LOCAL_DIR_MSG=Yerel dizinin içeriği okunamadı veya klasör mevcut değil.
STR_CONNECTION_CLOSE_ERR_MSG=426 Bağlantı kapatıldı.
STR_REMOTE_TERM_CONN_MSG=426 Uzak Sunucu bağlantıyı sonlandırdı.
STR_FAIL_LOGIN_MSG=300 Giriş Başarısız. Lütfen kullanıcı adınızı veya şifrenizi kontrol edin.
STR_FAIL_TIMEOUT_MSG=426 Başarısız. Bağlantı zaman aşımına uğradı.
STR_FAIL_DEL_DIR_MSG=Dizin silme başarısız
STR_FAIL_DEL_DIR_MSG=Dizin silinemedi
STR_DELETING=Siliniyor
STR_FAIL_DEL_FILE_MSG=Dosya silme başarısız
STR_FAIL_DEL_FILE_MSG=Dosya silinemedi
STR_DELETED=Silindi
STR_LINK=Link
STR_LINK=Bağlantı
STR_SHARE=Paylaş
STR_FAILED=310 Başarısız
STR_FAIL_CREATE_LOCAL_FILE_MSG=310 Yerelde dosya oluşturulumu başarısız
STR_INSTALL=Yükle
STR_INSTALLING=Yükleniyor
STR_FAIL_CREATE_LOCAL_FILE_MSG=310 Yerelde dosya oluşturulamadı
STR_INSTALL=Kur
STR_INSTALLING=Kuruluyor
STR_INSTALL_SUCCESS=Başarılı
STR_INSTALL_FAILED=Başarısız
STR_INSTALL_SKIPPED=Atlandı
STR_CHECK_HTTP_MSG=Uzaktan HTTP Sunucusu bağlantısı kontrol ediliyor
STR_FAILED_HTTP_CHECK=HTTP Sunucusuna bağlantı başarısız
STR_REMOTE_NOT_HTTP=Uzaktaki HTTP Sunucusu değil
STR_INSTALL_FROM_DATA_MSG=Paket /data ya da /mnt/usbX dizininde değil
STR_CHECK_HTTP_MSG=Uzak HTTP Sunucusuna bağlantı kontrol ediliyor
STR_FAILED_HTTP_CHECK=HTTP Sunucusuna bağlanılamadı
STR_REMOTE_NOT_HTTP=Uzak bir HTTP Sunucusu değil
STR_INSTALL_FROM_DATA_MSG=Paket /data veya /mnt/usbX klasöründe değil
STR_ALREADY_INSTALLED_MSG=Paket zaten kurulu
STR_INSTALL_FROM_URL=URL'den Kur
STR_CANNOT_READ_PKG_HDR_MSG=Paket başlık bilgisi okunamadı
STR_FAVORITE_URLS=Favori URL'ler
STR_SLOT=Yuva
STR_EDIT=Düzenle
STR_ONETIME_URL=Tek Seferlik URL
STR_NOT_A_VALID_PACKAGE=Geçerli bir Paket değil
STR_WAIT_FOR_INSTALL_MSG=Paketin kurulmasının tamamlanması bekleniyor
STR_FAIL_INSTALL_TMP_PKG_MSG=Geçici paketin kurulumu başarısız. Lütfen geçici paketi manuel olarak silin
STR_AUTO_DELETE_TMP_PKG=Kurulumdan sonra geçici indirilen paketi otomatik olarak sil
STR_PROTOCOL_NOT_SUPPORTED=Protokol desteklenmiyor
STR_COULD_NOT_RESOLVE_HOST=Ana bilgisayar adı çözülemedi
STR_EXTRACT=Çıkart
STR_EXTRACTING=Çıkartılıyor
STR_FAILED_TO_EXTRACT=Çıkartma başarısız oldu
STR_EXTRACT_LOCATION=Çıkartılacak Konum
STR_COMPRESS=Sıkıştır
STR_ZIP_FILE_PATH=Zip Dosya Adı
STR_COMPRESSING=Sıkıştırılıyor
STR_ERROR_CREATE_ZIP=Zip oluşturulurken hata oluştu
STR_UNSUPPORTED_FILE_FORMAT=Desteklenmeyen sıkıştırılmış dosya formatı
STR_CUT=Kes
STR_COPY=Kopyala
STR_PASTE=Yapıştır
STR_MOVING=Taşınıyor
STR_COPYING=Kopyalanıyor
STR_FAIL_MOVE_MSG=Dosya taşınamadı
STR_FAIL_COPY_MSG=Dosya kopyalanamadı
STR_CANT_MOVE_TO_SUBDIR_MSG=Ana dizin alt dizinine taşınamaz
STR_CANT_COPY_TO_SUBDIR_MSG=Ana dizin alt dizinine kopyalanamaz
STR_UNSUPPORTED_OPERATION_MSG=Desteklenmeyen işlem
STR_HTTP_PORT=Http Portu
STR_REINSTALL_CONFIRM_MSG=İçerik zaten kurulu. Kuruluma devam etmek istiyor musunuz?
STR_REMOTE_NOT_SUPPORT_MSG=Korunan sunucular için uzak paket kurulumu desteklenmiyor.
STR_CANNOT_CONNECT_REMOTE_MSG=Uzak HTTP Sunucusuna bağlanılamıyor.
STR_DOWNLOAD_INSTALL_MSG=Uzak Paket Kurulumu mümkün değil. Paketi indirip kurmak ister misiniz?
STR_CHECKING_REMOTE_SERVER_MSG=Uzak Paket Kurulumu için uzak sunucu kontrol ediliyor.
STR_FILES=Dosyalar
STR_EDITOR=Düzenleyici
STR_SAVE=Kaydet
STR_MAX_EDIT_FILE_SIZE_MSG=Boyutu şu büyüklükten büyük dosyalar düzenlenemez
STR_DELETE_LINE=Seçili Satırı Sil
STR_INSERT_LINE=Seçili Satırın Altına Ekle
STR_MODIFIED=Değiştirildi
STR_FAIL_GET_TOKEN_MSG=Erişim token'ı alınamadı
STR_GET_TOKEN_SUCCESS_MSG=Giriş Başarılı. Tarayıcıyı kapatıp uygulamaya dönebilirsiniz
STR_NEW_FILE=Yeni Dosya
STR_SETTINGS=Ayarlar
STR_GLOBAL=Genel
STR_COPY_LINE=Seçili satırı kopyala
STR_PASTE_LINE=Seçili satıra yapıştır
STR_SHOW_HIDDEN_FILES=Gizli dosyaları göster
STR_SET_DEFAULT_DIRECTORY=Varsayılan Klasörü Ayarla
STR_SET_DEFAULT_DIRECTORY_MSG=varsayılan dizin olarak ayarlandı
STR_NFS_EXP_PATH_MISSING_MSG=URL'de NFS dışa aktarma yolu eksik
STR_FAIL_INIT_NFS_CONTEXT=NFS bağlamı başlatılamadı
STR_FAIL_MOUNT_NFS_MSG=NFS paylaşımı bağlanamadı
STR_VIEW_IMAGE=Görüntüyü Görüntüle
+133
View File
@@ -0,0 +1,133 @@
STR_CONNECTION_SETTINGS=Cài đặt kết nối
STR_SITE=Vị trí
STR_LOCAL=Thiết bị
STR_REMOTE=Máy đích
STR_MESSAGES=Tin nhắn
STR_UPDATE_SOFTWARE=Cập nhật phần mềm
STR_CONNECT=Kết nối
STR_DISCONNECT=Ngắt kết nối
STR_SEARCH=Tìm kiếm
STR_REFRESH=Làm mới
STR_SERVER=Máy chủ
STR_USERNAME=Tên đăng nhập
STR_PASSWORD=Mật khẩu
STR_PORT=Cổng
STR_PASV=Pasv
STR_DIRECTORY=Thư mục
STR_FILTER=Bộ lọc
STR_YES=Đồng ý
STR_NO=Không
STR_CANCEL=Hủy bỏ
STR_CONTINUE=Tiếp tục
STR_CLOSE=Đóng
STR_FOLDER=Thư mục
STR_FILE=Tập tin
STR_TYPE=Loại
STR_NAME=Tên
STR_SIZE=Kích thước
STR_DATE=Ngày tháng
STR_NEW_FOLDER=Thư mục mới
STR_RENAME=Đổi tên
STR_DELETE=Xóa
STR_UPLOAD=Tải lên
STR_DOWNLOAD=Tải xuống
STR_SELECT_ALL=Chọn tất cả
STR_CLEAR_ALL=Bỏ chọn tất cả
STR_UPLOADING=Đang tải lên
STR_DOWNLOADING=Đang tải xuống
STR_OVERWRITE=Ghi đè
STR_DONT_OVERWRITE=Không ghi đè
STR_ASK_FOR_CONFIRM=Hỏi xác nhận
STR_DONT_ASK_CONFIRM=Không hỏi xác nhận
STR_ALLWAYS_USE_OPTION=Luôn dùng tùy chọn này và không hỏi lại
STR_ACTIONS=Hành động
STR_CONFIRM=Xác nhận
STR_OVERWRITE_OPTIONS=Tùy chọn ghi đè
STR_PROPERTIES=Thuộc tính
STR_PROGRESS=Tiến trình
STR_UPDATES=Cập nhật
STR_DEL_CONFIRM_MSG=Bạn có chắc chắn muốn xóa (những) tập tin / thư mục này không?
STR_CANCEL_ACTION_MSG=Hủy bỏ. Chờ hành động cuối cùng hoàn tất.Canceling.
STR_FAIL_UPLOAD_MSG=Không thể tải file lên
STR_FAIL_DOWNLOAD_MSG=Không thể tải file
STR_FAIL_READ_LOCAL_DIR_MSG=Không đọc được nội dung của thư mục hoặc thư mục không tồn tại.
STR_CONNECTION_CLOSE_ERR_MSG=426 Kết nối đã đóng.
STR_REMOTE_TERM_CONN_MSG=426 Máy chủ đã ngắt kết nối.
STR_FAIL_LOGIN_MSG=300 Đăng nhập thất bại. Vui lòng kiểm tra lại tên đăng nhập hoặc mật khẩu.
STR_FAIL_TIMEOUT_MSG=426 Thất bại. Quá thời gian kết nối.
STR_FAIL_DEL_DIR_MSG=Không thể xóa thư mục
STR_DELETING=Đang xóa
STR_FAIL_DEL_FILE_MSG=Không thể xóa tập tin
STR_DELETED=Đã xóa
STR_LINK=Liên kết
STR_SHARE=Chia sẻ
STR_FAILED=310 Lỗi
STR_FAIL_CREATE_LOCAL_FILE_MSG=310 Không thể tạo file trên máy này
STR_INSTALL=Cài đặt
STR_INSTALLING=Đang cài đặt
STR_INSTALL_SUCCESS=Thành công
STR_INSTALL_FAILED=Thất bại
STR_INSTALL_SKIPPED=Bỏ qua
STR_CHECK_HTTP_MSG=Đang kiểm tra kết nối tới máy chủ HTTP
STR_FAILED_HTTP_CHECK=Không thể kết nối tới máy chủ HTTP
STR_REMOTE_NOT_HTTP=Máy đích không phải máy chủ HTTP
STR_INSTALL_FROM_DATA_MSG=Package không có trong thư mục /data hoặc /mnt/usbX
STR_ALREADY_INSTALLED_MSG=Package đã được cài đặt
STR_INSTALL_FROM_URL=Cài từ địa chỉ URL
STR_CANNOT_READ_PKG_HDR_MSG=Không thể đọc được thông tin header của package
STR_FAVORITE_URLS=URLs yêu thích
STR_SLOT=Vị trí
STR_EDIT=Sửa
STR_ONETIME_URL=Url một lần
STR_NOT_A_VALID_PACKAGE=Package không đúng
STR_WAIT_FOR_INSTALL_MSG=Đợi Package kết thúc cài đặt
STR_FAIL_INSTALL_TMP_PKG_MSG=Không thể cài file pkg. Vui lòng xóa tmp pkg thủ công
STR_AUTO_DELETE_TMP_PKG=Tự động xóa file pkg được tải về tạm thời sau khi cài xong
STR_PROTOCOL_NOT_SUPPORTED=Protocol không được hỗ trợ
STR_COULD_NOT_RESOLVE_HOST=Không thể phân giải tên máy chủ
STR_EXTRACT=Giải nén
STR_EXTRACTING=Đang giải nén
STR_FAILED_TO_EXTRACT=Giải nén thất bại
STR_EXTRACT_LOCATION=Vị trí giải nén
STR_COMPRESS=Nén
STR_ZIP_FILE_PATH=Tên file Zip
STR_COMPRESSING=Đang nén
STR_ERROR_CREATE_ZIP=Lỗi khi tạo file zip
STR_UNSUPPORTED_FILE_FORMAT=Phần mở rộng file không được hỗ trợ
STR_CUT=Cắt
STR_COPY=Sao chép
STR_PASTE=Dán
STR_MOVING=Đang di chuyển
STR_COPYING=Đang copy
STR_FAIL_MOVE_MSG=Không thể di chuyển file
STR_FAIL_COPY_MSG=Không thể sao chép file
STR_CANT_MOVE_TO_SUBDIR_MSG=Không thể di chuyển thư mục cha vào bên trong thư mục con
STR_CANT_COPY_TO_SUBDIR_MSG=Không thể sao chép thư mục cha vào bên trong thư mục con
STR_UNSUPPORTED_OPERATION_MSG=Hành động không được hỗ trợ
STR_HTTP_PORT=Cổng Http
STR_REINSTALL_CONFIRM_MSG=Nội dung đã được cài đặt. Bạn có muốn tiếp tục cài đặt không?
STR_REMOTE_NOT_SUPPORT_MSG=Cài đặt gói từ xa không được hỗ trợ cho các máy chủ được bảo vệ.
STR_CANNOT_CONNECT_REMOTE_MSG=Máy chủ HTTP đích không thể truy cập được.
STR_DOWNLOAD_INSTALL_MSG=Không thể cài đặt package từ xa. Bạn có muốn tải package xuống và cài đặt không?
STR_CHECKING_REMOTE_SERVER_MSG=Đang kiểm tra máy chủ đích để cài đặt package từ xa.
STR_FILES=Tập tin
STR_EDITOR=Trình soạn thảo
STR_SAVE=Lưu
STR_MAX_EDIT_FILE_SIZE_MSG=Không thể chỉnh sửa các tập tin lớn hơn
STR_DELETE_LINE=Xóa dòng đã chọn
STR_INSERT_LINE=Chèn bên dưới dòng đã chọn
STR_MODIFIED=Đã sửa đổi
STR_FAIL_GET_TOKEN_MSG=Không thể lấy được access token từ
STR_GET_TOKEN_SUCCESS_MSG=Đăng nhập thành công. Bạn có thể đóng trình duyệt và quay lại ứng dụng
STR_NEW_FILE=Tập tin mới
STR_SETTINGS=Cài đặt
STR_GLOBAL=Toàn cục
STR_COPY_LINE=Sao chép dòng đã chọn
STR_PASTE_LINE=Dán vào dòng đã chọn
STR_SHOW_HIDDEN_FILES=Hiển thị các tập tin ẩn
STR_SET_DEFAULT_DIRECTORY=Đặt thư mục mặc định
STR_SET_DEFAULT_DIRECTORY_MSG=đã được thiết lập làm thư mục mặc định
STR_NFS_EXP_PATH_MISSING_MSG=Đường dẫn NFS bị thiếu trong URL
STR_FAIL_INIT_NFS_CONTEXT=Không khởi tạo được ngữ cảnh NFS
STR_FAIL_MOUNT_NFS_MSG=Không thể mount NFS share
STR_PROMOTING=Promoting
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Submodule ps4-ezremote-server added at 7644256a3c
+289 -86
View File
@@ -1,6 +1,7 @@
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <json-c/json.h>
#include <lexbor/html/parser.h>
#include <lexbor/dom/interfaces/element.h>
#include <minizip/unzip.h>
@@ -8,8 +9,11 @@
#include "clients/gdrive.h"
#include "clients/ftpclient.h"
#include "clients/smbclient.h"
#include "clients/webdavclient.h"
#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"
#include "clients/nfsclient.h"
@@ -17,6 +21,7 @@
#include "clients/rclone.h"
#include "clients/sftpclient.h"
#include "filehost/filehost.h"
#include "server/http_server.h"
#include "common.h"
#include "fs.h"
#include "config.h"
@@ -25,8 +30,6 @@
#include "lang.h"
#include "actions.h"
#include "installer.h"
#include "web/request.hpp"
#include "web/urn.hpp"
#include "system.h"
#include "sfo.h"
#include "zip_util.h"
@@ -366,6 +369,7 @@ namespace Actions
if (confirm_state == CONFIRM_YES)
{
sceRtcGetCurrentTick(&prev_tick);
return remoteclient->Put(src, dest);
}
@@ -411,6 +415,7 @@ namespace Actions
snprintf(activity_message, 1024, "%s %s", lang_strings[STR_UPLOADING], entries[i].path);
bytes_to_download = entries[i].file_size;
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
ret = UploadFile(entries[i].path, new_path);
if (ret <= 0)
{
@@ -485,9 +490,47 @@ namespace Actions
}
}
int BackgroundDownload(const char *src, const char *dest, uint64_t file_size)
{
uint64_t id = Util::GetTick();
json_object *params = json_object_new_object();
json_object_object_add(params, "type", json_object_new_int(remote_settings->type));
json_object_object_add(params, "url", json_object_new_string(remote_settings->server));
json_object_object_add(params, "username", json_object_new_string(remote_settings->username));
json_object_object_add(params, "password", json_object_new_string(remote_settings->password));
json_object_object_add(params, "src_path", json_object_new_string(src));
json_object_object_add(params, "dest_path", json_object_new_string(dest));
json_object_object_add(params, "size", json_object_new_uint64(file_size));
json_object_object_add(params, "id", json_object_new_uint64(id));
if (remote_settings->type == CLIENT_TYPE_HTTP_SERVER)
{
json_object_object_add(params, "http_server_type", json_object_new_string(remote_settings->http_server_type));
}
const char *params_str = json_object_to_json_string(params);
httplib::Client tmp_client = httplib::Client("http://127.0.0.1:" + std::to_string(http_int_server_port));
if (auto res = tmp_client.Post("/download_url", params_str, strlen(params_str), "application/json"))
{
if (HTTP_SUCCESS(res->status))
{
Util::Notify("%s queued for download", src);
return 1;
}
else
{
Util::Notify("Failed to queue %s for download in background", src);
return 0;
}
}
return 0;
}
int DownloadFile(const char *src, const char *dest)
{
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
if (!remoteclient->Size(src, &bytes_to_download))
{
remoteclient->Quit();
@@ -519,6 +562,11 @@ namespace Actions
if (confirm_state == CONFIRM_YES)
{
sceRtcGetCurrentTick(&prev_tick);
if (enable_background_download && bytes_to_download > minimum_backgrond_file_size)
{
return BackgroundDownload(src, dest, bytes_to_download);
}
return remoteclient->Get(dest, src);
}
@@ -647,39 +695,6 @@ namespace Actions
else
files.push_back(selected_remote_file);
bool download_and_install = false;
if (remote_settings->enable_rpi)
{
std::string url = INSTALLER::getRemoteUrl(files.begin()->path);
sprintf(activity_message, "%s", lang_strings[STR_CHECKING_REMOTE_SERVER_MSG]);
file_transfering = false;
if (!INSTALLER::canInstallRemotePkg(url))
{
confirm_state = CONFIRM_WAIT;
action_to_take = selected_action;
activity_inprogess = false;
while (confirm_state == CONFIRM_WAIT)
{
sceKernelUsleep(100000);
}
activity_inprogess = true;
selected_action = action_to_take;
if (confirm_state == CONFIRM_YES)
{
download_and_install = true;
}
else
{
goto finish;
}
}
}
else
{
download_and_install = true;
}
for (std::vector<DirEntry>::iterator it = files.begin(); it != files.end(); ++it)
{
if (stop_activity)
@@ -701,7 +716,7 @@ namespace Actions
{
if (BE32(header.pkg_magic) == PKG_MAGIC)
{
if (download_and_install)
if (!remote_settings->enable_rpi)
{
if (DownloadAndInstallPkg(it->path, &header) == 0)
failed++;
@@ -710,38 +725,78 @@ namespace Actions
}
else
{
std::string url = INSTALLER::getRemoteUrl(it->path, true);
std::string title = INSTALLER::GetRemotePkgTitle(remoteclient, it->path, &header);
if (remote_settings->enable_disk_cache)
{
SplitPkgInstallData *install_data = (SplitPkgInstallData*) malloc(sizeof(SplitPkgInstallData));
memset(install_data, 0, sizeof(SplitPkgInstallData));
if (INSTALLER::InstallRemotePkg(url, &header, title, true) == 0)
failed++;
OrbisTick tick;
sceRtcGetCurrentTick(&tick);
std::string install_pkg_path = std::string(temp_folder) + "/" + std::to_string(tick.mytick) + ".pkg";
SplitFile *sp = new SplitFile(install_pkg_path, INSTALL_ARCHIVE_PKG_SPLIT_SIZE/2);
install_data->split_file = sp;
install_data->remote_client = INSTALLER::GetRemoteClient(remote_settings);
install_data->path = it->path;
remoteclient->Size(it->path, &install_data->size);
install_data->stop_write_thread = false;
install_data->delete_client = true;
int ret = pthread_create(&install_data->thread, NULL, DownloadSplitPkg, install_data);
if (INSTALLER::InstallSplitPkg(it->path, install_data, false) == 0)
failed++;
else
success++;
}
else
success++;
{
std::string url = INSTALLER::getRemoteUrl(it->path, true);
std::string title = INSTALLER::GetRemotePkgTitle(remoteclient, it->path, &header);
if (INSTALLER::InstallRemotePkg(url, &header, title) == 0)
failed++;
else
success++;
if (it != files.end())
{
sleep(3);
}
}
}
}
else
skipped++;
}
}
else if (Util::EndsWith(path,".zip") || Util::EndsWith(path,".rar") ||
else if (Util::EndsWith(path,".zip") || Util::EndsWith(path,".rar") || Util::EndsWith(path,".7z") ||
Util::EndsWith(path,".tar.xz") || Util::EndsWith(path,".tar.gz"))
{
ArchiveEntry *entry = ZipUtil::GetPackageEntry(it->path, true);
ArchiveEntry *entry = ZipUtil::GetPackageEntry(it->path, remoteclient);
if (entry != nullptr)
{
ArchivePkgInstallData *install_data = (ArchivePkgInstallData*) malloc(sizeof(ArchivePkgInstallData));
memset(install_data, 0, sizeof(ArchivePkgInstallData));
while (entry != nullptr)
{
snprintf(activity_message, 1023, "%s %s", lang_strings[STR_INSTALLING], entry->filename.c_str());
std::string install_pkg_path = std::string(temp_folder) + "/" + entry->filename;
SplitFile *sp = new SplitFile(install_pkg_path, INSTALL_ARCHIVE_PKG_SPLIT_SIZE);
install_data->archive_entry = entry;
install_data->split_file = sp;
install_data->stop_write_thread = false;
ArchivePkgInstallData *install_data = (ArchivePkgInstallData*) malloc(sizeof(ArchivePkgInstallData));
memset(install_data, 0, sizeof(ArchivePkgInstallData));
int res = pthread_create(&install_data->thread, NULL, ExtractArchivePkg, install_data);
std::string install_pkg_path = std::string(temp_folder) + "/" + entry->filename;
SplitFile *sp = new SplitFile(install_pkg_path, INSTALL_ARCHIVE_PKG_SPLIT_SIZE);
install_data->archive_entry = entry;
install_data->split_file = sp;
install_data->stop_write_thread = false;
INSTALLER::InstallArchivePkg(entry->filename, install_data);
int res = pthread_create(&install_data->thread, NULL, ExtractArchivePkg, install_data);
INSTALLER::InstallArchivePkg(entry->filename, install_data);
ArchiveEntry *previos = entry;
entry = ZipUtil::GetNextPackageEntry(entry);
free(previos);
}
success++;
}
else
@@ -771,6 +826,7 @@ namespace Actions
if (res != 0)
{
activity_inprogess = false;
file_transfering = false;
multi_selected_remote_files.clear();
Windows::SetModalMode(false);
}
@@ -779,7 +835,7 @@ namespace Actions
void *ExtractArchivePkg(void *argp)
{
ssize_t len;
char buffer[ARCHIVE_TRANSFER_SIZE];
char *buffer = (char*) malloc(ARCHIVE_TRANSFER_SIZE);
ArchivePkgInstallData *install_data = (ArchivePkgInstallData*) argp;
SplitFile *sp = install_data->split_file;
@@ -788,7 +844,7 @@ namespace Actions
sp->Open();
while (!install_data->stop_write_thread)
{
len = archive_read_data(install_data->archive_entry->archive, buffer, sizeof buffer);
len = archive_read_data(install_data->archive_entry->archive, buffer, ARCHIVE_TRANSFER_SIZE);
if (len == 0)
break;
@@ -807,7 +863,19 @@ namespace Actions
}
sp->Close();
free(buffer);
return NULL;
}
void *DownloadSplitPkg(void *argp)
{
SplitPkgInstallData *install_data = (SplitPkgInstallData*) argp;
SplitFile *sp = install_data->split_file;
/* loop over file contents and write to fd */
sp->Open();
install_data->remote_client->Get(sp, install_data->path);
sp->Close();
return NULL;
}
@@ -871,19 +939,26 @@ namespace Actions
ArchiveEntry *entry = ZipUtil::GetPackageEntry(it->path);
if (entry != nullptr)
{
ArchivePkgInstallData *install_data = (ArchivePkgInstallData*) malloc(sizeof(ArchivePkgInstallData));
memset(install_data, 0, sizeof(ArchivePkgInstallData));
while (entry != nullptr)
{
ArchivePkgInstallData *install_data = (ArchivePkgInstallData*) malloc(sizeof(ArchivePkgInstallData));
memset(install_data, 0, sizeof(ArchivePkgInstallData));
std::string install_pkg_path = std::string(temp_folder) + "/" + entry->filename;
SplitFile *sp = new SplitFile(install_pkg_path, INSTALL_ARCHIVE_PKG_SPLIT_SIZE);
install_data->archive_entry = entry;
install_data->split_file = sp;
install_data->stop_write_thread = false;
std::string install_pkg_path = std::string(temp_folder) + "/" + entry->filename;
SplitFile *sp = new SplitFile(install_pkg_path, INSTALL_ARCHIVE_PKG_SPLIT_SIZE);
install_data->archive_entry = entry;
install_data->split_file = sp;
install_data->stop_write_thread = false;
int res = pthread_create(&install_data->thread, NULL, ExtractArchivePkg, install_data);
int res = pthread_create(&install_data->thread, NULL, ExtractArchivePkg, install_data);
INSTALLER::InstallArchivePkg(entry->filename, install_data);
INSTALLER::InstallArchivePkg(entry->filename, install_data);
ArchiveEntry *previous = entry;
entry = ZipUtil::GetNextPackageEntry(entry);
free(previous);
}
success++;
}
else
@@ -975,7 +1050,7 @@ namespace Actions
break;
if (!it->isDir)
{
int ret = ZipUtil::Extract(*it, extract_zip_folder, true);
int ret = ZipUtil::Extract(*it, extract_zip_folder, remoteclient);
if (ret == 0)
{
sprintf(status_message, "%s %s", lang_strings[STR_FAILED_TO_EXTRACT], it->name);
@@ -1051,9 +1126,10 @@ namespace Actions
}
}
void *InstallUrlPkgThread(void *argp)
void *InstallLocalUrlPkgThread(void *argp)
{
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
sprintf(status_message, "%s", "");
pkg_header header;
char filename[2000];
@@ -1061,10 +1137,10 @@ namespace Actions
OrbisTick tick;
sceRtcGetCurrentClockLocalTime(&now);
sceRtcGetTick(&now, &tick);
sprintf(filename, "%s/%lu.pkg", DATA_PATH, tick.mytick);
sprintf(filename, "%s/%lu.pkg", TMP_FOLDER_PATH, tick.mytick);
std::string full_url = std::string(install_pkg_url.url);
FileHost *filehost = FileHost::getFileHost(full_url);
FileHost *filehost = FileHost::getFileHost(full_url, install_pkg_url.enable_alldebrid, install_pkg_url.enable_realdebrid);
if (!filehost->IsValidUrl())
{
sprintf(status_message, "%s", lang_strings[STR_FAIL_TO_OBTAIN_GG_DL_MSG]);
@@ -1087,30 +1163,25 @@ namespace Actions
std::string host = full_url.substr(0, path_pos);
std::string path = full_url.substr(path_pos);
WebDAV::WebDavClient tmp_client;
tmp_client.Connect(host.c_str(), install_pkg_url.username, install_pkg_url.password, false);
BaseClient tmp_client;
tmp_client.Connect(host.c_str(), install_pkg_url.username, install_pkg_url.password);
sprintf(activity_message, "%s URL to %s", lang_strings[STR_DOWNLOADING], filename);
int s = sizeof(pkg_header);
memset(&header, 0, s);
WebDAV::dict_t response_headers{};
int ret = tmp_client.GetHeaders(path.c_str(), &response_headers);
if (!ret)
int ret = tmp_client.Size(path, &bytes_to_download);
if (ret == 0)
{
sprintf(status_message, "%s - %s", lang_strings[STR_FAILED], lang_strings[STR_CANNOT_READ_PKG_HDR_MSG]);
sprintf(status_message, "%s", tmp_client.LastResponse());
tmp_client.Quit();
activity_inprogess = false;
Windows::SetModalMode(false);
return NULL;
}
auto content_length = WebDAV::get(response_headers, "content-length");
if (content_length.length() > 0)
bytes_to_download = std::stol(content_length);
else
bytes_to_download = 1;
file_transfering = 1;
int is_performed = tmp_client.Get(filename, path.c_str());
int is_performed = tmp_client.Get(filename, path);
if (is_performed == 0)
{
@@ -1163,10 +1234,70 @@ namespace Actions
return NULL;
}
void *InstallRpiUrlPkgThread(void *argp)
{
json_object *params = json_object_new_object();
json_object_object_add(params, "url", json_object_new_string(install_pkg_url.url));
json_object_object_add(params, "use_alldebrid", json_object_new_boolean(install_pkg_url.enable_alldebrid));
json_object_object_add(params, "use_realdebrid", json_object_new_boolean(install_pkg_url.enable_realdebrid));
json_object_object_add(params, "use_disk_cache", json_object_new_boolean(install_pkg_url.enable_disk_cache));
const char *params_str = json_object_to_json_string(params);
char host[128];
sprintf(host, "http://127.0.0.1:%d", http_server_port);
httplib::Client tmp_client(host);
tmp_client.set_keep_alive(true);
tmp_client.set_follow_location(true);
tmp_client.set_connection_timeout(30);
tmp_client.set_read_timeout(30);
tmp_client.enable_server_certificate_verification(false);
auto res = tmp_client.Post("/__local__/install_url", params_str, strlen(params_str), "application/json");
if (res != nullptr && HTTP_SUCCESS(res->status))
{
json_object *jobj = json_tokener_parse(res->body.c_str());
if (jobj != nullptr)
{
json_object *result = json_object_object_get(jobj, "result");
if (result != nullptr)
{
bool success = json_object_get_boolean(json_object_object_get(result, "success"));
if (!success)
{
const char* error_message = json_object_get_string(json_object_object_get(result, "error"));
sprintf(status_message, "%s", error_message);
activity_inprogess = false;
Windows::SetModalMode(false);
}
}
}
else
{
activity_inprogess = false;
Windows::SetModalMode(false);
}
}
else
{
activity_inprogess = false;
Windows::SetModalMode(false);
}
return NULL;
}
void InstallUrlPkg()
{
int res;
sprintf(status_message, "%s", "");
int res = pthread_create(&bk_activity_thid, NULL, InstallUrlPkgThread, NULL);
if (!install_pkg_url.enable_rpi)
res = pthread_create(&bk_activity_thid, NULL, InstallLocalUrlPkgThread, NULL);
else
res = pthread_create(&bk_activity_thid, NULL, InstallRpiUrlPkgThread, NULL);
if (res != 0)
{
activity_inprogess = false;
@@ -1195,10 +1326,16 @@ namespace Actions
remoteclient = new NpxServeClient();
else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_RCLONE) == 0)
remoteclient = new RCloneClient();
else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_ARCHIVEORG) == 0)
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)
{
remoteclient = new WebDAV::WebDavClient();
remoteclient = new WebDAVClient();
}
else if (strncmp(remote_settings->server, "smb://", 6) == 0)
{
@@ -1329,6 +1466,7 @@ namespace Actions
if (confirm_state == CONFIRM_YES)
{
sceRtcGetCurrentTick(&prev_tick);
if (isCopy)
return FS::Copy(src, dest);
else
@@ -1347,6 +1485,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++)
@@ -1361,9 +1509,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)
{
@@ -1376,6 +1526,7 @@ namespace Actions
snprintf(activity_message, 1024, "%s %s", isCopy ? lang_strings[STR_COPYING] : lang_strings[STR_MOVING], entries[i].path);
bytes_to_download = entries[i].file_size;
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
ret = CopyOrMoveLocalFile(entries[i].path, new_path, isCopy);
if (ret <= 0)
{
@@ -1386,6 +1537,11 @@ namespace Actions
}
free(new_path);
}
if (!isCopy && FS::FolderExists(src.path))
{
FS::RmDir(src.path);
}
}
else
{
@@ -1427,7 +1583,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
{
@@ -1530,6 +1685,7 @@ namespace Actions
if (confirm_state == CONFIRM_YES)
{
sceRtcGetCurrentTick(&prev_tick);
if (isCopy)
return remoteclient->Copy(src, dest);
else
@@ -1625,6 +1781,7 @@ namespace Actions
snprintf(activity_message, 1024, "%s %s", lang_strings[STR_COPYING], entries[i].path);
bytes_to_download = entries[i].file_size;
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
ret = CopyOrMoveRemoteFile(entries[i].path, new_path, true);
if (ret <= 0)
{
@@ -1718,6 +1875,7 @@ namespace Actions
sprintf(activity_message, "%s %s to %s", lang_strings[STR_DOWNLOADING], filename.c_str(), local_file);
remoteclient->Size(filename, &bytes_to_download);
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
file_transfering = true;
int ret = remoteclient->Get(local_file, filename);
if (ret == 0)
@@ -1768,4 +1926,49 @@ namespace Actions
sprintf(remote_file_to_select, "%s", temp_file.c_str());
}
void RestartServer()
{
INSTALLER::StopEzRemoteServer();
sleep(2);
INSTALLER::StartEzRemoteServer();
sleep(2);
}
void GetBackgroundDownloadProgress()
{
httplib::Client client = httplib::Client("http://127.0.0.1:" + std::to_string(http_int_server_port));
if (auto res = client.Get("/get_download_state"))
{
if (HTTP_SUCCESS(res->status))
{
bg_download_progress.clear();
json_object *jobj = json_tokener_parse(res->body.c_str());
if (jobj != nullptr)
{
struct array_list *progress_list = json_object_get_array(jobj);
for (size_t idx = 0; idx < progress_list->length; ++idx)
{
DownloadProgress progress;
json_object *progress_obj = (json_object *)array_list_get_idx(progress_list, idx);
progress.path = json_object_get_string(json_object_object_get(progress_obj, "path"));
progress.bytes_transfered = json_object_get_uint64(json_object_object_get(progress_obj, "bytes_transfered"));
progress.file_size = json_object_get_uint64(json_object_object_get(progress_obj, "file_size"));
progress.state = state_strings[json_object_get_int(json_object_object_get(progress_obj, "state"))];
progress.timestamp = json_object_get_uint64(json_object_object_get(progress_obj, "timestamp"));
bg_download_progress.push_back(progress);
}
json_object_put(jobj);
}
}
else
{
snprintf(status_message, 1024, "Failed to get the download progress from ezRemote Server");
}
}
}
}
+8 -4
View File
@@ -1,5 +1,5 @@
#ifndef ACTIONS_H
#define ACTIONS_H
#ifndef EZ_ACTIONS_H
#define EZ_ACTIONS_H
#include <pthread.h>
#include "installer.h"
@@ -99,7 +99,8 @@ namespace Actions
void InstallRemotePkgs();
void *InstallLocalPkgsThread(void *argp);
void InstallLocalPkgs();
void *InstallUrlPkgThread(void *argp);
void *InstallLocalUrlPkgThread(void *argp);
void *InstallRpiUrlPkgThread(void *argp);
void InstallUrlPkg();
void *KeepAliveThread(void *argp);
void *ExtractZipThread(void *argp);
@@ -120,6 +121,9 @@ namespace Actions
void CreateLocalFile(char *filename);
void CreateRemoteFile(char *filename);
void *ExtractArchivePkg(void *argp);
void *DownloadSplitPkg(void *argp);
void RestartServer();
void GetBackgroundDownloadProgress();
}
#endif
#endif
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef BASE64_H_
#define BASE64_H_
#ifndef EZ_BASE64_H_
#define EZ_BASE64_H_
#include <string>
+23 -10
View File
@@ -128,18 +128,31 @@ std::vector<DirEntry> ApacheClient::ListDir(const std::string &path)
tmp_string = std::string((const char *)value, value_len);
if (tmp_string.compare("td") == 0)
{
value = lxb_dom_node_text_content(node, &value_len);
// get the child <a> element
lxb_dom_node_t *a_node = NextChildElement(lxb_dom_interface_element(node));
if (a_node == nullptr) continue;
value = lxb_dom_element_local_name(lxb_dom_interface_element(a_node), &value_len);
tmp_string = std::string((const char *)value, value_len);
tmp_string = Util::Rtrim(tmp_string, "/");
sprintf(entry.name, "%s", tmp_string.c_str());
sprintf(entry.directory, "%s", path.c_str());
if (path.length() > 0 && path[path.length() - 1] == '/')
if (tmp_string.compare("a") == 0)
{
sprintf(entry.path, "%s%s", path.c_str(), entry.name);
}
else
{
sprintf(entry.path, "%s/%s", path.c_str(), entry.name);
value = lxb_dom_element_get_attribute(lxb_dom_interface_element(a_node), (const lxb_char_t *)"href", 4, &value_len);
tmp_string = std::string((const char *)value, value_len);
tmp_string = Util::Rtrim(tmp_string, "/");
tmp_string = BaseClient::UnEscape(tmp_string);
if (tmp_string.compare("..") != 0)
{
sprintf(entry.directory, "%s", path.c_str());
sprintf(entry.name, "%s", tmp_string.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);
}
}
}
}
else continue; // not valid record
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef APACHE_H
#define APACHE_H
#ifndef EZ_APACHE_H
#define EZ_APACHE_H
#include <string>
#include <vector>
+362
View File
@@ -0,0 +1,362 @@
#include <lexbor/html/parser.h>
#include <lexbor/dom/interfaces/element.h>
#include <lexbor/dom/interfaces/node.h>
#include <fstream>
#include <map>
#include "common.h"
#include "clients/remote_client.h"
#include "clients/archiveorg.h"
#include "lang.h"
#include "util.h"
#include "system.h"
#include "windows.h"
using httplib::Client;
using httplib::Headers;
using httplib::Result;
struct InsensitiveCompare
{
bool operator()(const std::string &a, const std::string &b) const
{
return strcasecmp(a.c_str(), b.c_str()) < 0;
}
};
static std::map<std::string, int> month_map = {{"Jan", 1}, {"Feb", 2}, {"Mar", 3}, {"Apr", 4}, {"May", 5}, {"Jun", 6}, {"Jul", 7}, {"Aug", 8}, {"Sep", 9}, {"Oct", 10}, {"Nov", 11}, {"Dec", 12}};
static std::set<std::string, InsensitiveCompare> ignore_cookie_keys = {"path", "expires", "max-age", "domain", "secure"};
std::string ArchiveOrgClient::GenerateRandomId(const int len)
{
static const char alphanum[] = "0123456789abcdef";
std::string tmp_s;
tmp_s.reserve(len);
for (int i = 0; i < len; ++i) {
tmp_s += alphanum[rand() % (sizeof(alphanum) - 1)];
}
return tmp_s;
}
int ArchiveOrgClient::Connect(const std::string &url, const std::string &username, const std::string &password, bool send_ping)
{
this->host_url = url;
size_t scheme_pos = url.find("://");
size_t root_pos = url.find("/", scheme_pos + 3);
if (root_pos != std::string::npos)
{
this->host_url = url.substr(0, root_pos);
this->base_path = url.substr(root_pos);
}
client = new httplib::Client(this->host_url);
client->set_keep_alive(true);
client->set_follow_location(true);
client->set_connection_timeout(30);
client->set_read_timeout(30);
client->enable_server_certificate_verification(false);
this->cookies = {
{"donation-identifier", GenerateRandomId(32)},
{"test-cookie", "1"},
{"abtest-identifier", GenerateRandomId(32)}
};
if (username.length() > 0)
return Login(username, password);
else if (!send_ping)
this->connected = true;
else if (Ping())
this->connected = true;
return 1;
}
int ArchiveOrgClient::Login(const std::string &username, const std::string &password)
{
std::string url = std::string("/account/login");
Headers headers = {{ "User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0"}};
SetCookies(headers);
MultipartFormDataItems items = {
{"username", username, "", ""},
{"password", password, "", ""},
{"remember", "true", "", ""},
{"referer", "https://archive.org/", "", ""},
{"login", "true", "", ""},
{"submit_by_js", "true", "", ""}};
if (auto res = client->Post(url, headers, items))
{
if (HTTP_SUCCESS(res->status))
{
if (res->has_header("Set-Cookie"))
{
int cookies_count = res->get_header_value_count("Set-Cookie");
for (int i = 0; i < cookies_count; i++)
{
std::string cookie_str = res->get_header_value("Set-Cookie", i);
std::vector<std::string> cookies = Util::Split(cookie_str, ";");
for (std::vector<std::string>::iterator it = cookies.begin(); it != cookies.end();)
{
std::vector<std::string> cookie = Util::Split(*it, "=");
std::string key = Util::Trim(cookie[0], " ");
if (ignore_cookie_keys.find(key) == ignore_cookie_keys.end())
{
if (cookie.size() > 1)
this->cookies[key] = Util::Trim(cookie[1], " ");
else
this->cookies[key] = "";
}
++it;
}
}
this->connected = true;
return 1;
}
else
{
return 0;
}
}
else
{
return 0;
}
}
else
{
return 0;
}
}
std::vector<DirEntry> ArchiveOrgClient::ListDir(const std::string &path)
{
std::vector<DirEntry> out;
DirEntry entry;
Util::SetupPreviousFolder(path, &entry);
out.push_back(entry);
Headers headers;
SetCookies(headers);
std::string encoded_path = httplib::detail::encode_url(GetFullPath(path) + "/");
if (auto res = client->Get(encoded_path, headers))
{
lxb_status_t status;
lxb_dom_attr_t *attr;
lxb_dom_element_t *table_element, *tr_element, *td_element;
lxb_html_document_t *document;
lxb_dom_collection_t *table_collection;
lxb_dom_collection_t *tr_collection;
lxb_dom_collection_t *td_collection;
std::string tmp_string;
const lxb_char_t *value;
size_t value_len;
document = lxb_html_document_create();
status = lxb_html_document_parse(document, (lxb_char_t *)res->body.c_str(), res->body.length());
if (status != LXB_STATUS_OK)
{
lxb_html_document_destroy(document);
goto finish;
}
table_collection = lxb_dom_collection_make(&document->dom_document, 1);
if (table_collection == NULL)
{
lxb_html_document_destroy(document);
goto finish;
}
tr_collection = lxb_dom_collection_make(&document->dom_document, 128);
if (tr_collection == NULL)
{
lxb_html_document_destroy(document);
goto finish;
}
status = lxb_dom_elements_by_tag_name(lxb_dom_interface_element(document->body),
table_collection, (const lxb_char_t *)"table", 5);
if (status != LXB_STATUS_OK)
{
lxb_dom_collection_destroy(tr_collection, true);
lxb_dom_collection_destroy(table_collection, true);
lxb_html_document_destroy(document);
goto finish;
}
if (lxb_dom_collection_length(table_collection) < 1)
{
lxb_dom_collection_destroy(tr_collection, true);
lxb_dom_collection_destroy(table_collection, true);
lxb_html_document_destroy(document);
goto finish;
}
for (size_t i = 0; i < lxb_dom_collection_length(table_collection); i++)
{
table_element = lxb_dom_collection_element(table_collection, i);
value = lxb_dom_element_class(table_element, &value_len);
tmp_string = std::string((const char *)value, value_len);
if (tmp_string.compare("directory-listing-table") == 0)
break;
table_element = nullptr;
}
if (table_element == nullptr)
{
lxb_dom_collection_destroy(tr_collection, true);
lxb_dom_collection_destroy(table_collection, true);
lxb_html_document_destroy(document);
goto finish;
}
status = lxb_dom_elements_by_tag_name(table_element,
tr_collection, (const lxb_char_t *)"tr", 2);
if (status != LXB_STATUS_OK && lxb_dom_collection_length(tr_collection) < 2)
{
lxb_dom_collection_destroy(tr_collection, true);
lxb_dom_collection_destroy(table_collection, true);
lxb_html_document_destroy(document);
goto finish;
}
// skip row 0 , since it has the previous folder header
for (size_t i = 2; i < lxb_dom_collection_length(tr_collection); i++)
{
DirEntry entry;
std::string title, aclass;
memset(&entry.modified, 0, sizeof(DateTime));
tr_element = lxb_dom_collection_element(tr_collection, i);
td_collection = lxb_dom_collection_make(&document->dom_document, 5);
status = lxb_dom_elements_by_tag_name(tr_element,
td_collection, (const lxb_char_t *)"td", 2);
if (status != LXB_STATUS_OK || lxb_dom_collection_length(td_collection) < 3)
{
lxb_dom_collection_destroy(td_collection, true);
lxb_dom_collection_destroy(tr_collection, true);
lxb_dom_collection_destroy(table_collection, true);
lxb_html_document_destroy(document);
goto finish;
}
// td0 contains the <a> tag
td_element = lxb_dom_collection_element(td_collection, 0);
lxb_dom_node_t *a_node = NextChildElement(td_element);
// there is no a_node in protected links
if (a_node == nullptr)
{
lxb_dom_collection_destroy(td_collection, true);
continue;
}
value = lxb_dom_element_local_name(lxb_dom_interface_element(a_node), &value_len);
tmp_string = std::string((const char *)value, value_len);
if (tmp_string.compare("a") != 0)
{
lxb_dom_collection_destroy(td_collection, true);
lxb_dom_collection_destroy(tr_collection, true);
lxb_dom_collection_destroy(table_collection, true);
lxb_html_document_destroy(document);
goto finish;
}
value = lxb_dom_element_get_attribute(lxb_dom_interface_element(a_node), (const lxb_char_t *)"href", 4, &value_len);
tmp_string = std::string((const char *)value, value_len);
if (tmp_string[tmp_string.length()-1] == '/')
tmp_string = tmp_string.substr(0, tmp_string.length()-1);
tmp_string = BaseClient::UnEscape(tmp_string);
sprintf(entry.name, "%s", tmp_string.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);
}
// next td contains the date
td_element = lxb_dom_collection_element(td_collection, 1);
value = lxb_dom_node_text_content(NextChildTextNode(td_element), &value_len);
tmp_string = std::string((const char *)value, value_len);
std::vector<std::string> date_time = Util::Split(tmp_string, " ");
if (date_time.size() > 1)
{
std::vector<std::string> adate = Util::Split(date_time[0], "-");
if (adate.size() == 3)
{
entry.modified.day = atoi(adate[0].c_str());
entry.modified.month = month_map[adate[1]];
entry.modified.year = atoi(adate[2].c_str());
}
std::vector<std::string> atime = Util::Split(date_time[1], ":");
if (atime.size() == 2)
{
entry.modified.hours = atoi(atime[0].c_str());
entry.modified.minutes = atoi(atime[1].c_str());
}
}
// next td contains file size, if fize size is "-", then it's a directory
td_element = lxb_dom_collection_element(td_collection, 2);
value = lxb_dom_node_text_content(NextChildTextNode(td_element), &value_len);
tmp_string = std::string((const char *)value, value_len);
if (tmp_string.compare("-") == 0)
{
entry.isDir = true;
entry.selectable = true;
entry.file_size = 0;
sprintf(entry.display_size, "%s", lang_strings[STR_FOLDER]);
}
else
{
entry.isDir = false;
entry.selectable = true;
uint64_t multiplier = 0;
std::string size_formatted = tmp_string.substr(0, tmp_string.size()-1);
Util::ReplaceAll(size_formatted, ",", "");
float fsize = std::stof(size_formatted);
switch (tmp_string[tmp_string.size()-1]) {
case 'B':
multiplier = 1;
break;
case 'K':
multiplier = 1024;
break;
case 'M':
multiplier = 1048576;
break;
case 'G':
multiplier = 1073741824;
break;
default:
multiplier = 1;
}
entry.file_size = fsize * multiplier;
DirEntry::SetDisplaySize(&entry);
}
lxb_dom_collection_destroy(td_collection, true);
out.push_back(entry);
}
lxb_dom_collection_destroy(tr_collection, true);
lxb_dom_collection_destroy(table_collection, true);
lxb_html_document_destroy(document);
}
else
{
sprintf(this->response, "%s", httplib::to_string(res.error()).c_str());
return out;
}
finish:
return out;
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef EZ_ARCHIVEORG_H
#define EZ_ARCHIVEORG_H
#include <string>
#include <vector>
#include "http/httplib.h"
#include "clients/remote_client.h"
#include "clients/baseclient.h"
#include "common.h"
class ArchiveOrgClient : public BaseClient
{
public:
int Connect(const std::string &url, const std::string &username, const std::string &password, bool send_ping=false);
std::vector<DirEntry> ListDir(const std::string &path);
private:
int Login(const std::string &username, const std::string &password);
std::string GenerateRandomId(const int len);
};
#endif
+155 -14
View File
@@ -8,6 +8,7 @@
#include "windows.h"
using httplib::Client;
using httplib::DataSink;
using httplib::Headers;
using httplib::Result;
@@ -19,7 +20,54 @@ BaseClient::~BaseClient()
delete client;
};
int BaseClient::Connect(const std::string &url, const std::string &username, const std::string &password)
int BaseClient::SetCookies(Headers &headers)
{
if (this->cookies.size() > 0)
{
std::string cookie;
for (std::map<std::string, std::string>::iterator it = this->cookies.begin(); it != this->cookies.end();)
{
cookie.append(it->first).append("=").append(it->second);
if (std::next(it, 1) != this->cookies.end())
{
cookie.append("; ");
}
++it;
}
headers.emplace("Cookie", cookie);
}
return 1;
}
int BaseClient::Connect(const std::string &url, const std::string &username, const std::string &password, bool send_ping)
{
this->host_url = url;
size_t scheme_pos = url.find("://");
size_t root_pos = url.find("/", scheme_pos + 3);
if (root_pos != std::string::npos)
{
this->host_url = url.substr(0, root_pos);
this->base_path = url.substr(root_pos);
}
client = new httplib::Client(this->host_url);
if (username.length() > 0)
client->set_basic_auth(username, password);
client->set_keep_alive(true);
client->set_follow_location(true);
client->set_connection_timeout(30);
client->set_read_timeout(30);
client->enable_server_certificate_verification(false);
if (!send_ping)
this->connected = true;
else if (Ping())
this->connected = true;
return 1;
}
int BaseClient::Connect(const std::string &url, const std::string &api_key)
{
std::string scheme_host_port = url;
size_t scheme_pos = url.find("://");
@@ -29,9 +77,10 @@ int BaseClient::Connect(const std::string &url, const std::string &username, con
scheme_host_port = url.substr(0, root_pos);
this->base_path = url.substr(root_pos);
}
client = new httplib::Client(scheme_host_port);
if (username.length() > 0)
client->set_basic_auth(username, password);
if (api_key.length() > 0)
client->set_bearer_token_auth(api_key);
client->set_keep_alive(true);
client->set_follow_location(true);
client->set_connection_timeout(30);
@@ -56,12 +105,45 @@ int BaseClient::Rmdir(const std::string &path, bool recursive)
int BaseClient::Size(const std::string &path, int64_t *size)
{
if (auto res = client->Head(GetFullPath(path)))
Headers headers;
SetCookies(headers);
if (auto res = client->Head(GetFullPath(path), headers))
{
std::string content_length = res->get_header_value("Content-Length");
if (content_length.length() > 0)
*size = atoll(content_length.c_str());
return 1;
if (HTTP_SUCCESS(res->status))
{
std::string content_length = res->get_header_value("Content-Length");
if (content_length.length() > 0)
*size = atoll(content_length.c_str());
return 1;
}
else if (res->status == 405)// Server doesn't support HEAD request. Try get range with 0 bytes and grab size from the response header
// example: Content-Range: bytes 0-10/4372785
{
Headers headers = {{"Range", "bytes=0-1"}};
SetCookies(headers);
if (auto range_res = client->Get(GetFullPath(path), headers))
{
if (HTTP_SUCCESS(range_res->status))
{
if (range_res->has_header("Content-Range"))
{
std::string range = range_res->get_header_value("Content-Range");
std::vector<std::string> range_parts = Util::Split(range, "/");
if (range_parts.size() == 2)
{
*size = atoll(range_parts[1].c_str());
return 1;
}
}
}
}
}
else
{
sprintf(this->response, "%d - %s", res->status, detail::status_message(res->status));
}
}
else
{
@@ -74,7 +156,11 @@ int BaseClient::Get(const std::string &outputfile, const std::string &path, uint
{
std::ofstream file_stream(outputfile, std::ios::binary);
bytes_transfered = 0;
if (auto res = client->Get(GetFullPath(path),
sceRtcGetCurrentTick(&prev_tick);
Headers headers;
SetCookies(headers);
if (auto res = client->Get(GetFullPath(path), headers,
[&](const char *data, size_t data_length)
{
file_stream.write(data, data_length);
@@ -92,11 +178,39 @@ int BaseClient::Get(const std::string &outputfile, const std::string &path, uint
return 0;
}
int BaseClient::Get(SplitFile *split_file, const std::string &path, uint64_t offset)
{
Headers headers;
SetCookies(headers);
if (auto res = client->Get(GetFullPath(path), headers,
[&](const char *data, size_t data_length)
{
if (!split_file->IsClosed())
{
split_file->Write((char*)data, data_length);
return true;
}
else
return false;
}))
{
return 1;
}
else
{
sprintf(this->response, "%s", httplib::to_string(res.error()).c_str());
}
return 0;
}
int BaseClient::GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset)
{
char range_header[64];
sprintf(range_header, "bytes=%lu-%lu", offset, offset+size-1);
sprintf(range_header, "bytes=%lu-%lu", offset, offset + size - 1);
Headers headers = {{"Range", range_header}};
SetCookies(headers);
size_t bytes_read = 0;
if (auto res = client->Get(GetFullPath(path), headers,
[&](const char *data, size_t data_length)
@@ -118,8 +232,10 @@ int BaseClient::GetRange(const std::string &path, DataSink &sink, uint64_t size,
int BaseClient::GetRange(const std::string &path, void *buffer, uint64_t size, uint64_t offset)
{
char range_header[64];
sprintf(range_header, "bytes=%lu-%lu", offset, offset+size-1);
sprintf(range_header, "bytes=%lu-%lu", offset, offset + size - 1);
Headers headers = {{"Range", range_header}};
SetCookies(headers);
size_t bytes_read = 0;
std::vector<char> body;
if (auto res = client->Get(GetFullPath(path), headers,
@@ -179,6 +295,8 @@ int BaseClient::Head(const std::string &path, void *buffer, uint64_t len)
char range_header[64];
sprintf(range_header, "bytes=%lu-%lu", 0L, len - 1);
Headers headers = {{"Range", range_header}};
SetCookies(headers);
size_t bytes_read = 0;
std::vector<char> body;
if (auto res = client->Get(GetFullPath(path), headers,
@@ -280,10 +398,10 @@ ClientType BaseClient::clientType()
uint32_t BaseClient::SupportedActions()
{
return REMOTE_ACTION_DOWNLOAD | REMOTE_ACTION_INSTALL;
return REMOTE_ACTION_DOWNLOAD | REMOTE_ACTION_INSTALL | REMOTE_ACTION_EXTRACT;
}
std::string BaseClient::EncodeUrl(const std::string &url)
std::string BaseClient::Escape(const std::string &url)
{
CURL *curl = curl_easy_init();
if (curl)
@@ -300,7 +418,7 @@ std::string BaseClient::EncodeUrl(const std::string &url)
return "";
}
std::string BaseClient::DecodeUrl(const std::string &url)
std::string BaseClient::UnEscape(const std::string &url)
{
CURL *curl = curl_easy_init();
if (curl)
@@ -317,3 +435,26 @@ std::string BaseClient::DecodeUrl(const std::string &url)
}
return "";
}
void *BaseClient::Open(const std::string &path, int flags)
{
sprintf(this->response, "%s", lang_strings[STR_UNSUPPORTED_OPERATION_MSG]);
return nullptr;
}
void BaseClient::Close(void *fp)
{
sprintf(this->response, "%s", lang_strings[STR_UNSUPPORTED_OPERATION_MSG]);
}
int BaseClient::GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset)
{
sprintf(this->response, "%s", lang_strings[STR_UNSUPPORTED_OPERATION_MSG]);
return -1;
}
int BaseClient::GetRange(void *fp, void *buffer, uint64_t size, uint64_t offset)
{
sprintf(this->response, "%s", lang_strings[STR_UNSUPPORTED_OPERATION_MSG]);
return -1;
}
+17 -6
View File
@@ -1,5 +1,5 @@
#ifndef BASESERVER_H
#define BASESERVER_H
#ifndef EZ_BASESERVER_H
#define EZ_BASESERVER_H
#include <string>
#include <vector>
@@ -8,6 +8,7 @@
#include "http/httplib.h"
#include "clients/remote_client.h"
#include "http/httplib.h"
#include "split_file.h"
#include "common.h"
class BaseClient : public RemoteClient
@@ -15,13 +16,17 @@ class BaseClient : public RemoteClient
public:
BaseClient();
~BaseClient();
int Connect(const std::string &url, const std::string &username, const std::string &password);
int Connect(const std::string &url, const std::string &username, const std::string &password, bool send_ping=false);
int Connect(const std::string &url, const std::string &api_key);
int Mkdir(const std::string &path);
int Rmdir(const std::string &path, bool recursive);
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 GetRange(void *fp, void *buffer, uint64_t size, uint64_t offset);
int GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset);
int Put(const std::string &inputfile, const std::string &path, uint64_t offset=0);
int Rename(const std::string &src, const std::string &dst);
int Delete(const std::string &path);
@@ -32,20 +37,26 @@ public:
std::vector<DirEntry> ListDir(const std::string &path);
std::string GetPath(std::string path1, std::string path2);
std::string GetFullPath(std::string path1);
void *Open(const std::string &path, int flags);
void Close(void *fp);
bool IsConnected();
bool Ping();
const char *LastResponse();
int Quit();
ClientType clientType();
uint32_t SupportedActions();
static std::string EncodeUrl(const std::string &url);
static std::string DecodeUrl(const std::string &url);
static std::string Escape(const std::string &url);
static std::string UnEscape(const std::string &url);
protected:
int SetCookies(httplib::Headers &headers);
httplib::Client *client;
std::string base_path;
std::string host_url;
char response[512];
bool connected = false;
std::map<std::string, std::string> cookies;
};
#endif
#endif
+142 -61
View File
@@ -24,8 +24,6 @@
#define FTP_CLIENT_READ 1
#define FTP_CLIENT_WRITE 2
#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
FtpClient::FtpClient()
{
mp_ftphandle = static_cast<ftphandle *>(calloc(1, sizeof(ftphandle)));
@@ -48,7 +46,7 @@ FtpClient::~FtpClient()
free(mp_ftphandle);
}
int FtpClient::Connect(const std::string &url, const std::string &user, const std::string &pass)
int FtpClient::Connect(const std::string &url, const std::string &user, const std::string &pass, bool send_ping)
{
int port = 21;
std::string host = url.substr(6);
@@ -125,7 +123,7 @@ int FtpClient::Connect(const std::string &url, const std::string &user, const st
}
mp_ftphandle->handle = sControl;
if (ReadResponse('2', mp_ftphandle) == 0)
if (ReadResponse("2", mp_ftphandle) == 0)
{
close(mp_ftphandle->handle);
mp_ftphandle->handle = 0;
@@ -142,7 +140,7 @@ int FtpClient::Connect(const std::string &url, const std::string &user, const st
cmd = "USER anonymous";
}
if (!FtpSendCmd(cmd, '3', mp_ftphandle))
if (!FtpSendCmd(cmd, "3", mp_ftphandle))
{
if (mp_ftphandle->ctrl != NULL)
return 1;
@@ -161,7 +159,7 @@ int FtpClient::Connect(const std::string &url, const std::string &user, const st
cmd = "PASS " + pass;
int ret;
if ((ret = FtpSendCmd(cmd, '2', mp_ftphandle)))
if ((ret = FtpSendCmd(cmd, "2", mp_ftphandle)))
{
mp_ftphandle->is_connected = true;
}
@@ -179,7 +177,7 @@ int FtpClient::Connect(const std::string &url, const std::string &user, const st
*
* return 1 if proper response received, 0 otherwise
*/
int FtpClient::FtpSendCmd(const std::string &cmd, char expected_resp, ftphandle *nControl)
int FtpClient::FtpSendCmd(const std::string &cmd, const std::string &expected_resp, ftphandle *nControl)
{
char buf[512];
int x;
@@ -205,7 +203,7 @@ int FtpClient::FtpSendCmd(const std::string &cmd, char expected_resp, ftphandle
* return 0 if first char doesn't match
* return 1 if first char matches
*/
int FtpClient::ReadResponse(char c, ftphandle *nControl)
int FtpClient::ReadResponse(const std::string &c, ftphandle *nControl)
{
char match[5];
@@ -228,7 +226,7 @@ int FtpClient::ReadResponse(char c, ftphandle *nControl)
} while (strncmp(nControl->response, match, 4));
}
if (nControl->response[0] == c)
if (c.find(nControl->response[0]) != std::string::npos)
return 1;
return 0;
}
@@ -368,7 +366,7 @@ int FtpClient::FtpAccess(const std::string &path, accesstype type, transfermode
return 0;
}
sprintf(buf, "TYPE %c", mode);
if (!FtpSendCmd(buf, '2', nControl))
if (!FtpSendCmd(buf, "2", nControl))
return 0;
switch (type)
@@ -477,7 +475,7 @@ int FtpClient::FtpAcceptConnection(ftphandle *nData, ftphandle *nControl)
{
close(nData->handle);
nData->handle = 0;
ReadResponse('2', nControl);
ReadResponse("2", nControl);
rv = 0;
}
@@ -521,7 +519,7 @@ int FtpClient::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode
memset(&sin, 0, l);
sin.in.sin_family = AF_INET;
if (!FtpSendCmd("PASV", '2', nControl))
if (!FtpSendCmd("PASV", "2", nControl))
return -1;
cp = strchr(nControl->response, '(');
if (cp == NULL)
@@ -542,7 +540,7 @@ int FtpClient::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode
{
char buf[512];
sprintf(buf, "REST %lld", mp_ftphandle->offset);
if (!FtpSendCmd(buf, '3', nControl))
if (!FtpSendCmd(buf, "3", nControl))
return 0;
}
@@ -592,7 +590,7 @@ int FtpClient::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode
return -1;
}
if (!ReadResponse('1', nControl))
if (!ReadResponse("1", nControl))
{
close(sData);
return -1;
@@ -717,7 +715,7 @@ int FtpClient::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode
(unsigned char)sin.sa.sa_data[5],
(unsigned char)sin.sa.sa_data[0],
(unsigned char)sin.sa.sa_data[1]);
if (!FtpSendCmd(buf, '2', nControl))
if (!FtpSendCmd(buf, "2", nControl))
{
close(sData);
return -1;
@@ -727,7 +725,7 @@ int FtpClient::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode
{
char buf[512];
sprintf(buf, "REST %lld", mp_ftphandle->offset);
if (!FtpSendCmd(buf, '3', nControl))
if (!FtpSendCmd(buf, "3", nControl))
{
close(sData);
return 0;
@@ -747,7 +745,7 @@ int FtpClient::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode
return -1;
}
if (!FtpSendCmd(cmd, '1', nControl))
if (!FtpSendCmd(cmd, "1", nControl))
{
FtpClose(*nData);
*nData = NULL;
@@ -880,6 +878,33 @@ int FtpClient::FtpXfer(const std::string &localfile, const std::string &path, ft
return FtpClose(nData);
}
/*
* FtpXfer - issue a command and transfer data
*
* return 1 if successful, 0 otherwise
*/
int FtpClient::FtpXfer(SplitFile *split_file, const std::string &path, ftphandle *nControl, accesstype type, transfermode mode)
{
int l, c;
char *dbuf;
ftphandle *nData;
if (!FtpAccess(path, type, mode, nControl, &nData))
{
return 0;
}
dbuf = static_cast<char *>(malloc(FTP_CLIENT_BUFSIZ));
while ((l = FtpRead(dbuf, FTP_CLIENT_BUFSIZ, nData)) > 0)
{
if (split_file->Write(dbuf, l) < 0)
break;
}
free(dbuf);
return FtpClose(nData);
}
/*
* FtpWrite - write to a data connection
*/
@@ -1019,7 +1044,7 @@ int FtpClient::FtpClose(ftphandle *nData)
ctrl = nData->ctrl;
free(nData);
if (ctrl)
return ReadResponse('2', ctrl);
return ReadResponse("2", ctrl);
return 1;
}
@@ -1035,7 +1060,7 @@ int FtpClient::Quit()
strcpy(mp_ftphandle->response, "error: no anwser from server\n");
return 0;
}
FtpSendCmd("QUIT", '2', mp_ftphandle);
FtpSendCmd("QUIT", "2", mp_ftphandle);
shutdown(mp_ftphandle->handle, SHUT_WR);
struct linger lng = {1, 0};
setsockopt(mp_ftphandle->handle, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng));
@@ -1079,7 +1104,7 @@ int FtpClient::RawRead(void *buf, int max, ftphandle *handle)
int FtpClient::Site(const std::string &cmd)
{
std::string tmp = "SITE " + cmd;
if (!FtpSendCmd(tmp, '2', mp_ftphandle))
if (!FtpSendCmd(tmp, "2", mp_ftphandle))
return 0;
return 1;
}
@@ -1092,7 +1117,7 @@ int FtpClient::Site(const std::string &cmd)
int FtpClient::Raw(const std::string &cmd)
{
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
return 0;
return 1;
}
@@ -1111,7 +1136,7 @@ int FtpClient::SysType(char *buf, int max)
int l = max;
char *b = buf;
char *s;
if (!FtpSendCmd("SYST", '2', mp_ftphandle))
if (!FtpSendCmd("SYST", "2", mp_ftphandle))
return 0;
s = &mp_ftphandle->response[4];
while ((--l) && (*s != ' '))
@@ -1128,7 +1153,7 @@ int FtpClient::SysType(char *buf, int max)
int FtpClient::Mkdir(const std::string &path)
{
std::string cmd = "MKD " + path;
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
return 0;
return 1;
}
@@ -1141,7 +1166,7 @@ int FtpClient::Mkdir(const std::string &path)
int FtpClient::Chdir(const std::string &path)
{
std::string cmd = "CWD " + path;
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
return 0;
return 1;
}
@@ -1153,7 +1178,7 @@ int FtpClient::Chdir(const std::string &path)
*/
int FtpClient::Cdup()
{
if (!FtpSendCmd("CDUP", '2', mp_ftphandle))
if (!FtpSendCmd("CDUP", "2", mp_ftphandle))
return 0;
return 1;
}
@@ -1165,7 +1190,7 @@ int FtpClient::Cdup()
*/
bool FtpClient::Noop()
{
if (!FtpSendCmd("NOOP", '2', mp_ftphandle))
if (!FtpSendCmd("NOOP", "25", mp_ftphandle))
return 0;
return 1;
}
@@ -1183,7 +1208,7 @@ bool FtpClient::Ping()
int FtpClient::Rmdir(const std::string &path)
{
std::string cmd = "RMD " + path;
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
return 0;
return 1;
}
@@ -1246,11 +1271,11 @@ int FtpClient::Size(const std::string &path, int64_t *size)
return 0;
sprintf(cmd, "TYPE %c", FtpClient::transfermode::image);
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
return 0;
sprintf(cmd, "SIZE %s", path.c_str());
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
rv = 0;
else
{
@@ -1283,6 +1308,15 @@ int FtpClient::Get(const std::string &outputfile, const std::string &path, uint6
return FtpXfer(outputfile, path, mp_ftphandle, FtpClient::filereadappend, FtpClient::transfermode::image);
}
int FtpClient::Get(SplitFile *split_file, const std::string &path, uint64_t offset)
{
mp_ftphandle->offset = offset;
if (offset == 0)
return FtpXfer(split_file, path, mp_ftphandle, FtpClient::fileread, FtpClient::transfermode::image);
else
return FtpXfer(split_file, path, mp_ftphandle, FtpClient::filereadappend, FtpClient::transfermode::image);
}
int FtpClient::GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset)
{
ftphandle *nData;
@@ -1373,10 +1407,10 @@ int FtpClient::Put(const std::string &inputfile, const std::string &path, uint64
int FtpClient::Rename(const std::string &src, const std::string &dst)
{
std::string cmd = "RNFR " + src;
if (!FtpSendCmd(cmd, '3', mp_ftphandle))
if (!FtpSendCmd(cmd, "3", mp_ftphandle))
return 0;
cmd = "RNTO " + dst;
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
return 0;
return 1;
@@ -1385,7 +1419,7 @@ int FtpClient::Rename(const std::string &src, const std::string &dst)
int FtpClient::Delete(const std::string &path)
{
std::string cmd = "DELE " + path;
if (!FtpSendCmd(cmd, '2', mp_ftphandle))
if (!FtpSendCmd(cmd, "2", mp_ftphandle))
return 0;
return 1;
}
@@ -1466,9 +1500,16 @@ int FtpClient::ParseDirEntry(char *line, DirEntry *dirEntry)
dirEntry->modified.minutes = (uint8_t)strtoul(token + 3, NULL, 10);
// The PM period covers the 12 hours from noon to midnight
// Correct 12-hour clock: 12:xx AM = 00:xx, 12:xx PM = 12:xx
if (strstr(token, "PM") != NULL)
{
dirEntry->modified.hours += 12;
if (dirEntry->modified.hours != 12)
dirEntry->modified.hours += 12;
}
else
{
if (dirEntry->modified.hours == 12)
dirEntry->modified.hours = 0;
}
}
else
@@ -1492,7 +1533,7 @@ int FtpClient::ParseDirEntry(char *line, DirEntry *dirEntry)
else
{
// Save the size of the file
dirEntry->file_size = strtoul(token, NULL, 10);
dirEntry->file_size = strtoull(token, NULL, 10);
}
// Read filename field
@@ -1514,8 +1555,8 @@ int FtpClient::ParseDirEntry(char *line, DirEntry *dirEntry)
// Unix listing format?
else
{
// Check file permissions
if (strchr(token, 'd') != NULL)
// Check file permissions — 'd' must be at position 0 of the permissions field
if (token[0] == 'd')
{
dirEntry->isDir = true;
}
@@ -1545,7 +1586,7 @@ int FtpClient::ParseDirEntry(char *line, DirEntry *dirEntry)
return -1;
// Save the size of the file
dirEntry->file_size = strtoul(token, NULL, 10);
dirEntry->file_size = strtoull(token, NULL, 10);
// Read modification time (month)
token = strtok_r(NULL, " ", &p);
@@ -1586,12 +1627,13 @@ int FtpClient::ParseDirEntry(char *line, DirEntry *dirEntry)
// The format of the year is yyyy
dirEntry->modified.year = (uint16_t)strtoul(token, NULL, 10);
}
else if (strlen(token) == 5)
else if (strchr(token, ':') != NULL)
{
// The format of the time hh:mm
token[2] = '\0';
// The format of the time is hh:mm or h:mm
char *colon = strchr(token, ':');
*colon = '\0';
dirEntry->modified.hours = (uint8_t)strtoul(token, NULL, 10);
dirEntry->modified.minutes = (uint8_t)strtoul(token + 3, NULL, 10);
dirEntry->modified.minutes = (uint8_t)strtoul(colon + 1, NULL, 10);
dirEntry->modified.year = cur_time.tm_year + 1900;
}
else
@@ -1606,6 +1648,11 @@ int FtpClient::ParseDirEntry(char *line, DirEntry *dirEntry)
if (token == NULL)
return -1;
// For symlinks, strip the " -> target" suffix (e.g. "linkname -> /some/target")
char *arrow = strstr(token, " -> ");
if (arrow != NULL)
*arrow = '\0';
// Retrieve the length of the filename
n = strlen(token);
// Limit the number of characters to copy
@@ -1617,6 +1664,10 @@ int FtpClient::ParseDirEntry(char *line, DirEntry *dirEntry)
dirEntry->name[n] = '\0';
}
// Exclude hidden files and folders (names starting with '.')
if (!show_hidden_files && dirEntry->name[0] == '.')
return -1;
// The directory entry is valid
return 1;
}
@@ -1627,37 +1678,48 @@ int FtpClient::ParseMLSDDirEntry(char *line, DirEntry *dirEntry)
char *token;
char *facts;
char *keypair;
char *factsSave;
char key[128];
char value[128];
// Spilt string by space
// Split string by first space: facts portion and name portion
facts = strtok_r(line, " ", &p);
// path is the rest of the line after space
// path is the rest of the line after the space, strip trailing CR/LF
token = strtok_r(p, "\r\n", &p);
snprintf(dirEntry->name, 256, "%s", token);
// split properties by semi-colon and get the key value pair
while ((keypair = strtok_r(facts, ";", &facts)))
// Split facts by semicolon and parse each key=value pair
factsSave = NULL;
keypair = strtok_r(facts, ";", &factsSave);
while (keypair != NULL)
{
sscanf(keypair, "%[^=]=%s", key, value);
if (strcasecmp(key, "type") == 0)
key[0] = '\0';
value[0] = '\0';
if (sscanf(keypair, "%127[^=]=%127s", key, value) == 2)
{
dirEntry->isDir = false;
if (strcasecmp(value, "dir") == 0)
if (strcasecmp(key, "type") == 0)
{
dirEntry->isDir = true;
dirEntry->isDir = false;
// dir, cdir (current dir), and pdir (parent dir) are all directory types
if (strcasecmp(value, "dir") == 0 ||
strcasecmp(value, "cdir") == 0 ||
strcasecmp(value, "pdir") == 0)
{
dirEntry->isDir = true;
}
}
else if (strcasecmp(key, "size") == 0)
{
dirEntry->file_size = atoll(value);
}
else if (strcasecmp(key, "modify") == 0)
{
sscanf(value, "%4d%2d%2d%2d%2d%2d", &dirEntry->modified.year, &dirEntry->modified.month, &dirEntry->modified.day,
&dirEntry->modified.hours, &dirEntry->modified.minutes, &dirEntry->modified.seconds);
}
}
else if (strcasecmp(key, "size") == 0)
{
dirEntry->file_size = atoll(value);
}
else if (strcasecmp(key, "modify") == 0)
{
sscanf(value, "%4d%2d%2d%2d%2d%2d", &dirEntry->modified.year, &dirEntry->modified.month, &dirEntry->modified.day,
&dirEntry->modified.hours, &dirEntry->modified.minutes, &dirEntry->modified.seconds);
}
keypair = strtok_r(NULL, ";", &factsSave);
}
return 1;
}
@@ -1744,7 +1806,7 @@ ClientType FtpClient::clientType()
uint32_t FtpClient::SupportedActions()
{
return REMOTE_ACTION_ALL ^ REMOTE_ACTION_CUT ^ REMOTE_ACTION_COPY ^ REMOTE_ACTION_PASTE;
return REMOTE_ACTION_ALL ^ REMOTE_ACTION_CUT ^ REMOTE_ACTION_COPY ^ REMOTE_ACTION_PASTE ^ REMOTE_ACTION_RAW_READ;
}
std::string FtpClient::GetPath(std::string ppath1, std::string ppath2)
@@ -1783,4 +1845,23 @@ int FtpClient::Head(const std::string &path, void *buffer, uint64_t len)
if (l != len)
return 0;
return 1;
}
}
void *FtpClient::Open(const std::string &path, int flags)
{
return nullptr;
}
void FtpClient::Close(void *fp)
{
}
int FtpClient::GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset)
{
return -1;
}
int FtpClient::GetRange(void *fp, void *buffer, uint64_t size, uint64_t offset)
{
return -1;
}
+12 -6
View File
@@ -1,5 +1,5 @@
#ifndef FTPCLIENT_H
#define FTPCLIENT_H
#ifndef EZ_FTPCLIENT_H
#define EZ_FTPCLIENT_H
#include <sys/socket.h>
#include <arpa/inet.h>
@@ -67,7 +67,7 @@ public:
FtpClient();
~FtpClient();
int Connect(const std::string &url, const std::string &user, const std::string &pass);
int Connect(const std::string &url, const std::string &user, const std::string &pass, bool send_ping=false);
void SetConnmode(connmode mode);
int Site(const std::string &cmd);
int Raw(const std::string &cmd);
@@ -79,14 +79,19 @@ public:
int Rmdir(const std::string &path, bool recursive);
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 GetRange(void *fp, void *buffer, uint64_t size, uint64_t offset);
int GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset);
int Put(const std::string &inputfile, const std::string &path, uint64_t offset = 0);
int Rename(const std::string &src, const std::string &dst);
int Delete(const std::string &path);
int 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);
void *Open(const std::string &path, int flags);
void Close(void *fp);
std::vector<DirEntry> ListDir(const std::string &path);
void SetCallbackXferFunction(FtpCallbackXfer pointer);
void SetCallbackArg(void *arg);
@@ -109,12 +114,12 @@ private:
char server[128];
int server_port;
int FtpSendCmd(const std::string &cmd, char expected_resp, ftphandle *nControl);
int FtpSendCmd(const std::string &cmd, const std::string &expected_resp, ftphandle *nControl);
ftphandle *RawOpen(const std::string &path, accesstype type, transfermode mode);
int RawClose(ftphandle *handle);
int RawWrite(void *buf, int len, ftphandle *handle);
int RawRead(void *buf, int max, ftphandle *handle);
int ReadResponse(char c, ftphandle *nControl);
int ReadResponse(const std::string &c, ftphandle *nControl);
int Readline(char *buf, int max, ftphandle *nControl);
int Writeline(char *buf, int len, ftphandle *nData);
void ClearHandle();
@@ -124,6 +129,7 @@ private:
int CorrectPasvResponse(int *v);
int FtpAccess(const std::string &path, accesstype type, transfermode mode, ftphandle *nControl, ftphandle **nData);
int FtpXfer(const std::string &localfile, const std::string &path, ftphandle *nControl, accesstype type, transfermode mode);
int FtpXfer(SplitFile *split_file, const std::string &path, ftphandle *nControl, accesstype type, transfermode mode);
int FtpWrite(void *buf, int len, ftphandle *nData);
int FtpRead(void *buf, int max, ftphandle *nData);
int FtpClose(ftphandle *nData);
@@ -131,4 +137,4 @@ private:
int ParseMLSDDirEntry(char *line, DirEntry *dirEntry);
};
#endif
#endif
+252 -39
View File
@@ -111,7 +111,7 @@ int GDriveClient::RequestAuthorization()
std::string auth_url = std::string(GOOGLE_AUTH_URL "?client_id=") + gg_app.client_id + "&redirect_uri=" + GetRedirectUrl() +
"&response_type=code&access_type=offline&scope=" + GetScopes() + "&include_granted_scopes=true";
auth_url = EncodeUrl(auth_url);
auth_url = Escape(auth_url);
std::string launch_uri = std::string("pswebbrowser:search?url=") + auth_url;
int ret = sceShellUIUtilLaunchByUri(launch_uri.c_str(), &param);
@@ -159,7 +159,7 @@ GDriveClient::GDriveClient()
path_id_map.insert(std::make_pair("/" + shared_with_me, shared_with_me));
}
int GDriveClient::Connect(const std::string &url, const std::string &user, const std::string &pass)
int GDriveClient::Connect(const std::string &url, const std::string &user, const std::string &pass, bool send_ping)
{
if (strlen(remote_settings->gg_account.refresh_token) > 0)
{
@@ -222,11 +222,11 @@ int GDriveClient::Rename(const std::string &src, const std::string &dst)
if (src_id.compare("root") == 0 || dst_id.compare("root") == 0 || src_id.compare(src_drive_id) == 0 || dst_id.compare(dst_drive_id) == 0)
return 0;
std::string url = std::string("/drive/v3/files/") + BaseClient::EncodeUrl(src_id);
std::string url = std::string("/drive/v3/files/") + BaseClient::Escape(src_id);
if (!src_drive_id.empty())
url += "?supportsAllDrives=true";
std::string filename = dst.substr(dst.find_last_of("/") + 1);
std::string body = "{'name' : '" + filename + "'}";
std::string body = "{\"name\" : \"" + filename + "\"}";
if (auto res = client->Patch(url, body.c_str(), body.length(), "application/json; charset=UTF-8"))
{
sprintf(response, "%d", res->status);
@@ -276,7 +276,7 @@ int GDriveClient::Head(const std::string &path, void *buffer, uint64_t len)
std::string id = GetValue(path_id_map, path);
std::string drive_id = GetDriveId(path);
std::string url = std::string("/drive/v3/files/") + BaseClient::EncodeUrl(id) + "?alt=media";
std::string url = std::string("/drive/v3/files/") + BaseClient::Escape(id) + "?alt=media";
if (!drive_id.empty())
url += "&supportsAllDrives=true";
Headers headers;
@@ -307,10 +307,11 @@ int GDriveClient::Get(const std::string &outputfile, const std::string &path, ui
{
std::ofstream file_stream(outputfile, std::ios::binary);
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
std::string id = GetValue(path_id_map, path);
std::string drive_id = GetDriveId(path);
std::string url = std::string("/drive/v3/files/") + BaseClient::EncodeUrl(id) + "?alt=media";
std::string url = std::string("/drive/v3/files/") + BaseClient::Escape(id) + "?alt=media";
if (!drive_id.empty())
url += "&supportsAllDrives=true";
if (auto res = client->Get(url,
@@ -331,13 +332,41 @@ int GDriveClient::Get(const std::string &outputfile, const std::string &path, ui
return 0;
}
int GDriveClient::Get(SplitFile *split_file, const std::string &path, uint64_t offset)
{
std::string id = GetValue(path_id_map, path);
std::string drive_id = GetDriveId(path);
std::string url = std::string("/drive/v3/files/") + BaseClient::Escape(id) + "?alt=media";
if (!drive_id.empty())
url += "&supportsAllDrives=true";
if (auto res = client->Get(url,
[&](const char *data, size_t data_length)
{
if (!split_file->IsClosed())
{
split_file->Write((char*)data, data_length);
return true;
}
else
return false;
}))
{
return 1;
}
else
{
sprintf(this->response, "%s", httplib::to_string(res.error()).c_str());
}
return 0;
}
int GDriveClient::GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset)
{
size_t bytes_read = 0;
std::string id = GetValue(path_id_map, path);
std::string drive_id = GetDriveId(path);
std::string url = std::string("/drive/v3/files/") + BaseClient::EncodeUrl(id) + "?alt=media";
std::string url = std::string("/drive/v3/files/") + BaseClient::Escape(id) + "?alt=media";
if (!drive_id.empty())
url += "&supportsAllDrives=true";
Headers headers;
@@ -367,7 +396,7 @@ int GDriveClient::GetRange(const std::string &path, void *buffer, uint64_t size,
std::string id = GetValue(path_id_map, path);
std::string drive_id = GetDriveId(path);
std::string url = std::string("/drive/v3/files/") + BaseClient::EncodeUrl(id) + "?alt=media";
std::string url = std::string("/drive/v3/files/") + BaseClient::Escape(id) + "?alt=media";
if (!drive_id.empty())
url += "&supportsAllDrives=true";
Headers headers;
@@ -399,20 +428,20 @@ int GDriveClient::Update(const std::string &inputfile, const std::string &path)
{
bytes_to_download = FS::GetSize(inputfile);
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
std::ifstream file_stream(inputfile, std::ios::binary);
bytes_transfered = 0;
std::string id = GetValue(path_id_map, path);
std::string drive_id = GetDriveId(path);
std::string url = "/upload/drive/v3/files/" + BaseClient::EncodeUrl(id) + "?uploadType=resumable";
std::string url = "/upload/drive/v3/files/" + BaseClient::Escape(id) + "?uploadType=resumable";
if (!drive_id.empty())
url += "&supportsAllDrives=true";
Headers headers;
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))
@@ -428,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);
@@ -442,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
{
@@ -473,10 +579,9 @@ int GDriveClient::Put(const std::string &inputfile, const std::string &path, uin
bytes_to_download = FS::GetSize(inputfile);
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
std::ifstream file_stream(inputfile, std::ios::binary);
bytes_transfered = 0;
size_t path_pos = path.find_last_of("/");
std::string parent_dir;
if (path_pos == 0)
@@ -492,14 +597,16 @@ int GDriveClient::Put(const std::string &inputfile, const std::string &path, uin
if (!drive_id.empty())
url += "&supportsAllDrives=true";
std::string post_data = std::string("{'name': '") + filename + "'," +
(drive_id.empty() ? "" : "'driveId' : '" + drive_id + "',") +
"'parents': ['" + parent_id + "']}";
std::string post_data = std::string("{\"name\": \"") + filename + "\"," +
(drive_id.empty() ? "" : "\"driveId\" : \"" + drive_id + "\",") +
"\"parents\": [\"" + parent_id + "\"]}";
Headers headers;
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))
{
@@ -514,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
{
@@ -553,7 +743,7 @@ int GDriveClient::Size(const std::string &path, int64_t *size)
{
std::string id = GetValue(path_id_map, path);
std::string drive_id = GetDriveId(path);
std::string url = std::string("/drive/v3/files/") + BaseClient::EncodeUrl(id) + "?fields=size";
std::string url = std::string("/drive/v3/files/") + BaseClient::Escape(id) + "?fields=size";
if (!drive_id.empty())
url += "&supportsAllDrives=true";
if (auto res = client->Get(url))
@@ -605,10 +795,10 @@ int GDriveClient::Mkdir(const std::string &path)
std::string url = std::string("/drive/v3/files?fields=id");
if (!drive_id.empty())
url += "&supportsAllDrives=true";
std::string folder_metadata = "{'name' : '" + folder_name + "'," +
"'parents' : ['" + parent_id + "']," +
(drive_id.empty() ? "" : "'driveId' : '" + drive_id + "',") +
"'mimeType' : 'application/vnd.google-apps.folder'}";
std::string folder_metadata = "{\"name\" : \"" + folder_name + "\"," +
"\"parents\" : [\"" + parent_id + "\"]," +
(drive_id.empty() ? "" : "\"driveId\" : \"" + drive_id + "\",") +
"\"mimeType\" : \"application/vnd.google-apps.folder\"}";
if (auto res = client->Post(url, folder_metadata.c_str(), folder_metadata.length(), "application/json; charset=UTF-8"))
{
@@ -669,7 +859,7 @@ int GDriveClient::Delete(const std::string &path)
if (strcmp(id.c_str(), "root") == 0)
return 0;
std::string url = std::string("/drive/v3/files/") + BaseClient::EncodeUrl(id);
std::string url = std::string("/drive/v3/files/") + BaseClient::Escape(id);
if (!drive_id.empty())
url += "?supportsAllDrives=true";
if (auto res = client->Delete(url))
@@ -779,8 +969,8 @@ std::vector<DirEntry> GDriveClient::ListDir(const std::string &path)
}
std::string drive_id = GetDriveId(path);
std::string base_url = std::string("/drive/v3/files?q=") + BaseClient::EncodeUrl("\"" + id + "\" in parents") +
"&pageSize=1000&fields=" + BaseClient::EncodeUrl("files(id,mimeType,name,modifiedTime,size),nextPageToken");
std::string base_url = std::string("/drive/v3/files?q=") + BaseClient::Escape("\"" + id + "\" in parents") +
"&pageSize=1000&fields=" + BaseClient::Escape("files(id,mimeType,name,modifiedTime,size),nextPageToken");
if (!drive_id.empty())
{
base_url += "&driveId=" + drive_id + "&corpora=drive&includeItemsFromAllDrives=true&supportsAllDrives=true";
@@ -789,7 +979,7 @@ std::vector<DirEntry> GDriveClient::ListDir(const std::string &path)
bool find_no_parent = false;
if (id.compare(shared_with_me) == 0)
{
base_url = std::string("/drive/v3/files?q=sharedWithMe&pageSize=1000&fields=") + BaseClient::EncodeUrl("files(id,mimeType,name,modifiedTime,size),nextPageToken");
base_url = std::string("/drive/v3/files?q=sharedWithMe&pageSize=1000&fields=") + BaseClient::Escape("files(id,mimeType,name,modifiedTime,size),nextPageToken");
}
std::string next_page_url = base_url;
@@ -867,7 +1057,7 @@ std::vector<DirEntry> GDriveClient::ListDir(const std::string &path)
}
}
if (next_page_token != nullptr)
next_page_url = base_url + "&pageToken=" + BaseClient::EncodeUrl(json_object_get_string(next_page_token));
next_page_url = base_url + "&pageToken=" + BaseClient::Escape(json_object_get_string(next_page_token));
else
break;
}
@@ -887,7 +1077,7 @@ ClientType GDriveClient::clientType()
uint32_t GDriveClient::SupportedActions()
{
return REMOTE_ACTION_ALL ^ REMOTE_ACTION_CUT ^ REMOTE_ACTION_COPY ^ REMOTE_ACTION_PASTE;
return REMOTE_ACTION_ALL ^ REMOTE_ACTION_CUT ^ REMOTE_ACTION_COPY ^ REMOTE_ACTION_PASTE ^ REMOTE_ACTION_RAW_READ;
}
void *GDriveClient::RefreshTokenThread(void *argp)
@@ -941,4 +1131,27 @@ void GDriveClient::SetAccessToken(const std::string &token)
{
if (this->client != nullptr)
this->client->set_bearer_token_auth(token);
}
}
void *GDriveClient::Open(const std::string &path, int flags)
{
sprintf(this->response, "%s", lang_strings[STR_UNSUPPORTED_OPERATION_MSG]);
return nullptr;
}
void GDriveClient::Close(void *fp)
{
sprintf(this->response, "%s", lang_strings[STR_UNSUPPORTED_OPERATION_MSG]);
}
int GDriveClient::GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset)
{
sprintf(this->response, "%s", lang_strings[STR_UNSUPPORTED_OPERATION_MSG]);
return -1;
}
int GDriveClient::GetRange(void *fp, void *buffer, uint64_t size, uint64_t offset)
{
sprintf(this->response, "%s", lang_strings[STR_UNSUPPORTED_OPERATION_MSG]);
return -1;
}
+9 -4
View File
@@ -1,5 +1,5 @@
#ifndef GDRIVE_H
#define GDRIVE_H
#ifndef EZ_GDRIVE_H
#define EZ_GDRIVE_H
#include <string>
#include <vector>
@@ -17,11 +17,14 @@ class GDriveClient : public BaseClient
{
public:
GDriveClient();
int Connect(const std::string &url, const std::string &user, const std::string &pass);
int Connect(const std::string &url, const std::string &user, const std::string &pass, bool send_ping=false);
int Rename(const std::string &src, const std::string &dst);
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 GetRange(void *fp, void *buffer, uint64_t size, uint64_t offset);
int GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset);
int Put(const std::string &inputfile, const std::string &path, uint64_t offset=0);
int Head(const std::string &path, void *buffer, uint64_t len);
int Update(const std::string &inputfile, const std::string &path);
@@ -32,6 +35,8 @@ public:
bool FileExists(const std::string &path);
void SetAccessToken(const std::string &token);
std::vector<DirEntry> ListDir(const std::string &path);
void *Open(const std::string &path, int flags);
void Close(void *fp);
static void *RefreshTokenThread(void *argp);
static void StartRefreshToken();
static void StopRefreshToken();
@@ -46,4 +51,4 @@ private:
std::map<std::string, std::string> shared_drive_map;
};
#endif
#endif
+276
View File
@@ -0,0 +1,276 @@
#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, bool send_ping)
{
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";
this->m_download_url = "https://github.com";
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 (!send_ping)
this->connected = true;
else 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;
}
std::string GithubClient::GetDownloadUrl(const std::string &path)
{
if (!ParseReleases())
return "";
std::vector<std::string> path_parts = Util::Split(path, "/");
if (path_parts.size() != 2)
{
return "";
}
return this->m_download_url + Escape(m_assets[path_parts[0]][path_parts[1]].url);
}
+48
View File
@@ -0,0 +1,48 @@
#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, bool send_ping=false);
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);
std::string GetDownloadUrl(const std::string &path);
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;
std::string m_download_url;
BaseClient m_client;
bool ParseReleases();
};
#endif
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef IIS_H
#define IIS_H
#ifndef EZ_IIS_H
#define EZ_IIS_H
#include <string>
#include <vector>
+244
View File
@@ -0,0 +1,244 @@
#include <lexbor/html/parser.h>
#include <lexbor/dom/interfaces/element.h>
#include <lexbor/dom/interfaces/node.h>
#include <fstream>
#include <map>
#include "common.h"
#include "clients/remote_client.h"
#include "clients/myrient.h"
#include "lang.h"
#include "util.h"
#include "system.h"
#include "windows.h"
using httplib::Client;
using httplib::Headers;
using httplib::Result;
static std::map<std::string, int> month_map = {{"Jan", 1}, {"Feb", 2}, {"Mar", 3}, {"Apr", 4}, {"May", 5}, {"Jun", 6}, {"Jul", 7}, {"Aug", 8}, {"Sep", 9}, {"Oct", 10}, {"Nov", 11}, {"Dec", 12}};
std::vector<DirEntry> MyrientClient::ListDir(const std::string &path)
{
std::vector<DirEntry> out;
DirEntry entry;
Util::SetupPreviousFolder(path, &entry);
out.push_back(entry);
std::string encoded_path = httplib::detail::encode_url(GetFullPath(path) + "/");
if (auto res = client->Get(encoded_path))
{
lxb_status_t status;
lxb_dom_attr_t *attr;
lxb_dom_element_t *table_element, *tr_element, *td_element;
lxb_html_document_t *document;
lxb_dom_collection_t *table_collection;
lxb_dom_collection_t *tr_collection;
lxb_dom_collection_t *td_collection;
std::string tmp_string;
const lxb_char_t *value;
size_t value_len;
document = lxb_html_document_create();
status = lxb_html_document_parse(document, (lxb_char_t *)res->body.c_str(), res->body.length());
if (status != LXB_STATUS_OK)
{
lxb_html_document_destroy(document);
goto finish;
}
table_collection = lxb_dom_collection_make(&document->dom_document, 1);
if (table_collection == NULL)
{
lxb_html_document_destroy(document);
goto finish;
}
tr_collection = lxb_dom_collection_make(&document->dom_document, 128);
if (tr_collection == NULL)
{
lxb_html_document_destroy(document);
goto finish;
}
status = lxb_dom_elements_by_tag_name(lxb_dom_interface_element(document->body),
table_collection, (const lxb_char_t *)"table", 5);
if (status != LXB_STATUS_OK)
{
lxb_dom_collection_destroy(tr_collection, true);
lxb_dom_collection_destroy(table_collection, true);
lxb_html_document_destroy(document);
goto finish;
}
if (lxb_dom_collection_length(table_collection) < 1)
{
lxb_dom_collection_destroy(tr_collection, true);
lxb_dom_collection_destroy(table_collection, true);
lxb_html_document_destroy(document);
goto finish;
}
for (size_t i = 0; i < lxb_dom_collection_length(table_collection); i++)
{
table_element = lxb_dom_collection_element(table_collection, i);
value = lxb_dom_element_id(table_element, &value_len);
tmp_string = std::string((const char *)value, value_len);
if (tmp_string.compare("list") == 0)
break;
table_element = nullptr;
}
if (table_element == nullptr)
{
lxb_dom_collection_destroy(tr_collection, true);
lxb_dom_collection_destroy(table_collection, true);
lxb_html_document_destroy(document);
goto finish;
}
status = lxb_dom_elements_by_tag_name(table_element,
tr_collection, (const lxb_char_t *)"tr", 2);
if (status != LXB_STATUS_OK && lxb_dom_collection_length(tr_collection) < 2)
{
lxb_dom_collection_destroy(tr_collection, true);
lxb_dom_collection_destroy(table_collection, true);
lxb_html_document_destroy(document);
goto finish;
}
// skip row 0 , since it has the previous folder header
for (size_t i = 2; i < lxb_dom_collection_length(tr_collection); i++)
{
DirEntry entry;
std::string title, aclass;
memset(&entry.modified, 0, sizeof(DateTime));
tr_element = lxb_dom_collection_element(tr_collection, i);
td_collection = lxb_dom_collection_make(&document->dom_document, 5);
status = lxb_dom_elements_by_tag_name(tr_element,
td_collection, (const lxb_char_t *)"td", 2);
if (status != LXB_STATUS_OK || lxb_dom_collection_length(td_collection) < 3)
{
lxb_dom_collection_destroy(td_collection, true);
lxb_dom_collection_destroy(tr_collection, true);
lxb_dom_collection_destroy(table_collection, true);
lxb_html_document_destroy(document);
goto finish;
}
// td0 contains the <a> tag
td_element = lxb_dom_collection_element(td_collection, 0);
lxb_dom_node_t *a_node = NextChildElement(td_element);
value = lxb_dom_element_local_name(lxb_dom_interface_element(a_node), &value_len);
tmp_string = std::string((const char *)value, value_len);
if (tmp_string.compare("a") != 0)
{
lxb_dom_collection_destroy(td_collection, true);
lxb_dom_collection_destroy(tr_collection, true);
lxb_dom_collection_destroy(table_collection, true);
lxb_html_document_destroy(document);
goto finish;
}
value = lxb_dom_element_get_attribute(lxb_dom_interface_element(a_node), (const lxb_char_t *)"href", 4, &value_len);
tmp_string = std::string((const char *)value, value_len);
if (tmp_string[tmp_string.length()-1] == '/')
tmp_string = tmp_string.substr(0, tmp_string.length()-1);
tmp_string = BaseClient::UnEscape(tmp_string);
sprintf(entry.name, "%s", tmp_string.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);
}
// next td contains file size, if fize size is "-", then it's a directory
td_element = lxb_dom_collection_element(td_collection, 1);
value = lxb_dom_node_text_content(NextChildTextNode(td_element), &value_len);
tmp_string = std::string((const char *)value, value_len);
if (tmp_string.compare("-") == 0)
{
entry.isDir = true;
entry.selectable = true;
entry.file_size = 0;
sprintf(entry.display_size, "%s", lang_strings[STR_FOLDER]);
}
else
{
entry.isDir = false;
entry.selectable = true;
uint64_t multiplier = 1;
std::vector<std::string> fsize_parts = Util::Split(tmp_string, " ");
float fsize = std::stof(fsize_parts[0]);
if (fsize_parts.size() > 1)
{
switch (fsize_parts[1][0])
{
case 'K':
multiplier = 1024;
break;
case 'M':
multiplier = 1048576;
break;
case 'G':
multiplier = 1073741824;
break;
default:
multiplier = 1;
}
}
entry.file_size = fsize * multiplier;
DirEntry::SetDisplaySize(&entry);
}
// next td contains the date
td_element = lxb_dom_collection_element(td_collection, 2);
value = lxb_dom_node_text_content(NextChildTextNode(td_element), &value_len);
tmp_string = std::string((const char *)value, value_len);
std::vector<std::string> date_time = Util::Split(tmp_string, " ");
if (date_time.size() > 1)
{
std::vector<std::string> adate = Util::Split(date_time[0], "-");
if (adate.size() == 3)
{
entry.modified.day = atoi(adate[0].c_str());
entry.modified.month = month_map[adate[1]];
entry.modified.year = atoi(adate[2].c_str());
}
std::vector<std::string> atime = Util::Split(date_time[1], ":");
if (atime.size() == 2)
{
entry.modified.hours = atoi(atime[0].c_str());
entry.modified.minutes = atoi(atime[1].c_str());
}
}
lxb_dom_collection_destroy(td_collection, true);
if (strcmp(entry.name, "..") != 0 && strcmp(entry.name, ".") != 0)
{
out.push_back(entry);
}
}
lxb_dom_collection_destroy(tr_collection, true);
lxb_dom_collection_destroy(table_collection, true);
lxb_html_document_destroy(document);
}
else
{
sprintf(this->response, "%s", httplib::to_string(res.error()).c_str());
return out;
}
finish:
return out;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef EZ_MYRIENT_H
#define EZ_MYRIENT_H
#include <string>
#include <vector>
#include "http/httplib.h"
#include "clients/remote_client.h"
#include "clients/baseclient.h"
#include "common.h"
class MyrientClient : public BaseClient
{
public:
std::vector<DirEntry> ListDir(const std::string &path);
};
#endif
+125 -45
View File
@@ -17,7 +17,7 @@
#include "util.h"
#include "system.h"
#define BUF_SIZE 256*1024
#define BUF_SIZE 256 * 1024
NfsClient::NfsClient()
{
@@ -27,7 +27,7 @@ NfsClient::~NfsClient()
{
}
int NfsClient::Connect(const std::string &url, const std::string &user, const std::string &pass)
int NfsClient::Connect(const std::string &url, const std::string &user, const std::string &pass, bool send_ping)
{
nfs = nfs_init_context();
if (nfs == nullptr)
@@ -37,7 +37,8 @@ int NfsClient::Connect(const std::string &url, const std::string &user, const st
}
struct nfs_url *nfsurl = nfs_parse_url_full(nfs, url.c_str());
if (nfsurl == nullptr) {
if (nfsurl == nullptr)
{
sprintf(response, "%s", nfs_get_error(nfs));
nfs_destroy_context(nfs);
return 0;
@@ -203,7 +204,7 @@ int NfsClient::Get(const std::string &outputfile, const std::string &ppath, uint
return 0;
}
FILE* out = FS::Create(outputfile);
FILE *out = FS::Create(outputfile);
if (out == NULL)
{
sprintf(response, "%s", lang_strings[STR_FAILED]);
@@ -213,6 +214,7 @@ int NfsClient::Get(const std::string &outputfile, const std::string &ppath, uint
void *buff = malloc(BUF_SIZE);
int count = 0;
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
while ((count = nfs_read(nfs, nfsfh, BUF_SIZE, buff)) > 0)
{
if (count < 0)
@@ -220,7 +222,7 @@ int NfsClient::Get(const std::string &outputfile, const std::string &ppath, uint
sprintf(response, "%s", nfs_get_error(nfs));
FS::Close(out);
nfs_close(nfs, nfsfh);
free((void*)buff);
free((void *)buff);
return 0;
}
FS::Write(out, buff, count);
@@ -228,7 +230,42 @@ int NfsClient::Get(const std::string &outputfile, const std::string &ppath, uint
}
FS::Close(out);
nfs_close(nfs, nfsfh);
free((void*)buff);
free((void *)buff);
return 1;
}
int NfsClient::Get(SplitFile *split_file, const std::string &ppath, uint64_t offset)
{
struct nfsfh *nfsfh = nullptr;
int ret = nfs_open(nfs, ppath.c_str(), 0400, &nfsfh);
if (ret != 0)
{
sprintf(response, "%s", nfs_get_error(nfs));
return 0;
}
void *buff = malloc(BUF_SIZE);
int count = 0;
while ((count = nfs_read(nfs, nfsfh, BUF_SIZE, buff)) != 0)
{
if (count < 0)
{
sprintf(response, "%s", nfs_get_error(nfs));
nfs_close(nfs, nfsfh);
free((void *)buff);
return 0;
}
ret = split_file->Write((char *)buff, count);
if (ret < 0)
{
nfs_close(nfs, nfsfh);
free((void *)buff);
return 0;
}
}
nfs_close(nfs, nfsfh);
free((void *)buff);
return 1;
}
@@ -242,49 +279,51 @@ int NfsClient::GetRange(const std::string &path, DataSink &sink, uint64_t size,
return 0;
}
ret = nfs_lseek(nfs, nfsfh, offset, SEEK_SET, NULL);
ret = this->GetRange((void *)nfsfh, sink, size, offset);
nfs_close(nfs, nfsfh);
return ret;
}
int NfsClient::GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset)
{
struct nfsfh *nfsfh = (struct nfsfh *)fp;
int ret = nfs_lseek(nfs, nfsfh, offset, SEEK_SET, NULL);
if (ret != 0)
{
return 0;
}
void *buff = malloc(BUF_SIZE);
int count = 0;
size_t bytes_remaining = size;
do
{
size_t bytes_to_read = std::min<size_t>(BUF_SIZE, bytes_remaining);
count = nfs_read(nfs, nfsfh, bytes_to_read, buff);
if (count > 0)
{
bytes_remaining -= count;
bool ok = sink.write((char*)buff, count);
int count = 0;
size_t bytes_remaining = size;
do
{
size_t bytes_to_read = std::min<size_t>(BUF_SIZE, bytes_remaining);
count = nfs_read(nfs, nfsfh, bytes_to_read, buff);
if (count > 0)
{
bytes_remaining -= count;
bool ok = sink.write((char *)buff, count);
if (!ok)
{
free((void *)buff);
nfs_close(nfs, nfsfh);
return 0;
}
}
else
{
break;
}
} while (1);
free((void *)buff);
nfs_close(nfs, nfsfh);
}
else
{
break;
}
} while (1);
free((void *)buff);
return 1;
}
int NfsClient::GetRange(const std::string &ppath, void *buffer, uint64_t size, uint64_t offset)
{
if (!FileExists(ppath))
{
return 0;
}
struct nfsfh *nfsfh = nullptr;
int ret = nfs_open(nfs, ppath.c_str(), 0400, &nfsfh);
if (ret != 0)
@@ -293,18 +332,45 @@ int NfsClient::GetRange(const std::string &ppath, void *buffer, uint64_t size, u
return 0;
}
ret = nfs_lseek(nfs, nfsfh, offset, SEEK_SET, NULL);
ret = this->GetRange(nfsfh, buffer, size, offset);
nfs_close(nfs, nfsfh);
return ret;
}
int NfsClient::GetRange(void *fp, void *buffer, uint64_t size, uint64_t offset)
{
struct nfsfh *nfsfh = (struct nfsfh *)fp;
int ret = nfs_lseek(nfs, nfsfh, offset, SEEK_SET, NULL);
if (ret != 0)
{
sprintf(response, "%s", nfs_get_error(nfs));
return 0;
}
int count = nfs_read(nfs, nfsfh, size, buffer);
nfs_close(nfs, nfsfh);
if (count != size)
return 0;
size_t bytes_remaining = size;
char *buff = (char*)buffer;
int total = 0;
int count = 0;
do
{
count = nfs_read(nfs, nfsfh, bytes_remaining, buff);
if (count > 0)
{
bytes_remaining -= count;
buff += count;
total += count;
}
else
{
break;
}
} while (1);
if (total != size)
return 0;
return 1;
}
@@ -346,13 +412,13 @@ int NfsClient::Put(const std::string &inputfile, const std::string &ppath, uint6
return 0;
}
FILE* in = FS::OpenRead(inputfile);
FILE *in = FS::OpenRead(inputfile);
if (in == NULL)
{
sprintf(response, "%s", lang_strings[STR_FAILED]);
return 0;
}
struct nfsfh *nfsfh = nullptr;
int ret;
if (!FileExists(ppath))
@@ -368,14 +434,15 @@ int NfsClient::Put(const std::string &inputfile, const std::string &ppath, uint6
return 0;
}
void* buff = malloc(BUF_SIZE);
uint64_t count = 0;
void *buff = malloc(BUF_SIZE);
int count = 0;
bytes_transfered = 0;
while ((count = FS::Read(in, buff, BUF_SIZE)) > 0)
sceRtcGetCurrentTick(&prev_tick);
while ((count = FS::Read(in, buff, BUF_SIZE)) != 0)
{
if (count < 0)
{
sprintf(response, "%s", lang_strings[STR_FAILED]);
snprintf(response, sizeof(response), "%s", lang_strings[STR_FAILED]);
FS::Close(in);
nfs_close(nfs, nfsfh);
free(buff);
@@ -446,7 +513,8 @@ std::vector<DirEntry> NfsClient::ListDir(const std::string &path)
struct nfsdirent *nfsdirent;
int ret = nfs_opendir(nfs, path.c_str(), &nfsdir);
if (ret != 0) {
if (ret != 0)
{
sprintf(response, "%s", nfs_get_error(nfs));
return out;
}
@@ -513,7 +581,6 @@ std::vector<DirEntry> NfsClient::ListDir(const std::string &path)
}
if (strcmp(entry.name, "..") != 0 && strcmp(entry.name, ".") != 0)
out.push_back(entry);
}
nfs_closedir(nfs, nfsdir);
@@ -553,6 +620,19 @@ int NfsClient::Head(const std::string &ppath, void *buffer, uint64_t len)
return 1;
}
void *NfsClient::Open(const std::string &path, int flags)
{
struct nfsfh *nfsfh = nullptr;
nfs_open(nfs, path.c_str(), 0400, &nfsfh);
;
return nfsfh;
}
void NfsClient::Close(void *fp)
{
nfs_close(nfs, (struct nfsfh *)fp);
}
ClientType NfsClient::clientType()
{
return CLIENT_TYPE_NFS;
+9 -4
View File
@@ -1,5 +1,5 @@
#ifndef NFSCLIENT_H
#define NFSCLIENT_H
#ifndef EZ_NFSCLIENT_H
#define EZ_NFSCLIENT_H
#include <sys/socket.h>
#include <arpa/inet.h>
@@ -18,13 +18,16 @@ class NfsClient : public RemoteClient
public:
NfsClient();
~NfsClient();
int Connect(const std::string &url, const std::string &user, const std::string &pass);
int Connect(const std::string &url, const std::string &user, const std::string &pass, bool send_ping=false);
int Mkdir(const std::string &path);
int Rmdir(const std::string &path, bool recursive);
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 GetRange(void *fp, void *buffer, uint64_t size, uint64_t offset);
int GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset);
int Put(const std::string &inputfile, const std::string &path, uint64_t offset=0);
int Rename(const std::string &src, const std::string &dst);
int Delete(const std::string &path);
@@ -32,6 +35,8 @@ public:
int Copy(const std::string &from, const std::string &to);
int Move(const std::string &from, const std::string &to);
std::vector<DirEntry> ListDir(const std::string &path);
void *Open(const std::string &path, int flags);
void Close(void *fp);
bool IsConnected();
bool Ping();
const char *LastResponse();
@@ -48,4 +53,4 @@ private:
bool connected = false;
};
#endif
#endif
+1 -1
View File
@@ -84,7 +84,7 @@ std::vector<DirEntry> NginxClient::ListDir(const std::string &path)
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);
tmp = BaseClient::UnEscape(tmp);
if (tmp.compare("..") != 0)
{
sprintf(entry.directory, "%s", path.c_str());
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef NGINX_H
#define NGINX_H
#ifndef EZ_NGINX_H
#define EZ_NGINX_H
#include <string>
#include <vector>
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef NPXSERVE_H
#define NPXSERVE_H
#ifndef EZ_NPXSERVE_H
#define EZ_NPXSERVE_H
#include <string>
#include <vector>
+2 -3
View File
@@ -8,7 +8,6 @@
#include "util.h"
#include "system.h"
#include "windows.h"
#include "dbglogger.h"
using httplib::Client;
using httplib::Headers;
@@ -142,7 +141,7 @@ std::vector<DirEntry> RCloneClient::ListDir(const std::string &path)
tmp_string = std::string((const char *)value, value_len);
if (tmp_string[tmp_string.length()-1] == '/')
tmp_string = tmp_string.substr(0, tmp_string.length()-1);
tmp_string = BaseClient::DecodeUrl(tmp_string);
tmp_string = BaseClient::UnEscape(tmp_string);
sprintf(entry.name, "%s", tmp_string.c_str());
sprintf(entry.directory, "%s", path.c_str());
if (path.length() > 0 && path[path.length() - 1] == '/')
@@ -161,7 +160,7 @@ std::vector<DirEntry> RCloneClient::ListDir(const std::string &path)
lxb_dom_node_t *size_node = NextChildElement(td_element);
value = lxb_dom_node_text_content(size_node->first_child, &value_len);
tmp_string = std::string((const char *)value, value_len);
entry.file_size = atoi(tmp_string.c_str());
entry.file_size = atol(tmp_string.c_str());
DirEntry::SetDisplaySize(&entry);
}
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef RCLONE_H
#define RCLONE_H
#ifndef EZ_RCLONE_H
#define EZ_RCLONE_H
#include <string>
#include <vector>
+13 -5
View File
@@ -1,10 +1,11 @@
#ifndef REMOTECLIENT_H
#define REMOTECLIENT_H
#ifndef EZ_REMOTECLIENT_H
#define EZ_REMOTECLIENT_H
#include <string>
#include <vector>
#include "common.h"
#include "http/httplib.h"
#include "split_file.h"
enum RemoteActions
{
@@ -21,7 +22,8 @@ enum RemoteActions
REMOTE_ACTION_EDIT = 512,
REMOTE_ACTION_NEW_FILE = 1024,
REMOTE_ACTION_EXTRACT = 2048,
REMOTE_ACTION_ALL = 4095
REMOTE_ACTION_RAW_READ = 4096,
REMOTE_ACTION_ALL = 8191
};
enum ClientType
@@ -33,6 +35,7 @@ enum ClientType
CLIENT_TYPE_HTTP_SERVER,
CLIENT_TYPE_GOOGLE,
CLIENT_TYPE_NFS,
CLIENT_TYPE_FILEHOST,
CLINET_TYPE_UNKNOWN
};
@@ -43,11 +46,12 @@ class RemoteClient
public:
RemoteClient(){};
virtual ~RemoteClient(){};
virtual int Connect(const std::string &url, const std::string &username, const std::string &password) = 0;
virtual int Connect(const std::string &url, const std::string &username, const std::string &password, bool send_ping=false) = 0;
virtual int Mkdir(const std::string &path) = 0;
virtual int Rmdir(const std::string &path, bool recursive) = 0;
virtual int Size(const std::string &path, int64_t *size) = 0;
virtual int Get(const std::string &outputfile, const std::string &path, uint64_t offset=0) = 0;
virtual int Get(SplitFile *split_file, const std::string &path, uint64_t offset=0) = 0;
virtual int Put(const std::string &inputfile, const std::string &path, uint64_t offset=0) = 0;
virtual int Rename(const std::string &src, const std::string &dst) = 0;
virtual int Delete(const std::string &path) = 0;
@@ -56,8 +60,12 @@ public:
virtual int Head(const std::string &path, void *buffer, uint64_t len) = 0;
virtual int GetRange(const std::string &path, void *buffer, uint64_t size, uint64_t offset) = 0;
virtual int GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset) = 0;
virtual int GetRange(void *fp, void *buffer, uint64_t size, uint64_t offset) = 0;
virtual int GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset) = 0;
virtual bool FileExists(const std::string &path) = 0;
virtual std::vector<DirEntry> ListDir(const std::string &path) = 0;
virtual void *Open(const std::string &path, int flags) = 0;
virtual void Close(void *fp) = 0;
virtual std::string GetPath(std::string path1, std::string path2) = 0;
virtual bool IsConnected() = 0;
virtual bool Ping() = 0;
@@ -67,4 +75,4 @@ public:
virtual uint32_t SupportedActions() = 0;
};
#endif
#endif
+67 -6
View File
@@ -23,7 +23,7 @@ SFTPClient::SFTPClient()
SFTPClient::~SFTPClient(){};
int SFTPClient::Connect(const std::string &url, const std::string &username, const std::string &password)
int SFTPClient::Connect(const std::string &url, const std::string &username, const std::string &password, bool send_ping)
{
int port = 22;
std::string host = url.substr(7);
@@ -284,6 +284,8 @@ int SFTPClient::Get(const std::string &outputfile, const std::string &path, uint
char *buff = (char *)malloc(FTP_CLIENT_BUFSIZ);
int rc, count = 0;
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
do
{
rc = libssh2_sftp_read(sftp_handle, buff, FTP_CLIENT_BUFSIZ);
@@ -304,6 +306,36 @@ int SFTPClient::Get(const std::string &outputfile, const std::string &path, uint
return 1;
}
int SFTPClient::Get(SplitFile *split_file, const std::string &path, uint64_t offset)
{
LIBSSH2_SFTP_HANDLE *sftp_handle = libssh2_sftp_open(sftp_session, path.c_str(), LIBSSH2_FXF_READ, 0);
if (!sftp_handle)
{
sprintf(response, "Unable to open file with SFTP: %ld", libssh2_sftp_last_error(sftp_session));
return 0;
}
char *buff = (char *)malloc(FTP_CLIENT_BUFSIZ);
int rc, count = 0;
do
{
rc = libssh2_sftp_read(sftp_handle, buff, FTP_CLIENT_BUFSIZ);
if (rc > 0)
{
split_file->Write(buff, rc);
}
else
{
break;
}
} while (1);
free((char *)buff);
libssh2_sftp_close(sftp_handle);
return 1;
}
int SFTPClient::GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset)
{
LIBSSH2_SFTP_HANDLE *sftp_handle = libssh2_sftp_open(sftp_session, path.c_str(), LIBSSH2_FXF_READ, 0);
@@ -313,6 +345,16 @@ int SFTPClient::GetRange(const std::string &path, DataSink &sink, uint64_t size,
return 0;
}
int ret = this->GetRange((void *)sftp_handle, sink, size, offset);
libssh2_sftp_close(sftp_handle);
return ret;
}
int SFTPClient::GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset)
{
LIBSSH2_SFTP_HANDLE *sftp_handle = (LIBSSH2_SFTP_HANDLE *)fp;
libssh2_sftp_seek64(sftp_handle, offset);
char *buff = (char *)malloc(FTP_CLIENT_BUFSIZ);
@@ -329,7 +371,6 @@ int SFTPClient::GetRange(const std::string &path, DataSink &sink, uint64_t size,
if (!ok)
{
free((char *)buff);
libssh2_sftp_close(sftp_handle);
return 0;
}
}
@@ -340,9 +381,8 @@ int SFTPClient::GetRange(const std::string &path, DataSink &sink, uint64_t size,
} while (1);
free((char *)buff);
libssh2_sftp_close(sftp_handle);
return 1;
return 1;
}
int SFTPClient::GetRange(const std::string &path, void *buffer, uint64_t size, uint64_t offset)
@@ -359,9 +399,18 @@ int SFTPClient::GetRange(const std::string &path, void *buffer, uint64_t size, u
return 0;
}
int ret = this->GetRange(sftp_handle, buffer, size, offset);
libssh2_sftp_close(sftp_handle);
return ret;
}
int SFTPClient::GetRange(void *fp, void *buffer, uint64_t size, uint64_t offset)
{
LIBSSH2_SFTP_HANDLE *sftp_handle = (LIBSSH2_SFTP_HANDLE *)fp;
libssh2_sftp_seek64(sftp_handle, offset);
int count = libssh2_sftp_read(sftp_handle, (char *)buffer, size);
libssh2_sftp_close(sftp_handle);
if (count != size)
return 0;
@@ -401,6 +450,8 @@ int SFTPClient::Put(const std::string &inputfile, const std::string &path, uint6
buff = (char *)malloc(FTP_CLIENT_BUFSIZ);
int nread, count = 0;
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
do
{
nread = FS::Read(in, buff, FTP_CLIENT_BUFSIZ);
@@ -642,6 +693,16 @@ int SFTPClient::Quit()
return 1;
}
void *SFTPClient::Open(const std::string &path, int flags)
{
return libssh2_sftp_open(sftp_session, path.c_str(), LIBSSH2_FXF_READ, 0);
}
void SFTPClient::Close(void *fp)
{
libssh2_sftp_close((LIBSSH2_SFTP_HANDLE *)fp);
}
ClientType SFTPClient::clientType()
{
return CLIENT_TYPE_SFTP;
@@ -650,4 +711,4 @@ ClientType SFTPClient::clientType()
uint32_t SFTPClient::SupportedActions()
{
return REMOTE_ACTION_ALL ^ REMOTE_ACTION_CUT ^ REMOTE_ACTION_COPY ^ REMOTE_ACTION_PASTE;
}
}
+9 -4
View File
@@ -1,5 +1,5 @@
#ifndef SFTPCLIENT_H
#define SFTPCLIENT_H
#ifndef EZ_SFTPCLIENT_H
#define EZ_SFTPCLIENT_H
#include <libssh2.h>
#include <libssh2_sftp.h>
@@ -14,14 +14,17 @@ class SFTPClient : public RemoteClient
public:
SFTPClient();
~SFTPClient();
int Connect(const std::string &url, const std::string &username, const std::string &password);
int Connect(const std::string &url, const std::string &username, const std::string &password, bool send_ping=false);
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 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 GetRange(void *fp, void *buffer, uint64_t size, uint64_t offset);
int GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset);
int Put(const std::string &inputfile, const std::string &path, uint64_t offset=0);
int Rename(const std::string &src, const std::string &dst);
int Delete(const std::string &path);
@@ -30,6 +33,8 @@ public:
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);
void *Open(const std::string &path, int flags);
void Close(void *fp);
std::string GetPath(std::string path1, std::string path2);
bool IsConnected();
bool Ping();
@@ -46,4 +51,4 @@ protected:
bool connected = false;
};
#endif
#endif
+180 -54
View File
@@ -24,34 +24,43 @@ SmbClient::~SmbClient()
{
}
int SmbClient::Connect(const std::string &url, const std::string &user, const std::string &pass)
int SmbClient::Connect(const std::string &url, const std::string &user, const std::string &pass, bool send_ping)
{
struct smb2_url *smb_url;
smb2 = smb2_init_context();
if (smb2 == NULL)
{
sprintf(response, "Failed to init SMB context");
snprintf(response, sizeof(response), "Failed to init SMB context");
return 0;
}
smb_url = smb2_parse_url(smb2, url.c_str());
if (smb_url == NULL || smb_url->share == NULL || strlen(smb_url->share) == 0)
{
sprintf(response, "Invalid SMB Url");
if (smb_url != NULL)
smb2_destroy_url(smb_url);
smb2_destroy_context(smb2);
smb2 = NULL;
snprintf(response, sizeof(response), "Invalid SMB Url");
return 0;
}
if (pass.length() > 0)
smb2_set_password(smb2, pass.c_str());
smb2_set_security_mode(smb2, SMB2_NEGOTIATE_SIGNING_ENABLED);
smb2_set_version(smb2, SMB2_VERSION_ANY);
smb2_set_timeout(smb2, 30);
if (smb2_connect_share(smb2, smb_url->server, smb_url->share, user.c_str()) < 0)
{
sprintf(response, "%s", smb2_get_error(smb2));
snprintf(response, sizeof(response), "%s", smb2_get_error(smb2));
smb2_destroy_url(smb_url);
smb2_destroy_context(smb2);
smb2 = NULL;
return 0;
}
smb2_destroy_url(smb_url);
max_read_size = smb2_get_max_read_size(smb2);
max_write_size = smb2_get_max_write_size(smb2);
@@ -109,7 +118,7 @@ int SmbClient::Mkdir(const std::string &ppath)
path = Util::Trim(path, "/");
if (smb2_mkdir(smb2, path.c_str()) != 0)
{
sprintf(response, "%s", smb2_get_error(smb2));
snprintf(response, sizeof(response), "%s", smb2_get_error(smb2));
return 0;
}
return 1;
@@ -126,7 +135,7 @@ int SmbClient::_Rmdir(const std::string &ppath)
path = Util::Trim(path, "/");
if (smb2_rmdir(smb2, path.c_str()) != 0)
{
sprintf(response, "%s", smb2_get_error(smb2));
snprintf(response, sizeof(response), "%s", smb2_get_error(smb2));
return 0;
}
return 1;
@@ -193,32 +202,42 @@ int SmbClient::Get(const std::string &outputfile, const std::string &ppath, uint
path = Util::Trim(path, "/");
if (!Size(path.c_str(), &bytes_to_download))
{
sprintf(response, "%s", smb2_get_error(smb2));
snprintf(response, sizeof(response), "%s", smb2_get_error(smb2));
return 0;
}
struct smb2fh* in = smb2_open(smb2, path.c_str(), O_RDONLY);
if (in == NULL)
{
sprintf(response, "%s", smb2_get_error(smb2));
snprintf(response, sizeof(response), "%s", smb2_get_error(smb2));
return 0;
}
FILE* out = FS::Create(outputfile);
if (out == NULL)
{
sprintf(response, "%s", lang_strings[STR_FAILED]);
snprintf(response, sizeof(response), "%s", lang_strings[STR_FAILED]);
smb2_close(smb2, in);
return 0;
}
uint8_t *buff = (uint8_t*)malloc(max_read_size);
if (buff == NULL)
{
snprintf(response, sizeof(response), "%s", lang_strings[STR_FAILED]);
FS::Close(out);
smb2_close(smb2, in);
return 0;
}
int count = 0;
bytes_transfered = 0;
while ((count = smb2_read(smb2, in, buff, max_read_size)) > 0)
sceRtcGetCurrentTick(&prev_tick);
while ((count = smb2_read(smb2, in, buff, max_read_size)) != 0)
{
if (count < 0)
{
sprintf(response, "%s", smb2_get_error(smb2));
snprintf(response, sizeof(response), "%s", smb2_get_error(smb2));
FS::Close(out);
smb2_close(smb2, in);
free((void*)buff);
@@ -233,44 +252,94 @@ int SmbClient::Get(const std::string &outputfile, const std::string &ppath, uint
return 1;
}
int SmbClient::Get(SplitFile *split_file, const std::string &ppath, uint64_t offset)
{
std::string path = std::string(ppath);
path = Util::Trim(path, "/");
struct smb2fh *in = smb2_open(smb2, path.c_str(), O_RDONLY);
if (in == NULL)
{
snprintf(response, sizeof(response), "%s", smb2_get_error(smb2));
return 0;
}
uint8_t *buff = (uint8_t *)malloc(max_read_size);
if (buff == NULL)
{
smb2_close(smb2, in);
return 0;
}
int count = 0;
while ((count = smb2_read(smb2, in, buff, max_read_size)) != 0)
{
if (count < 0)
{
snprintf(response, sizeof(response), "%s", smb2_get_error(smb2));
smb2_close(smb2, in);
free((void *)buff);
return 0;
}
if (split_file->Write((char*)buff, count) < 0)
{
smb2_close(smb2, in);
free((void *)buff);
return 0;
}
}
smb2_close(smb2, in);
free((void *)buff);
return 1;
}
int SmbClient::GetRange(const std::string &ppath, DataSink &sink, uint64_t size, uint64_t offset)
{
std::string path = std::string(ppath);
path = Util::Trim(path, "/");
struct smb2fh* in = smb2_open(smb2, path.c_str(), O_RDONLY);
struct smb2fh *in = smb2_open(smb2, path.c_str(), O_RDONLY);
if (in == NULL)
{
return 0;
}
smb2_lseek(smb2, in, offset, SEEK_SET, NULL);
int ret = this->GetRange((void *)in, sink, size, offset);
smb2_close(smb2, in);
uint8_t *buff = (uint8_t*)malloc(max_read_size);
int count = 0;
size_t bytes_remaining = size;
do
{
size_t bytes_to_read = std::min<size_t>(max_read_size, bytes_remaining);
count = smb2_read(smb2, in, buff, bytes_to_read);
if (count > 0)
{
bytes_remaining -= count;
bool ok = sink.write((char*)buff, count);
return ret;
}
int SmbClient::GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset)
{
struct smb2fh *in = (struct smb2fh *)fp;
smb2_lseek(smb2, in, offset, SEEK_SET, NULL);
uint8_t *buff = (uint8_t *)malloc(max_read_size);
int count = 0;
size_t bytes_remaining = size;
do
{
size_t bytes_to_read = std::min<size_t>(max_read_size, bytes_remaining);
count = smb2_read(smb2, in, buff, bytes_to_read);
if (count > 0)
{
bytes_remaining -= count;
bool ok = sink.write((char *)buff, count);
if (!ok)
{
free((uint8_t *)buff);
smb2_close(smb2, in);
return 0;
}
}
else
{
break;
}
} while (1);
}
else
{
break;
}
} while (1);
free((char *)buff);
smb2_close(smb2, in);
free((char *)buff);
return 1;
}
@@ -285,19 +354,45 @@ int SmbClient::GetRange(const std::string &ppath, void *buffer, uint64_t size, u
return 0;
}
struct smb2fh* in = smb2_open(smb2, path.c_str(), O_RDONLY);
struct smb2fh *in = smb2_open(smb2, path.c_str(), O_RDONLY);
if (in == NULL)
{
return 0;
}
int ret = this->GetRange(in, buffer, size, offset);
smb2_close(smb2, in);
return ret;
}
int SmbClient::GetRange(void *fp, void *buffer, uint64_t size, uint64_t offset)
{
struct smb2fh *in = (struct smb2fh *)fp;
smb2_lseek(smb2, in, offset, SEEK_SET, NULL);
int count = smb2_read(smb2, in, (uint8_t*)buffer, size);
smb2_close(smb2, in);
if (count != size)
size_t bytes_remaining = size;
uint8_t *buff = (uint8_t*)buffer;
int count = 0;
uint64_t total = 0;
do
{
count = smb2_read(smb2, in, buff, bytes_remaining);
if (count > 0)
{
bytes_remaining -= count;
buff += count;
total += count;
}
else
{
break;
}
} while (1);
if (total != size)
return 0;
return 1;
}
@@ -323,14 +418,14 @@ int SmbClient::CopyToSocket(const std::string &ppath, int socket_fd)
return 0;
}
struct smb2fh* in = smb2_open(smb2, path.c_str(), O_RDONLY);
struct smb2fh *in = smb2_open(smb2, path.c_str(), O_RDONLY);
if (in == NULL)
{
sprintf(response, "%s", smb2_get_error(smb2));
return 0;
}
uint8_t *buff = (uint8_t*)malloc(max_read_size);
uint8_t *buff = (uint8_t *)malloc(max_read_size);
int count = 0;
while ((count = smb2_read(smb2, in, buff, max_read_size)) > 0)
{
@@ -338,7 +433,7 @@ int SmbClient::CopyToSocket(const std::string &ppath, int socket_fd)
{
sprintf(response, "%s", smb2_get_error(smb2));
smb2_close(smb2, in);
free((void*)buff);
free((void *)buff);
return 0;
}
int ret = sceNetSend(socket_fd, buff, count, 0);
@@ -348,7 +443,7 @@ int SmbClient::CopyToSocket(const std::string &ppath, int socket_fd)
}
}
smb2_close(smb2, in);
free((void*)buff);
free((void *)buff);
return 1;
}
@@ -360,7 +455,7 @@ bool SmbClient::FileExists(const std::string &ppath)
int ret = smb2_stat(smb2, path.c_str(), &st);
if (ret != 0)
{
sprintf(response, "%s", smb2_get_error(smb2));
snprintf(response, sizeof(response), "%s", smb2_get_error(smb2));
return false;
}
@@ -387,31 +482,48 @@ int SmbClient::Put(const std::string &inputfile, const std::string &ppath, uint6
FILE* in = FS::OpenRead(inputfile);
if (in == NULL)
{
sprintf(response, "%s", lang_strings[STR_FAILED]);
snprintf(response, sizeof(response), "%s", lang_strings[STR_FAILED]);
return 0;
}
struct smb2fh* out = smb2_open(smb2, path.c_str(), O_WRONLY | O_CREAT | O_TRUNC);
if (out == NULL)
{
sprintf(response, "%s", smb2_get_error(smb2));
snprintf(response, sizeof(response), "%s", smb2_get_error(smb2));
FS::Close(in);
return 0;
}
uint8_t* buff = (uint8_t*)malloc(max_write_size);
if (buff == NULL)
{
snprintf(response, sizeof(response), "%s", lang_strings[STR_FAILED]);
FS::Close(in);
smb2_close(smb2, out);
return 0;
}
int count = 0;
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
while ((count = FS::Read(in, buff, max_write_size)) > 0)
{
if (count < 0)
{
sprintf(response, "%s", lang_strings[STR_FAILED]);
snprintf(response, sizeof(response), "%s", lang_strings[STR_FAILED]);
FS::Close(in);
smb2_close(smb2, out);
free(buff);
return 0;
}
if (smb2_write(smb2, out, buff, count) < 0)
{
snprintf(response, sizeof(response), "%s", smb2_get_error(smb2));
FS::Close(in);
smb2_close(smb2, out);
free(buff);
return 0;
}
smb2_write(smb2, out, buff, count);
bytes_transfered += count;
}
FS::Close(in);
@@ -419,7 +531,6 @@ int SmbClient::Put(const std::string &inputfile, const std::string &ppath, uint6
free(buff);
return 1;
}
int SmbClient::Rename(const std::string &src, const std::string &dst)
@@ -430,7 +541,7 @@ int SmbClient::Rename(const std::string &src, const std::string &dst)
path2 = Util::Trim(path2, "/");
if (smb2_rename(smb2, path1.c_str(), path2.c_str()) != 0)
{
sprintf(response, "%s", smb2_get_error(smb2));
snprintf(response, sizeof(response), "%s", smb2_get_error(smb2));
return 0;
}
@@ -443,7 +554,7 @@ int SmbClient::Delete(const std::string &ppath)
path = Util::Trim(path, "/");
if (smb2_unlink(smb2, path.c_str()) != 0)
{
sprintf(response, "%s", smb2_get_error(smb2));
snprintf(response, sizeof(response), "%s", smb2_get_error(smb2));
return 0;
}
@@ -457,7 +568,7 @@ int SmbClient::Size(const std::string &ppath, int64_t *size)
smb2_stat_64 st;
if (smb2_stat(smb2, path.c_str(), &st) != 0)
{
sprintf(response, "%s", smb2_get_error(smb2));
snprintf(response, sizeof(response), "%s", smb2_get_error(smb2));
return 0;
}
*size = st.smb2_size;
@@ -526,7 +637,8 @@ std::vector<DirEntry> SmbClient::ListDir(const std::string &path)
sprintf(entry.display_size, "%s", lang_strings[STR_FOLDER]);
break;
}
if (strcmp(entry.name, "..") != 0 && strcmp(entry.name, ".") != 0)
if (strcmp(entry.name, "..") != 0 && strcmp(entry.name, ".") != 0 &&
(show_hidden_files || entry.name[0] != '.'))
out.push_back(entry);
}
smb2_closedir(smb2, dir);
@@ -559,14 +671,28 @@ int SmbClient::Head(const std::string &ppath, void *buffer, uint64_t len)
return 0;
}
int count = smb2_read(smb2, in, (uint8_t*)buffer, len);
uint64_t count = smb2_read(smb2, in, (uint8_t*)buffer, len);
smb2_close(smb2, in);
if (count != len)
if (count < 0 || count != len)
return 0;
return 1;
}
void *SmbClient::Open(const std::string &ppath, int flags)
{
std::string path = std::string(ppath);
path = Util::Trim(path, "/");
struct smb2fh *in = smb2_open(smb2, path.c_str(), flags);
return in;
}
void SmbClient::Close(void *fp)
{
smb2_close(smb2, (struct smb2fh *)fp);
}
ClientType SmbClient::clientType()
{
return CLIENT_TYPE_SMB;
+9 -4
View File
@@ -1,5 +1,5 @@
#ifndef SMBCLIENT_H
#define SMBCLIENT_H
#ifndef EZ_SMBCLIENT_H
#define EZ_SMBCLIENT_H
#include <sys/socket.h>
#include <arpa/inet.h>
@@ -19,13 +19,16 @@ class SmbClient : public RemoteClient
public:
SmbClient();
~SmbClient();
int Connect(const std::string &url, const std::string &user, const std::string &pass);
int Connect(const std::string &url, const std::string &user, const std::string &pass, bool send_ping=false);
int Mkdir(const std::string &path);
int Rmdir(const std::string &path, bool recursive);
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 GetRange(void *fp, void *buffer, uint64_t size, uint64_t offset);
int GetRange(void *fp, DataSink &sink, uint64_t size, uint64_t offset);
int Put(const std::string &inputfile, const std::string &path, uint64_t offset=0);
int Rename(const std::string &src, const std::string &dst);
int Delete(const std::string &path);
@@ -34,6 +37,8 @@ public:
int Move(const std::string &from, const std::string &to);
int CopyToSocket(const std::string &path, int socket_fd);
std::vector<DirEntry> ListDir(const std::string &path);
void *Open(const std::string &path, int flags);
void Close(void *fp);
bool IsConnected();
bool Ping();
const char *LastResponse();
@@ -52,4 +57,4 @@ private:
uint32_t max_write_size = 0;
};
#endif
#endif
+324
View File
@@ -0,0 +1,324 @@
#include <fstream>
#include "common.h"
#include "clients/remote_client.h"
#include "clients/webdav.h"
#include "pugixml/pugiext.hpp"
#include "fs.h"
#include "lang.h"
#include "util.h"
#include "system.h"
#include "windows.h"
using httplib::Client;
using httplib::ContentProvider;
using httplib::Headers;
using httplib::Progress;
using httplib::Result;
static const char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
std::string WebDAVClient::GetHttpUrl(std::string url)
{
std::string http_url = std::regex_replace(url, std::regex("webdav://"), "http://");
http_url = std::regex_replace(http_url, std::regex("webdavs://"), "https://");
return http_url;
}
int WebDAVClient::Connect(const std::string &host, const std::string &user, const std::string &pass, bool send_ping)
{
std::string url = GetHttpUrl(host);
return BaseClient::Connect(url, user, pass, send_ping);
}
Result WebDAVClient::PropFind(const std::string &path, int depth)
{
Request req;
Headers header = {{"Accept", "*/*"}, {"Depth", std::to_string(depth)}};
req.method = "PROPFIND";
req.path = path;
req.headers = header;
req.progress = Progress();
return client->send(req);
}
int WebDAVClient::Size(const std::string &path, int64_t *size)
{
std::string encoded_path = httplib::detail::encode_url(GetFullPath(path));
if (auto res = PropFind(encoded_path, 0))
{
if (HTTP_SUCCESS(res->status))
{
pugi::xml_document document;
document.load_buffer(res->body.c_str(), res->body.length());
auto multistatus = document.select_node("*[local-name()='multistatus']").node();
auto responses = multistatus.select_nodes("*[local-name()='response']");
for (auto response : responses)
{
pugi::xml_node href = response.node().select_node("*[local-name()='href']").node();
std::string resource_path = httplib::detail::decode_url(href.first_child().value(), true);
auto target_path_without_sep = GetFullPath(path);
if (!target_path_without_sep.empty() && target_path_without_sep.back() == '/')
target_path_without_sep.resize(target_path_without_sep.length() - 1);
auto resource_path_without_sep = resource_path.erase(resource_path.find_last_not_of('/') + 1);
size_t pos = resource_path_without_sep.find(this->host_url);
if (pos != std::string::npos)
resource_path_without_sep.erase(pos, this->host_url.length());
if (resource_path_without_sep != target_path_without_sep)
continue;
auto propstat = response.node().select_node("*[local-name()='propstat']").node();
auto prop = propstat.select_node("*[local-name()='prop']").node();
std::string content_length = prop.select_node("*[local-name()='getcontentlength']").node().first_child().value();
*size = std::strtoll(content_length.c_str(), nullptr, 10);
return 1;
}
}
}
else
{
sprintf(this->response, "%s", httplib::to_string(res.error()).c_str());
}
return 0;
}
std::vector<DirEntry> WebDAVClient::ListDir(const std::string &path)
{
std::vector<DirEntry> out;
DirEntry entry;
Util::SetupPreviousFolder(path, &entry);
out.push_back(entry);
std::string encoded_path = httplib::detail::encode_url(GetFullPath(path));
if (auto res = PropFind(encoded_path, 1))
{
pugi::xml_document document;
document.load_buffer(res->body.c_str(), res->body.length());
auto multistatus = document.select_node("*[local-name()='multistatus']").node();
auto responses = multistatus.select_nodes("*[local-name()='response']");
for (auto response : responses)
{
pugi::xml_node href = response.node().select_node("*[local-name()='href']").node();
std::string resource_path = httplib::detail::decode_url(href.first_child().value(), true);
auto target_path_without_sep = GetFullPath(path);
if (!target_path_without_sep.empty() && target_path_without_sep.back() == '/')
target_path_without_sep.resize(target_path_without_sep.length() - 1);
auto resource_path_without_sep = resource_path.erase(resource_path.find_last_not_of('/') + 1);
size_t pos = resource_path_without_sep.find(this->host_url);
if (pos != std::string::npos)
resource_path_without_sep.erase(pos, this->host_url.length());
if (resource_path_without_sep == target_path_without_sep)
continue;
pos = resource_path_without_sep.find_last_of('/');
auto name = resource_path_without_sep.substr(pos + 1);
auto propstat = response.node().select_node("*[local-name()='propstat']").node();
auto prop = propstat.select_node("*[local-name()='prop']").node();
std::string creation_date = prop.select_node("*[local-name()='creationdate']").node().first_child().value();
std::string content_length = prop.select_node("*[local-name()='getcontentlength']").node().first_child().value();
std::string m_date = prop.select_node("*[local-name()='getlastmodified']").node().first_child().value();
std::string resource_type = prop.select_node("*[local-name()='resourcetype']").node().first_child().name();
DirEntry entry;
memset(&entry, 0, sizeof(entry));
entry.selectable = true;
sprintf(entry.directory, "%s", path.c_str());
sprintf(entry.name, "%s", name.c_str());
if (path.length() == 1 and path[0] == '/')
{
sprintf(entry.path, "%s%s", path.c_str(), name.c_str());
}
else
{
sprintf(entry.path, "%s/%s", path.c_str(), name.c_str());
}
entry.isDir = resource_type.find("collection") != std::string::npos;
entry.file_size = 0;
if (!entry.isDir)
{
entry.file_size = std::stoll(content_length);
DirEntry::SetDisplaySize(&entry);
}
else
{
sprintf(entry.display_size, "%s", lang_strings[STR_FOLDER]);
}
char modified_date[32];
char *p_char = NULL;
sprintf(modified_date, "%s", m_date.c_str());
p_char = strchr(modified_date, ' ');
if (p_char)
{
OrbisDateTime gmt;
OrbisDateTime lt;
char month[5];
sscanf(p_char, "%hd %s %hd %hd:%hd:%hd", &gmt.day, month, &gmt.year, &gmt.hour, &gmt.minute, &gmt.second);
for (int k = 0; k < 12; k++)
{
if (strcmp(month, months[k]) == 0)
{
gmt.month = k + 1;
break;
}
}
convertUtcToLocalTime(&gmt, &lt);
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
{
sprintf(this->response, "%s", httplib::to_string(res.error()).c_str());
return out;
}
return out;
}
int WebDAVClient::Put(const std::string &inputfile, const std::string &path, uint64_t offset)
{
size_t bytes_remaining = FS::GetSize(inputfile);
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
FILE *in = FS::OpenRead(inputfile);
if (auto res = client->Put(GetFullPath(path), [&](size_t offset, DataSink &sink)
{
size_t buf_size = MIN(bytes_remaining, CPPHTTPLIB_RECV_BUFSIZ);
char* buf = (char*) malloc(buf_size);
FS::Seek(in, offset);
while (bytes_remaining > 0)
{
size_t bytes_read = FS::Read(in, buf, buf_size);
sink.write(buf, bytes_read);
bytes_transfered += bytes_read;
bytes_remaining -= bytes_read;
}
sink.done();
free(buf);
return true; }, "application/octet-stream"))
{
if (HTTP_SUCCESS(res->status))
{
FS::Close(in);
return 1;
}
}
FS::Close(in);
return 0;
}
int WebDAVClient::Mkdir(const std::string &path)
{
Request req;
Headers header = {{"Accept", "*/*"}, {"Connection", "Keep-Alive"}};
req.method = "MKCOL";
req.path = httplib::detail::encode_url(GetFullPath((path)));
req.headers = header;
req.progress = Progress();
if (auto res = client->send(req))
{
if (HTTP_SUCCESS(res->status))
return 1;
}
return 0;
}
int WebDAVClient::Rmdir(const std::string &path, bool recursive)
{
return Delete(path);
}
int WebDAVClient::Rename(const std::string &src, const std::string &dst)
{
return Move(src, dst);
}
int WebDAVClient::Delete(const std::string &path)
{
Request req;
Headers header = {{"Accept", "*/*"}, {"Connection", "Keep-Alive"}};
req.method = "DELETE";
req.path = httplib::detail::encode_url(GetFullPath((path)));
req.headers = header;
req.progress = Progress();
if (auto res = client->send(req))
{
if (HTTP_SUCCESS(res->status))
return 1;
}
return 0;
}
int WebDAVClient::Copy(const std::string &from, const std::string &to)
{
Request req;
Headers header = {{"Accept", "*/*"}, {"Destination", httplib::detail::encode_url(GetFullPath(to))}};
req.method = "COPY";
req.path = httplib::detail::encode_url(GetFullPath(from));
req.headers = header;
req.progress = Progress();
if (auto res = client->send(req))
{
if (HTTP_SUCCESS(res->status))
return 1;
}
return 0;
}
int WebDAVClient::Move(const std::string &from, const std::string &to)
{
Request req;
Headers header = {{"Accept", "*/*"}, {"Destination", httplib::detail::encode_url(GetFullPath(to))}};
req.method = "MOVE";
req.path = httplib::detail::encode_url(GetFullPath(from));
req.headers = header;
req.progress = Progress();
if (auto res = client->send(req))
{
if (HTTP_SUCCESS(res->status))
return 1;
}
return 0;
}
ClientType WebDAVClient::clientType()
{
return CLIENT_TYPE_WEBDAV;
}
uint32_t WebDAVClient::SupportedActions()
{
return REMOTE_ACTION_ALL ^ REMOTE_ACTION_RAW_READ;
}
+32
View File
@@ -0,0 +1,32 @@
#ifndef EZ_WEBDAV_H
#define EZ_WEBDAV_H
#include <string>
#include <vector>
#include "http/httplib.h"
#include "clients/baseclient.h"
#include "clients/remote_client.h"
#include "common.h"
class WebDAVClient : public BaseClient
{
public:
int Connect(const std::string &url, const std::string &user, const std::string &pass, bool send_ping=false);
int Mkdir(const std::string &path);
int Rmdir(const std::string &path, bool recursive);
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 Put(const std::string &inputfile, const std::string &path, uint64_t offset = 0);
int Size(const std::string &path, int64_t *size);
std::vector<DirEntry> ListDir(const std::string &path);
ClientType clientType();
uint32_t SupportedActions();
static std::string GetHttpUrl(std::string url);
private:
Result PropFind(const std::string &path, int depth);
};
#endif
-387
View File
@@ -1,387 +0,0 @@
#include <errno.h>
#include <unistd.h>
#include <cstring>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <inttypes.h>
#include <errno.h>
#include <fcntl.h>
#include "lang.h"
#include "webdav/client.hpp"
#include "clients/webdavclient.h"
#include "windows.h"
#include "util.h"
#include "system.h"
static const char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
namespace WebDAV
{
static int DownloadCallback(void *context, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
{
int64_t *bytes_transfered = (int64_t *)context;
*bytes_transfered = reinterpret_cast<int64_t>(dlnow);
return CURLE_OK;
}
static int UploadCallback(void *context, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
{
int64_t *bytes_transfered = (int64_t *)context;
*bytes_transfered = reinterpret_cast<int64_t>(ulnow);
return CURLE_OK;
}
int WebDavClient::Connect(const std::string &host, const std::string &user, const std::string &pass)
{
return Connect(host, user, pass, true);
}
WebDavClient::WebDavClient() {};
int WebDavClient::Connect(const std::string &host, const std::string &user, const std::string &pass, bool check_enabled)
{
std::string url = GetHttpUrl(host);
std::size_t scheme_pos = url.find_first_of("://");
std::string root_folder = "/";
if (scheme_pos != std::string::npos)
{
std::size_t root_folder_pos = url.find_first_of("/", scheme_pos + 3);
if (root_folder_pos != std::string::npos)
{
root_folder = url.substr(root_folder_pos);
url = url.substr(0, root_folder_pos);
}
}
WebDAV::dict_t options = {
{"webdav_hostname", url},
{"webdav_root", root_folder},
{"webdav_username", user},
{"webdav_password", pass},
{"check_enabled", check_enabled ? "1" : "0"}};
client = new WebDAV::Client(options);
connected = true;
return 1;
}
/*
* LastResponse - return a pointer to the last response received
*/
const char *WebDavClient::LastResponse()
{
return (const char *)response;
}
/*
* IsConnected - return true if connected to remote
*/
bool WebDavClient::IsConnected()
{
return connected;
}
/*
* Ping - return true if connected to remote
*/
bool WebDavClient::Ping()
{
connected = client->check();
sprintf(response, "Http Code %ld", client->status_code());
return connected;
}
/*
* Quit - disconnect from remote
*
* return 1 if successful, 0 otherwise
*/
int WebDavClient::Quit()
{
if (client != NULL)
delete (client);
client = NULL;
connected = false;
return 1;
}
/*
* Mkdir - create a directory at server
*
* return 1 if successful, 0 otherwise
*/
int WebDavClient::Mkdir(const std::string &ppath)
{
bool ret = client->create_directory(ppath);
sprintf(response, "Http Code %ld", client->status_code());
return ret;
}
/*
* Rmdir - remove directory and all files under directory at remote
*
* return 1 if successful, 0 otherwise
*/
int WebDavClient::_Rmdir(const std::string &ppath)
{
bool ret = client->clean(ppath);
sprintf(response, "Http Code %ld", client->status_code());
return ret;
}
/*
* Rmdir - remove directory and all files under directory at remote
*
* return 1 if successful, 0 otherwise
*/
int WebDavClient::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);
if (ret == 0)
{
sprintf(status_message, "%s %s", lang_strings[STR_FAIL_DEL_DIR_MSG], path.c_str());
return 0;
}
return 1;
}
/*
* Get - issue a GET command and write received data to output
*
* return 1 if successful, 0 otherwise
*/
int WebDavClient::Get(const std::string &outputfile, const std::string &ppath, uint64_t offset)
{
bool ret = client->download(ppath, outputfile, &bytes_transfered, DownloadCallback);
sprintf(response, "Http Code %ld", client->status_code());
return ret;
}
bool WebDavClient::FileExists(const std::string &ppath)
{
std::string path = ppath;
path = Util::Ltrim(path, "/");
bool ret = client->check(path);
sprintf(response, "Http Code %ld", client->status_code());
return ret;
}
int WebDavClient::GetRange(const std::string &path, void *buffer, uint64_t size, uint64_t offset)
{
char *buffer_ptr = nullptr;
unsigned long long buffer_size = 0;
bool ret = client->download_range_to(path, buffer_ptr, buffer_size, offset, offset+size-1);
sprintf(response, "Http Code %ld", client->status_code());
if (buffer_size != size)
{
return 0;
}
memcpy(buffer, buffer_ptr, size);
return 1;
}
int WebDavClient::GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset)
{
return client->download_range_to(path, sink, offset, offset+size-1);
}
/*
* Put - issue a PUT command and send data from input
*
* return 1 if successful, 0 otherwise
*/
int WebDavClient::Put(const std::string &inputfile, const std::string &ppath, uint64_t offset)
{
bool ret = client->upload(ppath, inputfile, &bytes_transfered, UploadCallback);
sprintf(response, "Http Code %ld", client->status_code());
return ret;
}
int WebDavClient::Rename(const std::string &src, const std::string &dst)
{
bool ret = client->move(src, dst);
sprintf(response, "Http Code %ld", client->status_code());
return ret;
}
int WebDavClient::Delete(const std::string &ppath)
{
bool ret = client->clean(ppath);
sprintf(response, "Http Code %ld", client->status_code());
return ret;
}
int WebDavClient::Copy(const std::string &from, const std::string &to)
{
bool ret = client->copy(from, to);
sprintf(response, "Http Code %ld", client->status_code());
return ret;
}
int WebDavClient::Move(const std::string &from, const std::string &to)
{
bool ret = client->move(from, to);
sprintf(response, "Http Code %ld", client->status_code());
return ret;
}
int WebDavClient::Size(const std::string &ppath, int64_t *size)
{
WebDAV::dict_t file_info = client->info(ppath);
std::string file_size = WebDAV::get(file_info, "size");
if (file_size.empty())
return 0;
*size = std::stoll(file_size);
return 1;
}
std::vector<DirEntry> WebDavClient::ListDir(const std::string &path)
{
std::vector<DirEntry> out;
DirEntry entry;
Util::SetupPreviousFolder(path, &entry);
out.push_back(entry);
WebDAV::dict_items_t files = client->list(path);
for (int i = 0; i < files.size(); i++)
{
DirEntry entry;
memset(&entry, 0, sizeof(entry));
entry.selectable = true;
sprintf(entry.directory, "%s", path.c_str());
sprintf(entry.name, "%s", WebDAV::get(files[i], "name").c_str());
if (path.length() == 1 and path[0] == '/')
{
sprintf(entry.path, "%s%s", path.c_str(), WebDAV::get(files[i], "name").c_str());
}
else
{
sprintf(entry.path, "%s/%s", path.c_str(), WebDAV::get(files[i], "name").c_str());
}
std::string resource_type = WebDAV::get(files[i], "type");
entry.isDir = resource_type.find("collection") != std::string::npos;
entry.file_size = 0;
if (!entry.isDir)
{
entry.file_size = std::stoll(WebDAV::get(files[i], "size"));
DirEntry::SetDisplaySize(&entry);
}
else
{
sprintf(entry.display_size, "%s", lang_strings[STR_FOLDER]);
}
char modified_date[32];
char *p_char = NULL;
sprintf(modified_date, "%s", WebDAV::get(files[i], "modified").c_str());
p_char = strchr(modified_date, ' ');
if (p_char)
{
OrbisDateTime gmt;
OrbisDateTime lt;
char month[5];
sscanf(p_char, "%hd %s %hd %hd:%hd:%hd", &gmt.day, month, &gmt.year, &gmt.hour, &gmt.minute, &gmt.second);
for (int k = 0; k < 12; k++)
{
if (strcmp(month, months[k]) == 0)
{
gmt.month = k + 1;
break;
}
}
convertUtcToLocalTime(&gmt, &lt);
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);
}
return out;
}
std::string WebDavClient::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;
}
int WebDavClient::Head(const std::string &path, void *buffer, uint64_t len)
{
char *buffer_ptr = nullptr;
unsigned long long buffer_size = 0;
bool ret = client->download_range_to(path, buffer_ptr, buffer_size, 0, len - 1);
sprintf(response, "Http Code %ld", client->status_code());
if (buffer_size != len)
{
return 0;
}
memcpy(buffer, buffer_ptr, len);
return 1;
}
bool WebDavClient::GetHeaders(const std::string &path, dict_t *headers)
{
return client->head(path, headers);
}
WebDAV::Client *WebDavClient::GetClient()
{
return this->client;
}
ClientType WebDavClient::clientType()
{
return CLIENT_TYPE_WEBDAV;
}
uint32_t WebDavClient::SupportedActions()
{
return REMOTE_ACTION_ALL;
}
}
-62
View File
@@ -1,62 +0,0 @@
#ifndef WEBDAVCLIENT_H
#define WEBDAVCLIENT_H
#include <time.h>
#include <string>
#include <vector>
#include <regex>
#include "http/httplib.h"
#include "webdav/client.hpp"
#include "clients/remote_client.h"
#include "common.h"
using namespace httplib;
namespace WebDAV
{
inline std::string GetHttpUrl(std::string url)
{
std::string http_url = std::regex_replace(url, std::regex("webdav://"), "http://");
http_url = std::regex_replace(http_url, std::regex("webdavs://"), "https://");
return http_url;
}
class WebDavClient : public RemoteClient
{
public:
WebDavClient();
int Connect(const std::string &url, const std::string &user, const std::string &pass);
int Connect(const std::string &url, const std::string &user, const std::string &pass, bool check_enabled);
int Mkdir(const std::string &path);
int Rmdir(const std::string &path, bool recursive);
int Size(const std::string &path, int64_t *size);
int Get(const std::string &outputfile, const std::string &path, uint64_t offset=0);
int GetRange(const std::string &path, void *buffer, uint64_t size, uint64_t offset);
int GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset);
int 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);
bool FileExists(const std::string &path);
std::vector<DirEntry> ListDir(const std::string &path);
bool IsConnected();
bool Ping();
const char *LastResponse();
int Quit();
std::string GetPath(std::string path1, std::string path2);
int Head(const std::string &path, void *buffer, uint64_t len);
bool GetHeaders(const std::string &path, dict_t *headers);
WebDAV::Client *GetClient();
ClientType clientType();
uint32_t SupportedActions();
private:
int _Rmdir(const std::string &path);
WebDAV::Client *client;
char response[1024];
bool connected = false;
};
}
#endif
+16 -3
View File
@@ -1,5 +1,5 @@
#ifndef COMMON_H
#define COMMON_H
#ifndef EZ_COMMON_H
#define EZ_COMMON_H
#include <string>
#include <vector>
@@ -11,6 +11,10 @@
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
enum DownloadState { STATE_PENDING, STATE_DOWNLOADING, STATE_RESUMED, STATE_FAILED, STATE_SUCCESS };
static const char* state_strings[] = {"Pending", "Downloading", "Resumed", "Failed", "Success"};
typedef struct
{
uint16_t year;
@@ -88,6 +92,15 @@ struct DirEntry
}
};
struct DownloadProgress
{
std::string path;
std::string state;
uint64_t bytes_transfered;
uint64_t file_size;
time_t timestamp;
};
static lxb_dom_node_t *NextChildElement(lxb_dom_element_t *element)
{
lxb_dom_node_t *node = element->node.first_child;
@@ -128,4 +141,4 @@ static lxb_dom_node_t *NextTextNode(lxb_dom_node_t *node)
return next;
}
#endif
#endif
+50 -9
View File
@@ -38,8 +38,12 @@ bool auto_delete_tmp_pkg;
int max_edit_file_size;
GoogleAppInfo gg_app;
bool show_hidden_files;
char alldebrid_api_key[32];
char alldebrid_api_key[64];
char realdebrid_api_key[64];
char temp_folder[256];
std::string ezremote_server_version;
bool enable_background_download;
uint64_t minimum_backgrond_file_size;
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'};
@@ -151,14 +155,18 @@ namespace CONFIG
FS::MkDirs(DATA_PATH);
}
memset(&install_pkg_url, 0, sizeof(install_pkg_url));
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", "Polish", "Portuguese_BR", "Russian", "Romanian", "Ryukyuan", "Spanish", "Turkish",
"Simplified Chinese", "Traditional Chinese", "Thai", "Ukrainian"};
"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_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" };
@@ -191,6 +199,12 @@ namespace CONFIG
sprintf(temp_folder, ReadString(CONFIG_GLOBAL, CONFIG_TMP_FOLDER_PATH, TMP_FOLDER_PATH));
WriteString(CONFIG_GLOBAL, CONFIG_TMP_FOLDER_PATH, temp_folder);
enable_background_download = ReadBool(CONFIG_GLOBAL, CONFIG_ENABLE_BG_DOWNLOAD, true);
WriteBool(CONFIG_GLOBAL, CONFIG_ENABLE_BG_DOWNLOAD, enable_background_download);
minimum_backgrond_file_size = ReadLong(CONFIG_GLOBAL, CONFIG_BG_DOWNLOAD_SIZE, 1024*1024*1024);
WriteLong(CONFIG_GLOBAL, CONFIG_BG_DOWNLOAD_SIZE, minimum_backgrond_file_size);
if (!FS::FolderExists(temp_folder))
{
FS::MkDirs(temp_folder);
@@ -202,16 +216,31 @@ namespace CONFIG
std::string encrypted_api_key;
if (strlen(tmp_api_key) > 0)
{
std::string decrypted__api_key;
int ret = Decrypt(tmp_api_key, decrypted__api_key);
std::string decrypted_api_key;
int ret = Decrypt(tmp_api_key, decrypted_api_key);
if (ret == 0)
sprintf(alldebrid_api_key, "%s", tmp_api_key);
else
sprintf(alldebrid_api_key, "%s", decrypted__api_key.c_str());
sprintf(alldebrid_api_key, "%s", decrypted_api_key.c_str());
Encrypt(alldebrid_api_key, encrypted_api_key);
}
WriteString(CONFIG_GLOBAL, CONFIG_ALLDEBRID_API_KEY, encrypted_api_key.c_str());
// realdebrid api key
sprintf(tmp_api_key, "%s", ReadString(CONFIG_GLOBAL, CONFIG_REALDEBRID_API_KEY, ""));
encrypted_api_key = "";
if (strlen(tmp_api_key) > 0)
{
std::string decrypted_api_key;
int ret = Decrypt(tmp_api_key, decrypted_api_key);
if (ret == 0)
sprintf(realdebrid_api_key, "%s", tmp_api_key);
else
sprintf(realdebrid_api_key, "%s", decrypted_api_key.c_str());
Encrypt(realdebrid_api_key, encrypted_api_key);
}
WriteString(CONFIG_GLOBAL, CONFIG_REALDEBRID_API_KEY, encrypted_api_key.c_str());
// 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);
@@ -282,6 +311,9 @@ namespace CONFIG
setting.enable_rpi = ReadBool(sites[i].c_str(), CONFIG_ENABLE_RPI, true);
WriteBool(sites[i].c_str(), CONFIG_ENABLE_RPI, setting.enable_rpi);
setting.enable_disk_cache = ReadBool(sites[i].c_str(), CONFIG_REMOTE_ENABLE_DISK_CACHE, false);
WriteBool(sites[i].c_str(), CONFIG_REMOTE_ENABLE_DISK_CACHE, setting.enable_disk_cache);
sprintf(setting.http_server_type, "%s", ReadString(sites[i].c_str(), CONFIG_REMOTE_HTTP_SERVER_TYPE, HTTP_SERVER_APACHE));
WriteString(sites[i].c_str(), CONFIG_REMOTE_HTTP_SERVER_TYPE, setting.http_server_type);
@@ -355,6 +387,7 @@ namespace CONFIG
WriteString(last_site, CONFIG_REMOTE_SERVER_USER, remote_settings->username);
WriteString(last_site, CONFIG_REMOTE_SERVER_PASSWORD, encrypted_text.c_str());
WriteBool(last_site, CONFIG_ENABLE_RPI, remote_settings->enable_rpi);
WriteBool(last_site, CONFIG_REMOTE_ENABLE_DISK_CACHE, remote_settings->enable_disk_cache);
WriteString(last_site, CONFIG_REMOTE_HTTP_SERVER_TYPE, remote_settings->http_server_type);
WriteString(last_site, CONFIG_REMOTE_DEFAULT_DIRECTORY, remote_settings->default_directory);
WriteString(CONFIG_GLOBAL, CONFIG_LAST_SITE, last_site);
@@ -392,11 +425,17 @@ namespace CONFIG
Encrypt(alldebrid_api_key, encrypted_api_key);
else
encrypted_api_key = std::string(alldebrid_api_key);
WriteString(CONFIG_GLOBAL, CONFIG_ALLDEBRID_API_KEY, encrypted_api_key.c_str());
if (strlen(realdebrid_api_key) > 0)
Encrypt(realdebrid_api_key, encrypted_api_key);
else
encrypted_api_key = std::string(realdebrid_api_key);
WriteString(CONFIG_GLOBAL, CONFIG_REALDEBRID_API_KEY, encrypted_api_key.c_str());
WriteString(CONFIG_GOOGLE, CONFIG_GOOGLE_CLIENT_SECRET, encrypted_secret.c_str());
WriteString(CONFIG_GOOGLE, CONFIG_GOOGLE_CLIENT_ID, gg_app.client_id);
WriteString(CONFIG_GOOGLE, CONFIG_GOOGLE_PERMISSIONS, gg_app.permissions);
WriteString(CONFIG_GLOBAL, CONFIG_ALLDEBRID_API_KEY, encrypted_api_key.c_str());
WriteString(CONFIG_GLOBAL, CONFIG_TMP_FOLDER_PATH, temp_folder);
WriteBool(CONFIG_GLOBAL, CONFIG_AUTO_DELETE_TMP_PKG, auto_delete_tmp_pkg);
WriteBool(CONFIG_GLOBAL, CONFIG_SHOW_HIDDEN_FILES, show_hidden_files);
@@ -404,6 +443,8 @@ namespace CONFIG
WriteInt(CONFIG_HTTP_SERVER, CONFIG_HTTP_SERVER_PORT, http_server_port);
WriteString(CONFIG_HTTP_SERVER, CONFIG_HTTP_SERVER_COMPRESSED_FILE_PATH, compressed_file_path);
WriteBool(CONFIG_HTTP_SERVER, CONFIG_HTTP_SERVER_ENABLED, web_server_enabled);
WriteBool(CONFIG_GLOBAL, CONFIG_ENABLE_BG_DOWNLOAD, enable_background_download);
WriteLong(CONFIG_GLOBAL, CONFIG_BG_DOWNLOAD_SIZE, minimum_backgrond_file_size);
WriteIniFile(CONFIG_INI_FILE);
CloseIniFile();
+22 -3
View File
@@ -1,5 +1,5 @@
#ifndef LAUNCHER_CONFIG_H
#define LAUNCHER_CONFIG_H
#ifndef EZ_CONFIG_H
#define EZ_CONFIG_H
#include <string>
#include <vector>
@@ -57,8 +57,10 @@
#define CONFIG_ENABLE_RPI "remote_server_enable_rpi"
#define CONFIG_REMOTE_HTTP_SERVER_TYPE "remote_server_http_server_type"
#define CONFIG_REMOTE_DEFAULT_DIRECTORY "remote_server_default_directory"
#define CONFIG_REMOTE_ENABLE_DISK_CACHE "remote_server_enable_disk_cache"
#define CONFIG_ALLDEBRID_API_KEY "alldebrid_api_key"
#define CONFIG_REALDEBRID_API_KEY "realdebrid_api_key"
#define CONFIG_VERSION "config_version"
#define CONFIG_VERSION_NUM 1
@@ -75,11 +77,19 @@
#define CONFIG_LANGUAGE "language"
#define CONFIG_ENABLE_BG_DOWNLOAD "enable_background_download"
#define CONFIG_BG_DOWNLOAD_SIZE "minimum_backgrond_file_size"
#define HTTP_SERVER_APACHE "Apache"
#define HTTP_SERVER_MS_IIS "Microsoft IIS"
#define HTTP_SERVER_NGINX "Nginx"
#define HTTP_SERVER_NPX_SERVE "Serve"
#define HTTP_SERVER_RCLONE "RClone"
#define HTTP_SERVER_ARCHIVEORG "Archive.org"
#define HTTP_SERVER_MYRIENT "Myrient"
#define HTTP_SERVER_GITHUB "Github"
#define EZREMOTE_SERVER_REQUIRED_VERSION "1.00"
#define MAX_EDIT_FILE_SIZE 32768
@@ -109,6 +119,7 @@ struct RemoteSettings
char http_server_type[24];
GoogleAccountInfo gg_account;
char default_directory[256];
bool enable_disk_cache;
};
struct PackageUrlInfo
@@ -116,6 +127,10 @@ struct PackageUrlInfo
char url[512];
char username[33];
char password[25];
bool enable_alldebrid;
bool enable_realdebrid;
bool enable_disk_cache;
bool enable_rpi;
};
extern std::vector<std::string> sites;
@@ -140,8 +155,12 @@ extern unsigned char cipher_key[32];
extern unsigned char cipher_iv[16];
extern GoogleAppInfo gg_app;
extern bool show_hidden_files;
extern char alldebrid_api_key[32];
extern char alldebrid_api_key[64];
extern char realdebrid_api_key[64];
extern char temp_folder[256];
extern std::string ezremote_server_version;
extern bool enable_background_download;
extern uint64_t minimum_backgrond_file_size;
namespace CONFIG
{
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef FICHIER_HOST_H
#define FICHIER_HOST_H
#ifndef EZ_FICHIER_HOST_H
#define EZ_FICHIER_HOST_H
#include "filehost.h"
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef ALLDEBRID_HOST_H
#define ALLDEBRID_HOST_H
#ifndef EZ_ALLDEBRID_HOST_H
#define EZ_ALLDEBRID_HOST_H
#include "filehost.h"
-2
View File
@@ -1,6 +1,4 @@
#include <regex>
#include <lexbor/html/parser.h>
#include <lexbor/dom/interfaces/element.h>
#include <http/httplib.h>
#include "common.h"
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef DIRECT_HOST_H
#define DIRECT_HOST_H
#ifndef EZ_DIRECT_HOST_H
#define EZ_DIRECT_HOST_H
#include "filehost.h"
+4 -1
View File
@@ -6,6 +6,7 @@
#include "filehost.h"
#include "1fichier.h"
#include "filehost/alldebrid.h"
#include "filehost/realdebrid.h"
#include "filehost/directhost.h"
#include "filehost/gdrive.h"
#include "filehost/mediafire.h"
@@ -25,7 +26,7 @@ std::string FileHost::GetUrl()
return url;
}
FileHost *FileHost::getFileHost(const std::string &url, bool use_alldebrid)
FileHost *FileHost::getFileHost(const std::string &url, bool use_alldebrid, bool use_realdebrid)
{
std::regex google_re(GDRIVE_REGEX);
std::regex mediafire_re(MEDIAFIRE_REGEX);
@@ -34,6 +35,8 @@ FileHost *FileHost::getFileHost(const std::string &url, bool use_alldebrid)
if (use_alldebrid)
return new AllDebridHost(url);
else if (use_realdebrid)
return new RealDebridHost(url);
else if (std::regex_match(url, google_re))
return new GDriveHost(url);
else if (std::regex_match(url, mediafire_re))
+3 -3
View File
@@ -1,5 +1,5 @@
#ifndef FILEHOST_H
#define FILEHOST_H
#ifndef EZ_FILEHOST_H
#define EZ_FILEHOST_H
#include <string>
#include <vector>
@@ -13,7 +13,7 @@ public:
virtual std::string GetDownloadUrl() = 0;
std::string GetUrl();
static FileHost *getFileHost(const std::string &url, bool use_alldebrid = false);
static FileHost *getFileHost(const std::string &url, bool use_alldebrid = false, bool use_realdebrid = false);
static std::string GetCachedDownloadUrl(std::string &hash);
static void AddCacheDownloadUrl(std::string &hash, std::string &url);
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef GDRIVE_HOST_H
#define GDRIVE_HOST_H
#ifndef EZ_GDRIVE_HOST_H
#define EZ_GDRIVE_HOST_H
#include "filehost.h"
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef MEDIAFIRE_HOST_H
#define MEDIAFIRE_HOST_H
#ifndef EZ_MEDIAFIRE_HOST_H
#define EZ_MEDIAFIRE_HOST_H
#include "filehost.h"
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef PIXELDRAIN_HOST_H
#define PIXELDRAIN_HOST_H
#ifndef EZ_PIXELDRAIN_HOST_H
#define EZ_PIXELDRAIN_HOST_H
#include "filehost.h"
+68
View File
@@ -0,0 +1,68 @@
#include <http/httplib.h>
#include <json-c/json.h>
#include "config.h"
#include "common.h"
#include "realdebrid.h"
RealDebridHost::RealDebridHost(const std::string &url) : FileHost(url)
{
}
bool RealDebridHost::IsValidUrl()
{
httplib::Client tmp_client("https://api.real-debrid.com");
tmp_client.set_keep_alive(true);
tmp_client.set_follow_location(true);
tmp_client.set_connection_timeout(30);
tmp_client.set_read_timeout(30);
tmp_client.enable_server_certificate_verification(false);
tmp_client.set_bearer_token_auth(realdebrid_api_key);
std::string path = std::string("/rest/1.0/unrestrict/check");
std::string post_data = std::string("link=") + httplib::detail::encode_url(this->url) + "&password=";
auto res = tmp_client.Post(path, post_data.c_str(), post_data.length(), "application/x-www-form-urlencoded");
if (HTTP_SUCCESS(res->status))
{
json_object *jobj = json_tokener_parse(res->body.c_str());
uint64_t supported = json_object_get_uint64(json_object_object_get(jobj, "supported"));
if (supported == 1)
return true;
}
return false;
}
std::string RealDebridHost::GetDownloadUrl()
{
httplib::Client tmp_client("https://api.real-debrid.com");
tmp_client.set_keep_alive(true);
tmp_client.set_follow_location(true);
tmp_client.set_connection_timeout(30);
tmp_client.set_read_timeout(30);
tmp_client.enable_server_certificate_verification(false);
tmp_client.set_bearer_token_auth(realdebrid_api_key);
std::string path = std::string("/rest/1.0/unrestrict/link");
std::string post_data = std::string("link=") + httplib::detail::encode_url(this->url) + "&password=&remote=0";
auto res = tmp_client.Post(path, post_data.c_str(), post_data.length(), "application/x-www-form-urlencoded");
if (HTTP_SUCCESS(res->status))
{
json_object *jobj = json_tokener_parse(res->body.c_str());
const char *download = json_object_get_string(json_object_object_get(jobj, "download"));
if (download != nullptr)
{
return std::string(download);
}
else
{
return "";
}
}
return "";
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef EZ_REALDEBRID_HOST_H
#define EZ_REALDEBRID_HOST_H
#include "filehost.h"
class RealDebridHost : public FileHost
{
public:
RealDebridHost(const std::string &url);
bool IsValidUrl();
std::string GetDownloadUrl();
};
#endif
+16 -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;
}
@@ -510,6 +513,8 @@ namespace FS
size_t bytes_read = 0;
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
const size_t buf_size = 0x10000;
unsigned char *buf = new unsigned char[buf_size];
@@ -547,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;
}
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef LAUNCHER_FS_H
#define LAUNCHER_FS_H
#ifndef EZ_FS_H
#define EZ_FS_H
#pragma once
#include <string.h>
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef LAUNCHER_GUI_H
#define LAUNCHER_GUI_H
#ifndef EZ_GUI_H
#define EZ_GUI_H
#include <string>
#include "SDL2/SDL.h"
+5 -1
View File
@@ -562,7 +562,7 @@ private:
size_t read_buff_off_ = 0;
size_t read_buff_content_size_ = 0;
static const size_t read_buff_size_ = 1024 * 16;
static const size_t read_buff_size_ = 1024 * 512;
};
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -3954,6 +3954,10 @@ bool Server::listen_internal() {
#endif
}
int const size = 1048576;
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
task_queue->enqueue([this, sock]() { process_and_close_socket(sock); });
}
+5 -3
View File
@@ -87,11 +87,11 @@
#endif
#ifndef CPPHTTPLIB_RECV_BUFSIZ
#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
#define CPPHTTPLIB_RECV_BUFSIZ size_t(524288u)
#endif
#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(524288u)
#endif
#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
@@ -353,7 +353,7 @@ private:
} // namespace detail
using Headers = std::map<std::string, std::string, detail::ci>;
using Headers = std::multimap<std::string, std::string, detail::ci>;
using Params = std::multimap<std::string, std::string>;
using Match = std::smatch;
@@ -1898,6 +1898,8 @@ make_basic_authentication_header(const std::string &username,
namespace detail {
const char *status_message(int status);
std::string encode_query_param(const std::string &value);
std::string encode_url(const std::string &s);
+2 -2
View File
@@ -16,8 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __IME_DIALOG_H__
#define __IME_DIALOG_H__
#ifndef __EZ_IME_DIALOG_H__
#define __EZ_IME_DIALOG_H__
#define IME_DIALOG_RESULT_NONE 0
#define IME_DIALOG_RESULT_RUNNING 1
+2 -2
View File
@@ -10692,7 +10692,7 @@ void ImGui::NavUpdateCreateMoveRequest()
// When using gamepad, we project the reference nav bounding box into window visible area.
// This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad all movements are relative
// (can't focus a visible object like we can with the mouse).
if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))
/*if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))
{
bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0;
bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0;
@@ -10709,7 +10709,7 @@ void ImGui::NavUpdateCreateMoveRequest()
window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel);
g.NavId = g.NavFocusScopeId = 0;
}
}
}*/
// For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)
ImRect scoring_rect;
+9
View File
@@ -84,6 +84,8 @@
#define BUTTON_RIGHT 0x00000020
#define BUTTON_UP 0x00000040
#define BUTTON_DOWN 0x00000080
#define BUTTON_C 0x00000100
#define BUTTON_D 0x00000200
static uint32_t previous_down = 0;
static int repeat_count = 0;
@@ -531,8 +533,15 @@ static void ImGui_ImplSDL2_UpdateGamepads()
down |= BUTTON_DOWN;
else if (SDL_GameControllerGetAxis(game_controller, SDL_CONTROLLER_AXIS_LEFTY) < -ANALOG_THRESHOLD)
down |= BUTTON_UP;
else if (SDL_GameControllerGetAxis(game_controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) > ANALOG_THRESHOLD)
down |= BUTTON_C;
else if (SDL_GameControllerGetAxis(game_controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) > ANALOG_THRESHOLD)
down |= BUTTON_D;
uint32_t pressed = down & ~previous_down;
io.AddKeyEvent(ImGuiKey_C, (pressed & BUTTON_C) != 0);
io.AddKeyEvent(ImGuiKey_D, (pressed & BUTTON_D) != 0);
if (previous_down == down)
{
uint64_t delay = 300;
+681 -88
View File
File diff suppressed because it is too large Load Diff
+27 -2
View File
@@ -4,6 +4,7 @@
#include "zip_util.h"
#include "split_file.h"
#include "pthread.h"
#include "config.h"
#define SWAP16(x) \
((uint16_t)((((uint16_t)(x)&UINT16_C(0x00FF)) << 8) | \
@@ -129,6 +130,19 @@ struct ArchivePkgInstallData
bool stop_write_thread;
};
struct SplitPkgInstallData
{
SplitFile *split_file;
RemoteClient *remote_client;
std::string path;
int64_t size;
pthread_t thread;
bool stop_write_thread;
bool delete_client;
};
static pthread_t bk_install_thid;
namespace INSTALLER
{
int Init(void);
@@ -146,5 +160,16 @@ namespace INSTALLER
ArchivePkgInstallData *GetArchivePkgInstallData(const std::string &hash);
void AddArchivePkgInstallData(const std::string &hash, ArchivePkgInstallData *pkg_data);
void RemoveArchivePkgInstallData(const std::string &hash);
bool InstallArchivePkg(const std::string &path, ArchivePkgInstallData* pkg_data);
}
bool InstallArchivePkg(const std::string &path, ArchivePkgInstallData* pkg_data, bool bg = false);
SplitPkgInstallData *GetSplitPkgInstallData(const std::string &hash);
void AddSplitPkgInstallData(const std::string &hash, SplitPkgInstallData *pkg_data);
void RemoveSplitPkgInstallData(const std::string &hash);
bool InstallSplitPkg(const std::string &path, SplitPkgInstallData* pkg_data, bool bg = false);
std::string EzRemoteServerVersion();
int StartEzRemoteServer();
void StopEzRemoteServer();
std::string StoreBgInstallHostData(RemoteSettings *remote_settings, const std::string &path);
RemoteClient *GetRemoteClient(int site_idx);
RemoteClient *GetRemoteClient(RemoteSettings *settings);
bool IsPortOpen(const char *ip, uint16_t port, int timeout_sec);
}
+183 -161
View File
@@ -10,166 +10,185 @@ char lang_identifiers[LANG_STRINGS_NUM][LANG_ID_SIZE] = {
// This is properly populated so that emulator won't crash if an user launches it without language INI files.
char lang_strings[LANG_STRINGS_NUM][LANG_STR_SIZE] = {
"Connection Settings", // STR_CONNECTION_SETTINGS
"Site", // STR_SITE
"Local", // STR_LOCAL
"Remote", // STR_REMOTE
"Messages", // STR_MESSAGES
"Update Software", // STR_UPDATE_SOFTWARE
"Connect", // STR_CONNECT
"Disconnect", // STR_DISCONNECT
"Search", // STR_SEARCH
"Refresh", // STR_REFRESH
"Server", // STR_SERVER
"Username", // STR_USERNAME
"Password", // STR_PASSWORD
"Port", // STR_PORT
"Pasv", // STR_PASV
"Directory", // STR_DIRECTORY
"Filter", // STR_FILTER
"Yes", // STR_YES
"No", // STR_NO
"Cancel", // STR_CANCEL
"Continue", // STR_CONTINUE
"Close", // STR_CLOSE
"Folder", // STR_FOLDER
"File", // STR_FILE
"Type", // STR_TYPE
"Name", // STR_NAME
"Size", // STR_SIZE
"Date", // STR_DATE
"New Folder", // STR_NEW_FOLDER
"Rename", // STR_RENAME
"Delete", // STR_DELETE
"Upload", // STR_UPLOAD
"Download", // STR_DOWNLOAD
"Select All", // STR_SELECT_ALL
"Clear All", // STR_CLEAR_ALL
"Uploading", // STR_UPLOADING
"Downloading", // STR_DOWNLOADING
"Overwrite", // STR_OVERWRITE
"Don't Overwrite", // STR_DONT_OVERWRITE
"Ask for Confirmation", // STR_ASK_FOR_CONFIRM
"Don't Ask for Confirmation", // STR_DONT_ASK_CONFIRM
"Always use this option and don't ask again", // STR_ALLWAYS_USE_OPTION
"Actions", // STR_ACTIONS
"Confirm", // STR_CONFIRM
"Overwrite Options", // STR_OVERWRITE_OPTIONS
"Properties", // STR_PROPERTIES
"Progress", // STR_PROGRESS
"Updates", // STR_UPDATES
"Are you sure you want to delete this file(s)/folder(s)?", // STR_DEL_CONFIRM_MSG
"Canceling. Waiting for last action to complete", // STR_CANCEL_ACTION_MSG
"Failed to upload file", // STR_FAIL_UPLOAD_MSG
"Failed to download file", // STR_FAIL_DOWNLOAD_MSG
"Failed to read contents of directory or folder does not exist.", // STR_FAIL_READ_LOCAL_DIR_MSG
"426 Connection closed.", // STR_CONNECTION_CLOSE_ERR_MSG
"426 Remote Server has terminated the connection.", // STR_REMOTE_TERM_CONN_MSG
"300 Failed Login. Please check your username or password.", // STR_FAIL_LOGIN_MSG
"426 Failed. Connection timeout.", // STR_FAIL_TIMEOUT_MSG
"Failed to delete directory", // STR_FAIL_DEL_DIR_MSG
"Deleting", // STR_DELETING
"Failed to delete file", // STR_FAIL_DEL_FILE_MSG
"Deleted", // STR_DELETED
"Link", // STR_LINK
"Share", // STR_SHARE
"310 Failed", // STR_FAILED
"310 Failed to create file on local", // STR_FAIL_CREATE_LOCAL_FILE_MSG
"Install", // STR_INSTALL
"Installing", // STR_INSTALLING
"Success", // STR_INSTALL_SUCCESS
"Failed", // STR_INSTALL_FAILED
"Skipped", // STR_INSTALL_SKIPPED
"Checking connection to remote HTTP Server", // STR_CHECK_HTTP_MSG
"Failed connecting to HTTP Server", // STR_FAILED_HTTP_CHECK
"Remote is not a HTTP Server", // STR_REMOTE_NOT_HTTP
"Package not in the /data or /mnt/usbX folder", // STR_INSTALL_FROM_DATA_MSG
"Package is already installed", // STR_ALREADY_INSTALLED_MSG
"Install from URL", // STR_INSTALL_FROM_URL
"Could not read package header info", // STR_CANNOT_READ_PKG_HDR_MSG
"Favorite URLs", // STR_FAVORITE_URLS
"Slot", // STR_SLOT
"Edit", // STR_EDIT
"One Time Url", // STR_ONETIME_URL
"Not a valid Package", // STR_NOT_A_VALID_PACKAGE
"Waiting for Package to finish installing", // STR_WAIT_FOR_INSTALL_MSG
"Failed to install pkg file. Please delete the tmp pkg manually", // STR_FAIL_INSTALL_TMP_PKG_MSG
"Failed to obtain download URL", // STR_FAIL_TO_OBTAIN_GG_DL_MSG
"Auto delete temporary downloaded pkg file after install", // STR_AUTO_DELETE_TMP_PKG
"Protocol not supported", // STR_PROTOCOL_NOT_SUPPORTED
"Could not resolve hostname", // STR_COULD_NOT_RESOLVE_HOST
"Extract", // STR_EXTRACT
"Extracting", // STR_EXTRACTING
"Failed to extract", // STR_FAILED_TO_EXTRACT
"Extract Location", // STR_EXTRACT_LOCATION
"Compress", // STR_COMPRESS
"Zip Filename", // STR_ZIP_FILE_PATH
"Compressing", // STR_COMPRESSING
"Error occured while creating zip", // STR_ERROR_CREATE_ZIP
"Unsupported compressed file format", // STR_UNSUPPORTED_FILE_FORMAT
"Cut", // STR_CUT
"Copy", // STR_COPY
"Paste", // STR_PASTE
"Moving", // STR_MOVING
"Copying", // STR_COPYING
"Failed to move file", // STR_FAIL_MOVE_MSG
"Failed to copy file", // STR_FAIL_COPY_MSG
"Cannot move parent directory to sub subdirectory", // STR_CANT_MOVE_TO_SUBDIR_MSG
"Cannot copy parent directory to sub subdirectory", // STR_CANT_COPY_TO_SUBDIR_MSG
"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 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
"RPI", // STR_ENABLE_RPI
"This option enables Remote Package Installation.", // STR_ENABLE_RPI_FTP_SMB_MSG
"This option enables Remote Package Installation.", // STR_ENABLE_RPI_WEBDAV_MSG
"Files", // STR_FILES
"Editor", // STR_EDITOR
"Save", // STR_SAVE
"Cannot edit files bigger than", // STR_MAX_EDIT_FILE_SIZE_MSG
"Delete Selected Line", // STR_DELETE_LINE
"Insert Below Selected Line", // STR_INSERT_LINE
"Modified", // STR_MODIFIED
"Failed to obtain an access token from", // STR_FAIL_GET_TOKEN_MSG
"Login Success. You may close the browser and return to the application", // STR_GET_TOKEN_SUCCESS_MSG
"See, edit, create, and delete all of your Google Drive files", // STR_PERM_DRIVE
"See, create, and delete its own configuration data in your Google Drive", // STR_PERM_DRIVE_APPDATA
"See, edit, create, and delete only the specific Google Drive files you use with this app", // STR_PERM_DRIVE_FILE
"View and manage metadata of files in your Google Drive", // STR_PERM_DRIVE_METADATA
"See information about your Google Drive files", // STR_PERM_DRIVE_METADATA_RO
"Google login failed", // STR_GOOGLE_LOGIN_FAIL_MSG
"Google login timed out", // STR_GOOGLE_LOGIN_TIMEOUT_MSG
"New File", // STR_NEW_FILE
"Settings", // STR_SETTINGS
"Client ID", // STR_CLIENT_ID
"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
"Set Default Folder", // STR_SET_DEFAULT_DIRECTORY
"has being set as default direcotry", // STR_SET_DEFAULT_DIRECTORY_MSG
"View Image", // STR_VIEW_IMAGE
"Package Information", // STR_VIEW_PKG_INFO
"NFS export path missing in URL", // STR_NFS_EXP_PATH_MISSING_MSG
"Failed to init NFS context", // STR_FAIL_INIT_NFS_CONTEXT
"Failed to mount NFS share", // STR_FAIL_MOUNT_NFS_MSG
"Web Server", // STR_WEB_SERVER
"Enable", // STR_ENABLE
"Compressed Files Location", // STR_COMPRESSED_FILE_PATH
"Location of where compressed files are stored on the web server", // STR_COMPRESSED_FILE_PATH_MSG
"AllDebrid", // STR_ALLDEBRID
"API Key", // STR_API_KEY
"Couldn't extract download url", // STR_CANT_EXTRACT_URL_MSG
"Failed to install from URL", // STR_FAIL_INSTALL_FROM_URL_MSG
"InValid URL", // STR_INVALID_URL
"To use this function, an API Key needs to be configured in the ezRemote Client settings", // STR_ALLDEBRID_API_KEY_MISSING_MSG
"Language", // STR_LANGUAGE
"Temp Directory", // STR_TEMP_DIRECTORY
"Connection Settings", // STR_CONNECTION_SETTINGS
"Site", // STR_SITE
"Local", // STR_LOCAL
"Remote", // STR_REMOTE
"Messages", // STR_MESSAGES
"Update Software", // STR_UPDATE_SOFTWARE
"Connect", // STR_CONNECT
"Disconnect", // STR_DISCONNECT
"Search", // STR_SEARCH
"Refresh", // STR_REFRESH
"Server", // STR_SERVER
"Username", // STR_USERNAME
"Password", // STR_PASSWORD
"Port", // STR_PORT
"Pasv", // STR_PASV
"Directory", // STR_DIRECTORY
"Filter", // STR_FILTER
"Yes", // STR_YES
"No", // STR_NO
"Cancel", // STR_CANCEL
"Continue", // STR_CONTINUE
"Close", // STR_CLOSE
"Folder", // STR_FOLDER
"File", // STR_FILE
"Type", // STR_TYPE
"Name", // STR_NAME
"Size", // STR_SIZE
"Date", // STR_DATE
"New Folder", // STR_NEW_FOLDER
"Rename", // STR_RENAME
"Delete", // STR_DELETE
"Upload", // STR_UPLOAD
"Download", // STR_DOWNLOAD
"Select All", // STR_SELECT_ALL
"Clear All", // STR_CLEAR_ALL
"Uploading", // STR_UPLOADING
"Downloading", // STR_DOWNLOADING
"Overwrite", // STR_OVERWRITE
"Don't Overwrite", // STR_DONT_OVERWRITE
"Ask for Confirmation", // STR_ASK_FOR_CONFIRM
"Don't Ask for Confirmation", // STR_DONT_ASK_CONFIRM
"Always use this option and don't ask again", // STR_ALLWAYS_USE_OPTION
"Actions", // STR_ACTIONS
"Confirm", // STR_CONFIRM
"Overwrite Options", // STR_OVERWRITE_OPTIONS
"Properties", // STR_PROPERTIES
"Progress", // STR_PROGRESS
"Updates", // STR_UPDATES
"Are you sure you want to delete this file(s)/folder(s)?", // STR_DEL_CONFIRM_MSG
"Canceling. Waiting for last action to complete", // STR_CANCEL_ACTION_MSG
"Failed to upload file", // STR_FAIL_UPLOAD_MSG
"Failed to download file", // STR_FAIL_DOWNLOAD_MSG
"Failed to read contents of directory or folder does not exist.", // STR_FAIL_READ_LOCAL_DIR_MSG
"426 Connection closed.", // STR_CONNECTION_CLOSE_ERR_MSG
"426 Remote Server has terminated the connection.", // STR_REMOTE_TERM_CONN_MSG
"300 Failed Login. Please check your username or password.", // STR_FAIL_LOGIN_MSG
"426 Failed. Connection timeout.", // STR_FAIL_TIMEOUT_MSG
"Failed to delete directory", // STR_FAIL_DEL_DIR_MSG
"Deleting", // STR_DELETING
"Failed to delete file", // STR_FAIL_DEL_FILE_MSG
"Deleted", // STR_DELETED
"Link", // STR_LINK
"Share", // STR_SHARE
"310 Failed", // STR_FAILED
"310 Failed to create file on local", // STR_FAIL_CREATE_LOCAL_FILE_MSG
"Install", // STR_INSTALL
"Installing", // STR_INSTALLING
"Success", // STR_INSTALL_SUCCESS
"Failed", // STR_INSTALL_FAILED
"Skipped", // STR_INSTALL_SKIPPED
"Checking connection to remote HTTP Server", // STR_CHECK_HTTP_MSG
"Failed connecting to HTTP Server", // STR_FAILED_HTTP_CHECK
"Remote is not a HTTP Server", // STR_REMOTE_NOT_HTTP
"Package not in the /data or /mnt/usbX folder", // STR_INSTALL_FROM_DATA_MSG
"Package is already installed", // STR_ALREADY_INSTALLED_MSG
"Install from URL", // STR_INSTALL_FROM_URL
"Could not read package header info", // STR_CANNOT_READ_PKG_HDR_MSG
"Favorite URLs", // STR_FAVORITE_URLS
"Slot", // STR_SLOT
"Edit", // STR_EDIT
"One Time Url", // STR_ONETIME_URL
"Not a valid Package", // STR_NOT_A_VALID_PACKAGE
"Waiting for Package to finish installing", // STR_WAIT_FOR_INSTALL_MSG
"Failed to install pkg file. Please delete the tmp pkg manually", // STR_FAIL_INSTALL_TMP_PKG_MSG
"Failed to obtain download URL", // STR_FAIL_TO_OBTAIN_GG_DL_MSG
"Auto delete temporary downloaded pkg file after install", // STR_AUTO_DELETE_TMP_PKG
"Protocol not supported", // STR_PROTOCOL_NOT_SUPPORTED
"Could not resolve hostname", // STR_COULD_NOT_RESOLVE_HOST
"Extract", // STR_EXTRACT
"Extracting", // STR_EXTRACTING
"Failed to extract", // STR_FAILED_TO_EXTRACT
"Extract Location", // STR_EXTRACT_LOCATION
"Compress", // STR_COMPRESS
"Zip Filename", // STR_ZIP_FILE_PATH
"Compressing", // STR_COMPRESSING
"Error occured while creating zip", // STR_ERROR_CREATE_ZIP
"Unsupported compressed file format", // STR_UNSUPPORTED_FILE_FORMAT
"Cut", // STR_CUT
"Copy", // STR_COPY
"Paste", // STR_PASTE
"Moving", // STR_MOVING
"Copying", // STR_COPYING
"Failed to move file", // STR_FAIL_MOVE_MSG
"Failed to copy file", // STR_FAIL_COPY_MSG
"Cannot move parent directory to sub subdirectory", // STR_CANT_MOVE_TO_SUBDIR_MSG
"Cannot copy parent directory to sub subdirectory", // STR_CANT_COPY_TO_SUBDIR_MSG
"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 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
"RPI", // STR_ENABLE_RPI
"This option enables Remote Package Installation.", // STR_ENABLE_RPI_FTP_SMB_MSG
"This option enables Remote Package Installation.", // STR_ENABLE_RPI_WEBDAV_MSG
"Files", // STR_FILES
"Editor", // STR_EDITOR
"Save", // STR_SAVE
"Cannot edit files bigger than", // STR_MAX_EDIT_FILE_SIZE_MSG
"Delete Selected Line", // STR_DELETE_LINE
"Insert Below Selected Line", // STR_INSERT_LINE
"Modified", // STR_MODIFIED
"Failed to obtain an access token from", // STR_FAIL_GET_TOKEN_MSG
"Login Success. You may close the browser and return to the application", // STR_GET_TOKEN_SUCCESS_MSG
"See, edit, create, and delete all of your Google Drive files", // STR_PERM_DRIVE
"See, create, and delete its own configuration data in your Google Drive", // STR_PERM_DRIVE_APPDATA
"See, edit, create, and delete only the specific Google Drive files you use with this app", // STR_PERM_DRIVE_FILE
"View and manage metadata of files in your Google Drive", // STR_PERM_DRIVE_METADATA
"See information about your Google Drive files", // STR_PERM_DRIVE_METADATA_RO
"Google login failed", // STR_GOOGLE_LOGIN_FAIL_MSG
"Google login timed out", // STR_GOOGLE_LOGIN_TIMEOUT_MSG
"New File", // STR_NEW_FILE
"Settings", // STR_SETTINGS
"Client ID", // STR_CLIENT_ID
"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
"Set Default Folder", // STR_SET_DEFAULT_DIRECTORY
"has being set as default direcotry", // STR_SET_DEFAULT_DIRECTORY_MSG
"View Image", // STR_VIEW_IMAGE
"Package Information", // STR_VIEW_PKG_INFO
"NFS export path missing in URL", // STR_NFS_EXP_PATH_MISSING_MSG
"Failed to init NFS context", // STR_FAIL_INIT_NFS_CONTEXT
"Failed to mount NFS share", // STR_FAIL_MOUNT_NFS_MSG
"Web Server", // STR_WEB_SERVER
"Enable", // STR_ENABLE
"Compressed Files Location", // STR_COMPRESSED_FILE_PATH
"Location of where compressed files are stored on the web server", // STR_COMPRESSED_FILE_PATH_MSG
"AllDebrid", // STR_ALLDEBRID
"API Key", // STR_API_KEY
"Couldn't extract download url", // STR_CANT_EXTRACT_URL_MSG
"Failed to install from URL", // STR_FAIL_INSTALL_FROM_URL_MSG
"InValid URL", // STR_INVALID_URL
"To use this function, an API Key needs to be configured in the ezRemote Client settings", // STR_ALLDEBRID_API_KEY_MISSING_MSG
"Language", // STR_LANGUAGE
"Temp Directory", // STR_TEMP_DIRECTORY
"Real-Debrid", // STR_REALDEBRID
"Package install is running in the background. Don't close the app while install is in progress", // STR_BACKGROUND_INSTALL_INPROGRESS
"Enable disk caching. Can improve package install speed in cases where connection to remote is slow, but breaks resuming of install", // STR_ENABLE_DISC_CACHE_MSG
"DC", // STR_ENABLE_DISK_CACHE
"Install Via AllDebrid", // STR_ENABLE_ALLDEBRID_MSG
"Install Via RealDebrid", // STR_ENABLE_REALDEBRID_MSG
"Enable Disk Cache", // STR_ENABLE_DISKCACHE_DESC
"Cannot perform operation while activity is in progress", // STR_ACTIVITY_IN_PROGRESS_MSG
"ezRemote Direct Package Installer payload is not loaded", // STR_ETAHEN_DPI_ERROR_MSG
"Start/Restart Server", // STR_RESTART_SERVER
"Stop Server", // STR_STOP_SERVER
"Warning", // STR_WARNING
"The version of ezRemote Server payload running does not match the required version needed by ezRemote Client.", // STR_WARNING_MSG_1
"If you are using Goldhen payload loader, then update the elf that comes with ezRemoteClient package.", // STR_WARNING_MSG_2
"You may goto the Global Settings and restart ezRemote Server with the version that came packaged.", // STR_WARNING_MSG_3
"Enable background download", // STR_ENABLE_BG_DOWNLOAD
"Minimum background file size (bytes)", // STR_BG_DOWNLOAD_MIN_SIZE
"Background Download Progress", // STR_BG_DOWNLOAD_PROGRESS
"Show Background Download Progress", // STR_SHOW_BG_DOWNLOAD_PROGRESS
};
bool needs_extended_font = false;
@@ -251,6 +270,9 @@ namespace Lang
case ORBIS_SYSTEM_PARAM_LANG_ROMANIAN:
sprintf(langFile, "%s", "/app0/assets/langs/Romanian.ini");
break;
case ORBIS_SYSTEM_PARAM_LANG_NORWEGIAN:
sprintf(langFile, "%s", "/app0/assets/langs/Norwegian.ini");
break;
default:
sprintf(langFile, "%s", "/app0/assets/langs/English.ini");
break;
@@ -287,4 +309,4 @@ namespace Lang
sscanf(last_site, "%[^ ] %d", buf, &num);
sprintf(display_site, "%s %d", lang_strings[STR_SITE], num);
}
}
}
+24 -5
View File
@@ -1,5 +1,5 @@
#ifndef __LANG_H__
#define __LANG_H__
#ifndef __EZ_LANG_H__
#define __EZ_LANG_H__
#include "config.h"
@@ -163,7 +163,26 @@
FUNC(STR_INVALID_URL) \
FUNC(STR_ALLDEBRID_API_KEY_MISSING_MSG) \
FUNC(STR_LANGUAGE) \
FUNC(STR_TEMP_DIRECTORY)
FUNC(STR_TEMP_DIRECTORY) \
FUNC(STR_REALDEBRID) \
FUNC(STR_BACKGROUND_INSTALL_INPROGRESS) \
FUNC(STR_ENABLE_DISC_CACHE_MSG) \
FUNC(STR_ENABLE_DISK_CACHE) \
FUNC(STR_ENABLE_ALLDEBRID_MSG) \
FUNC(STR_ENABLE_REALDEBRID_MSG) \
FUNC(STR_ENABLE_DISKCACHE_DESC) \
FUNC(STR_ACTIVITY_IN_PROGRESS_MSG) \
FUNC(STR_DPI_NOT_STARTED_ERROR_MSG) \
FUNC(STR_RESTART_SERVER) \
FUNC(STR_STOP_SERVER) \
FUNC(STR_WARNING) \
FUNC(STR_WARNING_MSG_1) \
FUNC(STR_WARNING_MSG_2) \
FUNC(STR_WARNING_MSG_3) \
FUNC(STR_ENABLE_BG_DOWNLOAD) \
FUNC(STR_BG_DOWNLOAD_MIN_SIZE) \
FUNC(STR_BG_DOWNLOAD_PROGRESS) \
FUNC(STR_SHOW_BG_DOWNLOAD_PROGRESS) \
#define GET_VALUE(x) x,
#define GET_STRING(x) #x,
@@ -173,7 +192,7 @@ enum
FOREACH_STR(GET_VALUE)
};
#define LANG_STRINGS_NUM 160
#define LANG_STRINGS_NUM 179
#define LANG_ID_SIZE 64
#define LANG_STR_SIZE 384
extern char lang_identifiers[LANG_STRINGS_NUM][LANG_ID_SIZE];
@@ -185,4 +204,4 @@ namespace Lang
void SetTranslation(int32_t lang_idx);
}
#endif
#endif
+18 -10
View File
@@ -11,6 +11,7 @@
#include <orbis/Pad.h>
#include <orbis/AudioOut.h>
#include <orbis/Net.h>
// #include <dbglogger.h>
#include "imgui.h"
#include "SDL2/SDL.h"
@@ -138,41 +139,43 @@ void InitImgui()
sceSystemServiceParamGetInt( ORBIS_SYSTEM_SERVICE_PARAM_ID_LANG, &lang_idx );
lang = Util::Trim(lang, " ");
if (lang.compare("Korean") == 0 || (lang.empty() && lang_idx == ORBIS_SYSTEM_PARAM_LANG_KOREAN))
bool use_system_lang = lang.empty() || lang.compare("Default") == 0;
if (lang.compare("Korean") == 0 || (use_system_lang && lang_idx == ORBIS_SYSTEM_PARAM_LANG_KOREAN))
{
io.Fonts->AddFontFromFileTTF("/app0/assets/fonts/Roboto_ext.ttf", 26.0f, NULL, io.Fonts->GetGlyphRangesKorean());
}
else if (lang.compare("Simplified Chinese") == 0 || (lang.empty() && lang_idx == ORBIS_SYSTEM_PARAM_LANG_CHINESE_S))
else if (lang.compare("Simplified Chinese") == 0 || (use_system_lang && lang_idx == ORBIS_SYSTEM_PARAM_LANG_CHINESE_S))
{
ImFontConfig config;
config.OversampleH = 1;
config.OversampleV = 1;
io.Fonts->AddFontFromFileTTF("/app0/assets/fonts/Roboto_ext.ttf", 26.0f, &config, io.Fonts->GetGlyphRangesChineseFull());
}
else if (lang.compare("Traditional Chinese") == 0 || (lang.empty() && lang_idx == ORBIS_SYSTEM_PARAM_LANG_CHINESE_T))
else if (lang.compare("Traditional Chinese") == 0 || (use_system_lang && lang_idx == ORBIS_SYSTEM_PARAM_LANG_CHINESE_T))
{
ImFontConfig config;
config.OversampleH = 1;
config.OversampleV = 1;
io.Fonts->AddFontFromFileTTF("/app0/assets/fonts/Roboto_ext.ttf", 26.0f, &config, io.Fonts->GetGlyphRangesChineseFull());
}
else if (lang.compare("Japanese") == 0 || lang.compare("Ryukyuan") == 0 || (lang.empty() && lang_idx == ORBIS_SYSTEM_PARAM_LANG_JAPANESE))
else if (lang.compare("Japanese") == 0 || lang.compare("Ryukyuan") == 0 || (use_system_lang && lang_idx == ORBIS_SYSTEM_PARAM_LANG_JAPANESE))
{
io.Fonts->AddFontFromFileTTF("/app0/assets/fonts/Roboto_ext.ttf", 26.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
}
else if (lang.compare("Thai") == 0 || (lang.empty() && lang_idx == ORBIS_SYSTEM_PARAM_LANG_THAI))
else if (lang.compare("Thai") == 0 || (use_system_lang && lang_idx == ORBIS_SYSTEM_PARAM_LANG_THAI))
{
io.Fonts->AddFontFromFileTTF("/app0/assets/fonts/Roboto_ext.ttf", 26.0f, NULL, io.Fonts->GetGlyphRangesThai());
}
else if (lang.compare("Vietnamese") == 0 || (lang.empty() && lang_idx == ORBIS_SYSTEM_PARAM_LANG_VIETNAMESE))
else if (lang.compare("Vietnamese") == 0 || (use_system_lang && lang_idx == ORBIS_SYSTEM_PARAM_LANG_VIETNAMESE))
{
io.Fonts->AddFontFromFileTTF("/app0/assets/fonts/Roboto_ext.ttf", 26.0f, NULL, io.Fonts->GetGlyphRangesVietnamese());
}
else if (lang.compare("Greek") == 0 || (lang.empty() && lang_idx == ORBIS_SYSTEM_PARAM_LANG_GREEK))
else if (lang.compare("Greek") == 0 || (use_system_lang && lang_idx == ORBIS_SYSTEM_PARAM_LANG_GREEK))
{
io.Fonts->AddFontFromFileTTF("/app0/assets/fonts/Roboto_ext.ttf", 26.0f, NULL, io.Fonts->GetGlyphRangesGreek());
}
else if (lang.compare("Arabic") == 0 || (lang.empty() && lang_idx == ORBIS_SYSTEM_PARAM_LANG_ARABIC))
else if (lang.compare("Arabic") == 0 || (use_system_lang && lang_idx == ORBIS_SYSTEM_PARAM_LANG_ARABIC))
{
io.Fonts->AddFontFromFileTTF("/app0/assets/fonts/Roboto_ext.ttf", 26.0f, NULL, arabic);
}
@@ -291,13 +294,12 @@ int main()
if (sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_AUDIOOUT) < 0 || sceAudioOutInit() != 0) return 0;
if (sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NET) < 0 || sceNetInit() != 0) return 0;
sceNetPoolCreate("simple", NET_HEAP_SIZE, 0);
sceNetPoolCreate("ezremote_client", NET_HEAP_SIZE, 0);
if (INSTALLER::Init() < 0)
return 0;
CONFIG::LoadConfig();
HttpServer::Start();
// Create a window context
window = SDL_CreateWindow("main", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, FRAME_WIDTH, FRAME_HEIGHT, 0);
@@ -326,6 +328,12 @@ int main()
atexit(terminate);
HttpServer::Start();
if (INSTALLER::StartEzRemoteServer() < 0)
{
Util::Notify("Cloud not load ezRemote Server. It is needed for background install and download. Please ensure Goldhen payload server is enabled");
}
GUI::RenderLoop(renderer);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
+197
View File
@@ -0,0 +1,197 @@
#include <stdio.h>
#include <stdio.h>
#include <string>
#include "common.h"
#include "mem_file.h"
MemFile::MemFile(const std::string &path, size_t block_size)
{
this->block_size = block_size;
this->complete = false;
sem_init(&this->block_ready, 0, 0);
}
MemFile::~MemFile()
{
for (int i = 0; i < this->mem_blocks.size(); i++)
{
if (this->mem_blocks[i] != nullptr && this->mem_blocks[i]->status != MEM_BLOCK_STATUS_DELETED)
{
if (this->mem_blocks[i]->buf != nullptr)
{
free(this->mem_blocks[i]->buf);
this->mem_blocks[i]->buf = nullptr;
}
free(this->mem_blocks[i]);
}
}
sem_destroy(&this->block_ready);
};
int MemFile::Open()
{
this->block_in_progress = NewBlock();
this->block_in_progress->buf = malloc(block_size);
return (block_in_progress->buf == nullptr);
}
size_t MemFile::Read(char *buf, size_t buf_size, size_t offset)
{
int first_block_num, block_num;
size_t block_offset;
size_t remaining;
size_t bytes_read;
size_t total_bytes_read;
MemBlock *block;
char *p;
first_block_num= offset / this->block_size;
block_num = first_block_num;
block_offset = offset % this->block_size;
while ((block_num >= this->mem_blocks.size() && !this->complete) ||
(block_num < this->mem_blocks.size() && this->mem_blocks[block_num]->status == MEM_BLOCK_STATUS_NOT_EXISTS))
{
sem_wait(&this->block_ready);
}
block = this->mem_blocks[block_num];
if (block->status == MEM_BLOCK_STATUS_DELETED)
{
return -1;
}
if (block_offset > block->size - 1 && this->complete)
{
// requested offset is pass the end of split file
return 0;
}
remaining = buf_size;
bool eof = false;
total_bytes_read = 0;
p = buf;
while (remaining > 0 && !eof)
{
uint8_t *src = (uint8_t*)block->buf;
src += block_offset;
bytes_read = block_size - block_offset;
memcpy(p, src, bytes_read);
if (bytes_read == remaining)
{
p += bytes_read;
total_bytes_read += bytes_read;
}
else
{
p += bytes_read;
total_bytes_read += bytes_read;
if (block->is_last)
{
eof = true;
continue;
}
}
remaining -= bytes_read;
if (remaining == 0)
continue;
block_num++;
block_offset = 0;
while ((block_num > this->mem_blocks.size() - 1 && !this->complete) ||
this->mem_blocks[block_num]->status == MEM_BLOCK_STATUS_NOT_EXISTS)
{
sem_wait(&this->block_ready);
}
block = this->mem_blocks[block_num];
}
// 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++)
{
if (this->mem_blocks[j]->status == MEM_BLOCK_STATUS_CREATED)
{
if (this->mem_blocks[j]->buf != nullptr)
{
free(this->mem_blocks[j]->buf);
this->mem_blocks[j]->buf = nullptr;
}
this->mem_blocks[j]->status = MEM_BLOCK_STATUS_DELETED;
}
}
return total_bytes_read;
}
size_t MemFile::Write(char *buf, size_t buf_size)
{
size_t bytes_written;
size_t block_space_remaining;
size_t bytes_to_write;
char *p = buf;
size_t total_bytes_written = 0;
size_t remaining_to_write = buf_size;
while (remaining_to_write > 0)
{
block_space_remaining = this->block_size - block_in_progress->size;
bytes_to_write = MIN(remaining_to_write, block_space_remaining);
memcpy(block_in_progress->buf, p, bytes_to_write);
bytes_written = bytes_to_write;
block_in_progress->size += bytes_written;
total_bytes_written += bytes_written;
remaining_to_write -= bytes_written;
block_space_remaining -= bytes_written;
p += bytes_written;
// error if bytes_to_write != bytes_written
if (bytes_written != bytes_to_write)
{
break;
}
if (block_space_remaining == 0)
{
block_in_progress->status = MEM_BLOCK_STATUS_CREATED;
this->mem_blocks.push_back(block_in_progress);
sem_post(&this->block_ready);
block_in_progress = NewBlock();
}
}
return total_bytes_written;
}
int MemFile::Close()
{
block_in_progress->status = MEM_BLOCK_STATUS_CREATED;
block_in_progress->is_last = true;
this->mem_blocks.push_back(block_in_progress);
this->complete = true;
sem_post(&this->block_ready);
return 0;
}
MemBlock *MemFile::NewBlock()
{
MemBlock *block = (MemBlock *)malloc(sizeof(MemBlock));
memset(block, 0, sizeof(MemBlock));
block->is_last = false;
block->size = 0;
block->buf = malloc(block_size);
return block;
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef EZ_MEM_FILE_H
#define EZ_MEM_FILE_H
#include <string>
#include <vector>
#include <mutex>
#include <pthread.h>
enum MemBlockStatus
{
MEM_BLOCK_STATUS_NOT_EXISTS,
MEM_BLOCK_STATUS_CREATED,
MEM_BLOCK_STATUS_DELETED
};
typedef struct
{
size_t size;
void* buf;
bool is_last;
MemBlockStatus status;
} MemBlock;
class MemFile
{
public:
MemFile(const std::string& path, size_t block_size);
~MemFile();
size_t Read(char* buf, size_t buf_size, size_t offset);
size_t Write(char* buf, size_t buf_size);
int Open();
int Close();
private:
std::vector<MemBlock*> mem_blocks;
size_t write_offset;
size_t block_size;
int write_error;
bool complete;
MemBlock *block_in_progress;
sem_t block_ready;
MemBlock *NewBlock();
};
#endif
+22 -15
View File
@@ -45,25 +45,32 @@ static void build_iovec(struct iovec** iov, int* iovlen, const char* name, const
int mount_large_fs(const char* device, const char* mountpoint, const char* fstype, const char* mode, unsigned int flags)
{
struct iovec* iov = NULL;
int iovlen = 0;
struct iovec* iov = NULL;
int iovlen = 0;
int ret;
unmount(mountpoint, 0);
build_iovec(&iov, &iovlen, "fstype", fstype, -1);
build_iovec(&iov, &iovlen, "fspath", mountpoint, -1);
build_iovec(&iov, &iovlen, "from", device, -1);
build_iovec(&iov, &iovlen, "large", "yes", -1);
build_iovec(&iov, &iovlen, "timezone", "static", -1);
build_iovec(&iov, &iovlen, "async", "", -1);
build_iovec(&iov, &iovlen, "ignoreacl", "", -1);
build_iovec(&iov, &iovlen, "fstype", fstype, -1);
build_iovec(&iov, &iovlen, "fspath", mountpoint, -1);
build_iovec(&iov, &iovlen, "from", device, -1);
build_iovec(&iov, &iovlen, "large", "yes", -1);
build_iovec(&iov, &iovlen, "timezone", "static", -1);
build_iovec(&iov, &iovlen, "async", "", -1);
build_iovec(&iov, &iovlen, "ignoreacl", "", -1);
if (mode) {
build_iovec(&iov, &iovlen, "dirmask", mode, -1);
build_iovec(&iov, &iovlen, "mask", mode, -1);
}
if (mode) {
build_iovec(&iov, &iovlen, "dirmask", mode, -1);
build_iovec(&iov, &iovlen, "mask", mode, -1);
}
ret = nmount(iov, iovlen, flags);
if (ret < 0) {
goto error;
}
else {
}
return nmount(iov, iovlen, flags);
error:
return ret;
}
// Variables for (un)jailbreaking
+540 -137
View File
@@ -8,6 +8,15 @@
#include "clients/smbclient.h"
#include "clients/ftpclient.h"
#include "clients/nfsclient.h"
#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"
#include "filehost/filehost.h"
#include "config.h"
#include "fs.h"
@@ -24,11 +33,21 @@
#define SUCCESS_MSG "{ \"result\": { \"success\": true, \"error\": null } }"
#define FAILURE_MSG "{ \"result\": { \"success\": false, \"error\": \"%s\" } }"
#define SUCCESS_MSG_LEN 48
#define PKG_INITIAL_REQUEST_SIZE 8388608ul
using namespace httplib;
struct RemoteDownloadData
{
RemoteClient *client = nullptr;
std::map<std::string, void *> fp_handles;
};
static RemoteDownloadData remote_data[100];
Server *svr;
int http_server_port = 8080;
int http_int_server_port = 6701;
char compressed_file_path[1024];
bool web_server_enabled = false;
@@ -91,22 +110,22 @@ namespace HttpServer
return s;
}
void failed(Response & res, int status, const std::string &msg)
void failed(Response &res, int status, const std::string &msg)
{
res.status = status;
char response_msg[msg.length()+strlen(FAILURE_MSG)+2];
char response_msg[msg.length() + strlen(FAILURE_MSG) + 2];
snprintf(response_msg, sizeof(response_msg), "{ \"result\": { \"success\": false, \"error\": \"%s\" } }", msg.c_str());
res.set_content(response_msg, strlen(response_msg), "application/json");
return;
}
void bad_request(Response & res, const std::string &msg)
void bad_request(Response &res, const std::string &msg)
{
failed(res, 200, msg);
return;
}
void success(Response & res)
void success(Response &res)
{
res.status = 200;
res.set_content(SUCCESS_MSG, SUCCESS_MSG_LEN, "application/json");
@@ -182,15 +201,93 @@ namespace HttpServer
return 1;
}
static RemoteClient *GetRemoteClient(int site_idx, bool new_client)
{
RemoteClient *tmp_client;
RemoteSettings *tmp_settings = &site_settings[sites[site_idx]];
if (!new_client)
{
tmp_client = remote_data[site_idx].client;
if (tmp_client != nullptr)
return tmp_client;
}
if (tmp_settings->type == CLIENT_TYPE_SFTP)
{
tmp_client = new SFTPClient();
}
else if (tmp_settings->type == CLIENT_TYPE_SMB)
{
tmp_client = new SmbClient();
}
else if (tmp_settings->type == CLIENT_TYPE_FTP)
{
tmp_client = new FtpClient();
}
else if (tmp_settings->type == CLIENT_TYPE_NFS)
{
tmp_client = new NfsClient();
}
else if (tmp_settings->type == CLIENT_TYPE_WEBDAV)
{
tmp_client = new WebDAVClient();
}
else if (tmp_settings->type == CLIENT_TYPE_GOOGLE)
{
if (remoteclient != nullptr && remoteclient->clientType() == CLIENT_TYPE_GOOGLE)
tmp_client = remoteclient;
else
tmp_client = new GDriveClient();
tmp_client->Connect("", "", "");
}
else if (tmp_settings->type == CLIENT_TYPE_HTTP_SERVER)
{
if (strcmp(remote_settings->http_server_type, HTTP_SERVER_APACHE) == 0)
tmp_client = new ApacheClient();
else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_MS_IIS) == 0)
tmp_client = new IISClient();
else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_NGINX) == 0)
tmp_client = new NginxClient();
else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_NPX_SERVE) == 0)
tmp_client = new NpxServeClient();
else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_RCLONE) == 0)
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)
tmp_client->Connect(tmp_settings->server, tmp_settings->username, tmp_settings->password);
if (!new_client && tmp_client->clientType() != CLIENT_TYPE_FTP)
{
remote_data[site_idx].client = tmp_client;
}
return tmp_client;
}
static void DeleteRemoteClient(RemoteClient *tmp_client, int site_idx)
{
if (tmp_client != nullptr && tmp_client->clientType() != CLIENT_TYPE_GOOGLE)
{
tmp_client->Quit();
delete tmp_client;
}
}
void *ServerThread(void *argp)
{
svr->Get("/", [&](const Request & req, Response & res)
{
res.set_redirect("/index.html");
});
svr->Get("/", [&](const Request &req, Response &res)
{ res.set_redirect("/index.html"); });
svr->Get("/index.html", [&](const Request & req, Response & res)
{
svr->Get("/index.html", [&](const Request &req, Response &res)
{
FILE *in = FS::OpenRead("/mnt/sandbox/pfsmnt/RMTC00001-app0/assets/index.html");
size_t size = FS::GetSize("/mnt/sandbox/pfsmnt/RMTC00001-app0/assets/index.html");
res.set_content_provider(
@@ -206,11 +303,10 @@ namespace HttpServer
},
[in](bool success) {
FS::Close(in);
});
});
}); });
svr->Get("/favicon.ico", [&](const Request & req, Response & res)
{
svr->Get("/favicon.ico", [&](const Request &req, Response &res)
{
FILE *in = FS::OpenRead("/mnt/sandbox/pfsmnt/RMTC00001-app0/assets/favicon.ico");
size_t size = FS::GetSize("/mnt/sandbox/pfsmnt/RMTC00001-app0/assets/favicon.ico");
res.set_content_provider(
@@ -226,10 +322,9 @@ namespace HttpServer
},
[in](bool success) {
FS::Close(in);
});
});
}); });
svr->Post("/__local__/list", [&](const Request & req, Response & res)
svr->Post("/__local__/list", [&](const Request &req, Response &res)
{
const char *path;
bool onlyFolders = false;
@@ -275,11 +370,11 @@ namespace HttpServer
json_object *results = json_object_new_object();
json_object_object_add(results, "result", json_files);
const char *results_str = json_object_to_json_string(results);
res.status = 200;
res.set_content(results_str, strlen(results_str), "application/json");
});
svr->Post("/__local__/rename", [&](const Request & req, Response & res)
res.status = 200;
res.set_content(results_str, strlen(results_str), "application/json"); });
svr->Post("/__local__/rename", [&](const Request &req, Response &res)
{
const char *item;
const char *newItemPath;
@@ -302,11 +397,16 @@ namespace HttpServer
FS::Rename(item, newItemPath);
success(res);
return;
});
return; });
svr->Post("/__local__/move", [&](const Request & req, Response & res)
svr->Post("/__local__/move", [&](const Request &req, Response &res)
{
if (activity_inprogess)
{
failed(res, 200, lang_strings[STR_ACTIVITY_IN_PROGRESS_MSG]);
return;
}
const json_object *items;
const char *newPath;
json_object *jobj = json_tokener_parse(req.body.c_str());
@@ -357,11 +457,16 @@ namespace HttpServer
failed(res, 200, error_msg);
}
else
success(res);
});
success(res); });
svr->Post("/__local__/copy", [&](const Request & req, Response & res)
svr->Post("/__local__/copy", [&](const Request &req, Response &res)
{
if (activity_inprogess)
{
failed(res, 200, lang_strings[STR_ACTIVITY_IN_PROGRESS_MSG]);
return;
}
const json_object *items;
const char *newPath;
const char *singleFilename;
@@ -432,11 +537,16 @@ namespace HttpServer
failed(res, 200, error_msg);
}
else
success(res);
});
success(res); });
svr->Post("/__local__/remove", [&](const Request & req, Response & res)
svr->Post("/__local__/remove", [&](const Request &req, Response &res)
{
if (activity_inprogess)
{
failed(res, 200, lang_strings[STR_ACTIVITY_IN_PROGRESS_MSG]);
return;
}
json_object *items;
json_object *jobj = json_tokener_parse(req.body.c_str());
if (jobj != nullptr)
@@ -472,11 +582,16 @@ namespace HttpServer
failed(res, 200, error_msg);
}
else
success(res);
});
success(res); });
svr->Post("/__local__/install", [&](const Request & req, Response & res)
svr->Post("/__local__/install", [&](const Request &req, Response &res)
{
if (activity_inprogess)
{
failed(res, 200, lang_strings[STR_ACTIVITY_IN_PROGRESS_MSG]);
return;
}
json_object *items;
json_object *jobj = json_tokener_parse(req.body.c_str());
if (jobj != nullptr)
@@ -512,7 +627,7 @@ namespace HttpServer
success(res);
});
svr->Post("/__local__/edit", [&](const Request & req, Response & res)
svr->Post("/__local__/edit", [&](const Request &req, Response &res)
{
const char *item;
const char *content;
@@ -543,10 +658,9 @@ namespace HttpServer
return;
}
success(res);
});
success(res); });
svr->Post("/__local__/getContent", [&](const Request & req, Response & res)
svr->Post("/__local__/getContent", [&](const Request &req, Response &res)
{
const char *item;
json_object *jobj = json_tokener_parse(req.body.c_str());
@@ -569,11 +683,12 @@ namespace HttpServer
json_object *result = json_object_new_object();
json_object_object_add(result, "result", json_object_new_string(content.data()));
const char *result_str = json_object_to_json_string(result);
res.status = 200;
res.set_content(result_str, strlen(result_str), "application/json");
});
svr->Post("/__local__/createFolder", [&](const Request & req, Response & res)
svr->Post("/__local__/createFolder", [&](const Request &req, Response &res)
{
const char *newPath;
json_object *jobj = json_tokener_parse(req.body.c_str());
@@ -596,13 +711,19 @@ namespace HttpServer
success(res);
});
svr->Post("/__local__/permission", [&](const Request & req, Response & res)
svr->Post("/__local__/permission", [&](const Request &req, Response &res)
{
failed(res, 200, "Operation not supported");
});
svr->Post("/__local__/compress", [&](const Request & req, Response & res)
svr->Post("/__local__/compress", [&](const Request &req, Response &res)
{
if (activity_inprogess)
{
failed(res, 200, lang_strings[STR_ACTIVITY_IN_PROGRESS_MSG]);
return;
}
json_object *items;
const char* destination;
const char* compressedFilename;
@@ -652,11 +773,16 @@ namespace HttpServer
else
{
failed(res, 200, "Failed to create zip");
}
});
} });
svr->Post("/__local__/extract", [&](const Request & req, Response & res)
svr->Post("/__local__/extract", [&](const Request &req, Response &res)
{
if (activity_inprogess)
{
failed(res, 200, lang_strings[STR_ACTIVITY_IN_PROGRESS_MSG]);
return;
}
const char* item;
const char* destination;
const char* folderName;
@@ -692,8 +818,7 @@ namespace HttpServer
else if (ret == -1)
failed(res, 200, "Unsupported compressed file format");
else
success(res);
});
success(res); });
svr->Get("/__local__/uploadResumeSize", [&](const Request &req, Response &res)
{
@@ -705,8 +830,7 @@ namespace HttpServer
size = FS::GetSize(file_path);
std::string result_str = "{\"size\":" + std::to_string(size) + "}";
res.status = 200;
res.set_content(result_str.c_str(), result_str.length(), "application/json");
});
res.set_content(result_str.c_str(), result_str.length(), "application/json"); });
svr->Post("/__local__/upload", [&](const Request &req, Response &res, const ContentReader &content_reader)
{
@@ -775,11 +899,10 @@ namespace HttpServer
{
FS::Close(out);
}
success(res);
});
success(res); });
// Download multiple files as ZIP
svr->Get("/__local__/downloadMultiple", [&](const Request & req, Response & res)
svr->Get("/__local__/downloadMultiple", [&](const Request &req, Response &res)
{
if (req.get_param_value_count("items") == 0 || req.get_param_value_count("toFilename") == 0)
{
@@ -834,11 +957,10 @@ namespace HttpServer
else
{
failed(res, 200, "Failed to create zip");
}
});
} });
// Download single file
svr->Get("/__local__/downloadFile", [&](const Request & req, Response & res)
svr->Get("/__local__/downloadFile", [&](const Request &req, Response &res)
{
std::string path = req.get_param_value("path", 0);
if (path.empty())
@@ -869,8 +991,7 @@ namespace HttpServer
},
[in](bool success) {
FS::Close(in);
});
});
}); });
svr->Get("/google_auth", [](const Request &req, Response &res)
{
@@ -883,7 +1004,7 @@ namespace HttpServer
std::string post_data = std::string("code=") + auth_code +
"&client_id=" + gg_app.client_id +
"&client_secret=" + gg_app.client_secret +
"&redirect_uri=http%3A//localhost%3A" + std::to_string(http_server_port) + "/google_auth"
"&redirect_uri=http%3A//127.0.0.1%3A" + std::to_string(http_server_port) + "/google_auth"
"&grant_type=authorization_code";
if (auto result = client.Post(url, post_data.c_str(), post_data.length(), "application/x-www-form-urlencoded"))
@@ -922,9 +1043,9 @@ namespace HttpServer
res.set_content(str.c_str(), "text/plain");
});
svr->Get("/rmt_inst/Site (\\d+)(/)(.*)", [&](const Request & req, Response & res)
svr->Get("/rmt_inst/Site (\\d+)(/)(.*)", [&](const Request &req, Response &res)
{
RemoteClient *tmp_client;
RemoteClient *tmp_client = nullptr;
RemoteSettings *tmp_settings;
auto site_idx = std::stoi(req.matches[1])-1;
std::string path;
@@ -932,33 +1053,6 @@ namespace HttpServer
if (site_idx != 98)
{
path = std::string("/") + std::string(req.matches[3]);
tmp_settings = &site_settings[sites[site_idx]];
if (tmp_settings->type == CLIENT_TYPE_SFTP)
{
tmp_client = new SFTPClient();
tmp_client->Connect(tmp_settings->server, tmp_settings->username, tmp_settings->password);
}
else if (tmp_settings->type == CLIENT_TYPE_SMB)
{
tmp_client = new SmbClient();
tmp_client->Connect(tmp_settings->server, tmp_settings->username, tmp_settings->password);
}
else if (tmp_settings->type == CLIENT_TYPE_FTP)
{
tmp_client = new FtpClient();
tmp_client->Connect(tmp_settings->server, tmp_settings->username, tmp_settings->password);
}
else if (tmp_settings->type == CLIENT_TYPE_NFS)
{
tmp_client = new NfsClient();
tmp_client->Connect(tmp_settings->server, tmp_settings->username, tmp_settings->password);
}
else
{
tmp_client = remoteclient;
}
}
else
{
@@ -973,33 +1067,34 @@ namespace HttpServer
tmp_client->Connect(host, "", "");
}
if (tmp_client == nullptr || !tmp_client->IsConnected())
{
res.status = 404;
return;
}
if (req.method == "HEAD")
{
int64_t file_size;
int ret;
if (site_idx != 98)
tmp_client = GetRemoteClient(site_idx, true);
ret = tmp_client->Size(path, &file_size);
if (!ret)
{
res.status = 500;
DeleteRemoteClient(tmp_client, site_idx);
return;
}
res.status = 204;
res.set_header("Content-Length", std::to_string(file_size));
res.set_header("Accept-Ranges", "bytes");
DeleteRemoteClient(tmp_client, site_idx);
return;
}
if (req.ranges.empty())
{
res.status = 200;
if (site_idx != 98)
tmp_client = GetRemoteClient(site_idx, true);
res.set_content_provider(
(1024*128), "application/octet-stream",
[tmp_client, path](size_t offset, size_t length, DataSink &sink) {
@@ -1007,15 +1102,7 @@ namespace HttpServer
return (ret == 1);
},
[tmp_client, path, site_idx](bool success) {
if (tmp_client != nullptr && (tmp_client->clientType() == CLIENT_TYPE_SFTP
|| tmp_client->clientType() == CLIENT_TYPE_SMB
|| tmp_client->clientType() == CLIENT_TYPE_FTP
|| tmp_client->clientType() == CLIENT_TYPE_NFS
|| (tmp_client->clientType() == CLIENT_TYPE_HTTP_SERVER && site_idx == 98)))
{
tmp_client->Quit();
delete tmp_client;
}
DeleteRemoteClient(tmp_client, site_idx);
});
}
else
@@ -1024,32 +1111,60 @@ namespace HttpServer
size_t range_len = (req.ranges[0].second - req.ranges[0].first) + 1;
if (req.ranges[0].second >= 18000000000000000000ul)
{
range_len = 65536ul - req.ranges[0].first;
range_len = PKG_INITIAL_REQUEST_SIZE;
res.set_header("Content-Length", std::to_string(range_len));
res.set_header("Content-Range", std::string("bytes ") + std::to_string(req.ranges[0].first)+"-65535/"+std::to_string(range_len));
res.set_header("Content-Range", std::string("bytes ") + std::to_string(req.ranges[0].first)+"-" + std::to_string(req.ranges[0].first+PKG_INITIAL_REQUEST_SIZE-1) + "/"+std::to_string(range_len));
sceRtcGetCurrentTick(&prev_tick);
if (site_idx != 98)
tmp_client = GetRemoteClient(site_idx, true);
}
else
{
if (site_idx != 98)
tmp_client = GetRemoteClient(site_idx, false);
}
std::pair<ssize_t, ssize_t> range = req.ranges[0];
res.set_content_provider(
range_len, "application/octet-stream",
[tmp_client, path, range, range_len](size_t offset, size_t length, DataSink &sink) {
int ret = tmp_client->GetRange(path, sink, range_len, range.first);
return (ret == 1);
},
[tmp_client, site_idx, path, range, range_len](bool success) {
if (tmp_client != nullptr && (tmp_client->clientType() == CLIENT_TYPE_SFTP
|| tmp_client->clientType() == CLIENT_TYPE_SMB
|| tmp_client->clientType() == CLIENT_TYPE_FTP
|| tmp_client->clientType() == CLIENT_TYPE_NFS
|| (tmp_client->clientType() == CLIENT_TYPE_HTTP_SERVER && site_idx == 98)))
[tmp_client, path, range, range_len, site_idx](size_t offset, size_t length, DataSink &sink) {
int ret;
if (range_len == PKG_INITIAL_REQUEST_SIZE)
{
tmp_client->Quit();
delete tmp_client;
ret = tmp_client->GetRange(path, sink, range_len, range.first);
}
else if ((tmp_client->SupportedActions() & REMOTE_ACTION_RAW_READ) == 0)
{
ret = tmp_client->GetRange(path, sink, range_len, range.first);
}
else
{
std::map<std::string, void *>::iterator it = remote_data[site_idx].fp_handles.find(path);
void *fp;
if (it == remote_data[site_idx].fp_handles.end())
{
fp = tmp_client->Open(path, O_RDONLY);
remote_data[site_idx].fp_handles[path] = fp;
}
else
{
fp = it->second;
}
ret = tmp_client->GetRange(fp, sink, range_len, range.first);
}
return (ret==1);
},
[tmp_client, path, range, site_idx](bool success) {
if (range.second >= 18000000000000000000ul ||
(tmp_client->clientType() == CLIENT_TYPE_HTTP_SERVER && site_idx == 98) ||
tmp_client->clientType() == CLIENT_TYPE_FTP)
{
DeleteRemoteClient(tmp_client, site_idx);
}
});
}
});
} });
svr->Get("/archive_inst/(.*)", [&](const Request & req, Response & res)
svr->Get("/archive_inst/(.*)", [&](const Request &req, Response &res)
{
RemoteClient *tmp_client;
RemoteSettings *tmp_settings;
@@ -1087,9 +1202,10 @@ namespace HttpServer
size_t range_len = (req.ranges[0].second - req.ranges[0].first) + 1;
if (req.ranges[0].second >= 18000000000000000000ul)
{
range_len = 65536ul - req.ranges[0].first;
range_len = PKG_INITIAL_REQUEST_SIZE;
res.set_header("Content-Length", std::to_string(range_len));
res.set_header("Content-Range", std::string("bytes ") + std::to_string(req.ranges[0].first)+"-65535/"+std::to_string(range_len));
res.set_header("Content-Range", std::string("bytes ") + std::to_string(req.ranges[0].first)+"-" + std::to_string(req.ranges[0].first+PKG_INITIAL_REQUEST_SIZE-1) + "/"+std::to_string(range_len));
sceRtcGetCurrentTick(&prev_tick);
}
std::pair<ssize_t, ssize_t> range = req.ranges[0];
res.set_content_provider(
@@ -1107,19 +1223,89 @@ namespace HttpServer
}
});
svr->Post("/__local__/install_url", [&](const Request & req, Response & res)
svr->Get("/split_inst/(.*)", [&](const Request &req, Response &res)
{
std::string hash = req.matches[1];
SplitPkgInstallData *pkg_data = INSTALLER::GetSplitPkgInstallData(hash);
if (pkg_data == nullptr)
{
failed(res, 500, "Cannot resume split_inst");
return;
}
if (req.method == "HEAD")
{
res.status = 204;
res.set_header("Content-Length", std::to_string(pkg_data->size));
res.set_header("Accept-Ranges", "bytes");
return;
}
if (req.ranges.empty())
{
res.status = 200;
res.set_content_provider(
131072, "application/octet-stream",
[pkg_data](size_t offset, size_t length, DataSink &sink) {
char *buf = (char*) malloc(131072);
size_t bytes_read = pkg_data->split_file->Read(buf, 131072, offset);
sink.write(buf, bytes_read);
free(buf);
return true;
},
[](bool success) {
return true;
});
}
else
{
res.status = 206;
size_t range_len = (req.ranges[0].second - req.ranges[0].first) + 1;
if (req.ranges[0].second >= 18000000000000000000ul)
{
range_len = PKG_INITIAL_REQUEST_SIZE;
res.set_header("Content-Length", std::to_string(range_len));
res.set_header("Content-Range", std::string("bytes ") + std::to_string(req.ranges[0].first)+"-" + std::to_string(req.ranges[0].first+PKG_INITIAL_REQUEST_SIZE-1) + "/"+std::to_string(range_len));
sceRtcGetCurrentTick(&prev_tick);
}
std::pair<ssize_t, ssize_t> range = req.ranges[0];
res.set_content_provider(
range_len, "application/octet-stream",
[pkg_data, range, range_len](size_t offset, size_t length, DataSink &sink) {
char *buf = (char*) malloc(range_len);
size_t bytes_read = pkg_data->split_file->Read(buf, range_len, range.first);
sink.write(buf, bytes_read);
free(buf);
return true;
},
[](bool success) {
return true;
});
} });
svr->Post("/__local__/install_url", [&](const Request &req, Response &res)
{
std::string url;
const char *url_param;
bool use_alldebrid;
bool use_alldebrid = false;
bool use_realdebrid = false;
bool use_disk_cache = false;
bool enable_rpi = false;
json_object *jobj = json_tokener_parse(req.body.c_str());
if (jobj != nullptr)
{
url_param = json_object_get_string(json_object_object_get(jobj, "url"));
use_alldebrid = json_object_get_boolean(json_object_object_get(jobj, "use_alldebrid"));
use_realdebrid = json_object_get_boolean(json_object_object_get(jobj, "use_realdebrid"));
use_disk_cache = json_object_get_boolean(json_object_object_get(jobj, "use_disk_cache"));
enable_rpi = json_object_get_boolean(json_object_object_get(jobj, "enable_rpi"));
if (url_param == nullptr)
{
bad_request(res, "Required url_param, use_alldebrid parameter missing");
bad_request(res, "Required url_param parameter missing");
return;
}
}
@@ -1129,14 +1315,14 @@ namespace HttpServer
return;
}
if (use_alldebrid && strlen(alldebrid_api_key) == 0)
if ((use_alldebrid && strlen(alldebrid_api_key) == 0) || (use_realdebrid && strlen(realdebrid_api_key) == 0))
{
failed(res, 200, lang_strings[STR_ALLDEBRID_API_KEY_MISSING_MSG]);
return;
}
url = std::string(url_param);
FileHost *filehost = FileHost::getFileHost(url, use_alldebrid);
FileHost *filehost = FileHost::getFileHost(url, use_alldebrid, use_realdebrid);
if (!filehost->IsValidUrl())
{
@@ -1145,14 +1331,24 @@ namespace HttpServer
}
std::string hash = Util::UrlHash(filehost->GetUrl());
snprintf(activity_message, 1023, "%s %s", lang_strings[STR_INSTALLING], filehost->GetUrl().c_str());
activity_inprogess = true;
file_transfering = true;
bytes_to_download = 100;
bytes_transfered = 0;
sceRtcGetCurrentTick(&prev_tick);
Windows::SetModalMode(true);
std::string download_url = filehost->GetDownloadUrl();
if (download_url.empty())
{
failed(res, 200, lang_strings[STR_CANT_EXTRACT_URL_MSG]);
activity_inprogess = false;
file_transfering = false;
Windows::SetModalMode(false);
return;
}
FileHost::AddCacheDownloadUrl(hash, download_url);
delete(filehost);
size_t scheme_pos = download_url.find("://");
@@ -1163,19 +1359,226 @@ namespace HttpServer
BaseClient *baseclient = new BaseClient();
baseclient->Connect(host, "", "");
baseclient->Head(path, &header, sizeof(pkg_header));
std::string title = INSTALLER::GetRemotePkgTitle(baseclient, path, &header);
delete(baseclient);
std::string remote_install_url = std::string("http://localhost:") + std::to_string(http_server_port) + "/rmt_inst/Site%2099/" + hash;
int rc = INSTALLER::InstallRemotePkg(remote_install_url, &header, title);
if (rc == 0)
if (!baseclient->FileExists(path))
{
failed(res, 200, lang_strings[STR_FAIL_INSTALL_FROM_URL_MSG]);
failed(res, 200, baseclient->LastResponse());
activity_inprogess = false;
file_transfering = false;
Windows::SetModalMode(false);
return;
}
baseclient->Head(path, &header, sizeof(pkg_header));
FileHost::AddCacheDownloadUrl(hash, download_url);
std::string title = INSTALLER::GetRemotePkgTitle(baseclient, path, &header);
if (BE32(header.pkg_magic) == 0x7F434E54)
{
if (enable_rpi && !use_disk_cache)
{
json_object *history_item_obj = json_object_new_object();
json_object_object_add(history_item_obj, "hash", json_object_new_string(hash.c_str()));
json_object_object_add(history_item_obj, "url", json_object_new_string(host.c_str()));
json_object_object_add(history_item_obj, "path", json_object_new_string(path.c_str()));
json_object_object_add(history_item_obj, "username", json_object_new_string(""));
json_object_object_add(history_item_obj, "password", json_object_new_string(""));
json_object_object_add(history_item_obj, "type", json_object_new_int(CLIENT_TYPE_FILEHOST));
const char *params_str = json_object_to_json_string(history_item_obj);
Client tmp_client = Client(std::string("http://127.0.0.1:") + std::to_string(http_int_server_port));
if (auto resp = tmp_client.Post("/store_bg_install_data", params_str, strlen(params_str), "application/json"))
{
if (HTTP_SUCCESS(resp->status))
{
std::string remote_install_url = std::string("http://localhost:") + std::to_string(http_int_server_port) + "/bg_install/" + hash;
int rc = INSTALLER::InstallRemotePkg(remote_install_url, &header, title);
activity_inprogess = false;
file_transfering = false;
Windows::SetModalMode(false);
sleep(2);
}
else
{
failed(res, 200, "Could not save host data for background install");
}
}
else
{
failed(res, 200, "Could not save host data for background install");
}
}
else if (enable_rpi && use_disk_cache)
{
SplitPkgInstallData *install_data = (SplitPkgInstallData*) malloc(sizeof(SplitPkgInstallData));
memset(install_data, 0, sizeof(SplitPkgInstallData));
OrbisTick tick;
sceRtcGetCurrentTick(&tick);
std::string install_pkg_path = std::string(temp_folder) + "/" + std::to_string(tick.mytick) + ".pkg";
SplitFile *sp = new SplitFile(install_pkg_path, INSTALL_ARCHIVE_PKG_SPLIT_SIZE/2);
install_data->split_file = sp;
install_data->remote_client = baseclient;
install_data->path = path;
baseclient->Size(path, &install_data->size);
install_data->stop_write_thread = false;
install_data->delete_client = true;
int ret = pthread_create(&install_data->thread, NULL, Actions::DownloadSplitPkg, install_data);
ret = INSTALLER::InstallSplitPkg(download_url, install_data, true);
if (ret == 0)
{
failed(res, 200, lang_strings[STR_FAIL_INSTALL_FROM_URL_MSG]);
activity_inprogess = false;
file_transfering = false;
Windows::SetModalMode(false);
return;
}
}
}
else
{
ArchiveEntry *entry = ZipUtil::GetPackageEntry(path, baseclient);
if (entry != nullptr)
{
ArchivePkgInstallData *install_data = (ArchivePkgInstallData*) malloc(sizeof(ArchivePkgInstallData));
memset(install_data, 0, sizeof(ArchivePkgInstallData));
std::string install_pkg_path = std::string(temp_folder) + "/" + entry->filename;
SplitFile *sp = new SplitFile(install_pkg_path, INSTALL_ARCHIVE_PKG_SPLIT_SIZE);
install_data->archive_entry = entry;
install_data->split_file = sp;
install_data->stop_write_thread = false;
int ret = pthread_create(&install_data->thread, NULL, Actions::ExtractArchivePkg, install_data);
ret = INSTALLER::InstallArchivePkg(entry->filename, install_data, true);
if (ret == 0)
{
failed(res, 200, lang_strings[STR_FAIL_INSTALL_FROM_URL_MSG]);
activity_inprogess = false;
file_transfering = false;
free(install_data);
Windows::SetModalMode(false);
return;
}
}
else
{
failed(res, 200, lang_strings[STR_FAIL_INSTALL_FROM_URL_MSG]);
activity_inprogess = false;
file_transfering = false;
Windows::SetModalMode(false);
return;
}
}
success(res);
});
svr->Post("/__local__/download_url", [&](const Request &req, Response &res)
{
std::string url;
std::string dest;
const char *url_param;
const char *dest_param;
bool use_alldebrid = false;
bool use_realdebrid = false;
json_object *jobj = json_tokener_parse(req.body.c_str());
if (jobj != nullptr)
{
url_param = json_object_get_string(json_object_object_get(jobj, "url"));
dest_param = json_object_get_string(json_object_object_get(jobj, "dest"));
use_alldebrid = json_object_get_boolean(json_object_object_get(jobj, "use_alldebrid"));
use_realdebrid = json_object_get_boolean(json_object_object_get(jobj, "use_realdebrid"));
if (url_param == nullptr || dest_param == nullptr)
{
bad_request(res, "Required url, dest parameter missing");
return;
}
}
else
{
bad_request(res, "Invalid payload");
return;
}
if ((use_alldebrid && strlen(alldebrid_api_key) == 0) || (use_realdebrid && strlen(realdebrid_api_key) == 0))
{
failed(res, 200, lang_strings[STR_ALLDEBRID_API_KEY_MISSING_MSG]);
return;
}
url = std::string(url_param);
FileHost *filehost = FileHost::getFileHost(url, use_alldebrid, use_realdebrid);
if (!filehost->IsValidUrl())
{
failed(res, 200, lang_strings[STR_INVALID_URL]);
return;
}
std::string download_url = filehost->GetDownloadUrl();
if (download_url.empty())
{
failed(res, 200, lang_strings[STR_CANT_EXTRACT_URL_MSG]);
return;
}
delete(filehost);
size_t scheme_pos = download_url.find("://");
size_t root_pos = download_url.find("/", scheme_pos + 3);
std::string host = download_url.substr(0, root_pos);
std::string path = download_url.substr(root_pos);
int64_t file_size;
RemoteClient *baseclient = new BaseClient();
baseclient->Connect(host, "", "");
baseclient->Size(path, &file_size);
delete baseclient;
OrbisTick tick;
sceRtcGetCurrentTick(&tick);
json_object *params = json_object_new_object();
json_object_object_add(params, "type", json_object_new_int(CLIENT_TYPE_FILEHOST));
json_object_object_add(params, "url", json_object_new_string(host.c_str()));
json_object_object_add(params, "username", json_object_new_string(""));
json_object_object_add(params, "password", json_object_new_string(""));
json_object_object_add(params, "src_path", json_object_new_string(path.c_str()));
json_object_object_add(params, "dest_path", json_object_new_string(dest_param));
json_object_object_add(params, "size", json_object_new_uint64(file_size));
json_object_object_add(params, "id", json_object_new_uint64(tick.mytick));
const char *params_str = json_object_to_json_string(params);
httplib::Client tmp_client = httplib::Client(std::string("http://127.0.0.1:") + std::to_string(http_int_server_port));
std::string download_req_url = + "/download_url";
if (auto resp = tmp_client.Post(download_req_url, params_str, strlen(params_str), "application/json"))
{
if (HTTP_SUCCESS(resp->status))
{
Util::Notify("%s queued for download", path.c_str());
success(res);
return;
}
else
{
Util::Notify("Failed to queue %s for download in background", path.c_str());
failed(res, 200, "Failed to download");
return;
}
}
failed(res, 200, "Failed to download");
});
svr->Get("/stop", [&](const Request & /*req*/, Response & /*res*/)
@@ -1201,7 +1604,7 @@ namespace HttpServer
svr->set_payload_max_length(1024 * 1024 * 12);
svr->set_tcp_nodelay(true);
svr->set_mount_point("/", "/");
if (web_server_enabled)
svr->listen("0.0.0.0", http_server_port);
else
+4 -3
View File
@@ -1,5 +1,5 @@
#ifndef HTTP_SERVER_H
#define HTTP_SERVER_H
#ifndef EZ_HTTP_SERVER_H
#define EZ_HTTP_SERVER_H
#include "http/httplib.h"
@@ -8,6 +8,7 @@ extern Server *svr;
static pthread_t http_server_thid;
extern int http_server_port;
extern int http_int_server_port;
extern char compressed_file_path[];
extern bool web_server_enabled;
@@ -18,4 +19,4 @@ namespace HttpServer
void Stop();
}
#endif
#endif
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef LAUNCHER_SFO_H
#define LAUNCHER_SFO_H
#ifndef EZ_SFO_H
#define EZ_SFO_H
#pragma once
+63 -16
View File
@@ -24,7 +24,7 @@ SplitFile::~SplitFile()
fclose(this->file_blocks[i]->fd);
}
remove(this->file_blocks[i]->block_file.c_str());
free(this->file_blocks[i]);
delete this->file_blocks[i];
}
}
sem_destroy(&this->block_ready);
@@ -33,8 +33,6 @@ SplitFile::~SplitFile()
int SplitFile::Open()
{
this->block_in_progress = NewBlock();
this->block_in_progress->fd = fopen(block_in_progress->block_file.c_str(), "w");
return (block_in_progress->fd == nullptr);
}
@@ -56,9 +54,16 @@ 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);
}
// If complete and block_num is past the end, the requested offset is beyond EOF
if (block_num >= this->file_blocks.size())
return 0;
block = this->file_blocks[block_num];
if (block->status == BLOCK_STATUS_DELETED)
{
@@ -118,19 +123,26 @@ size_t SplitFile::Read(char *buf, size_t buf_size, size_t offset)
block_offset = 0;
while ((block_num > this->file_blocks.size() - 1 && !this->complete) ||
this->file_blocks[block_num]->status == BLOCK_STATUS_NOT_EXISTS)
(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);
}
// If complete and block_num is past the end, no more data
if (block_num >= this->file_blocks.size())
break;
block = this->file_blocks[block_num];
}
// 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)
if (this->file_blocks[j] != nullptr && this->file_blocks[j]->status == BLOCK_STATUS_CREATED)
{
if (this->file_blocks[j]->fd != nullptr)
{
@@ -139,23 +151,29 @@ size_t SplitFile::Read(char *buf, size_t buf_size, size_t offset)
}
this->file_blocks[j]->status = BLOCK_STATUS_DELETED;
remove(this->file_blocks[j]->block_file.c_str());
delete (this->file_blocks[j]);
this->file_blocks[j] = nullptr;
}
}
this->read_offset = offset + total_bytes_read;
return total_bytes_read;
}
size_t SplitFile::Write(char *buf, size_t buf_size)
ssize_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;
char *p = buf;
size_t total_bytes_written = 0;
ssize_t total_bytes_written = 0;
size_t remaining_to_write = buf_size;
while (remaining_to_write > 0)
if (this->IsClosed())
return -1;
while (remaining_to_write > 0 && !this->complete)
{
block_space_remaining = this->block_size - block_in_progress->size;
bytes_to_write = MIN(remaining_to_write, block_space_remaining);
@@ -186,12 +204,18 @@ size_t SplitFile::Write(char *buf, size_t buf_size)
block_in_progress = NewBlock();
}
}
this->write_offset += total_bytes_written;
return total_bytes_written;
}
int SplitFile::Close()
{
if (this->complete)
return 0;
this->complete = true;
if (block_in_progress->fd != nullptr)
{
fflush(block_in_progress->fd);
@@ -201,16 +225,39 @@ 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;
}
bool SplitFile::IsClosed()
{
return this->complete;
}
FileBlock *SplitFile::NewBlock()
{
FileBlock *block = (FileBlock *)malloc(sizeof(FileBlock));
memset(block, 0, sizeof(FileBlock));
FileBlock *block = new FileBlock{};
block->is_last = false;
block->size = 0;
@@ -218,4 +265,4 @@ FileBlock *SplitFile::NewBlock()
block->fd = fopen(block->block_file.c_str(), "w");
return block;
}
}
+8 -5
View File
@@ -1,9 +1,10 @@
#ifndef SPLIT_FILE_H
#define SPLIT_FILE_H
#ifndef EZ_SPLIT_FILE_H
#define EZ_SPLIT_FILE_H
#include <string>
#include <vector>
#include <mutex>
#include <semaphore.h>
#include <pthread.h>
enum FileBlockStatus
@@ -28,14 +29,16 @@ public:
SplitFile(const std::string& path, size_t block_size);
~SplitFile();
size_t Read(char* buf, size_t buf_size, size_t offset);
size_t Write(char* buf, size_t buf_size);
ssize_t Write(char* buf, size_t buf_size);
int Open();
int Close();
bool IsClosed();
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;
@@ -45,4 +48,4 @@ private:
FileBlock *NewBlock();
};
#endif
#endif
+5
View File
@@ -1,3 +1,6 @@
#ifndef EZ_SYSTEM_H
#define EZ_SYSTEM_H
#include <stdint.h>
#ifdef __cplusplus
@@ -42,3 +45,5 @@ void convertLocalTimeToUtc(const OrbisDateTime *local_time, OrbisDateTime *utc);
#ifdef __cplusplus
}
#endif
#endif
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef LAUNCHER_TEXTURES_H
#define LAUNCHER_TEXTURES_H
#ifndef EZ_TEXTURES_H
#define EZ_TEXTURES_H
#include <string>
#include <SDL2/SDL.h>
+7
View File
@@ -88,6 +88,13 @@ namespace Util
return out;
}
static uint64_t GetTick()
{
static struct timeval tick;
gettimeofday(&tick, NULL);
return tick.tv_sec * 1000000 + tick.tv_usec;
}
static inline void Notify(const char *fmt, ...)
{
OrbisNotificationRequest request;
-120
View File
@@ -1,120 +0,0 @@
/*#***************************************************************************
# __ __ _____ _____
# Project | | | | | \ / ___|
# | |__| | | |\ \ / /
# | | | | ) ) ( (
# | /\ | | |/ / \ \___
# \_/ \_/ |_____/ \_____|
#
# Copyright (C) 2018, The WDC Project, <rusdevops@gmail.com>, et al.
#
# This software is licensed as described in the file LICENSE, which
# you should have received as part of this distribution.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the LICENSE file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
############################################################################*/
#include <algorithm>
#include <fstream>
#include <cstring>
#include "http/httplib.h"
#include "callback.hpp"
using namespace httplib;
namespace Web
{
namespace Callback
{
namespace Read
{
size_t stream(char* ptr, size_t item_size, size_t item_count, void* stream)
{
auto in_stream = reinterpret_cast<std::istream*>(stream);
auto read_bytes = static_cast<unsigned long long>(item_size * item_count);
auto position = static_cast<unsigned long long>(in_stream->tellg());
in_stream->seekg(0, std::ios::end);
auto size = static_cast<unsigned long long>(in_stream->tellg());
in_stream->seekg(position, std::ios::beg);
auto rest_bytes = size - position;
read_bytes = std::min<unsigned long long>(read_bytes, rest_bytes);
in_stream->read(ptr, read_bytes);
return read_bytes;
}
size_t buffer(char* ptr, size_t item_size, size_t item_count, void* buffer)
{
auto data = (Data*)buffer;
auto size = static_cast<unsigned long long>(item_size * item_count);
auto rest_bytes = data->size - data->position;
auto copied_bytes = std::min<unsigned long long>(size, rest_bytes);
memcpy(ptr, data->buffer, copied_bytes);
data->position += copied_bytes;
return copied_bytes;
}
} // namespace Read
namespace Write
{
size_t stream(char* ptr, size_t item_size, size_t item_count, void* stream)
{
auto out_stream = reinterpret_cast<std::ostream*>(stream);
size_t write_bytes = item_size * item_count;
out_stream->write(ptr, write_bytes);
return write_bytes;
}
size_t buffer(char* ptr, size_t item_size, size_t item_count, void* buffer)
{
auto data = reinterpret_cast<Data*>(buffer);
auto size = static_cast<unsigned long long>(item_size * item_count);
auto rest_bytes = data->size - data->position;
auto copied_bytes = std::min<unsigned long long>(size, rest_bytes);
memcpy(data->buffer, ptr, copied_bytes);
data->position += copied_bytes;
return copied_bytes;
}
} // namespace Write
namespace Append
{
size_t buffer(char* ptr, size_t item_size, size_t item_count, void* buffer)
{
auto data = reinterpret_cast<Data*>(buffer);
auto append_size = item_size * item_count;
auto new_buffer_size = data->size + append_size;
auto new_buffer = new char[new_buffer_size];
if (data->size != 0) memcpy(new_buffer, data->buffer, data->size);
memcpy(new_buffer + data->size, ptr, append_size);
delete[] data->buffer;
data->buffer = new_buffer;
data->size = new_buffer_size;
return append_size;
}
size_t stream(char* ptr, size_t item_size, size_t item_count, void* stream)
{
auto out_stream = reinterpret_cast<std::ostream*>(stream);
size_t write_bytes = item_size * item_count;
out_stream->seekp(0, std::ios::end);
out_stream->write(ptr, write_bytes);
return write_bytes;
}
size_t stream2sink(char* ptr, size_t item_size, size_t item_count, void* sink)
{
auto ostream = reinterpret_cast<DataSink*>(sink);
size_t write_bytes = item_size * item_count;
ostream->write(ptr, write_bytes);
return write_bytes;
}
} // namespace Append
} // namespace Callback
} // namespace Web
-68
View File
@@ -1,68 +0,0 @@
/*#***************************************************************************
# __ __ _____ _____
# Project | | | | | \ / ___|
# | |__| | | |\ \ / /
# | | | | ) ) ( (
# | /\ | | |/ / \ \___
# \_/ \_/ |_____/ \_____|
#
# Copyright (C) 2018, The WDC Project, <rusdevops@gmail.com>, et al.
#
# This software is licensed as described in the file LICENSE, which
# you should have received as part of this distribution.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the LICENSE file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
############################################################################*/
#ifndef WEB_CALLBACK_HPP
#define WEB_CALLBACK_HPP
namespace Web
{
struct Data
{
char* buffer;
unsigned long long position;
unsigned long long size;
void reset()
{
buffer = nullptr;
position = 0;
size = 0;
}
~Data()
{
delete[] buffer;
}
};
namespace Callback
{
namespace Read
{
size_t stream(char* data, size_t size, size_t count, void* stream);
size_t buffer(char* data, size_t size, size_t count, void* buffer);
}
namespace Write
{
size_t stream(char* data, size_t size, size_t count, void* stream);
size_t buffer(char* data, size_t size, size_t count, void* buffer);
}
namespace Append
{
size_t stream(char* data, size_t size, size_t count, void* stream);
size_t buffer(char* data, size_t size, size_t count, void* buffer);
size_t stream2sink(char* ptr, size_t item_size, size_t item_count, void* sink);
}
}
} // namespace Web
#endif
-42
View File
@@ -1,42 +0,0 @@
/*#***************************************************************************
# __ __ _____ _____
# Project | | | | | \ / ___|
# | |__| | | |\ \ / /
# | | | | ) ) ( (
# | /\ | | |/ / \ \___
# \_/ \_/ |_____/ \_____|
#
# Copyright (C) 2018, The WDC Project, <rusdevops@gmail.com>, et al.
#
# This software is licensed as described in the file LICENSE, which
# you should have received as part of this distribution.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the LICENSE file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
############################################################################*/
#include "fsinfo.hpp"
#include <fstream>
namespace Web
{
namespace FileInfo
{
auto exists(const std::string& path) -> bool
{
std::ifstream file(path);
return file.good();
}
auto size(const std::string& path_file) -> unsigned long long
{
std::ifstream file(path_file, std::ios::binary | std::ios::ate);
return static_cast<unsigned long long>(file.tellg());
}
} // namespace FileInfo
} // namespace Web
-38
View File
@@ -1,38 +0,0 @@
/*#***************************************************************************
# __ __ _____ _____
# Project | | | | | \ / ___|
# | |__| | | |\ \ / /
# | | | | ) ) ( (
# | /\ | | |/ / \ \___
# \_/ \_/ |_____/ \_____|
#
# Copyright (C) 2018, The WDC Project, <rusdevops@gmail.com>, et al.
#
# This software is licensed as described in the file LICENSE, which
# you should have received as part of this distribution.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the LICENSE file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
############################################################################*/
#ifndef WEB_FSINFO_HPP
#define WEB_FSINFO_HPP
#include <fstream>
#include <string>
namespace Web
{
namespace FileInfo
{
auto exists(const std::string& path) -> bool;
auto size(const std::string& path_file) -> unsigned long long;
} // namespace FileInfo
} // namespace Web
#endif
-69
View File
@@ -1,69 +0,0 @@
/*#***************************************************************************
# __ __ _____ _____
# Project | | | | | \ / ___|
# | |__| | | |\ \ / /
# | | | | ) ) ( (
# | /\ | | |/ / \ \___
# \_/ \_/ |_____/ \_____|
#
# Copyright (C) 2018, The WDC Project, <rusdevops@gmail.com>, et al.
#
# This software is licensed as described in the file LICENSE, which
# you should have received as part of this distribution.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the LICENSE file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
############################################################################*/
#include "header.hpp"
#include <curl/curl.h>
namespace Web
{
Header::Header(const std::initializer_list<std::string>& init_list) noexcept : handle(nullptr)
{
for (auto& item : init_list)
{
this->append(item);
}
}
Header::~Header() noexcept
{
curl_slist_free_all(reinterpret_cast<curl_slist*>(this->handle));
}
Header::Header(Header&& other) noexcept
{
handle = other.handle;
other.handle = nullptr;
}
auto Header::operator=(Header&& other) noexcept -> Header&
{
if (this != &other)
{
Header(std::move(other)).swap(*this);
}
return *this;
}
auto Header::swap(Header& other) noexcept -> void
{
using std::swap;
swap(handle, other.handle);
}
void
Header::append(const std::string& item) noexcept
{
this->handle = curl_slist_append(reinterpret_cast<curl_slist*>(this->handle), item.c_str());
}
} // namespace Web
-51
View File
@@ -1,51 +0,0 @@
/*#***************************************************************************
# __ __ _____ _____
# Project | | | | | \ / ___|
# | |__| | | |\ \ / /
# | | | | ) ) ( (
# | /\ | | |/ / \ \___
# \_/ \_/ |_____/ \_____|
#
# Copyright (C) 2018, The WDC Project, <rusdevops@gmail.com>, et al.
#
# This software is licensed as described in the file LICENSE, which
# you should have received as part of this distribution.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the LICENSE file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
############################################################################*/
#ifndef WEB_HEADER_HPP
#define WEB_HEADER_HPP
#include <initializer_list>
#include <string>
namespace Web
{
class Header final
{
public:
void* handle;
Header(const std::initializer_list<std::string>& init_list) noexcept;
Header(const Header& other) = delete;
Header(Header&& other) noexcept;
~Header() noexcept;
auto operator=(const Header& other) -> Header& = delete;
auto operator=(Header&& other) noexcept -> Header&;
void append(const std::string& item) noexcept;
private:
auto swap(Header& other) noexcept -> void;
};
} // namespace Web
#endif
-227
View File
@@ -1,227 +0,0 @@
/*#***************************************************************************
# __ __ _____ _____
# Project | | | | | \ / ___|
# | |__| | | |\ \ / /
# | | | | ) ) ( (
# | /\ | | |/ / \ \___
# \_/ \_/ |_____/ \_____|
#
# Copyright (C) 2018, The WDC Project, <rusdevops@gmail.com>, et al.
#
# This software is licensed as described in the file LICENSE, which
# you should have received as part of this distribution.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the LICENSE file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
############################################################################*/
#include "request.hpp"
#include "fsinfo.hpp"
#include "util.h"
namespace Web
{
static int sockopt_callback(void *clientp, curl_socket_t curlfd, curlsocktype purpose)
{
int const size = 1048576;
if (setsockopt(curlfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) == -1)
{
return CURL_SOCKOPT_ERROR;
}
if (setsockopt(curlfd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) == -1)
{
return CURL_SOCKOPT_ERROR;
}
return CURL_SOCKOPT_OK;
}
auto inline get(const dict_t &options, const std::string &&name) -> std::string
{
auto it = options.find(name);
if (it == options.end())
{
return std::string{""};
}
else
{
return it->second;
}
}
Request::Request(dict_t &&options_) : options(options_)
{
auto hostname = get(options, "hostname");
auto username = get(options, "username");
auto password = get(options, "password");
auto timeout = get(options, "password");
auto proxy_hostname = get(options, "proxy_hostname");
auto proxy_username = get(options, "proxy_username");
auto proxy_password = get(options, "proxy_password");
auto cert_path = get(options, "cert_path");
auto key_path = get(options, "key_path");
this->handle = curl_easy_init();
this->set(CURLOPT_SSL_VERIFYHOST, 0);
this->set(CURLOPT_SSL_VERIFYPEER, 0);
#ifdef _DEBUG
this->set(CURLOPT_VERBOSE, 1);
#else
this->set(CURLOPT_VERBOSE, 0);
#endif
if (this->cert_required())
{
this->set(CURLOPT_SSLCERTTYPE, "PEM");
this->set(CURLOPT_SSLKEYTYPE, "PEM");
this->set(CURLOPT_SSLCERT, const_cast<char *>(cert_path.c_str()));
this->set(CURLOPT_SSLKEY, const_cast<char *>(key_path.c_str()));
}
this->set(CURLOPT_URL, const_cast<char *>(hostname.c_str()));
if (!username.empty())
{
this->set(CURLOPT_HTTPAUTH, static_cast<int>(CURLAUTH_BASIC));
auto token = username + ":" + password;
this->set(CURLOPT_USERPWD, const_cast<char *>(token.c_str()));
}
this->set(CURLOPT_SOCKOPTFUNCTION, sockopt_callback);
this->set(CURLOPT_FOLLOWLOCATION, 1);
this->set(CURLOPT_COOKIEJAR, "/data/ps4-webdav-client/cookies.txt");
this->set(CURLOPT_COOKIEFILE, "/data/ps4-webdav-client/cookies.txt");
if (timeout.empty())
{
this->set(CURLOPT_CONNECTTIMEOUT, 15L);
}
else
{
this->set(CURLOPT_CONNECTTIMEOUT, atoi(timeout.c_str()));
}
if (!this->proxy_enabled())
return;
this->set(CURLOPT_PROXY, const_cast<char *>(proxy_hostname.c_str()));
this->set(CURLOPT_PROXYAUTH, static_cast<int>(CURLAUTH_BASIC));
if (proxy_username.empty())
return;
if (proxy_password.empty())
{
this->set(CURLOPT_PROXYUSERNAME, const_cast<char *>(proxy_username.c_str()));
}
else
{
if (!username.empty() && !password.empty())
{
auto token = proxy_username + ":" + proxy_password;
this->set(CURLOPT_PROXYUSERPWD, const_cast<char *>(token.c_str()));
}
}
}
Request::~Request() noexcept
{
if (this->handle != nullptr)
curl_easy_cleanup(this->handle);
}
auto Request::swap(Request &other) noexcept -> void
{
using std::swap;
swap(handle, other.handle);
}
Request::Request(Request &&other) noexcept : handle{
other.handle}
{
other.handle = nullptr;
}
auto Request::operator=(Request &&other) noexcept -> Request &
{
if (this != &other)
{
Request(std::move(other)).swap(*this);
}
return *this;
}
bool Request::perform() noexcept
{
if (this->handle == nullptr)
return false;
this->res = curl_easy_perform(this->handle);
auto is_performed = this->res == CURLE_OK;
if (!is_performed)
return false;
this->http_code = 0;
curl_easy_getinfo(this->handle, CURLINFO_RESPONSE_CODE, &this->http_code);
if (this->http_code < 200 || this->http_code > 299)
return false;
return true;
}
bool Request::proxy_enabled() const noexcept
{
auto proxy_hostname = get(options, "proxy_hostname");
auto proxy_username = get(options, "proxy_username");
auto proxy_password = get(options, "proxy_password");
bool proxy_hostname_presented = !proxy_hostname.empty();
if (!proxy_hostname_presented)
return false;
bool proxy_username_presented = !proxy_username.empty();
bool proxy_password_presented = !proxy_password.empty();
if (proxy_password_presented && !proxy_username_presented)
return false;
return true;
}
bool Request::cert_required() const noexcept
{
const auto cert_path = get(options, "cert_path");
const auto key_path = get(options, "key_path");
if (cert_path.empty())
return false;
bool cert_is_existed = FileInfo::exists(cert_path);
if (!cert_is_existed)
return false;
if (key_path.empty())
return false;
return FileInfo::exists(key_path);
}
long Request::status_code() const noexcept
{
return this->http_code;
}
int Request::result() const noexcept
{
return this->res;
}
class Environment
{
public:
Environment()
{
curl_global_init(CURL_GLOBAL_ALL);
}
~Environment()
{
curl_global_cleanup();
}
};
} // namespace Web
static const Web::Environment env;
-74
View File
@@ -1,74 +0,0 @@
/*#***************************************************************************
# __ __ _____ _____
# Project | | | | | \ / ___|
# | |__| | | |\ \ / /
# | | | | ) ) ( (
# | /\ | | |/ / \ \___
# \_/ \_/ |_____/ \_____|
#
# Copyright (C) 2018, The WDC Project, <rusdevops@gmail.com>, et al.
#
# This software is licensed as described in the file LICENSE, which
# you should have received as part of this distribution.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the LICENSE file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
############################################################################*/
#ifndef WEB_REQUEST_HPP
#define WEB_REQUEST_HPP
#include <curl/curl.h>
#include <map>
#include <string>
namespace Web
{
bool inline check_code(CURLcode code)
{
return code == CURLE_OK;
}
using dict_t = std::map<std::string, std::string>;
class Request
{
public:
explicit Request(dict_t &&options_);
Request(const Request &other) = delete;
Request(Request &&other) noexcept;
~Request() noexcept;
auto operator=(const Request &other) -> Request & = delete;
auto operator=(Request &&other) noexcept -> Request &;
template <typename T>
auto set(CURLoption option, T value) const noexcept -> bool
{
if (this->handle == nullptr)
return false;
return check_code(curl_easy_setopt(this->handle, option, value));
}
bool perform() noexcept;
long status_code() const noexcept;
int result() const noexcept;
void *handle;
private:
const dict_t options;
dict_t response_header = {};
long http_code;
int res;
bool proxy_enabled() const noexcept;
bool cert_required() const noexcept;
auto swap(Request &other) noexcept -> void;
};
} // namespace Web
#endif
-262
View File
@@ -1,262 +0,0 @@
/*#***************************************************************************
# __ __ _____ _____
# Project | | | | | \ / ___|
# | |__| | | |\ \ / /
# | | | | ) ) ( (
# | /\ | | |/ / \ \___
# \_/ \_/ |_____/ \_____|
#
# Copyright (C) 2018, The WDC Project, <rusdevops@gmail.com>, et al.
#
# This software is licensed as described in the file LICENSE, which
# you should have received as part of this distribution.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the LICENSE file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
############################################################################*/
#include <curl/curl.h>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
using std::string;
using std::vector;
#include "urn.hpp"
namespace Web
{
namespace Urn
{
const string Path::separate = "/";
const string Path::root = "/";
const string Path::param_separate = "&";
const string Path::query_separate = "?";
string encodeUrl(const string &url, void *request)
{
size_t scheme_pos = url.find("://");
size_t root_pos = url.find("/", scheme_pos+3);
if (root_pos == string::npos)
return url;
string uri = url.substr(root_pos);
auto path = Path(uri);
return url.substr(0, root_pos) + path.quote(request);
}
Path::Path(const string& path_, bool force_dir)
{
string path = path_;
if (path_.empty()) path = Path::root;
auto first_position = path.find(Path::separate);
if (first_position != 0) path = Path::root + path;
auto last_symbol_index = path.length() - 1;
auto last_symbol = path.substr(last_symbol_index, 1);
auto is_dir = last_symbol == Path::separate;
if (force_dir && !is_dir) path += Path::separate;
m_path = path;
auto double_separte = Path::separate + Path::separate;
bool is_find = false;
do
{
auto first_position = m_path.find(double_separte);
is_find = first_position != m_path.npos;
if (is_find)
{
m_path.replace(first_position, double_separte.size(), Path::separate);
}
}
while (is_find);
}
Path::Path(std::nullptr_t)
{
m_path = nullptr;
}
auto Path::path() const -> string
{
return m_path;
}
auto escape(void* request, const string& name) -> string
{
string path = curl_easy_escape(request, name.c_str(), static_cast<int>(name.length()));
return path;
}
auto split(const string& text, const string& delims) -> vector<string>
{
vector<string> tokens;
auto start = text.find_first_not_of(delims);
auto end = text.npos;
while ((end = text.find_first_of(delims, start)) != text.npos)
{
tokens.push_back(text.substr(start, end - start));
start = text.find_first_not_of(delims, end);
}
if (start != text.npos)
{
tokens.push_back(text.substr(start));
}
return tokens;
}
auto Path::quote(void* request) const -> string
{
if (this->is_root()) return m_path;
size_t query_pos = m_path.find_first_of(Path::query_separate);
string query;
string path;
if (query_pos != string::npos)
{
query = m_path.substr(query_pos+1);
path = m_path.substr(0, query_pos);
}
else
{
path = m_path;
}
auto names = split(path, Path::separate);
string quote_path;
std::for_each(names.begin(), names.end(), [&quote_path, request](string & name)
{
auto escape_name = escape(request, name);
quote_path.append(Path::separate);
quote_path.append(escape_name);
});
if (is_directory())
{
quote_path.append(Path::separate);
}
if (query.length()>0)
{
auto params = split(query, Path::param_separate);
if (params.size() > 0)
quote_path.append(Path::query_separate);
std::for_each(params.begin(), params.end(), [&quote_path, request](string & param)
{
auto param_pair = split(param, "=");
if (param_pair.size() == 0)
{
quote_path.append(Path::param_separate);
}
else
{
quote_path.append(escape(request, param_pair[0]));
quote_path.append("=");
if (param_pair.size() > 1)
{
quote_path.append(escape(request, param_pair[1]));
}
quote_path.append(Path::param_separate);
}
});
quote_path.pop_back();
}
return quote_path;
}
auto Path::name() const -> string
{
auto path = this->path();
auto is_root = path == Path::separate;
if (is_root) return string{""};
if (this->is_directory())
{
auto path_without_slash = path.substr(0, path.length() - 1);
auto pre_last_separate_position = path_without_slash.rfind(Path::separate);
auto name = path.substr(pre_last_separate_position + 1);
return name;
}
else
{
auto last_separate_position = path.rfind(Path::separate);
auto name = path.substr(last_separate_position + 1);
return name;
}
}
auto Path::parent() const -> Path
{
if (this->is_root()) return Path{m_path};
auto last_separate_position = m_path.rfind(Path::separate, m_path.length() - 2);
if (last_separate_position == 0) return Path{Path::separate};
auto parent = m_path.substr(0, last_separate_position + 1);
return Path{parent};
}
auto Path::is_directory() const -> bool
{
auto path = this->path();
auto last_symbol_index = path.length() - 1;
auto last_symbol = path.substr(last_symbol_index, 1);
auto is_equal = last_symbol == Path::separate;
return is_equal;
}
auto Path::is_root() const -> bool
{
return m_path == Path::separate;
}
auto Path::operator+(const string& rhs) const -> Path
{
return Path{ m_path + rhs };
}
auto Path::operator==(const Path& rhs) const -> bool
{
if (this->is_root() && rhs.is_root()) return true;
if (!this->is_root() && rhs.is_root()) return false;
string lhs_path;
bool is_dir = is_directory();
if (is_dir)
{
lhs_path = m_path.substr(0, m_path.length() - 1);
}
else
{
lhs_path = m_path;
}
string rhs_path;
if (rhs.is_directory())
{
rhs_path = rhs.path();
rhs_path = rhs_path.substr(0, rhs_path.length() - 1);
}
else
{
rhs_path = rhs.path();
}
return lhs_path == rhs_path;
}
} // namespace Urn
} // namespace Web
auto operator<<(std::ostream& stream, const Web::Urn::Path& path) -> std::ostream&
{
return stream << path.path();
}

Some files were not shown because too many files have changed in this diff Show More