-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

#!/bin/sh
<<\EOF
<!--
  Hey, you found the source code!

  That is a sign you hopefully know better than to blindly run unknown commands
  from the internet and/or are a curious person. We like you already.

  What this command will actually do, is set you up with a free "shell" account
  on one of our servers, that you can use as you see fit. It will also set you
  up with email, IRC, and other things on your shell, so you can keep in touch
  and learn/make awesome things with us.

  Don't trust us? Good! You probably don't know us (yet) so you shouldn't.

  To help address this we GPG sign this shell-setup script so you can verify
  it has not been changed by a third party before you run it by doing:

  > gpg --verify <(curl https://hashbang.sh)

  The safest method of running a script from a source you don't trust is to:


  Download and verify integrity with GnuPG
  > gpg --recv-keys 0xD2C4C74D8FAA96F5
  > curl https://hashbang.sh | gpg -o hashbang.sh

  Note that piping to sh there is dangerous:
  you do not know, before gpg returns,
  whether the signature is valid.


  Inspect source code
  > less hashbang.sh


  Run
  > chmod +x hashbang.sh
  > ./hashbang.sh


  As an advanced alternative you can bypass using this script and hit our user
  provisioning API directly with an SSH Public Key and desired username:

  > curl https://hashbang.sh/server/stats
  { "server.hashbang.sh": { infos ...}, ... }
  > curl -d '{"user":"someuser","key":"'"$(cat ~/.ssh/id_rsa.pub)"'","host":"someHost.hashbang.sh"}' -H 'Content-Type: application/json' https://hashbang.sh/user/create


  We look forward to seeing you on the other side.

  Note: #! has never provided user data to any government agency.
        Pay close attention to the removal of this notice.

  All code licensed via MIT: https://hashbang.sh/LICENSE.md

- -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>&lrm;</title>
    <meta name="viewport" content="initial-scale = 1, maximum-scale=1, user-scalable = 0"/>
    <meta name="apple-mobile-web-app-capable" content="yes"/>
    <meta name="apple-mobile-web-app-status-bar-style" content="black"/>
    <meta name="HandheldFriendly" content="true"/>
    <meta name="MobileOptimized" content="320"/>
    <link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet" type="text/css">
    <link href="/assets/local.css" rel="stylesheet" type="text/css">
  </head>
  <body>
    <script src="/assets/icon.js"></script>
    <div>
      <h1>#!</h1>
      <a href="view-source:https://hashbang.sh">
        <code>sh <(curl hashbang.sh | gpg)</code>
      </a>
    </div>
  </body>
</html>
<!--
EOF
#!/bin/sh
# This script first and foremost attempts to be POSIX compliant.
# Secondly, it attempts to be compatible with as many shell implementations as
# possible to provide an easy gateway for new users.

# If we're using bash, we do this
# shellcheck disable=SC3040,SC3044
if [ -n "$BASH" ]; then
	shopt -s extglob
	set -o posix
	# Bail out if any curl fails
	set -o pipefail
fi

# POSIX doesn't specify mktemp(1).
# This was checked against manpages for:
#  - OpenBSD: http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man1/mktemp.1?query=mktemp&sec=1
#  - FreeBSD: https://www.freebsd.org/cgi/man.cgi?query=mktemp&sektion=1
#  - OS X 10.9: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/mktemp.1.html
#  - Todd Miller's mktemp: http://www.mktemp.org/manual.html
#  - Solaris 10: https://docs.oracle.com/cd/E23823_01/html/816-5165/mktemp-1.html
#  - HP Tru64 UNIX: https://web.archive.org/web/20120117215524/http://h30097.www3.hp.com/docs/base_doc/DOCUMENTATION/V51B_HTML/MAN/MAN1/0251____.HTM
#  - GNU coreutils: https://www.gnu.org/software/coreutils/manual/html_node/mktemp-invocation.html#mktemp-invocation
tmp_hb_dir="$(mktemp -d -t hashbang.XXXXXX)"
trap 'rm -rf -- "${tmp_hb_dir}"' EXIT

# Fetch host data for later.
# If this fails there is no point in proceeding
bail() {
	echo " "
	echo " If you think this is a bug, please report it to ";
	echo " -> https://github.com/hashbang/hashbang.sh/issues/";
	echo " ";
	echo " The installer will not continue from here...";
	echo " ";
	exit 1
}

hbar() {
    printf -- ' %72s\n' ' ' | tr ' ' '-'
}

# checks if command is available
checkutil() {
	printf '%s' " * Checking for $1..."
	if command -v "$1" >/dev/null; then
		printf "ok!\n";
		return 0;
	else
		printf "not found!\n";
		return 1;
	fi
}

# This function can be called with two parameters:
#
# First is obligatory, and is the "question posed".
# For instance, one may ask "is pizza your favorite meal?", to which the
# responder may answer Y (yes) or N (no).
#
# Second parameter is optional, and can be either Y or N.
# The reasoning behind this is to have a default answer to the question,
# resulting in the responder being able to simple press [enter] and skip
# pressing Y or N, giving the default answer instead.
ask() {
	while true; do
		prompt=""
		default=""

		if [ "${2}" = "Y" ]; then
			prompt="Y/n"
			default=Y
		elif [ "${2}" = "N" ]; then
			prompt="y/N"
			default=N
		else
			prompt="y/n"
			default=
		fi

		# Ask the question
		echo " "
		printf "%s [%s] " "$1" "$prompt"
		read -r REPLY

		# Default?
		if [ -z "$REPLY" ]; then
			REPLY=$default
		fi

		# Check if the reply is valid
		case "$REPLY" in
			Y*|y*) return 0 ;;
			N*|n*) return 1 ;;
		esac

	done
	echo " "
}

# generate ssh keypair
makekey() {
	( checkutil ssh-keygen && checkutil ssh ) || bail
	if [ ! -e "$1" ]; then
		if ! ssh-keygen -t rsa -C "#! $username" -f "$1"; then
			echo " Unable to make key with that location"
		else
			chmod go-rw "$1"
			echo " Successfully generated key"
			return
		fi
	else
		if ask " Unable to generate key, do you want to delete the file?" N; then
			if ! rm "$1"; then
				echo " Unable to delete file, resetting"
			else
				echo " File deleted"
				if ! ssh-keygen -t rsa -C "#! $username" -f "$1"; then
					echo " Unable to generate key, resetting"
				fi
			fi
		else
			echo " Unable to make key with that path, resetting"
		fi
	fi
}

clear;
echo "   _  _    __ ";
echo " _| || |_ |  |  Welcome to #!. This network has three rules:";
echo "|_  __  _||  | ";
echo " _| || |_ |  |  1. When people need help, teach. Don't do it for them";
echo "|_  __  _||__|  2. Don't use our resources for closed source projects";
echo "  |_||_|  (__)  3. Be excellent to each other";
echo " ";
echo " We are a diverse community of people who love teaching and learning.";
echo " Putting a #! at the beginning of a \"script\" style program tells a ";
echo " computer that it needs to \"do something\" or \"execute\" this file.";
echo " Likewise, we are a community of people that like to \"do stuff\".";
echo " ";
echo " If you like technology, and you want to learn to write your first";
echo " program, learn to use Linux, or even take on interesting challenges";
echo " with some of the best in the industry, you are in the right place.";
echo " ";
echo " The following will set you up with a \"shell\" account on one of our";
echo " shared systems. From here you can run IRC chat clients to talk to us,";
echo " access to personal file storage and web hosting, and a wide range of";
echo " development tools. ";
echo " ";
echo " Everything should work perfectly, unless it doesn't";
echo " ";
echo " Please report any issues here: ";
echo "   -> https://github.com/hashbang/hashbang.sh/issues/";
echo " ";
printf " If you agree with the above and wish to continue, hit [Enter] ";
read -r _
clear

echo " ";
echo " ";
hbar;
echo " ";

echo " First, your system must be properly configured with the required";
echo " utilities and executables.";
echo " We will perform a short check for those now.";
echo " NOTE: If you see this message, it is likely because something is";
echo " not installed. Check the list below, and install any";
echo " missing applications.";

checkutil expr || bail
checkutil gpg || bail
( checkutil ssh-keygen && checkutil ssh ) || bail
checkutil curl || bail
clear;

echo " ";
echo " ";
hbar;
echo " ";

echo " Checking with servers if new user signup is open";
echo " ";
host_data="${tmp_hb_dir}/server_stats"
curl -sfH 'Accept:text/plain' https://hashbang.sh/server/stats > "$host_data" || bail
echo >> "$host_data"

echo " ";
echo " ";
hbar;
echo " ";

