#!/usr/bin/env sh

#tunlr has pulled the plug, this script isn't working anymore.

# The script works under ash and bash decide wich to use
`{ echo true | /usr/bin/env ash 2>/dev/null && echo '/usr/bin/env ash'; }  ||
{ echo true | /usr/bin/env bash 2>/dev/null && echo '/usr/bin/env bash'; } ||
{ echo 'bash or ash not found' >&2; echo 'exit 1'; }` <<'EOF'

####################
## -- Settings -- ##
####################

# Add your API token here from https://gatekeeper.tunlr.net/account_options
TOKEN='insert_your_token_here'

# Specify the client you want to use, the options are: curl wget
CLIENT='wget'

# Extra command line options to parse to the client.
xOPT=''

# Set to 0 to not log to console
LOGCONSOLE=1

# Secify the command to pipe the log messages to, e.g. logger
cmdLog=''

# Secify the command to pipe the warning messages to, e.g. logger
cmdWrn=''

# Secify the command to pipe the error messages to, e.g. logger
cmdErr=''

# If secure is 1 it uses https and if set to 0 it uses http
SECURE=0

# Agent string to send to the webserver
AGENT='update-script v2'

# Specify the verbosity to use:
# 1 Only show the error messages
# 2 Show error and warning messages
# 3 show error, warning and log messages
# 4 show error, warning, log and client transaction messages
VERBOSE=3

# Days left on a subscription before a warning message is shown
dWARN=7

####################
## -- messages -- ##
####################
messages() {
	case "$1" in
	  'invalid_token'      ) logErr "Your token is not valid, check your tunlr dashboard";;
	  'api_access_disabled') logErr "API access disabled, check your tunlr dashboard";;
	  'days_remaining_w'   ) logWrn "$2 days remaining on your $3 subscription plan";;
	  'days_remaining'     ) logMsg "$2 days remaining on your $3 subscription plan";; 
	  'up_for_renewal'     ) logWrn "Your account is up for renewal, please check your tunlr dashboard";;
	  'as/hosting'         ) logWrn "ISP-enforced transparent proxy, a VPN provider or a server provider has been detected";;
	  'as/abuse'           ) logWrn "Autonomous system is banned";;
	  'cidr/hosting'       ) logWrn "Network range belongs to a VPN or server provider";;
	  'cidr/abuse'         ) logWrn "Network range is banned";;
	  'proxy'              ) logWrn "A proxy has been detected";;
	  'no_ip_change'       ) logMsg "Your ip has not changed";;
	  'ip_change'	       ) logWrn "Your ip has changed";;
	  'state/expired'      ) logErr "There is currently no subscription plan, please check your tunlr dashboard";;
	  'state/active'       ) logMsg "Account is active";;
	  'state/banned'       ) logErr "Account is banned";;
	  'state/pending'      ) logErr "Account is pending, check your tunlr dashboard";;
	  'no_account'         ) logErr "Failed to get account info";;
	  'update/fail'        ) logErr "Failed to update ip address";;
	  'update/block'       ) logErr "Your current ip is blacklisted by tunlr";;
	  'update/none'        ) logWrn "Nothing to update";;
	  'update/done'        ) logWrn "Ip has been updated";;
	  'api/client'         ) logErr "wrong client selected, options are wget or curl";;
	  'api/get'            ) logMsg "Getting the json respone from ${2}TOKEN-redacted";;
	  'api/noConnect'      ) logErr "$2 failed to connect";;
	  'api/empty'          ) logErr "tunlr API returned an empty page";;
	  'json/exp1'          ) logErr "json: EXPECTED , or ] GOT $2";;
	  'json/exp2'          ) logErr "json: EXPECTED string GOT $2";;
	  'json/exp3'          ) logErr "json: EXPECTED : GOT $2";;
	  'json/exp4'          ) logErr "json: EXPECTED , or } GOT $2";;
	  'json/exp5'          ) logErr "json: EXPECTED value GOT $2";;
	  'json/exp6'          ) logErr "json: EXPECTED EOF GOT $2";;
	  *'false'|*'true'     ) ;; # don't output anything on false or true
	  *                    ) logWrn "Unkown: $*";; #unkown message handler
        esac
}


##############################
## -- Script starts here -- ##
##############################
logErr () {
  if [ "$VERBOSE" -ge 1 ]; then
    [ "$LOGCONSOLE" != '0' ] && echo "  $esc[31m$*$esc[0m" >&2
    [ -n "${cmdErr}" ] && echo "$*" | ${cmdErr}
  fi
  echo
  exit 1
}

logWrn () {
  if [ "$VERBOSE" -ge 2 ]; then
    [ "$LOGCONSOLE" != '0' ] && echo "  $esc[31m$*$esc[0m" >&2
    [ -n "${cmdWrn}" ] && echo "$*" | ${cmdWrn}
  fi
}

logMsg () {
  if [ "$VERBOSE" -ge 3 ]; then
    [ "$LOGCONSOLE" != '0' ] && echo "  $esc[32m$*$esc[0m" >&2
    [ -n "${cmdMsg}" ] && echo "$*" | ${cmdMsg}
  fi
}

awk_egrep () {
  local pattern_string=$1
  gawk '{
    while ($0) {
      start=match($0, pattern);
      token=substr($0, start, RLENGTH);
      print token;
      $0=substr($0, start+RLENGTH);
    }
  }' pattern=$pattern_string
}

