accountmanager.sh 6.08 KB
#!/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: