repo: shell-daemons action: blob revision: path_from: geminid revision_from: refs/heads/master: path_to: revision_to:
blob of:
/ geminid
refs/heads/master:/geminid
#!/bin/bash
# This work is marked with CC0 1.0. To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0
if [ "$1" = "" ];then
export SCHEME="gemini"
export SERVER_SOFTWARE="epoch-geminid/0.1"
export SERVER_PROTOCOL="gemini/0.1"
export LANG=C.UTF-8
if ! read -rt 10 REQUEST_URI;then
logger -t geminid -p crit "request from ${REMOTE_ADDR} timed out after 10 seconds. bailing."
exit 0
fi
REQUEST_METHOD="$(cut '-d ' -f1 <<< "$REQUEST_URI")"
if [ "${REQUEST_METHOD}" = "GET" ];then
printf 'HTTP/1.1 418 I am a teapot\r\n'
printf 'Content-Type: text/plain\r\n\r\n'
printf 'did you even look at the port? 1965. this is a gemini server. not an HTTP server.\n'
printf 'I suggest you fuck off.'
exit 0
fi
export REQUEST_URI=$(printf "%s\n" "${REQUEST_URI}" | tr -d '\r\n')
export REQUEST_DOMAIN="$(printf "%s\n" "${REQUEST_URI}" | uricut -d | tr 'A-Z' 'a-z')"
if [ ! "$REQUEST_DOMAIN" ];then
REQUEST_DOMAIN=gemini.thebackupbox.net
fi
export REQUEST_PORT="$(printf "%s\n" "${REQUEST_URI}" | uricut -P)"
if [ ! "$REQUEST_PORT" ];then
REQUEST_PORT="${SERVER_PORT}"
fi
export REQUEST_SCHEME="$(printf "%s\n" "${REQUEST_URI}" | uricut -s)"
if [ "$REQUEST_URI" = "" ];then
logger -t geminid -p crit "$REMOTE_ADDR is sending an empty request. gonna just bail on this."
exit 0
fi
logger -t geminid -p warn "request: $REQUEST_URI from: $REMOTE_ADDR"
if ! printf "%s\n" "$REQUEST_URI" | urimatch nf 2>&1 >/dev/null;then
logger -t geminid "someone requested with fragment!"
export REQUEST_URI_ORIGINAL="$REQUEST_URI"
export REQUEST_URI="$(printf "%s\n" "$REQUEST_URI" | cut '-d#' -f1)"
fi
# if we have SNI, we use that for SERVER_NAME,
# otherwise we use what was in the gemini request.
export SSL_TLS_SNI="$(printf "%s\n" "${SSL_TLS_SNI}" | tr 'A-Z' 'a-z')"
if [ ! "${SSL_TLS_SNI}" ];then
export SERVER_NAME="${SSL_TLS_SNI}"
else
export SERVER_NAME="${REQUEST_DOMAIN}"
fi
if [ "$SERVER_NAME" = "" ];then
export SERVER_NAME=gemini.thebackupbox.net
fi
#export IDENT="$(printf "$REMOTE_PORT, $SERVER_PORT\r\n" | nc $REMOTE_ADDR 113)"
#export IDENT="$(ident $SERVER_ADDR $REMOTE_ADDR 113 $REMOTE_PORT $SERVER_PORT)"
# usage: ident.sh our-host their-host 113 their-port our-port
## not sure exactly what this should contain.
if [ ! "${REQUEST_PORT}" ];then
if [ "${REQUEST_SCHEME}" = "gemini" ];then
export REQUEST_PORT=1965
fi
fi
if [ ! "${SERVER_PORT}" ];then ### this should get overridden by whatever runs this script.
export SERVER_PORT=1965
fi
REQUEST_DP="${REQUEST_DOMAIN}:${REQUEST_PORT}"
SERVER_DP="${SSL_TLS_SNI}:${SERVER_PORT}"
if [ "$SSL_TLS_SNI" = "" ];then
SERVER_DP="gemini.thebackupbox.net:1965"
fi
if [ "${REQUEST_DP}" != "${SERVER_DP}" ];then
if [ "${SSL_TLS_SNI}" = "epo.k.vu" ];then
uristart "${REQUEST_URI}" 2>/dev/null
exit 1
fi
printf "53 PROXY REQUEST REFUSED. requested (%s) != sni (%s)\r\n" "${REQUEST_DP}" "${SERVER_DP}"
exit 1
fi
export REQUEST_PATH="/$(printf "%s\n" "${REQUEST_URI}" | uricut -p)"
### need to preserve trailing / through normalpath
trailing_slash="$(printf "%s\n" "$REQUEST_PATH" | rev | cut -b1 | tr -cd '/')"
export SCRIPT_NAME="$(normalpath "/$(uriunescape "$REQUEST_PATH")")${trailing_slash}"
if [ "$SCRIPT_NAME" = "/~/" ];then
printf "20 text/gemini\r\n"
printf "# users with gemini sites here:\n"
ls -d /home/*/public_gemini | cut -d/ -f3 | sed 's|^\(.*\)$|=> /~\1/ \1|g'
exit 0
fi
###
if grep '^/~' <<< "$SCRIPT_NAME" 2>&1 >/dev/null ;then
export user="$(printf "%s\n" "$SCRIPT_NAME" | cut -d/ -f2 | tr -cd 'a-zA-Z0-9')"
if ! cd "/$(getent passwd "$user" | cut -d: -f6)/public_gemini";then
printf '59 BAD REQUEST (guru meditation: 1) "%s"\r\n' "$(getent passwd "$user" | cut -d: -f6)"
exit 1
fi
export DROP_TO="$user"
### export SCRIPT_NAME="$(printf "%s\n" "$SCRIPT_NAME" | cut -d/ -f3-)"
export SCRIPT_RELATIVE_FILENAME="$(printf "%s\n" "$SCRIPT_NAME" | cut -d/ -f3-)"
else
unset user
export DROP_TO="gemini"
cd "/var/gemini/vhosts/${SERVER_NAME}" 2>/dev/null || cd "/var/gemini/sites/default"
export SCRIPT_RELATIVE_FILENAME="$(printf "%s\n" "$SCRIPT_NAME" | cut -d/ -f2-)"
fi
export DOCUMENT_ROOT="$(pwd | sed 's|///*|/|g')"
exec supersu "$(id -u "${DROP_TO}")" "$(id -g "${DROP_TO}")" "$(id -g "${DROP_TO}")" "$0" stage2
elif [ "$1" = "stage2" ];then
### TODO: if I'm going to guess file extensions I need to take those into account.
trailing_slash="$(printf "%s\n" "$SCRIPT_RELATIVE_FILENAME" | rev | cut -b1 | tr -cd '/')"
if [ "$(path_info_find "${DOCUMENT_ROOT}/${SCRIPT_RELATIVE_FILENAME}")" ];then
export PATH_INFO="$(path_info_find "${DOCUMENT_ROOT}/${SCRIPT_RELATIVE_FILENAME}" | tail -n1)"
export SCRIPT_FILENAME="$(normalpath "/$(path_info_find "${DOCUMENT_ROOT}/${SCRIPT_RELATIVE_FILENAME}" | head -n1)")${trailing_slash}"
else
export PATH_INFO=""
export SCRIPT_FILENAME="$(normalpath "/${DOCUMENT_ROOT}/${SCRIPT_RELATIVE_FILENAME}")${trailing_slash}"
fi
# SCRIPT_FILENAME="$(normalpath "/${DOCUMENT_ROOT}/$SCRIPT_FILENAME")"
if ! printf "%s\n" "${SCRIPT_FILENAME}" | grep "^${DOCUMENT_ROOT}" >/dev/null 2>&1;then
printf '59 BAD REQUEST (guru meditation: 2) "%s" not in "%s"\r\n' "${SCRIPT_FILENAME}" "${DOCUMENT_ROOT}"
exit 1
fi
### response code then mime-type
### why is script_name and path separate?
### this needs to have the PATH_INFO stripped off. but it isn't used in geminid anyway, so fuck it for now.
export QUERY_STRING="$(printf "%s\n" "$REQUEST_URI" | uricut -q)"
export SCRIPT_FILENAME
#printf "20 text/plain\r\n"
#env
if [ -e "$SCRIPT_FILENAME" ];then #this will likely be always true because of how path info finder works.
CONTENT_TYPE="$(mime-type "$SCRIPT_FILENAME")"
case "$CONTENT_TYPE" in
inode/directory)
if [ ! "$PATH_INFO" ];then
if ! printf "%s\n" "$SCRIPT_NAME" | grep '/$' 2>&1 >/dev/null;then
printf "31 %s/\r\n" "$SCRIPT_NAME" ### SCRIPT_NAME should not be used this way.
exit 0
fi
fi
if [ -f $SCRIPT_FILENAME/.redirect ];then
printf "30 %s\r\n" "$(cat "$SCRIPT_FILENAME/.redirect")"
exit 0
fi
if [ -x "$SCRIPT_FILENAME/index.cgi" ];then
# we don't need to come back from here.
cd "$SCRIPT_FILENAME"
exec "$SCRIPT_FILENAME/index.cgi" | sed 's/\([^\r]\)$/\1\r/g' #only put the \r if it doesn't happen already
exit 0
fi
if [ -f "${SCRIPT_FILENAME}/index.php" ];then
cd "${SCRIPT_FILENAME}"
exec php "${SCRIPT_FILENAME}/index.php"
exit 0
fi
# printf "%s\n%s\n" "$REQUEST_DOMAIN" "$SSL_TLS_SNI"
# printf "SCRIPT_FILENAME: %s\n" "$SCRIPT_FILENAME"
if [ -f "$SCRIPT_FILENAME/index.gmi" ];then
if [ "$PATH_INFO" ];then
printf "51 PATH_INFO is set, but we didn't find a CGI to pass it to.\r\n"
exit 1
fi
printf "20 text/gemini\r\n"
cat "$SCRIPT_FILENAME/index.gmi" | sed 's/\([^\r]\)$/\1\r/g'
exit 0
fi
if [ -f "$SCRIPT_FILENAME/index.♊︎" ];then
if [ "$PATH_INFO" ];then
printf "51 PATH_INFO is set, but we didn't find a CGI to pass it to.\r\n"
exit 1
fi
printf "20 text/gemini\r\n"
cat "$SCRIPT_FILENAME/index.♊︎" | sed 's/\([^\r]\)$/\1\r/g'
exit 0
fi
printf "20 text/gemini\r\n"
#if [ -f "$SCRIPT_FILENAME/.autogen" ];then #why did I require this to be a thing?
printf "## autogenerated list of stuff in %s\r\n" "$SCRIPT_NAME"
printf "=> ..\r\n"
if [ "$user" ];then
find -H "$SCRIPT_FILENAME" -not -path '*/\.*' -maxdepth 1 -type d -print0 | xargs -r0 basename -zas .gmi | xargs -r0 printf "=> %s#dir\r\n" | tail -n+2 #skip self
find -H "$SCRIPT_FILENAME" -not -path '*/\.*' -maxdepth 1 -type f -print0 | xargs -r0 basename -zas .gmi | xargs -r0 printf "=> %s#file\r\n"
find -H "$SCRIPT_FILENAME" -not -path '*/\.*' -maxdepth 1 -type l -print0 | xargs -r0 basename -zas .gmi | xargs -r0 printf "=> %s#link\r\n"
# find -H "$SCRIPT_FILENAME" -not -path '*/\.*' -maxdepth 1 -type f | cut -d/ -f5- | grep . | cut -d. -f1 | tr '\n' '\0' | xargs -r0 printf "=> /~${user}/%s#file\r\n"
# find -H "$SCRIPT_FILENAME" -not -path '*/\.*' -maxdepth 1 -type l | cut -d/ -f5- | grep . | cut -d. -f1 | tr '\n' '\0' | xargs -r0 printf "=> /~${user}/%s#link\r\n"
else
find -H "$SCRIPT_FILENAME" -not -path '*/\.*' -maxdepth 1 -type d | cut -d/ -f6- | grep . | cut -d. -f1 | tr '\n' '\0' | xargs -r0 printf '=> /%s#dir\r\n' | tail -n+2 #skip self
find -H "$SCRIPT_FILENAME" -not -path '*/\.*' -maxdepth 1 -type f | cut -d/ -f6- | grep . | cut -d. -f1 | tr '\n' '\0' | xargs -r0 printf '=> /%s#file\r\n'
find -H "$SCRIPT_FILENAME" -not -path '*/\.*' -maxdepth 1 -type l | cut -d/ -f6- | grep . | cut -d. -f1 | tr '\n' '\0' | xargs -r0 printf '=> /%s#link\r\n'
fi
exit 0
#fi
#printf '51 %s Not Found I guess\r\n' "$SCRIPT_FILENAME"
#logger -t geminid 'not found: '"$REQUEST_URI"
# printf "wtf, george?\r\n"
# env
;;
*)
extension="$(rev <<< "${SCRIPT_FILENAME}" | cut -d. -f1 | rev)"
if [ "$extension" = "php" ];then #lol. who needs +x anyway? :D
cd "$(dirname "$SCRIPT_FILENAME")"
exec php "${SCRIPT_FILENAME}"
exit 0
fi
if [ -x "$SCRIPT_FILENAME" ];then
cd "$(dirname "$SCRIPT_FILENAME")"
exec "$SCRIPT_FILENAME"
exit 0
else
if [ "$PATH_INFO" ];then
printf "51 PATH_INFO is set, but we didn't find a CGI to pass it to.\r\n"
exit 1
fi
printf "20 %s\r\n" "$CONTENT_TYPE"
cat -- "$SCRIPT_FILENAME"
fi
;;
esac
else
logger -t geminid "HOW ARE WE GETTING HERE?!?"
if [ -x "$SCRIPT_FILENAME.cgi" ];then
if [ "$user" ];then
printf "47 CGIs for users not implemented yet\r\n" # wat?
else
cd "$(dirname "$SCRIPT_FILENAME")"
exec "$SCRIPT_FILENAME.cgi"
fi
fi
if [ -e "$SCRIPT_FILENAME.php" ];then
cd "$(dirname "${SCRIPT_FILENAME}")"
exec php "${SCRIPT_FILENAME}.php"
fi
if [ -e "${SCRIPT_FILENAME}.♊︎" ];then
if [ "$PATH_INFO" ];then
printf "51 PATH_INFO is set, but we didn't find a CGI to pass it to.\r\n"
exit 1
fi
printf "20 text/gemini\r\n"
printf "# TESTING 1\r\n"
cat "${SCRIPT_FILENAME}.♊︎" | sed 's/\([^\r]\)$/\1\r/g'
exit 1
fi
if [ -e "${SCRIPT_FILENAME}.gmi" ];then
if [ "$PATH_INFO" ];then
printf "51 PATH_INFO is set, but we didn't find a CGI to pass it to.\r\n"
exit 1
fi
printf "20 text/gemini\r\n"
printf "# TESTING 2\r\n"
cat "${SCRIPT_FILENAME}.gmi" | sed 's/\([^\r]\)$/\1\r/g'
exit 1
fi
if [ -x "${DOCUMENT_ROOT}/.rewrite" ];then
"${DOCUMENT_ROOT}/.rewrite"
else
printf "20 text/plain\r\nI don't know what you mean. %s\r\n" "$SCRIPT_FILENAME"
printf "%s\n" "$(date)"
env | sort
fi
fi
fi