You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
393 lines
8.3 KiB
Bash
393 lines
8.3 KiB
Bash
#!/bin/sh
|
|
|
|
set -e
|
|
|
|
# Validate environment
|
|
if ! command -v jq > /dev/null; then
|
|
printf "missing jq command. Please install it via your system's package manager\n"
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v msgbus > /dev/null; then
|
|
printf "missing msgbus command. Use: go install git.mills.io/prologic/msgbus/cmd/msgbus@latest\n"
|
|
exit 1
|
|
fi
|
|
|
|
for cmd in salty salty-keygen; do
|
|
if ! command -v $cmd > /dev/null; then
|
|
printf "missing %s command. Use go install go.mills.io/salty/cmd/$cmd@latest\n" "$cmd"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
if [ -n "$XDG_CONFIG_HOME" ]; then
|
|
data_path="$XDG_CONFIG_HOME/salty"
|
|
else
|
|
data_path="$HOME/.config/salty"
|
|
fi
|
|
|
|
if [ -z "$SALTY_IDENTITY" ]; then
|
|
export SALTY_IDENTITY="$data_path/$USER.key"
|
|
fi
|
|
|
|
clear_line() {
|
|
printf '\r\033[2K'
|
|
}
|
|
|
|
move_cursor_up() {
|
|
printf '\033[1A'
|
|
}
|
|
|
|
get_user () {
|
|
user=$(grep user: "$SALTY_IDENTITY" | awk '{print $3}')
|
|
if [ -z "$user" ]; then
|
|
user="$USER"
|
|
fi
|
|
echo "$user"
|
|
}
|
|
|
|
format_message() {
|
|
me="$1"
|
|
message="$2"
|
|
printf "%s\t(%s)\t%s" "$(date -u +%FT%TZ)" "$me" "$message"
|
|
}
|
|
|
|
stream () {
|
|
if [ -z "$SALTY_IDENTITY" ]; then
|
|
echo "SALTY_IDENTITY not set"
|
|
return 2
|
|
fi
|
|
|
|
sig=$(mktemp /tmp/salty.XXXXXX)
|
|
msg="$(jq -r '.payload' | base64 -d | salty -i "$SALTY_IDENTITY" -d 2> "$sig")"
|
|
|
|
if [ -n "$SALTY_CHATWITH" ]; then
|
|
if ! echo "$msg" | grep -q "\t($SALTY_CHATWITH)" > /dev/null; then
|
|
exit
|
|
fi
|
|
fi
|
|
|
|
echo '\007'
|
|
|
|
if [ -n "$SALTY_CHATKEY" ]; then
|
|
if ! grep -q "$SALTY_CHATKEY" "$sig" > /dev/null; then
|
|
echo "[warning key does not match!] $(cat "$sig") != $SALTY_CHATKEY"
|
|
fi
|
|
fi
|
|
rm "$sig"
|
|
|
|
move_cursor_up
|
|
clear_line
|
|
echo "$msg" # | lextwt-cli -salty | jq -r '[.created,.twter.nick,.text]|@csv'
|
|
printf ">"
|
|
}
|
|
|
|
check_cors() {
|
|
return 0
|
|
if [ $# -lt 1 ]; then
|
|
printf "check_cors takes 1 arugment %d given\n" "$#"
|
|
printf "Try %s check_cors uri\n" "$(basename "$0")"
|
|
return 1
|
|
fi
|
|
|
|
uri="$1"
|
|
|
|
if [ "$(curl -v -o - -X GET "$uri" 2>&1 | grep -c -i -E 'access-control-allow-(headers|origin)')" -lt 2 ]; then
|
|
return 1
|
|
fi
|
|
|
|
if [ "$(curl -v -o - -X OPTIONS "$uri" 2>&1 | grep -c -i -E 'access-control-allow-(headers|origin)')" -lt 2 ]; then
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
lookup () {
|
|
if [ $# -lt 1 ]; then
|
|
printf "lookup takes 1 arugment %d given\n" "$#"
|
|
printf "Try %s lookup nick@domain\n" "$(basename "$0")"
|
|
return 1
|
|
fi
|
|
|
|
user="$1"
|
|
nick="$(echo "$user" | awk -F@ '{ print $1 }')"
|
|
domain="$(echo "$user" | awk -F@ '{ print $2 }')"
|
|
hash="$(printf "%s" "$user" | sha256sum | cut -f 1 -d ' ')"
|
|
|
|
discovery_host="$(dig +short SRV _salty._tcp."$domain" | cut -f 4 -d' ' | sed 's/\.$//')"
|
|
if [ -z "$discovery_host" ]; then
|
|
discovery_host="$domain"
|
|
else
|
|
discovery_host="$(echo "$discovery_host" | sed 's/\.$//')"
|
|
fi
|
|
|
|
info=$(mktemp /tmp/salty.XXXXXX)
|
|
if ! curl -qfsSL "https://$discovery_host/.well-known/salty/${hash}.json" > "$info" 2> /dev/null; then
|
|
if ! curl -qfsSL "https://$discovery_host/.well-known/salty/${nick}.json" > "$info"; then
|
|
rm "$info"
|
|
echo "error: lookup failed"
|
|
return 1
|
|
else
|
|
if ! check_cors "https://$discovery_host/.well-known/salty/${nick}.json"; then
|
|
echo "error: lookup will fail for mobile users due to lack of CORS headers"
|
|
return 1
|
|
fi
|
|
fi
|
|
else
|
|
if ! check_cors "https://$discovery_host/.well-known/salty/${hash}.json"; then
|
|
echo "error: lookup will fail for mobile users due to lack of CORS headers"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
cat "$info"
|
|
rm "$info"
|
|
}
|
|
|
|
readmsgs () {
|
|
topic="$1"
|
|
|
|
salty_json="$(mktemp /tmp/salty.XXXXXX)"
|
|
lookup "$user" > "$salty_json"
|
|
endpoint="$(jq -r '.endpoint' < "$salty_json")"
|
|
key="$(jq -r '.key' < "$salty_json")"
|
|
rm "$salty_json"
|
|
|
|
MSGBUS_URI=$(dirname "$endpoint")
|
|
export MSGBUS_URI
|
|
topic=$(basename "$endpoint")
|
|
|
|
msgbus sub "$topic" "$0"
|
|
}
|
|
|
|
sendmsg () {
|
|
if [ $# -lt 2 ]; then
|
|
printf "sendmsg requires 2 arguments %d provided\n" "$#"
|
|
printf "Try %s send nick@domain message\n" "$(basename "$0")"
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "$SALTY_IDENTITY" ]; then
|
|
echo "SALTY_IDENTITY not set"
|
|
return 2
|
|
fi
|
|
|
|
user="$1"
|
|
message="$2"
|
|
|
|
if [ -z "$message" ]; then
|
|
echo "error: empty message"
|
|
exit 2
|
|
fi
|
|
|
|
salty_json="$(mktemp /tmp/salty.XXXXXX)"
|
|
|
|
lookup "$user" > "$salty_json"
|
|
|
|
endpoint="$(jq -r '.endpoint' < "$salty_json")"
|
|
key="$(jq -r '.key' < "$salty_json")"
|
|
|
|
rm "$salty_json"
|
|
|
|
me="$(get_user)"
|
|
|
|
format_message "$me" "$message" \
|
|
| salty -i "$SALTY_IDENTITY" -r "$key" \
|
|
| curl -fqsS --data-binary @- "$endpoint"
|
|
}
|
|
|
|
chatwith() {
|
|
if [ $# -lt 1 ]; then
|
|
printf "chatwith requires 1 arguments %d provided\n" "$#"
|
|
printf "Try %s chat nick@domain\n" "$(basename "$0")"
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "$SALTY_IDENTITY" ]; then
|
|
echo "SALTY_IDENTITY not set"
|
|
return 2
|
|
fi
|
|
|
|
user="$1"
|
|
|
|
salty_json="$(mktemp /tmp/salty.XXXXXX)"
|
|
|
|
lookup "$user" > "$salty_json"
|
|
|
|
endpoint="$(jq -r '.endpoint' < "$salty_json")"
|
|
key="$(jq -r '.key' < "$salty_json")"
|
|
|
|
rm "$salty_json"
|
|
|
|
me="$(get_user)"
|
|
nick="$(echo "$me" | awk -F@ '{ print $1 }')"
|
|
domain="$(echo "$me" | awk -F@ '{ print $2 }')"
|
|
|
|
export SALTY_CHATWITH="$user"
|
|
export SALTY_CHATKEY="$key"
|
|
echo chat with "$SALTY_CHATWITH" via "$endpoint" key "$key"
|
|
|
|
readmsgs &
|
|
READ_PID=$!
|
|
trap 'kill $READ_PID' EXIT
|
|
|
|
while true; do
|
|
printf "> "
|
|
read -r message
|
|
clear_line
|
|
if [ -z "$message" ]; then
|
|
continue
|
|
fi
|
|
|
|
format_message "$me" "$message" \
|
|
| salty -i "$SALTY_IDENTITY" -r "$key" \
|
|
| curl -fqsS --data-binary @- "$endpoint"
|
|
|
|
move_cursor_up
|
|
clear_line
|
|
|
|
format_message "$me" "$message"
|
|
echo
|
|
done
|
|
}
|
|
|
|
make_user () {
|
|
mkdir -p "$data_path"
|
|
|
|
if [ $# -lt 1 ]; then
|
|
user=$USER
|
|
else
|
|
user=$1
|
|
fi
|
|
|
|
identity_file="$data_path/$user.key"
|
|
|
|
if [ -f "$identity_file" ]; then
|
|
printf "user key already exists!"
|
|
return 1
|
|
fi
|
|
|
|
# Check for msgbus env.. probably can make it fallback to looking for a config file?
|
|
if [ -z "$MSGBUS_URI" ]; then
|
|
printf "missing MSGBUS_URI in environment"
|
|
return 1
|
|
fi
|
|
|
|
salty-keygen -o "$identity_file"
|
|
echo "# user: $user" >> "$identity_file"
|
|
|
|
pubkey=$(grep key: "$identity_file" | awk '{print $4}')
|
|
hash="$(printf "%s" "$user" | sha256sum | cut -f 1 -d ' ')"
|
|
cat <<- EOF
|
|
Create this file in your webserver well-known folder. https://hostname.tld/.well-known/salty/$hash.json
|
|
|
|
{
|
|
"endpoint": "$MSGBUS_URI/$user",
|
|
"key": "$pubkey"
|
|
}
|
|
|
|
EOF
|
|
}
|
|
|
|
register () {
|
|
mkdir -p "$data_path"
|
|
|
|
if [ $# -lt 1 ]; then
|
|
printf "usage: %s register nick@domain\n" "$0"
|
|
exit 1
|
|
else
|
|
user=$1
|
|
fi
|
|
|
|
nick="$(echo "$user" | awk -F@ '{ print $1 }')"
|
|
domain="$(echo "$user" | awk -F@ '{ print $2 }')"
|
|
|
|
discovery_host="$(dig +short SRV _salty._tcp."$domain" | cut -f 4 -d' ' | sed 's/\.$//')"
|
|
if [ -z "$discovery_host" ]; then
|
|
echo "register is only supported with a saltyd service"
|
|
exit 1
|
|
fi
|
|
|
|
identity_file="$data_path/$nick.key"
|
|
|
|
if [ -f "$identity_file" ]; then
|
|
echo "user key already exists!"
|
|
return 1
|
|
fi
|
|
|
|
salty-keygen -o "$identity_file"
|
|
echo "# user: $user" >> "$identity_file"
|
|
|
|
pubkey=$(grep key: "$identity_file" | awk '{print $4}')
|
|
|
|
export SALTY_IDENTITY="$identity_file"
|
|
msgbus -u "https://$discovery_host/inbox" sub "$pubkey" "$0" &
|
|
pid="$!"
|
|
|
|
sendmsg "salty@$domain" REGISTER
|
|
sleep 1
|
|
kill "$pid"
|
|
}
|
|
|
|
show_help() {
|
|
printf "Usage: %s [options] <command> [arguments]\n" "$(basename "$0")"
|
|
printf "\n"
|
|
printf "Options:\n"
|
|
printf " -h/--help Show this help"
|
|
printf "\n"
|
|
printf "\n"
|
|
printf "Commands:\n"
|
|
printf " help -- Display this help message\n"
|
|
printf " chat -- Chat with a user by nick@domain\n"
|
|
printf " lookup -- Lookup a user by nick@domain\n"
|
|
printf " check-cors -- Perform a CORS check on a uri\n"
|
|
printf " make-user -- Generate a new user key pair\n"
|
|
printf " read -- Reads your messages\n"
|
|
printf " send -- Sends a message to nick@domain\n"
|
|
printf " register -- Sends a register message to a broker bot\n"
|
|
}
|
|
|
|
# check if streaming
|
|
if [ ! -t 1 ]; then
|
|
stream
|
|
exit 0
|
|
fi
|
|
|
|
# Show Help
|
|
if [ $# -lt 1 ]; then
|
|
show_help
|
|
exit 1
|
|
fi
|
|
|
|
|
|
CMD=$1
|
|
shift
|
|
|
|
case $CMD in
|
|
-h|--help|help)
|
|
show_help
|
|
;;
|
|
chat)
|
|
chatwith "$@"
|
|
;;
|
|
send)
|
|
sendmsg "$@"
|
|
;;
|
|
read)
|
|
readmsgs "$@"
|
|
;;
|
|
lookup)
|
|
lookup "$@"
|
|
;;
|
|
check-cors)
|
|
check_cors "@"
|
|
;;
|
|
make-user)
|
|
make_user "$@"
|
|
;;
|
|
register)
|
|
register "$@"
|
|
;;
|
|
esac
|