net bug fix, myextapi now service agnostic
This commit is contained in:
+105
-38
@@ -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
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user