#!/bin/sh -e
#
# ssh-copy-id
# Copyright (C) 2001  Dmitry V. Levin <ldv@fandra.org>
#
# Shell script to install your public identity file on a remote machine
# Obviously, the remote machine must accept password authentication,
# or one of the other keys in your ssh-agent, for this to work.
#
# 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; either version 2 of the License, or
# (at your option) any later version.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#

PROG="${0##*/}"

ID_DIR="$HOME/.ssh"
DEFAULT_RSA1_ID="$ID_DIR/identity.pub"
DEFAULT_RSA2_ID="$ID_DIR/id_rsa.pub"
DEFAULT_DSA_ID="$ID_DIR/id_dsa.pub"

AUTH_DIR=".ssh"
AUTH_SSH1="authorized_keys"
AUTH_SSH2="authorized_keys2"

SSH1_IDS=
SSH2_IDS=
MANUAL_IDS=

USAGE()
{
	cat >&2 <<EOF
ssh-copy-id is a script to install your public identity files on a remote
machine. Obviously, the remote machine must accept password authentication,
or one of the other keys in your ssh-agent, for this to work.

ssh-copy-id is free software, covered by the GNU General Public License.
ssh-copy-id comes with ABSOLUTELY NO WARRANTY, see license for details.

Usage: $PROG [-i identity_file]* [user@]hostname

By default, ssh agent will be checked for identities, and if not found,
file "~/.ssh/identity.pub" will be used as RSA identity.
EOF
	[ -n "$1" ] && exit "$1" || exit
}

AddIdentityFile()
{
	local s="$(cat "$1" |head -c7)"
	local n="$(echo "$s" |head -c4)"
	if [ 'ssh-dss' == "$s" ]; then
		SSH2_IDS="$SSH2_IDS
$(cat "$1")"
	elif [ 'ssh-rsa' == "$s" ]; then
		SSH2_IDS="$SSH2_IDS
$(cat "$1")"
	elif [ 1 != "$[n+1]" ]; then
	SSH1_IDS="$SSH1_IDS
$(cat "$1")"
	else
		echo "Identity file \"$1\" has unrecognized type." >&2
		exit 1
	fi
}

UploadIds()
{
	local ids="$1"
	shift
	local auth="$1"
	shift
	echo "$ids" |ssh "$@" "set -e; test -d $AUTH_DIR ||mkdir $AUTH_DIR; cd $AUTH_DIR; chmod u+w . $auth &>/dev/null ||:; cat >>$auth; chmod go-w . .. $auth"
}

TEMP=`getopt -n $PROG -o i:h -l identity:,help -- "$@"` || USAGE
eval set -- "$TEMP"

while :; do
	case "$1" in
		-i|--identity)
			shift
			FILE="${1##*/}"
			if [ -z "$FILE" ]; then
				echo "Invalid identity filename: $1" >&2
				exit 1
			fi
			DIR="${1%/*}"
			[ -n "$DIR" -a "$DIR" != "$1" ] || DIR="$ID_DIR"
			ID_PATH="$DIR/$FILE"

			# Add .pub suffix if required
			if [ "$ID_PATH" == "${ID_PATH%.pub}" -a -f "$ID_PATH" -a -f "$ID_PATH.pub" ]; then
				ID_PATH="$ID_PATH.pub"
			fi
			if [ ! -f "$ID_PATH" ]; then
				echo "Identity file \"$ID_PATH\" is not available." >&2
				exit 1
			fi

			AddIdentityFile "$ID_PATH"
			MANUAL_IDS=1
			shift
			;;
		-h|--help) USAGE 0
			;;
		--) shift; break
			;;
		*) echo "$PROG: unrecognized option: $1" >&2; exit 1
			;;
	esac
done

[ -n "$*" ] || USAGE

if [ -z "$MANUAL_IDS" ]; then
	# Try to fetch keys from the authentication agent.
	if [ -n "$SSH_AUTH_SOCK" ] && AGENT_IDS="$(ssh-add -L)" && [ 'The agent has no identities.' != "$AGENT_IDS" ]; then
		PARSED_IDS=$(echo "$AGENT_IDS" |while read; do
			# Try to find out the method.
			s="$(echo "$REPLY" |head -c7)"
			n="$(echo "$s" |head -c4)"
			if [ ssh-dss == "$s" ]; then
					echo "SSH2	$REPLY"
			elif [ ssh-rsa == "$s" ]; then
					echo "SSH2	$REPLY"
			elif [ 1 != "$[n+1]" ]; then
					echo "SSH1	$REPLY"
			else
				echo "The agent returns unrecognized identity." >&2
				exit 1
			fi
		done)
		SSH1_IDS="$SSH1_IDS
$(echo "$PARSED_IDS" |grep '^SSH1	' |cut -f2-)"
		SSH2_IDS="$SSH2_IDS
$(echo "$PARSED_IDS" |grep '^SSH2	' |cut -f2-)"
	else
		[ -f "$DEFAULT_RSA1_ID" ] && AddIdentityFile "$DEFAULT_RSA1_ID" ||:
		[ -f "$DEFAULT_RSA2_ID" ] && AddIdentityFile "$DEFAULT_RSA2_ID" ||:
		[ -f "$DEFAULT_DSA_ID" ] && AddIdentityFile "$DEFAULT_DSA_ID" ||:
	fi
fi

SSH1_IDS="$(echo "$SSH1_IDS" |egrep -v '^(#|$)' ||:)"
if [ -n "$SSH1_IDS" ]; then
	SSH1_n=`echo "$SSH1_IDS" |wc -l |xargs echo`
	if [ 1 == $[SSH1_n%10] ]; then
		SSH1_s=identity
	else
		SSH1_s=identities
	fi
else
	SSH1_n=0
fi

SSH2_IDS="$(echo "$SSH2_IDS" |egrep -v '^(#|$)' ||:)"
if [ -n "$SSH2_IDS" ]; then
	SSH2_n=`echo "$SSH2_IDS" |wc -l |xargs echo`
	if [ 1 == $[SSH2_n%10] ]; then
		SSH2_s=identity
	else
		SSH2_s=identities
	fi
else
	SSH2_n=0
fi

if [ $SSH1_n -eq 0 -a $SSH2_n -eq 0 ]; then
	echo "No identities to install." >&2
	exit 1
fi

MODIFIED=
if [ $SSH2_n -gt 0 ]; then
	echo -n "Adding $SSH2_n SSH2 $SSH2_s... "
	if UploadIds "$SSH2_IDS" "$AUTH_SSH2" "$@"; then
		MODIFIED="$MODIFIED	$AUTH_DIR/$AUTH_SSH2"
		echo done.
	else
		echo failed.
	fi
fi

if [ $SSH1_n -gt 0 ]; then
	echo -n "Adding $SSH1_n SSH1 $SSH1_s... "
	if UploadIds "$SSH1_IDS" "$AUTH_SSH1" "$@"; then
		MODIFIED="$MODIFIED	$AUTH_DIR/$AUTH_SSH1"
		echo done.
	else
		echo failed.
	fi
fi

if [ -n "$MODIFIED" ]; then
	cat <<EOF
Now try logging to the remote host, with "ssh $@", and check in:

$MODIFIED

to make sure we haven't added extra keys that you weren't expecting.

EOF
fi
