geminawk/geminawk.sh

149 lines
3.5 KiB
Bash
Executable File

#!/bin/sh
usage() {
printf 'usage: geminawk [-R] [-h hostname ] ' >&2
printf '[-p default-port] [-r max-redirects]\n' >&2
exit 64
}
Rflag=
PORT="${PORT:-1965}"
REDIRECTS="${REDIRECTS:-5}"
while getopts Rh:p:r: opt; do
case "$opt" in
R) Rflag=1;;
h) HOST="$OPTARG";;
p) PORT="$OPTARG";;
r) REDIRECTS="$OPTARG";;
*) usage;;
esac
done
shift $((OPTIND - 1))
# Try the default nc(1) command
if ! [ "$NC" ] && command -v nc >/dev/null &&
nc -c 2>/dev/null | head -n1 | grep -q '^usage:'; then
NC=nc
fi
if ! [ "$NC" ]; then
# Try to find a nc(1) that supports TLS
IFS=:
for dir in ${PATH-/usr/local/bin:/usr/bin:/bin}; do
if [ -e "$dir/nc" ]; then
case "$("$dir/nc" -c 2>&1 | head -n1)" in
usage:*)NC="${NC:-$dir/nc}";;
esac
fi
done
unset IFS
fi
# Default to openssl(1)
NC="${NC:-openssl s_client}"
# openssl(1) needs the `-quiet' option, otherwise
# it mixes its output with the server's response.
case "$NC" in
*s_client*)
NCFLAGS="${NCFLAGS:--quiet -no_check_time}";;
esac
NCFLAGS="${NCFLAGS:--c -Tnoverify -Tnoname}"
geminawk() {
awk -vu="$1" -vR="$Rflag" -vh="$HOST" -vp="$PORT" \
-vr="$REDIRECTS" -vnc="$NC $NCFLAGS" '
function escape(s) {
gsub(/'\''/, "'\''\\'\'''\''", s)
return ("'\''" s "'\''")
}
function uriescape(s, c) {
split(s, uriescape_a, "")
uriescape_cmd = "printf %s " escape(s) \
" | od -An -tx1 | tr a-z A-Z"
uriescape_hex = ""
while (uriescape_cmd | getline uriescape_line)
uriescape_hex = uriescape_hex uriescape_line
close(uriescape_cmd)
sub(/^[[:space:]]+/, "", uriescape_hex)
split(uriescape_hex, uriescape_x, /[[:space:]]+/)
s = ""
for (uriescape_i = 1; uriescape_i in uriescape_a;
uriescape_i++) {
uriescape_c = uriescape_a[uriescape_i]
if (uriescape_c ~ /[-.0-9A-Z_a-z~]/ || \
index(c, uriescape_c) > 0)
s = s uriescape_c
else
s = s "%" uriescape_x[uriescape_i]
}
delete uriescape_a
delete uriescape_x
return (s);
}
function command(u) {
url = u
sub(/^gemini:\/\//, "", url)
sub(/^\/+/, "", url)
host = url
sub(/^[^\/]*@/, "", host) # userinfo
port = host
if (host ~ /^\[/) { # IPv6
sub(/^\[/, "", host)
sub(/^\].*/, "", host)
} else # IPv4/reg-name
sub(/[\/:].*/, "", host)
if (h !~ /./ && host == "")
exit(1)
sub(/^(\[[^\]]*\]|[^:\/]*)/, "", port) # hostname
if (port ~ /^:/) { # port
sub(/^:/, "", port)
sub(/\/.*/, "", port)
if (h ~ /./ && (port + 0 != port ||
port + 0 < 0))
exit 1
} else
port = p
u = R ? u : "gemini://" url
ehost = escape(h ~ /./ ? h : host)
eport = escape(h ~ /./ ? p : port)
cmd0 = "printf '\''%s\\r\\n'\'' " escape(u)
if (nc ~ /s_client/)
cmd1 = nc " -connect " ehost ":" eport
else
cmd1 = nc " -- " ehost " " eport
cmd = cmd0 " | " cmd1 " 2>/dev/null"
return (cmd)
}
BEGIN {
r += 0
for (redir = 0; redir < r; redir++) {
cmd = command(u)
cmd | getline
sub(/\r$/, "")
if ($1 ~ /^1/) { # INPUT
prompt = ""
for (i = 2; i <= NF; i++)
prompt = prompt \
(i > 2 ? " " : "") $i
print(i > 2 ? prompt : "?")
getline
sub(/\?.*/, "", u)
u = u "?" uriescape($0, \
"!$&'\''()*+,/:;=?@")
redir = -1;
} else if ($1 ~ /^2/) { # OK
while (cmd | getline)
print
exit
} else if ($1 ~ /^3/) { # REDIRECT
if (NF > 2)
exit 1
u = $2;
if ($2 ~ /^\/[^\/]/ && h !~ /./)
u = host $2
} else { # Error or unsupported
print
exit 1
}
close(cmd)
}
}'
}
for url in "$@"; do
geminawk "$url"
done