Skip to content

Commit 0b8a9f3

Browse files
programminghoch10binarynoise
authored andcommitted
separate http and https portal test urls
1 parent c708ab2 commit 0b8a9f3

File tree

7 files changed

+124
-29
lines changed

7 files changed

+124
-29
lines changed

app/src/main/kotlin/de/binarynoise/captiveportalautologin/ConnectivityChangeListenerService.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import de.binarynoise.captiveportalautologin.util.applicationContext
4848
import de.binarynoise.captiveportalautologin.util.mainHandler
4949
import de.binarynoise.captiveportalautologin.util.startService
5050
import de.binarynoise.liberator.Liberator
51+
import de.binarynoise.liberator.PortalTestURL
5152
import de.binarynoise.liberator.cast
5253
import de.binarynoise.liberator.tryOrDefault
5354
import de.binarynoise.liberator.tryOrNull
@@ -328,11 +329,11 @@ class ConnectivityChangeListenerService : Service() {
328329

329330
try {
330331
val userAgent: String by SharedPreferences.liberator_user_agent
331-
val portalTestUrl: String by SharedPreferences.liberator_captive_test_url
332+
val portalTestUrl: PortalTestURL by SharedPreferences.liberator_captive_test_url
332333

333334
val res = Liberator(
334335
{ okhttpClient -> okhttpClient.socketFactory(network.socketFactory) },
335-
portalTestUrl.toHttpUrl(),
336+
portalTestUrl,
336337
userAgent,
337338
ssid,
338339
).liberate()

app/src/main/kotlin/de/binarynoise/captiveportalautologin/GeckoViewActivity.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ class GeckoViewActivity : ComponentActivity() {
137137

138138
if (newState.hasPortal) {
139139
if (session.isOpen && (navigationDelegate.location == null || navigationDelegate.location == "about:blank")) {
140-
session.loadUri(portalTestUrl)
140+
session.loadUri(portalTestUrl.httpUrl.toString())
141141
}
142142
} else {
143143
if (session.isOpen) {
@@ -364,7 +364,7 @@ class GeckoViewActivity : ComponentActivity() {
364364

365365
private fun getHarName(): String {
366366
val ssid = networkState?.ssid
367-
val portalTestHost = portalTestUrl.toHttpUrl().host
367+
val portalTestHost = portalTestUrl.httpUrl.host
368368
val host =
369369
har.log.entries.asSequence().map { it.request.url.toHttpUrl().host }.firstOrNull { it != portalTestHost }
370370
?: portalTestHost

app/src/main/kotlin/de/binarynoise/captiveportalautologin/preferences/MainFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class MainFragment : AutoCleanupPreferenceFragment() {
169169
key = SharedPreferences.liberator_captive_test_url.key
170170
title = "Captive Portal Test Backend"
171171
entries = PortalDetection.backends.keys.toTypedArray()
172-
entryValues = PortalDetection.backends.values.toTypedArray()
172+
entryValues = PortalDetection.backends.keys.toTypedArray()
173173
summaryProvider = ListPreference.SimpleSummaryProvider.getInstance()
174174
setDefaultValue(PortalDetection.defaultBackend)
175175
}
Lines changed: 91 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,75 @@
11
package de.binarynoise.captiveportalautologin.preferences
22

3+
import kotlin.properties.ReadWriteProperty
34
import kotlin.reflect.KProperty
45
import androidx.core.content.edit
56
import androidx.preference.Preference
67
import androidx.preference.PreferenceManager
78
import de.binarynoise.captiveportalautologin.util.applicationContext
89
import de.binarynoise.liberator.PortalDetection
10+
import de.binarynoise.liberator.PortalTestURL
911

1012
object SharedPreferences {
11-
val liberator_automatically_liberate by PreferenceProperty(true)
12-
val liberator_captive_test_url by PreferenceProperty(PortalDetection.defaultBackend)
13-
val liberator_user_agent by PreferenceProperty(PortalDetection.defaultUserAgent)
14-
val liberator_send_stats by PreferenceProperty(true)
15-
val api_base by PreferenceProperty("")
13+
val liberator_automatically_liberate: PreferencePropertyDelegate<Boolean> by PreferenceProperty(true)
14+
val liberator_captive_test_url: DynamicPreferencePropertyDelegate<String, PortalTestURL> by DynamicPreferenceProperty(
15+
{ key -> PortalDetection.backends.getValue(key) },
16+
{ value -> PortalDetection.backends.entries.single { it.value == value }.key },
17+
PortalDetection.defaultBackendKey,
18+
)
19+
val liberator_user_agent: PreferencePropertyDelegate<String> by PreferenceProperty(PortalDetection.defaultUserAgent)
20+
val liberator_send_stats: PreferencePropertyDelegate<Boolean> by PreferenceProperty(true)
21+
val api_base: PreferencePropertyDelegate<String> by PreferenceProperty("")
1622

1723
val stats_last_retry_time by PreferenceProperty(0L)
1824

19-
private class PreferenceProperty<T>(private val defaultValue: T) {
25+
private class PreferenceProperty<T : Any>(private val defaultValue: T) {
2026
operator fun getValue(parent: Any, property: KProperty<*>): PreferencePropertyDelegate<T> {
2127
return PreferencePropertyDelegate(property, defaultValue)
2228
}
2329
}
30+
31+
private class DynamicPreferenceProperty<K : Any, V>(
32+
private val getValue: (K) -> V,
33+
private val getKey: (V) -> K,
34+
private val defaultKey: K,
35+
) {
36+
operator fun getValue(parent: Any, property: KProperty<*>): DynamicPreferencePropertyDelegate<K, V> {
37+
return DynamicPreferencePropertyDelegate(property, getValue, getKey, defaultKey)
38+
}
39+
}
2440
}
2541

2642

27-
class PreferencePropertyDelegate<T>(val parent: KProperty<*>, val defaultValue: T) : (Preference) -> Unit {
43+
open class PreferencePropertyDelegate<T : Any>(val parent: KProperty<*>, val defaultValue: T) :
44+
ReadWriteProperty<Any?, T?>, (Preference) -> Unit {
2845
val key = parent.name
2946

47+
init {
48+
require(defaultValue is Int || defaultValue is String || defaultValue is Boolean || defaultValue is Float || defaultValue is Long) {
49+
"Unsupported type: ${defaultValue::class}"
50+
}
51+
}
52+
3053
override fun invoke(preference: Preference) {
3154
preference.setDefaultValue(defaultValue)
3255
preference.key = key
3356
}
3457

3558
@Suppress("UNCHECKED_CAST")
36-
operator fun getValue(parent: Any?, property: KProperty<*>?): T {
59+
@JvmName("getValueNullable")
60+
operator fun getValue(thisRef: Any?, property: KProperty<*>?): T {
3761
with(PreferenceManager.getDefaultSharedPreferences(applicationContext)) {
3862
return if (contains(key)) all[key] as T
3963
else defaultValue
4064
}
4165
}
4266

43-
operator fun setValue(parent: Any?, property: KProperty<*>?, newValue: T) {
67+
override operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
68+
return getValue(thisRef, null)
69+
}
70+
71+
@JvmName("setValueNullable")
72+
operator fun setValue(thisRef: Any?, property: KProperty<*>?, newValue: T?) {
4473
PreferenceManager.getDefaultSharedPreferences(applicationContext).edit {
4574
when (newValue) {
4675
null -> remove(key)
@@ -54,7 +83,59 @@ class PreferencePropertyDelegate<T>(val parent: KProperty<*>, val defaultValue:
5483
}
5584
}
5685

86+
override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
87+
setValue(thisRef, null, value)
88+
}
89+
5790
fun get(): T = getValue(null, null)
5891

59-
fun set(newValue: T) = setValue(null, null, newValue)
92+
fun set(newValue: T?) = setValue(null, null, newValue)
93+
}
94+
95+
class DynamicPreferencePropertyDelegate<K : Any, V>(
96+
val parent: KProperty<*>,
97+
private val getValue: (K) -> V,
98+
private val getKey: (V) -> K,
99+
val defaultKey: K,
100+
) : ReadWriteProperty<Any?, V>, (Preference) -> Unit {
101+
val wrapped = PreferencePropertyDelegate(parent, defaultKey)
102+
103+
val key: String by wrapped::key
104+
105+
106+
override fun invoke(preference: Preference) {
107+
preference.setDefaultValue(getValue(defaultKey))
108+
preference.key = key
109+
}
110+
111+
@JvmName("getValueNullable")
112+
operator fun getValue(thisRef: Any?, property: KProperty<*>?): V {
113+
return getValue(wrapped.get())
114+
}
115+
116+
override operator fun getValue(thisRef: Any?, property: KProperty<*>): V {
117+
return getValue(thisRef, null)
118+
}
119+
120+
@JvmName("setKeyNullable")
121+
operator fun setValue(thisRef: Any?, property: KProperty<*>?, newKey: K) {
122+
wrapped.set(newKey)
123+
}
124+
125+
override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: V) {
126+
setValue(thisRef, null, value)
127+
}
128+
129+
@JvmName("setValueNullable")
130+
operator fun setValue(thisRef: Any?, property: KProperty<*>?, newValue: V) {
131+
setValue(thisRef, property, getKey(newValue))
132+
}
133+
134+
fun get(): V = getValue(null, null)
135+
136+
@JvmName("setKey")
137+
fun set(newKey: K) = setValue(null, null, newKey)
138+
139+
@JvmName("setValue")
140+
fun set(newValue: V) = setValue(null, null, getKey(newValue))
60141
}

liberator/src/main/kotlin/de/binarynoise/liberator/Liberator.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import org.jsoup.Jsoup
2222

2323
class Liberator(
2424
private val clientInit: (OkHttpClient.Builder) -> Unit,
25-
val portalTestUrl: HttpUrl,
25+
val portalTestUrl: PortalTestURL,
2626
private val userAgent: String,
2727
private val ssid: String?,
2828
) {
@@ -128,7 +128,7 @@ class Liberator(
128128
* Attempts to liberate the user by making a series of HTTP requests to the portal.
129129
*/
130130
fun liberate(): LiberationResult {
131-
val response = client.get(portalTestUrl, null)
131+
val response = client.get(portalTestUrl.httpUrl, null)
132132

133133
val res = recurse(response, 0)
134134

@@ -142,12 +142,12 @@ class Liberator(
142142
Thread.sleep(1000)
143143

144144
// check if the user is still in the portal
145-
val (httpIsInPortal, redirectedResponse) = isInPortal(portalTestUrl)
145+
val (httpIsInPortal, redirectedResponse) = isInPortal(portalTestUrl.httpUrl)
146146
portalResponse = redirectedResponse
147147
if (httpIsInPortal) continue
148148

149149
tryOrIgnore {
150-
val (httpsIsInPortal, redirectedResponse) = isInPortal(portalTestUrl.enforceHttps())
150+
val (httpsIsInPortal, redirectedResponse) = isInPortal(portalTestUrl.httpsUrl)
151151
portalResponse = redirectedResponse
152152
if (httpsIsInPortal) continue
153153
}

liberator/src/main/kotlin/de/binarynoise/liberator/PortalDetection.kt

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,32 @@
11
package de.binarynoise.liberator
22

3+
import okhttp3.HttpUrl
4+
import okhttp3.HttpUrl.Companion.toHttpUrl
5+
6+
data class PortalTestURL(
7+
val httpUrl: HttpUrl,
8+
val httpsUrl: HttpUrl,
9+
) {
10+
constructor(httpUrl: String, httpsUrl: String): this(httpUrl.toHttpUrl(), httpsUrl.toHttpUrl())
11+
constructor(unifiedUrl: HttpUrl) : this(unifiedUrl, unifiedUrl)
12+
constructor(unifiedUrl: String): this(unifiedUrl.toHttpUrl())
13+
}
14+
315
@Suppress("SpellCheckingInspection")
416
object PortalDetection {
517
val backends = mapOf(
6-
"Binarynoise" to "http://am-i-captured.binarynoise.de",
7-
"Google" to "http://connectivitycheck.gstatic.com/generate_204",
8-
"Apple" to "http://captive.apple.com/hotspot-detect.html",
9-
"Microsoft" to "http://www.msftconnecttest.com/connecttest.txt",
10-
"Gnome NetworkManager" to "http://nmcheck.gnome.org/check_network_status.txt",
11-
"KDE" to "http://networkcheck.kde.org/",
12-
"Fedora" to "http://fedoraproject.org/static/hotspot.txt",
13-
"Arch Linux" to "http://ping.archlinux.org/",
18+
"Binarynoise" to PortalTestURL("http://am-i-captured.binarynoise.de"),
19+
"Google" to PortalTestURL("http://connectivitycheck.gstatic.com/generate_204", "https://www.google.com/generate_204"),
20+
"Apple" to PortalTestURL("http://captive.apple.com/hotspot-detect.html"),
21+
"Microsoft" to PortalTestURL("http://www.msftconnecttest.com/connecttest.txt"),
22+
"Gnome NetworkManager" to PortalTestURL("http://nmcheck.gnome.org/check_network_status.txt"),
23+
"KDE" to PortalTestURL("http://networkcheck.kde.org/"),
24+
"Fedora" to PortalTestURL("http://fedoraproject.org/static/hotspot.txt"),
25+
"Arch Linux" to PortalTestURL("http://ping.archlinux.org/"),
1426
)
1527

16-
val defaultBackend: String = backends.entries.first().value
28+
val defaultBackend: PortalTestURL = backends.entries.first().value
29+
val defaultBackendKey: String = backends.entries.first().key
1730

1831
val userAgents = mapOf(
1932
"Chrome/Android" to "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.36",

linux/src/main/kotlin/Main.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ class CaptivePortalAutoLoginLinux : CliktCommand() {
167167

168168
val result = Liberator(
169169
{ okhttpClient -> if (oneshot) okhttpClient.connectionPool(ConnectionPool(0, 1, SECONDS)) },
170-
PortalDetection.defaultBackend.toHttpUrl(),
170+
PortalDetection.defaultBackend,
171171
PortalDetection.defaultUserAgent,
172172
ssid,
173173
).liberate()

0 commit comments

Comments
 (0)