Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9854437c94 | |||
| 7a908ebf1b | |||
| f83629d107 | |||
| f0c0213940 | |||
| 9baf6c18fa | |||
| 263822ef66 | |||
| 487e288635 | |||
| 267551c979 | |||
| b761596fe3 | |||
| 99568b9990 | |||
| 414a8a4b50 | |||
| 628312360c | |||
| b4f6a8f763 | |||
| acfdd00f4f | |||
| ac55a1e5a6 | |||
| ffdfa6d04d | |||
| 7855c9a7f4 | |||
| f4625bce73 | |||
| 6b0808e48f | |||
| e8eb3fa9da | |||
| df6a068bd3 | |||
| c1307d4221 | |||
| 832c60862b | |||
| 529d88af06 | |||
| d26baf817e | |||
| 01f2f44942 | |||
| e28e562a10 | |||
| 83661c7fcb | |||
| 8aba3939a8 | |||
| 51eb0760d8 | |||
| 8c8d23603c | |||
| 338107c951 | |||
| 499dbc1b27 | |||
| e02e81acc1 | |||
| d6c97303b0 | |||
| d98284a050 | |||
| 3dc2054e44 | |||
| b18f764d27 | |||
| 3354af7f88 | |||
| 0dac26d98c | |||
| 1f2b27b52a |
+16
-1
@@ -40,6 +40,15 @@ add_executable(ezremote_client
|
||||
source/clients/smbclient.cpp
|
||||
source/clients/webdavclient.cpp
|
||||
source/clients/sftpclient.cpp
|
||||
source/clients/rclone.cpp
|
||||
source/filehost/alldebrid.cpp
|
||||
source/filehost/realdebrid.cpp
|
||||
source/filehost/directhost.cpp
|
||||
source/filehost/gdrive.cpp
|
||||
source/filehost/filehost.cpp
|
||||
source/filehost/1fichier.cpp
|
||||
source/filehost/mediafire.cpp
|
||||
source/filehost/pixeldrain.cpp
|
||||
source/server/http_server.cpp
|
||||
source/actions.cpp
|
||||
source/config.cpp
|
||||
@@ -58,11 +67,12 @@ add_executable(ezremote_client
|
||||
source/textures.cpp
|
||||
source/windows.cpp
|
||||
source/zip_util.cpp
|
||||
source/split_file.cpp
|
||||
)
|
||||
|
||||
add_self(ezremote_client)
|
||||
|
||||
add_pkg(ezremote_client ${CMAKE_SOURCE_DIR}/data "RMTC00001" "ezRemote Client" "01.08" 32 0)
|
||||
add_pkg(ezremote_client ${CMAKE_SOURCE_DIR}/data "RMTC00001" "ezRemote Client" "01.16" 32 0)
|
||||
|
||||
target_link_libraries(ezremote_client
|
||||
c
|
||||
@@ -85,6 +95,11 @@ target_link_libraries(ezremote_client
|
||||
minizip
|
||||
un7zip
|
||||
unrar
|
||||
bz2
|
||||
b2
|
||||
lzma
|
||||
lz4
|
||||
archive
|
||||
json-c
|
||||
ssh2
|
||||
kernel
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
# 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.
|
||||

|
||||
|
||||
**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.
|
||||
|
||||
**New:** As of version 1.0.9. Remote Package Installation does not require you to host an external Http Server. The embedded Web Server built into ezRemote Client acts as a Proxy Server between the PS4 and remote server (FTP, SFTP, SMB, NFS, WebDav, HttpServer(IIS/Nginx/Apache/Serve) and GoogleDrive). There's no data written to the PS4 hard drive in the process, rather everything is streamed via embedded Web Server directly to the PS4 installer.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
@@ -83,7 +87,7 @@ Tested with following WebDAV server:
|
||||
- drivehq.com
|
||||
|
||||
## Remote Package Installer Feature
|
||||
Remote Package Installation only works if the WebDAV server allow anonymous access. It's a limitation of the PS4 Installer not able to access protected links. As suggested, use the [Dufs](https://github.com/sigoden/dufs) app for WebDAV.
|
||||
Remote Package Installation with all Remote Server, even if they are password protected.
|
||||
|
||||
## Features Native Application##
|
||||
- Transfer files back and forth between PS4 and FTP/SMB/NFS/WebDAV server
|
||||
@@ -110,6 +114,7 @@ Remote Package Installation only works if the WebDAV server allow anonymous acce
|
||||
- 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).
|
||||
|
||||
## 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.
|
||||
@@ -167,6 +172,7 @@ Romanian
|
||||
Ryukyuan
|
||||
Thai
|
||||
Turkish
|
||||
Ukrainian
|
||||
```
|
||||
User must modify the file **/data/ezremote-client/config.ini** located in the ps4 hard drive and update the **language** setting to with the **exact** values from the list above.
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
permissionsUrl: '/__local__/permission',
|
||||
installUrl: '/__local__/install',
|
||||
uploadResumeSizeUrl: '__local__/uploadResumeSize',
|
||||
installUrlUrl: '/__local__/install_url',
|
||||
multipleDownloadFileName: 'ezremote-client.zip',
|
||||
isExtractableFilePattern: /\.(7z|rar|zip)$/i,
|
||||
pickCallback: function (item) {
|
||||
|
||||
+113
-36
@@ -1,75 +1,152 @@
|
||||
STR_CONNECTION_SETTINGS=إعداد الاتصال
|
||||
STR_CONNECTION_SETTINGS=إعدادات الإتصال
|
||||
STR_SITE=الموقع
|
||||
STR_LOCAL=محلي
|
||||
STR_REMOTE=بعيد
|
||||
STR_MESSAGES=الرسائل
|
||||
STR_UPDATE_SOFTWARE=تحديث التطبيق
|
||||
STR_CONNECT=توصيل
|
||||
STR_UPDATE_SOFTWARE=تحديث البرنامج
|
||||
STR_CONNECT=اتصال
|
||||
STR_DISCONNECT=قطع الاتصال
|
||||
STR_SEARCH=بحث
|
||||
STR_REFRESH=تحديث
|
||||
STR_REFRESH=تنشيط
|
||||
STR_SERVER=الخادم
|
||||
STR_USERNAME=اسم المستخدم
|
||||
STR_PASSWORD=كلمة المرور
|
||||
STR_PORT=المنفذ
|
||||
STR_PASV=متجاوز
|
||||
STR_DIRECTORY=المسار
|
||||
STR_FILTER=الفلتر
|
||||
STR_FILTER=المرشح
|
||||
STR_YES=نعم
|
||||
STR_NO=لا
|
||||
STR_CANCEL=إلغاء
|
||||
STR_CANCEL=الغاء
|
||||
STR_CONTINUE=استمرار
|
||||
STR_CLOSE=اغلاق
|
||||
STR_FOLDER=المجلد
|
||||
STR_FILE=الملف
|
||||
STR_FOLDER=مجلد
|
||||
STR_FILE=ملف
|
||||
STR_TYPE=النوع
|
||||
STR_NAME=الاسم
|
||||
STR_SIZE=الحجم
|
||||
STR_DATE=التاريخ
|
||||
STR_NEW_FOLDER=مجلد جديد
|
||||
STR_RENAME=أعادة تسمية
|
||||
STR_RENAME=اعادة تسمية
|
||||
STR_DELETE=حذف
|
||||
STR_UPLOAD=رفع
|
||||
STR_DOWNLOAD=تحميل
|
||||
STR_DOWNLOAD=تنزيل
|
||||
STR_SELECT_ALL=تحديد الكل
|
||||
STR_CLEAR_ALL=تجاهل الكل
|
||||
STR_CLEAR_ALL=الغاء الكل
|
||||
STR_UPLOADING=قيد الرفع
|
||||
STR_DOWNLOADING=قيد التحميل
|
||||
STR_OVERWRITE=الكتابة فوق
|
||||
STR_DOWNLOADING=قيد التنزيل
|
||||
STR_OVERWRITE=كتابة فوق
|
||||
STR_DONT_OVERWRITE=لاتكتب فوق
|
||||
STR_ASK_FOR_CONFIRM=أسال لتأكيد
|
||||
STR_DONT_ASK_CONFIRM=لاتسأل لتأكيد
|
||||
STR_ALLWAYS_USE_OPTION=استخدم هذا الاختيار بشكل دائم ولاتسأل مجددا
|
||||
STR_ACTIONS=الإجراءات
|
||||
STR_ASK_FOR_CONFIRM=اطلب التاكيد
|
||||
STR_DONT_ASK_CONFIRM=لاتطلب التاكيد
|
||||
STR_ALLWAYS_USE_OPTION=استخدم دائما هذا الخيار ولاتسال مجددا
|
||||
STR_ACTIONS=الاجرءات
|
||||
STR_CONFIRM=تأكيد
|
||||
STR_OVERWRITE_OPTIONS=خيارات الاستبدال او الكتابة فوق
|
||||
STR_PROPERTIES=الخصائص
|
||||
STR_OVERWRITE_OPTIONS=خيارات الكتابة فوف
|
||||
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=٤٢٦ أغلق الاتصال.
|
||||
STR_REMOTE_TERM_CONN_MSG=٤٢٦ الخادم البعيد قطع الإتصال.
|
||||
STR_FAIL_LOGIN_MSG=٣٠٠ فشل تسجيل الدخول. الرجاء التاكد من اسم المستخدم وكلمة المرور.
|
||||
STR_FAIL_TIMEOUT_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_DEL_DIR_MSG=فشل في حذف المسار
|
||||
STR_DELETING=قيد الحذف
|
||||
STR_FAIL_DEL_FILE_MSG=فشل في حذف الملف
|
||||
STR_DELETED=تم الحذف
|
||||
STR_LINK=ربط
|
||||
STR_LINK=رابط
|
||||
STR_SHARE=مشاركة
|
||||
STR_FAILED=٣١٠ فشل
|
||||
STR_FAIL_CREATE_LOCAL_FILE_MSG= فشل في انشاء الملف في المحلي ٣١٠
|
||||
STR_FAILED=310 فشل
|
||||
STR_FAIL_CREATE_LOCAL_FILE_MSG=310 فشل إنشاء ملف على المستوى المحلي
|
||||
STR_INSTALL=تثبيت
|
||||
STR_INSTALLING=يقوم بالتثبيت
|
||||
STR_INSTALLING=قيد التثبيت
|
||||
STR_INSTALL_SUCCESS=تم بنجاح
|
||||
STR_INSTALL_FAILED=فشل
|
||||
STR_INSTALL_SKIPPED=تم التخطي
|
||||
STR_CHECK_HTTP_MSG=فحص الإتصال بخادم HTTP البعيد
|
||||
STR_FAILED_HTTP_CHECK=فشل الإتصال بخادم HTTP
|
||||
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_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_FAIL_TO_OBTAIN_GG_DL_MSG=فشل الحصول على رابط تحميل قوقل
|
||||
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=اسم الملف المضغوط
|
||||
STR_COMPRESSING=قيد الضغط
|
||||
STR_ERROR_CREATE_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_ENABLE_RPI=RPI
|
||||
STR_ENABLE_RPI_FTP_SMB_MSG=يتيح هذا الخيار تثبيت الحزمة عن بُعد عبر خادم الويب المضمن.
|
||||
STR_ENABLE_RPI_WEBDAV_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_PERM_DRIVE=يمكنك الاطلاع على جميع ملفات Google Drive وتعديلها وإنشاؤها وحذفها
|
||||
STR_PERM_DRIVE_APPDATA=مشاهدة وإنشاء وحذف بيانات التكوين الخاصة به في Google Drive الخاص بك
|
||||
STR_PERM_DRIVE_FILE=يمكنك مشاهدة ملفات Google Drive المحددة التي تستخدمها مع هذا التطبيق وتعديلها وإنشاؤها وحذفها فقط
|
||||
STR_PERM_DRIVE_METADATA=عرض وإدارة البيانات الوصفية للملفات في Google Drive
|
||||
STR_PERM_DRIVE_METADATA_RO=اطلع على معلومات حول ملفات Google Drive الخاصة بك
|
||||
STR_GOOGLE_LOGIN_FAIL_MSG=فشل تسجيل الدخول إلى Google
|
||||
STR_GOOGLE_LOGIN_TIMEOUT_MSG=انتهت مهلة تسجيل الدخول إلى Google
|
||||
STR_NEW_FILE=ملف جديد
|
||||
STR_SETTINGS=الاعدادات
|
||||
STR_CLIENT_ID=معرف العميل
|
||||
STR_CLIENT_SECRET=اسرار الخادم
|
||||
STR_GLOBAL=عالمي
|
||||
STR_GOOGLE=قوقل
|
||||
STR_COPY_LINE=نسخ النص المحدد
|
||||
STR_PASTE_LINE=لصق في الخط المخصص
|
||||
STR_SHOW_HIDDEN_FILES=اضهار الملفات المخفيه
|
||||
STR_SET_DEFAULT_DIRECTORY=تعيين المجلد الافتراضي
|
||||
STR_SET_DEFAULT_DIRECTORY_MSG=تم تعيينه كمسار افتراضي
|
||||
STR_VIEW_IMAGE=عرض الصور
|
||||
STR_VIEW_PKG_INFO=معلومات الحزمة
|
||||
STR_NFS_EXP_PATH_MISSING_MSG=مسار تصدير NFS مفقود في URL
|
||||
STR_FAIL_INIT_NFS_CONTEXT=فشل في تهيئة سياق NFS
|
||||
STR_FAIL_MOUNT_NFS_MSG=فشل تحميل مشاركة NFS
|
||||
STR_WEB_SERVER=خادم الويب
|
||||
STR_ENABLE=تمكين
|
||||
STR_COMPRESSED_FILE_PATH=مسار الملف المظغوط
|
||||
STR_COMPRESSED_FILE_PATH_MSG=المسار حيث يتم تخزين الملفات المضغوطة على خادم الويب
|
||||
|
||||
@@ -82,7 +82,7 @@ STR_ONETIME_URL=One Time Url
|
||||
STR_NOT_A_VALID_PACKAGE=Not a valid Package
|
||||
STR_WAIT_FOR_INSTALL_MSG=Waiting for Package to finish installing
|
||||
STR_FAIL_INSTALL_TMP_PKG_MSG=Failed to install pkg file. Please delete the tmp pkg manually
|
||||
STR_FAIL_TO_OBTAIN_GG_DL_MSG=Failed to obtain google download URL
|
||||
STR_FAIL_TO_OBTAIN_GG_DL_MSG=Failed to obtain download URL
|
||||
STR_AUTO_DELETE_TMP_PKG=Auto delete temporary downloaded pkg file after install
|
||||
STR_PROTOCOL_NOT_SUPPORTED=Protocol not supported
|
||||
STR_COULD_NOT_RESOLVE_HOST=Could not resolve hostname
|
||||
@@ -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. This requires a HTTP Server setup on the same host sharing the same folder with anonymous access.
|
||||
STR_ENABLE_RPI_WEBDAV_MSG=This option enables Remote Package Installation. This requires the Server with anonymous access that does not need username/password.
|
||||
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_FILES=Files
|
||||
STR_EDITOR=Editor
|
||||
STR_SAVE=Save
|
||||
@@ -150,3 +150,13 @@ STR_WEB_SERVER=Web Server
|
||||
STR_ENABLE=Enable
|
||||
STR_COMPRESSED_FILE_PATH=Compressed Files Location
|
||||
STR_COMPRESSED_FILE_PATH_MSG=Location of where compressed files are stored on the web server
|
||||
STR_ALLDEBRID=AllDebrid
|
||||
STR_API_KEY=API Key
|
||||
STR_CANT_EXTRACT_URL_MSG=Couldn't extract download url
|
||||
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_REALDEBRID=Real-Debrid
|
||||
STR_BACKGROUND_INSTALL_INPROGRESS=Package install is running in the background. Don't close the app while install is in progress
|
||||
|
||||
@@ -112,8 +112,8 @@ STR_CANNOT_CONNECT_REMOTE_MSG=Servidor HTTP remoto não esta acessível.
|
||||
STR_DOWNLOAD_INSTALL_MSG=A instalação remota do pacote não é possível. Você gostaria de baixar o pacote e instalar?
|
||||
STR_CHECKING_REMOTE_SERVER_MSG=Verificando servidor remoto para instalação de pacote remoto.
|
||||
STR_ENABLE_RPI=RPI
|
||||
STR_ENABLE_RPI_FTP_SMB_MSG=Esta opção permite a instalação remota de pacotes. Isso requer uma configuração de servidor HTTP no mesmo Host compartilhando a mesma pasta com acesso anônimo.
|
||||
STR_ENABLE_RPI_WEBDAV_MSG=Esta opção permite a instalação remota de pacotes. Isso requer o servidor com acesso anônimo que não precisa de nome de usuário/senha.
|
||||
STR_ENABLE_RPI_FTP_SMB_MSG=Esta opção permite a instalação remota de pacotes.
|
||||
STR_ENABLE_RPI_WEBDAV_MSG=Esta opção permite a instalação remota de pacotes.
|
||||
STR_FILES=Arquivos
|
||||
STR_EDITOR=Editar
|
||||
STR_SAVE=Salvar
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
STR_CONNECTION_SETTINGS=Налаштування підключення
|
||||
STR_SITE=Сайт
|
||||
STR_LOCAL=Локальний
|
||||
STR_REMOTE=Дистанційний
|
||||
STR_MESSAGES=Повідомлення
|
||||
STR_UPDATE_SOFTWARE=Оновлення ПО
|
||||
STR_CONNECT=Підключитися
|
||||
STR_DISCONNECT=Від'єднатися
|
||||
STR_SEARCH=Пошук
|
||||
STR_REFRESH=Оновити
|
||||
STR_SERVER=Сервер
|
||||
STR_USERNAME=Ім'я користувача
|
||||
STR_PASSWORD=Пароль
|
||||
STR_PORT=Порт
|
||||
STR_PASV=Пасв
|
||||
STR_DIRECTORY=Каталог
|
||||
STR_FILTER=Фільтир
|
||||
STR_YES=Так
|
||||
STR_NO=Ні
|
||||
STR_CANCEL=Відмінити
|
||||
STR_CONTINUE=Продовжити
|
||||
STR_CLOSE=Закрити
|
||||
STR_FOLDER=Тека
|
||||
STR_FILE=Файл
|
||||
STR_TYPE=Тип
|
||||
STR_NAME=Ім'я
|
||||
STR_SIZE=Розмір
|
||||
STR_DATE=Дата
|
||||
STR_NEW_FOLDER=Нова тека
|
||||
STR_RENAME=Перейменувати
|
||||
STR_DELETE=Видалити
|
||||
STR_UPLOAD=Викласти
|
||||
STR_DOWNLOAD=Завантажити
|
||||
STR_SELECT_ALL=Вибрати Все
|
||||
STR_CLEAR_ALL=Очистити Все
|
||||
STR_UPLOADING=Викладання
|
||||
STR_DOWNLOADING=Завантаження
|
||||
STR_OVERWRITE=Перезапис
|
||||
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_UPDATES=Оновлення
|
||||
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_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=Встановити з посилання
|
||||
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=Не вдалося встановити файловий пакет. Будь ласка видаліть тимчасовий пакет вручну
|
||||
STR_AUTO_DELETE_TMP_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 ресурс
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+166
-86
@@ -4,6 +4,7 @@
|
||||
#include <lexbor/html/parser.h>
|
||||
#include <lexbor/dom/interfaces/element.h>
|
||||
#include <minizip/unzip.h>
|
||||
#include <orbis/SystemService.h>
|
||||
#include "clients/gdrive.h"
|
||||
#include "clients/ftpclient.h"
|
||||
#include "clients/smbclient.h"
|
||||
@@ -13,7 +14,9 @@
|
||||
#include "clients/npxserve.h"
|
||||
#include "clients/nfsclient.h"
|
||||
#include "clients/iis.h"
|
||||
#include "clients/rclone.h"
|
||||
#include "clients/sftpclient.h"
|
||||
#include "filehost/filehost.h"
|
||||
#include "common.h"
|
||||
#include "fs.h"
|
||||
#include "config.h"
|
||||
@@ -25,6 +28,7 @@
|
||||
#include "web/request.hpp"
|
||||
#include "web/urn.hpp"
|
||||
#include "system.h"
|
||||
#include "sfo.h"
|
||||
#include "zip_util.h"
|
||||
|
||||
namespace Actions
|
||||
@@ -365,6 +369,7 @@ namespace Actions
|
||||
return remoteclient->Put(src, dest);
|
||||
}
|
||||
|
||||
sceSystemServicePowerTick();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -460,6 +465,7 @@ namespace Actions
|
||||
}
|
||||
activity_inprogess = false;
|
||||
file_transfering = false;
|
||||
multi_selected_local_files.clear();
|
||||
Windows::SetModalMode(false);
|
||||
selected_action = ACTION_REFRESH_REMOTE_FILES;
|
||||
return NULL;
|
||||
@@ -516,6 +522,7 @@ namespace Actions
|
||||
return remoteclient->Get(dest, src);
|
||||
}
|
||||
|
||||
sceSystemServicePowerTick();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -672,6 +679,7 @@ namespace Actions
|
||||
{
|
||||
download_and_install = true;
|
||||
}
|
||||
|
||||
for (std::vector<DirEntry>::iterator it = files.begin(); it != files.end(); ++it)
|
||||
{
|
||||
if (stop_activity)
|
||||
@@ -702,7 +710,10 @@ namespace Actions
|
||||
}
|
||||
else
|
||||
{
|
||||
if (INSTALLER::InstallRemotePkg(it->path, &header) == 0)
|
||||
std::string url = INSTALLER::getRemoteUrl(it->path, true);
|
||||
std::string title = INSTALLER::GetRemotePkgTitle(remoteclient, it->path, &header);
|
||||
|
||||
if (INSTALLER::InstallRemotePkg(url, &header, title, true) == 0)
|
||||
failed++;
|
||||
else
|
||||
success++;
|
||||
@@ -712,6 +723,37 @@ namespace Actions
|
||||
skipped++;
|
||||
}
|
||||
}
|
||||
else if (Util::EndsWith(path,".zip") || Util::EndsWith(path,".rar") ||
|
||||
Util::EndsWith(path,".tar.xz") || Util::EndsWith(path,".tar.gz"))
|
||||
{
|
||||
ArchiveEntry *entry = ZipUtil::GetPackageEntry(it->path, remoteclient);
|
||||
if (entry != nullptr)
|
||||
{
|
||||
while (entry != nullptr)
|
||||
{
|
||||
ArchivePkgInstallData *install_data = (ArchivePkgInstallData*) malloc(sizeof(ArchivePkgInstallData));
|
||||
memset(install_data, 0, sizeof(ArchivePkgInstallData));
|
||||
|
||||
std::string install_pkg_path = std::string(temp_folder) + "/" + entry->filename;
|
||||
SplitFile *sp = new SplitFile(install_pkg_path, INSTALL_ARCHIVE_PKG_SPLIT_SIZE);
|
||||
|
||||
install_data->archive_entry = entry;
|
||||
install_data->split_file = sp;
|
||||
install_data->stop_write_thread = false;
|
||||
|
||||
int res = pthread_create(&install_data->thread, NULL, ExtractArchivePkg, install_data);
|
||||
|
||||
INSTALLER::InstallArchivePkg(entry->filename, install_data);
|
||||
|
||||
ArchiveEntry *previos = entry;
|
||||
entry = ZipUtil::GetNextPackageEntry(entry);
|
||||
free(previos);
|
||||
}
|
||||
success++;
|
||||
}
|
||||
else
|
||||
skipped++;
|
||||
}
|
||||
else
|
||||
skipped++;
|
||||
}
|
||||
@@ -741,6 +783,41 @@ namespace Actions
|
||||
}
|
||||
}
|
||||
|
||||
void *ExtractArchivePkg(void *argp)
|
||||
{
|
||||
ssize_t len;
|
||||
char *buffer = (char*) malloc(ARCHIVE_TRANSFER_SIZE);
|
||||
|
||||
ArchivePkgInstallData *install_data = (ArchivePkgInstallData*) argp;
|
||||
SplitFile *sp = install_data->split_file;
|
||||
|
||||
/* loop over file contents and write to fd */
|
||||
sp->Open();
|
||||
while (!install_data->stop_write_thread)
|
||||
{
|
||||
len = archive_read_data(install_data->archive_entry->archive, buffer, ARCHIVE_TRANSFER_SIZE);
|
||||
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
sprintf(status_message, "error archive_read_data('%s')", install_data->archive_entry->filename.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
if (sp->Write(buffer, len) != len)
|
||||
{
|
||||
sprintf(status_message, "error write('%s')", install_data->archive_entry->filename.c_str());
|
||||
break;;
|
||||
}
|
||||
}
|
||||
|
||||
sp->Close();
|
||||
free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *InstallLocalPkgsThread(void *argp)
|
||||
{
|
||||
int failed = 0;
|
||||
@@ -764,7 +841,7 @@ namespace Actions
|
||||
{
|
||||
std::string path = std::string(it->path);
|
||||
path = Util::ToLower(path);
|
||||
if (path.size() > 4 && path.substr(path.size() - 4) == ".pkg")
|
||||
if (Util::EndsWith(path,".pkg"))
|
||||
{
|
||||
pkg_header header;
|
||||
memset(&header, 0, sizeof(header));
|
||||
@@ -795,6 +872,37 @@ namespace Actions
|
||||
skipped++;
|
||||
}
|
||||
}
|
||||
else if (Util::EndsWith(path,".zip") || Util::EndsWith(path,".rar") || Util::EndsWith(path,".7z") ||
|
||||
Util::EndsWith(path,".tar.xz") || Util::EndsWith(path,".tar.gz") || Util::EndsWith(path,".tar.bz2") )
|
||||
{
|
||||
ArchiveEntry *entry = ZipUtil::GetPackageEntry(it->path);
|
||||
if (entry != nullptr)
|
||||
{
|
||||
while (entry != nullptr)
|
||||
{
|
||||
ArchivePkgInstallData *install_data = (ArchivePkgInstallData*) malloc(sizeof(ArchivePkgInstallData));
|
||||
memset(install_data, 0, sizeof(ArchivePkgInstallData));
|
||||
|
||||
std::string install_pkg_path = std::string(temp_folder) + "/" + entry->filename;
|
||||
SplitFile *sp = new SplitFile(install_pkg_path, INSTALL_ARCHIVE_PKG_SPLIT_SIZE);
|
||||
|
||||
install_data->archive_entry = entry;
|
||||
install_data->split_file = sp;
|
||||
install_data->stop_write_thread = false;
|
||||
|
||||
int res = pthread_create(&install_data->thread, NULL, ExtractArchivePkg, install_data);
|
||||
|
||||
INSTALLER::InstallArchivePkg(entry->filename, install_data);
|
||||
|
||||
ArchiveEntry *previous = entry;
|
||||
entry = ZipUtil::GetNextPackageEntry(entry);
|
||||
free(previous);
|
||||
}
|
||||
success++;
|
||||
}
|
||||
else
|
||||
skipped++;
|
||||
}
|
||||
else
|
||||
skipped++;
|
||||
}
|
||||
@@ -866,6 +974,49 @@ namespace Actions
|
||||
}
|
||||
}
|
||||
|
||||
void *ExtractRemoteZipThread(void *argp)
|
||||
{
|
||||
FS::MkDirs(extract_zip_folder);
|
||||
std::vector<DirEntry> files;
|
||||
if (multi_selected_remote_files.size() > 0)
|
||||
std::copy(multi_selected_remote_files.begin(), multi_selected_remote_files.end(), std::back_inserter(files));
|
||||
else
|
||||
files.push_back(selected_remote_file);
|
||||
|
||||
for (std::vector<DirEntry>::iterator it = files.begin(); it != files.end(); ++it)
|
||||
{
|
||||
if (stop_activity)
|
||||
break;
|
||||
if (!it->isDir)
|
||||
{
|
||||
int ret = ZipUtil::Extract(*it, extract_zip_folder, remoteclient);
|
||||
if (ret == 0)
|
||||
{
|
||||
sprintf(status_message, "%s %s", lang_strings[STR_FAILED_TO_EXTRACT], it->name);
|
||||
sceKernelUsleep(100000);
|
||||
}
|
||||
}
|
||||
}
|
||||
activity_inprogess = false;
|
||||
multi_selected_remote_files.clear();
|
||||
Windows::SetModalMode(false);
|
||||
selected_action = ACTION_REFRESH_LOCAL_FILES;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ExtractRemoteZips()
|
||||
{
|
||||
sprintf(status_message, "%s", "");
|
||||
int res = pthread_create(&bk_activity_thid, NULL, ExtractRemoteZipThread, NULL);
|
||||
if (res != 0)
|
||||
{
|
||||
file_transfering = false;
|
||||
activity_inprogess = false;
|
||||
multi_selected_remote_files.clear();
|
||||
Windows::SetModalMode(false);
|
||||
}
|
||||
}
|
||||
|
||||
void *MakeZipThread(void *argp)
|
||||
{
|
||||
zipFile zf = zipOpen64(zip_file_path, APPEND_STATUS_CREATE);
|
||||
@@ -914,87 +1065,6 @@ namespace Actions
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetGoogleDownloadUrl(std::string &url)
|
||||
{
|
||||
size_t scheme_pos = url.find_first_of("://");
|
||||
size_t path_pos = url.find_first_of("/", scheme_pos + 3);
|
||||
std::string host = url.substr(0, path_pos);
|
||||
std::string path = url.substr(path_pos);
|
||||
|
||||
std::string first_download_path;
|
||||
size_t file_id_start_pos = path.find("/file/d/");
|
||||
if (file_id_start_pos != std::string::npos)
|
||||
{
|
||||
file_id_start_pos = file_id_start_pos + 8;
|
||||
std::string file_id = path.substr(file_id_start_pos);
|
||||
size_t file_id_end_pos = file_id.find_first_of("/");
|
||||
file_id = file_id.substr(0, file_id_end_pos);
|
||||
first_download_path = "/uc?export=download&id=" + file_id;
|
||||
}
|
||||
else if (path.find("/uc?export=download") != std::string::npos)
|
||||
{
|
||||
first_download_path = path;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
WebDAV::WebDavClient tmp_client;
|
||||
tmp_client.Connect(host.c_str(), "", "", false);
|
||||
WebDAV::dict_t headers;
|
||||
tmp_client.GetHeaders(first_download_path.c_str(), &headers);
|
||||
|
||||
std::string content_type = WebDAV::get(headers, "content-type");
|
||||
if (content_type.find("application/octet-stream") != std::string::npos)
|
||||
{
|
||||
return first_download_path;
|
||||
}
|
||||
else if (content_type.find("text/html") == std::string::npos)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
char *buffer_ptr = nullptr;
|
||||
unsigned long long buffer_size = 0;
|
||||
tmp_client.GetClient()->download_to(first_download_path, buffer_ptr, buffer_size);
|
||||
|
||||
lxb_status_t status;
|
||||
lxb_dom_element_t *element;
|
||||
lxb_html_document_t *document;
|
||||
lxb_dom_collection_t *collection;
|
||||
lxb_dom_attr_t *attr;
|
||||
document = lxb_html_document_create();
|
||||
status = lxb_html_document_parse(document, (lxb_char_t *)buffer_ptr, buffer_size);
|
||||
if (status != LXB_STATUS_OK)
|
||||
return "";
|
||||
collection = lxb_dom_collection_make(&document->dom_document, 128);
|
||||
if (collection == NULL)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
status = lxb_dom_elements_by_tag_name(lxb_dom_interface_element(document->body),
|
||||
collection, (const lxb_char_t *)"form", 4);
|
||||
if (status != LXB_STATUS_OK)
|
||||
return "";
|
||||
std::string download_url;
|
||||
for (size_t i = 0; i < lxb_dom_collection_length(collection); i++)
|
||||
{
|
||||
element = lxb_dom_collection_element(collection, i);
|
||||
std::string form_id((char *)element->attr_id->value->data, element->attr_id->value->length);
|
||||
if (form_id == "download-form")
|
||||
{
|
||||
size_t value_len;
|
||||
const lxb_char_t *value = lxb_dom_element_get_attribute(element, (const lxb_char_t *)"action", 6, &value_len);
|
||||
download_url = std::string((char *)value, value_len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
lxb_dom_collection_destroy(collection, true);
|
||||
lxb_html_document_destroy(document);
|
||||
|
||||
return download_url;
|
||||
}
|
||||
|
||||
void *InstallUrlPkgThread(void *argp)
|
||||
{
|
||||
bytes_transfered = 0;
|
||||
@@ -1008,8 +1078,16 @@ namespace Actions
|
||||
sprintf(filename, "%s/%lu.pkg", DATA_PATH, tick.mytick);
|
||||
|
||||
std::string full_url = std::string(install_pkg_url.url);
|
||||
if (full_url.find("https://drive.google.com") != std::string::npos)
|
||||
full_url = GetGoogleDownloadUrl(full_url);
|
||||
FileHost *filehost = FileHost::getFileHost(full_url);
|
||||
if (!filehost->IsValidUrl())
|
||||
{
|
||||
sprintf(status_message, "%s", lang_strings[STR_FAIL_TO_OBTAIN_GG_DL_MSG]);
|
||||
activity_inprogess = false;
|
||||
Windows::SetModalMode(false);
|
||||
return NULL;
|
||||
}
|
||||
full_url = filehost->GetDownloadUrl();
|
||||
delete(filehost);
|
||||
|
||||
if (full_url.empty())
|
||||
{
|
||||
@@ -1129,6 +1207,8 @@ namespace Actions
|
||||
remoteclient = new NginxClient();
|
||||
else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_NPX_SERVE) == 0)
|
||||
remoteclient = new NpxServeClient();
|
||||
else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_RCLONE) == 0)
|
||||
remoteclient = new RCloneClient();
|
||||
}
|
||||
else if (strncmp(remote_settings->server, "webdavs://", 10) == 0 || strncmp(remote_settings->server, "webdav://", 9) == 0)
|
||||
{
|
||||
@@ -1647,7 +1727,7 @@ namespace Actions
|
||||
OrbisTick tick;
|
||||
sceRtcGetCurrentClockLocalTime(&now);
|
||||
sceRtcGetTick(&now, &tick);
|
||||
sprintf(local_file, "%s/%lu.pkg", DATA_PATH, tick.mytick);
|
||||
sprintf(local_file, "%s/%lu.pkg", temp_folder, tick.mytick);
|
||||
|
||||
sprintf(activity_message, "%s %s to %s", lang_strings[STR_DOWNLOADING], filename.c_str(), local_file);
|
||||
remoteclient->Size(filename, &bytes_to_download);
|
||||
|
||||
+5
-1
@@ -56,7 +56,8 @@ enum ACTIONS
|
||||
ACTION_VIEW_LOCAL_IMAGE,
|
||||
ACTION_VIEW_REMOTE_IMAGE,
|
||||
ACTION_VIEW_LOCAL_PKG,
|
||||
ACTION_VIEW_REMOTE_PKG
|
||||
ACTION_VIEW_REMOTE_PKG,
|
||||
ACTION_EXTRACT_REMOTE_ZIP,
|
||||
};
|
||||
|
||||
enum OverWriteType
|
||||
@@ -103,6 +104,8 @@ namespace Actions
|
||||
void *KeepAliveThread(void *argp);
|
||||
void *ExtractZipThread(void *argp);
|
||||
void ExtractLocalZips();
|
||||
void *ExtractRemoteZipThread(void *argp);
|
||||
void ExtractRemoteZips();
|
||||
void *MakeZipThread(void *argp);
|
||||
void MakeLocalZip();
|
||||
void *MoveLocalFilesThread(void *argp);
|
||||
@@ -116,6 +119,7 @@ namespace Actions
|
||||
int DownloadAndInstallPkg(const std::string &filename, pkg_header *header);
|
||||
void CreateLocalFile(char *filename);
|
||||
void CreateRemoteFile(char *filename);
|
||||
void *ExtractArchivePkg(void *argp);
|
||||
}
|
||||
|
||||
#endif
|
||||
+6
-2
@@ -6,7 +6,7 @@
|
||||
class Base64
|
||||
{
|
||||
public:
|
||||
static int Encode(const std::string &input, std::string &out)
|
||||
static int Encode(unsigned char *input, size_t in_len, std::string &out)
|
||||
{
|
||||
static constexpr char sEncodingTable[] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
@@ -18,7 +18,6 @@ public:
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'};
|
||||
|
||||
size_t in_len = input.size();
|
||||
size_t out_len = 4 * ((in_len + 2) / 3);
|
||||
out.resize(out_len);
|
||||
size_t i;
|
||||
@@ -50,6 +49,11 @@ public:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Encode(const std::string &input, std::string &out)
|
||||
{
|
||||
return Encode((unsigned char*)input.data(), input.size(), out);
|
||||
}
|
||||
|
||||
static int Decode(const std::string &input, std::string &out)
|
||||
{
|
||||
static constexpr unsigned char kDecodingTable[] = {
|
||||
|
||||
@@ -12,26 +12,6 @@ using httplib::Client;
|
||||
using httplib::Headers;
|
||||
using httplib::Result;
|
||||
|
||||
lxb_dom_node_t *nextChildElement(lxb_dom_element_t *element)
|
||||
{
|
||||
lxb_dom_node_t *node = element->node.first_child;
|
||||
while (node != nullptr && node->type != LXB_DOM_NODE_TYPE_ELEMENT)
|
||||
{
|
||||
node = node->next;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
lxb_dom_node_t *nextElement(lxb_dom_node_t *node)
|
||||
{
|
||||
lxb_dom_node_t *next = node->next;
|
||||
while (next != nullptr && next->type != LXB_DOM_NODE_TYPE_ELEMENT)
|
||||
{
|
||||
next = next->next;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
std::vector<DirEntry> ApacheClient::ListDir(const std::string &path)
|
||||
{
|
||||
std::vector<DirEntry> out;
|
||||
@@ -101,7 +81,7 @@ std::vector<DirEntry> ApacheClient::ListDir(const std::string &path)
|
||||
memset(&entry, 0, sizeof(DirEntry));
|
||||
|
||||
element = lxb_dom_collection_element(collection, i);
|
||||
node = nextChildElement(element);
|
||||
node = NextChildElement(element);
|
||||
if (node == nullptr) continue;
|
||||
|
||||
value = lxb_dom_element_local_name(lxb_dom_interface_element(node), &value_len);
|
||||
@@ -114,7 +94,7 @@ std::vector<DirEntry> ApacheClient::ListDir(const std::string &path)
|
||||
if (tmp_string.compare("td") == 0)
|
||||
{
|
||||
// get the child img element
|
||||
lxb_dom_node_t *img = nextChildElement(lxb_dom_interface_element(node));
|
||||
lxb_dom_node_t *img = NextChildElement(lxb_dom_interface_element(node));
|
||||
if (img == nullptr) continue;
|
||||
|
||||
value = lxb_dom_element_local_name(lxb_dom_interface_element(img), &value_len);
|
||||
@@ -142,7 +122,7 @@ std::vector<DirEntry> ApacheClient::ListDir(const std::string &path)
|
||||
else continue; // invalid record
|
||||
|
||||
// file/folder name
|
||||
node = nextElement(node);
|
||||
node = NextElement(node);
|
||||
if (node == nullptr) continue;
|
||||
value = lxb_dom_element_local_name(lxb_dom_interface_element(node), &value_len);
|
||||
tmp_string = std::string((const char *)value, value_len);
|
||||
@@ -165,7 +145,7 @@ std::vector<DirEntry> ApacheClient::ListDir(const std::string &path)
|
||||
else continue; // not valid record
|
||||
|
||||
// datetime
|
||||
node = nextElement(node);
|
||||
node = NextElement(node);
|
||||
if (node == nullptr) continue;
|
||||
value = lxb_dom_element_local_name(lxb_dom_interface_element(node), &value_len);
|
||||
tmp_string = std::string((const char *)value, value_len);
|
||||
@@ -195,7 +175,7 @@ std::vector<DirEntry> ApacheClient::ListDir(const std::string &path)
|
||||
else continue; // invalid record
|
||||
|
||||
// filesize
|
||||
node = nextElement(node);
|
||||
node = NextElement(node);
|
||||
if (node == nullptr) continue;
|
||||
value = lxb_dom_element_local_name(lxb_dom_interface_element(node), &value_len);
|
||||
tmp_string = std::string((const char *)value, value_len);
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#include <lexbor/html/parser.h>
|
||||
#include <lexbor/dom/interfaces/element.h>
|
||||
#include <fstream>
|
||||
#include <curl/curl.h>
|
||||
#include "common.h"
|
||||
@@ -44,6 +42,30 @@ int BaseClient::Connect(const std::string &url, const std::string &username, con
|
||||
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("://");
|
||||
size_t root_pos = url.find("/", scheme_pos + 3);
|
||||
if (root_pos != std::string::npos)
|
||||
{
|
||||
scheme_host_port = url.substr(0, root_pos);
|
||||
this->base_path = url.substr(root_pos);
|
||||
}
|
||||
|
||||
client = new httplib::Client(scheme_host_port);
|
||||
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);
|
||||
client->set_read_timeout(30);
|
||||
client->enable_server_certificate_verification(false);
|
||||
if (Ping())
|
||||
this->connected = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int BaseClient::Mkdir(const std::string &path)
|
||||
{
|
||||
sprintf(this->response, "%s", lang_strings[STR_UNSUPPORTED_OPERATION_MSG]);
|
||||
@@ -94,6 +116,29 @@ int BaseClient::Get(const std::string &outputfile, const std::string &path, uint
|
||||
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);
|
||||
Headers headers = {{"Range", range_header}};
|
||||
size_t bytes_read = 0;
|
||||
if (auto res = client->Get(GetFullPath(path), headers,
|
||||
[&](const char *data, size_t data_length)
|
||||
{
|
||||
bytes_read += data_length;
|
||||
bool ok = sink.write(data, data_length);
|
||||
return ok;
|
||||
}))
|
||||
{
|
||||
return bytes_read == size;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(this->response, "%s", httplib::to_string(res.error()).c_str());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int BaseClient::GetRange(const std::string &path, void *buffer, uint64_t size, uint64_t offset)
|
||||
{
|
||||
char range_header[64];
|
||||
@@ -259,7 +304,7 @@ ClientType BaseClient::clientType()
|
||||
|
||||
uint32_t BaseClient::SupportedActions()
|
||||
{
|
||||
return REMOTE_ACTION_DOWNLOAD | REMOTE_ACTION_INSTALL;
|
||||
return REMOTE_ACTION_DOWNLOAD | REMOTE_ACTION_INSTALL | REMOTE_ACTION_EXTRACT;
|
||||
}
|
||||
|
||||
std::string BaseClient::EncodeUrl(const std::string &url)
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <lexbor/html/parser.h>
|
||||
#include <lexbor/dom/interfaces/element.h>
|
||||
#include "http/httplib.h"
|
||||
#include "clients/remote_client.h"
|
||||
#include "http/httplib.h"
|
||||
#include "common.h"
|
||||
|
||||
class BaseClient : public RemoteClient
|
||||
@@ -13,11 +16,13 @@ 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 &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 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);
|
||||
|
||||
@@ -132,14 +132,31 @@ int FtpClient::Connect(const std::string &url, const std::string &user, const st
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string cmd = "USER " + user;
|
||||
std::string cmd;
|
||||
if (user.length() > 0)
|
||||
{
|
||||
cmd = "USER " + user;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd = "USER anonymous";
|
||||
}
|
||||
|
||||
if (!FtpSendCmd(cmd, '3', mp_ftphandle))
|
||||
{
|
||||
if (mp_ftphandle->ctrl != NULL)
|
||||
return 1;
|
||||
if (*LastResponse() == '2')
|
||||
{
|
||||
mp_ftphandle->is_connected = true;
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Quit();
|
||||
sprintf(mp_ftphandle->response, "%s", lang_strings[STR_FAIL_LOGIN_MSG]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
cmd = "PASS " + pass;
|
||||
@@ -1266,6 +1283,45 @@ 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::GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset)
|
||||
{
|
||||
ftphandle *nData;
|
||||
mp_ftphandle->offset = offset;
|
||||
if (!FtpAccess(path, FtpClient::fileread, FtpClient::transfermode::image, mp_ftphandle, &nData))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
char buf[FTP_CLIENT_BUFSIZ];
|
||||
int count = 0;
|
||||
size_t bytes_remaining = size;
|
||||
|
||||
do
|
||||
{
|
||||
size_t bytes_to_read = std::min<size_t>(FTP_CLIENT_BUFSIZ, bytes_remaining);
|
||||
count = FtpRead(buf, bytes_to_read, nData);
|
||||
if (count > 0)
|
||||
{
|
||||
bytes_remaining -= count;
|
||||
bool ok = sink.write((char *)buf, count);
|
||||
if (!ok)
|
||||
{
|
||||
FtpClose(nData);
|
||||
mp_ftphandle->offset = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
FtpClose(nData);
|
||||
mp_ftphandle->offset = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int FtpClient::GetRange(const std::string &path, void *buffer, uint64_t size, uint64_t offset)
|
||||
{
|
||||
ftphandle *nData;
|
||||
@@ -1278,7 +1334,7 @@ int FtpClient::GetRange(const std::string &path, void *buffer, uint64_t size, ui
|
||||
char buf[8192];
|
||||
int l = 0;
|
||||
uint64_t remaining = size;
|
||||
char *p = (char*) buffer;
|
||||
char *p = (char *)buffer;
|
||||
while ((l = FtpRead(buf, 8192, nData)) > 0)
|
||||
{
|
||||
if (l <= remaining)
|
||||
@@ -1297,7 +1353,6 @@ int FtpClient::GetRange(const std::string &path, void *buffer, uint64_t size, ui
|
||||
mp_ftphandle->offset = 0;
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "clients/remote_client.h"
|
||||
#include "http/httplib.h"
|
||||
|
||||
#define FTP_CLIENT_MAX_FILENAME_LEN 128
|
||||
#define FTP_CLIENT_MAX_FILENAME_LEN 255
|
||||
|
||||
typedef int (*FtpCallbackXfer)(int64_t xfered, void *arg);
|
||||
|
||||
@@ -79,6 +80,7 @@ public:
|
||||
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);
|
||||
|
||||
@@ -226,7 +226,7 @@ int GDriveClient::Rename(const std::string &src, const std::string &dst)
|
||||
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);
|
||||
@@ -331,6 +331,35 @@ int GDriveClient::Get(const std::string &outputfile, const std::string &path, ui
|
||||
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";
|
||||
if (!drive_id.empty())
|
||||
url += "&supportsAllDrives=true";
|
||||
Headers headers;
|
||||
headers.insert(std::make_pair("Range", "bytes=" + std::to_string(offset) + "-" + std::to_string(offset + size - 1)));
|
||||
if (auto res = client->Get(url, headers,
|
||||
[&](const char *data, size_t data_length)
|
||||
{
|
||||
bytes_read += data_length;
|
||||
bool ok = sink.write(data, data_length);
|
||||
return ok;
|
||||
}))
|
||||
{
|
||||
return bytes_read == size;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(this->response, "%s", httplib::to_string(res.error()).c_str());
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int GDriveClient::GetRange(const std::string &path, void *buffer, uint64_t size, uint64_t offset)
|
||||
{
|
||||
size_t bytes_read = 0;
|
||||
@@ -463,9 +492,9 @@ 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)));
|
||||
@@ -576,10 +605,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"))
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "http/httplib.h"
|
||||
#include "clients/remote_client.h"
|
||||
#include "clients/baseclient.h"
|
||||
#include "http/httplib.h"
|
||||
#include "common.h"
|
||||
|
||||
static pthread_t refresh_token_thid;
|
||||
@@ -20,6 +21,7 @@ public:
|
||||
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 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 Head(const std::string &path, void *buffer, uint64_t len);
|
||||
int Update(const std::string &inputfile, const std::string &path);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "util.h"
|
||||
#include "system.h"
|
||||
|
||||
#define BUF_SIZE 64*1024
|
||||
#define BUF_SIZE 256*1024
|
||||
|
||||
NfsClient::NfsClient()
|
||||
{
|
||||
@@ -233,6 +233,51 @@ int NfsClient::Get(const std::string &outputfile, const std::string &ppath, uint
|
||||
return 1;
|
||||
}
|
||||
|
||||
int NfsClient::GetRange(const std::string &path, DataSink &sink, uint64_t size, uint64_t offset)
|
||||
{
|
||||
struct nfsfh *nfsfh = nullptr;
|
||||
int ret = nfs_open(nfs, path.c_str(), 0400, &nfsfh);
|
||||
if (ret != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!ok)
|
||||
{
|
||||
free((void *)buff);
|
||||
nfs_close(nfs, nfsfh);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
free((void *)buff);
|
||||
nfs_close(nfs, nfsfh);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int NfsClient::GetRange(const std::string &ppath, void *buffer, uint64_t size, uint64_t offset)
|
||||
{
|
||||
if (!FileExists(ppath))
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "nfsc/libnfs-raw.h"
|
||||
#include "nfsc/libnfs-raw-mount.h"
|
||||
#include "clients/remote_client.h"
|
||||
#include "http/httplib.h"
|
||||
#include "common.h"
|
||||
|
||||
class NfsClient : public RemoteClient
|
||||
@@ -23,6 +24,7 @@ public:
|
||||
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);
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
#include <lexbor/html/parser.h>
|
||||
#include <lexbor/dom/interfaces/element.h>
|
||||
#include <fstream>
|
||||
#include "common.h"
|
||||
#include "clients/remote_client.h"
|
||||
#include "clients/rclone.h"
|
||||
#include "lang.h"
|
||||
#include "util.h"
|
||||
#include "system.h"
|
||||
#include "windows.h"
|
||||
|
||||
using httplib::Client;
|
||||
using httplib::Headers;
|
||||
using httplib::Result;
|
||||
|
||||
std::vector<DirEntry> RCloneClient::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 *tbody_element, *tr_element, *td_element;
|
||||
lxb_html_document_t *document;
|
||||
lxb_dom_collection_t *tbody_collection;
|
||||
lxb_dom_collection_t *tr_collection;
|
||||
lxb_dom_collection_t *td_collection;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
tbody_collection = lxb_dom_collection_make(&document->dom_document, 1);
|
||||
if (tbody_collection == NULL)
|
||||
{
|
||||
lxb_html_document_destroy(document);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
tr_collection = lxb_dom_collection_make(&document->dom_document, 128);
|
||||
if (tbody_collection == NULL)
|
||||
{
|
||||
lxb_html_document_destroy(document);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
status = lxb_dom_elements_by_tag_name(lxb_dom_interface_element(document->body),
|
||||
tbody_collection, (const lxb_char_t *)"tbody", 5);
|
||||
if (status != LXB_STATUS_OK)
|
||||
{
|
||||
lxb_dom_collection_destroy(tr_collection, true);
|
||||
lxb_dom_collection_destroy(tbody_collection, true);
|
||||
lxb_html_document_destroy(document);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (lxb_dom_collection_length(tbody_collection) < 1)
|
||||
{
|
||||
lxb_dom_collection_destroy(tr_collection, true);
|
||||
lxb_dom_collection_destroy(tbody_collection, true);
|
||||
lxb_html_document_destroy(document);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
// Get the first tbody which should only be 1
|
||||
tbody_element = lxb_dom_collection_element(tbody_collection, 0);
|
||||
status = lxb_dom_elements_by_tag_name(tbody_element,
|
||||
tr_collection, (const lxb_char_t *)"tr", 2);
|
||||
if (status != LXB_STATUS_OK)
|
||||
{
|
||||
lxb_dom_collection_destroy(tr_collection, true);
|
||||
lxb_dom_collection_destroy(tbody_collection, true);
|
||||
lxb_html_document_destroy(document);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
// skip row 0 , since it has the previous folder header
|
||||
for (size_t i = 1; i < lxb_dom_collection_length(tr_collection); i++)
|
||||
{
|
||||
DirEntry entry;
|
||||
std::string title, aclass;
|
||||
memset(&entry.modified, 0, sizeof(DateTime));
|
||||
const lxb_char_t *value;
|
||||
size_t value_len;
|
||||
std::string tmp_string;
|
||||
|
||||
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) < 0)
|
||||
{
|
||||
lxb_dom_collection_destroy(td_collection, true);
|
||||
lxb_dom_collection_destroy(tr_collection, true);
|
||||
lxb_dom_collection_destroy(tbody_collection, true);
|
||||
lxb_html_document_destroy(document);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
// td 0 is empty, td 1 is file or folder
|
||||
td_element = lxb_dom_collection_element(td_collection, 1);
|
||||
lxb_dom_node_t *use_node = NextChildElement(lxb_dom_interface_element(NextChildElement(td_element)));
|
||||
value = lxb_dom_element_local_name(lxb_dom_interface_element(use_node), &value_len);
|
||||
tmp_string = std::string((const char *)value, value_len);
|
||||
if (tmp_string.compare("use") != 0)
|
||||
{
|
||||
lxb_dom_collection_destroy(td_collection, true);
|
||||
lxb_dom_collection_destroy(tr_collection, true);
|
||||
lxb_dom_collection_destroy(tbody_collection, true);
|
||||
lxb_html_document_destroy(document);
|
||||
goto finish;
|
||||
}
|
||||
value = lxb_dom_element_get_attribute(lxb_dom_interface_element(use_node), (const lxb_char_t *)"xlink:href", 10, &value_len);
|
||||
tmp_string = std::string((const char *)value, value_len);
|
||||
if (tmp_string.compare("#folder") == 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;
|
||||
}
|
||||
|
||||
// <a> element contains the file/folder name
|
||||
lxb_dom_node_t *a_node = NextChildElement(lxb_dom_interface_element(NextElement(NextChildElement(td_element))));
|
||||
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::DecodeUrl(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);
|
||||
}
|
||||
|
||||
// td 3 - filesize
|
||||
if (!entry.isDir)
|
||||
{
|
||||
td_element = lxb_dom_collection_element(td_collection, 2);
|
||||
lxb_dom_node_t *size_node = NextChildElement(td_element);
|
||||
value = lxb_dom_node_text_content(size_node->first_child, &value_len);
|
||||
tmp_string = std::string((const char *)value, value_len);
|
||||
entry.file_size = atol(tmp_string.c_str());
|
||||
DirEntry::SetDisplaySize(&entry);
|
||||
}
|
||||
|
||||
// td 4 - datetime
|
||||
td_element = lxb_dom_collection_element(td_collection, 3);
|
||||
lxb_dom_node_t *date_node = NextChildElement(td_element);
|
||||
value = lxb_dom_element_get_attribute(lxb_dom_interface_element(date_node), (const lxb_char_t *)"datetime", 8, &value_len);
|
||||
tmp_string = std::string((const char *)value, value_len);
|
||||
std::vector<std::string> date_time = Util::Split(tmp_string, " ");
|
||||
|
||||
OrbisDateTime gmt;
|
||||
OrbisDateTime lt;
|
||||
|
||||
if (date_time.size() > 1)
|
||||
{
|
||||
std::vector<std::string> adate = Util::Split(date_time[0], "-");
|
||||
if (adate.size() == 3)
|
||||
{
|
||||
gmt.year = atoi(adate[0].c_str());
|
||||
gmt.month = atoi(adate[1].c_str());
|
||||
gmt.day = atoi(adate[2].c_str());
|
||||
}
|
||||
|
||||
std::vector<std::string> atime = Util::Split(date_time[1], ":");
|
||||
if (atime.size() == 3)
|
||||
{
|
||||
gmt.hour = atoi(atime[0].c_str());
|
||||
gmt.minute = atoi(atime[1].c_str());
|
||||
|
||||
std::vector<std::string> sec_msec = Util::Split(atime[2], ".");
|
||||
if (sec_msec.size() > 0)
|
||||
{
|
||||
gmt.second = atoi(sec_msec[0].c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
lxb_dom_collection_destroy(td_collection, true);
|
||||
out.push_back(entry);
|
||||
}
|
||||
|
||||
lxb_dom_collection_destroy(tr_collection, true);
|
||||
lxb_dom_collection_destroy(tbody_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 RCLONE_H
|
||||
#define RCLONE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "http/httplib.h"
|
||||
#include "clients/baseclient.h"
|
||||
#include "clients/remote_client.h"
|
||||
#include "common.h"
|
||||
|
||||
class RCloneClient : public BaseClient
|
||||
{
|
||||
public:
|
||||
std::vector<DirEntry> ListDir(const std::string &path);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common.h"
|
||||
#include "http/httplib.h"
|
||||
|
||||
enum RemoteActions
|
||||
{
|
||||
@@ -19,12 +20,14 @@ enum RemoteActions
|
||||
REMOTE_ACTION_INSTALL = 256,
|
||||
REMOTE_ACTION_EDIT = 512,
|
||||
REMOTE_ACTION_NEW_FILE = 1024,
|
||||
REMOTE_ACTION_ALL = 2047
|
||||
REMOTE_ACTION_EXTRACT = 2048,
|
||||
REMOTE_ACTION_ALL = 4095
|
||||
};
|
||||
|
||||
enum ClientType
|
||||
{
|
||||
CLIENT_TYPE_FTP,
|
||||
CLIENT_TYPE_SFTP,
|
||||
CLIENT_TYPE_SMB,
|
||||
CLIENT_TYPE_WEBDAV,
|
||||
CLIENT_TYPE_HTTP_SERVER,
|
||||
@@ -33,6 +36,8 @@ enum ClientType
|
||||
CLINET_TYPE_UNKNOWN
|
||||
};
|
||||
|
||||
using namespace httplib;
|
||||
|
||||
class RemoteClient
|
||||
{
|
||||
public:
|
||||
@@ -50,6 +55,7 @@ public:
|
||||
virtual int Move(const std::string &from, const std::string &to) = 0;
|
||||
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 bool FileExists(const std::string &path) = 0;
|
||||
virtual std::vector<DirEntry> ListDir(const std::string &path) = 0;
|
||||
virtual std::string GetPath(std::string path1, std::string path2) = 0;
|
||||
|
||||
@@ -255,6 +255,7 @@ int SFTPClient::Size(const std::string &path, int64_t *size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
*size = attrs.filesize;
|
||||
return 1;
|
||||
}
|
||||
@@ -296,12 +297,54 @@ int SFTPClient::Get(const std::string &outputfile, const std::string &path, uint
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
free((char *)buff);
|
||||
FS::Close(out);
|
||||
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);
|
||||
if (!sftp_handle)
|
||||
{
|
||||
sprintf(response, "Unable to open file with SFTP: %ld", libssh2_sftp_last_error(sftp_session));
|
||||
return 0;
|
||||
}
|
||||
|
||||
libssh2_sftp_seek64(sftp_handle, offset);
|
||||
|
||||
char *buff = (char *)malloc(FTP_CLIENT_BUFSIZ);
|
||||
int rc, count = 0;
|
||||
size_t bytes_remaining = size;
|
||||
do
|
||||
{
|
||||
size_t bytes_to_read = std::min<size_t>(FTP_CLIENT_BUFSIZ, bytes_remaining);
|
||||
rc = libssh2_sftp_read(sftp_handle, buff, bytes_to_read);
|
||||
if (rc > 0)
|
||||
{
|
||||
bytes_remaining -= rc;
|
||||
bool ok = sink.write(buff, rc);
|
||||
if (!ok)
|
||||
{
|
||||
free((char *)buff);
|
||||
libssh2_sftp_close(sftp_handle);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
free((char *)buff);
|
||||
libssh2_sftp_close(sftp_handle);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int SFTPClient::GetRange(const std::string &path, void *buffer, uint64_t size, uint64_t offset)
|
||||
{
|
||||
int64_t filesize;
|
||||
@@ -601,7 +644,7 @@ int SFTPClient::Quit()
|
||||
|
||||
ClientType SFTPClient::clientType()
|
||||
{
|
||||
return CLIENT_TYPE_FTP;
|
||||
return CLIENT_TYPE_SFTP;
|
||||
}
|
||||
|
||||
uint32_t SFTPClient::SupportedActions()
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "clients/remote_client.h"
|
||||
#include "http/httplib.h"
|
||||
#include "common.h"
|
||||
|
||||
class SFTPClient : public RemoteClient
|
||||
@@ -20,6 +21,7 @@ public:
|
||||
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);
|
||||
|
||||
@@ -34,7 +34,14 @@ int SmbClient::Connect(const std::string &url, const std::string &user, const st
|
||||
sprintf(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");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pass.length() > 0)
|
||||
smb2_set_password(smb2, pass.c_str());
|
||||
smb2_set_security_mode(smb2, SMB2_NEGOTIATE_SIGNING_ENABLED);
|
||||
@@ -226,6 +233,48 @@ int SmbClient::Get(const std::string &outputfile, const std::string &ppath, uint
|
||||
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);
|
||||
if (in == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
free((char *)buff);
|
||||
smb2_close(smb2, in);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int SmbClient::GetRange(const std::string &ppath, void *buffer, uint64_t size, uint64_t offset)
|
||||
{
|
||||
std::string path = std::string(ppath);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <vector>
|
||||
#include <smb2/smb2.h>
|
||||
#include <smb2/libsmb2.h>
|
||||
#include "http/httplib.h"
|
||||
#include "clients/remote_client.h"
|
||||
#include "common.h"
|
||||
|
||||
@@ -24,6 +25,7 @@ public:
|
||||
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);
|
||||
|
||||
@@ -215,6 +215,11 @@ namespace WebDAV
|
||||
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
|
||||
*
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
#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)
|
||||
@@ -29,6 +32,7 @@ namespace WebDAV
|
||||
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);
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
#include <lexbor/html/parser.h>
|
||||
#include <lexbor/dom/interfaces/element.h>
|
||||
|
||||
#define HTTP_SUCCESS(x) (x >= 200 && x < 300)
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
@@ -86,4 +88,44 @@ struct DirEntry
|
||||
}
|
||||
};
|
||||
|
||||
static lxb_dom_node_t *NextChildElement(lxb_dom_element_t *element)
|
||||
{
|
||||
lxb_dom_node_t *node = element->node.first_child;
|
||||
while (node != nullptr && node->type != LXB_DOM_NODE_TYPE_ELEMENT)
|
||||
{
|
||||
node = node->next;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static lxb_dom_node_t *NextElement(lxb_dom_node_t *node)
|
||||
{
|
||||
lxb_dom_node_t *next = node->next;
|
||||
while (next != nullptr && next->type != LXB_DOM_NODE_TYPE_ELEMENT)
|
||||
{
|
||||
next = next->next;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
static lxb_dom_node_t *NextChildTextNode(lxb_dom_element_t *element)
|
||||
{
|
||||
lxb_dom_node_t *node = element->node.first_child;
|
||||
while (node != nullptr && node->type != LXB_DOM_NODE_TYPE_TEXT)
|
||||
{
|
||||
node = node->next;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static lxb_dom_node_t *NextTextNode(lxb_dom_node_t *node)
|
||||
{
|
||||
lxb_dom_node_t *next = node->next;
|
||||
while (next != nullptr && next->type != LXB_DOM_NODE_TYPE_TEXT)
|
||||
{
|
||||
next = next->next;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
#endif
|
||||
+81
-8
@@ -25,6 +25,7 @@ char remote_directory[255];
|
||||
char app_ver[6];
|
||||
char last_site[32];
|
||||
char display_site[32];
|
||||
std::vector<std::string> langs;
|
||||
char language[128];
|
||||
std::vector<std::string> sites;
|
||||
std::vector<std::string> http_servers;
|
||||
@@ -37,6 +38,9 @@ bool auto_delete_tmp_pkg;
|
||||
int max_edit_file_size;
|
||||
GoogleAppInfo gg_app;
|
||||
bool show_hidden_files;
|
||||
char alldebrid_api_key[64];
|
||||
char realdebrid_api_key[64];
|
||||
char temp_folder[256];
|
||||
|
||||
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'};
|
||||
@@ -82,10 +86,14 @@ namespace CONFIG
|
||||
{
|
||||
setting->type = CLIENT_TYPE_SMB;
|
||||
}
|
||||
else if (strncmp(setting->server, "ftp://", 6) == 0 || strncmp(setting->server, "sftp://", 7) == 0)
|
||||
else if (strncmp(setting->server, "ftp://", 6) == 0)
|
||||
{
|
||||
setting->type = CLIENT_TYPE_FTP;
|
||||
}
|
||||
else if (strncmp(setting->server, "sftp://", 7) == 0)
|
||||
{
|
||||
setting->type = CLIENT_TYPE_SFTP;
|
||||
}
|
||||
else if (strncmp(setting->server, "webdav://", 9) == 0 || strncmp(setting->server, "webdavs://", 10) == 0)
|
||||
{
|
||||
setting->type = CLIENT_TYPE_WEBDAV;
|
||||
@@ -147,7 +155,11 @@ namespace CONFIG
|
||||
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"};
|
||||
|
||||
http_servers = {HTTP_SERVER_APACHE, HTTP_SERVER_MS_IIS, HTTP_SERVER_NGINX, HTTP_SERVER_NPX_SERVE};
|
||||
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"};
|
||||
|
||||
http_servers = {HTTP_SERVER_APACHE, HTTP_SERVER_MS_IIS, HTTP_SERVER_NGINX, HTTP_SERVER_NPX_SERVE, HTTP_SERVER_RCLONE};
|
||||
text_file_extensions = { ".txt", ".ini", ".log", ".json", ".xml", ".html", ".xhtml", ".conf", ".config" };
|
||||
image_file_extensions = { ".bmp", ".jpg", ".jpeg", ".png", ".webp" };
|
||||
|
||||
@@ -162,7 +174,7 @@ namespace CONFIG
|
||||
WriteInt(CONFIG_GLOBAL, CONFIG_VERSION, CONFIG_VERSION_NUM);
|
||||
|
||||
// Load global config
|
||||
sprintf(language, "%s", ReadString(CONFIG_GLOBAL, CONFIG_LANGUAGE, ""));
|
||||
sprintf(language, "%s", ReadString(CONFIG_GLOBAL, CONFIG_LANGUAGE, "Default"));
|
||||
WriteString(CONFIG_GLOBAL, CONFIG_LANGUAGE, language);
|
||||
|
||||
sprintf(local_directory, "%s", ReadString(CONFIG_GLOBAL, CONFIG_LOCAL_DIRECTORY, "/"));
|
||||
@@ -177,6 +189,45 @@ namespace CONFIG
|
||||
show_hidden_files = ReadBool(CONFIG_GLOBAL, CONFIG_SHOW_HIDDEN_FILES, false);
|
||||
WriteBool(CONFIG_GLOBAL, CONFIG_SHOW_HIDDEN_FILES, show_hidden_files);
|
||||
|
||||
sprintf(temp_folder, ReadString(CONFIG_GLOBAL, CONFIG_TMP_FOLDER_PATH, TMP_FOLDER_PATH));
|
||||
WriteString(CONFIG_GLOBAL, CONFIG_TMP_FOLDER_PATH, temp_folder);
|
||||
|
||||
if (!FS::FolderExists(temp_folder))
|
||||
{
|
||||
FS::MkDirs(temp_folder);
|
||||
}
|
||||
|
||||
// alldebrid api key
|
||||
char tmp_api_key[512];
|
||||
sprintf(tmp_api_key, "%s", ReadString(CONFIG_GLOBAL, CONFIG_ALLDEBRID_API_KEY, ""));
|
||||
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);
|
||||
if (ret == 0)
|
||||
sprintf(alldebrid_api_key, "%s", tmp_api_key);
|
||||
else
|
||||
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);
|
||||
@@ -244,10 +295,7 @@ namespace CONFIG
|
||||
}
|
||||
WriteString(sites[i].c_str(), CONFIG_REMOTE_SERVER_PASSWORD, encrypted_password.c_str());
|
||||
|
||||
setting.http_port = ReadInt(sites[i].c_str(), CONFIG_REMOTE_SERVER_HTTP_PORT, 80);
|
||||
WriteInt(sites[i].c_str(), CONFIG_REMOTE_SERVER_HTTP_PORT, setting.http_port);
|
||||
|
||||
setting.enable_rpi = ReadBool(sites[i].c_str(), CONFIG_ENABLE_RPI, false);
|
||||
setting.enable_rpi = ReadBool(sites[i].c_str(), CONFIG_ENABLE_RPI, true);
|
||||
WriteBool(sites[i].c_str(), CONFIG_ENABLE_RPI, setting.enable_rpi);
|
||||
|
||||
sprintf(setting.http_server_type, "%s", ReadString(sites[i].c_str(), CONFIG_REMOTE_HTTP_SERVER_TYPE, HTTP_SERVER_APACHE));
|
||||
@@ -322,7 +370,6 @@ namespace CONFIG
|
||||
WriteString(last_site, CONFIG_REMOTE_SERVER_URL, remote_settings->server);
|
||||
WriteString(last_site, CONFIG_REMOTE_SERVER_USER, remote_settings->username);
|
||||
WriteString(last_site, CONFIG_REMOTE_SERVER_PASSWORD, encrypted_text.c_str());
|
||||
WriteInt(last_site, CONFIG_REMOTE_SERVER_HTTP_PORT, remote_settings->http_port);
|
||||
WriteBool(last_site, CONFIG_ENABLE_RPI, remote_settings->enable_rpi);
|
||||
WriteString(last_site, CONFIG_REMOTE_HTTP_SERVER_TYPE, remote_settings->http_server_type);
|
||||
WriteString(last_site, CONFIG_REMOTE_DEFAULT_DIRECTORY, remote_settings->default_directory);
|
||||
@@ -355,17 +402,43 @@ namespace CONFIG
|
||||
Encrypt(gg_app.client_secret, encrypted_secret);
|
||||
else
|
||||
encrypted_secret = std::string(gg_app.client_secret);
|
||||
|
||||
std::string encrypted_api_key;
|
||||
if (strlen(alldebrid_api_key) > 0)
|
||||
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_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);
|
||||
WriteString(CONFIG_GLOBAL, CONFIG_LANGUAGE, language);
|
||||
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);
|
||||
|
||||
WriteIniFile(CONFIG_INI_FILE);
|
||||
CloseIniFile();
|
||||
|
||||
if (!FS::FolderExists(temp_folder))
|
||||
{
|
||||
FS::MkDirs(temp_folder);
|
||||
}
|
||||
|
||||
if (!FS::FolderExists(compressed_file_path))
|
||||
{
|
||||
FS::MkDirs(compressed_file_path);
|
||||
}
|
||||
}
|
||||
|
||||
void SaveLocalDirecotry(const std::string &path)
|
||||
|
||||
+10
-1
@@ -17,6 +17,7 @@
|
||||
#define TMP_EDITOR_FILE DATA_PATH "/tmp_editor.txt"
|
||||
#define TMP_SFO_PATH DATA_PATH "/tmp_pkg.sfo"
|
||||
#define TMP_ICON_PATH DATA_PATH "/tmp_icon.png"
|
||||
#define TMP_FOLDER_PATH DATA_PATH "/tmp"
|
||||
|
||||
#define CONFIG_GLOBAL "Global"
|
||||
|
||||
@@ -57,6 +58,9 @@
|
||||
#define CONFIG_REMOTE_HTTP_SERVER_TYPE "remote_server_http_server_type"
|
||||
#define CONFIG_REMOTE_DEFAULT_DIRECTORY "remote_server_default_directory"
|
||||
|
||||
#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
|
||||
|
||||
@@ -68,6 +72,7 @@
|
||||
#define CONFIG_AUTO_DELETE_TMP_PKG "auto_delete_tmp_pkg"
|
||||
|
||||
#define CONFIG_LOCAL_DIRECTORY "local_directory"
|
||||
#define CONFIG_TMP_FOLDER_PATH "temp_folder"
|
||||
|
||||
#define CONFIG_LANGUAGE "language"
|
||||
|
||||
@@ -75,6 +80,7 @@
|
||||
#define HTTP_SERVER_MS_IIS "Microsoft IIS"
|
||||
#define HTTP_SERVER_NGINX "Nginx"
|
||||
#define HTTP_SERVER_NPX_SERVE "Serve"
|
||||
#define HTTP_SERVER_RCLONE "RClone"
|
||||
|
||||
#define MAX_EDIT_FILE_SIZE 32768
|
||||
|
||||
@@ -98,7 +104,6 @@ struct RemoteSettings
|
||||
char server[256];
|
||||
char username[33];
|
||||
char password[128];
|
||||
int http_port;
|
||||
ClientType type;
|
||||
bool enable_rpi;
|
||||
uint32_t supported_actions;
|
||||
@@ -124,6 +129,7 @@ extern char remote_directory[255];
|
||||
extern char app_ver[6];
|
||||
extern char last_site[32];
|
||||
extern char display_site[32];
|
||||
extern std::vector<std::string> langs;
|
||||
extern char language[128];
|
||||
extern RemoteSettings *remote_settings;
|
||||
extern RemoteClient *remoteclient;
|
||||
@@ -135,6 +141,9 @@ 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[64];
|
||||
extern char realdebrid_api_key[64];
|
||||
extern char temp_folder[256];
|
||||
|
||||
namespace CONFIG
|
||||
{
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
#include <regex>
|
||||
#include <lexbor/html/parser.h>
|
||||
#include <lexbor/dom/interfaces/element.h>
|
||||
#include <http/httplib.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "1fichier.h"
|
||||
|
||||
#define VALIDATION_REGEX "https:\\/\\/1fichier\\.com\\/(.*)"
|
||||
|
||||
FichierHost::FichierHost(const std::string &url) : FileHost(url)
|
||||
{
|
||||
}
|
||||
|
||||
bool FichierHost::IsValidUrl()
|
||||
{
|
||||
std::regex regex(VALIDATION_REGEX);
|
||||
|
||||
if (std::regex_match(url, regex))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string FichierHost::GetDownloadUrl()
|
||||
{
|
||||
std::regex re("https:\\/\\/1fichier\\.com");
|
||||
std::string path = std::regex_replace(url, re, "");
|
||||
|
||||
httplib::Client tmp_client("https://1fichier.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);
|
||||
|
||||
auto res = tmp_client.Get(path);
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
lxb_status_t status;
|
||||
lxb_dom_element_t *element;
|
||||
lxb_dom_node_t *node;
|
||||
lxb_html_document_t *document;
|
||||
lxb_dom_collection_t *collection;
|
||||
lxb_dom_attr_t *attr;
|
||||
const lxb_char_t *value;
|
||||
size_t value_len;
|
||||
std::string download_url = "";
|
||||
|
||||
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)
|
||||
return "";
|
||||
collection = lxb_dom_collection_make(&document->dom_document, 128);
|
||||
if (collection == NULL)
|
||||
{
|
||||
lxb_html_document_destroy(document);
|
||||
return "";
|
||||
}
|
||||
|
||||
status = lxb_dom_elements_by_tag_name(lxb_dom_interface_element(document->body),
|
||||
collection, (const lxb_char_t *)"input", 5);
|
||||
if (status != LXB_STATUS_OK)
|
||||
{
|
||||
lxb_dom_collection_destroy(collection, true);
|
||||
lxb_html_document_destroy(document);
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string post_data;
|
||||
for (size_t i = 0; i < lxb_dom_collection_length(collection); i++)
|
||||
{
|
||||
element = lxb_dom_collection_element(collection, i);
|
||||
value = lxb_dom_element_get_attribute(element, (const lxb_char_t *)"name", 4, &value_len);
|
||||
if (value != nullptr)
|
||||
{
|
||||
std::string name_attr((char *)value, value_len);
|
||||
if (name_attr == "adz")
|
||||
{
|
||||
value = lxb_dom_element_get_attribute(element, (const lxb_char_t *)"value", 5, &value_len);
|
||||
std::string adz_value((char *)value, value_len);
|
||||
post_data = std::string("adz=") + adz_value + "&did=0&dl_no_ssl=off&dlinline=on";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
lxb_dom_collection_destroy(collection, true);
|
||||
lxb_html_document_destroy(document);
|
||||
|
||||
if (auto res = tmp_client.Post(path, post_data.c_str(), post_data.length(), "application/x-www-form-urlencoded"))
|
||||
{
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
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)
|
||||
return "";
|
||||
|
||||
collection = lxb_dom_collection_make(&document->dom_document, 128);
|
||||
if (collection == NULL)
|
||||
{
|
||||
lxb_html_document_destroy(document);
|
||||
return "";
|
||||
}
|
||||
|
||||
status = lxb_dom_elements_by_tag_name(lxb_dom_interface_element(document->body),
|
||||
collection, (const lxb_char_t *)"a", 1);
|
||||
if (status != LXB_STATUS_OK)
|
||||
{
|
||||
lxb_dom_collection_destroy(collection, true);
|
||||
lxb_html_document_destroy(document);
|
||||
return "";
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < lxb_dom_collection_length(collection); i++)
|
||||
{
|
||||
element = lxb_dom_collection_element(collection, i);
|
||||
value = lxb_dom_element_get_attribute(element, (const lxb_char_t *)"class", 5, &value_len);
|
||||
if (value != nullptr)
|
||||
{
|
||||
std::string class_value((char*) value, value_len);
|
||||
if (class_value == "ok btn-general btn-orange")
|
||||
{
|
||||
value = lxb_dom_element_get_attribute(element, (const lxb_char_t *)"href", 4, &value_len);
|
||||
if (value != nullptr)
|
||||
{
|
||||
download_url = std::string((char*) value, value_len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
lxb_dom_collection_destroy(collection, true);
|
||||
lxb_html_document_destroy(document);
|
||||
}
|
||||
}
|
||||
|
||||
return download_url;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef FICHIER_HOST_H
|
||||
#define FICHIER_HOST_H
|
||||
|
||||
#include "filehost.h"
|
||||
|
||||
class FichierHost : public FileHost
|
||||
{
|
||||
public:
|
||||
FichierHost(const std::string &url);
|
||||
bool IsValidUrl();
|
||||
std::string GetDownloadUrl();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
#include <http/httplib.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "alldebrid.h"
|
||||
|
||||
AllDebridHost::AllDebridHost(const std::string &url) : FileHost(url)
|
||||
{
|
||||
}
|
||||
|
||||
bool AllDebridHost::IsValidUrl()
|
||||
{
|
||||
httplib::Client tmp_client("https://api.alldebrid.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);
|
||||
|
||||
std::string path = std::string("/v4/link/unlock?agent=ezRemoteClient&apikey=") + alldebrid_api_key + "&link=" + httplib::detail::encode_url(url);
|
||||
auto res = tmp_client.Get(path);
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
json_object *jobj = json_tokener_parse(res->body.c_str());
|
||||
const char *status = json_object_get_string(json_object_object_get(jobj, "status"));
|
||||
|
||||
if (strcmp(status, "success") == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string AllDebridHost::GetDownloadUrl()
|
||||
{
|
||||
httplib::Client tmp_client("https://api.alldebrid.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);
|
||||
|
||||
std::string path = std::string("/v4/link/unlock?agent=ezRemoteClient&apikey=") + alldebrid_api_key + "&link=" + httplib::detail::encode_url(url);
|
||||
auto res = tmp_client.Get(path);
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
json_object *jobj = json_tokener_parse(res->body.c_str());
|
||||
const char *status = json_object_get_string(json_object_object_get(jobj, "status"));
|
||||
|
||||
if (status != nullptr && strcmp(status, "success") == 0)
|
||||
{
|
||||
json_object *data = json_object_object_get(jobj, "data");
|
||||
const char *link = json_object_get_string(json_object_object_get(data, "link"));
|
||||
return std::string(link);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef ALLDEBRID_HOST_H
|
||||
#define ALLDEBRID_HOST_H
|
||||
|
||||
#include "filehost.h"
|
||||
|
||||
class AllDebridHost : public FileHost
|
||||
{
|
||||
public:
|
||||
AllDebridHost(const std::string &url);
|
||||
bool IsValidUrl();
|
||||
std::string GetDownloadUrl();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <regex>
|
||||
#include <lexbor/html/parser.h>
|
||||
#include <lexbor/dom/interfaces/element.h>
|
||||
#include <http/httplib.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "directhost.h"
|
||||
|
||||
|
||||
#define VALIDATION_REGEX "(.*)"
|
||||
|
||||
DirectHost::DirectHost(const std::string &url) : FileHost(url)
|
||||
{
|
||||
}
|
||||
|
||||
bool DirectHost::IsValidUrl()
|
||||
{
|
||||
std::regex regex_1(VALIDATION_REGEX);
|
||||
|
||||
if (std::regex_match(url, regex_1))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string DirectHost::GetDownloadUrl()
|
||||
{
|
||||
return url;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef DIRECT_HOST_H
|
||||
#define DIRECT_HOST_H
|
||||
|
||||
#include "filehost.h"
|
||||
|
||||
class DirectHost : public FileHost
|
||||
{
|
||||
public:
|
||||
DirectHost(const std::string &url);
|
||||
bool IsValidUrl();
|
||||
std::string GetDownloadUrl();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,60 @@
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#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"
|
||||
#include "filehost/pixeldrain.h"
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
|
||||
#define GDRIVE_REGEX "https:\\/\\/drive\\.google\\.com\\/(.*)"
|
||||
#define MEDIAFIRE_REGEX "https:\\/\\/www\\.mediafire\\.com\\/(.*)"
|
||||
#define PIXELDRAIN_REGEX "https:\\/\\/pixeldrain\\.com\\/(.*)"
|
||||
#define FICHIER_REGEX "https:\\/\\/1fichier\\.com\\/(.*)"
|
||||
|
||||
static std::map<std::string, std::string> cache_downloal_urls;
|
||||
|
||||
std::string FileHost::GetUrl()
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
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);
|
||||
std::regex pixeldrain_re(PIXELDRAIN_REGEX);
|
||||
std::regex fichier_re(FICHIER_REGEX);
|
||||
|
||||
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))
|
||||
return new MediaFireHost(url);
|
||||
else if (std::regex_match(url, pixeldrain_re))
|
||||
return new PixelDrainHost(url);
|
||||
else
|
||||
return new DirectHost(url);
|
||||
}
|
||||
|
||||
std::string FileHost::GetCachedDownloadUrl(std::string &hash)
|
||||
{
|
||||
return cache_downloal_urls[hash];
|
||||
}
|
||||
|
||||
void FileHost::AddCacheDownloadUrl(std::string &hash, std::string &url)
|
||||
{
|
||||
std::pair<std::string, std::string> pair = std::make_pair(hash, url);
|
||||
cache_downloal_urls.erase(hash);
|
||||
cache_downloal_urls.insert(pair);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef FILEHOST_H
|
||||
#define FILEHOST_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class FileHost
|
||||
{
|
||||
public:
|
||||
FileHost(const std::string &url) { this->url = url; };
|
||||
virtual ~FileHost(){};
|
||||
virtual bool IsValidUrl() = 0;
|
||||
virtual std::string GetDownloadUrl() = 0;
|
||||
std::string GetUrl();
|
||||
|
||||
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);
|
||||
|
||||
protected:
|
||||
std::string url;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,118 @@
|
||||
#include <regex>
|
||||
#include <lexbor/html/parser.h>
|
||||
#include <lexbor/dom/interfaces/element.h>
|
||||
#include <http/httplib.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "gdrive.h"
|
||||
|
||||
#define VALIDATION_REGEX_1 "https:\\/\\/drive\\.google\\.com\\/file\\/d\\/(.*)\\/(edit|view)\\?usp=(.*)"
|
||||
#define VALIDATION_REGEX_2 "https:\\/\\/drive\\.google\\.com\\/(.*)uc\\?(id=|export=)(.*)&(id=|export=)(.*)"
|
||||
|
||||
GDriveHost::GDriveHost(const std::string &url) : FileHost(url)
|
||||
{
|
||||
}
|
||||
|
||||
bool GDriveHost::IsValidUrl()
|
||||
{
|
||||
std::regex regex_1(VALIDATION_REGEX_1);
|
||||
std::regex regex_2(VALIDATION_REGEX_2);
|
||||
|
||||
if (std::regex_match(url, regex_1) || std::regex_match(url, regex_2))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GDriveHost::GetDownloadUrl()
|
||||
{
|
||||
std::regex regex_1(VALIDATION_REGEX_1);
|
||||
std::smatch matches;
|
||||
|
||||
std::string path;
|
||||
if(std::regex_search(url, matches, regex_1))
|
||||
{
|
||||
path = std::string("/uc?export=download&id=") + matches[1].str();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::regex re("https:\\/\\/drive\\.google\\.com");
|
||||
path = std::regex_replace(url, re, "");
|
||||
}
|
||||
|
||||
httplib::Client tmp_client("https://drive.google.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);
|
||||
|
||||
auto res = tmp_client.Head(path);
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
std::string content_type = res->get_header_value("Content-Type");
|
||||
if (content_type == "application/octet-stream")
|
||||
return url;
|
||||
else if (content_type.find("text/html") == std::string::npos)
|
||||
return "";
|
||||
}
|
||||
else
|
||||
return "";
|
||||
|
||||
res = tmp_client.Get(path);
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
lxb_status_t status;
|
||||
lxb_dom_element_t *element;
|
||||
lxb_html_document_t *document;
|
||||
lxb_dom_collection_t *collection;
|
||||
lxb_dom_attr_t *attr;
|
||||
|
||||
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);
|
||||
return "";
|
||||
}
|
||||
|
||||
collection = lxb_dom_collection_make(&document->dom_document, 128);
|
||||
if (collection == NULL)
|
||||
{
|
||||
lxb_html_document_destroy(document);
|
||||
return "";
|
||||
}
|
||||
|
||||
status = lxb_dom_elements_by_tag_name(lxb_dom_interface_element(document->body),
|
||||
collection, (const lxb_char_t *)"form", 4);
|
||||
if (status != LXB_STATUS_OK)
|
||||
{
|
||||
lxb_dom_collection_destroy(collection, true);
|
||||
lxb_html_document_destroy(document);
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string download_url;
|
||||
for (size_t i = 0; i < lxb_dom_collection_length(collection); i++)
|
||||
{
|
||||
element = lxb_dom_collection_element(collection, i);
|
||||
if (element->attr_id != nullptr)
|
||||
{
|
||||
std::string form_id((char *)element->attr_id->value->data, element->attr_id->value->length);
|
||||
if (form_id == "download-form")
|
||||
{
|
||||
size_t value_len;
|
||||
const lxb_char_t *value = lxb_dom_element_get_attribute(element, (const lxb_char_t *)"action", 6, &value_len);
|
||||
download_url = std::string((char *)value, value_len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lxb_dom_collection_destroy(collection, true);
|
||||
lxb_html_document_destroy(document);
|
||||
|
||||
return download_url;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef GDRIVE_HOST_H
|
||||
#define GDRIVE_HOST_H
|
||||
|
||||
#include "filehost.h"
|
||||
|
||||
class GDriveHost : public FileHost
|
||||
{
|
||||
public:
|
||||
GDriveHost(const std::string &url);
|
||||
bool IsValidUrl();
|
||||
std::string GetDownloadUrl();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,89 @@
|
||||
#include <regex>
|
||||
#include <lexbor/html/parser.h>
|
||||
#include <lexbor/dom/interfaces/element.h>
|
||||
#include <http/httplib.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "mediafire.h"
|
||||
|
||||
#define VALIDATION_REGEX "https:\\/\\/www\\.mediafire\\.com\\/file\\/(.*)\\/(.*)\\/file"
|
||||
|
||||
MediaFireHost::MediaFireHost(const std::string &url) : FileHost(url)
|
||||
{
|
||||
}
|
||||
|
||||
bool MediaFireHost::IsValidUrl()
|
||||
{
|
||||
std::regex regex(VALIDATION_REGEX);
|
||||
|
||||
if (std::regex_match(url, regex))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string MediaFireHost::GetDownloadUrl()
|
||||
{
|
||||
std::regex re("https:\\/\\/www\\.mediafire\\.com");
|
||||
std::string path = std::regex_replace(url, re, "");
|
||||
|
||||
httplib::Client tmp_client("https://www.mediafire.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);
|
||||
|
||||
auto res = tmp_client.Get(path);
|
||||
if (HTTP_SUCCESS(res->status))
|
||||
{
|
||||
lxb_status_t status;
|
||||
lxb_dom_element_t *element;
|
||||
lxb_html_document_t *document;
|
||||
lxb_dom_collection_t *collection;
|
||||
lxb_dom_attr_t *attr;
|
||||
|
||||
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)
|
||||
return "";
|
||||
collection = lxb_dom_collection_make(&document->dom_document, 128);
|
||||
if (collection == NULL)
|
||||
{
|
||||
lxb_html_document_destroy(document);
|
||||
return "";
|
||||
}
|
||||
|
||||
status = lxb_dom_elements_by_tag_name(lxb_dom_interface_element(document->body),
|
||||
collection, (const lxb_char_t *)"a", 1);
|
||||
if (status != LXB_STATUS_OK)
|
||||
{
|
||||
lxb_dom_collection_destroy(collection, true);
|
||||
lxb_html_document_destroy(document);
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string download_url;
|
||||
for (size_t i = 0; i < lxb_dom_collection_length(collection); i++)
|
||||
{
|
||||
element = lxb_dom_collection_element(collection, i);
|
||||
if (element->attr_id != nullptr)
|
||||
{
|
||||
std::string a_id((char *)element->attr_id->value->data, element->attr_id->value->length);
|
||||
|
||||
if (a_id == "downloadButton")
|
||||
{
|
||||
size_t value_len;
|
||||
const lxb_char_t *value = lxb_dom_element_get_attribute(element, (const lxb_char_t *)"href", 4, &value_len);
|
||||
download_url = std::string((char *)value, value_len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
lxb_dom_collection_destroy(collection, true);
|
||||
lxb_html_document_destroy(document);
|
||||
|
||||
return download_url;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef MEDIAFIRE_HOST_H
|
||||
#define MEDIAFIRE_HOST_H
|
||||
|
||||
#include "filehost.h"
|
||||
|
||||
class MediaFireHost : public FileHost
|
||||
{
|
||||
public:
|
||||
MediaFireHost(const std::string &url);
|
||||
bool IsValidUrl();
|
||||
std::string GetDownloadUrl();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
#include <regex>
|
||||
#include <lexbor/html/parser.h>
|
||||
#include <lexbor/dom/interfaces/element.h>
|
||||
#include <http/httplib.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "pixeldrain.h"
|
||||
|
||||
|
||||
#define VALIDATION_REGEX "https:\\/\\/pixeldrain\\.com\\/u\\/(.*)"
|
||||
|
||||
PixelDrainHost::PixelDrainHost(const std::string &url) : FileHost(url)
|
||||
{
|
||||
}
|
||||
|
||||
bool PixelDrainHost::IsValidUrl()
|
||||
{
|
||||
std::regex re(VALIDATION_REGEX);
|
||||
|
||||
if (std::regex_match(url, re))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string PixelDrainHost::GetDownloadUrl()
|
||||
{
|
||||
std::regex re(VALIDATION_REGEX);
|
||||
std::smatch matches;
|
||||
|
||||
if(std::regex_search(url, matches, re))
|
||||
{
|
||||
if (matches.size() > 1)
|
||||
{
|
||||
return std::string("https://pixeldrain.com/api/file/") + matches[1].str() + "?download=";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef PIXELDRAIN_HOST_H
|
||||
#define PIXELDRAIN_HOST_H
|
||||
|
||||
#include "filehost.h"
|
||||
|
||||
class PixelDrainHost : public FileHost
|
||||
{
|
||||
public:
|
||||
PixelDrainHost(const std::string &url);
|
||||
bool IsValidUrl();
|
||||
std::string GetDownloadUrl();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -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 REALDEBRID_HOST_H
|
||||
#define REALDEBRID_HOST_H
|
||||
|
||||
#include "filehost.h"
|
||||
|
||||
class RealDebridHost : public FileHost
|
||||
{
|
||||
public:
|
||||
RealDebridHost(const std::string &url);
|
||||
bool IsValidUrl();
|
||||
std::string GetDownloadUrl();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -353,7 +353,7 @@ private:
|
||||
|
||||
} // namespace detail
|
||||
|
||||
using Headers = std::multimap<std::string, std::string, detail::ci>;
|
||||
using Headers = std::map<std::string, std::string, detail::ci>;
|
||||
|
||||
using Params = std::multimap<std::string, std::string>;
|
||||
using Match = std::smatch;
|
||||
@@ -1900,6 +1900,7 @@ namespace detail {
|
||||
|
||||
std::string encode_query_param(const std::string &value);
|
||||
|
||||
std::string encode_url(const std::string &s);
|
||||
std::string decode_url(const std::string &s, bool convert_plus_to_space);
|
||||
|
||||
void read_file(const std::string &path, std::string &out);
|
||||
|
||||
@@ -10692,7 +10692,7 @@ void ImGui::NavUpdateCreateMoveRequest()
|
||||
// When using gamepad, we project the reference nav bounding box into window visible area.
|
||||
// This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad all movements are relative
|
||||
// (can't focus a visible object like we can with the mouse).
|
||||
if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))
|
||||
/*if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))
|
||||
{
|
||||
bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0;
|
||||
bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0;
|
||||
@@ -10709,7 +10709,7 @@ void ImGui::NavUpdateCreateMoveRequest()
|
||||
window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel);
|
||||
g.NavId = g.NavFocusScopeId = 0;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)
|
||||
ImRect scoring_rect;
|
||||
|
||||
+424
-100
@@ -9,9 +9,11 @@
|
||||
#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 "server/http_server.h"
|
||||
#include "installer.h"
|
||||
#include "util.h"
|
||||
#include "config.h"
|
||||
@@ -19,14 +21,25 @@
|
||||
#include "lang.h"
|
||||
#include "system.h"
|
||||
#include "fs.h"
|
||||
#include "sfo.h"
|
||||
#include "clients/webdavclient.h"
|
||||
#include "clients/remote_client.h"
|
||||
|
||||
#define BGFT_HEAP_SIZE (1 * 1024 * 1024)
|
||||
|
||||
struct BgProgressCheck
|
||||
{
|
||||
ArchivePkgInstallData* 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;
|
||||
|
||||
namespace INSTALLER
|
||||
{
|
||||
int Init(void)
|
||||
@@ -97,11 +110,102 @@ namespace INSTALLER
|
||||
s_bgft_initialized = false;
|
||||
}
|
||||
|
||||
std::string getRemoteUrl(const std::string filename, bool encodeUrl)
|
||||
{
|
||||
if (remoteclient->clientType() == CLIENT_TYPE_WEBDAV || remoteclient->clientType() == CLIENT_TYPE_HTTP_SERVER)
|
||||
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 "";
|
||||
}
|
||||
|
||||
pkg_table_entry *entries = (pkg_table_entry *)entry_table_data;
|
||||
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)
|
||||
{
|
||||
std::string full_url = WebDAV::GetHttpUrl(remote_settings->server + filename);
|
||||
if (BE32(entries[i].id) == PKG_ENTRY_ID__PARAM_SFO)
|
||||
{
|
||||
param_sfo_offset = BE32(entries[i].offset);
|
||||
param_sfo_size = BE32(entries[i].size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(entry_table_data);
|
||||
|
||||
std::string title;
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
std::string GetLocalPkgTitle(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);
|
||||
|
||||
FILE *fd = FS::OpenRead(path);
|
||||
FS::Seek(fd, entry_table_offset);
|
||||
FS::Read(fd, entry_table_data, entry_table_size);
|
||||
|
||||
pkg_table_entry *entries = (pkg_table_entry *)entry_table_data;
|
||||
void* param_sfo_data = NULL;
|
||||
uint32_t param_sfo_offset = 0;
|
||||
uint32_t param_sfo_size = 0;
|
||||
void *icon0_png_data = NULL;
|
||||
uint32_t icon0_png_offset = 0;
|
||||
uint32_t icon0_png_size = 0;
|
||||
for (size_t i = 0; i < entry_count; ++i)
|
||||
{
|
||||
if (BE32(entries[i].id) == PKG_ENTRY_ID__PARAM_SFO)
|
||||
{
|
||||
param_sfo_offset = BE32(entries[i].offset);
|
||||
param_sfo_size = BE32(entries[i].size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(entry_table_data);
|
||||
|
||||
std::string title;
|
||||
if (param_sfo_offset > 0 && param_sfo_size > 0)
|
||||
{
|
||||
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");
|
||||
if (tmp_title != nullptr)
|
||||
title = std::string(tmp_title);
|
||||
free(param_sfo_data);
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
std::string full_url = WebDAV::GetHttpUrl(remote_settings->server + path);
|
||||
size_t scheme_pos = full_url.find("://");
|
||||
if (scheme_pos == std::string::npos)
|
||||
return "";
|
||||
@@ -121,24 +225,17 @@ namespace INSTALLER
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string full_url = std::string(remote_settings->server);
|
||||
size_t scheme_pos = full_url.find("://");
|
||||
if (scheme_pos == std::string::npos)
|
||||
return "";
|
||||
size_t root_pos = full_url.find("/", scheme_pos + 3);
|
||||
std::string host = full_url.substr(scheme_pos + 3, (root_pos - (scheme_pos + 3)));
|
||||
size_t port_pos = host.find(":");
|
||||
if (port_pos != std::string::npos)
|
||||
host = host.substr(0, port_pos);
|
||||
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 path = std::string(filename);
|
||||
if (encodeUrl)
|
||||
{
|
||||
Web::Urn::Path uri(path);
|
||||
CURL *curl = curl_easy_init();
|
||||
path = uri.quote(curl);
|
||||
}
|
||||
return "http://" + host + ":" + std::to_string(remote_settings->http_port) + path;
|
||||
return full_url;
|
||||
}
|
||||
|
||||
return "";
|
||||
@@ -146,47 +243,18 @@ namespace INSTALLER
|
||||
|
||||
bool canInstallRemotePkg(const std::string &url)
|
||||
{
|
||||
if (remoteclient->clientType() == CLIENT_TYPE_WEBDAV)
|
||||
{
|
||||
if (strlen(remote_settings->username) > 0)
|
||||
{
|
||||
sprintf(confirm_message, "%s %s", lang_strings[STR_REMOTE_NOT_SUPPORT_MSG], lang_strings[STR_DOWNLOAD_INSTALL_MSG]);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t scheme_pos = url.find_first_of("://");
|
||||
size_t path_pos = url.find_first_of("/", scheme_pos + 3);
|
||||
std::string host = url.substr(0, path_pos);
|
||||
std::string path = url.substr(path_pos);
|
||||
|
||||
WebDAV::WebDavClient tmp_client;
|
||||
tmp_client.Connect(host.c_str(), "", "", false);
|
||||
WebDAV::dict_t response_headers{};
|
||||
int ret = tmp_client.GetHeaders(path.c_str(), &response_headers);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
sprintf(confirm_message, "%s %s", lang_strings[STR_CANNOT_CONNECT_REMOTE_MSG], lang_strings[STR_DOWNLOAD_INSTALL_MSG]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int InstallRemotePkg(const std::string &filename, pkg_header *header)
|
||||
int InstallRemotePkg(const std::string &url, pkg_header *header, std::string title, bool prompt)
|
||||
{
|
||||
std::string url = getRemoteUrl(filename, true);
|
||||
if (url.empty())
|
||||
return 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 = title.length() > 0 ? title : cid;
|
||||
int user_id;
|
||||
ret = sceUserServiceGetForegroundUser(&user_id);
|
||||
const char *package_type;
|
||||
@@ -222,6 +290,7 @@ namespace INSTALLER
|
||||
is_patch = true;
|
||||
}
|
||||
|
||||
OrbisBgftTaskProgress progress_info;
|
||||
OrbisBgftDownloadParam params;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
{
|
||||
@@ -229,7 +298,7 @@ namespace INSTALLER
|
||||
params.entitlementType = 5;
|
||||
params.id = (char *)header->pkg_content_id;
|
||||
params.contentUrl = url.c_str();
|
||||
params.contentName = cid.c_str();
|
||||
params.contentName = display_title.c_str();
|
||||
params.iconPath = "";
|
||||
params.playgoScenarioId = "0";
|
||||
params.option = ORBIS_BGFT_TASK_OPT_DISABLE_CDN_QUERY_PARAM;
|
||||
@@ -246,18 +315,30 @@ namespace INSTALLER
|
||||
ret = sceBgftServiceIntDebugDownloadRegisterPkg(¶ms, &task_id);
|
||||
if (ret == 0x80990088 || ret == 0x80990015)
|
||||
{
|
||||
sprintf(confirm_message, "%s - %s?", filename.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 (prompt)
|
||||
{
|
||||
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)
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = sceAppInstUtilAppUnInstall(cid.c_str());
|
||||
if (ret != 0)
|
||||
@@ -266,55 +347,74 @@ namespace INSTALLER
|
||||
}
|
||||
}
|
||||
else if (ret > 0)
|
||||
{
|
||||
goto err;
|
||||
|
||||
}
|
||||
ret = sceBgftServiceDownloadStartTask(task_id);
|
||||
if (ret)
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
Util::Notify("%s queued", cid.c_str());
|
||||
return 1;
|
||||
Util::Notify("%s queued", display_title.c_str());
|
||||
|
||||
if (prompt)
|
||||
{
|
||||
file_transfering = true;
|
||||
bytes_to_download = 100;
|
||||
bytes_transfered = 0;
|
||||
while (bytes_transfered < 99)
|
||||
{
|
||||
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);
|
||||
sceSystemServicePowerTick();
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
err:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int InstallLocalPkg(const std::string &filename)
|
||||
int InstallLocalPkg(const std::string &path)
|
||||
{
|
||||
int ret;
|
||||
pkg_header header;
|
||||
memset(&header, 0, sizeof(header));
|
||||
if (FS::Head(filename.c_str(), (void *)&header, sizeof(header)) == 0)
|
||||
if (FS::Head(path.c_str(), (void *)&header, sizeof(header)) == 0)
|
||||
return 0;
|
||||
|
||||
if (BE32(header.pkg_magic) != PKG_MAGIC)
|
||||
return 0;
|
||||
|
||||
char filepath[1024];
|
||||
snprintf(filepath, 1023, "%s", filename.c_str());
|
||||
if (strncmp(filename.c_str(), "/data/", 6) == 0)
|
||||
snprintf(filepath, 1023, "/user%s", filename.c_str());
|
||||
snprintf(filepath, 1023, "%s", path.c_str());
|
||||
if (strncmp(path.c_str(), "/data/", 6) == 0)
|
||||
snprintf(filepath, 1023, "/user%s", path.c_str());
|
||||
|
||||
char titleId[18];
|
||||
memset(titleId, 0, sizeof(titleId));
|
||||
int is_app = -1;
|
||||
ret = sceAppInstUtilGetTitleIdFromPkg(filename.c_str(), titleId, &is_app);
|
||||
ret = sceAppInstUtilGetTitleIdFromPkg(path.c_str(), titleId, &is_app);
|
||||
if (ret)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string title = GetLocalPkgTitle(path, &header);
|
||||
std::string display_title = title.length() > 0 ? title : std::string(titleId);
|
||||
|
||||
OrbisBgftTaskProgress progress_info;
|
||||
int prog = 0;
|
||||
OrbisBgftDownloadParamEx download_params;
|
||||
memset(&download_params, 0, sizeof(download_params));
|
||||
{
|
||||
download_params.params.entitlementType = 5;
|
||||
download_params.params.id = (char *)header.pkg_content_id;
|
||||
download_params.params.contentUrl = filepath;
|
||||
download_params.params.contentName = (char *)header.pkg_content_id;
|
||||
download_params.params.contentName = display_title.c_str();
|
||||
;
|
||||
download_params.params.iconPath = "";
|
||||
download_params.params.playgoScenarioId = "0";
|
||||
@@ -339,15 +439,19 @@ namespace INSTALLER
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
Util::Notify("%s queued", titleId);
|
||||
Util::Notify("%s queued", display_title.c_str());
|
||||
|
||||
while (prog < 99)
|
||||
file_transfering = true;
|
||||
bytes_to_download = 100;
|
||||
bytes_transfered = 0;
|
||||
while (bytes_transfered < 99)
|
||||
{
|
||||
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;
|
||||
prog = (uint32_t)(((float)progress_info.transferred / progress_info.length) * 100.f);
|
||||
bytes_transfered = (uint32_t)(((float)progress_info.transferred / progress_info.length) * 100.f);
|
||||
sceSystemServicePowerTick();
|
||||
}
|
||||
return 1;
|
||||
|
||||
@@ -355,27 +459,31 @@ namespace INSTALLER
|
||||
return 0;
|
||||
}
|
||||
|
||||
int InstallLocalPkg(const std::string &filename, pkg_header *header, bool remove_after_install)
|
||||
int InstallLocalPkg(const std::string &path, pkg_header *header, bool remove_after_install)
|
||||
{
|
||||
int ret;
|
||||
if (strncmp(filename.c_str(), "/data/", 6) != 0 &&
|
||||
strncmp(filename.c_str(), "/user/data/", 11) != 0 &&
|
||||
strncmp(filename.c_str(), "/mnt/usb", 8) != 0)
|
||||
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);
|
||||
char filepath[1024];
|
||||
snprintf(filepath, 1023, "%s", filename.c_str());
|
||||
if (strncmp(filename.c_str(), "/data/", 6) == 0)
|
||||
snprintf(filepath, 1023, "/user%s", filename.c_str());
|
||||
snprintf(filepath, 1023, "%s", path.c_str());
|
||||
if (strncmp(path.c_str(), "/data/", 6) == 0)
|
||||
snprintf(filepath, 1023, "/user%s", path.c_str());
|
||||
char titleId[18];
|
||||
memset(titleId, 0, sizeof(titleId));
|
||||
int is_app = -1;
|
||||
ret = sceAppInstUtilGetTitleIdFromPkg(filename.c_str(), titleId, &is_app);
|
||||
ret = sceAppInstUtilGetTitleIdFromPkg(path.c_str(), titleId, &is_app);
|
||||
if (ret)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string title = GetLocalPkgTitle(path, header);
|
||||
std::string display_title = title.length() > 0 ? title : std::string(titleId);
|
||||
|
||||
OrbisBgftTaskProgress progress_info;
|
||||
int prog = 0;
|
||||
OrbisBgftDownloadParamEx download_params;
|
||||
@@ -384,7 +492,7 @@ namespace INSTALLER
|
||||
download_params.params.entitlementType = 5;
|
||||
download_params.params.id = (char *)header->pkg_content_id;
|
||||
download_params.params.contentUrl = filepath;
|
||||
download_params.params.contentName = (char *)header->pkg_content_id;
|
||||
download_params.params.contentName = display_title.c_str();
|
||||
;
|
||||
download_params.params.iconPath = "";
|
||||
download_params.params.playgoScenarioId = "0";
|
||||
@@ -397,7 +505,7 @@ namespace INSTALLER
|
||||
ret = sceBgftServiceIntDownloadRegisterTaskByStorageEx(&download_params, &task_id);
|
||||
if (ret == 0x80990088 || ret == 0x80990015)
|
||||
{
|
||||
sprintf(confirm_message, "%s - %s?", filename.c_str(), lang_strings[STR_REINSTALL_CONFIRM_MSG]);
|
||||
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;
|
||||
@@ -418,7 +526,7 @@ namespace INSTALLER
|
||||
else
|
||||
{
|
||||
if (auto_delete_tmp_pkg)
|
||||
FS::Rm(filename);
|
||||
FS::Rm(path);
|
||||
}
|
||||
}
|
||||
else if (ret > 0)
|
||||
@@ -432,7 +540,7 @@ namespace INSTALLER
|
||||
|
||||
if (!remove_after_install)
|
||||
{
|
||||
Util::Notify("%s queued", titleId);
|
||||
Util::Notify("%s queued", display_title.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -450,24 +558,24 @@ namespace INSTALLER
|
||||
bytes_transfered = progress_info.transferred;
|
||||
}
|
||||
if (auto_delete_tmp_pkg)
|
||||
FS::Rm(filename);
|
||||
FS::Rm(path);
|
||||
return 1;
|
||||
|
||||
err:
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ExtractLocalPkg(const std::string &filename, const std::string sfo_path, const std::string icon_path)
|
||||
bool ExtractLocalPkg(const std::string &path, const std::string sfo_path, const std::string icon_path)
|
||||
{
|
||||
pkg_header tmp_hdr;
|
||||
FS::Head(filename, &tmp_hdr, sizeof(pkg_header));
|
||||
FS::Head(path, &tmp_hdr, sizeof(pkg_header));
|
||||
|
||||
size_t entry_count = BE32(tmp_hdr.pkg_entry_count);
|
||||
uint32_t entry_table_offset = BE32(tmp_hdr.pkg_table_offset);
|
||||
uint64_t entry_table_size = entry_count * sizeof(pkg_table_entry);
|
||||
void *entry_table_data = malloc(entry_table_size);
|
||||
|
||||
FILE *fd = FS::OpenRead(filename);
|
||||
FILE *fd = FS::OpenRead(path);
|
||||
FS::Seek(fd, entry_table_offset);
|
||||
FS::Read(fd, entry_table_data, entry_table_size);
|
||||
|
||||
@@ -528,10 +636,10 @@ namespace INSTALLER
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExtractRemotePkg(const std::string &filename, const std::string sfo_path, const std::string icon_path)
|
||||
bool ExtractRemotePkg(const std::string &path, const std::string sfo_path, const std::string icon_path)
|
||||
{
|
||||
pkg_header tmp_hdr;
|
||||
if (!remoteclient->Head(filename, &tmp_hdr, sizeof(pkg_header)))
|
||||
if (!remoteclient->Head(path, &tmp_hdr, sizeof(pkg_header)))
|
||||
return false;
|
||||
|
||||
size_t entry_count = BE32(tmp_hdr.pkg_entry_count);
|
||||
@@ -539,7 +647,7 @@ namespace INSTALLER
|
||||
uint64_t entry_table_size = entry_count * sizeof(pkg_table_entry);
|
||||
void *entry_table_data = malloc(entry_table_size);
|
||||
|
||||
if (!remoteclient->GetRange(filename, entry_table_data, entry_table_size, entry_table_offset))
|
||||
if (!remoteclient->GetRange(path, entry_table_data, entry_table_size, entry_table_offset))
|
||||
return false;
|
||||
|
||||
pkg_table_entry *entries = (pkg_table_entry *)entry_table_data;
|
||||
@@ -577,7 +685,7 @@ namespace INSTALLER
|
||||
{
|
||||
param_sfo_data = malloc(param_sfo_size);
|
||||
FILE *out = FS::Create(sfo_path);
|
||||
if (!remoteclient->GetRange(filename, param_sfo_data, param_sfo_size, param_sfo_offset))
|
||||
if (!remoteclient->GetRange(path, param_sfo_data, param_sfo_size, param_sfo_offset))
|
||||
{
|
||||
FS::Close(out);
|
||||
return false;
|
||||
@@ -591,7 +699,7 @@ namespace INSTALLER
|
||||
{
|
||||
icon0_png_data = malloc(icon0_png_size);
|
||||
FILE *out = FS::Create(icon_path);
|
||||
if (!remoteclient->GetRange(filename, icon0_png_data, icon0_png_size, icon0_png_offset))
|
||||
if (!remoteclient->GetRange(path, icon0_png_data, icon0_png_size, icon0_png_offset))
|
||||
{
|
||||
FS::Close(out);
|
||||
return false;
|
||||
@@ -603,4 +711,220 @@ namespace INSTALLER
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ArchivePkgInstallData *GetArchivePkgInstallData(const std::string &hash)
|
||||
{
|
||||
return archive_pkg_install_data_list[hash];
|
||||
}
|
||||
|
||||
void AddArchivePkgInstallData(const std::string &hash, ArchivePkgInstallData *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);
|
||||
}
|
||||
|
||||
void RemoveArchivePkgInstallData(const std::string &hash)
|
||||
{
|
||||
archive_pkg_install_data_list.erase(hash);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
sceSystemServicePowerTick();
|
||||
sceKernelUsleep(500000);
|
||||
}
|
||||
finish:
|
||||
bg_check_data->pkg_data->stop_write_thread = true;
|
||||
pthread_join(bg_check_data->pkg_data->thread, NULL);
|
||||
delete(bg_check_data->pkg_data->split_file);
|
||||
free(bg_check_data->pkg_data);
|
||||
RemoveArchivePkgInstallData(bg_check_data->hash);
|
||||
free(bg_check_data);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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://localhost:") + std::to_string(http_server_port) + "/archive_inst/" + hash;
|
||||
AddArchivePkgInstallData(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 = 100;
|
||||
bytes_transfered = 0;
|
||||
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;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BgProgressCheck *bg_check_data = (BgProgressCheck*) malloc(sizeof(BgProgressCheck));
|
||||
memset(bg_check_data, 0, sizeof(BgProgressCheck));
|
||||
bg_check_data->pkg_data = pkg_data;
|
||||
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);
|
||||
free(pkg_data);
|
||||
RemoveArchivePkgInstallData(hash);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+29
-6
@@ -1,5 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "clients/remote_client.h"
|
||||
#include "zip_util.h"
|
||||
#include "split_file.h"
|
||||
#include "pthread.h"
|
||||
|
||||
#define SWAP16(x) \
|
||||
((uint16_t)((((uint16_t)(x)&UINT16_C(0x00FF)) << 8) | \
|
||||
(((uint16_t)(x)&UINT16_C(0xFF00)) >> 8)))
|
||||
@@ -44,6 +49,8 @@
|
||||
#define PKG_ENTRY_ID__PARAM_SFO 0x1000
|
||||
#define PKG_ENTRY_ID__ICON0_PNG 0x1200
|
||||
|
||||
#define INSTALL_ARCHIVE_PKG_SPLIT_SIZE 10485760
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t pkg_magic; // 0x000 - 0x7F434E54
|
||||
@@ -114,16 +121,32 @@ enum pkg_content_type
|
||||
PKG_CONTENT_TYPE_DP = 0x1E, /* pkg_ps4_delta_patch */
|
||||
};
|
||||
|
||||
struct ArchivePkgInstallData
|
||||
{
|
||||
SplitFile *split_file;
|
||||
ArchiveEntry *archive_entry;
|
||||
pthread_t thread;
|
||||
bool stop_write_thread;
|
||||
};
|
||||
|
||||
static pthread_t bk_install_thid;
|
||||
|
||||
namespace INSTALLER
|
||||
{
|
||||
int Init(void);
|
||||
void Exit(void);
|
||||
|
||||
bool canInstallRemotePkg(const std::string &url);
|
||||
std::string getRemoteUrl(const std::string filename, bool encodeUrl = false);
|
||||
int InstallRemotePkg(const std::string &filename, pkg_header *header);
|
||||
int InstallLocalPkg(const std::string &filename);
|
||||
int InstallLocalPkg(const std::string &filename, pkg_header *header, bool remove_after_install = false);
|
||||
bool ExtractLocalPkg(const std::string &filename, const std::string sfo_path, const std::string icon_path);
|
||||
bool ExtractRemotePkg(const std::string &filename, const std::string sfo_path, const std::string icon_path);
|
||||
std::string getRemoteUrl(const std::string path, bool encodeUrl = false);
|
||||
int InstallRemotePkg(const std::string &path, pkg_header *header, std::string title, bool prompt = false);
|
||||
int InstallLocalPkg(const std::string &path);
|
||||
int InstallLocalPkg(const std::string &path, pkg_header *header, bool remove_after_install = false);
|
||||
bool ExtractLocalPkg(const std::string &path, const std::string sfo_path, const std::string icon_path);
|
||||
bool ExtractRemotePkg(const std::string &path, const std::string sfo_path, const std::string icon_path);
|
||||
std::string GetRemotePkgTitle(RemoteClient *client, const std::string &path, pkg_header *header);
|
||||
std::string GetLocalPkgTitle(const std::string &path, pkg_header *header);
|
||||
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 bg = false);
|
||||
}
|
||||
+163
-155
@@ -10,160 +10,168 @@ 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 google 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. "
|
||||
"This requires a HTTP Server setup on the same host sharing the same folder with anonymous access.", // STR_ENABLE_RPI_FTP_SMB_MSG
|
||||
"This option enables Remote Package Installation. "
|
||||
"This requires the Server with anonymous access that does not need username/password.", // 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
|
||||
"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
|
||||
};
|
||||
|
||||
bool needs_extended_font = false;
|
||||
@@ -177,7 +185,7 @@ namespace Lang
|
||||
|
||||
std::string lang = std::string(language);
|
||||
lang = Util::Trim(lang, " ");
|
||||
if (lang.size() > 0)
|
||||
if (lang.size() > 0 && lang.compare("Default") != 0)
|
||||
{
|
||||
sprintf(langFile, "/app0/assets/langs/%s.ini", lang.c_str());
|
||||
}
|
||||
|
||||
+164
-154
@@ -3,159 +3,169 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define FOREACH_STR(FUNC) \
|
||||
FUNC(STR_CONNECTION_SETTINGS) \
|
||||
FUNC(STR_SITE) \
|
||||
FUNC(STR_LOCAL) \
|
||||
FUNC(STR_REMOTE) \
|
||||
FUNC(STR_MESSAGES) \
|
||||
FUNC(STR_UPDATE_SOFTWARE) \
|
||||
FUNC(STR_CONNECT) \
|
||||
FUNC(STR_DISCONNECT) \
|
||||
FUNC(STR_SEARCH) \
|
||||
FUNC(STR_REFRESH) \
|
||||
FUNC(STR_SERVER) \
|
||||
FUNC(STR_USERNAME) \
|
||||
FUNC(STR_PASSWORD) \
|
||||
FUNC(STR_PORT) \
|
||||
FUNC(STR_PASV) \
|
||||
FUNC(STR_DIRECTORY) \
|
||||
FUNC(STR_FILTER) \
|
||||
FUNC(STR_YES) \
|
||||
FUNC(STR_NO) \
|
||||
FUNC(STR_CANCEL) \
|
||||
FUNC(STR_CONTINUE) \
|
||||
FUNC(STR_CLOSE) \
|
||||
FUNC(STR_FOLDER) \
|
||||
FUNC(STR_FILE) \
|
||||
FUNC(STR_TYPE) \
|
||||
FUNC(STR_NAME) \
|
||||
FUNC(STR_SIZE) \
|
||||
FUNC(STR_DATE) \
|
||||
FUNC(STR_NEW_FOLDER) \
|
||||
FUNC(STR_RENAME) \
|
||||
FUNC(STR_DELETE) \
|
||||
FUNC(STR_UPLOAD) \
|
||||
FUNC(STR_DOWNLOAD) \
|
||||
FUNC(STR_SELECT_ALL) \
|
||||
FUNC(STR_CLEAR_ALL) \
|
||||
FUNC(STR_UPLOADING) \
|
||||
FUNC(STR_DOWNLOADING) \
|
||||
FUNC(STR_OVERWRITE) \
|
||||
FUNC(STR_DONT_OVERWRITE) \
|
||||
FUNC(STR_ASK_FOR_CONFIRM) \
|
||||
FUNC(STR_DONT_ASK_CONFIRM) \
|
||||
FUNC(STR_ALLWAYS_USE_OPTION) \
|
||||
FUNC(STR_ACTIONS) \
|
||||
FUNC(STR_CONFIRM) \
|
||||
FUNC(STR_OVERWRITE_OPTIONS) \
|
||||
FUNC(STR_PROPERTIES) \
|
||||
FUNC(STR_PROGRESS) \
|
||||
FUNC(STR_UPDATES) \
|
||||
FUNC(STR_DEL_CONFIRM_MSG) \
|
||||
FUNC(STR_CANCEL_ACTION_MSG) \
|
||||
FUNC(STR_FAIL_UPLOAD_MSG) \
|
||||
FUNC(STR_FAIL_DOWNLOAD_MSG) \
|
||||
FUNC(STR_FAIL_READ_LOCAL_DIR_MSG) \
|
||||
FUNC(STR_CONNECTION_CLOSE_ERR_MSG) \
|
||||
FUNC(STR_REMOTE_TERM_CONN_MSG) \
|
||||
FUNC(STR_FAIL_LOGIN_MSG) \
|
||||
FUNC(STR_FAIL_TIMEOUT_MSG) \
|
||||
FUNC(STR_FAIL_DEL_DIR_MSG) \
|
||||
FUNC(STR_DELETING) \
|
||||
FUNC(STR_FAIL_DEL_FILE_MSG) \
|
||||
FUNC(STR_DELETED) \
|
||||
FUNC(STR_LINK) \
|
||||
FUNC(STR_SHARE) \
|
||||
FUNC(STR_FAILED) \
|
||||
FUNC(STR_FAIL_CREATE_LOCAL_FILE_MSG) \
|
||||
FUNC(STR_INSTALL) \
|
||||
FUNC(STR_INSTALLING) \
|
||||
FUNC(STR_INSTALL_SUCCESS) \
|
||||
FUNC(STR_INSTALL_FAILED) \
|
||||
FUNC(STR_INSTALL_SKIPPED) \
|
||||
FUNC(STR_CHECK_HTTP_MSG) \
|
||||
FUNC(STR_FAILED_HTTP_CHECK) \
|
||||
FUNC(STR_REMOTE_NOT_HTTP) \
|
||||
FUNC(STR_INSTALL_FROM_DATA_MSG) \
|
||||
FUNC(STR_ALREADY_INSTALLED_MSG) \
|
||||
FUNC(STR_INSTALL_FROM_URL) \
|
||||
FUNC(STR_CANNOT_READ_PKG_HDR_MSG) \
|
||||
FUNC(STR_FAVORITE_URLS) \
|
||||
FUNC(STR_SLOT) \
|
||||
FUNC(STR_EDIT) \
|
||||
FUNC(STR_ONETIME_URL) \
|
||||
FUNC(STR_NOT_A_VALID_PACKAGE) \
|
||||
FUNC(STR_WAIT_FOR_INSTALL_MSG) \
|
||||
FUNC(STR_FAIL_INSTALL_TMP_PKG_MSG) \
|
||||
FUNC(STR_FAIL_TO_OBTAIN_GG_DL_MSG) \
|
||||
FUNC(STR_AUTO_DELETE_TMP_PKG) \
|
||||
FUNC(STR_PROTOCOL_NOT_SUPPORTED) \
|
||||
FUNC(STR_COULD_NOT_RESOLVE_HOST) \
|
||||
FUNC(STR_EXTRACT) \
|
||||
FUNC(STR_EXTRACTING) \
|
||||
FUNC(STR_FAILED_TO_EXTRACT) \
|
||||
FUNC(STR_EXTRACT_LOCATION) \
|
||||
FUNC(STR_COMPRESS) \
|
||||
FUNC(STR_ZIP_FILE_PATH) \
|
||||
FUNC(STR_COMPRESSING) \
|
||||
FUNC(STR_ERROR_CREATE_ZIP) \
|
||||
FUNC(STR_UNSUPPORTED_FILE_FORMAT) \
|
||||
FUNC(STR_CUT) \
|
||||
FUNC(STR_COPY) \
|
||||
FUNC(STR_PASTE) \
|
||||
FUNC(STR_MOVING) \
|
||||
FUNC(STR_COPYING) \
|
||||
FUNC(STR_FAIL_MOVE_MSG) \
|
||||
FUNC(STR_FAIL_COPY_MSG) \
|
||||
FUNC(STR_CANT_MOVE_TO_SUBDIR_MSG) \
|
||||
FUNC(STR_CANT_COPY_TO_SUBDIR_MSG) \
|
||||
FUNC(STR_UNSUPPORTED_OPERATION_MSG) \
|
||||
FUNC(STR_HTTP_PORT) \
|
||||
FUNC(STR_REINSTALL_CONFIRM_MSG) \
|
||||
FUNC(STR_REMOTE_NOT_SUPPORT_MSG) \
|
||||
FUNC(STR_CANNOT_CONNECT_REMOTE_MSG) \
|
||||
FUNC(STR_DOWNLOAD_INSTALL_MSG) \
|
||||
FUNC(STR_CHECKING_REMOTE_SERVER_MSG) \
|
||||
FUNC(STR_ENABLE_RPI) \
|
||||
FUNC(STR_ENABLE_RPI_FTP_SMB_MSG) \
|
||||
FUNC(STR_ENABLE_RPI_WEBDAV_MSG) \
|
||||
FUNC(STR_FILES) \
|
||||
FUNC(STR_EDITOR) \
|
||||
FUNC(STR_SAVE) \
|
||||
FUNC(STR_MAX_EDIT_FILE_SIZE_MSG) \
|
||||
FUNC(STR_DELETE_LINE) \
|
||||
FUNC(STR_INSERT_LINE) \
|
||||
FUNC(STR_MODIFIED) \
|
||||
FUNC(STR_FAIL_GET_TOKEN_MSG) \
|
||||
FUNC(STR_GET_TOKEN_SUCCESS_MSG) \
|
||||
FUNC(STR_PERM_DRIVE) \
|
||||
FUNC(STR_PERM_DRIVE_APPDATA) \
|
||||
FUNC(STR_PERM_DRIVE_FILE) \
|
||||
FUNC(STR_PERM_DRIVE_METADATA) \
|
||||
FUNC(STR_PERM_DRIVE_METADATA_RO) \
|
||||
FUNC(STR_GOOGLE_LOGIN_FAIL_MSG) \
|
||||
FUNC(STR_GOOGLE_LOGIN_TIMEOUT_MSG) \
|
||||
FUNC(STR_NEW_FILE) \
|
||||
FUNC(STR_SETTINGS) \
|
||||
FUNC(STR_CLIENT_ID) \
|
||||
FUNC(STR_CLIENT_SECRET) \
|
||||
FUNC(STR_GLOBAL) \
|
||||
FUNC(STR_GOOGLE) \
|
||||
FUNC(STR_COPY_LINE) \
|
||||
FUNC(STR_PASTE_LINE) \
|
||||
FUNC(STR_SHOW_HIDDEN_FILES) \
|
||||
FUNC(STR_SET_DEFAULT_DIRECTORY) \
|
||||
FUNC(STR_SET_DEFAULT_DIRECTORY_MSG) \
|
||||
FUNC(STR_VIEW_IMAGE) \
|
||||
FUNC(STR_VIEW_PKG_INFO) \
|
||||
FUNC(STR_NFS_EXP_PATH_MISSING_MSG) \
|
||||
FUNC(STR_FAIL_INIT_NFS_CONTEXT) \
|
||||
FUNC(STR_FAIL_MOUNT_NFS_MSG) \
|
||||
FUNC(STR_WEB_SERVER) \
|
||||
FUNC(STR_ENABLE) \
|
||||
FUNC(STR_COMPRESSED_FILE_PATH) \
|
||||
FUNC(STR_COMPRESSED_FILE_PATH_MSG)
|
||||
#define FOREACH_STR(FUNC) \
|
||||
FUNC(STR_CONNECTION_SETTINGS) \
|
||||
FUNC(STR_SITE) \
|
||||
FUNC(STR_LOCAL) \
|
||||
FUNC(STR_REMOTE) \
|
||||
FUNC(STR_MESSAGES) \
|
||||
FUNC(STR_UPDATE_SOFTWARE) \
|
||||
FUNC(STR_CONNECT) \
|
||||
FUNC(STR_DISCONNECT) \
|
||||
FUNC(STR_SEARCH) \
|
||||
FUNC(STR_REFRESH) \
|
||||
FUNC(STR_SERVER) \
|
||||
FUNC(STR_USERNAME) \
|
||||
FUNC(STR_PASSWORD) \
|
||||
FUNC(STR_PORT) \
|
||||
FUNC(STR_PASV) \
|
||||
FUNC(STR_DIRECTORY) \
|
||||
FUNC(STR_FILTER) \
|
||||
FUNC(STR_YES) \
|
||||
FUNC(STR_NO) \
|
||||
FUNC(STR_CANCEL) \
|
||||
FUNC(STR_CONTINUE) \
|
||||
FUNC(STR_CLOSE) \
|
||||
FUNC(STR_FOLDER) \
|
||||
FUNC(STR_FILE) \
|
||||
FUNC(STR_TYPE) \
|
||||
FUNC(STR_NAME) \
|
||||
FUNC(STR_SIZE) \
|
||||
FUNC(STR_DATE) \
|
||||
FUNC(STR_NEW_FOLDER) \
|
||||
FUNC(STR_RENAME) \
|
||||
FUNC(STR_DELETE) \
|
||||
FUNC(STR_UPLOAD) \
|
||||
FUNC(STR_DOWNLOAD) \
|
||||
FUNC(STR_SELECT_ALL) \
|
||||
FUNC(STR_CLEAR_ALL) \
|
||||
FUNC(STR_UPLOADING) \
|
||||
FUNC(STR_DOWNLOADING) \
|
||||
FUNC(STR_OVERWRITE) \
|
||||
FUNC(STR_DONT_OVERWRITE) \
|
||||
FUNC(STR_ASK_FOR_CONFIRM) \
|
||||
FUNC(STR_DONT_ASK_CONFIRM) \
|
||||
FUNC(STR_ALLWAYS_USE_OPTION) \
|
||||
FUNC(STR_ACTIONS) \
|
||||
FUNC(STR_CONFIRM) \
|
||||
FUNC(STR_OVERWRITE_OPTIONS) \
|
||||
FUNC(STR_PROPERTIES) \
|
||||
FUNC(STR_PROGRESS) \
|
||||
FUNC(STR_UPDATES) \
|
||||
FUNC(STR_DEL_CONFIRM_MSG) \
|
||||
FUNC(STR_CANCEL_ACTION_MSG) \
|
||||
FUNC(STR_FAIL_UPLOAD_MSG) \
|
||||
FUNC(STR_FAIL_DOWNLOAD_MSG) \
|
||||
FUNC(STR_FAIL_READ_LOCAL_DIR_MSG) \
|
||||
FUNC(STR_CONNECTION_CLOSE_ERR_MSG) \
|
||||
FUNC(STR_REMOTE_TERM_CONN_MSG) \
|
||||
FUNC(STR_FAIL_LOGIN_MSG) \
|
||||
FUNC(STR_FAIL_TIMEOUT_MSG) \
|
||||
FUNC(STR_FAIL_DEL_DIR_MSG) \
|
||||
FUNC(STR_DELETING) \
|
||||
FUNC(STR_FAIL_DEL_FILE_MSG) \
|
||||
FUNC(STR_DELETED) \
|
||||
FUNC(STR_LINK) \
|
||||
FUNC(STR_SHARE) \
|
||||
FUNC(STR_FAILED) \
|
||||
FUNC(STR_FAIL_CREATE_LOCAL_FILE_MSG) \
|
||||
FUNC(STR_INSTALL) \
|
||||
FUNC(STR_INSTALLING) \
|
||||
FUNC(STR_INSTALL_SUCCESS) \
|
||||
FUNC(STR_INSTALL_FAILED) \
|
||||
FUNC(STR_INSTALL_SKIPPED) \
|
||||
FUNC(STR_CHECK_HTTP_MSG) \
|
||||
FUNC(STR_FAILED_HTTP_CHECK) \
|
||||
FUNC(STR_REMOTE_NOT_HTTP) \
|
||||
FUNC(STR_INSTALL_FROM_DATA_MSG) \
|
||||
FUNC(STR_ALREADY_INSTALLED_MSG) \
|
||||
FUNC(STR_INSTALL_FROM_URL) \
|
||||
FUNC(STR_CANNOT_READ_PKG_HDR_MSG) \
|
||||
FUNC(STR_FAVORITE_URLS) \
|
||||
FUNC(STR_SLOT) \
|
||||
FUNC(STR_EDIT) \
|
||||
FUNC(STR_ONETIME_URL) \
|
||||
FUNC(STR_NOT_A_VALID_PACKAGE) \
|
||||
FUNC(STR_WAIT_FOR_INSTALL_MSG) \
|
||||
FUNC(STR_FAIL_INSTALL_TMP_PKG_MSG) \
|
||||
FUNC(STR_FAIL_TO_OBTAIN_GG_DL_MSG) \
|
||||
FUNC(STR_AUTO_DELETE_TMP_PKG) \
|
||||
FUNC(STR_PROTOCOL_NOT_SUPPORTED) \
|
||||
FUNC(STR_COULD_NOT_RESOLVE_HOST) \
|
||||
FUNC(STR_EXTRACT) \
|
||||
FUNC(STR_EXTRACTING) \
|
||||
FUNC(STR_FAILED_TO_EXTRACT) \
|
||||
FUNC(STR_EXTRACT_LOCATION) \
|
||||
FUNC(STR_COMPRESS) \
|
||||
FUNC(STR_ZIP_FILE_PATH) \
|
||||
FUNC(STR_COMPRESSING) \
|
||||
FUNC(STR_ERROR_CREATE_ZIP) \
|
||||
FUNC(STR_UNSUPPORTED_FILE_FORMAT) \
|
||||
FUNC(STR_CUT) \
|
||||
FUNC(STR_COPY) \
|
||||
FUNC(STR_PASTE) \
|
||||
FUNC(STR_MOVING) \
|
||||
FUNC(STR_COPYING) \
|
||||
FUNC(STR_FAIL_MOVE_MSG) \
|
||||
FUNC(STR_FAIL_COPY_MSG) \
|
||||
FUNC(STR_CANT_MOVE_TO_SUBDIR_MSG) \
|
||||
FUNC(STR_CANT_COPY_TO_SUBDIR_MSG) \
|
||||
FUNC(STR_UNSUPPORTED_OPERATION_MSG) \
|
||||
FUNC(STR_HTTP_PORT) \
|
||||
FUNC(STR_REINSTALL_CONFIRM_MSG) \
|
||||
FUNC(STR_REMOTE_NOT_SUPPORT_MSG) \
|
||||
FUNC(STR_CANNOT_CONNECT_REMOTE_MSG) \
|
||||
FUNC(STR_DOWNLOAD_INSTALL_MSG) \
|
||||
FUNC(STR_CHECKING_REMOTE_SERVER_MSG) \
|
||||
FUNC(STR_ENABLE_RPI) \
|
||||
FUNC(STR_ENABLE_RPI_FTP_SMB_MSG) \
|
||||
FUNC(STR_ENABLE_RPI_WEBDAV_MSG) \
|
||||
FUNC(STR_FILES) \
|
||||
FUNC(STR_EDITOR) \
|
||||
FUNC(STR_SAVE) \
|
||||
FUNC(STR_MAX_EDIT_FILE_SIZE_MSG) \
|
||||
FUNC(STR_DELETE_LINE) \
|
||||
FUNC(STR_INSERT_LINE) \
|
||||
FUNC(STR_MODIFIED) \
|
||||
FUNC(STR_FAIL_GET_TOKEN_MSG) \
|
||||
FUNC(STR_GET_TOKEN_SUCCESS_MSG) \
|
||||
FUNC(STR_PERM_DRIVE) \
|
||||
FUNC(STR_PERM_DRIVE_APPDATA) \
|
||||
FUNC(STR_PERM_DRIVE_FILE) \
|
||||
FUNC(STR_PERM_DRIVE_METADATA) \
|
||||
FUNC(STR_PERM_DRIVE_METADATA_RO) \
|
||||
FUNC(STR_GOOGLE_LOGIN_FAIL_MSG) \
|
||||
FUNC(STR_GOOGLE_LOGIN_TIMEOUT_MSG) \
|
||||
FUNC(STR_NEW_FILE) \
|
||||
FUNC(STR_SETTINGS) \
|
||||
FUNC(STR_CLIENT_ID) \
|
||||
FUNC(STR_CLIENT_SECRET) \
|
||||
FUNC(STR_GLOBAL) \
|
||||
FUNC(STR_GOOGLE) \
|
||||
FUNC(STR_COPY_LINE) \
|
||||
FUNC(STR_PASTE_LINE) \
|
||||
FUNC(STR_SHOW_HIDDEN_FILES) \
|
||||
FUNC(STR_SET_DEFAULT_DIRECTORY) \
|
||||
FUNC(STR_SET_DEFAULT_DIRECTORY_MSG) \
|
||||
FUNC(STR_VIEW_IMAGE) \
|
||||
FUNC(STR_VIEW_PKG_INFO) \
|
||||
FUNC(STR_NFS_EXP_PATH_MISSING_MSG) \
|
||||
FUNC(STR_FAIL_INIT_NFS_CONTEXT) \
|
||||
FUNC(STR_FAIL_MOUNT_NFS_MSG) \
|
||||
FUNC(STR_WEB_SERVER) \
|
||||
FUNC(STR_ENABLE) \
|
||||
FUNC(STR_COMPRESSED_FILE_PATH) \
|
||||
FUNC(STR_COMPRESSED_FILE_PATH_MSG) \
|
||||
FUNC(STR_ALLDEBRID) \
|
||||
FUNC(STR_API_KEY) \
|
||||
FUNC(STR_CANT_EXTRACT_URL_MSG) \
|
||||
FUNC(STR_FAIL_INSTALL_FROM_URL_MSG) \
|
||||
FUNC(STR_INVALID_URL) \
|
||||
FUNC(STR_ALLDEBRID_API_KEY_MISSING_MSG) \
|
||||
FUNC(STR_LANGUAGE) \
|
||||
FUNC(STR_TEMP_DIRECTORY) \
|
||||
FUNC(STR_REALDEBRID) \
|
||||
FUNC(STR_BACKGROUND_INSTALL_INPROGRESS)
|
||||
|
||||
#define GET_VALUE(x) x,
|
||||
#define GET_STRING(x) #x,
|
||||
@@ -165,7 +175,7 @@ enum
|
||||
FOREACH_STR(GET_VALUE)
|
||||
};
|
||||
|
||||
#define LANG_STRINGS_NUM 152
|
||||
#define LANG_STRINGS_NUM 162
|
||||
#define LANG_ID_SIZE 64
|
||||
#define LANG_STR_SIZE 384
|
||||
extern char lang_identifiers[LANG_STRINGS_NUM][LANG_ID_SIZE];
|
||||
|
||||
@@ -2,9 +2,70 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/uio.h>
|
||||
#include <orbis/libkernel.h>
|
||||
#include <libjbc.h>
|
||||
|
||||
#define SYSCALL(nr, fn) __attribute__((naked)) fn\
|
||||
{\
|
||||
asm volatile("mov $" #nr ", %rax\nmov %rcx, %r10\nsyscall\nret");\
|
||||
}
|
||||
|
||||
SYSCALL(22, static int unmount(const char* path, int flags))
|
||||
SYSCALL(378, static int nmount(struct iovec* iov, unsigned int niov, int flags))
|
||||
|
||||
static void build_iovec(struct iovec** iov, int* iovlen, const char* name, const void* val, size_t len) {
|
||||
int i;
|
||||
|
||||
if (*iovlen < 0)
|
||||
return;
|
||||
|
||||
i = *iovlen;
|
||||
*iov = (struct iovec*)realloc(*iov, sizeof **iov * (i + 2));
|
||||
if (*iov == NULL) {
|
||||
*iovlen = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
(*iov)[i].iov_base = strdup(name);
|
||||
(*iov)[i].iov_len = strlen(name) + 1;
|
||||
++i;
|
||||
|
||||
(*iov)[i].iov_base = (void*)val;
|
||||
if (len == (size_t)-1) {
|
||||
if (val != NULL)
|
||||
len = strlen((const char*)val) + 1;
|
||||
else
|
||||
len = 0;
|
||||
}
|
||||
(*iov)[i].iov_len = (int)len;
|
||||
|
||||
*iovlen = ++i;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
if (mode) {
|
||||
build_iovec(&iov, &iovlen, "dirmask", mode, -1);
|
||||
build_iovec(&iov, &iovlen, "mask", mode, -1);
|
||||
}
|
||||
|
||||
return nmount(iov, iovlen, flags);
|
||||
}
|
||||
|
||||
// Variables for (un)jailbreaking
|
||||
jbc_cred g_Cred;
|
||||
jbc_cred g_RootCreds;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#ifndef __ORBIS_JBC_H__
|
||||
#define __ORBIS_JBC_H__
|
||||
|
||||
#define MNT_UPDATE 0x0000000000010000ULL
|
||||
|
||||
int initialize_jbc();
|
||||
void terminate_jbc();
|
||||
int mount_large_fs(const char* device, const char* mountpoint, const char* fstype, const char* mode, unsigned int flags);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
#include <string>
|
||||
#include <json-c/json.h>
|
||||
#include <range_parser/range_parser.hpp>
|
||||
#include "http/httplib.h"
|
||||
#include "server/http_server.h"
|
||||
#include "clients/gdrive.h"
|
||||
#include "clients/sftpclient.h"
|
||||
#include "clients/smbclient.h"
|
||||
#include "clients/ftpclient.h"
|
||||
#include "clients/nfsclient.h"
|
||||
#include "filehost/filehost.h"
|
||||
#include "config.h"
|
||||
#include "fs.h"
|
||||
#include "windows.h"
|
||||
#include "lang.h"
|
||||
#include "system.h"
|
||||
#include "zip_util.h"
|
||||
#include "util.h"
|
||||
#include "installer.h"
|
||||
|
||||
#define SERVER_CERT_FILE "/app0/assets/certs/domain.crt"
|
||||
@@ -19,6 +26,7 @@
|
||||
#define SUCCESS_MSG_LEN 48
|
||||
|
||||
using namespace httplib;
|
||||
|
||||
Server *svr;
|
||||
int http_server_port = 8080;
|
||||
char compressed_file_path[1024];
|
||||
@@ -382,7 +390,15 @@ namespace HttpServer
|
||||
{
|
||||
const char *src = json_object_get_string(json_object_array_get_idx(items, 0));
|
||||
std::string dest = std::string(newPath) + "/" + singleFilename;
|
||||
if (dest.compare(src) != 0 && !FS::Copy(src, dest))
|
||||
|
||||
std::string temp = std::string(src);
|
||||
size_t slash_pos = temp.find_last_of("/");
|
||||
DirEntry entry;
|
||||
sprintf(entry.name, "%s", temp.substr(slash_pos+1).c_str());
|
||||
sprintf(entry.path, "%s", src);
|
||||
entry.isDir = FS::IsFolder(src);
|
||||
if (entry.isDir)
|
||||
if (dest.compare(src) != 0 && !CopyOrMove(entry, dest.c_str(), true))
|
||||
{
|
||||
failed_items += src;
|
||||
}
|
||||
@@ -699,6 +715,7 @@ namespace HttpServer
|
||||
size_t chunk_size = 0;
|
||||
size_t chunk_number = -1;
|
||||
size_t total_size = 0;
|
||||
size_t currentChunkSize = 0;
|
||||
FILE *out = nullptr;
|
||||
std::string new_file;
|
||||
content_reader(
|
||||
@@ -742,6 +759,11 @@ namespace HttpServer
|
||||
std::stringstream ss(items.back().content);
|
||||
ss >> total_size;
|
||||
}
|
||||
else if (items.back().name == "_currentChunkSize")
|
||||
{
|
||||
std::stringstream ss(items.back().content);
|
||||
ss >> currentChunkSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (out != nullptr)
|
||||
@@ -900,6 +922,299 @@ namespace HttpServer
|
||||
res.set_content(str.c_str(), "text/plain");
|
||||
});
|
||||
|
||||
svr->Get("/rmt_inst/Site (\\d+)(/)(.*)", [&](const Request & req, Response & res)
|
||||
{
|
||||
RemoteClient *tmp_client;
|
||||
RemoteSettings *tmp_settings;
|
||||
auto site_idx = std::stoi(req.matches[1])-1;
|
||||
std::string path;
|
||||
|
||||
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
|
||||
{
|
||||
std::string hash = std::string(req.matches[3]);
|
||||
std::string url = FileHost::GetCachedDownloadUrl(hash);
|
||||
size_t scheme_pos = url.find("://");
|
||||
size_t root_pos = url.find("/", scheme_pos + 3);
|
||||
std::string host = url.substr(0, root_pos);
|
||||
path = url.substr(root_pos);
|
||||
|
||||
tmp_client = new BaseClient();
|
||||
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;
|
||||
ret = tmp_client->Size(path, &file_size);
|
||||
if (!ret)
|
||||
{
|
||||
res.status = 500;
|
||||
return;
|
||||
}
|
||||
|
||||
res.status = 204;
|
||||
res.set_header("Content-Length", std::to_string(file_size));
|
||||
res.set_header("Accept-Ranges", "bytes");
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.ranges.empty())
|
||||
{
|
||||
res.status = 200;
|
||||
res.set_content_provider(
|
||||
(1024*128), "application/octet-stream",
|
||||
[tmp_client, path](size_t offset, size_t length, DataSink &sink) {
|
||||
int ret = tmp_client->GetRange(path, sink, length, offset);
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
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 = 65536ul - req.ranges[0].first;
|
||||
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));
|
||||
}
|
||||
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->Quit();
|
||||
delete tmp_client;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
svr->Get("/archive_inst/(.*)", [&](const Request & req, Response & res)
|
||||
{
|
||||
RemoteClient *tmp_client;
|
||||
RemoteSettings *tmp_settings;
|
||||
std::string hash = req.matches[1];
|
||||
|
||||
ArchivePkgInstallData *pkg_data = INSTALLER::GetArchivePkgInstallData(hash);
|
||||
|
||||
if (req.method == "HEAD")
|
||||
{
|
||||
res.status = 204;
|
||||
res.set_header("Content-Length", std::to_string(pkg_data->archive_entry->filesize));
|
||||
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 = 65536ul - req.ranges[0].first;
|
||||
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));
|
||||
}
|
||||
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 = 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"));
|
||||
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)
|
||||
{
|
||||
bad_request(res, "Required url_param 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 hash = Util::UrlHash(filehost->GetUrl());
|
||||
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);
|
||||
pkg_header header;
|
||||
|
||||
BaseClient *baseclient = new BaseClient();
|
||||
baseclient->Connect(host, "", "");
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
failed(res, 200, lang_strings[STR_FAIL_INSTALL_FROM_URL_MSG]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ArchiveEntry *entry = ZipUtil::GetPackageEntry(path, baseclient);
|
||||
if (entry != nullptr)
|
||||
{
|
||||
ArchivePkgInstallData *install_data = (ArchivePkgInstallData*) malloc(sizeof(ArchivePkgInstallData));
|
||||
memset(install_data, 0, sizeof(ArchivePkgInstallData));
|
||||
|
||||
std::string install_pkg_path = std::string(temp_folder) + "/" + entry->filename;
|
||||
SplitFile *sp = new SplitFile(install_pkg_path, INSTALL_ARCHIVE_PKG_SPLIT_SIZE);
|
||||
|
||||
install_data->archive_entry = entry;
|
||||
install_data->split_file = sp;
|
||||
install_data->stop_write_thread = false;
|
||||
|
||||
int ret = pthread_create(&install_data->thread, NULL, Actions::ExtractArchivePkg, install_data);
|
||||
|
||||
ret = INSTALLER::InstallArchivePkg(entry->filename, install_data, true);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
failed(res, 200, lang_strings[STR_FAIL_INSTALL_FROM_URL_MSG]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
failed(res, 200, lang_strings[STR_FAIL_INSTALL_FROM_URL_MSG]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
success(res);
|
||||
});
|
||||
|
||||
svr->Get("/stop", [&](const Request & /*req*/, Response & /*res*/)
|
||||
{
|
||||
svr->stop();
|
||||
@@ -919,7 +1234,7 @@ namespace HttpServer
|
||||
dbglogger_log("%s", log(req, res).c_str());
|
||||
});
|
||||
*/
|
||||
|
||||
|
||||
svr->set_payload_max_length(1024 * 1024 * 12);
|
||||
svr->set_tcp_nodelay(true);
|
||||
svr->set_mount_point("/", "/");
|
||||
@@ -948,4 +1263,4 @@ namespace HttpServer
|
||||
if (svr != nullptr)
|
||||
svr->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
#include <stdio.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#include "common.h"
|
||||
#include "split_file.h"
|
||||
|
||||
SplitFile::SplitFile(const std::string &path, size_t block_size)
|
||||
{
|
||||
this->block_size = block_size;
|
||||
this->path = path;
|
||||
this->complete = false;
|
||||
sem_init(&this->block_ready, 0, 0);
|
||||
}
|
||||
|
||||
SplitFile::~SplitFile()
|
||||
{
|
||||
for (int i = 0; i < this->file_blocks.size(); i++)
|
||||
{
|
||||
if (this->file_blocks[i] != nullptr && this->file_blocks[i]->status != BLOCK_STATUS_DELETED)
|
||||
{
|
||||
if (this->file_blocks[i]->fd != nullptr)
|
||||
{
|
||||
fclose(this->file_blocks[i]->fd);
|
||||
}
|
||||
remove(this->file_blocks[i]->block_file.c_str());
|
||||
free(this->file_blocks[i]);
|
||||
}
|
||||
}
|
||||
sem_destroy(&this->block_ready);
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
size_t SplitFile::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;
|
||||
FileBlock *block;
|
||||
FILE *fd;
|
||||
char *p;
|
||||
|
||||
first_block_num= offset / this->block_size;
|
||||
block_num = first_block_num;
|
||||
block_offset = offset % this->block_size;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
block = this->file_blocks[block_num];
|
||||
if (block->status == 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)
|
||||
{
|
||||
fd = block->fd;
|
||||
if (fd == nullptr)
|
||||
{
|
||||
fd = fopen(block->block_file.c_str(), "rb");
|
||||
block->fd = fd;
|
||||
}
|
||||
|
||||
fseek(fd, block_offset, SEEK_SET);
|
||||
bytes_read = fread(p, 1, remaining, fd);
|
||||
|
||||
if (bytes_read == remaining)
|
||||
{
|
||||
p += bytes_read;
|
||||
total_bytes_read += bytes_read;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (feof(fd))
|
||||
{
|
||||
p += bytes_read;
|
||||
total_bytes_read += bytes_read;
|
||||
if (block->is_last)
|
||||
{
|
||||
eof = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
remaining -= bytes_read;
|
||||
|
||||
if (remaining == 0)
|
||||
continue;
|
||||
|
||||
block_num++;
|
||||
block_offset = 0;
|
||||
|
||||
while ((block_num > this->file_blocks.size() - 1 && !this->complete) ||
|
||||
this->file_blocks[block_num]->status == BLOCK_STATUS_NOT_EXISTS)
|
||||
{
|
||||
sem_wait(&this->block_ready);
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
if (this->file_blocks[j]->status == BLOCK_STATUS_CREATED)
|
||||
{
|
||||
if (this->file_blocks[j]->fd != nullptr)
|
||||
{
|
||||
fclose(this->file_blocks[j]->fd);
|
||||
this->file_blocks[j]->fd = nullptr;
|
||||
}
|
||||
this->file_blocks[j]->status = BLOCK_STATUS_DELETED;
|
||||
remove(this->file_blocks[j]->block_file.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return total_bytes_read;
|
||||
}
|
||||
|
||||
size_t SplitFile::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);
|
||||
|
||||
bytes_written = fwrite(p, 1, bytes_to_write, block_in_progress->fd);
|
||||
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)
|
||||
{
|
||||
fflush(block_in_progress->fd);
|
||||
fclose(block_in_progress->fd);
|
||||
block_in_progress->fd = nullptr;
|
||||
block_in_progress->status = BLOCK_STATUS_CREATED;
|
||||
this->file_blocks.push_back(block_in_progress);
|
||||
|
||||
sem_post(&this->block_ready);
|
||||
|
||||
block_in_progress = NewBlock();
|
||||
}
|
||||
}
|
||||
|
||||
return total_bytes_written;
|
||||
}
|
||||
|
||||
int SplitFile::Close()
|
||||
{
|
||||
if (block_in_progress->fd != nullptr)
|
||||
{
|
||||
fflush(block_in_progress->fd);
|
||||
fclose(block_in_progress->fd);
|
||||
block_in_progress->fd = nullptr;
|
||||
}
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FileBlock *SplitFile::NewBlock()
|
||||
{
|
||||
FileBlock *block = (FileBlock *)malloc(sizeof(FileBlock));
|
||||
memset(block, 0, sizeof(FileBlock));
|
||||
|
||||
block->is_last = false;
|
||||
block->size = 0;
|
||||
block->block_file = this->path + "." + std::to_string(this->file_blocks.size());
|
||||
block->fd = fopen(block->block_file.c_str(), "w");
|
||||
|
||||
return block;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef SPLIT_FILE_H
|
||||
#define SPLIT_FILE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <pthread.h>
|
||||
|
||||
enum FileBlockStatus
|
||||
{
|
||||
BLOCK_STATUS_NOT_EXISTS,
|
||||
BLOCK_STATUS_CREATED,
|
||||
BLOCK_STATUS_DELETED
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
std::string block_file;
|
||||
size_t size;
|
||||
FILE* fd;
|
||||
bool is_last;
|
||||
FileBlockStatus status;
|
||||
} FileBlock;
|
||||
|
||||
class SplitFile
|
||||
{
|
||||
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);
|
||||
int Open();
|
||||
int Close();
|
||||
|
||||
private:
|
||||
std::vector<FileBlock*> file_blocks;
|
||||
size_t write_offset;
|
||||
size_t block_size;
|
||||
std::string path;
|
||||
int write_error;
|
||||
bool complete;
|
||||
FileBlock *block_in_progress;
|
||||
sem_t block_ready;
|
||||
|
||||
FileBlock *NewBlock();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <algorithm>
|
||||
#include <stdarg.h>
|
||||
#include <orbis/libkernel.h>
|
||||
#include "base64.h"
|
||||
#include "openssl/md5.h"
|
||||
#include "lang.h"
|
||||
|
||||
namespace Util
|
||||
@@ -47,6 +49,13 @@ namespace Util
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline bool EndsWith(std::string const &value, std::string const &ending)
|
||||
{
|
||||
if (ending.size() > value.size())
|
||||
return false;
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
||||
static inline std::vector<std::string> Split(const std::string &str, const std::string &delimiter)
|
||||
{
|
||||
std::string text = std::string(str);
|
||||
@@ -66,6 +75,19 @@ namespace Util
|
||||
return tokens;
|
||||
}
|
||||
|
||||
static inline std::string UrlHash(const std::string &text)
|
||||
{
|
||||
std::vector<unsigned char> res(16);
|
||||
MD5((const unsigned char *)text.c_str(), text.length(), res.data());
|
||||
|
||||
std::string out;
|
||||
Base64::Encode(res.data(), res.size(), out);
|
||||
Util::ReplaceAll(out, "=", "_");
|
||||
Util::ReplaceAll(out, "+", "_");
|
||||
out = out + ".pkg";
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline void Notify(const char *fmt, ...)
|
||||
{
|
||||
OrbisNotificationRequest request;
|
||||
|
||||
@@ -23,9 +23,12 @@
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include "http/httplib.h"
|
||||
|
||||
#include "callback.hpp"
|
||||
|
||||
using namespace httplib;
|
||||
|
||||
namespace Web
|
||||
{
|
||||
namespace Callback
|
||||
@@ -104,6 +107,14 @@ namespace Web
|
||||
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
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace Web
|
||||
{
|
||||
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
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "web/request.hpp"
|
||||
#include "web/urn.hpp"
|
||||
#include "util.h"
|
||||
#include "http/httplib.h"
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
@@ -234,6 +235,57 @@ bool
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Client::sync_download_range_to(
|
||||
const std::string &remote_file,
|
||||
DataSink &sink,
|
||||
uint64_t range_from,
|
||||
uint64_t range_to,
|
||||
callback_t callback,
|
||||
progress_data_t progress_data,
|
||||
progress_t progress)
|
||||
{
|
||||
bool is_existed = this->check(remote_file);
|
||||
if (!is_existed)
|
||||
return false;
|
||||
|
||||
auto root_urn = Path(this->webdav_root, true);
|
||||
auto file_urn = root_urn + remote_file;
|
||||
|
||||
Request request(this->options());
|
||||
|
||||
auto url = this->webdav_hostname + file_urn.quote(request.handle);
|
||||
struct curl_slist *list = NULL;
|
||||
char range_header[64];
|
||||
sprintf(range_header, "Range: bytes=%lu-%lu", range_from, range_to);
|
||||
list = curl_slist_append(list, range_header);
|
||||
request.set(CURLOPT_CUSTOMREQUEST, "GET");
|
||||
request.set(CURLOPT_URL, url.c_str());
|
||||
request.set(CURLOPT_HEADER, 0L);
|
||||
request.set(CURLOPT_HTTPHEADER, list);
|
||||
request.set(CURLOPT_WRITEDATA, reinterpret_cast<void*>(&sink));
|
||||
request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast<size_t>(Web::Callback::Append::stream2sink));
|
||||
#ifdef WDC_VERBOSE
|
||||
request.set(CURLOPT_VERBOSE, 1);
|
||||
#endif
|
||||
if (progress != nullptr)
|
||||
{
|
||||
request.set(CURLOPT_XFERINFODATA, progress_data);
|
||||
request.set(CURLOPT_XFERINFOFUNCTION, progress);
|
||||
request.set(CURLOPT_NOPROGRESS, 0L);
|
||||
}
|
||||
|
||||
bool is_performed = request.perform();
|
||||
this->http_code = request.status_code();
|
||||
this->result = request.result();
|
||||
if (callback != nullptr)
|
||||
callback(is_performed);
|
||||
if (!is_performed)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Client::sync_download_to(
|
||||
const std::string &remote_file,
|
||||
@@ -777,6 +829,18 @@ bool
|
||||
return this->sync_download_range_to(remote_file, buffer_ptr, buffer_size, range_from, range_to, nullptr, progress_data, std::move(progress));
|
||||
}
|
||||
|
||||
bool
|
||||
Client::download_range_to(
|
||||
const std::string &remote_file,
|
||||
DataSink &sink,
|
||||
uint64_t range_from,
|
||||
uint64_t range_to,
|
||||
progress_data_t progress_data,
|
||||
progress_t progress)
|
||||
{
|
||||
return this->sync_download_range_to(remote_file, sink, range_from, range_to, nullptr, progress_data, std::move(progress));
|
||||
}
|
||||
|
||||
bool
|
||||
Client::download_to(
|
||||
const std::string &remote_file,
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <curl/curl.h>
|
||||
#include "http/httplib.h"
|
||||
|
||||
using namespace httplib;
|
||||
|
||||
namespace WebDAV
|
||||
{
|
||||
@@ -208,6 +211,24 @@ namespace WebDAV
|
||||
progress_t progress = nullptr
|
||||
) -> bool;
|
||||
|
||||
///
|
||||
/// Download a remote file to a buffer for specified range
|
||||
/// \param[in] remote_file
|
||||
/// \param[out] sink,
|
||||
/// \param[in] range_from,
|
||||
/// \param[in] range_to
|
||||
/// \param[in] progress
|
||||
/// \snippet client/download.cpp download_to_buffer
|
||||
///
|
||||
auto download_range_to(
|
||||
const std::string &remote_file,
|
||||
DataSink &sink,
|
||||
uint64_t range_from,
|
||||
uint64_t range_to,
|
||||
progress_data_t progress_data = nullptr,
|
||||
progress_t progress = nullptr
|
||||
) -> bool;
|
||||
|
||||
///
|
||||
/// Download a remote file to a stream
|
||||
/// \param[in] remote_file
|
||||
@@ -330,6 +351,16 @@ namespace WebDAV
|
||||
progress_t progress = nullptr
|
||||
) -> bool;
|
||||
|
||||
auto sync_download_range_to(
|
||||
const std::string &remote_file,
|
||||
DataSink &sink,
|
||||
uint64_t range_from,
|
||||
uint64_t range_to,
|
||||
callback_t callback,
|
||||
progress_data_t progress_data,
|
||||
progress_t progress
|
||||
) -> bool;
|
||||
|
||||
bool sync_download_to(
|
||||
const std::string& remote_file,
|
||||
std::ostream& stream,
|
||||
|
||||
+186
-85
@@ -39,7 +39,6 @@ static char *ime_single_field;
|
||||
static int ime_field_size;
|
||||
|
||||
static char txt_http_server_port[6];
|
||||
static char txt_http_port[6];
|
||||
|
||||
bool handle_updates = false;
|
||||
int64_t bytes_transfered;
|
||||
@@ -118,7 +117,6 @@ namespace Windows
|
||||
sprintf(status_message, "");
|
||||
sprintf(local_filter, "");
|
||||
sprintf(remote_filter, "");
|
||||
sprintf(txt_http_port, "%d", remote_settings->http_port);
|
||||
sprintf(txt_http_server_port, "%d", http_server_port);
|
||||
dont_prompt_overwrite = false;
|
||||
confirm_transfer_state = -1;
|
||||
@@ -245,20 +243,33 @@ namespace Windows
|
||||
{
|
||||
std::string zipfolder;
|
||||
std::vector<DirEntry> files;
|
||||
bool local_browser_selected = saved_selected_browser & LOCAL_BROWSER;
|
||||
bool remote_browser_selected = saved_selected_browser & REMOTE_BROWSER;
|
||||
|
||||
if (multi_selected_local_files.size() > 0)
|
||||
std::copy(multi_selected_local_files.begin(), multi_selected_local_files.end(), std::back_inserter(files));
|
||||
if (local_browser_selected)
|
||||
{
|
||||
if (multi_selected_local_files.size() > 0)
|
||||
std::copy(multi_selected_local_files.begin(), multi_selected_local_files.end(), std::back_inserter(files));
|
||||
else
|
||||
files.push_back(selected_local_file);
|
||||
}
|
||||
else
|
||||
files.push_back(selected_local_file);
|
||||
{
|
||||
if (multi_selected_remote_files.size() > 0)
|
||||
std::copy(multi_selected_remote_files.begin(), multi_selected_remote_files.end(), std::back_inserter(files));
|
||||
else
|
||||
files.push_back(selected_remote_file);
|
||||
}
|
||||
|
||||
if (strncmp(files.begin()->directory, "/data", 5) != 0 &&
|
||||
strncmp(files.begin()->directory, "/mnt/usb", 8) != 0)
|
||||
if (strncmp(local_directory, "/data", 5) != 0 &&
|
||||
strncmp(local_directory, "/mnt/usb", 8) != 0 &&
|
||||
strncmp(local_directory, "/user/data", 10) != 0)
|
||||
{
|
||||
zipfolder = "/data";
|
||||
}
|
||||
else if (files.size() > 1)
|
||||
{
|
||||
zipfolder = files.begin()->directory;
|
||||
zipfolder = local_directory;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -320,7 +331,6 @@ namespace Windows
|
||||
sprintf(display_site, "%s", site_id);
|
||||
remote_settings = &site_settings[sites[n]];
|
||||
sprintf(remote_directory, "%s", remote_settings->default_directory);
|
||||
sprintf(txt_http_port, "%d", remote_settings->http_port);
|
||||
}
|
||||
|
||||
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
|
||||
@@ -402,66 +412,42 @@ namespace Windows
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
if (remote_settings->type != CLIENT_TYPE_GOOGLE)
|
||||
if (remote_settings->type != CLIENT_TYPE_NFS && remote_settings->type != CLIENT_TYPE_GOOGLE)
|
||||
{
|
||||
if (remote_settings->type != CLIENT_TYPE_NFS)
|
||||
{
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s:", lang_strings[STR_PASSWORD]);
|
||||
ImGui::SameLine();
|
||||
|
||||
sprintf(id, "%s##password", hidden_password.c_str());
|
||||
pos = ImGui::GetCursorPos();
|
||||
if (ImGui::Button(id, ImVec2(100, 0)))
|
||||
{
|
||||
ime_single_field = remote_settings->password;
|
||||
ResetImeCallbacks();
|
||||
ime_field_size = 127;
|
||||
ime_callback = SingleValueImeCallback;
|
||||
Dialog::initImeDialog(lang_strings[STR_PASSWORD], remote_settings->password, 127, ORBIS_TYPE_BASIC_LATIN, pos.x, pos.y);
|
||||
gui_mode = GUI_MODE_IME;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s:", lang_strings[STR_ENABLE_RPI]);
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s:", lang_strings[STR_PASSWORD]);
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Checkbox("###enable_rpi", &remote_settings->enable_rpi))
|
||||
sprintf(id, "%s##password", hidden_password.c_str());
|
||||
pos = ImGui::GetCursorPos();
|
||||
if (ImGui::Button(id, ImVec2(100, 0)))
|
||||
{
|
||||
CONFIG::SaveConfig();
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(450, 135));
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 440);
|
||||
ImGui::Text("%s", (remote_settings->type == CLIENT_TYPE_SMB || remote_settings->type == CLIENT_TYPE_FTP) ? lang_strings[STR_ENABLE_RPI_FTP_SMB_MSG] : lang_strings[STR_ENABLE_RPI_WEBDAV_MSG]);
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
ime_single_field = remote_settings->password;
|
||||
ResetImeCallbacks();
|
||||
ime_field_size = 127;
|
||||
ime_callback = SingleValueImeCallback;
|
||||
Dialog::initImeDialog(lang_strings[STR_PASSWORD], remote_settings->password, 127, ORBIS_TYPE_BASIC_LATIN, pos.x, pos.y);
|
||||
gui_mode = GUI_MODE_IME;
|
||||
}
|
||||
}
|
||||
|
||||
if ((remote_settings->type == CLIENT_TYPE_NFS || remote_settings->type == CLIENT_TYPE_SMB || remote_settings->type == CLIENT_TYPE_FTP) && remote_settings->enable_rpi)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s:", lang_strings[STR_HTTP_PORT]);
|
||||
ImGui::SameLine();
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s:", lang_strings[STR_ENABLE_RPI]);
|
||||
ImGui::SameLine();
|
||||
|
||||
sprintf(id, "%s##http_port", txt_http_port);
|
||||
pos = ImGui::GetCursorPos();
|
||||
if (ImGui::Button(id, ImVec2(65, 0)))
|
||||
{
|
||||
ime_single_field = txt_http_port;
|
||||
ResetImeCallbacks();
|
||||
ime_field_size = 24;
|
||||
ime_callback = SingleValueImeCallback;
|
||||
ime_after_update = AfterHttpPortChangeCallback;
|
||||
Dialog::initImeDialog(lang_strings[STR_PASSWORD], txt_http_port, 24, ORBIS_TYPE_NUMBER, pos.x, pos.y);
|
||||
gui_mode = GUI_MODE_IME;
|
||||
}
|
||||
}
|
||||
if (ImGui::Checkbox("###enable_rpi", &remote_settings->enable_rpi))
|
||||
{
|
||||
CONFIG::SaveConfig();
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(450, 70));
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 440);
|
||||
ImGui::Text("%s", lang_strings[STR_ENABLE_RPI_FTP_SMB_MSG]);
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
@@ -1076,26 +1062,29 @@ namespace Windows
|
||||
ImGui::PopID();
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushID("Extract##settings");
|
||||
if (ImGui::Selectable(lang_strings[STR_EXTRACT], false, getSelectableFlag(REMOTE_ACTION_EXTRACT) | ImGuiSelectableFlags_DontClosePopups, ImVec2(220, 0)))
|
||||
{
|
||||
ResetImeCallbacks();
|
||||
sprintf(extract_zip_folder, "%s", getExtractFolder().c_str());
|
||||
ime_single_field = extract_zip_folder;
|
||||
ime_field_size = 255;
|
||||
ime_callback = SingleValueImeCallback;
|
||||
if (local_browser_selected)
|
||||
ime_after_update = AfterExtractFolderCallback;
|
||||
else
|
||||
ime_after_update = AfterExtractRemoteFolderCallback;
|
||||
Dialog::initImeDialog(lang_strings[STR_EXTRACT_LOCATION], extract_zip_folder, 255, ORBIS_TYPE_BASIC_LATIN, 600, 350);
|
||||
gui_mode = GUI_MODE_IME;
|
||||
file_transfering = false;
|
||||
SetModalMode(false);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::PopID();
|
||||
ImGui::Separator();
|
||||
|
||||
if (local_browser_selected)
|
||||
{
|
||||
ImGui::PushID("Extract##settings");
|
||||
if (ImGui::Selectable(lang_strings[STR_EXTRACT], false, getSelectableFlag(REMOTE_ACTION_NONE) | ImGuiSelectableFlags_DontClosePopups, ImVec2(220, 0)))
|
||||
{
|
||||
ResetImeCallbacks();
|
||||
sprintf(extract_zip_folder, "%s", getExtractFolder().c_str());
|
||||
ime_single_field = extract_zip_folder;
|
||||
ime_field_size = 255;
|
||||
ime_callback = SingleValueImeCallback;
|
||||
ime_after_update = AfterExtractFolderCallback;
|
||||
Dialog::initImeDialog(lang_strings[STR_EXTRACT_LOCATION], extract_zip_folder, 255, ORBIS_TYPE_BASIC_LATIN, 600, 350);
|
||||
gui_mode = GUI_MODE_IME;
|
||||
file_transfering = true;
|
||||
SetModalMode(false);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::PopID();
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushID("Compress##settings");
|
||||
if (ImGui::Selectable(lang_strings[STR_COMPRESS], false, getSelectableFlag(REMOTE_ACTION_NONE) | ImGuiSelectableFlags_DontClosePopups, ImVec2(220, 0)))
|
||||
{
|
||||
@@ -1632,11 +1621,38 @@ namespace Windows
|
||||
ImGui::OpenPopup(lang_strings[STR_SETTINGS]);
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(1050, 80));
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(850, 80), ImVec2(850, 500), NULL, NULL);
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(850, 80), ImVec2(850, 650), NULL, NULL);
|
||||
if (ImGui::BeginPopupModal(lang_strings[STR_SETTINGS], NULL, ImGuiWindowFlags_AlwaysAutoResize))
|
||||
{
|
||||
char id[192];
|
||||
ImVec2 field_size;
|
||||
float width;
|
||||
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s", lang_strings[STR_GLOBAL]);
|
||||
ImGui::Separator();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 15);
|
||||
ImGui::Text("%s", lang_strings[STR_LANGUAGE]);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 15);
|
||||
ImGui::SetNextItemWidth(690);
|
||||
if (ImGui::BeginCombo("##Language", language, ImGuiComboFlags_PopupAlignLeft | ImGuiComboFlags_HeightLargest))
|
||||
{
|
||||
for (int n = 0; n < langs.size(); n++)
|
||||
{
|
||||
const bool is_selected = strcmp(langs[n].c_str(), language) == 0;
|
||||
if (ImGui::Selectable(langs[n].c_str(), is_selected))
|
||||
{
|
||||
sprintf(language, "%s", langs[n].c_str());
|
||||
}
|
||||
|
||||
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
|
||||
if (is_selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 15);
|
||||
ImGui::Text("%s", lang_strings[STR_AUTO_DELETE_TMP_PKG]);
|
||||
ImGui::SameLine();
|
||||
@@ -1650,6 +1666,26 @@ namespace Windows
|
||||
ImGui::Checkbox("##show_hidden_files", &show_hidden_files);
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 15);
|
||||
ImGui::Text("%s", lang_strings[STR_TEMP_DIRECTORY]);
|
||||
ImGui::SameLine();
|
||||
field_size = ImGui::CalcTextSize(lang_strings[STR_TEMP_DIRECTORY]);
|
||||
width = field_size.x + 45;
|
||||
sprintf(id, "%s##temp_direcotry", temp_folder);
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 15);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 1.0f));
|
||||
if (ImGui::Button(id, ImVec2(835-width, 0)))
|
||||
{
|
||||
ResetImeCallbacks();
|
||||
ime_single_field = temp_folder;
|
||||
ime_field_size = 512;
|
||||
ime_callback = SingleValueImeCallback;
|
||||
Dialog::initImeDialog(lang_strings[STR_COMPRESSED_FILE_PATH], temp_folder, 255, ORBIS_TYPE_BASIC_LATIN, 1050, 80);
|
||||
gui_mode = GUI_MODE_IME;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::Separator();
|
||||
|
||||
// Web Server settings
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s", lang_strings[STR_WEB_SERVER]);
|
||||
ImGui::Separator();
|
||||
@@ -1660,15 +1696,13 @@ namespace Windows
|
||||
ImGui::Checkbox("##web_server_enabled", &web_server_enabled);
|
||||
ImGui::Separator();
|
||||
|
||||
ImVec2 field_size;
|
||||
field_size = ImGui::CalcTextSize(lang_strings[STR_PORT]);
|
||||
float width = field_size.x + 45;
|
||||
width = field_size.x + 45;
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 15);
|
||||
ImGui::Text("%s", lang_strings[STR_PORT]);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 1.0f));
|
||||
|
||||
char id[192];
|
||||
sprintf(id, "%s##http_server_port", txt_http_server_port);
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 15);
|
||||
if (ImGui::Button(id, ImVec2(835-width, 0)))
|
||||
@@ -1701,6 +1735,60 @@ namespace Windows
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s", lang_strings[STR_ALLDEBRID]);
|
||||
ImGui::Separator();
|
||||
|
||||
field_size = ImGui::CalcTextSize(lang_strings[STR_API_KEY]);
|
||||
width = field_size.x + 45;
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 15);
|
||||
ImGui::Text("%s", lang_strings[STR_API_KEY]);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(width);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 1.0f));
|
||||
|
||||
if (strlen(alldebrid_api_key) > 0)
|
||||
sprintf(id, "%s", "*********************************************##alldebrid_api_key");
|
||||
else
|
||||
sprintf(id, "%s", "##alldebrid_api_key");
|
||||
if (ImGui::Button(id, ImVec2(835-width, 0)))
|
||||
{
|
||||
ResetImeCallbacks();
|
||||
ime_single_field = alldebrid_api_key;
|
||||
ime_field_size = 63;
|
||||
ime_callback = SingleValueImeCallback;
|
||||
Dialog::initImeDialog(lang_strings[STR_API_KEY], alldebrid_api_key, 63, ORBIS_TYPE_BASIC_LATIN, 1050, 80);
|
||||
gui_mode = GUI_MODE_IME;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s", lang_strings[STR_REALDEBRID]);
|
||||
ImGui::Separator();
|
||||
|
||||
field_size = ImGui::CalcTextSize(lang_strings[STR_API_KEY]);
|
||||
width = field_size.x + 45;
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 15);
|
||||
ImGui::Text("%s", lang_strings[STR_API_KEY]);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(width);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 1.0f));
|
||||
|
||||
if (strlen(realdebrid_api_key) > 0)
|
||||
sprintf(id, "%s", "*********************************************##realdebrid_api_key");
|
||||
else
|
||||
sprintf(id, "%s", "##realdebrid_api_key");
|
||||
if (ImGui::Button(id, ImVec2(835-width, 0)))
|
||||
{
|
||||
ResetImeCallbacks();
|
||||
ime_single_field = realdebrid_api_key;
|
||||
ime_field_size = 63;
|
||||
ime_callback = SingleValueImeCallback;
|
||||
Dialog::initImeDialog(lang_strings[STR_API_KEY], realdebrid_api_key, 63, ORBIS_TYPE_BASIC_LATIN, 1050, 80);
|
||||
gui_mode = GUI_MODE_IME;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::Separator();
|
||||
|
||||
// Google settings
|
||||
ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s", lang_strings[STR_GOOGLE]);
|
||||
ImGui::Separator();
|
||||
@@ -1999,10 +2087,19 @@ namespace Windows
|
||||
activity_inprogess = true;
|
||||
sprintf(activity_message, "%s", "");
|
||||
stop_activity = false;
|
||||
file_transfering = true;
|
||||
file_transfering = false;
|
||||
selected_action = ACTION_NONE;
|
||||
Actions::ExtractLocalZips();
|
||||
break;
|
||||
case ACTION_EXTRACT_REMOTE_ZIP:
|
||||
sprintf(status_message, "%s", "");
|
||||
activity_inprogess = true;
|
||||
sprintf(activity_message, "%s", "");
|
||||
stop_activity = false;
|
||||
file_transfering = false;
|
||||
selected_action = ACTION_NONE;
|
||||
Actions::ExtractRemoteZips();
|
||||
break;
|
||||
case ACTION_CREATE_LOCAL_ZIP:
|
||||
sprintf(status_message, "%s", "");
|
||||
activity_inprogess = true;
|
||||
@@ -2382,6 +2479,11 @@ namespace Windows
|
||||
selected_action = ACTION_EXTRACT_LOCAL_ZIP;
|
||||
}
|
||||
|
||||
void AfterExtractRemoteFolderCallback(int ime_result)
|
||||
{
|
||||
selected_action = ACTION_EXTRACT_REMOTE_ZIP;
|
||||
}
|
||||
|
||||
void AfterZipFileCallback(int ime_result)
|
||||
{
|
||||
selected_action = ACTION_CREATE_LOCAL_ZIP;
|
||||
@@ -2399,7 +2501,6 @@ namespace Windows
|
||||
{
|
||||
if (ime_result == IME_DIALOG_RESULT_FINISHED)
|
||||
{
|
||||
remote_settings->http_port = atoi(txt_http_port);
|
||||
http_server_port = atoi(txt_http_server_port);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +206,7 @@ namespace Windows
|
||||
void AfterPackageUrlCallback(int ime_result);
|
||||
void AfterFavoriteUrlCallback(int ime_result);
|
||||
void AfterExtractFolderCallback(int ime_result);
|
||||
void AfterExtractRemoteFolderCallback(int ime_result);
|
||||
void AfterZipFileCallback(int ime_result);
|
||||
void AferServerChangeCallback(int ime_result);
|
||||
void AfterHttpPortChangeCallback(int ime_result);
|
||||
|
||||
+499
-119
@@ -9,18 +9,24 @@
|
||||
#include <minizip/zip.h>
|
||||
#include <un7zip.h>
|
||||
#include <unrar.h>
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
|
||||
#include "clients/remote_client.h"
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "fs.h"
|
||||
#include "ime_dialog.h"
|
||||
#include "lang.h"
|
||||
#include "system.h"
|
||||
#include "windows.h"
|
||||
#include "util.h"
|
||||
#include "zip_util.h"
|
||||
|
||||
#define TRANSFER_SIZE (128 * 1024)
|
||||
|
||||
namespace ZipUtil
|
||||
{
|
||||
static char filename_extracted[256];
|
||||
static char password[128];
|
||||
|
||||
void callback_7zip(const char *fileName, unsigned long fileSize, unsigned fileNum, unsigned numFiles)
|
||||
{
|
||||
@@ -90,12 +96,12 @@ namespace ZipUtil
|
||||
}
|
||||
|
||||
// Add file to zip
|
||||
void *buf = memalign(4096, TRANSFER_SIZE);
|
||||
void *buf = memalign(4096, ARCHIVE_TRANSFER_SIZE);
|
||||
uint64_t seek = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int read = FS::Read(fd, buf, TRANSFER_SIZE);
|
||||
int read = FS::Read(fd, buf, ARCHIVE_TRANSFER_SIZE);
|
||||
if (read < 0)
|
||||
{
|
||||
free(buf);
|
||||
@@ -217,153 +223,527 @@ namespace ZipUtil
|
||||
return 1;
|
||||
}
|
||||
|
||||
CompressFileType getCompressFileType(const std::string &file)
|
||||
/* duplicate a path name, possibly converting to lower case */
|
||||
static char *pathdup(const char *path)
|
||||
{
|
||||
char buf[8];
|
||||
char *str;
|
||||
size_t i, len;
|
||||
|
||||
memset(buf, 0, 8);
|
||||
int ret = FS::Head(file, buf, 8);
|
||||
if (ret == 0)
|
||||
return COMPRESS_FILE_TYPE_UNKNOWN;
|
||||
if (path == NULL || path[0] == '\0')
|
||||
return (NULL);
|
||||
|
||||
if (strncmp(buf, (const char *)MAGIC_7Z_1, 6) == 0)
|
||||
return COMPRESS_FILE_TYPE_7Z;
|
||||
else if (strncmp(buf, (const char *)MAGIC_RAR_1, 7) == 0 || strncmp(buf, (const char *)MAGIC_RAR_2, 8) == 0)
|
||||
return COMPRESS_FILE_TYPE_RAR;
|
||||
else if (strncmp(buf, (const char *)MAGIC_ZIP_1, 4) == 0 || strncmp(buf, (const char *)MAGIC_ZIP_2, 4) == 0 || strncmp(buf, (const char *)MAGIC_ZIP_3, 4) == 0)
|
||||
return COMPRESS_FILE_TYPE_ZIP;
|
||||
len = strlen(path);
|
||||
while (len && path[len - 1] == '/')
|
||||
len--;
|
||||
if ((str = (char *)malloc(len + 1)) == NULL)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
}
|
||||
memcpy(str, path, len);
|
||||
|
||||
return COMPRESS_FILE_TYPE_UNKNOWN;
|
||||
str[len] = '\0';
|
||||
|
||||
return (str);
|
||||
}
|
||||
|
||||
int ExtractZip(const DirEntry &file, const std::string &dir)
|
||||
/* concatenate two path names */
|
||||
static char *pathcat(const char *prefix, const char *path)
|
||||
{
|
||||
file_transfering = true;
|
||||
unz_global_info global_info;
|
||||
unz_file_info file_info;
|
||||
unzFile zipfile = unzOpen(file.path);
|
||||
std::string dest_dir = std::string(dir);
|
||||
if (dest_dir[dest_dir.length() - 1] != '/')
|
||||
{
|
||||
dest_dir = dest_dir + "/";
|
||||
}
|
||||
if (zipfile == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
unzGetGlobalInfo(zipfile, &global_info);
|
||||
unzGoToFirstFile(zipfile);
|
||||
uint64_t curr_extracted_bytes = 0;
|
||||
uint64_t curr_file_bytes = 0;
|
||||
int num_files = global_info.number_entry;
|
||||
char fname[512];
|
||||
char ext_fname[512];
|
||||
char read_buffer[TRANSFER_SIZE];
|
||||
char *str;
|
||||
size_t prelen, len;
|
||||
|
||||
for (int zip_idx = 0; zip_idx < num_files; ++zip_idx)
|
||||
prelen = prefix ? strlen(prefix) + 1 : 0;
|
||||
len = strlen(path) + 1;
|
||||
if ((str = (char *)malloc(prelen + len)) == NULL)
|
||||
{
|
||||
if (stop_activity)
|
||||
break;
|
||||
unzGetCurrentFileInfo(zipfile, &file_info, fname, 512, NULL, 0, NULL, 0);
|
||||
sprintf(ext_fname, "%s%s", dest_dir.c_str(), fname);
|
||||
const size_t filename_length = strlen(ext_fname);
|
||||
bytes_transfered = 0;
|
||||
bytes_to_download = file_info.uncompressed_size;
|
||||
if (ext_fname[filename_length - 1] != '/')
|
||||
{
|
||||
snprintf(activity_message, 255, "%s %s: %s", lang_strings[STR_EXTRACTING], file.name, fname);
|
||||
curr_file_bytes = 0;
|
||||
unzOpenCurrentFile(zipfile);
|
||||
FS::MkDirs(ext_fname, true);
|
||||
FILE *f = fopen(ext_fname, "wb");
|
||||
while (curr_file_bytes < file_info.uncompressed_size)
|
||||
{
|
||||
int rbytes = unzReadCurrentFile(zipfile, read_buffer, TRANSFER_SIZE);
|
||||
if (rbytes > 0)
|
||||
{
|
||||
fwrite(read_buffer, 1, rbytes, f);
|
||||
curr_extracted_bytes += rbytes;
|
||||
curr_file_bytes += rbytes;
|
||||
bytes_transfered = curr_file_bytes;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
unzCloseCurrentFile(zipfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
FS::MkDirs(ext_fname, true);
|
||||
}
|
||||
if ((zip_idx + 1) < num_files)
|
||||
{
|
||||
unzGoToNextFile(zipfile);
|
||||
}
|
||||
errno = ENOMEM;
|
||||
}
|
||||
unzClose(zipfile);
|
||||
return 1;
|
||||
if (prefix)
|
||||
{
|
||||
memcpy(str, prefix, prelen); /* includes zero */
|
||||
str[prelen - 1] = '/'; /* splat zero */
|
||||
}
|
||||
memcpy(str + prelen, path, len); /* includes zero */
|
||||
|
||||
return (str);
|
||||
}
|
||||
|
||||
int Extract7Zip(const DirEntry &file, const std::string &dir)
|
||||
/*
|
||||
* Extract a directory.
|
||||
*/
|
||||
static void extract_dir(struct archive *a, struct archive_entry *e, const std::string &path)
|
||||
{
|
||||
file_transfering = false;
|
||||
FS::MkDirs(dir, true);
|
||||
sprintf(filename_extracted, "%s", file.name);
|
||||
int res = Extract7zFileEx(file.path, dir.c_str(), callback_7zip, DEFAULT_IN_BUF_SIZE);
|
||||
return res == 0;
|
||||
int mode;
|
||||
|
||||
if (path[0] == '\0')
|
||||
return;
|
||||
|
||||
FS::MkDirs(path);
|
||||
archive_read_data_skip(a);
|
||||
}
|
||||
|
||||
int ExtractRar(const DirEntry &file, const std::string &dir)
|
||||
/*
|
||||
* Extract to a file descriptor
|
||||
*/
|
||||
static int extract2fd(struct archive *a, const std::string &pathname, int fd)
|
||||
{
|
||||
file_transfering = false;
|
||||
HANDLE hArcData; // Archive Handle
|
||||
struct RAROpenArchiveDataEx rarOpenArchiveData;
|
||||
struct RARHeaderDataEx rarHeaderData;
|
||||
char destPath[256];
|
||||
ssize_t len;
|
||||
unsigned char *buffer = (unsigned char *) malloc(ARCHIVE_TRANSFER_SIZE);
|
||||
|
||||
memset(&rarOpenArchiveData, 0, sizeof(rarOpenArchiveData));
|
||||
memset(&rarHeaderData, 0, sizeof(rarHeaderData));
|
||||
|
||||
sprintf(destPath, "%s", dir.c_str());
|
||||
rarOpenArchiveData.ArcName = (char *)file.path;
|
||||
rarOpenArchiveData.CmtBuf = NULL;
|
||||
rarOpenArchiveData.CmtBufSize = 0;
|
||||
rarOpenArchiveData.OpenMode = RAR_OM_EXTRACT;
|
||||
hArcData = RAROpenArchiveEx(&rarOpenArchiveData);
|
||||
|
||||
if (rarOpenArchiveData.OpenResult != ERAR_SUCCESS)
|
||||
/* loop over file contents and write to fd */
|
||||
for (int n = 0;; n++)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
len = archive_read_data(a, buffer, ARCHIVE_TRANSFER_SIZE);
|
||||
|
||||
while (RARReadHeaderEx(hArcData, &rarHeaderData) == ERAR_SUCCESS)
|
||||
{
|
||||
sprintf(activity_message, "%s %s: %s", lang_strings[STR_EXTRACTING], file.name, rarHeaderData.FileName);
|
||||
if (RARProcessFile(hArcData, RAR_EXTRACT, destPath, NULL) != ERAR_SUCCESS)
|
||||
if (len == 0)
|
||||
{
|
||||
RARCloseArchive(hArcData);
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
sprintf(status_message, "error archive_read_data('%s')", pathname.c_str());
|
||||
free(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (write(fd, buffer, len) != len)
|
||||
{
|
||||
sprintf(status_message, "error write('%s')", pathname.c_str());
|
||||
free(buffer);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
RARCloseArchive(hArcData);
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Extract(const DirEntry &file, const std::string &dir)
|
||||
/*
|
||||
* Extract a regular file.
|
||||
*/
|
||||
static void extract_file(struct archive *a, struct archive_entry *e, const std::string &path)
|
||||
{
|
||||
CompressFileType fileType = getCompressFileType(file.path);
|
||||
struct stat sb;
|
||||
int fd;
|
||||
const char *linkname;
|
||||
|
||||
if (fileType == COMPRESS_FILE_TYPE_ZIP)
|
||||
return ExtractZip(file, dir);
|
||||
else if (fileType == COMPRESS_FILE_TYPE_7Z)
|
||||
return Extract7Zip(file, dir);
|
||||
else if (fileType == COMPRESS_FILE_TYPE_RAR)
|
||||
return ExtractRar(file, dir);
|
||||
/* look for existing file of same name */
|
||||
recheck:
|
||||
if (lstat(path.c_str(), &sb) == 0)
|
||||
{
|
||||
(void)unlink(path.c_str());
|
||||
}
|
||||
|
||||
/* process symlinks */
|
||||
linkname = archive_entry_symlink(e);
|
||||
if (linkname != NULL)
|
||||
{
|
||||
if (symlink(linkname, path.c_str()) != 0)
|
||||
{
|
||||
sprintf(status_message, "error symlink('%s')", path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
/* set access and modification time */
|
||||
return;
|
||||
}
|
||||
|
||||
if ((fd = open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0777)) < 0)
|
||||
{
|
||||
sprintf(status_message, "error open('%s')", path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
extract2fd(a, path, fd);
|
||||
|
||||
/* set access and modification time */
|
||||
if (close(fd) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void extract(struct archive *a, struct archive_entry *e, const std::string &base_dir)
|
||||
{
|
||||
char *pathname, *realpathname;
|
||||
mode_t filetype;
|
||||
|
||||
if ((pathname = pathdup(archive_entry_pathname(e))) == NULL)
|
||||
{
|
||||
archive_read_data_skip(a);
|
||||
return;
|
||||
}
|
||||
filetype = archive_entry_filetype(e);
|
||||
|
||||
/* sanity checks */
|
||||
if (pathname[0] == '/' ||
|
||||
strncmp(pathname, "../", 3) == 0 ||
|
||||
strstr(pathname, "/../") != NULL)
|
||||
{
|
||||
archive_read_data_skip(a);
|
||||
free(pathname);
|
||||
return;
|
||||
}
|
||||
|
||||
/* I don't think this can happen in a zipfile.. */
|
||||
if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype))
|
||||
{
|
||||
archive_read_data_skip(a);
|
||||
free(pathname);
|
||||
return;
|
||||
}
|
||||
|
||||
realpathname = pathcat(base_dir.c_str(), pathname);
|
||||
|
||||
/* ensure that parent directory exists */
|
||||
FS::MkDirs(realpathname, true);
|
||||
|
||||
if (S_ISDIR(filetype))
|
||||
extract_dir(a, e, realpathname);
|
||||
else
|
||||
{
|
||||
sprintf(status_message, "%s - %s", file.name, lang_strings[STR_UNSUPPORTED_FILE_FORMAT]);
|
||||
return -1;
|
||||
snprintf(activity_message, 255, "%s: %s", lang_strings[STR_EXTRACTING], pathname);
|
||||
extract_file(a, e, realpathname);
|
||||
}
|
||||
|
||||
free(realpathname);
|
||||
free(pathname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback function for reading passphrase.
|
||||
* Originally from cpio.c and passphrase.c, libarchive.
|
||||
*/
|
||||
static const char *passphrase_callback(struct archive *a, void *_client_data)
|
||||
{
|
||||
Dialog::initImeDialog(lang_strings[STR_PASSWORD], password, 127, ORBIS_TYPE_DEFAULT, 560, 200);
|
||||
int ime_result = Dialog::updateImeDialog();
|
||||
if (ime_result == IME_DIALOG_RESULT_FINISHED || ime_result == IME_DIALOG_RESULT_CANCELED)
|
||||
{
|
||||
if (ime_result == IME_DIALOG_RESULT_FINISHED)
|
||||
{
|
||||
snprintf(password, 127, "%s", (char *)Dialog::getImeDialogInputText());
|
||||
return password;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(password, 0, sizeof(password));
|
||||
}
|
||||
}
|
||||
|
||||
memset(password, 0, sizeof(password));
|
||||
return password;
|
||||
}
|
||||
|
||||
static RemoteArchiveData *OpenRemoteArchive(const std::string &file, RemoteClient *client)
|
||||
{
|
||||
RemoteArchiveData *data;
|
||||
|
||||
data = (RemoteArchiveData *)malloc(sizeof(RemoteArchiveData));
|
||||
memset(data, 0, sizeof(RemoteArchiveData));
|
||||
|
||||
data->offset = 0;
|
||||
client->Size(file, &data->size);
|
||||
data->client = client;
|
||||
data->path = file;
|
||||
return data;
|
||||
}
|
||||
|
||||
static ssize_t ReadRemoteArchive(struct archive *a, void *client_data, const void **buff)
|
||||
{
|
||||
ssize_t to_read;
|
||||
int ret;
|
||||
RemoteArchiveData *data;
|
||||
|
||||
data = (RemoteArchiveData *)client_data;
|
||||
*buff = data->buf;
|
||||
|
||||
to_read = data->size - data->offset;
|
||||
if (to_read == 0)
|
||||
return 0;
|
||||
|
||||
to_read = MIN(to_read, ARCHIVE_TRANSFER_SIZE);
|
||||
ret = data->client->GetRange(data->path, data->buf, to_read, data->offset);
|
||||
if (ret == 0)
|
||||
return -1;
|
||||
data->offset = data->offset + to_read;
|
||||
|
||||
return to_read;
|
||||
}
|
||||
|
||||
static int CloseRemoteArchive(struct archive *a, void *client_data)
|
||||
{
|
||||
if (client_data != nullptr)
|
||||
free(client_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main loop: open the zipfile, iterate over its contents and decide what
|
||||
* to do with each entry.
|
||||
*/
|
||||
int Extract(const DirEntry &file, const std::string &basepath, RemoteClient *client)
|
||||
{
|
||||
struct archive *a;
|
||||
struct archive_entry *e;
|
||||
RemoteArchiveData *client_data = nullptr;
|
||||
int ret;
|
||||
uintmax_t total_size, file_count, error_count;
|
||||
|
||||
if ((a = archive_read_new()) == NULL)
|
||||
sprintf(status_message, "%s", "archive_read_new failed");
|
||||
|
||||
archive_read_support_format_all(a);
|
||||
archive_read_support_filter_all(a);
|
||||
archive_read_set_passphrase_callback(a, NULL, &passphrase_callback);
|
||||
|
||||
if (client == nullptr)
|
||||
{
|
||||
ret = archive_read_open_filename(a, file.path, ARCHIVE_TRANSFER_SIZE);
|
||||
if (ret < ARCHIVE_OK)
|
||||
{
|
||||
sprintf(status_message, "%s", "archive_read_open_filename failed");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
client_data = OpenRemoteArchive(file.path, client);
|
||||
if (client_data == nullptr)
|
||||
{
|
||||
sprintf(status_message, "%s", "archive_read_open_filename failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = archive_read_open(a, client_data, NULL, ReadRemoteArchive, CloseRemoteArchive);
|
||||
if (ret < ARCHIVE_OK)
|
||||
{
|
||||
if (client_data != nullptr)
|
||||
{
|
||||
free(client_data);
|
||||
}
|
||||
sprintf(status_message, "%s", "archive_read_open failed");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (stop_activity)
|
||||
break;
|
||||
|
||||
ret = archive_read_next_header(a, &e);
|
||||
|
||||
if (ret < ARCHIVE_OK)
|
||||
{
|
||||
sprintf(status_message, "%s", "archive_read_next_header failed");
|
||||
archive_read_free(a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ret == ARCHIVE_EOF)
|
||||
break;
|
||||
|
||||
extract(a, e, basepath);
|
||||
}
|
||||
|
||||
archive_read_free(a);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
ArchiveEntry *GetPackageEntry(const std::string &zip_file, RemoteClient *client)
|
||||
{
|
||||
struct archive *a;
|
||||
struct archive_entry *e;
|
||||
RemoteArchiveData *client_data = nullptr;
|
||||
char *pathname;
|
||||
mode_t filetype;
|
||||
ArchiveEntry *pkg_entry = nullptr;
|
||||
int ret;
|
||||
|
||||
if ((a = archive_read_new()) == NULL)
|
||||
sprintf(status_message, "%s", "archive_read_new failed");
|
||||
|
||||
archive_read_support_format_all(a);
|
||||
archive_read_support_filter_all(a);
|
||||
archive_read_set_passphrase_callback(a, NULL, &passphrase_callback);
|
||||
|
||||
if (client == nullptr)
|
||||
{
|
||||
ret = archive_read_open_filename(a, zip_file.c_str(), ARCHIVE_TRANSFER_SIZE);
|
||||
if (ret < ARCHIVE_OK)
|
||||
{
|
||||
sprintf(status_message, "%s", "archive_read_open_filename failed");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
client_data = OpenRemoteArchive(zip_file, client);
|
||||
if (client_data == nullptr)
|
||||
{
|
||||
sprintf(status_message, "%s", "archive_read_open_filename failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret = archive_read_open(a, client_data, NULL, ReadRemoteArchive, CloseRemoteArchive);
|
||||
if (ret < ARCHIVE_OK)
|
||||
{
|
||||
if (client_data != nullptr)
|
||||
{
|
||||
free(client_data);
|
||||
}
|
||||
sprintf(status_message, "%s", "archive_read_open_filename failed");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
ret = archive_read_next_header(a, &e);
|
||||
|
||||
if (ret < ARCHIVE_OK)
|
||||
{
|
||||
sprintf(status_message, "%s", "archive_read_next_header failed");
|
||||
if (client_data != nullptr)
|
||||
{
|
||||
free(client_data);
|
||||
}
|
||||
archive_read_free(a);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ret == ARCHIVE_EOF)
|
||||
break;
|
||||
|
||||
if ((pathname = pathdup(archive_entry_pathname(e))) == NULL)
|
||||
{
|
||||
archive_read_data_skip(a);
|
||||
continue;
|
||||
}
|
||||
|
||||
filetype = archive_entry_filetype(e);
|
||||
|
||||
/* sanity checks */
|
||||
if (pathname[0] == '/' ||
|
||||
strncmp(pathname, "../", 3) == 0 ||
|
||||
strstr(pathname, "/../") != NULL)
|
||||
{
|
||||
archive_read_data_skip(a);
|
||||
free(pathname);
|
||||
continue;
|
||||
;
|
||||
}
|
||||
|
||||
/* I don't think this can happen in a zipfile.. */
|
||||
if (!S_ISREG(filetype))
|
||||
{
|
||||
archive_read_data_skip(a);
|
||||
free(pathname);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Util::EndsWith(Util::ToLower(pathname), ".pkg"))
|
||||
{
|
||||
pkg_entry = (ArchiveEntry *)malloc(sizeof(ArchiveEntry));
|
||||
memset(pkg_entry, 0, sizeof(ArchiveEntry));
|
||||
|
||||
pkg_entry->archive = a;
|
||||
pkg_entry->entry = e;
|
||||
pkg_entry->client_data = client_data;
|
||||
pkg_entry->filename = pathname;
|
||||
pkg_entry->filesize = archive_entry_size(e);
|
||||
|
||||
free(pathname);
|
||||
return pkg_entry;
|
||||
}
|
||||
|
||||
free(pathname);
|
||||
}
|
||||
|
||||
archive_read_free(a);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ArchiveEntry *GetNextPackageEntry(ArchiveEntry *archive_entry)
|
||||
{
|
||||
struct archive *a = archive_entry->archive;
|
||||
struct archive_entry *e = nullptr;
|
||||
RemoteArchiveData *client_data = archive_entry->client_data;
|
||||
char *pathname;
|
||||
mode_t filetype;
|
||||
ArchiveEntry *pkg_entry = nullptr;
|
||||
int ret;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
ret = archive_read_next_header(a, &e);
|
||||
|
||||
if (ret < ARCHIVE_OK)
|
||||
{
|
||||
sprintf(status_message, "%s", "archive_read_next_header failed");
|
||||
if (client_data != nullptr)
|
||||
{
|
||||
free(client_data);
|
||||
}
|
||||
archive_read_free(a);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ret == ARCHIVE_EOF)
|
||||
break;
|
||||
|
||||
if ((pathname = pathdup(archive_entry_pathname(e))) == NULL)
|
||||
{
|
||||
archive_read_data_skip(a);
|
||||
continue;
|
||||
}
|
||||
|
||||
filetype = archive_entry_filetype(e);
|
||||
|
||||
/* sanity checks */
|
||||
if (pathname[0] == '/' ||
|
||||
strncmp(pathname, "../", 3) == 0 ||
|
||||
strstr(pathname, "/../") != NULL)
|
||||
{
|
||||
archive_read_data_skip(a);
|
||||
free(pathname);
|
||||
continue;
|
||||
;
|
||||
}
|
||||
|
||||
/* I don't think this can happen in a zipfile.. */
|
||||
if (!S_ISREG(filetype))
|
||||
{
|
||||
archive_read_data_skip(a);
|
||||
free(pathname);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Util::EndsWith(Util::ToLower(pathname), ".pkg"))
|
||||
{
|
||||
pkg_entry = (ArchiveEntry *)malloc(sizeof(ArchiveEntry));
|
||||
memset(pkg_entry, 0, sizeof(ArchiveEntry));
|
||||
|
||||
pkg_entry->archive = a;
|
||||
pkg_entry->entry = e;
|
||||
pkg_entry->client_data = client_data;
|
||||
pkg_entry->filename = pathname;
|
||||
pkg_entry->filesize = archive_entry_size(e);
|
||||
|
||||
free(pathname);
|
||||
return pkg_entry;
|
||||
}
|
||||
|
||||
free(pathname);
|
||||
}
|
||||
|
||||
archive_read_free(a);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
+25
-1
@@ -5,9 +5,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <minizip/unzip.h>
|
||||
#include <minizip/zip.h>
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
#include "common.h"
|
||||
#include "fs.h"
|
||||
|
||||
#define ARCHIVE_TRANSFER_SIZE 5242880
|
||||
|
||||
static uint8_t MAGIC_ZIP_1[4] = {0x50, 0x4B, 0x03, 0x04};
|
||||
static uint8_t MAGIC_ZIP_2[4] = {0x50, 0x4B, 0x05, 0x06};
|
||||
static uint8_t MAGIC_ZIP_3[4] = {0x50, 0x4B, 0x07, 0x08};
|
||||
@@ -24,9 +28,29 @@ enum CompressFileType {
|
||||
COMPRESS_FILE_TYPE_UNKNOWN
|
||||
};
|
||||
|
||||
struct RemoteArchiveData
|
||||
{
|
||||
std::string path;
|
||||
ssize_t size;
|
||||
ssize_t offset;
|
||||
uint8_t buf[ARCHIVE_TRANSFER_SIZE];
|
||||
RemoteClient *client;
|
||||
};
|
||||
|
||||
struct ArchiveEntry
|
||||
{
|
||||
struct archive *archive;
|
||||
struct archive_entry *entry;
|
||||
std::string filename;
|
||||
size_t filesize;
|
||||
RemoteArchiveData *client_data;
|
||||
};
|
||||
|
||||
namespace ZipUtil
|
||||
{
|
||||
int ZipAddPath(zipFile zf, const std::string &path, int filename_start, int level);
|
||||
int Extract(const DirEntry &file, const std::string &dir);
|
||||
int Extract(const DirEntry &file, const std::string &dir, RemoteClient *client = nullptr);
|
||||
ArchiveEntry *GetPackageEntry(const std::string &zip_file, RemoteClient *client = nullptr);
|
||||
ArchiveEntry *GetNextPackageEntry(ArchiveEntry *archive_entry);
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user