#!/usr/bin/perl
#
# mat.pl - MiniVend Administration Tool
#
# Version 0.2
# Copyright 1996 by Mike Heins <mikeh@iac.net>
#
# Many routines came from Vend 0.2
# Copyright 1995 by Andrew Wilcox <awilcox@maine.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
############### Configurable Variables ######################
#
# Where the MiniVend root directory is
BEGIN {
$VendRoot = '/usr/local/lib/minivend';
}
#
# The product directory
$PageDir = 'pages';

# Set up for the proper type by configure
use Fcntl;

# Where Perl is
$PERL			= '/usr/bin/perl';

# Where the config files are
$ConfDir = '/usr/local/lib/minivend/etc';

# Where the executables are
$BinDir			= "$VendRoot/bin";

# Where the MiniVend page directory is
$VendPageDir	= "$VendRoot/$PageDir";

# Where the main HTML directory is
$HtmlDir		= '/usr/local/apache/htdocs';

$ConfigFile = 'minivend.cfg';

$DefaultsFile 	= "$ConfDir/minivend.def";

$HelpFile 	    = "$ConfDir/mvconf.cmt";

$ErrorFile	= "$VendRoot/error.log";


############## END CONFIGURABLE VARIABLES ###################

use lib $::VendRoot;
use Config;
use Vend::Util;
require "find.pl";

sub errorbox {
    local($title,@message) = @_;
	&html_header($title);
	print "<H1>$title</H1>\n";
    for(@message) {
		s/\n/<BR>\n/g;
		print;
	}
	&html_trailer('');
    exit;
}

sub set_expire {
	my $amount = shift;
	my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
		gmtime(time + $amount);
	my (@wd) = qw(Sun Mon Tue Wed Thu Fri Sat);
	my (@mon) = qw(Jan Feb Mar Apr May Jun
					Jul Aug Sep Oct Nov Dec);
	sprintf "Expires: %s, %02d %s %d %02d:%02d:%02d GMT\n",
		$wd[$wday], $mday, $mon[$mon], $year, $hour, $min, $sec;
}

sub cgi_decode {
	$FORM = '';
	unless (defined $ENV{'REQUEST_METHOD'}) {
		warn "Unrecognized request type";
		return undef;
	}
	if ($ENV{'REQUEST_METHOD'} eq "GET") {
		return 0;
	}
	elsif ($ENV{'REQUEST_METHOD'} eq "POST") {
		read(STDIN,$FORM,$ENV{'CONTENT_LENGTH'});
	} 
	elsif ($ENV{'REQUEST_METHOD'} eq "SHELL") {
		undef $/;
		$FORM = <>;
		chop($FORM);
		$/ = "\n";
	} 
	else {
		$FORM = '';
		errorbox "Unrecognized request type";
		return undef;
	}
#my $form = $FORM;
#$form =~ s/&/\n/g;
#errorbox("Got here!", "$form");
  for (split(/&/,$FORM)) {
    s/\+/ /g;

    s/%([0-9A-Fa-f]{2})/pack('c',hex($1))/eg;
    ($key, $val) = split(/=/,$_,2);

	if (defined $FORM{$key}) {
    	$FORM{$key} .= "\0";
	}
	else {
		push(@FORM,$key);
		$FORM{$key} = '';
	}
    $FORM{$key} .= $val;
  }
  return 1; 
}

sub regularize {
	for (@_) {
			s/[\\]\n//g;
			s/\n\s+/ /g;
			s/\s+$//g;
	}
	wantarray ? @_ : $_[0];
}

sub dontwarn {
	
}

sub html_trailer {
	print <<EOF ;
</body></html>
EOF
}

sub html_header {
	local($title) = $_[0];

	print <<EOF ;
Content-type: text/html

<HEAD><TITLE>
$title
</title></head>
<HTML><BODY>
EOF
}

sub content_html {
	print "Content-type: text/html\n\n";
}

=head1 NAME

mat - administrative interface for MiniVend

=head1 SYNOPSIS

    http://SERVER_NAME/protected-bin/mat

=head1 DESCRIPTION

The MiniVend Administration Tool (MAT) puts an HTML adminstrative
interface on the MiniVend shopping cart software, allowing non-UNIX
users to administer it.

When VendRoot is referred to in the following text, it refers to
the directory where MiniVend is installed.

It reads the set of configurable variables from a defaults
file (normally F<VendRoot/bin/minivend.def>), and modifies them with those in
the MiniVend configuration file (by default F<VendRoot/minivend.cfg>).

It is dispatched by an SUID executable compiled from C,
I<which must be in a password-protected CGI directory>.  It should 
B<NEVER BE SUID ROOT>!  The author won't be responsible in any case,
and especially if the program is left unprotected.

The HTML DocumentRoot and the MiniVend PageDir must be defined in 
program variables.  That is normally done by the MiniVend configuration
utility, but can be done manually.

It's default action when called with the GET method is to dispatch
its form, with the current configuration filled in.  If it receives
a post back from a form that it has dispatched, and that it has current
authorization for, it will perform one of several actions.

=over 4

=item 1. Start Server
Starts the MiniVend Server.

=item 2. Stop Server
Starts the MiniVend Server.

=item 3. Re-read Configuration
Restarts the MiniVend Server, in effect making it re-read configuration
and possibly re-build the database.

=item 4. Display Error Log
Displays the MiniVend error log.

=item 5. Rename .htm to .html
Rename any .htm files in the MiniVend page or HTML page directory to
.html extenstions.  Skips the automatically-generated catalog directory.

=item 6. Add Carriage Returns
Add carriage returns to any files in the MiniVend page or HTML page directory.
Skips the automatically-generated catalog directory.

=item 7. Trim Carriage Returns
Remove carriage returns from any files in the MiniVend page or HTML page directory.
Skips the automatically-generated catalog directory.

=item 8. Display Sessions
Displays the MiniVend sessions in the database.

=item 9. Expire Sessions
Expires the MiniVend session file according to the setting of SessionExpire.

=item 10. Configure
Change the MiniVend configuration file.

=back

=head2 Options

None, other than those in the configuration files and program variables. 

=head2 Configuration File Format

The defaults file, then the configuration file, are scanned at
program startup.  There is only one variable type -- simple (scalar) variables. 
A new variable may be installed by simply adding it to the F<minivend.def>
file, along with any default value.

The format is simple.  The variable name is the first entry on the line,
and may contain only alpha-numeric characters, dashes, and underscores.
It is followed by any number of spaces and/or tabs or equals signs, then
the variable value. The variable value is everything till the end of the
line, with no same-line comments allowed.

The MiniVend configuration file is then scanned, and values are checked against
the defaults.  If a variable differs, it will be written to the configuration
file. If it is the same, I<it will not be written>.

B<WARNING>:Only program hackers should change the minivend.def file, for if
it differs from the configuration in the minivend.pl file,
B<the system will not work>.

The variables themselves are described in the MiniVend documentation.

=head2 Start Server Function

Simply calls the VendRoot/bin/start file, starting the MiniVend
server. This option will not be presented if the server is running.

=head2 Stop Server Function

Simply calls the VendRoot/bin/stop file, stopping the MiniVend
server. This option will not be presented if the server is not running.

=head2 Restart Server Function

Simply calls the VendRoot/bin/restart file, restarting the MiniVend
server.  This makes MiniVend re-read configuration, but not rebuild
the database. This option will not be presented if the server is not running.

=head2 Rename .htm to .html Function

This is activated by clicking the I<Rename .htm to .html> button in the form.
The RENAME function simply changes all files in a directory tree that
have a .htm file extension to a .html extension.  It is I<recursive>,
which means that ALL files under the VendRoot/pages and/or HTML
DocumentRoot (as selected) will be changed.

B<WARNING:> This will destroy any contents of files with the
same root file name that have a .html extension.
It does check to see that the .htm file is newer, but be careful.

