diff --git a/app/src/main/java/corewala/buran/OmniTerm.kt b/app/src/main/java/corewala/buran/OmniTerm.kt
index 4f705d3..4ebf4a2 100644
--- a/app/src/main/java/corewala/buran/OmniTerm.kt
+++ b/app/src/main/java/corewala/buran/OmniTerm.kt
@@ -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(){
diff --git a/app/src/main/java/corewala/buran/io/gemini/Datasource.kt b/app/src/main/java/corewala/buran/io/gemini/Datasource.kt
index a69c04f..e4c608e 100644
--- a/app/src/main/java/corewala/buran/io/gemini/Datasource.kt
+++ b/app/src/main/java/corewala/buran/io/gemini/Datasource.kt
@@ -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
diff --git a/app/src/main/java/corewala/buran/io/gemini/GeminiDatasource.kt b/app/src/main/java/corewala/buran/io/gemini/GeminiDatasource.kt
index 8bd29f6..a42dd38 100644
--- a/app/src/main/java/corewala/buran/io/gemini/GeminiDatasource.kt
+++ b/app/src/main/java/corewala/buran/io/gemini/GeminiDatasource.kt
@@ -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)
}
diff --git a/app/src/main/java/corewala/buran/ui/GemActivity.kt b/app/src/main/java/corewala/buran/ui/GemActivity.kt
index c369452..926ddb4 100644
--- a/app/src/main/java/corewala/buran/ui/GemActivity.kt
+++ b/app/src/main/java/corewala/buran/ui/GemActivity.kt
@@ -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()
diff --git a/app/src/main/java/corewala/buran/ui/GemViewModel.kt b/app/src/main/java/corewala/buran/ui/GemViewModel.kt
index 8d04b83..572d76d 100644
--- a/app/src/main/java/corewala/buran/ui/GemViewModel.kt
+++ b/app/src/main/java/corewala/buran/ui/GemViewModel.kt
@@ -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)
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 9235283..a5c3a1f 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -68,6 +68,8 @@
Ouvrir en interne
Mandataire HTTP
Pas de mandataire HTTP
+ Ce contenu est visualisé via un mandataire
+ Ouvrir l\'original
Images locales en ligne
Seuls les magasins de clés client PKCS12 sont actuellement supportés.
Certificat Client
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 24b2fb3..6ea9044 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -68,6 +68,8 @@
Open internally
HTTP proxy
No HTTP proxy set
+ This content is rendered through a proxy
+ Open original
Inline local images
Only PKCS12 client keystores are currently supported.
Client Certificate