echo " To create your account we first need a username.";
echo " ";
echo " A valid username must:";
echo "  * be between between 1 and 31 characters long";
echo "  * consist of only 0-9 and a-z (lowercase only)";
echo "  * begin with a letter";
echo " ";
echo " Traditional unix usernames are first initial, optional middle initial,";
echo " and the first 6 characters of the last name, but feel free to use ";
echo " whatever you want";
echo " ";

while [ "$username" = "" ]; do
	printf " Username: ";
	read -r input;

	# Keep in sync with the description and the validate_username function in
	#  https://github.com/hashbang/hashbang.sh/blob/master/server.py
	if echo "$input" | grep -E "^[a-z][a-z0-9]{0,30}$" >/dev/null; then
		username="$input"
	else
		echo " ";
		echo " \"$input\" is not a valid username."
		echo " Please read the instructions and try again"
		echo " ";
	fi
done

echo " ";
hbar;
echo " ";
echo " Now we will need an SSH Public Key."
echo " ";
echo " SSH Keys are a type of public/private key system that let you identify";
echo " yourself to systems like this one without ever sending your password ";
echo " over the internet, and thus by nature we won't even know what it is";

for keytype in id_ed25519 id_ecdsa id_rsa id_dsa; do
	if [ -e ~/.ssh/${keytype}.pub ]; then
		if ask " We found a public key in [ ~/.ssh/${keytype}.pub ]. Use this key?" Y; then
			if [ -e ~/.ssh/${keytype} ] || ask " Are you sure? There is no corresponding private key file." N; then
				private_keyfile="${HOME}/.ssh/${keytype}"
				public_key="$(cat ~/.ssh/${keytype}.pub)"
				break
			fi
		fi
	fi
done

if [ -z "$public_key" ]; then
	echo " No SSH key for login to server found, attempting to generate one"
	while true; do
		echo " "
		printf " Path to new or existing connection key (~/.ssh/id_rsa): "
		read -r private_keyfile
		if [ -z "$private_keyfile" ]; then
			private_keyfile="$HOME/.ssh/id_rsa"
		fi
		private_keyfile=$(echo "$private_keyfile" | sed "s@~@$HOME@")
		echo " "
		if [ ! -e "$private_keyfile" ] && [ ! -e "$private_keyfile.pub" ]; then
			if ask " Do you want us to generate a key for you?" Y; then
				if [ -e "$private_keyfile" ]; then
					if ask " File exists: $private_keyfile - delete?" Y; then
						rm "$private_keyfile"
						if [ -e "${private_keyfile}.pub" ]; then
							rm "${private_keyfile}.pub"
						fi
					else
						continue
					fi
				fi
				if makekey "${private_keyfile}"; then
					break
				fi
			fi
		elif [ ! -e "$private_keyfile" ] && [ -e "${private_keyfile}.pub" ]; then
			if ask " Found public keyfile, missing private. Do you wish to continue?" N; then
				echo " Using public key ${private_keyfile}.pub"
				break
			else
				echo " Resetting"
			fi
		elif [ ! -e "${private_keyfile}.pub" ]; then
			echo " Unable to find public key ${private_keyfile}.pub"
		else
			echo " Using public key ${private_keyfile}.pub"
			break
		fi
	done
	public_key=$(cat "${private_keyfile}.pub")
fi

n=0
echo
hbar;
echo
echo " Please choose a server to create your account on."
echo
hbar;
printf -- '  %-1s | %-4s | %-36s | %-8s | %-8s\n' \
	"#" "Host" "Location" "Users" "Latency"
hbar;
while IFS="|" read -r host _ location current_users max_users _; do
	host=$(echo "$host" | cut -d. -f1)
	latency=$(ping -c 1 "${host}.hashbang.sh" | awk -F'=' '/time=[0-9]+/ { print $NF }')
	n=$((n+1))
	printf -- '  %-1s | %-4s | %-36s | %8s | %-8s\n' \
		"$n" \
		"$host" \
		"$location" \
		"$current_users/$max_users" \
		"$latency"
done < "$host_data"
hbar;

echo
while true; do
	printf ' Enter Number 1-%i : ' "$n"
	read -r choice
	number=$(echo "$choice" | awk '/[^0-9]/ { print "no" }')
	if [ "$number" != "no" ] && \
	   [ "$choice" -ge 1 ] && \
	   [ "$choice" -le $n ]; then
		break;
	fi
done
host=$(head -n "$choice" "$host_data" | tail -n1 | cut -d \| -f1)