Unless the number of files is very large (greater than 1000), the
function should not take long.  On completion, the status of the
operation is returned to the browser in HTML format.

=head2 Add/Trim Carriage Returns Function

This is activated by clicking the I<Add Carriage Returns> or
I<Trim Carriage Returns> buttons in the form.  Depending on whether
the HTML Directory or Page Directory checkboxes are selected, it will
operate on the HTML tree or the MiniVend tree.  It is I<recursive>, which means
that ALL files under the VendRoot/pages and/or HTML DocumentRoot (as
selected) will be changed.

The Add Carriage Returns function simply adds or trims carriage returns
(^M characters) to all .htm, .html, .cfg, .asc, or .txt files in a
directory tree. If adding, it adds them to a line before the linefeed
only if not already present.

If removing, it removes all unencoded carriage returns at the end of a line.

On completion, the status of the operation is returned to
the browser.

=head2 Display Error Log Function

The Display Error Log function simply shows the contents of MiniVend's
error log.

=head2 Display Sessions Function

The Display Sessions function simply dumps any user sessions in the session
database to the browser in plain/text format. Lines will not
be wrapped, so the output may be hard to read. Save to a file
to examine all information.

=head2 Expire Function

The Expire function simply expires all sessions that are older
than allowed by the SessionExpire variable. This is normally best
done on a daily basis by a crontab(8) entry, but is provided for
convenience.

=head2 Configuration Editing

Each configuration variable is presented in a fill-out form, 
and is written as is. Any variables that are not the default value will
be at the top -- the ones that have the default value will be
in alphabetical order toward the bottom of the file.

Certain variables may have an effect on sessions in progress, in
particular the variables which set the frames for certain functions.

After the file is written, the file is tested, and if there
are any problems reported, the old file will be restored.

=head2 Bugs

None yet known in this version. Just wait.

=head2 Author

I<Mike Heins>, F<mikeh@iac.net>

=head2 Credits

Few routines but much of the inspiration came from Andrew Wilcox's
Vend 0.2 package.  Give him the credit.

=cut

sub check_pid {
	opendir(CHECKPID, $ConfDir)
			or die "Couldn't open $ConfDir: $!\n";
	my ($pid,@out);
	my(@pids) = grep /minivend\.pid[0-9]?/, readdir CHECKPID;
	closedir CHECKPID;
	for(@pids) {
    	open(PIDFILE,"$ConfDir/$_")
			|| warn "Couldn't open $_: $!\n";
    	chomp($pid = <PIDFILE>);
		close(PIDFILE);
    	push @out, $pid;
	}
	return scalar(@pids), @pids;
}

sub wanted {
	my $rename = defined $FORM{'func_rename'};
	my $dosify = defined $FORM{'func_dosify'};
	my $unixify = defined $FORM{'func_unixify'};
	my ($mod, $lmod, $lname);

	unless ($rename || $dosify || $unixify) {
		errorbox("ERROR: No utility function specified");
	}

	RENAME: {
		if($rename) {
			last RENAME unless $_ =~ /\.htm$/;
			$lname  = "${name}l";
			if( -e  $lname) {
				push(@Errors, "$lname: file exists\n");
				$lmod = (stat(_))[9];
				$mod = (stat($name))[9];
				if ($mod < $lmod) {
					push(@Errors, "$lname: newer than $name -- unchanged.\n");
					last RENAME;
				}
				unlink $lname;
			}
			rename $name, $lname;
		}
	}

	DOSIFY: {
		if ($dosify) {
			last DOSIFY unless -f $name;	#skip directories
			last DOSIFY unless $name =~ /\.(html?|txt|conf|cfg|asc)$/;
			$lname = "$name.dosify";
			unless (rename $name, $lname) {
				errorbox "ERROR: Couldn't rename",
							"Couldn't rename $name to $lname";
			}
			open(RENIN, $lname) || die "Couldn't read $lname: $!\n";
			open(RENOUT, ">$name") || die "Couldn't create $name: $!\n";
			while(<RENIN>) {
				s/\r*$/\r/;
				print RENOUT $_;
			}
			close RENIN;
			close RENOUT;
			unlink $lname;
		}
	}

	UNIXIFY: {
		if ($unixify) {
			last UNIXIFY unless -f $name;	#skip directories
			last UNIXIFY unless $name =~ /\.(html?|txt|conf|cfg|asc)$/;
			$lname = "$name.unixify";
			unless (rename $name, $lname) {
				errorbox "ERROR: Couldn't rename",
							"Couldn't rename $name to $lname";
			}
			open(RENIN, $lname) || die "Couldn't read $lname: $!\n";
			open(RENOUT, ">$name") || die "Couldn't create $name: $!\n";
			while(<RENIN>) {
				s/\r+$//;
				print RENOUT $_;
			}
			close RENIN;
			close RENOUT;
			unlink $lname;
		}
	}
}

