#!/bin/sh

##
##        === gosher ===
##
##  gosher is a simple gopher server in a POSIX shell script:
##
##     $ ./gosher [<PORT> [<GOPHERDIR>]
##
##  If PORT is not specified, the default is 70. If GOPHERDIR is not
##  specified, "./" is assumed
##
##
##  (c) 2018 Vincenzo 'KatolaZ' Nicosia <katolaz@freaknet.org>
##
##

######################

##
## If the script is called with basename "gosher", launch the netcat
## server...
##

##
## NETCAT: the netcat command to use, and any additional option
##
### Original netcat
##NETCAT="nc.traditional"
##
### ncat (from nmap)
##NETCAT="ncat"
##
### Openbsd netcat
NETCAT="nc.openbsd"

##
## STYLE: The way in which netcat will talk to gosher_serve
##
### fork with "-c" (Does *not* work with OpenBSD netcat!!!!!)
#STYLE='fork'
##
### use named pipes (Does *not* work with original netcat!!!!!)
STYLE='pipe'

### prefix of the input FIFO
IPREFIX=/tmp/inf_

DEBUG=
#DEBUG=yes


[ -n "$DEBUG" ] && {
	set -e
	set -x
}


## function
cleanup(){
	[ -n "$INF"  ] &&  [ -p "$INF"  ] && rm -f ${INF}
	exit 1
}

MYNAME=$(basename $0)
MYDIR=$(dirname $(realpath $0))
NETCAT=$(which $NETCAT)

if [ -z "${MYNAME#gosher}" ]; then
	## we are called as gosher -- launch the server

	PORT=${1:-70}
	GOPHERDIR=${2:-"./"}
	
	[ ! -f "${NETCAT}" -o ! -x "${NETCAT}" ] && {
		echo "Wrong NETCAT -- Exiting" >&2
		exit 2
	}
	
	if [ -f "${MYDIR}/gosher_serve" -o -h "${MYDIR}/gosher_serve" ]; then
		GOSHER_SERVE="${MYDIR}/gosher_serve"
		trap cleanup 0 HUP INT TRAP TERM QUIT
	
		INF=${IPREFIX}$$
		[ "$STYLE" = "pipe" ] && {
			mkfifo -m 600 $INF
			while [ 1 -eq 1 ]; do
				${GOSHER_SERVE} ${GOPHERDIR} <$INF | ${NETCAT} -vvvvv -l -p ${PORT} >$INF
			done
			rm -f $INF
			exit 0
		}
		[ "$STYLE" = 'fork' ] && {
			while [ 1 -eq 1 ]; do
				${NETCAT} -vv -l -p $PORT -c "${GOSHERSERVE} ${GOPHERDIR}"
			done
			exit 0
		}
		echo "Wrong STYLE specified -- Exiting" >&2
		exit 2
	else
		echo "Cannot find gosher_serve -- Exiting">&2
		exit 3
	fi
fi

######################

##
## ...otherwise, serve a request
##


## function
invalid_selector(){
	sel="$1"
	echo "3Error: Invalid selector: \"$sel\""
	printf ".\r\n"
	exec 1>&-
	exec 2>&-
	exit 1
}

## function
serve_selector(){
	sel="$1"

	cat "${sel}"
	echo "$0: selector $sel served -- exiting " >&2
	exec 1>&-
	exec 2>&-
	exit 0
}


### transform a .gph file into a gophermap
## function
serve_index(){
        IDX=$1
        IFS='
'
        while read line; do
                rline=$(echo $line | sed -r -e 's/\r//g')
                case $rline in
                        '['*)
                                echo $rline | sed -r -e 's/\[//g;s/\]//g;s/\|/\t/g;s/\t//;s/$/\r/g'
                        ;;
                       t*)
                               echo $rline | cut -c 2-
                       ;;
                        *)
                                echo $line
                esac
        done < $IDX
	printf ".\r\n"
	exec 1>&-
	exec 2>&-
	exit 0
}

### Serve an HTML URL through a redirect page
## function
serve_redirect(){
	url=$1
	cat<< EOF
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>gopher redirect</title>
  </head>
  <body>
	Click to be redirected to: <a href="$url">$url</a>
  </body>
EOF
	exec 1>&-
	exec 2>&-
	exit 0

}




GOPHERDIR=${1:-"./"}

read selector

selector=$(echo $selector | sed -r 's:\$.*::g;s:\r::g' )

[ -n "$DEBUG" ] && {
	echo "iGOPHERDIR: ${GOPHERDIR}"
	echo "iselector: \"${selector}\""
}

case $selector in
	URL:*)
		url=$(echo $selector | cut -d ":" -f 2-)
		serve_redirect $url
        ;;
	/?*|"")
		RP1=$(realpath "${GOPHERDIR}"/"${selector}" || "")
		[ $? -eq 0 ] || invalid_selector "$selector"
		RP2=$(realpath "${GOPHERDIR}")"${selector}"
		[ $? -eq 0 ] || invalid_selector "$selector"
		[ -n "$DEBUG" ] && {
			echo "iRP1: ${RP1}"
			echo "iRP2: ${RP2}"
		}
		
		if [ "${RP1}" = "${RP2}" ]; then
			if [ -f "${RP1}" ]; then
				if [ -n "$(echo $RP1 | sed -n '/\.gph$/p')" ]; then 
					serve_index $RP1
				else
					serve_selector "${RP1}"
				fi
			elif [ -d "${RP1}" ]; then
				[ -f "${RP1}/gophermap" ] && serve_selector "${RP1}/gophermap"
				[ -f "${RP1}/index.gph" ] && serve_index "${RP1}/index.gph"
			fi
		fi
		invalid_selector "$selector"
	;;
	*)
		[ -f "${GOPHERDIR}/gophermap" ] && serve_selector "${GOPHERDIR}/gophermap"
		[ -f "${GOPHERDIR}/index.gph" ] && serve_index "${GOPHERDIR}/index.gph"
		echo "got invalid selector: \"$selector\"" >&2
		invalid_selector "/"
	;;
esac