if [ -n "$public_key" ] && [ -n "$username" ]; then
	echo " ";
	hbar;
	echo " ";
	echo " We are going to create an account with the following information";
	echo
	echo " Username: $username";
	echo " Public Key: ${private_keyfile}.pub";
	echo " Host: $host";
	echo
	if ask " Does this look correct?" Y ; then
		echo
		printf ' Creating your account... '
		format="{\"user\":\"$username\",\"key\":\"$public_key\",\"host\":\"$host\"}"
		headers="${tmp_hb_dir}/create_headers"
		output=$(curl -H "Content-Type: application/json" -d "$format" https://hashbang.sh/user/create -D "$headers" 2>&-)
		status=$(awk 'NR == 1 { print $2; exit }' "$headers")
		if [ "$status" -eq 200 ]; then
			echo " Account Created!"
		else
			echo " Account creation failed: $(echo "$output" | sed -e 's/.*\"message\": \?\"\([^\"]\+\)\".*/\1/')";
			bail
		fi

		if ask " Would you like to add trusted/signed keys for our servers to your .ssh/known_hosts?" Y ; then
			echo " Downloading GPG keys"
			echo " "
			gpg --keyserver keys.gnupg.net \
			    --recv-keys 0xD2C4C74D8FAA96F5
			echo " "
			echo " Downloading key list"
			echo " "

			if ! curl -s 'https://hashbang.sh/static/known_hosts.asc' |
				gpg --decrypt --output "${tmp_hb_dir}/known_hosts"; then
				echo " "
				echo " Unable to verify keys"
				bail
			fi
			cat "${tmp_hb_dir}/known_hosts" >> ~/.ssh/known_hosts
			echo " "
			echo " Keys downloaded and saved"
		fi

		if ask " Would you like an alias (shortcut) added to your .ssh/config?" Y ; then
			printf '\nHost hashbang\n  HostName %s\n  IdentitiesOnly yes\n  User %s\n  IdentityFile %s\n' \
			       "${host}" "$username" "$private_keyfile" \
			>> ~/.ssh/config
			chmod 600 ~/.ssh/config
			echo " You can now connect any time by entering the command:";
			echo " ";
			echo " > ssh hashbang";
		else
			echo " You can now connect any time by entering the command:";
			echo " ";
			echo " > ssh ${username}@${host}";
		fi

	else
		echo " Please re-run script to make corrections.";
		bail
	fi

	if ask " Do you want us to log you in now?" Y; then
		if [ -e "$private_keyfile" ]; then
			ssh -i "$private_keyfile" "${username}@${host}"
		else
			ssh "${username}@${host}"
		fi
	fi
fi

# exit [n]. if [n] is not specified, then exit shall use the return code of the
# last command.
exit 0
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCgAdFiEERWK8VmkyzxwW8tfM0sTHTY+qlvUFAmRYdwkACgkQ0sTHTY+q
lvVswQ//eR6Eh1rFLthPrtLK8lceHlT9dlzuAYq42WE6/QMbfbDRIZ3tmk192Mpu
oCmlQBzpzEb8Y1ROJK2B13r28RqQmF9s72HEeWu7uT6Ezcn8YYg16sQERBETvpUu
uOuAd6ioagnUhVByKbrf3B/6WH+DJ3uVmzOsNqOkNh9ILmGAvjHvYvnl3Kdr7Q6q
DoSaNoSAljMhoeFkJt24ICAHgumVL/Ee1UT3avOqPnpYe4cUmujFCL2UdCV//mRc
VUQSzqhKx6AY1vy42vuKYVenjoOVyZowVkh2oY3ZjwmPbnH4jQ5lWrRPJxXl7UX7
iGPlTAhYy4JXaNFFPlIAtGWwEzdTkTEYcA5q1cbL2D/QvPS5DuCK3TaCZm32Lppg
C8zo4VOX6vzJswhMdi4BOgb6fO4SdpPCJRRtl7ZucxBvG53JkZc6VaQ0osaFgjkQ
mKvfyHIclowUrpCWQ4igtgzPaNg4McOleZlNi6SoIXFMS74F0hYKIhwNBxNOYngk
wXbL24KcSXfxra1DrdD4UfN3GbUNu+YxXMffqEzo2QTI5MS4ExJK+fC76yMlDjM7
/LTH2RBVkX8Vlzyq1zVHTQiOvkXOz7LGFb086Bn+mF8066rBeUYhZdYntefRyePt
r++wVtVorMAfQh+qwe/MJaQJdC9SsknpqVE3RDZFOK9oSlp6ZM0=
=AebV
-----END PGP SIGNATURE-----