PKG_NAME:=travelmate
PKG_VERSION:=2.4.7
-PKG_RELEASE:=2
+PKG_RELEASE:=3
PKG_LICENSE:=GPL-3.0-or-later
PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>
Finally enable e-mail support in travelmate and add a valid e-mail receiver address.
**Captive portal auto-logins**
-For automated captive portal logins you can reference an external shell script per uplink. All login scripts have to be executable and located in `/etc/travelmate` with the extension `.login`. The package ships multiple ready to run auto-login scripts:
+For automated captive portal logins you can reference an external shell script per uplink. All login scripts have to be executable and located in `/etc/travelmate` with the extension `.login`. A login script signals its result via the exit code: `0` means the login succeeded, any other value means it failed. Only `0` makes travelmate re-check the connectivity right away, every other value is just logged. The package ships multiple ready to run auto-login scripts:
* 'wifibahn.login' for german DB railway hotspots
* 'telekom.login' for telekom hotspots (DE)
user.info trm-2.4.7-1[26222]: connected to uplink 'radio1/WIFI@DB/-' with mac 'B2:9D:F5:96:86:A4' (1/3)
[...]
```
+**Building your own login script**
+The fastest way to a working script is to record the login once by hand and then replay it with curl. Any browser's developer tools can do the recording:
+
+1. Connect a client to the hotspot - either directly, or through travelmate's own AP while the uplink is up - and open the portal page.
+2. Open the developer tools (usually `F12`) and switch to the `Network` tab. Enable `Preserve log` (Chromium, Edge, Safari) resp. `Persist Logs` (Firefox, behind the gear icon) and `Disable cache`. A portal login almost always ends in a redirect, and without these options the recorded entries are dropped at that point.
+3. Perform the login manually and watch which requests are sent.
+4. Right-click the request that carries your credentials and choose `Copy` -> `Copy as cURL`. You now have the exact URL, method, headers, cookies and form fields as the browser sent them. `Save All As HAR` resp. `Export HAR` records the whole session if you want to study it later - note that recent Chromium versions strip cookies and authorization headers from the export unless you allow sensitive data in the devtools settings.
+5. Strip the copied command down: the browser adds a lot of `Accept*`, `Sec-*` and `Priority` headers that no portal cares about. Keep the request body, the `Content-Type` and whatever the portal actually validates, then replace curl's flags with travelmate's `${trm_fetchcmd} ${trm_fetchparm}` and `--user-agent "${trm_useragent}"`.
+6. Look for values that are only valid for one session - CSRF tokens, session ids, `sid` parameters. Those must not be copied into the script but fetched at runtime, see `wifibahn.login` (cookie jar plus awk) or `vodafone.login` (json response plus jsonfilter) for the two usual patterns.
+7. Never hardcode credentials. Pass them via the uplink's `script_args` option and read them as `${1}` and `${2}`, like `generic-user-pass.login` does.
+8. Test the script on the router while the portal is actually in the way: `sh -x /etc/travelmate/my.login user pass; echo "rc: ${?}"`. Make sure it only exits `0` when the login really succeeded - a script that reports success too eagerly is worse than one that fails, because travelmate will happily keep the uplink.
+
+The portal domain travelmate detected is in the system log: `logread -e "trm-"` shows it as `captive portal domain '<domain>' added to dhcp rebind allowlist`.
Hopefully more scripts for different captive portals will be provided by the community!
# login with credentials
#
-raw_html="$("${trm_fetch}" ${trm_fetchparm} --user-agent "${trm_useragent}" --header "Content-Type:application/x-www-form-urlencoded" --data "username=${user}&password=${password}" "http://${trm_domain}")"
-[ -z "${raw_html}" ] && exit 0 || exit 255
-
+"${trm_fetchcmd}" ${trm_fetchparm} --user-agent "${trm_useragent}" --header "Content-Type:application/x-www-form-urlencoded" --data "username=${user}&password=${password}" --output /dev/null "http://${trm_domain}"
+[ "${?}" = "0" ] && exit 0 || exit 255
# url encoding function
#
-urlencode()
-{
+urlencode() {
local chr str="${1}" len="${#1}" pos=0
while [ "${pos}" -lt "${len}" ]; do
chr="${str:pos:1}"
case "${chr}" in
- [a-zA-Z0-9.~_-])
- printf "%s" "${chr}"
- ;;
- " ")
- printf "%%20"
- ;;
- *)
- printf "%%%02X" "'${chr}"
- ;;
+ [a-zA-Z0-9.~_-])
+ printf "%s" "${chr}"
+ ;;
+ " ")
+ printf "%%20"
+ ;;
+ *)
+ printf "%%%02X" "'${chr}"
+ ;;
esac
pos=$((pos + 1))
- done
+ done
}
export LC_ALL=C
# get redirect url
#
-raw_html="$("${trm_fetch}" ${trm_fetchparm} --user-agent "${trm_useragent}" "${trm_captiveurl}")"
+raw_html="$("${trm_fetchcmd}" ${trm_fetchparm} --user-agent "${trm_useragent}" "${trm_captiveurl}")"
redirect_url="$(printf "%s" "${raw_html}" | "${trm_awkcmd}" 'match(tolower($0),/<loginurl>.*<\/loginurl>/){printf "%s",substr($0,RSTART+10,RLENGTH-21)}' 2>/dev/null | "${trm_awkcmd}" '{gsub("&","\\&");printf "%s",$0}' 2>/dev/null)"
[ -z "${redirect_url}" ] && exit 1
# final login request
#
-raw_html="$("${trm_fetch}" ${trm_fetchparm} --user-agent "${trm_useragent}" --referer "https://${trm_domain}/wlan/rest/freeLogin" --header "content-type: application/x-www-form-urlencoded" --data "UserName=${username}&Password=${password}&FNAME=0&button=Login&OriginatingServer=http%3A%2F%2F${trm_captiveurl}" "${redirect_url}")"
+raw_html="$("${trm_fetchcmd}" ${trm_fetchparm} --user-agent "${trm_useragent}" --referer "https://${trm_domain}/wlan/rest/freeLogin" --header "content-type: application/x-www-form-urlencoded" --data "UserName=${username}&Password=${password}&FNAME=0&button=Login&OriginatingServer=http%3A%2F%2F${trm_captiveurl}" "${redirect_url}")"
login_url="$(printf "%s" "${raw_html}" | "${trm_awkcmd}" 'match(tolower($0),/<logoffurl>.*<\/logoffurl>/){printf "%s",substr($0,RSTART+11,RLENGTH-23)}' 2>/dev/null)"
[ -n "${login_url}" ] && exit 0 || exit 255
# get sid
#
-redirect_url="$("${trm_fetch}" ${trm_fetchparm} --user-agent "${trm_useragent}" --write-out "%{redirect_url}" --output /dev/null "${trm_captiveurl}")"
+redirect_url="$("${trm_fetchcmd}" ${trm_fetchparm} --user-agent "${trm_useragent}" --write-out "%{redirect_url}" --output /dev/null "${trm_captiveurl}")"
sid="$(printf "%s" "${redirect_url}" 2>/dev/null | "${trm_awkcmd}" 'BEGIN{FS="[=&]"}{printf "%s",$2}')"
[ -z "${sid}" ] && exit 1
# get session
#
-raw_html="$("${trm_fetch}" ${trm_fetchparm} --user-agent "${trm_useragent}" --referer "http://${trm_domain}/portal/?sid=${sid}" "https://${trm_domain}/api/v4/session?sid=${sid}")"
+raw_html="$("${trm_fetchcmd}" ${trm_fetchparm} --user-agent "${trm_useragent}" --referer "http://${trm_domain}/portal/?sid=${sid}" "https://${trm_domain}/api/v4/session?sid=${sid}")"
session="$(printf "%s" "${raw_html}" 2>/dev/null | "${trm_jsoncmd}" -q -l1 -e '@.session')"
[ -z "${session}" ] && exit 2
# final login request
#
if [ "${login_id}" = "4" ] && [ -n "${username}" ] && [ -n "${password}" ]; then
- raw_html="$("${trm_fetch}" ${trm_fetchparm} --user-agent "${trm_useragent}" --referer "http://${trm_domain}/portal/?sid=${sid}" --data "loginProfile=${login_id}&accessType=${access_type}&accountType=${account_type}&password=${password}&session=${session}&username=${username}" "https://${trm_domain}/api/v4/login?sid=${sid}")"
+ raw_html="$("${trm_fetchcmd}" ${trm_fetchparm} --user-agent "${trm_useragent}" --referer "http://${trm_domain}/portal/?sid=${sid}" --data "loginProfile=${login_id}&accessType=${access_type}&accountType=${account_type}&password=${password}&session=${session}&username=${username}" "https://${trm_domain}/api/v4/login?sid=${sid}")"
fi
success="$(printf "%s" "${raw_html}" 2>/dev/null | "${trm_jsoncmd}" -q -l1 -e '@.success')"
[ "${success}" = "true" ] && exit 0 || exit 255
# get security token
#
-"${trm_fetch}" ${trm_fetchparm} --user-agent "${trm_useragent}" --cookie-jar "/tmp/${trm_domain}.cookie" --output /dev/null "https://${trm_domain}/en/"
-sec_token="$("${trm_awkcmd}" '/csrf/{print $7}' "/tmp/${trm_domain}.cookie" 2>/dev/null)"
+"${trm_fetchcmd}" ${trm_fetchparm} --user-agent "${trm_useragent}" --cookie-jar "/tmp/${trm_domain}.cookie" --output /dev/null "https://${trm_domain}/en/"
+[ "${?}" = "0" ] && sec_token="$("${trm_awkcmd}" '/csrf/{print $7}' "/tmp/${trm_domain}.cookie" 2>/dev/null)"
rm -f "/tmp/${trm_domain}.cookie"
[ -z "${sec_token}" ] && exit 2
# final post request
#
-raw_html="$("${trm_fetch}" ${trm_fetchparm} --user-agent "${trm_useragent}" --header "Cookie: csrf=${sec_token}" --data "login=true&CSRFToken=${sec_token}" "https://${trm_domain}/en/")"
-[ -z "${raw_html}" ] && exit 0 || exit 255
+"${trm_fetchcmd}" ${trm_fetchparm} --user-agent "${trm_useragent}" --header "Cookie: csrf=${sec_token}" --data "login=true&CSRFToken=${sec_token}" --output /dev/null "https://${trm_domain}/en/"
+[ "${?}" = "0" ] && exit 0 || exit 255