#!/bin/bash # # unhttpd: A really dumb web server. :^) # # http://ioioio.net/unhttpd/ # http://ducks.openverse.com/~andy/unhttpd/ # # Copyright (C) 2004 # Andy Goth # # This code is available under the GNU General Public License; see COPYING. # Edit these... prefix=http://localhost # Default URL prefix. root=~/public_html # Where your files are. sp=" " # Space. cr=$'\r' # Carriage return. (HTTP terminates lines with \r\n.) lf=$'\n' # Line feed. tab=$'\t' # Tab. version=unhttpd/0.2 author_name="Andy Goth" author_email=unununium@openverse.com # Beginning of this month last year. rightnow=$(date +%s) lastyear=$(date -d"$(($(date +%Y) - 1))-$(date +%m)-31" +%s) # Read the first line. Example: "GET / HTTP/1.1". IFS=$sp$cr$lf read cmd euri ver [ "$(echo $cmd | tr A-Z a-z)" != get ] && exit 1 # Read the rest of the client request, which is terminated by a blank line. while true; do IFS=$sp$cr$lf read -r key val [ -z $key ] && break key=$(echo $key | tr A-Z a-z) val=$(echo $val | tr -d $cr$lf) case $key in range:) # Client doesn't want the whole file, just a byte range. beg=$(echo $val | awk -F '[=-]' '{print $2}') end=$(echo $val | awk -F '[=-]' '{print $3}') range=1 ;; host:) # Virtual host. :^) prefix=http://$(echo $val | sed 's/:80$//') ;; esac done # $uri = uniform resource indicator; $euri = escaped uri. uri=$(echo $euri | urlesc -r | sed -r 's:(^|/)\.($|/):/:g;s://*:/:g') if echo $uri | egrep -q '(^|/)\.\.($|/)'; then # Crack attempt. file=/tmp/unhttpd.$$.html code="404 file does not exist" echo "nice try, lamer" > $file elif [ -d "$root$uri" ]; then if [ "${uri:(-1)}" != / ]; then # Redirect. file=/tmp/unhttpd.$$.html code="301 moved permanently" redir=$(echo "$prefix$uri/" | urlesc) echo -n "document moved permanently to " > $file echo "here" >> $file else # Attempt to send index.html. uri="$uri"index.html fi fi if [ -z "$code" ]; then if [ -f "$root$uri" ]; then if [ -r "$root$uri" ]; then # Send file. file="$root$uri" code="200 OK" else # Uh oh, forbidden. file=/tmp/unhttpd.$$.html code="403 forbidden" echo "it is forbidden" > $file fi else # File doesn't exist. Try to generate it. dir=$(dirname "$uri") [ "$dir" == . -o "$dir" == / ] && dir= if [ ! -d "$root$dir" ]; then # Bad directory. uri=404 fi case "$uri" in */index.tar|*/index.tar.gz|*/index.tar.bz2) # Directory archive. code="200 OK" # Compression? case "$uri" in *.tar) cmp=""; file=/tmp/unhttpd.$$.tar ;; *.gz ) cmp=z ; file=/tmp/unhtppd.$$.tar.gz ;; *.bz2) cmp=j ; file=/tmp/unhttpd.$$.tar.bz2 ;; esac # Make an archive that extracts into a subdirectory. (cd "$root$dir"; tdir=$(basename "$PWD"); cd ..; tar c"$cmp" "$tdir") > $file ;; */index.txt) # Textual listing. code="200 OK" file=/tmp/unhttpd.$$.txt # List URL's for ordinary files in current directory. (cd "$root$dir" for node in *; do [ -f "$node" ] || continue echo "$prefix$dir/$node" | urlesc done) > $file ;; */index.m3u) # mpegurl listing. code="200 OK" file=/tmp/unhttpd.$$.txt # List URL's for ordinary files in current directory. (cd "$root$dir" echo "#EXTM3U" for node in *; do [ -f "$node" ] || continue case "$node" in *.mp3) mp3info -p'#EXTINF:%S,%a - %t\n' "$node" ;; # *.ogg # # Untested. # ogginfo -v "$node" > /tmp/unhttp.$$.tmp # artist=$(sed "/^${tab}artist=/s/.*=//p" /tmp/unhttp.$$.tmp) # title=$(sed "/^${tab}title=/s/.*=//p" /tmp/unhttp.$$.tmp) # length=$(sed "/^${tab}Playback length: /s/.*: //p" \ # /tmp/unhttp.$$.tmp | awk -FS 'm|:|s' '{print $1 * 60 + $3}') # echo "#EXTINF:$length,$artist - $title" *) echo "#EXTINF:0,$node" ;; esac echo "$prefix$dir/$node"| urlesc done) > $file ;; */index.html|*/index.html?*) # Pretty HTML listing. code="200 OK" file=/tmp/unhttpd.$$.html if echo "$euri" | fgrep -q ?; then query=$(echo "$euri" | sed 's:.*?::') name=$(echo "$query" | sed -r 's:.*name=([^&]*)($|\&).*:\1:') name=$(echo "$name" | urlesc -r) flag=$(echo "$query" | sed -r 's:.*flag=([^&]*)($|\&).*:\1:') flag=$(echo "$flag" | urlesc -r) [ "$flag" != -regex ] && flag=-name files=$(cd "$root$dir"; find . "$flag" "$name" | sed s:..:: | sort) else files=$(cd "$root$dir"; ls | sort) fi (cd "$root$dir" echo "$prefix$dir" path= sdir=$(echo "$dir" | sed 's:/: :g') echo -n "Navigation: " for node in "" $sdir; do path="$path$node/" echo -n "" if [ -z "$node" ]; then echo -n "$(echo "$prefix/" | htmlesc)" else echo -n "$(echo "$node/" | htmlesc)" fi done echo -n "
" if [ ! -z "$query" ]; then echo echo -n "Results: (find " [ "$dir" == "" ] && echo -n "/" || echo -n "$dir" echo -n " $flag \"$name\")" fi echo "
"

            dcount=0
            # XXX: big bug!!
            for node in $files; do
                [ -d "$node" ] || continue

                dtime=$(stat -c%Y "$node")
                [ $dtime -le $lastyear -o \
                  $dtime -gt $rightnow ] && dfmt=" %Y" || dfmt=%R

                echo -n "           $(date -r"$node" +"%b %e $dfmt")  "
                echo -n ""
                echo    "$(echo "$node" | htmlesc)/"

                ((++dcount))
            done

            fsize=0
            fcount=0
            # XXX: big bug!!
            for node in $files; do
                [ -f "$node" ] || continue

                size=$(stat -c%s $node)
                ftime=$(stat -c%Y $node)
                [ $ftime -le $lastyear -o \
                  $ftime -gt $rightnow ] && dfmt=" %Y" || dfmt=%R

                echo -n "$(printf %9d "$size")  "
                echo -n "$(date -r"$node" +"%b %e $dfmt")  "
                echo -n ""
                echo    "$(echo "$node" | htmlesc)"

                ((fsize += $size))
                ((++fcount))
            done

            echo -n "
" if [ ! -z "$query" ]; then echo "Reset search
" fi echo -n "$dcount director" [ $dcount -ne 1 ] && echo -n ies || echo -n y echo -n ", $fcount file"; [ $fcount -ne 1 ] && echo -n s echo -n ", $fsize byte"; [ $fsize -ne 1 ] && echo -n s echo "
" echo -n "Plain-text listing: " echo -n "index.txt " echo "(doesn't include directories)
" echo "Directory archive:" echo "index.tar" echo "index.tar.gz" echo "index.tar.bz2
" echo "
" echo "Find: " echo "" echo "" echo "

" echo -n "$version by $author_name <$author_email>
$(date)
" echo "") > $file ;; */index.htm|*/Default.htm) # Idiot proofing. :^) file=/tmp/unhttpd.$$.html code="301 moved permanently" redir=$(echo "$prefix$dir/index.html" | urlesc) echo -n "grow a clue, then go " > $file echo "here" >> $file ;; 404|*) # Last resort. file=/tmp/unhttpd.$$.html code="404 file does not exist" echo "try harder" > $file ;; esac fi fi # Calculate the content type. case "$file" in *.html) ctype=text/html ;; *.tar ) ctype=application/x-tar ;; *.tgz | *.tar.gz ) ctype=application/x-tgz ;; *.tbz | *.tar.bz2) ctype=application/x-tbz ;; *.zip ) ctype=application/x-zip ;; *.ogg ) ctype=application/ogg ;; *.mp3 ) ctype=audio/mpeg ;; *.gif ) ctype=image/gif ;; *.jpg ) ctype=image/jpeg ;; *.png ) ctype=image/png ;; * ) ctype=text/plain ;; esac # Calculate the file length, content length, and range values. flen=$(stat -c%s "$file") [ -z "$beg" -o ${beg:-0} -lt 0 ] && beg=0 [ -z "$end" -o ${end:-0} -ge $flen ] && end=$(($flen - 1)) clen=$(($end - $beg + 1)) # Finally, send it! echo "HTTP/1.1 $code$cr" echo "Server: $version$cr" [ -z "$redir" ] || echo "Location: $redir$cr" echo "Date: $(date +"%a, %d %b %Y %T %Z")$cr" echo "Connection: close$cr" echo "Content-Type: $ctype$cr" echo "Content-Length: $clen$cr" [ -z $range ] || echo "Content-Range: bytes $beg-$end/$flen$cr" echo "$cr" range $beg $end < "$file" # Clean up the temporary file, if it exists. case "$file" in /tmp/unhttpd.$$.*) rm "$file" ;; esac # vim: set ts=4 sts=4 sw=4 tw=80 et: