#!/bin/ksh
# @(#) webpage.ksh 2.1 97/01/21
# 94/11/16 john h. dubois iii (john@armory.com)
# 96/01/08 Print a slightly more useful message if a non-option is entered.
# 96/11/07 2.0 Added all options.
# 96/11/29 2.1 Added CheckPerms and FixPerms

### Start of kstat lib
# @(#) kstat.ksh 1.0 96/11/29
# 96/11/29 john h. dubois iii (john@armory.com)

# Make these print in octal
typeset -i8  S_IFMT=8#0170000	# File type bitmask
typeset -i8 S_IRWXU=8#0000700	# Owner perms bitmask
typeset -i8 S_IRWXG=8#0000070	# Group perms bitmask
typeset -i8 S_IRWXO=8#0000007	# Other perms bitmask
typeset -i8 S_IFREG=8#0100000	# Reg file
typeset -i8 S_IFBLK=8#0060000	# Block file
typeset -i8 S_IFDIR=8#0040000	# Directory
typeset -i8 S_IFCHR=8#0020000	# Char file
typeset -i8 S_IFIFO=8#0010000	# Named pipe
typeset -i8 S_IFNAM=8#0050000	# Special named file
typeset -i8 S_IFLNK=8#0120000	# Symlink
typeset -i8 S_ISUID=8#0004000	# setuid
typeset -i8 S_ISGID=8#0002000	# setgid
typeset -i8 S_ISVTX=8#0001000	# sticky bit
typeset -i8 S_IRUSR=8#0000400	# owner read
typeset -i8 S_IWUSR=8#0000200	# owner write
typeset -i8 S_IXUSR=8#0000100	# owner execute
typeset -i8 S_IRGRP=8#0000040	# group read
typeset -i8 S_IWGRP=8#0000020	# group write
typeset -i8 S_IXGRP=8#0000010	# group execute
typeset -i8 S_IROTH=8#0000004	# other read
typeset -i8 S_IWOTH=8#0000002	# other write
typeset -i8 S_IXOTH=8#0000001	# other execute

typeset -i8 ls2mode
# Usage: ls2mode ls-mode-field
# Returns the file mode in ls2mode
function ls2mode {
    typeset perm nperm comp name=${0##*/}
    typeset -i ptype ret

    typeset perm=$1

    nperm=${perm#?}
    case "${perm%$nperm}" in
    -) ret=S_IFREG;;
    d) ret=S_IFDIR;;
    l) ret=S_IFLNK;;
    b) ret=S_IFBLK;;
    c) ret=S_IFCHR;;
    p) ret=S_IFIFO;;
    s) ret=S_IFNAM;;
    m) ret=S_IFNAM;;
    *) print -ru2 -- "$name: unknown type character: ${perm%$nperm}"
	return 2;;
    esac
    perm=$nperm

    for ptype in 6 3 0; do		# get perms for user, group and other
	for wperm in r w xsStT; do	# strip off each mode letter
	    nperm=${perm#?}
	    # get next component of perms
	    comp=${perm%$nperm}
	    # Make sure each mode bit has one of the expected values for it
	    if [[ "$comp" != [-$wperm] ]]; then
		print -u2 "$name: unrecognized permission character: $comp"
		return 3
	    fi
	    case $comp in
	    -) ;;
	    r) ret='ret|4<<ptype';;
	    w) ret='ret|2<<ptype';;
	    [xst]) ret='ret|1<<ptype';;
	    esac
	    case $comp in
	    [sS]) [ ptype -eq 3 ] && ret='ret|S_ISGID' ||
		  ret='ret|S_ISUID'
		;;
	    [tT]) ret='ret|S_ISVTX';;
	    esac
	    perm=$nperm
	done
    done
    ls2mode=ret
}

typeset -i8 kstat_mode
typeset -i kstat_nlink kstat_uid kstat_gid kstat_major kstat_minor kstat_size

# Usage: kstat file ...
function kstat {
    typeset file line IFS=" "
    typeset -i n=0

    ls -lLnd "$@" | while read line; do
	ls2mode $line
	kstat_mode[n]=ls2mode
	set -- $line
	kstat_nlink[n]=$2
	# store uid & gid because username and groupname may be ambiguous
	kstat_uid[n]=$3
	kstat_gid[n]=$4
	case "$5" in
	*,?*)
	    kstat_major[n]=${5%,*}
	    kstat_minor[n]=${5#*,}
	    kstat_size[n]=-1
	    ;;
	*,)
	    kstat_major[n]=${5%,}
	    kstat_minor[n]=$6
	    kstat_size[n]=-1
	    shift
	    ;;
	*)
	    kstat_major[n]=-1
	    kstat_minor[n]=-1
	    kstat_size[n]=$5
	    ;;
	esac
	kstat_date[n]="$6 $7 $8"
	shift 8
	kstat_name[n]="$*"
	let n+=1
    done
    [ n -gt 0 ] && return 0 || return 1
}
### End of kstat lib

function CheckPerms {
    typeset home=$1 pageDir=$2 homePage=$3
    typeset fail=false badFiles badDirs
    typeset -i OXR="S_IXOTH|S_IROTH"

    kstat "$home" || return 1
    if ((!(kstat_mode&S_IXOTH))); then
	print -r \
"Your home directory must be made executable by \"other\" in order for the
web server to access it.  Use the 'Fix permissions' option, or the command:
chmod o+x \$HOME
"
	fail=true
    fi
    kstat "$pageDir" || return 1
    # require web dir to be publicly readable too because some web servers
    # require that in order to locate default file if /~user/ is given.
    if [ "kstat_mode&OXR" -ne OXR ]; then
	print -r \
"Your web directory ($pageDir) must be made
executable & readable by \"other\" in order for the web server to access it.  
Use the 'Fix permissions' option, or use the command:
chmod o+rx $pageDir
"
	fail=true
    fi
    kstat "$homePage" || return 1
    if ((!(kstat_mode&S_IROTH))); then
	print -r \
"Your home page must be made readable by \"other\" in order for the
web server to access it.  Use the 'Fix permissions' option, or the command:
chmod o+r $homePage
"
	fail=true
    fi
    cd "$pageDir" || return 1
    badFiles=$(find . -type f ! -perm -004 -print)
    if [ -n "$badFiles" ]; then
	print -r \
"Some files in your web directory ($pageDir)
are not readable by the web server.  Any that are intended to be web-served
should be made publicly readable with the command: chmod o+r filename
where filename is the name of the file to be made publicly readable.
Alternately, the 'Fix permissions' option may be used; this will make all
files in your web directory publicly readable.  The names of the files 
(relative to your web directory) that may need to be modified are:
$badFiles
"
	fail=true
    fi
    badDirs=$(find . -type d ! -perm -001 -print)
    if [ -n "$badFiles" ]; then
	print -r \
"Some directories under your web directory ($pageDir)
are not executable by the web server.  Any that contain files that are intended
to be web-served should be made publicly executable with the command:
chmod o+x dir-name-name
where dir-name is the name of the directory to be made publicly executable. 
Alternately, the 'Fix permissions' option may be used; this will make all
directories under your web directory publicly readable.  The names of the
directories (relative to your web directory) that may need to be modified are:
$badDirs
"
	fail=true
    fi
    $fail || print -r "The permissions on your web-served files are correct."
}

function FixPerms {
    typeset home=$1 pageDir=$2 homePage=$3
    typeset -i OXR="S_IXOTH|S_IROTH"

    print -nr \
"This will make your web directory ($pageDir)
publicly readable and executable, your home directory and all directories under
your web directory publicly executable, and all regular files in your web
directory publicly readable.  Are you sure you want to do this? [y/n]: "
    read response
    if [[ "$response" != [yY]* ]]; then
	print -r "Permissions fix aborted."
	return
    fi
    print -r "Fixing permissions (names of modified files follow)..."
    # Check whether home & page dir really need to be modified so that an
    # accurate list of modified filenames can be printed.
    kstat "$home" || return 1
    if ((!(kstat_mode&S_IXOTH))); then
	print -r "$home"
	chmod a+x "$home"
    fi
    kstat "$pageDir" || return 1
    if [ "kstat_mode&OXR" -ne OXR ]; then
	print -r "$pageDir"
	chmod a+rx "$pageDir"
    fi
    cd "$pageDir" || return 1
    find . -type f ! -perm -004 -print -exec chmod a+r {} \;
    find . -type d ! -perm -001 -print -exec chmod a+x {} \;
    print "Done.\n"
}

function MakePage {
    print -u2 "Making prototype web page..."

    set -o noclobber	# just to be sure we don't overwrite anything

    if [ ! -d $HomePageDir ]; then
	mkdir $HomePageDir || {
	    print -u2 "Could not make a directory for your web page."
	    exit 1
	}
    fi

    # Make the possessive form of NAME
    [[ "$NAME" = *s ]] && PSName="$NAME'" || PSName="$NAME's"
    # Create prototype user page, substituting in vars
    if [ -f "$PROTOPAGE" -a -r "$PROTOPAGE" ]; then
	cat $PROTOPAGE
    else
	print -r -- "$defPage"
    fi | sed "
s@%PSNAME%@$PSName@
s@%NAME%@$NAME@
s@%USER%@$USER@
s@%URL%@$URL@
s@%MAILNAME%@$MAILNAME@
" > $HomePage

    chmod a+rx $Home $HomePageDir
    chmod a+r $HomePage

    print \
"A prototype home page has been created for you.  It is in the file:
$HomePage
You can edit the file and replace its contents with material of your own
creation.  The URL for your web page is:
$URL
You can get information on HTML, URLs, etc. by selecting the 'Help' option
within your web browser.
"
}

### Start of main program
# 95/12/23 john h. dubois iii (john@armory.com)
# Usage: host2addr <hostname>
# Sets global host2addr to first ip address associated with hostname
# Return value: 0 on success, 1 on failure
function host2addr {
    typeset hostname=$1 name t ip

    if [[ "$hostname" = +([0-9])+(.+([0-9])) ]]; then
	host2addr=$hostname
    else
	/etc/dig +pfset=0x2020 "$hostname" | while read name t ip; do
	    # Ignore CNAMEs and such.  dig will automatically look up the A
	    # record for the name the CNAME points to.
	    [ "$t" = A ] && break
	done
	[[ "$ip" != +([0-9])+(.+([0-9])) ]] && return 1
	host2addr=$ip
    fi
    return 0
}

# Usage: addr2host <addr>
# Sets global addr2host to first hostname associated with addr
# Return value: 0 on success, 1 on failure
function addr2host {
    typeset addr=$1 name t ip

    if [[ $# -eq 0 || "$1" != +([0-9.]) ]]; then
	addr2host=
	return 1
    fi
    /etc/dig +pfset=0x2020 -x "$addr" | while read ip t name; do
	[ "$t" = PTR ] && break
    done
    [[ -z "$name" ]] && return 1
    addr2host=$name
    return 0
}

l_name=${0##*/}
Usage=\
"Usage: $l_name [-h] [-e<editor>] [-g<graphical-browser>] [-t<text-browser>]
       [-w<www-name>] [-m<mailname>] [-i<index-page>] [-d<homepage-directory>]
       [-p<prototype-page>]"

defProtoPage=/usr/local/lib/protopage
PROTOPAGE=$defProtoPage
# save value of editor from environment, if any, so that config file won't
# overwrite it
if [ -n "$VISUAL" ]; then
    sEDITOR=$VISUAL
elif [ -n "$EDITOR" ]; then
    sEDITOR=$EDITOR
fi

TBROWSER=lynx
GBROWSER=Mosaic
INDEXPAGE=index.html
PAGEDIR=.public_html
defFile=/etc/default/webpage
defPage="<title>%PSNAME% WWW Home Page</title>
<h1>%PSNAME% WWW Home Page</h1>
This is a prototype web page for %NAME%.
<hr>
This web page maintained by <a href=%URL%>
%USER%@%MAILNAME%</a>"
userDefFile=.webpage
[ -f $defFile -a -r $defFile ] && . $defFile
[ -f $HOME/$userDefFile -a -r $HOME/$userDefFile ] && . $HOME/$userDefFile

if [ -n "$sEDITOR" ]; then
    EDITOR=$sEDITOR
elif [ -z "$EDITOR" ]; then
    EDITOR=vi
fi


while getopts he:g:t:w:m:i:d:p: opt; do
    case $opt in
    h)
	print -r -- \
"$l_name: create, view, or modify a user-maintained web page.
$l_name creates a web page directory for the invoking user, puts a prototype
home page in it, and then lets the user view and modify the page.
For viewing the page, $l_name uses Mosaic if the user is on an X display, and
lynx if not.  If the VISUAL or EDITOR environment variables are set, they are
used to get the name of the editor to invoke.  If not, vi is used.
$l_name will only invoke the editor on the top (index) page.  It tells the user
the location of the web page directory so that the user can create a more
complex web site directly.
To get the hostname of the local system for use in URLs, $l_name first tries
replacing the leftmost component of the local hostname with \"www\".  If this
gives the same IP address as the unmodified hostname, it is used; otherwise
the unchanged hostname is used.
$l_name depends on the web server interpreting a URL of the form /~username/
as a reference to a file named index.html in the user's home page directory
within their UNIX home directory.  The default home page directory is
\".public_html\".
To generate the prototype web page, $l_name reads the file $defProtoPage
and makes the following substitutions in it:
%PSNAME% is replaced with the possessive form of NAME (e.g. John DuBois ->
    John DuBois')
%NAME% is replaced with the name of the invoking user, from the NAME
    environment variable.
%USER% is replaced with the username of the invoking user, from the USER
    environment variable.
%URL% is replaced with the URL of the page being generated.
%MAILNAME% is replaced by the hostname of the local system for mail purposes.
If -m is not given, this is the same as the unmodified hostname.
The default is:
$defPage
<br>
$Usage
Options:
Some of the following options can also be set by assigning values to variables
in either of two configuration files.  First, $defFile is read;
then the file named $userDefFile in the invoking user's home directory is read. 
Assignments in the latter override assignments in the former.  In these files,
values are assigned to variables in shell style, e.g. \"VARNAME=value\". 
Variable names are given in parentheses following option descriptions.
-h: Print this help.
-e<editor>: Use <editor> to edit the page.  (EDITOR)
-g<graphical-browser>: Use <graphical-browser> as the browser if on an X
    display.  (GBROWSER)
-t<text-browser>: Use <text-browser> as the browser if on a text display.
    (TBROWSER)
-w<www-name>: Set the hostname of the local system, for use in URLs, to
    <www-name>.  (WWWNAME)
-m<mailname>: Set the hostname of the local system for use in email addresses
    to <mailname>.  (MAILNAME)
-i<index-page>: Set the filename of the index page to <index-page> instead of
    index.html.  (INDEXPAGE)
-d<homepage-directory>: Set the homepage directory to <homepage-directory>;
    this should the name of the directory under each user's UNIX home directory
    in which the web server will look for /~username/ URLs.  (PAGEDIR)
-p<prototype-page>: Set the filename of the web page prototype to
    <prototype-page>.  (PROTOPAGE)"
	exit 0
	;;
    d)
	PAGEDIR=$OPTARG
	;;
    e)
	EDITOR=$OPTARG
	;;
    g)
	GBROWSER=$OPTARG
	;;
    i)
	INDEXPAGE=$OPTARG
	;;
    m)
	MAILNAME=$OPTARG
	;;
    p)
	PROTOPAGE=$OPTARG
	;;
    t)
	TBROWSER=$OPTARG
	;;
    w)
	WWWNAME=$OPTARG
	;;
    ?)
	print -r -u2 "Use -h for help."
	exit 1
	;;
    esac
