#!/bin/sh AMNGRDB="${AMNGRDB:-${HOME}/.account.db}" AMNGRID="${AMNGRID:-${USER}@${HOSTNAME}}" AMNGRPWLEN="${AMNGRPWLEN:-10}" alias random="cat /dev/urandom" function rand_printable() { local OPT OPTARG OPTIND local USAGE="$(printf "Usage: %s [-h] [len]" "$0")" while getopts h OPT do case $OPT in h) echo "${USAGE}" return 0;; ?) echo "${USAGE}" return 1;; esac done shift $(($OPTIND-1)) if ! [[ "${1}" =~ ^[0-9]*$ ]] then echo "${USAGE}" return 1 fi echo -n "$(random | tr -dc ' !#-&(-~' | head -c${1:-512})" } function amngrdbinit() { local OPT OPTARG OPTIND DB local USAGE="$(printf "Usage: %s [-h] [dbfile]" "$0")" while getopts h OPT do case $OPT in h) echo "${USAGE}" return 0;; ?) echo "${USAGE}" return 1;; esac done shift $(($OPTIND-1)) DB="${1:-${AMNGRDB}}" if [ ! \( -d "$(dirname "${DB}")" \) ] then echo "${USAGE}" return 1 fi sqlite3 "${DB}" <<-EOD PRAGMA foreign_keys = ON; CREATE TABLE IF NOT EXISTS account ( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(128) UNIQUE NOT NULL, desc TEXT DEFAULT NULL); CREATE TABLE IF NOT EXISTS cred ( id INTEGER PRIMARY KEY AUTOINCREMENT, user VARCHAR(128) NOT NULL, pass TEXT NOT NULL); CREATE TABLE IF NOT EXISTS account_cred ( account_id INTEGER, cred_id INTEGER, state SMALLINT(1) NOT NULL, FOREIGN KEY(account_id) REFERENCES account(id) ON UPDATE CASCADE ON DELETE CASCADE, FOREIGN KEY(cred_id) REFERENCES cred(id) ON UPDATE CASCADE ON DELETE CASCADE); EOD } function amngrdbdestroy() { local OPT OPTARG OPTIND DB CHECK local USAGE="$(printf "Usage: %s [-h] [dbfile]" "$0")" while getopts h OPT do case $OPT in h) echo "${USAGE}" return 0;; ?) echo "${USAGE}" return 1;; esac done shift $(($OPTIND-1)) DB="${1:-${AMNGRDB}}" cat <<-EOT WARNING: You are about to remove your account data. There is no way to recover from this. Are you really sure you want to do this? EOT printf "Really remove %s (Yes|[No]): " "${DB}" && read CHECK CHECK="${CHECK:-No}" test "${CHECK}" == "Yes" && rm -f "${DB}" } function amngrid() { local OPT OPTARG OPTIND ACCOUNT_NAME DB local USAGE="$(printf "Usage: %s [-h] [-D dbfile] account" "$0")" while getopts hD: OPT do case $OPT in D) DB="${OPTARG}";; h) echo "${USAGE}" return 0;; ?) echo "${USAGE}" return 1;; esac done shift $(($OPTIND-1)) ACCOUNT_NAME="${1}" DB="${DB:-${AMNGRDB}}" if [ -z "${ACCOUNT_NAME}" ] then echo "${USAGE}" return 1 fi echo "$(sqlite3 "${DB}" <<-EOD SELECT id FROM account WHERE name='${ACCOUNT_NAME}'; EOD )" } function amngrcrypt() { local OPT OPTARG OPTIND PLAIN RECIPIENT local OPTEND=0 local USAGE="$(printf "Usage: %s [-h] [-r recipient] password" "$0")" while getopts hr:- OPT do case $OPT in r) RECIPIENT="${OPTARG}";; h) echo "${USAGE}" return 0;; -) OPTEND=1;; ?) echo "${USAGE}" return 1;; esac test ${OPTEND} -eq 1 && break done shift $(($OPTIND-1)) RECIPIENT="${RECIPIENT:-${AMNGRID}}" PLAIN="${1}" if [ -z "${PLAIN}" ] then echo "${USAGE}" return 1 fi echo -n "${PLAIN}" | gpg -aeqr "${RECIPIENT}" } function amngrgen() { local OPT OPTARG OPTIND RECIPIENT local USAGE="$(printf "Usage: %s [-h] [-r recipient] [len]" "$0")" while getopts hr: OPT do case $OPT in r) RECIPIENT="${OPTARG}";; h) echo "${USAGE}" return 0;; ?) echo "${USAGE}" return 1;; esac done shift $(($OPTIND-1)) local LEN="${1:-${AMNGRPWLEN}}" if [ "${RECIPIENT}" ] then amngrcrypt -r "${RECIPIENT}" "$(rand_printable "${LEN}")" else amngrcrypt "$(rand_printable "${LEN}")" fi } function amngradd() { local OPT OPTARG OPTIND DESCRIPTION RECIPIENT DB local OPTEND=0 local USAGE="$(cat <<-EOT Usage: $0 [-h] [-d description] [-D dbfile] [-r recipient] account [username [password]] EOT )" while getopts d:D:hr:- OPT do case $OPT in d) DESCRIPTION="${OPTARG}";; D) DB="${OPTARG}";; r) RECIPIENT="${OPTARG}";; h) echo "${USAGE}" return 0;; -) OPTEND=1;; ?) echo "${USAGE}" return 1;; esac test ${OPTEND} -eq 1 && break done shift $(($OPTIND-1)) RECIPIENT="${RECIPIENT:-${AMNGRID}}" DB="${DB:-${AMNGRDB}}" local ACCOUNT_NAME="${1}" DESCRIPTION="${DESCRIPTION:-NO DESCRIPTION}" if [ -z "${ACCOUNT_NAME}" ] then echo "No account name given." echo "${USAGE}" return 1 fi if [ "${2}" ] then USER="${2}" else USER="$(amngrgetuser -D "${DB}" -c "${ACCOUNT_NAME}")" fi if [ -z "${USER}" ] then printf "Can't find current user for account %s\n" "${ACCOUNT_NAME}" echo "${USAGE}" return 2 fi if [ "${3}" ] then PASSWORD="$(amngrcrypt -r "${RECIPIENT}" -- "${3}")" else PASSWORD="$(amngrgen -r "${RECIPIENT}")" echo -n "${PASSWORD}" | gpg -dq | xclip -i fi if [ -z "${PASSWORD}" ] then printf "Failed to create encrypted password for account %s\n" \ "${ACCOUNT_NAME}" echo "${USAGE}" return 3 fi local ACCOUNT_ID="$(amngrid -D "${DB}" "${ACCOUNT_NAME}")" local QUERY="$(cat <<-EOD PRAGMA foreign_keys = ON; BEGIN TRANSACTION; INSERT INTO cred (user, pass) VALUES ('${USER}', '${PASSWORD}'); EOD )" if [ -z "${ACCOUNT_ID}" ] then QUERY="$(cat <<-EOD ${QUERY} INSERT INTO account (name, desc) VALUES ('${ACCOUNT_NAME}', '${DESCRIPTION}'); EOD )" else QUERY="$(cat <<-EOD ${QUERY} UPDATE account_cred SET state=0 WHERE account_id=(SELECT id FROM account WHERE name='${ACCOUNT_NAME}' AND state=2); UPDATE account_cred SET state=2 WHERE account_id=(SELECT id FROM account WHERE name='${ACCOUNT_NAME}' AND state=1); EOD )" fi QUERY="$(cat <<-EOD ${QUERY} INSERT INTO account_cred (account_id, cred_id, state) VALUES ((SELECT id FROM account WHERE name='${ACCOUNT_NAME}'), (SELECT id FROM cred WHERE user='${USER}' AND pass='${PASSWORD}'), 1); COMMIT TRANSACTION; EOD )" sqlite3 "${DB}" "${QUERY}" } function amngrgetuser() { local OPT OPTARG OPTIND DB local STDOUT=0 local USAGE="$(cat <<-EOT Usage: $0 [-h] [-c] [-D dbfile] account [state] EOT )" while getopts hcD: OPT do case $OPT in c) STDOUT=1;; D) DB="${OPTARG}";; h) echo "${USAGE}" return 0;; ?) echo "${USAGE}" return 1;; esac done shift $(($OPTIND-1)) local ACCOUNT_NAME="${1}" local STATE="${2:-"1"}" DB="${DB:-${AMNGRDB}}" if [ -z "${ACCOUNT_NAME}" ] then echo "${USAGE}" return 1 fi test "${STATE}" != "1" -a "${STATE}" != "2" && STATE="1" if [ $STDOUT -eq 0 ] then sqlite3 "${DB}" <<-EOD |\ awk 'NR>1{print p}{p=$0}END{ORS="";print}' | xclip -i SELECT user FROM account JOIN account_cred ON account.id=account_cred.account_id JOIN cred ON cred.id=account_cred.cred_id WHERE name='${ACCOUNT_NAME}' AND state=${STATE}; EOD else sqlite3 "${DB}" <<-EOD |\ awk 'NR>1{print p}{p=$0}END{ORS="";print}' SELECT user FROM account JOIN account_cred ON account.id=account_cred.account_id JOIN cred ON cred.id=account_cred.cred_id WHERE name='${ACCOUNT_NAME}' AND state=${STATE}; EOD fi } function amngrgetolduser() { amngrgetuser "$@" "2" } function amngrgetpass() { local OPT OPTARG OPTIND DB local USAGE="$(printf "Usage: %s [-h] [-D dbfile] account [state]" "$0")" while getopts hD: OPT do case $OPT in D) DB="${OPTARG}";; h) echo "${USAGE}" return 0;; ?) echo "${USAGE}" return 1;; esac done shift $(($OPTIND-1)) local ACCOUNT_NAME="${1}" local STATE="${2:-1}" DB="${DB:-${AMNGRDB}}" if [ -z "${ACCOUNT_NAME}" ] then echo "${USAGE}" return 1 fi test "${STATE}" != "1" -a "${STATE}" != "2" && STATE="1" sqlite3 "${DB}" <<-EOD |\ awk 'NR>1{print p}{p=$0}END{ORS="";print}' | gpg -dq | xclip -i SELECT pass FROM account JOIN account_cred ON account.id=account_cred.account_id JOIN cred ON cred.id=account_cred.cred_id WHERE name='${ACCOUNT_NAME}' AND state=${STATE}; EOD } function amngrgetoldpass() { amngrgetpass "$@" "2" } function amngrrename() { local OPT OPTARG OPTIND DB local USAGE="$(cat <<-EOT Usage: $0 [-h] [-D dbfile] old_account new_account EOT )" while getopts hD: OPT do case $OPT in D) DB="${OPTARG}";; h) echo "${USAGE}" return 0;; ?) echo "${USAGE}" return 1;; esac done shift $(($OPTIND-1)) local OLD_NAME="${1}" local NEW_NAME="${2}" DB="${DB:-${AMNGRDB}}" if [ -z "${OLD_NAME}" -o -z "${NEW_NAME}" ] then echo "${USAGE}" return 1 fi sqlite3 "${DB}" <<-EOD UPDATE account SET name='${NEW_NAME}' WHERE name='${OLD_NAME}'; EOD } function amngrdelete() { local OPT OPTARG OPTIND DB local USAGE="$(printf "Usage: %s [-h] [-D dbfile] account" "$0")" while getopts hD: OPT do case $OPT in D) DB="${OPTARG}";; h) echo "${USAGE}" return 0;; ?) echo "${USAGE}" return 1;; esac done shift $(($OPTIND-1)) local ACCOUNT_NAME="${1}" DB="${DB:-${AMNGRDB}}" if [ -z "${ACCOUNT_NAME}" ] then echo "${USAGE}" return 1 fi sqlite3 "${DB}" <<-EOD PRAGMA foreign_keys = ON; BEGIN TRANSACTION; DELETE FROM cred WHERE id IN ( SELECT cred_id FROM account JOIN account_cred ON account.id=account_cred.account_id WHERE name='${ACCOUNT_NAME}'); DELETE FROM account WHERE name='${ACCOUNT_NAME}'; COMMIT TRANSACTION; EOD } function amngrsearch() { local OPT OPTARG OPTIND SEPARATOR PATTERN local USAGE="$(cat <<-EOT Usage: $0 [-h] [-D dbfile] [-s separator] [pattern] EOT )" while getopts hD:s: OPT do case $OPT in D) DB="${OPTARG}";; s) SEPARATOR="${OPTARG}";; h) echo "${USAGE}" return 0;; ?) echo "${USAGE}" return 1;; esac done shift $(($OPTIND-1)) PATTERN="${1}" SEPARATOR="${SEPARATOR:-" => "}" DB="${DB:-${AMNGRDB}}" sqlite3 -separator "${SEPARATOR}" "${DB}" <<-EOD SELECT name, user, desc FROM account JOIN account_cred ON account.id=account_cred.account_id JOIN cred ON cred.id=account_cred.cred_id WHERE state=1 AND ( name LIKE '%${PATTERN}%' OR user LIKE '%${PATTERN}%' OR desc LIKE '%${PATTERN}%'); EOD } case "$(basename -- "$0")" in random) random;; rand_printable) rand_printable "$@";; amngrdbinit) amngrdbinit "$@";; amngrdbdestroy) amngrdbdestroy "$@";; amngradd) amngradd "$@";; amngrcreate) amngrcreate "$@";; amngrcrypt) amngrcrypt "$@";; amngrdelete) amngrdelete "$@";; amngrgen) amngrgen "$@";; amngrgetoldpass) amngrgetoldpass "$@";; amngrgetolduser) amngrgetolduser "$@";; amngrgetpass) amngrgetpass "$@";; amngrgetuser) amngrgetuser "$@";; amngrid) amngrid "$@";; amngrlist) amngrlist "$@";; amngrrename) amngrrename "$@";; amngrsearch) amngrsearch "$@";; *) ;; esac # vim: set ft=sh ts=4 sw=4: