Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 28c046d5a5 | |||
| 99099a6b82 | |||
| c30a8ab70e | |||
| 046083ccf6 | |||
| 6d36073413 | |||
| b831dd7db4 | |||
| 3ba286a016 | |||
| 7395056a75 | |||
| 81a5143f2f | |||
| 432c0020be | |||
| fd1b9e5d96 | |||
| a3741a0a6c | |||
| ce71f5af9a | |||
| cd42afcb6b | |||
| b6eaef82fd | |||
| 9d0790fccc | |||
| bfe2505efc | |||
| 749a79d8b9 | |||
| c03201c040 | |||
| 7be30e358a | |||
| d58fc0eac2 | |||
| ed3f8a71c4 | |||
| 13279b6040 | |||
| eb465356b6 | |||
| 8602a5353e | |||
| 467a459665 | |||
| b7fe46cb94 | |||
| 3bcf136d2a | |||
| e5d5ddb8bc | |||
| 3e4b5e3ea2 | |||
| c4889ec160 | |||
| 3076f4b179 | |||
| 93fb338ce0 | |||
| 785a073bce | |||
| a71bd1eb28 | |||
| 0a1420dd96 | |||
| dcdc1fc54b | |||
| 5c254589e6 | |||
| 2b25272379 | |||
| 6b66d39eba | |||
| b143c3e3c5 | |||
| c3cc6d5954 | |||
| cfcb420b9d | |||
| 294455c735 | |||
| 4ff119b34d | |||
| 114c1974e1 | |||
| a99bf163d8 | |||
| b9ab71577f | |||
| f80b3112c6 | |||
| e0c72c85e5 | |||
| 01f568feac | |||
| 2046a0e096 | |||
| 49f535dfde | |||
| 5a250a4182 | |||
| 89fa9aba69 | |||
| 0feff205d2 | |||
| 5df5447eb0 | |||
| b87dc91c6f | |||
| 14b32504e0 | |||
| d35345a270 | |||
| f041172768 | |||
| 232a6233ca | |||
| c620478691 | |||
| 2615372288 | |||
| 0889b0923d | |||
| a2b26102de | |||
| dff469a9d7 | |||
| 37074f0102 | |||
| eb4184d488 | |||
| c328b26480 | |||
| 1e702aa0e2 | |||
| d35b311946 | |||
| f02944e596 | |||
| 338eacfd3f | |||
| 0bd0a0f273 | |||
| bf3f4330c0 | |||
| f16850fed9 | |||
| a30a20f6db | |||
| 54b9a80410 | |||
| fe55ddeb8a | |||
| a5a5f8d611 | |||
| 9854437c94 |
@@ -11,3 +11,4 @@ CTestTestfile.cmake
|
||||
_deps
|
||||
.vscode
|
||||
build
|
||||
data/daemon
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "ps4-ezremote-server"]
|
||||
path = ps4-ezremote-server
|
||||
url = git@github.com:cy33hc/ps4-ezremote-server.git
|
||||
+19
-13
@@ -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.15" 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
|
||||
)
|
||||
@@ -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
|
||||
| | | |
|
||||
|----------|-----------|---|
|
||||
|  | |  |
|
||||
|
||||
- For Myrient repos, entry **https://myrient.erista.me/files** in the server field.
|
||||

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

