#!/bin/sh
# Ref: http://info2html.sourceforge.net/cgi-bin/info2html-demo/info2html?%28pinentry%29Protocol

set -e

VERSION="0.1";

if [ -z "$DISPLAY" ]; then
    DISPLAY=":1";
    export DISPLAY;
fi
prompt_string_default="PIN: "

title="";
prompt_string="$prompt_string_default";
description="";
keyinfo="";
repeat="";

error__password_mismatch="Passwords don't match";
AP_YES="Yes";
AP_NO="No";

prompt_action='/home/tavo/.config/scripts/menu/menu "$AP_PROMPT" pass';
confirm_action='echo -e "$AP_YES\n$AP_NO" | /home/tavo/.config/scripts/menu/menu "$AP_PROMPT"';
display_error_action='notify-send -a "Pinentry" "$AP_ERROR"';

# :: Prompt string (default if empty)
ask_password() {
  export AP_PROMPT="${1:-"$prompt_string"}";
  printf '' | sh -c "$prompt_action" 2> /dev/null;
}
# :: Prompt string (default if empty)
confirm() {
  export AP_PROMPT="${1:-"$prompt_string"}";
  export AP_YES; export AP_NO;
  printf '' | sh -c "$confirm_action" 2> /dev/null;
}
# :: Error text
show_error() {
  export AP_ERROR="$1";
  sh -c "$display_error_action" 2> /dev/null;
}

cancelled_error() { echo "ERR 83886179 Operation cancelled <Pinentry>"; }
com_error() { echo "ERR 83886360 IP parameter error <Pinentry>>"; }
not_implemented_error() { echo "ERR 536870981 Not implemented <User defined source 1>"; }
unknown_error() { echo "ERR 536871187 Unknown IPC command <User defined source 1>"; }


reset() {
    description= 
    prompt_string="$prompt_string_default" 
    keyinfo= 
    repeat= 
    error__password_mismatch= 
    setok= 
    setnotok= 
    setcancel= 
    title= 
}

save_option() {
  echo "OK";
}

get_info() {
  arg=$(echo "$1" | tr a-z A-Z)
  case "$arg" in
    VERSION)  echo "D $VERSION" && echo "OK" ;;
    PID)      echo "D $$" && echo "OK" ;;
    *)        com_error ;;
  esac;
}

# :: Repeat string -> Password -> OK | ERR
password_prompt() {
  pass="";
  if pass=$(ask_password "$([ -z "$1" ] && echo "$repeat")"); then
    if [ -n "$1" ]; then
      password_prompt ""  "$pass";
    else
      # If password repeat attempt failed, try again,
      # Else continue
      if [ -n "$2" ] && [ "$pass" != "$2" ]; then
        show_error "$error__password_mismatch";
        password_prompt "$repeat";
      else
        if [ ! -z "$pass" ]; then
            echo "D $pass" | 
                sed 's/%/%25/; s/\r/%0D/' |  # % = 25 and CR = %0D
                awk 'BEGIN {ORS="%0A"} /^..*$/ {print}' |  # LF = %0A
                sed 's/%0A$//' # Strip trailing %0A
            echo
        fi
        echo "OK";
      fi;
    fi;
  else
    cancelled_error;
  fi;
  repeat=
}

# :: OK | ERR
confirm_prompt() {
  if [ "$(confirm)" = "$AP_YES" ]; then
    echo "OK"
  else
    cancelled_error;
  fi
}

pinentry_help() {
    cat <<END
# NOP
# CANCEL
# OPTION
# BYE
# AUTH
# RESET
# END
# HELP
# SETDESC
# SETPROMPT
# SETKEYINFO
# SETREPEAT
# SETREPEATERROR
# SETERROR
# SETOK
# SETNOTOK
# SETCANCEL
# GETPIN
# CONFIRM
# MESSAGE
# SETQUALITYBAR
# SETQUALITYBAR_TT
# SETGENPIN
# SETGENPIN_TT
# GETINFO
# SETTITLE
# SETTIMEOUT
# CLEARPASSPHRASE
END
}

