#!/usr/bin/env bash
# Turn one rotating ISP Proxy line into static lines, each with its own fixed IP,
# or check that saved static lines still exit through their IPs.

usage() {
  cat >&2 <<'EOF'
Usage:
  isp-static.sh [-p socks5|http] LINE [COUNT]    make COUNT static lines (default 10), save to static.txt
  isp-static.sh check [-p socks5|http] [FILE]    check lines from FILE (default static.txt)

LINE is HOST:PORT:LOGIN:PASSWORD, optionally with a socks5:// or http:// prefix.
A prefix in the line wins over -p; without both the script uses SOCKS5.
EOF
  exit 1
}

ECHO_URL=http://ifconfig.me/ip
MODE=make
[ "$1" = check ] && { MODE=check; shift; }
OPT_PROTO=
while getopts "p:" opt; do
  case $opt in
    p) OPT_PROTO=$OPTARG ;;
    *) usage ;;
  esac
done
shift $((OPTIND - 1))
case $OPT_PROTO in ''|socks5|http) ;; *) usage ;; esac

parse() {
  local l=$1
  PREFIX=
  case $l in
    http://*) PREFIX=http:// ;;
    socks5://*) PREFIX=socks5:// ;;
    socks5h://*) PREFIX=socks5h:// ;;
  esac
  l=${l#"$PREFIX"}
  if [ -n "$PREFIX" ]; then PROTO=${PREFIX%://}; else PROTO=${OPT_PROTO:-socks5}; fi
  [ "$PROTO" = http ] && PROXY="http://" || PROXY="socks5h://"
  IFS=: read -r HOST PORT LOGIN PASS <<< "$l"
  PROXY="$PROXY$HOST:$PORT"
}

ip_of() { curl -s -m 15 -x "$PROXY" -U "$1:$PASS" "$ECHO_URL" < /dev/null; }

bar() {
  [ -t 2 ] || return 0
  local done=$1 total=$2 width=30
  local fill; fill=$(printf '%*s' $((done * width / total)) '' | tr ' ' '=')
  [ "$done" -lt "$total" ] && fill="$fill>"
  printf '\r%3d%%[%-*s] %d/%d' $((done * 100 / total)) "$width" "$fill" "$done" "$total" >&2
}

say() {
  [ -t 2 ] && printf '\r\033[K' >&2
  echo "$1"
}

make_lines() {
  [ -n "$1" ] || usage
  parse "$1"
  local count=${2:-10} found=0 tries=0 answered=0 seen=" " ip
  : > static.txt
  bar 0 "$count"
  while [ "$found" -lt "$count" ] && [ "$tries" -lt $((count * 3)) ]; do
    tries=$((tries + 1))
    ip=$(ip_of "$LOGIN")
    if ! [[ $ip =~ ^[0-9.]+$ ]]; then
      if [ "$answered" -eq 0 ] && [ "$tries" -ge 3 ]; then
        say "No answer through $PROXY: check the line, the port and the protocol (-p)" >&2
        exit 1
      fi
      continue
    fi
    answered=1
    case $seen in *" $ip "*) continue ;; esac
    seen="$seen$ip "
    [ "$(ip_of "$LOGIN-ip-$ip")" = "$ip" ] || continue
    found=$((found + 1))
    echo "$PREFIX$HOST:$PORT:$LOGIN-ip-$ip:$PASS" >> static.txt
    bar "$found" "$count"
  done
  [ -t 2 ] && echo >&2
  echo "Static lines: $found of $count, saved to static.txt"
}

check_lines() {
  local file=${1:-static.txt} total n=0 ok=0 line want got
  [ -r "$file" ] || { echo "Cannot read $file" >&2; exit 1; }
  total=$(grep -c . "$file")
  [ "$total" -gt 0 ] || { echo "No lines in $file" >&2; exit 1; }
  bar 0 "$total"
  while IFS= read -r line; do
    line=${line%$'\r'}
    [ -n "$line" ] || continue
    parse "$line"
    want=${LOGIN##*-ip-}
    [ "$want" = "$LOGIN" ] && want="(no -ip- in login)"
    got=$(ip_of "$LOGIN")
    n=$((n + 1))
    if [ "$got" = "$want" ]; then
      ok=$((ok + 1))
      say "$(printf '%-15s -> %-15s OK' "$want" "$got")"
    else
      say "$(printf '%-15s -> %-15s FAIL' "$want" "${got:-no answer}")"
    fi
    bar "$n" "$total"
  done < "$file"
  [ -t 2 ] && echo >&2
  echo "Checked $n: $ok OK, $((n - ok)) failed"
}

if [ "$MODE" = check ]; then check_lines "$1"; else make_lines "$1" "$2"; fi