done

# remove args that were options
let OPTIND=OPTIND-1
shift $OPTIND

HomePageDir="$HOME/$PAGEDIR"
HomePage="$HomePageDir/$INDEXPAGE"
[ -z "$DISPLAY" ] && browser=$TBROWSER || browser=$GBROWSER
if [ -z "$WWWNAME" ]; then
    # Try to get the best name for web purposes.
    # If the local system's domain name with the host name replaced with 'www'
    # gives the same ip address as the hostname does, use it; else use the
    # original domain name.
    hostname=$(hostname)
    wwwname_maybe=www.${hostname#*.}
    WWWNAME=$hostname
    if host2addr "$hostname"; then
	ip=$host2addr
	if host2addr "$wwwname_maybe"; then
	    [ $host2addr = $ip ] && WWWNAME=$wwwname_maybe
	fi
    fi
fi
if [ -z "$MAILNAME" ]; then
    [ -z "$hostname" ] && hostname=$(hostname)
    MAILNAME=$hostname
fi

URL=http://$WWWNAME/~$USER/

if [ -f $HomePage ]; then
    print \
"You already have a web page.  The file name is:
$HomePage
The URL is: $URL
"
else
    MakePage
fi

select ans in \
"View your home page as HTML source" \
"Edit your home page with $EDITOR" \
"Look at your home page with $browser" \
"Check the permissions on your web-served files" \
"Fix the permissions on your web-served files" \
"Quit"
do
    case "$ans" in
    View*)
	${PAGER:-more} "$HomePage";;
    Edit*)
	"$EDITOR" "$HomePage";;
    Look*)
	"$browser" "$URL";;
    Check*)
	CheckPerms "$HOME" "$HomePageDir" "$HomePage";;
    Fix*)
	FixPerms "$HOME" "$HomePageDir" "$HomePage";;
    Quit)
	break 2;;
    *)
	case "$REPLY" in
	[qQeExX]*)
	    break 2;;
	*)
	    print -u2 \
	"Please enter 1, 2, 3, or 4, or press return for a list of options.."
	    ;;
	esac
	;;
    esac
done