sub readconfig {
	my($status,$key,$val);
	open(DEFAULTS, $DefaultsFile)
		|| do {
				errorbox("Couldn't open defaults file: $!\n");
			};
	open(CONFIG, $ConfigFile)
		|| do {
				errorbox("Couldn't open config file: $!\n");
			};

	while(<DEFAULTS>) {
		next if /^\s*#/;	# Ignore comments
		next unless /\S/;	# Ignore blanks
		chomp;
		($key, $value) = split(/[\t =]+/, $_, 2);
		$Default{$key} = $value;
	}
	close DEFAULTS;

	while(<CONFIG>) {
		next if /^\s*#/;	# Ignore comments
		next unless /\S/;	# Ignore blanks
		chomp;
		($key, $value) = split(/[\s=]+/, $_, 2);
		$Vendfig{$key} = $value;
	}
	close CONFIG;

	%Current = %Default;

	# Check that all config file values are valid
	for(keys %Vendfig) {
		unless (defined $Default{$_}) {
			errorbox( "Configuration file error",
					"$_ doesn't exist!");
		}
		$Current{$_} = $Vendfig{$_}
	}

	for(keys %Default) {
		if ($Default{$_} eq 'UNDEFINED' and ! $Vendfig{$_}) {
			$Error{$_} = "$_ mandatory, can't be blank.";
			$Current{$_} = "ERROR: ";
		}
		elsif ($Current{$_} eq 'BLANK_DEFAULT') {
			$Current{$_} = '';
		}
	}
	1;
}

# Takes a file name as an argument, and returns associative
# array with help text
sub gethelp {
	my $file = shift;
	my ($thing, $var, $comment);
	my (%help);

	if (open(HELP, $file)) {
    	$/ = '';
    	while(<HELP>) {
        	($thing, $comment) = split /\n/, $_, 2;
        	($var, undef) =  split /\s+/, $thing, 2;
        	$help{$var} = $comment;
    	}
    	close HELP;
    	$/ = "\n";
	}
	else {
		 return undef;
	}
	%help;
}

sub writeconfig {
	my($status,$key,$val);
	open(WRITECONFIG, ">$ConfigFile")
		|| die "Couldn't write $ConfigFile: $!, died";
	my %remaining = %Default;
	for(sort keys %Default) {
		delete $remaining{$_};
		next if $FORM{$_} eq $Default{$_};
		next unless $FORM{$_};
		printf WRITECONFIG "%-20s %s\n", $_, $FORM{$_};
	}
	close WRITECONFIG;
	if (scalar(keys %remaining)) {
		push(@Errors, "Not all values present in form!\n");
		return undef;
	}
	return 1;
}

sub sendform {
	html_header("MiniVend Configuration Menu");
	$HR = $ENV{'HTTP_USER_AGENT'} =~ /lynx/i ? '<HR>' : '';
	print <<EOF ;
<H1>MiniVend Configuration</H1>
<P>
<FORM METHOD=POST ACTION="$ENV{'SCRIPT_NAME'}"
		ENCTYPE="application/x-www-form-urlencoded">
<TABLE BORDER=1>
EOF
	if($ServerRunning) {
		print <<EOF ;
<TR><TD WIDTH=100><B>Server Control</B></TD>
	<TD>
	<INPUT TYPE=SUBMIT	NAME="func_stop" VALUE="Stop Server (PID $Pid)"><BR>
	</TD>
	<TD>
	<INPUT TYPE=SUBMIT	NAME="func_restart" VALUE="Re-read Config"><BR>
	</TD>
</TR>
$HR
EOF
	}
	else {
		print <<EOF ;
<TR><TD WIDTH=100><B>Server Control</B></TD>
	<TD COLSPAN=2>
	<INPUT TYPE=SUBMIT	NAME="func_start" VALUE="Start Server"><BR>
	</TD>
</TR>
$HR
EOF
	}
	print <<EOF;
<TR><TD WIDTH=100><B>Error Log</B></TD>
	<TD COLSPAN=2>
	<INPUT TYPE=SUBMIT	NAME="func_disperror" VALUE="Display Error Log"><BR>
	</TD>
</TR>
$HR
<TR><TD WIDTH=100 ROWSPAN=3><B>Utility</B></TD>
	<TD>
	<INPUT TYPE=SUBMIT	NAME="func_rename" VALUE="Rename .htm to .html"><BR>
	</TD>
	<TD>
	<INPUT TYPE=CHECKBOX NAME="rendir" VALUE="$HtmlDir" CHECKED>
	HTML Directory<BR>
	<INPUT TYPE=CHECKBOX NAME="rendir" VALUE="$VendPageDir" CHECKED>
	Page Directory<BR>
	</TD>
</TR>
$HR
<TR>
	<TD>
	<INPUT TYPE=SUBMIT	NAME="func_dosify" VALUE="Add carriage returns"><BR>
	</TD>
	<TD>
	<INPUT TYPE=CHECKBOX NAME="dosifydir" VALUE="$HtmlDir" CHECKED>
	HTML Directory<BR>
	<INPUT TYPE=CHECKBOX NAME="dosifydir" VALUE="$VendPageDir" CHECKED>
	Page Directory<BR>
	</TD>
$HR
<TR>
	<TD>
	<INPUT TYPE=SUBMIT	NAME="func_unixify" VALUE="Trim carriage returns"><BR>
	</TD>
	<TD>
	<INPUT TYPE=CHECKBOX NAME="unixifydir" VALUE="$HtmlDir" CHECKED>
	HTML Directory<BR>
	<INPUT TYPE=CHECKBOX NAME="unixifydir" VALUE="$VendPageDir" CHECKED>
	Page Directory<BR>
	</TD>
</TR>
$HR
<TR><TD WIDTH=100><B>Session</B></TD>
	<TD>
	<INPUT TYPE=SUBMIT	NAME="func_dump" VALUE="Display sessions"><BR>
	</TD>
	<TD>
	<INPUT TYPE=SUBMIT	NAME="func_expire" VALUE="Expire sessions"><BR>
	</TD>
</TR>
EOF
	print "</TABLE>\n";
	
	if(defined %Error) {
		print "<H2>Values that are in error</H2>\n";
		print "<TABLE>\n";
		for (sort keys %Error) {
			print <<EOF ;
<TR>
	<TD>
	<A HREF="$ENV{'SCRIPT_NAME'}#$_">$_</A>
	</TD>
	<TD>
	<INPUT NAME="$_" VALUE="$Current{$_}$Error{$_}" SIZE="60"><BR>
	</TD>
</TR>
EOF
			$Done{$_} = 1;
		}
		print "</TABLE>\n";
	}
	
	print "<BR><H2>Values set in configuration file </H2>\n";
	print "<TABLE>\n";
	for (sort keys %Vendfig) {
		next if $Done{$_};
		print <<EOF ;
<TR>
	<TD>
	<A HREF="$ENV{'SCRIPT_NAME'}#$_">$_</A>
	</TD>
	<TD>
	<INPUT NAME="$_" VALUE="$Current{$_}" SIZE="60"><BR>
	</TD>
</TR>
EOF
		$Done{$_} = 1;
	}
	print "</TABLE>\n";

	print "<TABLE>\n";
	print "<TR><TD WIDTH=100></TD><TD>";
	print qq|<INPUT TYPE=SUBMIT	NAME="func_config" VALUE="Configure">\n|;
	print "</TD></TR></TABLE>\n";
	
	print "<BR><H2>Values that are default</H2>\n";
	print "<TABLE>\n";
	for (sort keys %Default) {
		next if $Done{$_};
		print <<EOF ;
<TR>
	<TD>
	<A HREF="$ENV{'SCRIPT_NAME'}#$_">$_</A>
	</TD>
	<TD>
	<INPUT NAME="$_" VALUE="$Current{$_}" SIZE="60"><BR>
	</TD>
</TR>
EOF
		$Done{$_} = 1;
	}
	print "</TABLE>\n";

	print "</FORM>\n";
	print "<HR SIZE=4><H2>Directive Descriptions</H2>";
	PRINTHELP: {
		last PRINTHELP unless defined %Help;
		for (sort keys %Default) {
			print <<EOF ;
<A NAME="$_">
<HR SIZE=1>
<H3>$_</H3></A>
$Help{$_}
<BR>
EOF
		}
	} # end PRINTHELP
	html_trailer;
	
}

sub dofunction {
	
	my $funcs;
	my $function;
	my $tmpfile = "$TMPDIR/MAT~.tmp";
	
	for(
		qw( func_translate func_makedbm func_rename func_unixify
			func_restart func_start func_stop func_dump
			func_makepage func_disperror func_dosify
			func_expire func_config ) 
		) {
		next unless defined $FORM{$_};
		$funcs++;
		$function = $_;
		$::Function = $function;
		$::Function =~ s/^func_(.*)/\u$1/;
	}

	if ($funcs != 1) {
		errorbox 'ERROR: Function Specification',
				"There was no action (or too many actions) specified.";
	}

	if($function eq 'func_disperror') {
		open(RESULTS,"$ErrorFile")
			|| die "Couldn't fork: $!\n";
		while(<RESULTS>) {
			push(@Results,$_);
		}
		close RESULTS;
		$status = $?;
		if (-s $tmpfile) {
			open(ERRORS,$tmpfile) || die;
			push(@Errors,<ERRORS>);
			close ERRORS;
		}
		unless ($?) {
			push(@Results,"Error log dump complete.");
		}
		else {
			unshift(@Errors,@Results);
			push(@Errors,"ERROR: Error log dump unsuccessful.");
			return undef;
		}
	}
	elsif($function eq 'func_stop') {
		$cmd = "$BinDir/stop";
		open(RESULTS,"$cmd 2>$tmpfile |")
			|| die "Couldn't fork (or maybe open $tmpfile): $!\n";
		while(<RESULTS>) {
			push(@Results,$_);
		}
		close RESULTS;
		$status = $?;

		if (-s $tmpfile) {
			open(ERRORS,$tmpfile) || die;
			push(@Errors,<ERRORS>);
			close ERRORS;
		}
		unlink $tmpfile;

		unless ($?) {
			push(@Results,"STOP server complete.");
		}
		else {
			unshift(@Errors,@Results);
			push(@Errors,"ERROR: STOP server command unsuccessful.");
			return undef;
		}

	}
	elsif($function eq 'func_start') {
		$cmd = "$BinDir/start";
		my $gw_interface_save = $ENV{'GATEWAY_INTERFACE'};
		$ = 0;
		undef $ENV{'GATEWAY_INTERFACE'};
		$ = 1;
		open(RESULTS,"$cmd 2>$tmpfile |")
			|| die "Couldn't fork (or maybe open $tmpfile): $!\n";
		while(<RESULTS>) {
			push(@Results,$_);
		}
		close RESULTS;
		$status = $?;
		$ENV{'GATEWAY_INTERFACE'} = $gw_interface_save;

		if (-s $tmpfile) {
			open(ERRORS,$tmpfile) || die;
			push(@Errors,<ERRORS>);
			close ERRORS;
		}
		unlink $tmpfile;

		unless ($?) {
			push(@Results,"START server complete.");
		}
		else {
			unshift(@Errors,@Results);
			push(@Errors,"ERROR: START server command unsuccessful.");
			return undef;
		}

	}
	elsif($function eq 'func_restart') {
		$cmd = "$BinDir/restart";
		open(RESULTS,"$cmd 2>$tmpfile |")
			|| die "Couldn't fork (or maybe open $tmpfile): $!\n";
		while(<RESULTS>) {
			push(@Results,$_);
		}
		close RESULTS;
		$status = $?;

		if (-s $tmpfile) {
			open(ERRORS,$tmpfile) || die;
			push(@Errors,<ERRORS>);
			close ERRORS;
		}
		unlink $tmpfile;

		unless ($?) {
			push(@Results,"RESTART server complete.");
		}
		else {
			unshift(@Errors,@Results);
			push(@Errors,"ERROR: RESTART server command unsuccessful.");
		}

	}
	elsif($function eq 'func_rename') {
		my @dirs;
		@dirs = split /\0/, $FORM{'rendir'}
			if defined $FORM{'rendir'};
		if (scalar(@dirs)) {
			$ = 0;
			&find( @dirs );
			$ = 1;
			push(@Results,"RENAME function complete.");
		}
		else {
			push(@Errors,"No directory selected.");
			return undef;
		}
	}
	elsif($function eq 'func_dosify') {
		my @dirs;
		@dirs = split /\0/, $FORM{'dosifydir'}
			if defined $FORM{'dosifydir'};
		if (scalar(@dirs)) {
			$ = 0;
			&find( @dirs );
			$ = 1;
			push(@Results,"DOSIFY function complete.");
		}
		else {
			push(@Errors,"No directory selected.");
			return undef;
		}
	}
	elsif($function eq 'func_unixify') {
		my @dirs;
		@dirs = split /\0/, $FORM{'unixifydir'}
			if defined $FORM{'unixifydir'};
		if (scalar(@dirs)) {
			$ = 0;
			&find( @dirs );
			$ = 1;
			push(@Results,"Trim carriage return function complete.");
		}
		else {
			push(@Errors,"No directory selected.");
			return undef;
		}
	}
	elsif($function eq 'func_dump') {
		my $gw_interface_save = $ENV{'GATEWAY_INTERFACE'};
		$ = 0;
		undef $ENV{'GATEWAY_INTERFACE'};
		$ = 1;
		open(RESULTS,"$PERL $VendRoot/minivend.pl -dump-sessions 2>$tmpfile |")
			|| die "Couldn't fork: $!\n";
		while(<RESULTS>) {
			push(@Results,$_);
		}
		close RESULTS;
		$status = $?;
		$ENV{'GATEWAY_INTERFACE'} = $gw_interface_save;
		if (-s $tmpfile) {
			open(ERRORS,$tmpfile) || die;
			push(@Errors,<ERRORS>);
			close ERRORS;
		}
		unlink $tmpfile;
		unless ($?) {
			push(@Results,"DUMP command successful!");
		}
		else {
			unshift(@Errors,@Results);
			push(@Errors,"ERROR: DUMP command unsuccessful.");
			return undef;
		}
	}
	elsif($function eq 'func_expire') {
		my $gw_interface_save = $ENV{'GATEWAY_INTERFACE'};
		$ = 0;
		undef $ENV{'GATEWAY_INTERFACE'};
		$ = 1;
		open(RESULTS,"$PERL $VendRoot/minivend.pl -expire 2>$tmpfile |")
			|| die "Couldn't fork: $!\n";
		while(<RESULTS>) {
			push(@Results,$_);
		}
		close RESULTS;
		$status = $?;
		$ENV{'GATEWAY_INTERFACE'} = $gw_interface_save;
		if (-s $tmpfile) {
			open(ERRORS,$tmpfile) || die;
			push(@Errors,<ERRORS>);
			close ERRORS;
		}
		unless ($?) {
			push(@Results,"EXPIRE command successful!");
		}
		else {
			unshift(@Errors,@Results);
			push(@Errors,"ERROR: DUMP command unsuccessful.");
			return undef;
		}
	}
	elsif($function eq 'func_config') {
		rename "$ConfigFile", "$VendRoot/vendconf.bak"
			|| die "Couldn't back up $ConfigFile, aborting"; 
		unless($ServerRunning) {
			my $gw_interface_save = $ENV{'GATEWAY_INTERFACE'};
			$ = 0;
			undef $ENV{'GATEWAY_INTERFACE'};
			$ = 1;
			if(writeconfig($ConfigFile)) {
				open(RESULTS,"$PERL $VendRoot/minivend.pl -test 2>$tmpfile |")
					|| die "Couldn't fork: $!\n";
				while(<RESULTS>) {
					push(@Results,$_);
				}
				close RESULTS;
				$status = $?;
			}
			else { $status = 1 }	# Force failure if writeconfig failed
			$ENV{'GATEWAY_INTERFACE'} = $gw_interface_save;
			if (-s $tmpfile) {
				open(ERRORS,$tmpfile) || die;
				push(@Errors,<ERRORS>);
				close ERRORS;
			}
		}
		else {
			$status = ! writeconfig($ConfigFile);
			push(@Results,
				"Configuration will take effect on server restart.\n");
		}
		unless ($status) {
			unlink "$VendRoot/vendconf.bak";
			push(@Results,"Configuration written successfully!");
		}
		else {
			unlink $ConfigFile;
			rename "$VendRoot/vendconf.bak", $ConfigFile;
			unshift(@Errors,@Results);
			push(@Errors,"ERROR: Configuration failed.");
			return undef;
		}
	}
	1;
}

