#!/bin/bash
#
# Simple shell script to update IP2Location database files
#
# For updating non-sample files, credentials are required:
#  provided by file $HOME/.ip2location
#   login=EMAIL
#   password=PASSWORD
#
# Project    : ipv6calc/IP2Location
# File       : IP2Location-update.sh
# Version    : $Id: ff80418cab5a71eecfe25c9f61fb991fd1aae770 $
# Copyright  : 2012-2020 by Peter Bieringer <pb (at) bieringer.de>
# License    : GNU GPL version 2

# 20150416: remove softlink support, no longer needed

IP2LOCATION_CONFIG=${IP2LOCATION_CONFIG:-"$HOME/.ip2location"}

IP2LOCATION_DAT_DIR_DEFAULT="/usr/share/IP2Location"
[ -z "$IP2LOCATION_DAT_DIR" -a -n "$IPV6CALC_DB_IP2LOCATION_DIR" ] && IP2LOCATION_DAT_DIR="$IPV6CALC_DB_IP2LOCATION_DIR"
[ -z "$IP2LOCATION_DAT_DIR" ] && IP2LOCATION_DAT_DIR="$IP2LOCATION_DAT_DIR_DEFAULT"

IP2LOCATION_DAT_URL_BASE="http://www.ip2location.com/download?login=@LOGIN@&password=@PASSWORD@"
IP2LOCATION_DAT_URL_BASE_SAMPLE="http://www.ip2location.com/downloads"
IP2LOCATION_DAT_URL_BASE_LITE_FREE="http://download.ip2location.com/lite"

# Format: Proto:DB-Number

# free downloads
IP2LOCATION_DAT_FILES_LITE_FREE=${IP2LOCATION_DAT_FILES_LITE_FREE:-"IPv4:1 IPv6:1"}

# (free) subscription is required for LITE DB-Number > 1
IP2LOCATION_DAT_FILES_LITE=${IP2LOCATION_DAT_FILES_LITE:-"IPv4:11 IPv6:11"}

# list of normal (purchased subscription required)
IP2LOCATION_DAT_FILES="${IP2LOCATION_DAT_FILES}"

# list of recommended SAMPLE
IP2LOCATION_DAT_FILES_SAMPLE=${IP2LOCATION_DAT_FILES_SAMPLE:-"IPv4:20 IPv6:20 IPv4:24 IPv6:24"}


help() {
	cat <<END
Usage: $(basename "$0") [-D <dir>] [-n] [-s]
	-D <dir>	database destination directory (optional)
	-n		no action (dry-run)
	-s		skip already successfully downloaded files
	-v		verbose

	database directory: $IP2LOCATION_DAT_DIR (default: $IP2LOCATION_DAT_DIR_DEFAULT)

	it honors externally defined environment value:
		prio 1: IP2LOCATION_DAT_DIR
		prio 2: IPV6CALC_DB_IP2LOCATION_DIR

 this script will download data from ip2location.com
  into given/set database directory

 Sample databases:
  IP2LOCATION_DAT_FILES_SAMPLE=$IP2LOCATION_DAT_FILES_SAMPLE

 Lite (free) databases:
  IP2LOCATION_DAT_FILES_LITE_FREE=$IP2LOCATION_DAT_FILES_LITE_FREE

 With authentication (requires login/password in $IP2LOCATION_CONFIG) and a valid subscription
  IP2LOCATION_DAT_FILES_LITE=$IP2LOCATION_DAT_FILES_LITE
  IP2LOCATION_DAT_FILES=$IP2LOCATION_DAT_FILES

 Credentials must be defined in \$HOME/.ip2location by
  login=IP2LOCATION-LOGIN
  password=IP2LOCATION-PASSWORD

 In addition settings from above can be overwritten by setting related variables
END
}

# source credentials (must provide login= and password=)
authenticated=1
if [ -f "$IP2LOCATION_CONFIG" ]; then
	source "$IP2LOCATION_CONFIG"

	for token in login password; do
		if [ -z "${!token}" ]; then
			echo "NOTICE: missing credential token in: $IP2LOCATION_CONFIG [$token=<VALUE>] (skip authenticated downloads)"
			authenticated=0
		fi
	done

	if [ $authenticated -ne 0 ]; then
		# replace placeholders
		IP2LOCATION_DAT_URL_BASE=${IP2LOCATION_DAT_URL_BASE//@LOGIN@/$login}
		IP2LOCATION_DAT_URL_BASE=${IP2LOCATION_DAT_URL_BASE//@PASSWORD@/$password}
	fi
else
	authenticated=0
fi

if [ $authenticated -eq 0 ]; then
	echo "NOTICE: IP2Location credential file not found, clear IP2LOCATION_DAT_FILES"
	IP2LOCATION_DAT_FILES=""

	echo "NOTICE: IP2Location credential file not found, clear IP2LOCATION_DAT_FILES_LITE"
	IP2LOCATION_DAT_FILES_LITE=""
fi

# option parsing
while getopts "vfsnD:h\?" opt; do
	case $opt in
	    D)
		IP2LOCATION_DAT_DIR=$OPTARG
		;;
	    n)
		NO_ACTION=1
		;;
	    s)
		SKIP_OK=1
		;;
	    v)
		verbose=1
		;;
	    *)
		help
		exit 1
		;;
	esac
done


