#!/bin/sh AMNGRDB="${AMNGRDB:-${HOME}/.account.db}" AMNGRID="${AMNGRID:-${USER}@${HOSTNAME}}" AMNGRPWLEN="${AMNGRPWLEN:-10}" alias random="cat /dev/urandom" function rand_printable() { if ! [[ "${1}" =~ "^[0-9]+$" ]] then printf "Usage: %s: [len]" "$0" fi echo -n "$(random | tr -dc ' !#-&(-~' | head -c${1:-512})" } function amngrdbinit() { local DB="${1:-${AMNGRDB}}" 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 DB="${1:-${AMNGRDB}}" local CHECK cat <<-EOT WARNING: You are about to remove your account data. There is no way to recover from this. Are you really shure you want to do this? EOT echo -n "[Yes|[No]]: " && read CHECK CHECK="${CHECK:-No}" test "${CHECK}" == "Yes" && rm -f "${AMNGRDB}" } function amngrid() { local ACCOUNT_NAME="${1}" echo "$(sqlite3 "${AMNGRDB}" <<-EOD SELECT id FROM account WHERE name='${ACCOUNT_NAME}'; EOD )" } function amngrcrypt() { local PLAIN="${1}" echo -n "${PLAIN}" | gpg -aeqr "${AMNGRID}" } function amngrgen() { local LEN="${0:-${AMNGRPWLEN}}" amngrcrypt "$(rand_printable "${LEN}")" } function amngradd() { local ACCOUNT_NAME="${1}" local USER="${2}" local PASSWORD="$(amngrcrypt "${3}")" local DESCRIPTION="${4:-NO DESCRIPTION}" local ACCOUNT_ID="$(amngrid "${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 "${AMNGRDB}" "${QUERY}" } function amngrcreate() { local ACCOUNT_NAME="${1}" local USER="${2}" local DESCRIPTION="${3:-NO DESCRIPTION}" local PASSWORD="$(rand_printable 10)" amngradd "${ACCOUNT_NAME}" "${USER}" "${PASSWORD}" "${DESCRIPTION}" amngrgetpass "${ACCOUNT_NAME}" } function amngrgetuser() { local ACCOUNT_NAME="${1}" local STATE="${2:-"1"}" test "${STATE}" != "1" -a "${STATE}" != "2" && STATE="1" sqlite3 "${AMNGRDB}" <<-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 } function amngrgetolduser() { local ACCOUNT_NAME="${1}" amngrgetuser "${ACCOUNT_NAME}" "2" } function amngrgetpass() { local ACCOUNT_NAME="${1}" local STATE="${2:-1}" test "${STATE}" != "1" -a "${STATE}" != "2" && STATE="1" sqlite3 "${AMNGRDB}" <<-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() { local ACCOUNT_NAME="${1}" amngrgetpass "${ACCOUNT_NAME}" "2" } function amngrrename() { local OLD_NAME="${1}" local NEW_NAME="${2}" test -z "${OLD_NAME}" -o -z "${NEW_NAME}" && return 1 sqlite3 "${AMNGRDB}" <<-EOD UPDATE account SET name='${NEW_NAME}' WHERE name='${OLD_NAME}'; EOD } function amngrdelete() { local ACCOUNT_NAME="${1}" sqlite3 "${AMNGRDB}" <<-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 amngrlist() { local SEPARATOR="${1:-" => "}" sqlite3 -separator "${SEPARATOR}" "${AMNGRDB}" <<-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; EOD } function amngrsearch() { local PATTERN local DELIMITER=" => " local USAGE="$(printf "Usage: %s: [-d delimiter] pattern" "$0")" while getopts d: opt do case $opt in d) DELIMITER="${OPTARG}";; ?) echo "${USAGE}" exit 1;; esac done shift $(($OPTIND-1)) if [ $# -lt 2 ] then echo "${USAGE}" exit 1;; fi PATTERN="${1}" sqlite3 -separator " => " "${AMNGRDB}" <<-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: