User:Fstirlitz/quick and dirty geoip lookup script

From ArchWiki

in fact a wrapper around mmdblookupAUR (whose pseudo-JSON output format is, to put it mildly, fucking braindead). you also need jshon (and bash, and maybe host).

#!/bin/bash
MMDB_PATHNAME="/var/lib/geoip/GeoLite2-City.mmdb" # wiki.archlinux.org/index.php/User:Fstirlitz/geolite2_update_script

json_mode=0

show_help() {
	echo "usage: $0 [-j] [-4|-6] \$ip_address"
	echo "  -j   output JSON (real JSON, not this pseudo-JSON mmdblookup spews)"
	echo "  -4   resolve hostnames to IPv4 addresses only"
	echo "  -6   resolve hostnames to IPv6 addresses (default)"
	exit 1
}

ip_mode=6

get_ip_addr() {
	host "$1" | awk -v ip_mode="$ip_mode" '
		/has address/ { print $4 }
		/has IPv6 address/ { if (ip_mode == 6) print $5 }
	' | tail -1
}

while (( $# )); do
	case "$1" in
		-h|--help)
			show_help
		;;
		-j)
			json_mode=1
		;;
		-4|--ipv4)
			ip_mode=4
		;;
		-6|--ipv6)
			ip_mode=6
		;;
		-*)
			echo "invalid option: $1"
			exit 1
		;;
		[0-9A-Fa-f]:*|[0-9A-Fa-f][0-9A-Fa-f]:*|[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]:*|[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]:*|::*)
			ip="$1"
		;;
		[0-9].*|[0-9][0-9].*|[0-9][0-9][0-9].*|0x*)
			ip="$1"
		;;
		[a-z0-9]*)
			ip="$(get_ip_addr "$1")"
			if -z "$ip" ; then
				echo "cannot resolve '$1'"
				exit 1
			fi
			show_ip=1
		;;
	esac
	shift
done

lookup() {
	mmdblookup -f "$MMDB_PATHNAME" -i "$1" | sed -ne '
		s/<[^>]*>$// ; s/ *$// ; s/[^:{[]$/&,/ ; N ; s/,\(\n\s*[\x5d}]\)/\1/ ; s/,\n\s*$// ; P ; D'
}

has_key() {
	jshon -Q "$@" >/dev/null
}

if -z "$ip" ; then
	show_help
fi

json="$(lookup "$ip")"

if (( json_mode )); then
	echo "$json"
else
	lang=en

	# for dblang in $(
	# 	mmdblookup -f "$MMDB_PATHNAME" -v -i 0.0.0.0 2>/dev/null \
	# 		| sed -ne 's/^\s*Languages:\s*//p' | tr - _
	# ); do if \
	# 	[[ "${LC_MESSAGES:-$LANG}" == "$dblang".*
	# 	|| "${LC_MESSAGES:-$LANG}" == "$dblang"_*.*
	# 	|| "${LC_MESSAGES:-$LANG}" == "${dblang%_*}"_*.*
	# 	|| "${LC_MESSAGES:-$LANG}" == "${dblang%_*}".*
	# 	]]; then
	# 		lang="$dblang"
	# 	fi
	# done
	
	if (( show_ip )); then
		printf '%-18s : %s\n' "IP address" "$ip"
	fi

	if has_key -e continent <<< "$json"; then
		printf '%-18s : %s [%s]\n' "Continent" \
			"$(jshon <<< "$json" -e continent -e names -e "$lang" -u)" \
			"$(jshon <<< "$json" -e continent -e code -u)"
	fi

	if has_key -e registered_country <<< "$json"; then
		printf '%-18s : %s [%s]\n' "Registered country" \
			"$(jshon <<< "$json" -e registered_country -e names -e "$lang" -u)" \
			"$(jshon <<< "$json" -e registered_country -e iso_code -u)"
	fi

	if has_key -e country <<< "$json"; then
		country_code="$(jshon <<< "$json" -e country -e iso_code -u)"
		printf '%-18s : %s [%s]\n' "Country" \
			"$(jshon <<< "$json" -e country -e names -e "$lang" -u)" \
			"$country_code"
	fi

	if has_key -e subdivisions <<< "$json"; then
		case "$country_code" in
			CA)
				subdiv=('Province')
			;;
			CZ)
				subdiv=('Region' 'District')
			;;
			ES)
				subdiv=('Community' 'Province')
			;;
			NL)
				subdiv=('Province')
			;;
			PL)
				subdiv=('Voivodeship')
			;;
			US)
				subdiv=('State')
			;;
			*)
				subdiv=('Subdivision' 'Subdivision' 'Subdivision' 'Subdivision')
			;;
		esac
		eval "names=($(jshon <<< "$json" -e subdivisions -a -e names -e "$lang"))"
		eval "codes=($(jshon <<< "$json" -e subdivisions -a -e iso_code))"
		for i in `seq 0 $((${#codes[@]} - 1))`; do
			printf '%-18s : %s [%s]\n' "${subdiv[$i]}" "${names[$i]}" "${codes[$i]}"
		done
	fi

	if has_key -e city <<< "$json"; then
		if postal="$(jshon -Q <<< "$json" -e postal -e code -u)"; then
			postal=" ($postal)"
		fi
		printf '%-18s : %s%s\n' "City" \
			"$(jshon <<< "$json" -e city -e names -e "$lang" -u)" \
			"$postal"
	fi

	if has_key -e location <<< "$json"; then
		if lon="$(jshon -Q <<< "$json" -e location -e longitude)"; then
			if "$lon" = -* ; then
				lon="${lon#-}"
				lonm=W
			else
				lonm=E
			fi
			
			lat="$(jshon <<< "$json" -e location -e latitude)"
			if "$lat" = -* ; then
				lat="${lat#-}"
				latm=S
			else
				latm=N
			fi

			printf '%-18s : %.1f°%s %.1f°%s\n' "Coordinates" "$lon" "$lonm" "$lat" "$latm"
		fi
		
		if tz="$(jshon -Q <<< "$json" -e location -e time_zone -u)"; then
			printf '%-18s : %s\n' "Time zone" "$tz"
		fi
	fi
fi