# directory check
if [ ! -d "$IP2LOCATION_DAT_DIR" ]; then
	echo "ERROR : missing directory: $IP2LOCATION_DAT_DIR (check option -D or set IP2LOCATION_DAT_DIR)"
	exit 1
fi

if [ ! -w "$IP2LOCATION_DAT_DIR" ]; then
	echo "ERROR : missing write permissions on directory: $IP2LOCATION_DAT_DIR"
	exit 1
fi

# required binary check
if ! which unzip >/dev/null 2>&1; then
	echo "ERROR : missing binary: unzip"
	exit 1
fi


# Download and unpack non-sample files
for list in IP2LOCATION_DAT_FILES_LITE IP2LOCATION_DAT_FILES IP2LOCATION_DAT_FILES_LITE_FREE IP2LOCATION_DAT_FILES_SAMPLE; do
	if [ -z "${!list}" ]; then
		echo "NOTICE: empty list, skip: $list"
		continue
	fi

	[ "$verbose" = "1" ] && echo "INFO  : download list $list: ${!list}"

	url_base="$IP2LOCATION_DAT_URL_BASE"

	case $list in
	    *_LITE|*_LITE_FREE)
		code_suffix="LITEBIN"
		file_prefix="IP2LOCATION-LITE"

		case $list in
		    *_LITE_FREE)
			url_base="$IP2LOCATION_DAT_URL_BASE_LITE_FREE"
			;;
		esac
		;;
	    *_SAMPLE)
		url_base="$IP2LOCATION_DAT_URL_BASE_SAMPLE"
		;;
	    *)
		code_suffix="BIN"
		file_prefix="IP2LOCATION"
		;;
	esac

	file_dest="$IP2LOCATION_DAT_DIR/$file"

	for token in ${!list}; do
		proto=${token/:*}
		number=${token/*:}

		case $list in
		    *_SAMPLE)
			case $proto in
			    IPv4)
				prototoken=""
				;;
			    IPv6)
				prototoken="6"
				;;
			    *)
				echo "ERROR : unsupported proto in token: $token"
				exit 1
				;;
			esac

			file="sample${prototoken}.bin.db${number}.zip"
			;;
		    *)
			case $proto in
			    IPv4)
				code="DB${number}${code_suffix}"
				file="${file_prefix}-DB${number}.BIN.ZIP"
				;;
			    IPv6)
				code="DB${number}${code_suffix}IPV6"
				file="${file_prefix}-DB${number}.IPV6.BIN.ZIP"
				;;
			    *)
				echo "ERROR : unsupported proto in token: $token"
				exit 1
				;;
			esac

			;;
		esac

		case $list in
		    *_SAMPLE|*_LITE_FREE)
			url="$url_base/$file"
			;;
		    *)
			url="$url_base&productcode=$code"
			;;
		esac

		file_dest="$IP2LOCATION_DAT_DIR/$file"

		if [ "$NO_ACTION" = "1" ]; then
			echo "NOTICE: download skipped by option: ${url/password=*&/password=********&} ($file_dest)"
			continue
		fi

		if [ "$SKIP_OK" = "1" ]; then
			if file "$file_dest" | grep -q "Zip archive data"; then
				echo "NOTICE: download skipped by option because file already existing and is a ZIP file: $code ($file_dest)"

				file_unzip=$(unzip -l "$file_dest" '*.BIN' | grep "\.BIN$" | awk '{ print $NF }' | head -1)

				if [ -n "$file_unzip" ]; then
					if [ ! -f "$IP2LOCATION_DAT_DIR/$file_unzip" ]; then
						unzip -o -d "$IP2LOCATION_DAT_DIR" "$file_dest" '*.BIN'
						if [ $? -ne 0 ]; then
							echo "ERROR : unzip of file not successful: $file_dest"
							continue
						else
							echo "INFO  : unzip of file successful: $file_dest"
						fi
					else
						echo "INFO  : skip unzip because unzipped file already existing: $code ($file_dest)"
					fi
				fi
				continue
			fi
		fi

		[ "$verbose" = "1" ] && echo "INFO  : try to download URL: ${url/password=*&/password=********&} ($file_dest)"
		wget -q -O "$file_dest" "$url"
		result=$?
			
		if [ $result -ne 0 ]; then
			echo "ERROR : download of file not successful: $file_dest ($!)"
			continue
		fi
		[ "$verbose" = "1" ] && echo "INFO  : download of file successful: $file_dest"

		if [ ! -s "$file_dest" ]; then
			echo "ERROR : downloaded file has ZERO size: $file_dest"
			continue
		fi

		if ! file "$file_dest" | grep -q "Zip archive data"; then
			if file "$file_dest" | grep -q "ASCII text"; then
				echo "ERROR : downloaded file is not a ZIP archive: $file_dest ($(head -1 "$file_dest"))"
			else
				echo "ERROR : downloaded file is not a ZIP archive: $file_dest"
			fi
			continue
		fi

		unzip -o -d "$IP2LOCATION_DAT_DIR" "$file_dest" '*.BIN'
		if [ $? -ne 0 ]; then
			echo "ERROR : unzip of file not successful: $file_dest"
			continue
		fi
		[ "$verbose" = "1" ] && echo "INFO  : unzip of file successful: $file_dest (remove downloaded file now)"
		rm -f "$file_dest"

		echo "NOTICE: download/unzip successful: $token"
	done
done

# Adjust permissions
if [ "$NO_ACTION" != "1" ]; then
	chmod 644 $IP2LOCATION_DAT_DIR/*.BIN
fi
