#!/bin/sh
#
#    ssh-import-id - authorize a user by fetching their key
#                    from a public SSH keyserver; Launchpad.net
#                    by default
#
#    Copyright (C) 2010 Canonical Ltd.
#
#    Authors: Dustin Kirkland <kirkland@canonical.com>
#             Scott Moser <smoser@canonical.com>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, version 3 of the License.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Abort on any unhandled error
set -e

usage() {
	cat <<EOF
Usage: ${0##*/} [options] USER_ID [USER_ID_2] ... [USER_ID_n]

   import ssh keys for listed users, writing output to a file.

   options:
      -h | --help        this help
      -o | --output F    write output to file 'F' 
                         (default ~/.ssh/authorized_keys)
EOF
}
bad_usage() { usage 1>&2; echo "$@" 1>&2; exit 1; }

error() {
	printf "ERROR: %s\n" "$@" 1>&2
	exit 1
}

warn() {
	printf "WARNING: %s\n" "$@" 1>&2
}

info() {
	printf "INFO: %s\n" "$@" 1>&2
}

url_encode() {
	# from http://andy.wordpress.com/2008/09/17/urlencode-in-bash-with-perl/
	printf "%s" "$1" | perl -pe's/([^-_.~A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg'
}

validate_keys() {
	# Split the retrieved public keys into one-line-per-file
	# Validate that ssh can calculate the fingerprint of each key
	# Show fingerprints on stderr (not stdout)
	# Return non-zero of any one fails to compute
	# Remove blank lines
	LC_COLLATE=C sed -i -e '/^$/d' -e '/^\r/d' "$1"
	local tmpdir=$(mktemp -d)
	split -l 1 "$1" "$tmpdir/"
	local i
	local rc=0
	for i in "$tmpdir/"*; do
		ssh-keygen -l -f "$i" 1>&2 || rc=1
	done
	rm -rf "$tmpdir"
	[ "$rc" = "0" ]
}

get_authkeypath() {
	# Only support writing to this user's authorized_keys file
	local home="${HOME}"
	if [ -z "$HOME" ]; then
		uid=$(id -u) || error "Cannot determine user id"
		[ -n "$uid" ] || error "User id cannot be empty"
		pwline=$(getent passwd "$uid") || error "Cannot get passwd entry"
		home=$(echo "$pwline" | awk -F: '{print $6}') || error "Cannot determine home directory"
		[ -n "$home" ] || error "Home directory cannot be empty"
	fi

	_RET="${home}/.ssh/authorized_keys"
}

# Obtain the URL string
if [ -z "$URL" ]; then
	URL="https://launchpad.net/~%s/+sshkeys"
	{ [ ! -e /etc/ssh/ssh_import_id ] || . /etc/ssh/ssh_import_id ; } || error "failed to source /etc/ssh/ssh_import_id"
fi
[ -n "$URL" ] || error "URL must be set"

short_opts="heo:"
long_opts="help,environment,output:"
getopt_out=$(getopt --shell sh --name "${0##*/}" \
	--options "${short_opts}" --long "${long_opts}" -- "$@") &&
	eval set -- "${getopt_out}" ||
	bad_usage

output=""
clean_env="env -i"
while [ $# -ne 0 ]; do
	cur=${1}; next=${2};
	case "$cur" in
		-h|--help) usage; exit 0;;
		-e|--environment) clean_env="" ;;
		-o|--output) output="${2}"; shift;;
		--) shift; break;;
	esac
	shift;
done

[ -n "$1" ] || bad_usage "must give user"

if [ -z "${output}" ]; then
	get_authkeypath
	output=${_RET}
	dir=${output%/*}
	mkdir -p -m 0700 "${dir}" 2>/dev/null || true
	[ -d "$dir" ] || error "Cannot create directory [$dir]"
	[ -e "$output" ] || (umask 0177 && touch "$output") ||
		error "Cannot create [$output]"
fi

tmp=$(mktemp)
trap "rm -f $tmp" EXIT HUP INT QUIT TERM
rc=0
for i in "$@"; do
	i=$(url_encode "$i") || error "Failed encoding [$i]"
	url=$(printf "$URL" "$i")
	if ! $clean_env wget --quiet -O- "$url" > "$tmp"; then
 		rc=$(($rc+1));
		warn "Failed to retrieve key for [$i] from [$url]"
		continue
	fi
	echo >> "$tmp" # needed for wc
	if ! validate_keys "$tmp"; then
		warn "Invalid keys at [$url]"
 		rc=$(($rc+1));
		continue
	fi
	if [ "${output}" = "-" ]; then
		cat "$tmp" || error "Could not write to stdout";
	else
		cat "$tmp" >> "$output" || error "Could not write to [$output]";
	fi
	info "Successfully authorized [$i]"
done
exit $rc

# vi: ts=4 noexpandtab