interpret_command() {
  # Refuse lines of more than 1000 bytes + newline
  if [ $(echo "$1" | wc -c) -gt 1001 ]; then
    echo "ERR too long"
    return
  fi
  cmd="$(echo "$1" | cut -d' ' -f1 | tr a-z A-Z)";
  data="$(echo "$1" | cut -d' ' -f2-)";

  case "${cmd}" in
    NOP)              ;;
    CANCEL)           not_implemented_error ;;
    OPTION)           save_option "$data" ;;
    BYE)              exit 0 ;;
    AUTH)             not_implemented_error ;;
    RESET)            reset ;;
    END)              not_implemented_error ;;
    HELP)             pinentry_help ;;
    SETDESC)          description="$data"; echo "OK" ;;
    SETPROMPT)        prompt_string="$data"; echo "OK" ;;
    SETKEYINFO)       keyinfo="$data"; echo "OK" ;;
    SETREPEAT)        repeat="${data:-"repeat"}"; echo "OK" ;;
    SETREPEATERROR)   error__password_mismatch="$data"; echo "OK" ;;
    SETERROR)         not_implemented_error ;;
    SETOK)            setok="$data" ;;
    SETNOTOK)         setnotok="$data" ;;
    SETCANCEL)        setcancel="$data" ;;
    GETPIN)           password_prompt "$repeat" "" ;;
    CONFIRM)          confirm_prompt ;;
    MESSAGE)          not_implemented_error ;;
    SETQUALITYBAR)    not_implemented_error ;;
    SETQUALITYBAR_TT) not_implemented_error ;;
    SETGENPIN)        not_implemented_error ;;
    SETGENPIN_TT)     not_implemented_error ;;
    GETINFO)          get_info "$data" ;;
    SETTITLE)         title="$data"; echo "OK" ;;
    SETTIMEOUT)       not_implemented_error ;;
    CLEARPASSPHRASE)  not_implemented_error ;;
    '' | \#*)         ;;
    *)                unknown_error ;;
  esac;
}




help() {
  echo "Usage: $0 [-D DISPLAY] [--prompt script] [--confirm script] [-hv]";

cat <<END
$0 (pinentry) $VERSION
Copyright (C) 2020 Akshay Nair
This software is released under the MIT License <https://www.mit-license.org/>

END

printf "Usage: %s [options] (-h for help)\n" "$0" >&2

cat <<END
Ask securely for a secret and print it to stdout.
Options:
     --prompt CMD           How to ask for the secret
     --confirm CMD          How to confirm the secret
     --error-command CMD    What to do if secret is not confirmed

CMD will be executed by sh(1). The defaults are:

prompt ='dmenu -P -p "$prompt_string"';
confirm ='echo -e "$AP_YES\n$AP_NO" | dmenu -p "$prompt_string"';
error-command ='notify-send -a "Pinentry" "$error__password_mismatch"';

Standard pinentry options
 -d, --debug                Turn on debugging output
 -D, --display DISPLAY      Set the X display
 -T, --ttyname FILE         Set the tty terminal node name
 -N, --ttytype NAME         Set the tty terminal type
 -C, --lc-ctype STRING      Set the tty LC_CTYPE value
 -M, --lc-messages STRING   Set the tty LC_MESSAGES value
 -o, --timeout SECS         Timeout waiting for input after this many seconds
 -g, --no-global-grab       Grab keyboard only while window is focused
 -W, --parent-wid           Parent window ID (for positioning)
 -c, --colors STRING        Set custom colors for ncurses
 -a, --ttyalert STRING      Set the alert mode (none, beep or flash)

Please report bugs to <https://github.com/phenax/any-pinentry/issues>.
END
}

parse_cliargs() {
  getopt -T || exit_status=$?
  if [ $exit_status -ne 4 ]; then
      printf "Your version of getopt(1) is out of date\n" >&2
      exit 1
  fi

  TEMP=$(getopt -n "$0" -o dD:T:N:C:M:o:gWc:a:h \
                --long debug,display:,ttyname:,ttytype:,lc-ctype:,lc-messages:,timeout:,no-global-grab,parent-wid,colors:,ttyalert:,prompt:,confirm:,error:,help,version \
                -- "$@")

  if [ $? -ne 0 ]; then
      help
      exit 1
  fi

  eval set -- "$TEMP"
  unset TEMP

  while true; do
      case "$1" in
          -d | --debug)          shift ;;
          -D | --display)        shift 2 ;;
          -T | --ttyname)        shift 2 ;;
          -N | --ttytype)        shift 2 ;;
          -C | --lc-ctype)       shift 2 ;;
          -M | --lc-messages)    shift 2 ;;
          -o | --timeout)        shift 2 ;;
          -g | --no-global-grab) shift ;;
          -W | --parent-wid)     shift ;;
          -c | --colors)         echo "colors=$2"; shift 2 ;;
          -a | --ttyalert)       shift 2 ;;
               --error-command)  display_error_action="$2"; shift 2 ;;
               --prompt)         prompt_action="$2"; shift 2 ;;
               --confirm)        confirm_action="$2"; shift 2 ;;
          -h | --help)           help && exit 0 ;;
               --version)        echo "anypinentry-$VERSION" && exit 0 ;;
          --)                    shift; break ;;
          *)                     ;;
      esac
  done
}



# Main
parse_cliargs "$@";

# Initialize protocol
echo "OK Pleased to meet you"

while read -r line; do
  interpret_command "$line";
done;

#  "SETERROR"
#  "SETOK"
#  "SETNOTOK"
#  "SETCANCEL"
#  "MESSAGE"
#  "SETQUALITYBAR"
#  "SETQUALITYBAR_TT"
#  "SETTIMEOUT"
#  "CLEARPASSPHRASE"