Add universal captcha detection

This commit is contained in:
NerdNumber9 2019-04-18 22:08:09 -04:00
parent f0109aa796
commit 5c2fbec80a
3 changed files with 101 additions and 36 deletions

View File

@ -15,8 +15,8 @@ import java.nio.charset.Charset
class AutoSolvingWebViewClient(activity: SolveCaptchaActivity, class AutoSolvingWebViewClient(activity: SolveCaptchaActivity,
source: CaptchaCompletionVerifier, source: CaptchaCompletionVerifier,
injectScript: String?, injectScript: String?,
private val headers: Map<String, String>) headers: Map<String, String>)
: BasicWebViewClient(activity, source, injectScript) { : HeadersInjectingWebViewClient(activity, source, injectScript, headers) {
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? { override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
// Inject our custom script into the recaptcha iframes // Inject our custom script into the recaptcha iframes
@ -32,24 +32,6 @@ class AutoSolvingWebViewClient(activity: SolveCaptchaActivity,
doc.toString().byteInputStream(Charset.forName("UTF-8")).buffered() doc.toString().byteInputStream(Charset.forName("UTF-8")).buffered()
) )
} }
if(headers.isNotEmpty()) {
val response = activity.httpClient.newCall(request.toOkHttpRequest()
.newBuilder()
.apply {
headers.forEach { (n, v) -> addHeader(n, v) }
}
.build())
.execute()
return WebResourceResponse(
response.body()?.contentType()?.let { "${it.type()}/${it.subtype()}" },
response.body()?.contentType()?.charset()?.toString(),
response.code(),
response.message(),
response.headers().toMultimap().mapValues { it.value.joinToString(",") },
response.body()?.byteStream()
)
}
return super.shouldInterceptRequest(view, request) return super.shouldInterceptRequest(view, request)
} }
} }

View File

@ -0,0 +1,80 @@
package exh.ui.captcha
import android.os.Build
import android.support.annotation.RequiresApi
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
open class HeadersInjectingWebViewClient(activity: SolveCaptchaActivity,
source: CaptchaCompletionVerifier,
injectScript: String?,
private val headers: Map<String, String>)
: BasicWebViewClient(activity, source, injectScript) {
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
// Temp disabled as it's unreliable
/*if(headers.isNotEmpty()) {
val response = activity.httpClient.newCall(request.toOkHttpRequest()
.newBuilder()
.apply {
headers.forEach { (n, v) -> header(n, v) }
}
.build())
.execute()
return WebResourceResponse(
response.body()?.contentType()?.let { "${it.type()}/${it.subtype()}" },
response.body()?.contentType()?.charset()?.toString(),
response.code(),
response.message().nullIfBlank() ?: FALLBACK_REASON_PHRASES[response.code()] ?: "Unknown status",
response.headers().toMultimap().mapValues { it.value.joinToString(",") },
response.body()?.byteStream()
)
}*/
return super.shouldInterceptRequest(view, request)
}
companion object {
private val FALLBACK_REASON_PHRASES = mapOf(
100 to "Continue",
101 to "Switching Protocols",
200 to "OK",
201 to "Created",
202 to "Accepted",
203 to "Non-Authoritative Information",
204 to "No Content",
205 to "Reset Content",
206 to "Partial Content",
300 to "Multiple Choices",
301 to "Moved Permanently",
302 to "Moved Temporarily",
303 to "See Other",
304 to "Not Modified",
305 to "Use Proxy",
400 to "Bad Request",
401 to "Unauthorized",
402 to "Payment Required",
403 to "Forbidden",
404 to "Not Found",
405 to "Method Not Allowed",
406 to "Not Acceptable",
407 to "Proxy Authentication Required",
408 to "Request Time-out",
409 to "Conflict",
410 to "Gone",
411 to "Length Required",
412 to "Precondition Failed",
413 to "Request Entity Too Large",
414 to "Request-URI Too Large",
415 to "Unsupported Media Type",
500 to "Internal Server Error",
501 to "Not Implemented",
502 to "Bad Gateway",
503 to "Service Unavailable",
504 to "Gateway Time-out",
505 to "HTTP Version not supported"
)
}
}

View File

@ -124,23 +124,26 @@ class SolveCaptchaActivity : AppCompatActivity() {
} }
} }
webview.webViewClient = if (preferencesHelper.eh_autoSolveCaptchas().getOrDefault() webview.webViewClient = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if(preferencesHelper.eh_autoSolveCaptchas().getOrDefault()) {
// Fetch auto-solve credentials early for speed // Fetch auto-solve credentials early for speed
credentialsObservable = httpClient.newCall(Request.Builder() credentialsObservable = httpClient.newCall(Request.Builder()
// Rob demo credentials // Rob demo credentials
.url("https://speech-to-text-demo.ng.bluemix.net/api/v1/credentials") .url("https://speech-to-text-demo.ng.bluemix.net/api/v1/credentials")
.build()) .build())
.asObservableSuccess() .asObservableSuccess()
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.map { .map {
val json = jsonParser.parse(it.body()!!.string()) val json = jsonParser.parse(it.body()!!.string())
it.close() it.close()
json["token"].string json["token"].string
}.melt() }.melt()
webview.addJavascriptInterface(this@SolveCaptchaActivity, "exh") webview.addJavascriptInterface(this@SolveCaptchaActivity, "exh")
AutoSolvingWebViewClient(this, source, script, headers) AutoSolvingWebViewClient(this, source, script, headers)
} else {
HeadersInjectingWebViewClient(this, source, script, headers)
}
} else { } else {
BasicWebViewClient(this, source, script) BasicWebViewClient(this, source, script)
} }