sub sendresult {
	my $iteration = @_;
	html_header("Results from MAT");
	print "<PRE>\n";
	print @Results;
	print "</PRE>\n";
	if (defined @Errors) {
		print "<BR><H3>There were some errors:</H3>\n";
		print "<PRE>\n";
		print @Errors;
		print "</PRE>\n";
	}
	print qq|<A HREF="$ENV{'SCRIPT_NAME'}">Return to Menu</A>|;
	html_trailer;
}
	

# START OF MAIN LOOP

# Set temporary directory
$TMPDIR = $ENV{'TMP'} || $ENV{'TEMP'} || '/tmp';


# Explicitly set these
$ENV{'PATH'} = '/bin:/usr/bin:';
$ENV{'SHELL'} = '/bin/sh';
$ENV{'IFS'} = '';

unless (defined $ENV{'GATEWAY_INTERFACE'}) {
	 die "ERROR: Not in CGI mode.  This program runs in web mode.\n";
}
unless (defined $ENV{'REMOTE_USER'}) {
	 # uncomment this if you MUST, preferably only for testing
	 errorbox("Unh-unh-unh!", "Can't run unprotected with a password.");
}

chdir $VendRoot 
	|| die "Couldn't change directory to $VendRoot: $!\n";


# This doesn't return if executed, just sends form
unless (cgi_decode == 1) {
	readconfig;
	($ServerRunning,$Pid) = check_pid;
	$ServerRunning = !$ServerRunning;
	%Help = gethelp($HelpFile);
	sendform;
	exit;
}

readconfig;
%Help = gethelp($HelpFile);

if (dofunction) {
	sendresult $::Function;
}
else {
	errorbox "Function failed!", @Errors, 
		qq|<A HREF="$ENV{'SCRIPT_NAME'}">Return to menu</A>|; 
}
