net bug fix, myextapi now service agnostic

This commit is contained in:
2026-10-13 13:55:24 +02:00
parent 2b4732d0e3
commit 01ec151ade
3 changed files with 117 additions and 40 deletions
+2 -2
View File
@@ -124,7 +124,7 @@ A bar-style prompt showing current time, execution time of the last command
| `mcd` | filefct | Create a directory and immediately move into it |
| `mdcat` | disp | Render Markdown files (or stdin) in terminal with colors, inline formatting, tables and framed code blocks; honours `NO_COLOR` |
| `meteo` | info | Display weather forecast for the configured or given city |
| `myextip` | net | Get information about your public IP address |
| `myextip` | net | Get information about your public IP address (IP, ISP, location, coordinates, AS, timezone) from an HTTPS lookup service; needs `jq` for the formatted output, `-R` shows the raw JSON |
| `get_pkgmgr` | packages | Detect the active package manager of the running distribution (`apt`, `dnf`, `yum`, `zypper`, `pacman`, `apk`, `portage`, `xbps`, `nix`) |
| `pinfo` | info | Display system information read from `/proc` and `/sys` only |
| `pkgf` | packages | Find which installed package owns a given file (distro-aware via `get_pkgmgr`) |
@@ -307,7 +307,7 @@ change the default without having to pass flags every time.
| --- | --- | --- |
| `DWL_PREFERRED_TOOL` | _(empty)_ | Force `dwl` to use `curl`, `wget`, or `fetch` (auto-detected when unset) |
| `DWL_DEFAULT_RESUME` | `0` | Enable `dwl` resume mode by default (`1`/`true`/`yes`/`on`); applies to file downloads |
| `MYEXTIP_DEFAULT_URL` | `https://ip-api.com/json` | API endpoint used by `myextip` |
| `MYEXTIP_DEFAULT_URL` | `https://ipinfo.io/json` | HTTPS JSON API used by `myextip`. `https://ipwho.is/` is also supported, as is the (HTTP-only on its free tier) ip-api.com format; a non-HTTPS URL triggers a warning |
**`[packages]`**
+10
View File
@@ -96,6 +96,16 @@ Versions follow `MAJOR.MINOR.PATCH-REVISION_STAGE_N` (e.g. `3.99.1-4_rc_1`).
`setlocale fr_FR` could pick a non-UTF-8 charset; shortcut functions are built
without evaluating the raw configuration value, and an alias cannot replace
`setlocale` or `setc`.
- `myextip`: now uses the HTTPS `https://ipinfo.io/json` API by default, since
the free ip-api.com tier is HTTP only (HTTPS answers 403); ipwho.is and the
ip-api.com format are still understood, a Timezone line and the postal code
are shown, an unexpected answer is reported instead of printing `jq` errors,
and a non-HTTPS URL gives a warning.
- `net.sh`: `dwl` always returned 0 (the cleanup after the transfer masked
curl's status), so no caller ever saw a failed download; HTTP errors (404,
403, ...) are now failures instead of saved error pages; `urlencode`
mis-encoded accents under `LC_ALL` and now uses `%20` for spaces; `isipv4`
leaked a variable.
- `compress.sh`: option flags and loop variables no longer leak into the
interactive shell.
+105 -38
View File
@@ -44,7 +44,8 @@ dwl()
local timeout=""
local resume=0
case "${DWL_DEFAULT_RESUME,,}" in
local _res="${DWL_DEFAULT_RESUME:-}"
case "${_res,,}" in
1|true|yes|on)
resume=1
;;
@@ -116,7 +117,9 @@ dwl()
_try_curl()
{
local args=(-sL)
# -f: an HTTP error (404, 500, ...) is a failure, not a saved error
# page; -S keeps error messages visible despite -s.
local args=(-fsSL)
[[ -n "$timeout" ]] && args+=(--max-time "$timeout" --connect-timeout "$timeout")
if [[ -z "$output" ]]; then
if (( resume == 1 )); then
@@ -155,31 +158,40 @@ dwl()
fi
}
# Pick the tool: the configured one, otherwise the first available.
# Helpers are removed on every exit path; the exit status of the transfer
# itself is returned (the final "unset -f" must not mask it).
local tool=""
if [[ -n "$preferred" ]]; then
command -v "$preferred" >/dev/null 2>&1 || {
if ! command -v "$preferred" >/dev/null 2>&1; then
echo "Error: preferred download tool '$preferred' is not installed." >&2
unset -f _try_curl _try_wget _try_fetch
return 1
}
fi
case "$preferred" in
curl) _try_curl ;;
wget) _try_wget ;;
fetch) _try_fetch ;;
curl|wget|fetch) tool="$preferred" ;;
*)
echo "Error: DWL_PREFERRED_TOOL '$preferred' is not supported (use curl, wget or fetch)." >&2
unset -f _try_curl _try_wget _try_fetch
return 1
;;
esac
elif command -v curl >/dev/null 2>&1; then
_try_curl
tool=curl
elif command -v wget >/dev/null 2>&1; then
_try_wget
tool=wget
elif command -v fetch >/dev/null 2>&1; then
_try_fetch
tool=fetch
else
echo "Error: No download utility (curl, wget, or fetch) found." >&2
unset -f _try_curl _try_wget _try_fetch
return 1
fi
local rc=0
"_try_${tool}" || rc=$?
unset -f _try_curl _try_wget _try_fetch
return "$rc"
}
export -f dwl
# ------------------------------------------------------------------------------
@@ -196,10 +208,8 @@ isipv4()
# Start with a regex format test (four octets)
if [[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
local old_ifs=$IFS
IFS='.'
read -r -a ip_arr <<< "$ip"
IFS=$old_ifs
local -a ip_arr=()
IFS='.' read -r -a ip_arr <<< "$ip"
# Ensure each octet is between 0 and 255
local oct
@@ -325,11 +335,16 @@ export -f isipv6
# ------------------------------------------------------------------------------
# Encode a string so it can be used as a URL parameter
# Encode a string so it can be used in a URL path or query parameter: every
# byte outside the unreserved set becomes %XX (UTF-8 bytes included, space is
# %20, valid in both a path and a query).
# Usage: urlencode <string>
urlencode()
{
local LANG=C
# LC_ALL (not LANG) is what overrides all the locale variables: with the
# C locale each character is one byte, so multibyte characters are encoded
# byte by byte (é -> %C3%A9) whatever the user's locale is.
local LC_ALL=C
local str="$*"
local length="${#str}"
local i
@@ -337,7 +352,6 @@ urlencode()
local c="${str:i:1}"
case "$c" in
[a-zA-Z0-9.~_-]) printf '%s' "$c" ;;
' ') printf '+' ;;
*) printf '%%%02X' "'$c" ;;
esac
done
@@ -416,34 +430,87 @@ myextip()
shift
done
# Fetch data. Allow overriding endpoint via MYEXTIP_DEFAULT_URL config key.
local api_url="${MYEXTIP_DEFAULT_URL:-https://ip-api.com/json/}"
# Fetch data. The endpoint can be changed with the MYEXTIP_DEFAULT_URL
# configuration key; the default is the HTTPS ipinfo.io JSON API (no key
# needed). ip-api.com is not usable: its free tier is HTTP only.
local api_url="${MYEXTIP_DEFAULT_URL:-https://ipinfo.io/json}"
if [[ $api_url != https://* ]]; then
disp W "The lookup service ($api_url) is not HTTPS: the answer is not protected."
fi
local response
if ! response=$(dwl "$api_url"); then
if ! response=$(dwl -t 10 "$api_url"); then
disp E "Failed to fetch external IP information from $api_url"
return 2
fi
# Parse with jq when available and when raw wasn't requested. The jq filter
# is tolerant to field-name differences between providers (ip-api / ipinfo).
if command -v jq >/dev/null 2>&1 && [[ "$show_raw" != true ]]; then
echo "$response" | jq -r --argjson all "$all" --argjson ip "$show_ip" \
--argjson isp "$show_isp" --argjson loc "$show_loc" \
--argjson coord "$show_coord" --argjson as "$show_as" '
[
(if $all or $ip then "IP Address : \(.query // .ip)" else empty end),
(if $all or $isp then "ISP : \(.isp // .org)" else empty end),
(if $all or $loc then
("Location : " + ((.city // "") + (if .city then ", " else "" end) + (if .regionName then .regionName else .region end) + (if .country then ", " + .country else "" end)))
else empty end),
(if $all or $coord then (if (.lat and .lon) then "Coordinates: \(.lat), \(.lon)" elif .loc then "Coordinates: \(.loc)" else empty end) else empty end),
(if $all or $as then "AS : \(.as // .org)" else empty end)
] | .[]'
else
[[ "$show_raw" != true ]] && disp W "jq is not installed, displaying raw JSON response."
echo "$response"
if [[ "$show_raw" == true ]]; then
printf '%s\n' "$response"
return 0
fi
if ! command -v jq >/dev/null 2>&1; then
disp W "jq is not installed, displaying raw JSON response."
printf '%s\n' "$response"
return 0
fi
# The first jq stage turns the answer of any supported provider into one
# common object, so the display stage below does not care where the data
# comes from:
# ipinfo.io : ip, org "AS<n> <name>", city, region, country (code),
# loc "lat,lon", timezone (string), postal
# ipwho.is : ip, connection{asn,org,isp}, country (name), latitude,
# longitude, timezone{id}, postal (recognised by .connection)
# ip-api.com: query, isp, as, regionName, lat, lon, timezone, zip
# (recognised by .query)
# A missing field is simply left out of the output. An answer without any
# IP (error message, rate limit, ...) is reported as unexpected.
local out
out=$(printf '%s' "$response" | jq -r \
--argjson all "$all" --argjson ip "$show_ip" \
--argjson isp "$show_isp" --argjson loc "$show_loc" \
--argjson coord "$show_coord" --argjson as "$show_as" '
def clean: map(select(. != null and . != "")) ;
(if (.connection | type) == "object" then
{ ip: .ip,
isp: (.connection.isp // .connection.org),
as: (if .connection.asn then
(["AS\(.connection.asn)", .connection.org] | clean | join(" "))
else null end),
city: .city, region: .region, country: .country, zip: .postal,
lat: .latitude, lon: .longitude, tz: .timezone.id }
elif .query then
{ ip: .query, isp: .isp, as: .as, city: .city, region: .regionName,
country: .country, zip: .zip, lat: .lat, lon: .lon, tz: .timezone }
else
((.loc // "" | split(",")) as $l
| { ip: .ip,
isp: (.org // null | if . then sub("^AS[0-9]+ +"; "") else . end),
as: .org, city: .city, region: .region, country: .country,
zip: .postal,
lat: ($l[0] // null), lon: ($l[1] // null), tz: .timezone })
end) as $d
| if $d.ip == null then "unexpected response" | halt_error(3) else
[
(if ($all or $ip) and $d.ip then "IP Address : \($d.ip)" else empty end),
(if ($all or $isp) and $d.isp then "ISP : \($d.isp)" else empty end),
(if ($all or $loc) then
([$d.city, $d.region, $d.country] | clean | join(", ")
| select(. != "")
| "Location : \(.)" + (if $d.zip then " (\($d.zip))" else "" end))
else empty end),
(if ($all or $coord) and $d.lat and $d.lon then "Coordinates : \($d.lat), \($d.lon)" else empty end),
(if ($all or $as) and $d.as then "AS : \($d.as)" else empty end),
(if $all and $d.tz then "Timezone : \($d.tz)" else empty end)
] | .[]
end' 2>/dev/null)
local rc=$?
if (( rc != 0 )) || [[ -z $out ]]; then
disp E "Unexpected answer from $api_url (use --raw to see it)."
return 3
fi
printf '%s\n' "$out"
}
export -f myextip
# ------------------------------------------------------------------------------