|
||||
|
||||
Tested with following WebDAV server:
|
||||
- **(Recommeded)** [Dufs](https://github.com/sigoden/dufs) - For hosting your own WebDAV server. (Recommended since this allow anonymous access which is required for Remote Package Install)
|
||||
- [SFTPgo](https://github.com/drakkan/sftpgo) - For local hosted WebDAV server. Can also be used as a webdav frontend for Cloud Storage like AWS S3, Azure Blob or Google Storage.
|
||||
@@ -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 |
@@ -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>
|
||||
|
||||
@@ -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=حفظ
|
||||
|
||||
@@ -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
|
||||
@@ -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=이미지 보기
|
||||
|
||||
@@ -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
|
||||
@@ -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=查看图片
|
||||
@@ -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 iş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
|
||||
|
||||
@@ -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
|
||||
+1
-1
File diff suppressed because one or more lines are too long
BIN
Binary file not shown.
|
After Width: | Height: | Size: 165 KiB |
Submodule
+1
Submodule ps4-ezremote-server added at 7644256a3c
+251
-62
@@ -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,20 +725,51 @@ 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, remoteclient);
|
||||
@@ -731,6 +777,8 @@ namespace Actions
|
||||
{
|
||||
while (entry != nullptr)
|
||||
{
|
||||
snprintf(activity_message, 1023, "%s %s", lang_strings[STR_INSTALLING], entry->filename.c_str());
|
||||
|
||||
ArchivePkgInstallData *install_data = (ArchivePkgInstallData*) malloc(sizeof(ArchivePkgInstallData));
|
||||
memset(install_data, 0, sizeof(ArchivePkgInstallData));
|
||||
|
||||
@@ -778,6 +826,7 @@ namespace Actions
|
||||
if (res != 0)
|
||||
{
|
||||
activity_inprogess = false;
|
||||
file_transfering = false;
|
||||
multi_selected_remote_files.clear();
|
||||
Windows::SetModalMode(false);
|
||||
}
|
||||
@@ -818,6 +867,18 @@ namespace Actions
|
||||
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;
|
||||
}
|
||||
|
||||
void *InstallLocalPkgsThread(void *argp)
|
||||
{
|
||||
int failed = 0;
|
||||
@@ -1065,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];
|
||||
@@ -1075,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]);
|
||||
@@ -1101,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)
|
||||
{
|
||||
@@ -1177,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;
|
||||
@@ -1209,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)
|
||||
{
|
||||
@@ -1343,6 +1466,7 @@ namespace Actions
|
||||
|
||||
if (confirm_state == CONFIRM_YES)
|
||||
{
|
||||
sceRtcGetCurrentTick(&prev_tick);
|
||||
if (isCopy)
|
||||
return FS::Copy(src, dest);
|
||||
else
|
||||
@@ -1361,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++)
|
||||
@@ -1375,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)
|
||||
{
|
||||
@@ -1390,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)
|
||||
{
|
||||
@@ -1400,6 +1537,11 @@ namespace Actions
|
||||
}
|
||||
free(new_path);
|
||||
}
|
||||
|
||||
if (!isCopy && FS::FolderExists(src.path))
|
||||
{
|
||||
FS::RmDir(src.path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1441,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
|
||||
{
|
||||
@@ -1544,6 +1685,7 @@ namespace Actions
|
||||
|
||||
if (confirm_state == CONFIRM_YES)
|
||||
{
|
||||
sceRtcGetCurrentTick(&prev_tick);
|
||||
if (isCopy)
|
||||
return remoteclient->Copy(src, dest);
|
||||
else
|
||||
@@ -1639,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)
|
||||
{
|
||||
@@ -1732,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)
|
||||
@@ -1782,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
@@ -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
@@ -1,5 +1,5 @@
|
||||
#ifndef BASE64_H_
|
||||
#define BASE64_H_
|
||||
#ifndef EZ_BASE64_H_
|
||||
#define EZ_BASE64_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
+23
-10
@@ -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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef APACHE_H
|
||||
#define APACHE_H
|
||||
#ifndef EZ_APACHE_H
|
||||
#define EZ_APACHE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
+154
-13
@@ -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,
|
||||
@@ -283,7 +401,7 @@ uint32_t BaseClient::SupportedActions()
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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(), ¶m);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef IIS_H
|
||||
#define IIS_H
|
||||
#ifndef EZ_IIS_H
|
||||
#define EZ_IIS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef NGINX_H
|
||||
#define NGINX_H
|
||||
#ifndef EZ_NGINX_H
|
||||
#define EZ_NGINX_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef NPXSERVE_H
|
||||
#define NPXSERVE_H
|
||||
#ifndef EZ_NPXSERVE_H
|
||||
#define EZ_NPXSERVE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -141,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] == '/')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef RCLONE_H
|
||||
#define RCLONE_H
|
||||
#ifndef EZ_RCLONE_H
|
||||
#define EZ_RCLONE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, <);
|
||||
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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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, <);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include <regex>
|
||||
#include <lexbor/html/parser.h>
|
||||
#include <lexbor/dom/interfaces/element.h>
|
||||
#include <http/httplib.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 "";
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
@@ -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); });
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
+677
-87
@@ -5,14 +5,29 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <json-c/json.h>
|
||||
#include <orbis/libkernel.h>
|
||||
#include <orbis/Bgft.h>
|
||||
#include <orbis/AppInstUtil.h>
|
||||
#include <orbis/UserService.h>
|
||||
#include <orbis/SystemService.h>
|
||||
#include <curl/curl.h>
|
||||
#include <web/request.hpp>
|
||||
#include <web/urn.hpp>
|
||||
#include "clients/webdav.h"
|
||||
#include "clients/remote_client.h"
|
||||
#include "clients/smbclient.h"
|
||||
#include "clients/sftpclient.h"
|
||||
#include "clients/ftpclient.h"
|
||||
#include "clients/github.h"
|
||||
#include "clients/nfsclient.h"
|
||||
#include "clients/webdav.h"
|
||||
#include "clients/apache.h"
|
||||
#include "clients/archiveorg.h"
|
||||
#include "clients/iis.h"
|
||||
#include "clients/github.h"
|
||||
#include "clients/myrient.h"
|
||||
#include "clients/nginx.h"
|
||||
#include "clients/npxserve.h"
|
||||
#include "clients/rclone.h"
|
||||
|
||||
#include "server/http_server.h"
|
||||
#include "installer.h"
|
||||
#include "util.h"
|
||||
@@ -22,19 +37,33 @@
|
||||
#include "system.h"
|
||||
#include "fs.h"
|
||||
#include "sfo.h"
|
||||
#include "clients/webdavclient.h"
|
||||
#include "clients/remote_client.h"
|
||||
|
||||
#define SERVER_ELF_PATH "/mnt/sandbox/pfsmnt/RMTC00001-app0/daemon/daemon.elf"
|
||||
|
||||
#define BGFT_HEAP_SIZE (1 * 1024 * 1024)
|
||||
|
||||
struct BgProgressCheck
|
||||
{
|
||||
ArchivePkgInstallData *archive_pkg_data;
|
||||
SplitPkgInstallData *split_pkg_data;
|
||||
int task_id;
|
||||
std::string hash;
|
||||
};
|
||||
|
||||
static OrbisBgftInitParams s_bgft_init_params;
|
||||
|
||||
static bool s_bgft_initialized = false;
|
||||
|
||||
static std::map<std::string, ArchivePkgInstallData *> archive_pkg_install_data_list;
|
||||
static std::map<std::string, SplitPkgInstallData *> split_pkg_install_data_list;
|
||||
|
||||
namespace INSTALLER
|
||||
{
|
||||
static int FtpCallback(int64_t xfered, void *arg)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Init(void)
|
||||
{
|
||||
int ret;
|
||||
@@ -103,22 +132,22 @@ namespace INSTALLER
|
||||
s_bgft_initialized = false;
|
||||
}
|
||||
|
||||
std::string GetRemotePkgTitle(RemoteClient *client, const std::string &path, pkg_header *header)
|
||||
{
|
||||
std::string GetRemotePkgTitle(RemoteClient *client, const std::string &path, pkg_header *header)
|
||||
{
|
||||
size_t entry_count = BE32(header->pkg_entry_count);
|
||||
uint32_t entry_table_offset = BE32(header->pkg_table_offset);
|
||||
uint64_t entry_table_size = entry_count * sizeof(pkg_table_entry);
|
||||
void *entry_table_data = malloc(entry_table_size);
|
||||
|
||||
int ret = client->GetRange(path, entry_table_data, entry_table_size, entry_table_offset);
|
||||
if (ret == 0)
|
||||
{
|
||||
free(entry_table_data);
|
||||
return "";
|
||||
}
|
||||
int ret = client->GetRange(path, entry_table_data, entry_table_size, entry_table_offset);
|
||||
if (ret == 0)
|
||||
{
|
||||
free(entry_table_data);
|
||||
return "";
|
||||
}
|
||||
|
||||
pkg_table_entry *entries = (pkg_table_entry *)entry_table_data;
|
||||
void* param_sfo_data = nullptr;
|
||||
void *param_sfo_data = nullptr;
|
||||
uint32_t param_sfo_offset = 0;
|
||||
uint32_t param_sfo_size = 0;
|
||||
for (size_t i = 0; i < entry_count; ++i)
|
||||
@@ -128,7 +157,7 @@ namespace INSTALLER
|
||||
param_sfo_offset = BE32(entries[i].offset);
|
||||
param_sfo_size = BE32(entries[i].size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(entry_table_data);
|
||||
|
||||
@@ -136,10 +165,10 @@ namespace INSTALLER
|
||||
if (param_sfo_offset > 0 && param_sfo_size > 0)
|
||||
{
|
||||
param_sfo_data = malloc(param_sfo_size);
|
||||
int ret = client->GetRange(path, param_sfo_data, param_sfo_size, param_sfo_offset);
|
||||
if (ret)
|
||||
int ret = client->GetRange(path, param_sfo_data, param_sfo_size, param_sfo_offset);
|
||||
if (ret)
|
||||
{
|
||||
const char* tmp_title = SFO::GetString((const char*)param_sfo_data, param_sfo_size, "TITLE");
|
||||
const char *tmp_title = SFO::GetString((const char *)param_sfo_data, param_sfo_size, "TITLE");
|
||||
if (tmp_title != nullptr)
|
||||
title = std::string(tmp_title);
|
||||
}
|
||||
@@ -147,7 +176,7 @@ namespace INSTALLER
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetLocalPkgTitle(const std::string &path, pkg_header *header)
|
||||
{
|
||||
@@ -161,7 +190,7 @@ namespace INSTALLER
|
||||
FS::Read(fd, entry_table_data, entry_table_size);
|
||||
|
||||
pkg_table_entry *entries = (pkg_table_entry *)entry_table_data;
|
||||
void* param_sfo_data = NULL;
|
||||
void *param_sfo_data = NULL;
|
||||
uint32_t param_sfo_offset = 0;
|
||||
uint32_t param_sfo_size = 0;
|
||||
void *icon0_png_data = NULL;
|
||||
@@ -184,7 +213,7 @@ namespace INSTALLER
|
||||
param_sfo_data = malloc(param_sfo_size);
|
||||
FS::Seek(fd, param_sfo_offset);
|
||||
FS::Read(fd, param_sfo_data, param_sfo_size);
|
||||
const char* tmp_title = SFO::GetString((const char*)param_sfo_data, param_sfo_size, "TITLE");
|
||||
const char *tmp_title = SFO::GetString((const char *)param_sfo_data, param_sfo_size, "TITLE");
|
||||
if (tmp_title != nullptr)
|
||||
title = std::string(tmp_title);
|
||||
free(param_sfo_data);
|
||||
@@ -193,12 +222,55 @@ namespace INSTALLER
|
||||
return title;
|
||||
}
|
||||
|
||||
std::string StoreBgInstallHostData(RemoteSettings *settings, const std::string &path)
|
||||
{
|
||||
std::string hash = Util::UrlHash(settings->server + path + settings->username + settings->password + std::to_string(settings->type));
|
||||
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(settings->server));
|
||||
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(settings->username));
|
||||
json_object_object_add(history_item_obj, "password", json_object_new_string(settings->password));
|
||||
json_object_object_add(history_item_obj, "type", json_object_new_int(settings->type));
|
||||
|
||||
if (settings->type == CLIENT_TYPE_HTTP_SERVER)
|
||||
{
|
||||
json_object_object_add(history_item_obj, "http_server_type", json_object_new_string(settings->http_server_type));
|
||||
}
|
||||
|
||||
const char *params_str = json_object_to_json_string(history_item_obj);
|
||||
|
||||
httplib::Client client = httplib::Client(std::string("http://127.0.0.1:") + std::to_string(http_int_server_port));
|
||||
client.set_connection_timeout(1);
|
||||
|
||||
if (auto res = client.Post("/store_bg_install_data", params_str, "application/json"))
|
||||
{
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
std::string getRemoteUrl(const std::string path, bool encodeUrl)
|
||||
{
|
||||
if (strlen(remote_settings->username) == 0 && strlen(remote_settings->password) == 0 &&
|
||||
(remoteclient->clientType() == CLIENT_TYPE_WEBDAV || remoteclient->clientType() == CLIENT_TYPE_HTTP_SERVER))
|
||||
if (remote_settings->type == CLIENT_TYPE_HTTP_SERVER && strcmp(remote_settings->http_server_type, HTTP_SERVER_GITHUB) == 0)
|
||||
{
|
||||
std::string full_url = WebDAV::GetHttpUrl(remote_settings->server + path);
|
||||
GithubClient *tmp_client = (GithubClient*) remoteclient;
|
||||
return tmp_client->GetDownloadUrl(path);
|
||||
}
|
||||
if ( strlen(remote_settings->username) == 0 &&
|
||||
strlen(remote_settings->password) == 0 &&
|
||||
(remote_settings->type == CLIENT_TYPE_WEBDAV ||
|
||||
(remote_settings->type == CLIENT_TYPE_HTTP_SERVER && strcmp(remote_settings->http_server_type, HTTP_SERVER_ARCHIVEORG) == 0)
|
||||
)
|
||||
)
|
||||
{
|
||||
std::string full_url = WebDAVClient::GetHttpUrl(remote_settings->server + path);
|
||||
size_t scheme_pos = full_url.find("://");
|
||||
if (scheme_pos == std::string::npos)
|
||||
return "";
|
||||
@@ -208,32 +280,73 @@ namespace INSTALLER
|
||||
|
||||
if (encodeUrl)
|
||||
{
|
||||
Web::Urn::Path uri(path);
|
||||
CURL *curl = curl_easy_init();
|
||||
path = uri.quote(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
path = httplib::detail::encode_url(path);
|
||||
}
|
||||
|
||||
return host + path;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string encoded_path = path;
|
||||
std::string encoded_site_name = remote_settings->site_name;
|
||||
Web::Urn::Path uri(encoded_path);
|
||||
Web::Urn::Path site_name(encoded_site_name);
|
||||
CURL *curl = curl_easy_init();
|
||||
encoded_path = uri.quote(curl);
|
||||
encoded_site_name = site_name.quote(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
std::string full_url = std::string("http://localhost:") + std::to_string(http_server_port) + "/rmt_inst" + encoded_site_name + encoded_path;
|
||||
|
||||
std::string hash = StoreBgInstallHostData(remote_settings, path);
|
||||
std::string full_url = std::string("http://127.0.0.1:") + std::to_string(http_int_server_port) + "/bg_install/" + hash;
|
||||
return full_url;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void *CheckBgInstallTaskThread(void *argp)
|
||||
{
|
||||
bool completed = false;
|
||||
OrbisBgftTaskProgress progress_info;
|
||||
BgProgressCheck *bg_check_data = (BgProgressCheck *)argp;
|
||||
int ret;
|
||||
|
||||
while (!completed)
|
||||
{
|
||||
memset(&progress_info, 0, sizeof(progress_info));
|
||||
ret = sceBgftServiceDownloadGetProgress(bg_check_data->task_id, &progress_info);
|
||||
if (ret || (progress_info.transferred > 0 && progress_info.errorResult != 0))
|
||||
{
|
||||
goto finish;
|
||||
}
|
||||
if (progress_info.length > 0)
|
||||
{
|
||||
completed = progress_info.transferred == progress_info.length;
|
||||
bytes_to_download = progress_info.length;
|
||||
bytes_transfered = progress_info.transferred;
|
||||
}
|
||||
sceSystemServicePowerTick();
|
||||
sceKernelUsleep(500000);
|
||||
}
|
||||
finish:
|
||||
if (bg_check_data->archive_pkg_data != nullptr)
|
||||
{
|
||||
bg_check_data->archive_pkg_data->stop_write_thread = true;
|
||||
pthread_join(bg_check_data->archive_pkg_data->thread, NULL);
|
||||
delete (bg_check_data->archive_pkg_data->split_file);
|
||||
free(bg_check_data->archive_pkg_data);
|
||||
RemoveArchivePkgInstallData(bg_check_data->hash);
|
||||
free(bg_check_data);
|
||||
}
|
||||
else if (bg_check_data->split_pkg_data != nullptr)
|
||||
{
|
||||
bg_check_data->split_pkg_data->stop_write_thread = true;
|
||||
bg_check_data->split_pkg_data->split_file->Close();
|
||||
pthread_join(bg_check_data->split_pkg_data->thread, NULL);
|
||||
delete (bg_check_data->split_pkg_data->split_file);
|
||||
if (bg_check_data->split_pkg_data->delete_client)
|
||||
delete (bg_check_data->split_pkg_data->remote_client);
|
||||
free(bg_check_data->split_pkg_data);
|
||||
RemoveSplitPkgInstallData(bg_check_data->hash);
|
||||
free(bg_check_data);
|
||||
}
|
||||
activity_inprogess = false;
|
||||
file_transfering = false;
|
||||
Windows::SetModalMode(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool canInstallRemotePkg(const std::string &url)
|
||||
{
|
||||
return true;
|
||||
@@ -254,6 +367,7 @@ namespace INSTALLER
|
||||
uint32_t content_type = BE32(header->pkg_content_type);
|
||||
uint32_t flags = BE32(header->pkg_content_flags);
|
||||
bool is_patch = false;
|
||||
bool completed = false;
|
||||
|
||||
switch (content_type)
|
||||
{
|
||||
@@ -350,22 +464,40 @@ namespace INSTALLER
|
||||
}
|
||||
|
||||
Util::Notify("%s queued", display_title.c_str());
|
||||
|
||||
if (prompt)
|
||||
{
|
||||
file_transfering = true;
|
||||
bytes_to_download = 100;
|
||||
bytes_to_download = header->pkg_content_size;
|
||||
bytes_transfered = 0;
|
||||
while (bytes_transfered < 99)
|
||||
sceRtcGetCurrentTick(&prev_tick);
|
||||
|
||||
while (!completed)
|
||||
{
|
||||
memset(&progress_info, 0, sizeof(progress_info));
|
||||
ret = sceBgftServiceDownloadGetProgress(task_id, &progress_info);
|
||||
if (ret || (progress_info.transferred > 0 && progress_info.errorResult != 0))
|
||||
return 0;
|
||||
bytes_transfered = (uint32_t)(((float)progress_info.transferred / progress_info.length) * 100.f);
|
||||
|
||||
if (progress_info.length > 0)
|
||||
{
|
||||
completed = progress_info.transferred == progress_info.length;
|
||||
bytes_to_download = progress_info.length;
|
||||
bytes_transfered = progress_info.transferred;
|
||||
}
|
||||
sceSystemServicePowerTick();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BgProgressCheck *bg_check_data = (BgProgressCheck *)malloc(sizeof(BgProgressCheck));
|
||||
memset(bg_check_data, 0, sizeof(BgProgressCheck));
|
||||
bg_check_data->archive_pkg_data = nullptr;
|
||||
bg_check_data->split_pkg_data = nullptr;
|
||||
bg_check_data->task_id = task_id;
|
||||
bg_check_data->hash = "";
|
||||
ret = pthread_create(&bk_install_thid, NULL, CheckBgInstallTaskThread, bg_check_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
err:
|
||||
@@ -376,6 +508,8 @@ namespace INSTALLER
|
||||
{
|
||||
int ret;
|
||||
pkg_header header;
|
||||
bool completed = false;
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
if (FS::Head(path.c_str(), (void *)&header, sizeof(header)) == 0)
|
||||
return 0;
|
||||
@@ -435,15 +569,23 @@ namespace INSTALLER
|
||||
Util::Notify("%s queued", display_title.c_str());
|
||||
|
||||
file_transfering = true;
|
||||
bytes_to_download = 100;
|
||||
bytes_to_download = header.pkg_content_size;
|
||||
bytes_transfered = 0;
|
||||
while (bytes_transfered < 99)
|
||||
sceRtcGetCurrentTick(&prev_tick);
|
||||
|
||||
while (!completed)
|
||||
{
|
||||
memset(&progress_info, 0, sizeof(progress_info));
|
||||
ret = sceBgftServiceDownloadGetProgress(task_id, &progress_info);
|
||||
if (ret || (progress_info.transferred > 0 && progress_info.errorResult != 0))
|
||||
return 0;
|
||||
bytes_transfered = (uint32_t)(((float)progress_info.transferred / progress_info.length) * 100.f);
|
||||
|
||||
if (progress_info.length > 0)
|
||||
{
|
||||
completed = progress_info.transferred == progress_info.length;
|
||||
bytes_to_download = progress_info.length;
|
||||
bytes_transfered = progress_info.transferred;
|
||||
}
|
||||
sceSystemServicePowerTick();
|
||||
}
|
||||
return 1;
|
||||
@@ -455,12 +597,14 @@ namespace INSTALLER
|
||||
int InstallLocalPkg(const std::string &path, pkg_header *header, bool remove_after_install)
|
||||
{
|
||||
int ret;
|
||||
bool completed = false;
|
||||
|
||||
if (strncmp(path.c_str(), "/data/", 6) != 0 &&
|
||||
strncmp(path.c_str(), "/user/data/", 11) != 0 &&
|
||||
strncmp(path.c_str(), "/mnt/usb", 8) != 0)
|
||||
return -1;
|
||||
|
||||
std::string filename = path.substr(path.find_last_of("/")+1);
|
||||
std::string filename = path.substr(path.find_last_of("/") + 1);
|
||||
char filepath[1024];
|
||||
snprintf(filepath, 1023, "%s", path.c_str());
|
||||
if (strncmp(path.c_str(), "/data/", 6) == 0)
|
||||
@@ -538,17 +682,24 @@ namespace INSTALLER
|
||||
}
|
||||
|
||||
sprintf(activity_message, "%s", lang_strings[STR_WAIT_FOR_INSTALL_MSG]);
|
||||
bytes_to_download = 1;
|
||||
bytes_to_download = header->pkg_content_size;
|
||||
bytes_transfered = 0;
|
||||
while (prog < 99)
|
||||
sceRtcGetCurrentTick(&prev_tick);
|
||||
|
||||
while (!completed)
|
||||
{
|
||||
memset(&progress_info, 0, sizeof(progress_info));
|
||||
ret = sceBgftServiceDownloadGetProgress(task_id, &progress_info);
|
||||
if (ret || (progress_info.transferred > 0 && progress_info.errorResult != 0))
|
||||
return -3;
|
||||
prog = (uint32_t)(((float)progress_info.transferred / progress_info.length) * 100.f);
|
||||
bytes_to_download = progress_info.length;
|
||||
bytes_transfered = progress_info.transferred;
|
||||
|
||||
if (progress_info.length > 0)
|
||||
{
|
||||
completed = progress_info.transferred == progress_info.length;
|
||||
bytes_to_download = progress_info.length;
|
||||
bytes_transfered = progress_info.transferred;
|
||||
}
|
||||
sceSystemServicePowerTick();
|
||||
}
|
||||
if (auto_delete_tmp_pkg)
|
||||
FS::Rm(path);
|
||||
@@ -573,7 +724,7 @@ namespace INSTALLER
|
||||
FS::Read(fd, entry_table_data, entry_table_size);
|
||||
|
||||
pkg_table_entry *entries = (pkg_table_entry *)entry_table_data;
|
||||
void* param_sfo_data = NULL;
|
||||
void *param_sfo_data = NULL;
|
||||
uint32_t param_sfo_offset = 0;
|
||||
uint32_t param_sfo_size = 0;
|
||||
void *icon0_png_data = NULL;
|
||||
@@ -644,7 +795,7 @@ namespace INSTALLER
|
||||
return false;
|
||||
|
||||
pkg_table_entry *entries = (pkg_table_entry *)entry_table_data;
|
||||
void* param_sfo_data = NULL;
|
||||
void *param_sfo_data = NULL;
|
||||
uint32_t param_sfo_offset = 0;
|
||||
uint32_t param_sfo_size = 0;
|
||||
void *icon0_png_data = NULL;
|
||||
@@ -712,7 +863,7 @@ namespace INSTALLER
|
||||
|
||||
void AddArchivePkgInstallData(const std::string &hash, ArchivePkgInstallData *pkg_data)
|
||||
{
|
||||
std::pair<std::string, ArchivePkgInstallData*> pair = std::make_pair(hash, pkg_data);
|
||||
std::pair<std::string, ArchivePkgInstallData *> pair = std::make_pair(hash, pkg_data);
|
||||
archive_pkg_install_data_list.erase(hash);
|
||||
archive_pkg_install_data_list.insert(pair);
|
||||
}
|
||||
@@ -722,10 +873,10 @@ namespace INSTALLER
|
||||
archive_pkg_install_data_list.erase(hash);
|
||||
}
|
||||
|
||||
bool InstallArchivePkg(const std::string &path, ArchivePkgInstallData* pkg_data)
|
||||
bool InstallArchivePkg(const std::string &path, ArchivePkgInstallData *pkg_data, bool bg)
|
||||
{
|
||||
pkg_header header;
|
||||
pkg_data->split_file->Read((char*)&header, sizeof(pkg_header), 0);
|
||||
pkg_data->split_file->Read((char *)&header, sizeof(pkg_header), 0);
|
||||
|
||||
int ret;
|
||||
std::string cid = std::string((char *)header.pkg_content_id);
|
||||
@@ -768,7 +919,7 @@ namespace INSTALLER
|
||||
}
|
||||
|
||||
std::string hash = Util::UrlHash(path);
|
||||
std::string full_url = std::string("http://localhost:") + std::to_string(http_server_port) + "/archive_inst/" + hash;
|
||||
std::string full_url = std::string("http://127.0.0.1:") + std::to_string(http_server_port) + "/archive_inst/" + hash;
|
||||
AddArchivePkgInstallData(hash, pkg_data);
|
||||
|
||||
OrbisBgftTaskProgress progress_info;
|
||||
@@ -796,18 +947,31 @@ namespace INSTALLER
|
||||
ret = sceBgftServiceIntDebugDownloadRegisterPkg(¶ms, &task_id);
|
||||
if (ret == 0x80990088 || ret == 0x80990015)
|
||||
{
|
||||
sprintf(confirm_message, "%s - %s?", display_title.c_str(), lang_strings[STR_REINSTALL_CONFIRM_MSG]);
|
||||
confirm_state = CONFIRM_WAIT;
|
||||
action_to_take = selected_action;
|
||||
activity_inprogess = false;
|
||||
while (confirm_state == CONFIRM_WAIT)
|
||||
if (!bg)
|
||||
{
|
||||
sceKernelUsleep(100000);
|
||||
}
|
||||
activity_inprogess = true;
|
||||
selected_action = action_to_take;
|
||||
sprintf(confirm_message, "%s - %s?", display_title.c_str(), lang_strings[STR_REINSTALL_CONFIRM_MSG]);
|
||||
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)
|
||||
if (confirm_state == CONFIRM_YES)
|
||||
{
|
||||
ret = sceAppInstUtilAppUnInstall(cid.c_str());
|
||||
if (ret != 0)
|
||||
{
|
||||
ret = 0;
|
||||
goto finish;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = sceAppInstUtilAppUnInstall(cid.c_str());
|
||||
if (ret != 0)
|
||||
@@ -833,35 +997,461 @@ namespace INSTALLER
|
||||
|
||||
Util::Notify("%s queued", display_title.c_str());
|
||||
|
||||
file_transfering = true;
|
||||
bytes_to_download = 100;
|
||||
bytes_transfered = 0;
|
||||
while (!completed)
|
||||
if (!bg)
|
||||
{
|
||||
memset(&progress_info, 0, sizeof(progress_info));
|
||||
ret = sceBgftServiceDownloadGetProgress(task_id, &progress_info);
|
||||
if (ret || (progress_info.transferred > 0 && progress_info.errorResult != 0))
|
||||
{
|
||||
ret = 0;
|
||||
goto finish;
|
||||
}
|
||||
bytes_transfered = (uint32_t)(((float)progress_info.transferred / progress_info.length) * 100.f);
|
||||
if (progress_info.length > 0)
|
||||
{
|
||||
completed = progress_info.transferred == progress_info.length;
|
||||
}
|
||||
sceSystemServicePowerTick();
|
||||
}
|
||||
file_transfering = true;
|
||||
bytes_to_download = header.pkg_content_size;
|
||||
bytes_transfered = 0;
|
||||
sceRtcGetCurrentTick(&prev_tick);
|
||||
|
||||
while (!completed)
|
||||
{
|
||||
memset(&progress_info, 0, sizeof(progress_info));
|
||||
ret = sceBgftServiceDownloadGetProgress(task_id, &progress_info);
|
||||
if (ret || (progress_info.transferred > 0 && progress_info.errorResult != 0))
|
||||
{
|
||||
ret = 0;
|
||||
goto finish;
|
||||
}
|
||||
if (progress_info.length > 0)
|
||||
{
|
||||
completed = progress_info.transferred == progress_info.length;
|
||||
bytes_to_download = progress_info.length;
|
||||
bytes_transfered = progress_info.transferred;
|
||||
}
|
||||
sceSystemServicePowerTick();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BgProgressCheck *bg_check_data = (BgProgressCheck *)malloc(sizeof(BgProgressCheck));
|
||||
memset(bg_check_data, 0, sizeof(BgProgressCheck));
|
||||
bg_check_data->archive_pkg_data = pkg_data;
|
||||
bg_check_data->split_pkg_data = nullptr;
|
||||
bg_check_data->task_id = task_id;
|
||||
bg_check_data->hash = hash;
|
||||
ret = pthread_create(&bk_install_thid, NULL, CheckBgInstallTaskThread, bg_check_data);
|
||||
return 1;
|
||||
}
|
||||
ret = 1;
|
||||
finish:
|
||||
pkg_data->stop_write_thread = true;
|
||||
pthread_join(pkg_data->thread, NULL);
|
||||
delete(pkg_data->split_file);
|
||||
delete (pkg_data->split_file);
|
||||
free(pkg_data);
|
||||
RemoveArchivePkgInstallData(hash);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
SplitPkgInstallData *GetSplitPkgInstallData(const std::string &hash)
|
||||
{
|
||||
return split_pkg_install_data_list[hash];
|
||||
}
|
||||
|
||||
void AddSplitPkgInstallData(const std::string &hash, SplitPkgInstallData *pkg_data)
|
||||
{
|
||||
std::pair<std::string, SplitPkgInstallData *> pair = std::make_pair(hash, pkg_data);
|
||||
split_pkg_install_data_list.erase(hash);
|
||||
split_pkg_install_data_list.insert(pair);
|
||||
}
|
||||
|
||||
void RemoveSplitPkgInstallData(const std::string &hash)
|
||||
{
|
||||
split_pkg_install_data_list.erase(hash);
|
||||
}
|
||||
|
||||
bool InstallSplitPkg(const std::string &path, SplitPkgInstallData *pkg_data, bool bg)
|
||||
{
|
||||
pkg_header header;
|
||||
pkg_data->split_file->Read((char *)&header, sizeof(pkg_header), 0);
|
||||
|
||||
int ret;
|
||||
std::string cid = std::string((char *)header.pkg_content_id);
|
||||
cid = cid.substr(cid.find_first_of("-") + 1, 9);
|
||||
std::string display_title = cid;
|
||||
int user_id;
|
||||
ret = sceUserServiceGetForegroundUser(&user_id);
|
||||
const char *package_type;
|
||||
uint32_t content_type = BE32(header.pkg_content_type);
|
||||
uint32_t flags = BE32(header.pkg_content_flags);
|
||||
bool is_patch = false;
|
||||
bool completed = false;
|
||||
|
||||
switch (content_type)
|
||||
{
|
||||
case PKG_CONTENT_TYPE_GD:
|
||||
package_type = "PS4GD";
|
||||
break;
|
||||
case PKG_CONTENT_TYPE_AC:
|
||||
package_type = "PS4AC";
|
||||
break;
|
||||
case PKG_CONTENT_TYPE_AL:
|
||||
package_type = "PS4AL";
|
||||
break;
|
||||
case PKG_CONTENT_TYPE_DP:
|
||||
package_type = "PS4DP";
|
||||
break;
|
||||
default:
|
||||
package_type = NULL;
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags & PKG_CONTENT_FLAGS_FIRST_PATCH ||
|
||||
flags & PKG_CONTENT_FLAGS_SUBSEQUENT_PATCH ||
|
||||
flags & PKG_CONTENT_FLAGS_DELTA_PATCH ||
|
||||
flags & PKG_CONTENT_FLAGS_CUMULATIVE_PATCH)
|
||||
{
|
||||
is_patch = true;
|
||||
}
|
||||
|
||||
std::string hash = Util::UrlHash(path);
|
||||
std::string full_url = std::string("http://127.0.0.1:") + std::to_string(http_server_port) + "/split_inst/" + hash;
|
||||
AddSplitPkgInstallData(hash, pkg_data);
|
||||
|
||||
OrbisBgftTaskProgress progress_info;
|
||||
OrbisBgftDownloadParam params;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
{
|
||||
params.userId = user_id;
|
||||
params.entitlementType = 5;
|
||||
params.id = (char *)header.pkg_content_id;
|
||||
params.contentUrl = full_url.c_str();
|
||||
params.contentName = display_title.c_str();
|
||||
params.iconPath = "";
|
||||
params.playgoScenarioId = "0";
|
||||
params.option = ORBIS_BGFT_TASK_OPT_DISABLE_CDN_QUERY_PARAM;
|
||||
params.packageType = package_type;
|
||||
params.packageSubType = "";
|
||||
params.packageSize = BE64(header.pkg_size);
|
||||
}
|
||||
|
||||
retry:
|
||||
int task_id = -1;
|
||||
if (!is_patch)
|
||||
ret = sceBgftServiceIntDownloadRegisterTask(¶ms, &task_id);
|
||||
else
|
||||
ret = sceBgftServiceIntDebugDownloadRegisterPkg(¶ms, &task_id);
|
||||
if (ret == 0x80990088 || ret == 0x80990015)
|
||||
{
|
||||
if (!bg)
|
||||
{
|
||||
sprintf(confirm_message, "%s - %s?", display_title.c_str(), lang_strings[STR_REINSTALL_CONFIRM_MSG]);
|
||||
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)
|
||||
{
|
||||
ret = sceAppInstUtilAppUnInstall(cid.c_str());
|
||||
if (ret != 0)
|
||||
{
|
||||
ret = 0;
|
||||
goto finish;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = sceAppInstUtilAppUnInstall(cid.c_str());
|
||||
if (ret != 0)
|
||||
{
|
||||
ret = 0;
|
||||
goto finish;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
else if (ret > 0)
|
||||
{
|
||||
ret = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
ret = sceBgftServiceDownloadStartTask(task_id);
|
||||
if (ret)
|
||||
{
|
||||
ret = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
Util::Notify("%s queued", display_title.c_str());
|
||||
|
||||
if (!bg)
|
||||
{
|
||||
file_transfering = true;
|
||||
bytes_to_download = pkg_data->size;
|
||||
bytes_transfered = 0;
|
||||
sceRtcGetCurrentTick(&prev_tick);
|
||||
|
||||
while (!completed)
|
||||
{
|
||||
memset(&progress_info, 0, sizeof(progress_info));
|
||||
ret = sceBgftServiceDownloadGetProgress(task_id, &progress_info);
|
||||
if (ret || (progress_info.transferred > 0 && progress_info.errorResult != 0))
|
||||
{
|
||||
ret = 0;
|
||||
goto finish;
|
||||
}
|
||||
if (progress_info.length > 0)
|
||||
{
|
||||
completed = progress_info.transferred == progress_info.length;
|
||||
bytes_to_download = progress_info.length;
|
||||
bytes_transfered = progress_info.transferred;
|
||||
}
|
||||
sceSystemServicePowerTick();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BgProgressCheck *bg_check_data = (BgProgressCheck *)malloc(sizeof(BgProgressCheck));
|
||||
memset(bg_check_data, 0, sizeof(BgProgressCheck));
|
||||
bg_check_data->split_pkg_data = pkg_data;
|
||||
bg_check_data->archive_pkg_data = nullptr;
|
||||
bg_check_data->task_id = task_id;
|
||||
bg_check_data->hash = hash;
|
||||
ret = pthread_create(&bk_install_thid, NULL, CheckBgInstallTaskThread, bg_check_data);
|
||||
return 1;
|
||||
}
|
||||
ret = 1;
|
||||
finish:
|
||||
pkg_data->stop_write_thread = true;
|
||||
pkg_data->split_file->Close();
|
||||
pthread_join(pkg_data->thread, NULL);
|
||||
delete (pkg_data->split_file);
|
||||
if (pkg_data->delete_client)
|
||||
delete (pkg_data->remote_client);
|
||||
free(pkg_data);
|
||||
RemoveSplitPkgInstallData(hash);
|
||||
activity_inprogess = false;
|
||||
file_transfering = false;
|
||||
Windows::SetModalMode(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string EzRemoteServerVersion()
|
||||
{
|
||||
if (!IsPortOpen("127.0.0.1", 6701, 1))
|
||||
return "";
|
||||
|
||||
httplib::Client tmp_client = httplib::Client("http://127.0.0.1:6701");
|
||||
|
||||
tmp_client.set_connection_timeout(1);
|
||||
|
||||
if (auto res = tmp_client.Get("/version"))
|
||||
{
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
return res->body;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool IsPortOpen(const char *ip, uint16_t port, int timeout_sec)
|
||||
{
|
||||
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0)
|
||||
return 0;
|
||||
|
||||
// Set socket to non-blocking mode
|
||||
int flags = fcntl(sockfd, F_GETFL, 0);
|
||||
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
inet_pton(AF_INET, ip, &addr.sin_addr);
|
||||
|
||||
// Attempt connection
|
||||
int res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));
|
||||
|
||||
if (res < 0)
|
||||
{
|
||||
if (errno == EINPROGRESS)
|
||||
{
|
||||
// Wait for connection completion using select()
|
||||
fd_set write_fds;
|
||||
struct timeval timeout;
|
||||
|
||||
FD_ZERO(&write_fds);
|
||||
FD_SET(sockfd, &write_fds);
|
||||
|
||||
timeout.tv_sec = timeout_sec;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
res = select(sockfd + 1, NULL, &write_fds, NULL, &timeout);
|
||||
|
||||
if (res > 0)
|
||||
{
|
||||
// Check if connection succeeded or failed with an error
|
||||
int so_error;
|
||||
socklen_t len = sizeof(so_error);
|
||||
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len);
|
||||
|
||||
if (so_error == 0)
|
||||
{
|
||||
// Port is open! Clean up and return success
|
||||
close(sockfd);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Connected instantly (e.g., local loopback)
|
||||
close(sockfd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
close(sockfd);
|
||||
return 0; // Port is closed or timed out
|
||||
}
|
||||
|
||||
int StartEzRemoteServer()
|
||||
{
|
||||
char buffer[8192];
|
||||
in_addr_t in_addr;
|
||||
in_addr_t server_addr;
|
||||
int filefd;
|
||||
int sockfd;
|
||||
ssize_t i;
|
||||
ssize_t read_return;
|
||||
struct hostent *hostent;
|
||||
struct sockaddr_in sockaddr_in;
|
||||
unsigned short server_port = 9090;
|
||||
|
||||
if (IsPortOpen("127.0.0.1", 6701, 1))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
filefd = open(SERVER_ELF_PATH, O_RDONLY);
|
||||
if (filefd == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
hostent = gethostbyname("127.0.0.1");
|
||||
if (hostent == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
in_addr = inet_addr(inet_ntoa(*(struct in_addr *)*(hostent->h_addr_list)));
|
||||
if (in_addr == (in_addr_t)-1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
sockaddr_in.sin_addr.s_addr = in_addr;
|
||||
sockaddr_in.sin_family = AF_INET;
|
||||
sockaddr_in.sin_port = htons(server_port);
|
||||
|
||||
if (connect(sockfd, (struct sockaddr *)&sockaddr_in, sizeof(sockaddr_in)) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
read_return = read(filefd, buffer, 8192);
|
||||
if (read_return == 0)
|
||||
break;
|
||||
if (read_return == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (write(sockfd, buffer, read_return) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
close(filefd);
|
||||
close(sockfd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StopEzRemoteServer()
|
||||
{
|
||||
httplib::Client tmp_client = httplib::Client("http://127.0.0.1:6701");
|
||||
tmp_client.Get("/stop");
|
||||
}
|
||||
|
||||
RemoteClient *GetRemoteClient(int site_idx)
|
||||
{
|
||||
RemoteClient *tmp_client = nullptr;
|
||||
RemoteSettings *tmp_settings = &site_settings[sites[site_idx]];
|
||||
|
||||
return GetRemoteClient(tmp_settings);
|
||||
}
|
||||
|
||||
RemoteClient *GetRemoteClient(RemoteSettings *settings)
|
||||
{
|
||||
RemoteClient *tmp_client = nullptr;
|
||||
|
||||
if (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_GITHUB) == 0)
|
||||
tmp_client = new GithubClient();
|
||||
else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_MYRIENT) == 0)
|
||||
tmp_client = new MyrientClient();
|
||||
}
|
||||
else if (settings->type == CLIENT_TYPE_WEBDAV)
|
||||
{
|
||||
tmp_client = new WebDAVClient();
|
||||
}
|
||||
else if (settings->type == CLIENT_TYPE_SMB)
|
||||
{
|
||||
tmp_client = new SmbClient();
|
||||
}
|
||||
else if (settings->type == CLIENT_TYPE_SFTP)
|
||||
{
|
||||
tmp_client = new SFTPClient();
|
||||
}
|
||||
else if (settings->type == CLIENT_TYPE_FTP)
|
||||
{
|
||||
tmp_client = new FtpClient();
|
||||
FtpClient *ftp_client = (FtpClient *)tmp_client;
|
||||
ftp_client->SetCallbackXferFunction(FtpCallback);
|
||||
}
|
||||
else if (settings->type == CLIENT_TYPE_NFS)
|
||||
{
|
||||
tmp_client = new NfsClient();
|
||||
}
|
||||
|
||||
tmp_client->Connect(settings->server, settings->username, settings->password, false);
|
||||
|
||||
return tmp_client;
|
||||
}
|
||||
}
|
||||
|
||||
+25
-2
@@ -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,17 @@ 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
|
||||
@@ -148,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
@@ -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
@@ -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
|
||||
|
||||
+17
-10
@@ -139,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);
|
||||
}
|
||||
@@ -292,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);
|
||||
@@ -327,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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
+507
-139
@@ -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,13 +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;
|
||||
}
|
||||
|
||||
delete(filehost);
|
||||
|
||||
size_t scheme_pos = download_url.find("://");
|
||||
@@ -1162,20 +1359,86 @@ namespace HttpServer
|
||||
|
||||
BaseClient *baseclient = new BaseClient();
|
||||
baseclient->Connect(host, "", "");
|
||||
|
||||
if (!baseclient->FileExists(path))
|
||||
{
|
||||
failed(res, 200, baseclient->LastResponse());
|
||||
activity_inprogess = false;
|
||||
file_transfering = false;
|
||||
Windows::SetModalMode(false);
|
||||
return;
|
||||
}
|
||||
baseclient->Head(path, &header, sizeof(pkg_header));
|
||||
|
||||
if (BE32(header.pkg_magic) == 0x7F434E54)
|
||||
{
|
||||
FileHost::AddCacheDownloadUrl(hash, download_url);
|
||||
std::string title = INSTALLER::GetRemotePkgTitle(baseclient, path, &header);
|
||||
delete(baseclient);
|
||||
FileHost::AddCacheDownloadUrl(hash, download_url);
|
||||
std::string title = INSTALLER::GetRemotePkgTitle(baseclient, path, &header);
|
||||
|
||||
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 (BE32(header.pkg_magic) == 0x7F434E54)
|
||||
{
|
||||
if (enable_rpi && !use_disk_cache)
|
||||
{
|
||||
failed(res, 200, lang_strings[STR_FAIL_INSTALL_FROM_URL_MSG]);
|
||||
return;
|
||||
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
|
||||
@@ -1195,22 +1458,127 @@ namespace HttpServer
|
||||
|
||||
int ret = pthread_create(&install_data->thread, NULL, Actions::ExtractArchivePkg, install_data);
|
||||
|
||||
ret = INSTALLER::InstallArchivePkg(entry->filename, install_data);
|
||||
free(entry);
|
||||
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*/)
|
||||
@@ -1236,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
|
||||
|
||||
@@ -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
@@ -1,5 +1,5 @@
|
||||
#ifndef LAUNCHER_SFO_H
|
||||
#define LAUNCHER_SFO_H
|
||||
#ifndef EZ_SFO_H
|
||||
#define EZ_SFO_H
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
+63
-16
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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(), ["e_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(), ["e_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();
|
||||
}
|
||||
@@ -1,70 +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_URN_HPP
|
||||
#define WEB_URN_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace Web
|
||||
{
|
||||
namespace Urn
|
||||
{
|
||||
using std::string;
|
||||
using std::nullptr_t;
|
||||
|
||||
string encodeUrl(const string &url, void *request);
|
||||
|
||||
class Path
|
||||
{
|
||||
public:
|
||||
|
||||
explicit Path(const string& path_, bool force_dir = false);
|
||||
explicit Path(nullptr_t);
|
||||
|
||||
auto operator+(const std::string& rhs) const -> Path;
|
||||
auto operator==(const Path& rhs) const -> bool;
|
||||
|
||||
auto is_directory() const -> bool;
|
||||
auto is_root() const -> bool;
|
||||
auto name() const -> string;
|
||||
auto parent() const -> Path;
|
||||
auto path() const -> string;
|
||||
auto quote(void* request) const -> string;
|
||||
|
||||
private:
|
||||
|
||||
string m_path;
|
||||
|
||||
static const string separate;
|
||||
static const string root;
|
||||
static const string param_separate;
|
||||
static const string query_separate;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
auto operator<<(std::ostream& stream, const Web::Urn::Path& path) -> std::ostream&;
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user