mirror of
https://github.com/Corewala/Buran
synced 2024-12-21 23:32:40 +00:00
Added HTTP proxy
It's a beautiful half-broken mess
This commit is contained in:
parent
affa99e8f2
commit
467e3fc0b7
7 changed files with 79 additions and 35 deletions
|
@ -56,14 +56,17 @@ class OmniTerm(private val listener: Listener) {
|
|||
when {
|
||||
link.startsWith(GEM_SCHEME) -> uri.set(link)
|
||||
link.startsWith("//") -> uri.set("gemini:$link")
|
||||
link.startsWith("http://") or link.startsWith("https://") -> {
|
||||
uri.set(link)
|
||||
}
|
||||
link.contains(":") -> listener.openExternal(link)
|
||||
else -> uri.resolve(link)
|
||||
}
|
||||
|
||||
val address = uri.toString().replace("//", "/").replace("gemini:/", "gemini://")
|
||||
println("OmniTerm resolved address: $address")
|
||||
val address = uri.toString().replace("//", "/").replace(":/", "://")
|
||||
|
||||
if(invokeListener) listener.request(address)
|
||||
println("OmniTerm resolved address: $address")
|
||||
}
|
||||
|
||||
fun reset(){
|
||||
|
|
|
@ -6,7 +6,7 @@ import corewala.buran.io.database.history.BuranHistory
|
|||
import java.net.URI
|
||||
|
||||
interface Datasource {
|
||||
fun request(address: String, forceDownload: Boolean, clientCertPassword: String?, onUpdate: (state: GemState) -> Unit)
|
||||
fun request(address: String, forceDownload: Boolean, clientCertPassword: String?, alternativeRequest: String?, onUpdate: (state: GemState) -> Unit)
|
||||
fun isRequesting(): Boolean
|
||||
fun cancel()
|
||||
fun canGoBack(): Boolean
|
||||
|
|
|
@ -8,9 +8,9 @@ import corewala.buran.OppenURI
|
|||
import corewala.buran.io.GemState
|
||||
import corewala.buran.io.database.history.BuranHistory
|
||||
import corewala.buran.io.keymanager.BuranKeyManager
|
||||
import corewala.toURI
|
||||
import corewala.toUri
|
||||
import java.io.*
|
||||
import java.lang.IllegalStateException
|
||||
import java.net.ConnectException
|
||||
import java.net.URI
|
||||
import java.net.UnknownHostException
|
||||
|
@ -32,7 +32,7 @@ class GeminiDatasource(private val context: Context, val history: BuranHistory):
|
|||
|
||||
private var currentRequestAddress: String? = null
|
||||
|
||||
override fun request(address: String, forceDownload: Boolean, clientCertPassword: String?, onUpdate: (state: GemState) -> Unit) {
|
||||
override fun request(address: String, forceDownload: Boolean, clientCertPassword: String?, alternativeRequest: String?, onUpdate: (state: GemState) -> Unit){
|
||||
this.forceDownload = forceDownload
|
||||
|
||||
this.onUpdate = onUpdate
|
||||
|
@ -41,10 +41,12 @@ class GeminiDatasource(private val context: Context, val history: BuranHistory):
|
|||
|
||||
onUpdate(GemState.Requesting(uri))
|
||||
|
||||
currentRequestAddress = address
|
||||
if(address.startsWith("gemini://")){
|
||||
currentRequestAddress = address
|
||||
}
|
||||
|
||||
GlobalScope.launch {
|
||||
geminiRequest(uri, onUpdate, clientCertPassword)
|
||||
geminiRequest(uri, onUpdate, clientCertPassword, alternativeRequest)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,14 +68,20 @@ class GeminiDatasource(private val context: Context, val history: BuranHistory):
|
|||
socketFactory = sslContext.socketFactory
|
||||
}
|
||||
|
||||
private fun geminiRequest(uri: URI, onUpdate: (state: GemState) -> Unit, clientCertPassword: String?){
|
||||
private fun geminiRequest(uri: URI, onUpdate: (state: GemState) -> Unit, clientCertPassword: String?, alternativeRequest: String?){
|
||||
val protocol = "TLS"
|
||||
|
||||
initSSLFactory(protocol, clientCertPassword)
|
||||
|
||||
val port = if(uri.port != -1){
|
||||
uri.port
|
||||
}else{
|
||||
1965
|
||||
}
|
||||
|
||||
val socket: SSLSocket?
|
||||
try {
|
||||
socket = socketFactory?.createSocket(uri.host, 1965) as SSLSocket
|
||||
socket = socketFactory?.createSocket(uri.host, port) as SSLSocket
|
||||
|
||||
println("Buran socket handshake with ${uri.host}")
|
||||
socket.startHandshake()
|
||||
|
@ -102,7 +110,12 @@ class GeminiDatasource(private val context: Context, val history: BuranHistory):
|
|||
val bufferedWriter = BufferedWriter(outputStreamWriter)
|
||||
val outWriter = PrintWriter(bufferedWriter)
|
||||
|
||||
val requestEntity = uri.toString() + "\r\n"
|
||||
val requestEntity = if(alternativeRequest.isNullOrEmpty()){
|
||||
uri.toString()
|
||||
}else{
|
||||
alternativeRequest
|
||||
} + "\r\n"
|
||||
|
||||
println("Buran socket requesting $requestEntity")
|
||||
outWriter.print(requestEntity)
|
||||
outWriter.flush()
|
||||
|
@ -134,10 +147,10 @@ class GeminiDatasource(private val context: Context, val history: BuranHistory):
|
|||
when {
|
||||
currentRequestAddress != uri.toString() -> {}
|
||||
header.code == GeminiResponse.INPUT -> onUpdate(GemState.ResponseInput(uri, header))
|
||||
header.code == GeminiResponse.REDIRECT -> onUpdate(GemState.Redirect(resolve(uri.host, header.meta)))
|
||||
header.code == GeminiResponse.REDIRECT -> onUpdate(GemState.Redirect(resolve(uri, header.meta)))
|
||||
header.code == GeminiResponse.CLIENT_CERTIFICATE_REQUIRED -> onUpdate(GemState.ClientCertRequired(uri, header))
|
||||
header.code != GeminiResponse.SUCCESS -> onUpdate(GemState.ResponseError(header))
|
||||
header.meta.startsWith("text/gemini") -> getGemtext(bufferedReader, uri, header, onUpdate)
|
||||
header.meta.startsWith("text/gemini") -> getGemtext(bufferedReader, requestEntity.trim().toURI(), header, onUpdate)
|
||||
header.meta.startsWith("text/") -> getString(socket, uri, header, onUpdate)
|
||||
header.meta.startsWith("image/") -> getBinary(socket, uri, header, onUpdate)
|
||||
else -> {
|
||||
|
@ -172,10 +185,6 @@ class GeminiDatasource(private val context: Context, val history: BuranHistory):
|
|||
|
||||
val processed = GemtextHelper.findCodeBlocks(lines)
|
||||
|
||||
when {
|
||||
!uri.toString().startsWith("gemini://") -> throw IllegalStateException("Not a Gemini Uri")
|
||||
}
|
||||
|
||||
updateHistory(uri)
|
||||
onUpdate(GemState.ResponseGemtext(uri, header, processed))
|
||||
}
|
||||
|
@ -232,9 +241,9 @@ class GeminiDatasource(private val context: Context, val history: BuranHistory):
|
|||
}
|
||||
}
|
||||
|
||||
private fun resolve(host: String, address: String): String{
|
||||
private fun resolve(uri: URI, address: String): String{
|
||||
val ouri = OppenURI()
|
||||
ouri.set("gemini://$host")
|
||||
ouri.set(uri.scheme + uri.host)
|
||||
return ouri.resolve(address)
|
||||
}
|
||||
|
||||
|
|
|
@ -81,6 +81,8 @@ class GemActivity : AppCompatActivity() {
|
|||
|
||||
private var certPassword: String? = null
|
||||
|
||||
private var proxiedAddress: String? = null
|
||||
|
||||
private var previousPosition: Int = 0
|
||||
|
||||
private var initialised: Boolean = false
|
||||
|
@ -96,7 +98,7 @@ class GemActivity : AppCompatActivity() {
|
|||
private val onLink: (link: URI, longTap: Boolean, adapterPosition: Int) -> Unit = { uri, longTap, _: Int ->
|
||||
if(longTap){
|
||||
val globalURI = if(!uri.toString().contains("//") and !uri.toString().contains(":")){
|
||||
(omniTerm.getCurrent() + uri.toString()).replace("//", "/").replace("gemini:/", "gemini://")
|
||||
(omniTerm.getCurrent() + uri.toString()).replace("//", "/").replace(":/", "://")
|
||||
} else {
|
||||
uri.toString()
|
||||
}
|
||||
|
@ -112,6 +114,7 @@ class GemActivity : AppCompatActivity() {
|
|||
binding.addressEdit.hint = getString(R.string.main_input_hint)
|
||||
inSearch = false
|
||||
}
|
||||
|
||||
omniTerm.navigation(uri.toString())
|
||||
}
|
||||
}
|
||||
|
@ -496,7 +499,9 @@ class GemActivity : AppCompatActivity() {
|
|||
is GemState.Requesting -> {
|
||||
loadingView(true)
|
||||
}
|
||||
is GemState.NotGeminiRequest -> externalProtocol(state)
|
||||
is GemState.NotGeminiRequest -> {
|
||||
externalProtocol(state.uri)
|
||||
}
|
||||
is GemState.ResponseError -> {
|
||||
omniTerm.reset()
|
||||
showAlert("${GeminiResponse.getCodeString(state.header.code)}:\n\n${state.header.meta}")
|
||||
|
@ -506,7 +511,14 @@ class GemActivity : AppCompatActivity() {
|
|||
updateClientCertIcon()
|
||||
showAlert("${GeminiResponse.getCodeString(state.header.code)}:\n\n${state.header.meta}")
|
||||
}
|
||||
is GemState.ResponseGemtext -> renderGemtext(state)
|
||||
is GemState.ResponseGemtext -> {
|
||||
if(state.uri.scheme != "gemini://"){
|
||||
Snackbar.make(binding.root, getString(R.string.proxied_content), Snackbar.LENGTH_LONG).setAction(getString(R.string.open_original)) {
|
||||
externalProtocol(state.uri)
|
||||
}.show()
|
||||
}
|
||||
renderGemtext(state)
|
||||
}
|
||||
is GemState.ResponseText -> renderText(state)
|
||||
is GemState.ResponseImage -> renderImage(state)
|
||||
is GemState.ResponseBinary -> renderBinary(state)
|
||||
|
@ -531,7 +543,7 @@ class GemActivity : AppCompatActivity() {
|
|||
.setMessage("${state.uri}")
|
||||
.setPositiveButton(getString(R.string.download).toUpperCase()) { _, _ ->
|
||||
loadingView(true)
|
||||
model.requestBinaryDownload(state.uri, clientCertPassword)
|
||||
model.requestBinaryDownload(state.uri, clientCertPassword, null)
|
||||
}
|
||||
.setNegativeButton(getString(R.string.cancel).toUpperCase()) { _, _ -> }
|
||||
.show()
|
||||
|
@ -638,15 +650,15 @@ class GemActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun externalProtocol(state: GemState.NotGeminiRequest) = runOnUiThread {
|
||||
private fun externalProtocol(uri: URI) = runOnUiThread {
|
||||
loadingView(false)
|
||||
val uri = state.uri.toString()
|
||||
val uri = uri.toString()
|
||||
|
||||
when {
|
||||
(uri.startsWith("http://") || uri.startsWith("https://")) -> openExternalLink(uri)
|
||||
else -> {
|
||||
val viewIntent = Intent(Intent.ACTION_VIEW)
|
||||
viewIntent.data = Uri.parse(state.uri.toString())
|
||||
viewIntent.data = Uri.parse(uri.toString())
|
||||
|
||||
try {
|
||||
startActivity(viewIntent)
|
||||
|
@ -654,7 +666,7 @@ class GemActivity : AppCompatActivity() {
|
|||
showAlert(
|
||||
String.format(
|
||||
getString(R.string.no_app_installed_that_can_open),
|
||||
state.uri
|
||||
uri
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -683,7 +695,6 @@ class GemActivity : AppCompatActivity() {
|
|||
}else{
|
||||
val viewIntent = Intent(Intent.ACTION_VIEW)
|
||||
viewIntent.data = Uri.parse(address)
|
||||
|
||||
startActivity(viewIntent)
|
||||
}
|
||||
}
|
||||
|
@ -691,7 +702,7 @@ class GemActivity : AppCompatActivity() {
|
|||
private fun renderGemtext(state: GemState.ResponseGemtext) = runOnUiThread {
|
||||
loadingView(false)
|
||||
|
||||
omniTerm.set(state.uri.toString())
|
||||
omniTerm.set(proxiedAddress ?: state.uri.toString())
|
||||
|
||||
//todo - colours didn't change when switching themes, so disabled for now
|
||||
//val addressSpan = SpannableString(state.uri.toString())
|
||||
|
@ -911,12 +922,29 @@ class GemActivity : AppCompatActivity() {
|
|||
}
|
||||
updateClientCertIcon()
|
||||
|
||||
if(address.startsWith("http://") or address.startsWith("https://")){
|
||||
val httpProxy = prefs.getString("http_proxy", null)
|
||||
|
||||
if (httpProxy != null) {
|
||||
if(
|
||||
httpProxy.isNullOrEmpty()
|
||||
or !httpProxy.startsWith("gemini://")
|
||||
or httpProxy.contains(" ")
|
||||
or !httpProxy.contains(".")
|
||||
){
|
||||
openExternalLink(address)
|
||||
}else{
|
||||
model.request(httpProxy, certPassword, address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(getInternetStatus()){
|
||||
if(initialised){
|
||||
if(address.isEmpty()){
|
||||
loadLocalHome()
|
||||
}else{
|
||||
model.request(address, certPassword)
|
||||
model.request(address, certPassword, null)
|
||||
}
|
||||
}else{
|
||||
initialise()
|
||||
|
|
|
@ -20,12 +20,12 @@ class GemViewModel: ViewModel() {
|
|||
this.onState = onState
|
||||
|
||||
if(home.startsWith("gemini://") and !home.contains(" ")){
|
||||
request(home, null)
|
||||
request(home, null, null)
|
||||
}
|
||||
}
|
||||
|
||||
fun request(address: String, clientCertPassword: String?) {
|
||||
gemini.request(address, false, clientCertPassword){ state ->
|
||||
fun request(address: String, clientCertPassword: String?, alternativeRequest: String?) {
|
||||
gemini.request(address, false, clientCertPassword, alternativeRequest){ state ->
|
||||
onState(state)
|
||||
}
|
||||
}
|
||||
|
@ -38,15 +38,15 @@ class GemViewModel: ViewModel() {
|
|||
gemini.cancel()
|
||||
}
|
||||
|
||||
fun requestBinaryDownload(uri: URI, clientCertPassword: String?) {
|
||||
gemini.request(uri.toString(), true, clientCertPassword){ state ->
|
||||
fun requestBinaryDownload(uri: URI, clientCertPassword: String?, alternativeRequest: String?) {
|
||||
gemini.request(uri.toString(), true, clientCertPassword, alternativeRequest){ state ->
|
||||
onState(state)
|
||||
}
|
||||
}
|
||||
|
||||
//todo - same action as above... refactor
|
||||
fun requestInlineImage(uri: URI, clientCertPassword: String?, onImageReady: (cacheUri: Uri?) -> Unit){
|
||||
gemini.request(uri.toString(), false, clientCertPassword){ state ->
|
||||
gemini.request(uri.toString(), false, clientCertPassword, null){ state ->
|
||||
when (state) {
|
||||
is GemState.ResponseImage -> onImageReady(state.cacheUri)
|
||||
else -> onState(state)
|
||||
|
|
|
@ -68,6 +68,8 @@
|
|||
<string name="web_content_switch_label">Ouvrir en interne</string>
|
||||
<string name="http_proxy">Mandataire HTTP</string>
|
||||
<string name="no_http_proxy_set">Pas de mandataire HTTP</string>
|
||||
<string name="proxied_content">Ce contenu est visualisé via un mandataire</string>
|
||||
<string name="open_original">Ouvrir l\'original</string>
|
||||
<string name="show_inline_images">Images locales en ligne</string>
|
||||
<string name="pkcs_notice">Seuls les magasins de clés client PKCS12 sont actuellement supportés.</string>
|
||||
<string name="client_certificate">Certificat Client</string>
|
||||
|
|
|
@ -68,6 +68,8 @@
|
|||
<string name="web_content_switch_label">Open internally</string>
|
||||
<string name="http_proxy">HTTP proxy</string>
|
||||
<string name="no_http_proxy_set">No HTTP proxy set</string>
|
||||
<string name="proxied_content">This content is rendered through a proxy</string>
|
||||
<string name="open_original">Open original</string>
|
||||
<string name="show_inline_images">Inline local images</string>
|
||||
<string name="pkcs_notice">Only PKCS12 client keystores are currently supported.</string>
|
||||
<string name="client_certificate">Client Certificate</string>
|
||||
|
|
Loading…
Reference in a new issue