tokenize () {
  local GREP
  local ESCAPE
  local CHAR

  if echo "test string" | egrep -ao --color=never "test" &>/dev/null
  then
    GREP='egrep -ao --color=never'
  else
    GREP='egrep -ao'
  fi

  if echo "test string" | egrep -o "test" &>/dev/null
  then
    ESCAPE='(\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})'
    CHAR='[^[:cntrl:]"\\]'
  else
    GREP=awk_egrep
    ESCAPE='(\\\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})'
    CHAR='[^[:cntrl:]"\\\\]'
  fi

  local STRING="\"$CHAR*($ESCAPE$CHAR*)*\""
  local NUMBER='-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?'
  local KEYWORD='null|false|true'
  local SPACE='[[:space:]]+'

  $GREP "$STRING|$NUMBER|$KEYWORD|$SPACE|." | egrep -v "^$SPACE$"
}

parse_array () {
  local index=0
  local ary=''
  read -r token
  case "$token" in
    ']') ;;
    *)
      while :
      do
        parse_value "$1" "$index"
        index=$((index+1))
        ary="$ary""$value" 
        read -r token
        case "$token" in
          ']') break ;;
          ',') ary="$ary," ;;
          *) messages 'json/exp1' "${token:-EOF}";;
        esac
        read -r token
      done
      ;;
  esac
  :
}

parse_object () {
  local key
  local obj=''
  read -r token
  case "$token" in
    '}') ;;
    *)
      while :
      do
        case "$token" in
          '"'*'"') key=$token ;;
          *) messages 'json/exp2' "${token:-EOF}" "EXPECTED string GOT ${token:-EOF}" ;;
        esac
        read -r token
        case "$token" in
          ':') ;;
          *) messages 'json/exp3' "${token:-EOF}" ;;
        esac
        read -r token
        parse_value "$1" "$key"
        obj="$obj$key:$value"        
        read -r token
        case "$token" in
          '}') break ;;
          ',') obj="$obj," ;;
          *) messages 'json/exp4' "${token:-EOF}" ;;
        esac
        read -r token
      done
    ;;
  esac
  :
}

parse_value () {
  local jpath="${1:+$1,}$2" isleaf=0 isempty=0
  case "$token" in
    '{') parse_object "$jpath" ;;
    '[') parse_array  "$jpath" ;;
    ''|[!0-9]) messages 'json/exp5' "${token:-EOF}" ;;
    *) value=$token
       isleaf=1
       [ "$value" = '""' ] && isempty=1
       ;;
  esac
  [ "$value" = '' ] && return
  [ "$isleaf" -eq 1 ] && printf "[%s]\t%s\n" "$jpath" "$value"
  :
}

parse () {
  read -r token
  parse_value
  read -r token
  case "$token" in
    '') ;;
    *) messages 'json/exp6' "$token" ;;
  esac
}

getField() {
	echo "$fields" | grep "\[${1}\]" |
	  sed 's/.*\t//;s/^"//;s/"$//;s/^null$/false/;s/^$/false/'
}

tunlrApi() {
	local url='' json=''
	[ "$SECURE" = '1' ] && url='https' || url='http'
        url="$url://gatekeeper-api.tunlr.net/$API/$1/"
	messages 'api/get' "$url"
	url="${url}${TOKEN}"
	json=`{
		[ "$VERBOSE" -ge 4 ] || exec 2>/dev/null
		case "${CLIENT}" in
		  wget) wget -O - -U "${AGENT}" ${xOPT} "$url" ;;
		  curl) curl --fail --user-agent "${AGENT}" ${xOPT} "$url" ;;
		  *) messages 'api/client' ;;
		esac
	}` || messages 'api/noConnect' "${CLIENT}"
	[ -z "$json" ] && messages 'api/empty'
	echo "${json}" | tokenize | parse
}

tunlrUpdate() {
	local fields message plan days ipChange esc
	esc="$(echo | tr '\n' '\033')"

	echo
	fields=`tunlrApi get_account` || exit 1

	message=`getField '"message"'`
	messages "$message"

	[ "$(getField '"success"')" != "true" ] && messages 'no_account'
	messages "state/$(getField '"account","state"')"

	plan=`getField '"subscription","plan_name"'`
	[ "$plan" = "false" ] && messages 'state/expired'

	days=`getField '"subscription","days_remaining"'`
	if [ "$days" != 'false' ] && [ "$days" -le "$dWARN" ]; then
		messages 'days_remaining_w' "$days" "$plan"
	else
		messages 'days_remaining' "$days" "$plan"
	fi

	[ "$(getField '"subscription","up_for_renewal"')" = 'true' ] && messages 'up_for_renewal'

	ipChange=`getField '"subscription","has_ip_changed"'`
	case "$ipChange" in
	  false) messages 'no_ip_change';;
	  true ) messages 'ip_change';;
	  *    ) messages "$ipChange";;
	esac

	case "$message" in
	  *hosting | *abuse | *proxy) messages 'update/block';;
	esac

	if [ "$ipChange" = false ]; then
		messages 'update/none'
		echo ""
		exit 0
	fi
	fields=`tunlrApi update_ip` || exit 1
	message=`getField '"message"'`
	messages "$message"
	[ "$(getField '"success"')" != "true" ] && messages 'update/fail' || messages 'update/done'
	echo
}

API='api_v1'
tunlrUpdate
EOF