Home all error fix, multi plugin cookie support, completion url semi-wildcard support, delete captcha button, critical exception support, dev portal can now request captchas. WIP Consent fix

This commit is contained in:
Kelvin 2023-10-20 14:33:37 +02:00
parent 14df7c8d43
commit a04acbd4a5
15 changed files with 141 additions and 16 deletions

View File

@ -217,6 +217,9 @@ function pluginUpdateTestPlugin(config) {
} }
function pluginLoginTestPlugin() { function pluginLoginTestPlugin() {
return syncGET("/plugin/loginTestPlugin", {}); return syncGET("/plugin/loginTestPlugin", {});
}//captchaLoginTestPlugin
function pluginCaptchaTestPlugin(url, html) {
return syncPOST("/plugin/captchaTestPlugin?url=" + url, {}, html);
} }
function pluginLogoutTestPlugin() { function pluginLogoutTestPlugin() {
return syncGET("/plugin/logoutTestPlugin", {}); return syncGET("/plugin/logoutTestPlugin", {});

View File

@ -681,6 +681,9 @@
}); });
}, 1000); }, 1000);
}, },
captchaTestPlugin() {
captchaLoginTestPlugin();
},
logoutTestPlugin() { logoutTestPlugin() {
pluginLogoutTestPlugin(); pluginLogoutTestPlugin();
}, },
@ -838,6 +841,12 @@
this.Testing.lastResultError = ""; this.Testing.lastResultError = "";
} }
catch(ex) { catch(ex) {
if(ex.plugin_type == "CaptchaRequiredException") {
let shouldCaptcha = confirm("Do you want to request captcha?");
if(shouldCaptcha) {
pluginCaptchaTestPlugin(ex.url, ex.body);
}
}
console.error("Failed to run test for " + req.title, ex); console.error("Failed to run test for " + req.title, ex);
this.Testing.lastResult = "" this.Testing.lastResult = ""
if(ex.message) if(ex.message)

View File

@ -72,6 +72,11 @@ class CaptchaRequiredException extends Error {
this.body = body; this.body = body;
} }
} }
class CriticalException extends ScriptException {
constructor(msg) {
super("CriticalException", msg);
}
}
class UnavailableException extends ScriptException { class UnavailableException extends ScriptException {
constructor(msg) { constructor(msg) {
super("UnavailableException", msg); super("UnavailableException", msg);

View File

@ -7,6 +7,7 @@ import com.futo.platformplayer.api.media.models.comments.IPlatformComment
import com.futo.platformplayer.api.media.models.contents.IPlatformContent import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails
import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.api.media.structures.IPager
import com.futo.platformplayer.states.StateApp
import java.util.* import java.util.*
class DevJSClient : JSClient { class DevJSClient : JSClient {
@ -24,6 +25,10 @@ class DevJSClient : JSClient {
_auth = auth; _auth = auth;
_captcha = captcha; _captcha = captcha;
this.devID = devID ?: UUID.randomUUID().toString().substring(0, 5); this.devID = devID ?: UUID.randomUUID().toString().substring(0, 5);
onCaptchaException.subscribe { client, captcha ->
StateApp.instance.handleCaptchaException(client, captcha);
}
} }
//TODO: Misisng auth/captcha pass on purpose? //TODO: Misisng auth/captcha pass on purpose?
constructor(context: Context, descriptor: SourcePluginDescriptor, script: String, auth: SourceAuth? = null, captcha: SourceCaptchaData? = null, savedState: String? = null, devID: String? = null): super(context, descriptor, savedState, script) { constructor(context: Context, descriptor: SourcePluginDescriptor, script: String, auth: SourceAuth? = null, captcha: SourceCaptchaData? = null, savedState: String? = null, devID: String? = null): super(context, descriptor, savedState, script) {
@ -31,6 +36,10 @@ class DevJSClient : JSClient {
_auth = auth; _auth = auth;
_captcha = captcha; _captcha = captcha;
this.devID = devID ?: UUID.randomUUID().toString().substring(0, 5); this.devID = devID ?: UUID.randomUUID().toString().substring(0, 5);
onCaptchaException.subscribe { client, captcha ->
StateApp.instance.handleCaptchaException(client, captcha);
}
} }
fun setCaptcha(captcha: SourceCaptchaData? = null) { fun setCaptcha(captcha: SourceCaptchaData? = null) {

View File

@ -68,7 +68,12 @@ class JSHttpClient : ManagedHttpClient {
if(cookiesToApply.size > 0) { if(cookiesToApply.size > 0) {
val cookieString = cookiesToApply.map { it.key + "=" + it.value }.joinToString("; "); val cookieString = cookiesToApply.map { it.key + "=" + it.value }.joinToString("; ");
request.headers["Cookie"] = cookieString;
val existingCookies = request.headers["Cookie"];
if(!existingCookies.isNullOrEmpty())
request.headers["Cookie"] = existingCookies.trim(';') + "; " + cookieString;
else
request.headers["Cookie"] = cookieString;
} }
//printTestCode(request.url, request.body, auth.headers, cookieString, request.headers.filter { !auth.headers.containsKey(it.key) }); //printTestCode(request.url, request.body, auth.headers, cookieString, request.headers.filter { !auth.headers.containsKey(it.key) });
} }

View File

@ -69,9 +69,17 @@ abstract class MultiRefreshPager<T>: IRefreshPager<T>, IPager<T> {
if(pagerToAdd == null) { if(pagerToAdd == null) {
if(toReplacePager != null && toReplacePager is PlaceholderPager && error != null) { if(toReplacePager != null && toReplacePager is PlaceholderPager && error != null) {
val pluginId = toReplacePager.placeholderFactory.invoke().id?.pluginId ?: ""; val pluginId = toReplacePager.placeholderFactory.invoke().id?.pluginId ?: "";
_currentPager = PlaceholderPager(5) {
_pagersReusable.add((PlaceholderPager(5) {
return@PlaceholderPager PlatformContentPlaceholder(pluginId, error) return@PlaceholderPager PlatformContentPlaceholder(pluginId, error)
} as IPager<T>; } as IPager<T>).asReusable());
_currentPager = recreatePager(getCurrentSubPagers());
if(_currentPager is MultiParallelPager<*>)
runBlocking { (_currentPager as MultiParallelPager).initialize(); };
else if(_currentPager is MultiPager<*>)
(_currentPager as MultiPager).initialize()
onPagerChanged.emit(_currentPager); onPagerChanged.emit(_currentPager);
} }
return; return;

View File

@ -1,6 +1,7 @@
package com.futo.platformplayer.developer package com.futo.platformplayer.developer
import android.content.Context import android.content.Context
import com.futo.platformplayer.activities.CaptchaActivity
import com.futo.platformplayer.activities.LoginActivity import com.futo.platformplayer.activities.LoginActivity
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.api.http.server.HttpContext import com.futo.platformplayer.api.http.server.HttpContext
@ -201,6 +202,28 @@ class DeveloperEndpoints(private val context: Context) {
context.respondCode(500, (ex::class.simpleName + ":" + ex.message) ?: "", "text/plain") context.respondCode(500, (ex::class.simpleName + ":" + ex.message) ?: "", "text/plain")
} }
} }
@HttpPOST("/plugin/captchaTestPlugin")
fun pluginCaptchaTestPlugin(context: HttpContext) {
val config = _testPlugin?.config as SourcePluginConfig;
val url = context.query.get("url")
val html = context.readContentString();
try {
val captchaConfig = config.captcha;
if(captchaConfig == null) {
context.respondCode(403, "This plugin doesn't support captcha");
return;
}
CaptchaActivity.showCaptcha(StateApp.instance.context, config, url, html) {
_testPluginVariables.clear();
_testPlugin = V8Plugin(StateApp.instance.context, config, null, JSHttpClient(null, null, it), JSHttpClient(null, null, it));
};
context.respondCode(200, "Captcha started");
}
catch(ex: Throwable) {
context.respondCode(500, (ex::class.simpleName + ":" + ex.message) ?: "", "text/plain")
}
}
@HttpGET("/plugin/loginTestPlugin") @HttpGET("/plugin/loginTestPlugin")
fun pluginLoginTestPlugin(context: HttpContext) { fun pluginLoginTestPlugin(context: HttpContext) {
val config = _testPlugin?.config as SourcePluginConfig; val config = _testPlugin?.config as SourcePluginConfig;

View File

@ -298,6 +298,7 @@ class V8Plugin {
private fun throwExceptionFromV8(config: IV8PluginConfig, pluginType: String, msg: String, innerEx: Exception? = null, stack: String? = null, code: String? = null) { private fun throwExceptionFromV8(config: IV8PluginConfig, pluginType: String, msg: String, innerEx: Exception? = null, stack: String? = null, code: String? = null) {
when(pluginType) { when(pluginType) {
"ScriptException" -> throw ScriptException(config, msg, innerEx, stack, code); "ScriptException" -> throw ScriptException(config, msg, innerEx, stack, code);
"CriticalException" -> throw ScriptCriticalException(config, msg, innerEx, stack, code);
"AgeException" -> throw ScriptAgeException(config, msg, innerEx, stack, code); "AgeException" -> throw ScriptAgeException(config, msg, innerEx, stack, code);
"UnavailableException" -> throw ScriptUnavailableException(config, msg, innerEx, stack, code); "UnavailableException" -> throw ScriptUnavailableException(config, msg, innerEx, stack, code);
"ScriptExecutionException" -> throw ScriptExecutionException(config, msg, innerEx, stack, code); "ScriptExecutionException" -> throw ScriptExecutionException(config, msg, innerEx, stack, code);

View File

@ -0,0 +1,17 @@
package com.futo.platformplayer.engine.exceptions
import com.caoccao.javet.values.reference.V8ValueObject
import com.futo.platformplayer.engine.IV8PluginConfig
import com.futo.platformplayer.getOrThrow
open class ScriptCriticalException(config: IV8PluginConfig, error: String, ex: Exception? = null, stack: String? = null, code: String? = null) : ScriptException(config, error, ex, stack, code) {
companion object {
fun fromV8(config: IV8PluginConfig, obj: V8ValueObject) : ScriptException {
return ScriptCriticalException(config, obj.getOrThrow(config, "message", "ScriptCriticalException"));
}
}
}

View File

@ -258,11 +258,17 @@ class SourceDetailFragment : MainFragment() {
} }
} }
val clientIfExists = StatePlugins.instance.getPlugin(config.id);
groups.add( groups.add(
BigButtonGroup(c, "Management", BigButtonGroup(c, "Management",
BigButton(c, "Uninstall", "Removes the plugin from the app", R.drawable.ic_block) { BigButton(c, "Uninstall", "Removes the plugin from the app", R.drawable.ic_block) {
uninstallSource(); uninstallSource();
}.withBackground(R.drawable.background_big_button_red) }.withBackground(R.drawable.background_big_button_red),
if(clientIfExists?.captchaEncrypted != null)
BigButton(c, "Delete Captcha", "Deletes captcha for this plugin", R.drawable.ic_block) {
clientIfExists?.updateCaptcha(null);
}.withBackground(R.drawable.background_big_button_red)
else null
) )
) )

View File

@ -2,6 +2,7 @@ package com.futo.platformplayer.others
import android.net.Uri import android.net.Uri
import android.webkit.* import android.webkit.*
import com.futo.platformplayer.BuildConfig
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.api.media.Serializer import com.futo.platformplayer.api.media.Serializer
import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event1
@ -43,6 +44,8 @@ class LoginWebViewClient : WebViewClient {
private var urlFound = false; private var urlFound = false;
override fun onPageFinished(view: WebView?, url: String?) { override fun onPageFinished(view: WebView?, url: String?) {
if(BuildConfig.DEBUG)
Logger.i(TAG, "Login Url Page: " + url);
super.onPageFinished(view, url); super.onPageFinished(view, url);
onPageLoaded.emit(view, url); onPageLoaded.emit(view, url);
} }
@ -56,11 +59,29 @@ class LoginWebViewClient : WebViewClient {
return null; return null;
} }
var completionUrlExcludeQuery = false
var completionUrlToCheck = if(urlFound) null else _authConfig.completionUrl;
if(completionUrlToCheck != null) {
if(completionUrlToCheck.endsWith("?*")) {
completionUrlToCheck = completionUrlToCheck.substring(0, completionUrlToCheck.length - 2);
completionUrlExcludeQuery = true;
}
}
val domain = request.url.host; val domain = request.url.host;
val domainLower = request.url.host?.lowercase(); val domainLower = request.url.host?.lowercase();
val urlString = request.url.toString();
if(_authConfig.completionUrl == null) if(_authConfig.completionUrl == null)
urlFound = true; urlFound = true;
else urlFound = urlFound || request.url == Uri.parse(_authConfig.completionUrl); else urlFound = urlFound || (
if(completionUrlExcludeQuery)
(if(urlString.contains("?"))
urlString.substring(0, urlString.indexOf("?")) == completionUrlToCheck
else urlString == completionUrlToCheck)
else
request.url == Uri.parse(_authConfig.completionUrl)
);
//HEADERS //HEADERS
if(domainLower != null) { if(domainLower != null) {

View File

@ -29,6 +29,7 @@ import com.futo.platformplayer.activities.CaptchaActivity
import com.futo.platformplayer.activities.IWithResultLauncher import com.futo.platformplayer.activities.IWithResultLauncher
import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.api.media.Serializer import com.futo.platformplayer.api.media.Serializer
import com.futo.platformplayer.api.media.platforms.js.DevJSClient
import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.JSClient
import com.futo.platformplayer.api.media.platforms.js.internal.JSHttpClient import com.futo.platformplayer.api.media.platforms.js.internal.JSHttpClient
import com.futo.platformplayer.background.BackgroundWorker import com.futo.platformplayer.background.BackgroundWorker
@ -672,13 +673,20 @@ class StateApp {
UIDialogs.showConfirmationDialog(context, "Captcha required\nPlugin [${client.config.name}]", { UIDialogs.showConfirmationDialog(context, "Captcha required\nPlugin [${client.config.name}]", {
CaptchaActivity.showCaptcha(context, client.config, exception.url, exception.body) { CaptchaActivity.showCaptcha(context, client.config, exception.url, exception.body) {
hasCaptchaDialog = false; hasCaptchaDialog = false;
StatePlugins.instance.setPluginCaptcha(client.config.id, it);
scopeOrNull?.launch(Dispatchers.IO) { if(client is DevJSClient) {
try { client.setCaptcha(it);
StatePlatform.instance.reloadClient(context, client.config.id); client.recreate(context);
} catch (e: Throwable) { }
Logger.e(SourceDetailFragment.TAG, "Failed to reload client.", e) else {
return@launch; StatePlugins.instance.setPluginCaptcha(client.config.id, it);
scopeOrNull?.launch(Dispatchers.IO) {
try {
StatePlatform.instance.reloadClient(context, client.config.id);
} catch (e: Throwable) {
Logger.e(SourceDetailFragment.TAG, "Failed to reload client.", e)
return@launch;
}
} }
} }
} }

View File

@ -16,6 +16,7 @@ import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.constructs.Event2 import com.futo.platformplayer.constructs.Event2
import com.futo.platformplayer.engine.exceptions.PluginException import com.futo.platformplayer.engine.exceptions.PluginException
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
import com.futo.platformplayer.engine.exceptions.ScriptCriticalException
import com.futo.platformplayer.exceptions.ChannelException import com.futo.platformplayer.exceptions.ChannelException
import com.futo.platformplayer.findNonRuntimeException import com.futo.platformplayer.findNonRuntimeException
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
@ -320,7 +321,16 @@ class StateSubscriptions {
synchronized(failedPlugins) { synchronized(failedPlugins) {
//Fail all subscription calls to plugin if it has a captcha issue //Fail all subscription calls to plugin if it has a captcha issue
if(ex.config is SourcePluginConfig && !failedPlugins.contains(ex.config.id)) { if(ex.config is SourcePluginConfig && !failedPlugins.contains(ex.config.id)) {
Logger.w(TAG, "Subscriptions fetch ignoring plugin [${ex.config.name}] due to Captcha"); Logger.w(TAG, "Subscriptionsgnoring plugin [${ex.config.name}] due to Captcha");
failedPlugins.add(ex.config.id);
}
}
}
else if(ex is ScriptCriticalException) {
synchronized(failedPlugins) {
//Fail all subscription calls to plugin if it has a critical issue
if(ex.config is SourcePluginConfig && !failedPlugins.contains(ex.config.id)) {
Logger.w(TAG, "Subscriptions ignoring plugin [${ex.config.name}] due to critical exception:\n" + ex.message);
failedPlugins.add(ex.config.id); failedPlugins.add(ex.config.id);
} }
} }

View File

@ -14,13 +14,13 @@ class BigButtonGroup : LinearLayout {
_header = findViewById(R.id.header_title); _header = findViewById(R.id.header_title);
_buttons = findViewById(R.id.buttons); _buttons = findViewById(R.id.buttons);
} }
constructor(context: Context, header: String, vararg buttons: BigButton) : super(context) { constructor(context: Context, header: String, vararg buttons: BigButton?) : super(context) {
inflate(context, R.layout.big_button_group, this); inflate(context, R.layout.big_button_group, this);
_header = findViewById(R.id.header_title); _header = findViewById(R.id.header_title);
_buttons = findViewById(R.id.buttons); _buttons = findViewById(R.id.buttons);
_header.text = header; _header.text = header;
for(button in buttons) for(button in buttons.filterNotNull())
_buttons.addView(button); _buttons.addView(button);
} }

@ -1 +1 @@
Subproject commit d05a959174bcceb616c9f42043466e9e1258f519 Subproject commit 4ffd2a48c7ebed2c8512e092a6bae4b5447aff34