#!/usr/bin/perl
#
# Wala.pm
# version 1.1.8
#
# This script is in the public domain, and was originally written by
# Brent P. Newhall <brent@stun-software.com>.
# If you re-distribute this code, modified or otherwise, please give credit
# where due.
#
# This is a Wala, which is a derivation of a wiki that incorporates appending
# text directly to pages, turning a wiki into something more like a forum while
# retaining all the wonderful full-page editing features of a wiki.
#
# INSTALLATION:
# This script is a self-contained package, which makes the code easy to test.
# To actually use it as a wala, create a script named "wala.pl" in the same
# directory, containing the following three lines of code (changed appropriately
# for your setup of Perl):
#
#      #!/usr/bin/perl
#      use Wala;
#      Wala::run;
#
# You can experiment with the wala by use'ing it and calling its functions
# without calling "run".  Any files and/or directories required by this script
# are created as needed.  Make sure that the web server has write access to the
# directory.
#
# Modify the OPTIONS section about fifteen lines down to set up specific
# options for this installation.
#
# FEATURES:
# ''italics'' and '''bold''', ----, ":" for indentation, " " for # code, "#"
# and "*" for lists, "|" for tables, [[Bareword]] pagenames,
# [[Page||alternate link text]], linkifying of http:// and related protocols,
# linkifying of mailto:, inline images, logged diffs, and cookie preferences


# Setup stuff...
use strict;
package Wala;
use UNIVERSAL qw(isa);



###############
#   OPTIONS   #
###############

my $RecentChangesMaxLines = 50;  # Max lines to display in RecentChanges
my $DefaultUserName       = 'Anonymous';  # Default user name for "Add to page"
my $StyleSheet            = '/style.css';  # URL of style sheet
my $PrinterStyleSheet     = '';  # URL of style sheet for printing
my $DefaultPageText       = "Enter text here.\n";
my $CookieSurvivalDays    = 90;  # Number of days for cookies to remain

# To set up and run the script, you should not have to edit anything below
# this line.



# "Global" variables, used by multiple functions
my $ScriptName = substr( $0, rindex( $0, "/" ) + 1 );
my $page;
my %cookies;
my %parameters;
my $last_wiki_line_type = '';
my $current_list_level = 0;
my $last_recent_changes_date = '';



sub run
# This is the "main()" function of the script.  Call this function to run the
# wala.
	{
	my( $query, $result );
	&setup;
	&parse_cookies( $ENV{'HTTP_COOKIE'} );
	$page = 'HomePage';
	if( length( $ENV{'QUERY_STRING'} ) > 0  &&
	    index( $ENV{'QUERY_STRING'}, '=' ) < 0 )
		{
		# We got a plain WikiWord as the only parameter, so that's the page
		$page = $ENV{'QUERY_STRING'};
		# Strip out ; to prevent security breaches
		if( index( $page, ';' ) >= 0 )
			{
			$page = substr( $page, 0, index( $page, ';' ) );
			}
		}
	elsif( length($ENV{'QUERY_STRING'}) > 0  ||  $ENV{'CONTENT_LENGTH'} > 0 )
		{
		# We have one or more parameters; read and parse them
		if( $ENV{'CONTENT_LENGTH'} > 0 )
			{
			read( STDIN, $query, $ENV{'CONTENT_LENGTH'} );
			}
		else
			{
			$query = $ENV{'QUERY_STRING'};
			}
		$result = &parse_parameters( $query, $page );
		}
	print &get_header( $page );
	if( $result ne '' )
		{
		print( $result );
		print( "\n</body>\n</html>\n" );
		}
	else
		{
		print( &print_page( $page ) . &print_standard_footer( $page ) );
		}
	}




############################
#   Wiki Word Processing   #
############################


sub make_links_of_wiki_words
	{
	my( $text ) = shift;
	$text =~ s/\[\[([A-Za-z0-9| ]*)\]\]/&get_wiki_word_link($1)/geos;
	$text =~ s/(?<![A-Za-z0-9\=\/\?\.\,])([A-Z][a-z0-9]+)([A-Z][a-z0-9]+)+/&get_wiki_word_link($&)/geos;
	return $text;
	}




sub get_wiki_word_link
	{
	my( $wikiword ) = shift;
	my $label = $wikiword;
	my $bar_posit = index( $wikiword, '|' );
	if( $bar_posit > 0 )
		{
		$label = substr( $wikiword, $bar_posit + 1 );
		$wikiword = substr( $wikiword, 0, $bar_posit );
		}
	if( -e "pages/$wikiword" )
		{
		return "<a href=\"$ScriptName?$wikiword\" class=\"existingpagelink\">" .
		       "$label</a>";
		}
	else
		{
		return "<a href=\"$ScriptName?action=edit&id=$wikiword\" " .
		          "class=\"nonexistingpagelink\">$label</a>";
		}
	}




#######################
#   Link Processing   #
#######################


sub convert_links
	{
	my( $text ) = shift;
	$text =~ s/(?<!\[)(http|ftp|gopher|news):\/\/[A-Za-z0-9\/\.\-\=\?\&\%\~\_]*/&get_link($&)/geos;
	$text =~ s/\[(http|ftp|gopher|news):\/\/[\s\S]*\]/&get_bracketed_link($&)/geos;
	$text =~ s/mailto:*\S+/&get_email_link($&)/geos;
	return $text;
	}




sub get_bracketed_link
	{
	my( $text ) = shift;
	my( $posit );
	$text = substr( $text, 1, length( $text ) - 2 );
	$posit = index( $text, ' ' );
	if( $posit >= 0 )
		{
		return "<a href=\"" . substr( $text, 0, $posit ) .
		       "\" class=\"externallink\">" . substr( $text, $posit + 1 ) .
		       "</a>";
		}
	else
		{
		return "<a href=\"$text\" class=\"externallink\">$text</a>";
		}
	}




sub get_link
	{
	my( $URL ) = shift;
	my $URL_stub = $URL;
	$URL_stub =~ s|^.*?://(.*)$|$1|;
	my $protocol = substr( $URL, 0, index( $URL, ':' ) );
	if( $URL =~ /^(http:|https:|ftp:).+\.(gif|jpg|jpeg|png$)/ )
		{
		return "<img src=\"$URL\" alt=\"[IMAGE]\" />";
		}
	else
		{
		return "<a href=\"$URL\" class=\"externallink\"><span class=\"printonly\">$protocol://</span>$URL_stub</a>";
		}
	}




sub get_email_link
	{
	my( $email_with_mailto ) = shift;
	return "<a href=\"$email_with_mailto\">" . substr( $email_with_mailto, 7 ) .
	       "</a>";
	}




###################
#   Wiki Markup   #
###################

sub wiki_page_to_html
	{
	my( $page ) = shift;
	my( @lines, $line, $result );
	$last_wiki_line_type = '';
	@lines = split( "\n", $page );
	foreach $line (@lines)
		{
		if( ord( substr( $line, length($line) - 1 ) ) < 20 ) # Remove EOL
			{ chop( $line ); }
		$result .= &wiki_line_to_html( $line ) . "\n";
		}
	$result .= &finish_wiki_tags( $result );
	return $result;
	}




sub wiki_line_to_html
	{
	my( $text ) = shift;
	$text =~ s/&/&amp;/g;
	$text =~ s/</&lt;/g;
	$text =~ s/>/&gt;/g;
	$text = &convert_links( $text );
	$text = &replace_wiki_markup_matched_tags( $text, "'''", '<strong>',
	                                           '</strong>' );
	$text = &replace_wiki_markup_matched_tags( $text, "''", '<em>', '</em>' );
	$text =~ s/----*/<hr \/>/;
	if( substr( $text, 0, 1 ) eq ' ' )
		{
		$text = &setup_wiki_line_type( $text, ' ', '<pre>' );
		}
	elsif( substr( $text, 0, 1 ) eq ':' )
		{
		$text = &setup_wiki_line_type( $text, ':', '<blockquote>' );
		}
	elsif( substr( $text, 0, 1 ) eq '*' )
		{
		$text = &setup_wiki_line_type( $text, '*', '<ul>' );
		}
	elsif( substr( $text, 0, 1 ) eq '#' )
		{
		$text = &setup_wiki_line_type( $text, '#', '<ol>' );
		}
	elsif( substr( $text, 0, 1 ) eq '|' )
		{
		$text = &setup_wiki_line_type( $text, '|', '<table>' );
		}
	else
		{
		$text = &make_paragraph( $text );
		$text = &finish_wiki_tags( $text ) . $text;
		}
	$text = &make_links_of_wiki_words( $text );
	$text =~ s/&lt;recentchanges +(\d+)&gt;/&print_recent_changes($1)/geosi;
	return $text;
	}




sub setup_wiki_line_type
# Processes a Wiki line that starts with a special character like : or #
	{
	my( $text, $first_char, $tag ) = @_;
	my( $num_levels, $index );
	if( $first_char eq '*'  ||  $first_char eq '#' )
		{
		while( substr( $text, 0, 1 ) eq $first_char )
			{
			$text = substr( $text, 1 );
			$num_levels = $num_levels + 1;
			}
		$text = '<li> ' . $text . '</li>';
		if( $num_levels < $current_list_level  &&
		    $last_wiki_line_type eq $first_char )
			{
			$text = &repeat( '</' . substr( $tag, 1 ),
			                 $current_list_level - $num_levels ) . $text;
			}
		if( $num_levels > $current_list_level  &&
		    $last_wiki_line_type eq $first_char )
			{
			$text = &repeat( $tag, $num_levels - $current_list_level ) . $text;
			}
		$current_list_level = $num_levels;
		}
	elsif( $first_char eq '|' )
		{
		$text = "<tr><td>" . substr( $text, 1 );
		$text =~ s/\|/<\/td><td>/g;
		$text .= "</td></tr>";
		}
	elsif( $first_char eq ':' )
		{
		$text = substr( $text, 1 );
		}
	if( $last_wiki_line_type ne $first_char )
		{
		$text = $tag . $text;
		}
	$last_wiki_line_type = $first_char;
	return $text;
	}




sub finish_wiki_tags
	{
	my( $result, $index );
	if( $last_wiki_line_type ne '' )
		{
		if( $last_wiki_line_type eq ' ' )
			{
			$result = '</pre>';
			}
		elsif( $last_wiki_line_type eq ':' )
			{
			$result = '</blockquote>';
			}
		elsif( $last_wiki_line_type eq '*' )
			{
			$result = &repeat( '</ul>', $current_list_level );
			}
		elsif( $last_wiki_line_type eq '#' )
			{
			$result = &repeat( '</ol>', $current_list_level );
			}
		elsif( $last_wiki_line_type eq '|' )
			{
			$result = '</table>';
			}
		}
	$last_wiki_line_type = '';
	return $result;
	}




sub replace_wiki_markup_matched_tags
# Takes text, a type of Wiki markup (like ''), and start and end HTML tags.
# Replaces two occurrences of the markup with the start and end tags.
	{
	my( $text, $markup, $start_tag, $end_tag ) = @_;
	$text =~ s/($markup)([\s\S]*?)($markup)/$start_tag$2$end_tag/g;
	return $text;
	}




sub make_paragraph
	{
	my( $text ) = @_;
	if( $text ne ""  &&  $text ne "<hr />"  &&  $last_wiki_line_type eq '' )
		{
		$text = '<p>' . $text . '</p>';
		}
	return $text;
	}




##########################
#   Webpage Processing   #
##########################


sub write_page
	{
	my( $pagename, $file_text, $summary ) = @_;
	&write_diff( $pagename, $file_text );
	open( FILE, ">", "pages/$pagename" );
	print( FILE $file_text );
	close( FILE );
	&log_page_edit( $pagename, $summary, &get_username );
	return print_page( $pagename ) . print_standard_footer( $pagename );
	}




sub add_to_page
	{
	my( $pagename, $new_text ) = @_;
	my( $file_text, $summary, $posit );
	$file_text = &get_file_text( "pages/$pagename" );
	$file_text .= "\n<[[" . &get_username . "]]> $new_text\n";
	$posit = index( $new_text, "\n" );
	if( $posit > 0 )
		{
		$summary = substr( $new_text, 0, $posit );
		}
	else
		{
		$summary = $new_text;
		}
	return &write_page( $pagename, $file_text, $summary );
	}




sub get_header
	{
	my( $pagename ) = shift;
	my( $result );
	$result = "Content-type: text/html\n\n" .
	          "<html>\n" .
	          "<head>\n" .
	          "<title>Wala - $pagename</title>\n";
	if( $StyleSheet ne '' )
		{
		$result .= "<link rel=\"stylesheet\" href=\"$StyleSheet\" " .
		                 "type=\"text/css\" media=\"screen\">\n";
		}
	if( $PrinterStyleSheet ne '' )
		{
		$result .= "<link rel=\"stylesheet\" href=\"$PrinterStyleSheet\" " .
		                 "type=\"text/css\" media=\"print\">\n";
		}
	$result .= "</head>\n" .
	           "<body>\n" .
	           "<h1>$pagename</h1>\n" .
	           "<div style=\"text-align: right\">\n" .
	           " <a href=\"$ScriptName?HomePage\" class=\"existingpagelink\">" .
	           "Discussion Home</a> | <a href=\"$ScriptName?RecentChanges\" " .
	           "class=\"existingpagelink\">Recent Changes</a> || " .
	           "<a href=\"$ScriptName?action=edit&id=$pagename\" " .
	           "class=\"existingpagelink\">Edit this page</a>\n" .
	           "</div>\n" .
	           "<hr />\n\n";
	return $result;
	}




sub print_standard_footer
	{
	my( $pagename ) = shift;
	my( $text, $datestamp );
	$text = "\n\n\n<div style=\"background-color: #BBBBBB; " .
	                     "border: 1px solid black; margin-top: 50px; " .
	                     "padding: 5px\">\n" .
	        "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" " .
	               "style=\"font-size: 100%\">\n" .
	        " <form action=\"$ScriptName\" method=\"post\">\n" .
	        " <input type=\"hidden\" name=\"action\" value=\"addtext\">\n".
	        " <input type=\"hidden\" name=\"id\" value=\"$pagename\">\n" .
	        " <tr><td style=\"background-color: #BBBBBB\"></td>" .
	             "<td style=\"background-color: #BBBBBB\">Add your " .
	                                              "response:</td></tr>\n" .
	        " <tr>\n" .
	        "  <td style=\"background-color: #BBBBBB\" valign=\"top\">&lt;" .
	        &get_username . "&gt;";
	if( &get_username eq $DefaultUserName )
		{
		$text .= "<br /><a href=\"$ScriptName?action=editprefs&" .
		         "originalpage=$page\">Change name</a>";
		}
	$text .= "</td>\n" .
	         "  <td style=\"background-color: #BBBBBB\" rowspan=\"2\">" .
	         "<textarea name=\"text\" rows=\"5\" cols=\"60\">" .
	         "</textarea></td>\n" .
	         " </tr>\n" .
	         " <tr><td style=\"background-color: #BBBBBB\" valign=\"bottom\">" .
	              "<input type=\"submit\" value=\"Add\"></td></tr>\n" .
	         " <tr><td style=\"background-color: #BBBBBB\"></td>" .
	              "<td style=\"background-color: #BBBBBB\">\n" .
	         "  To format your text, see the <a href=\"$ScriptName?" .
	         "TextFormattingRules\">Text Formatting Rules</a>\n" .
	         " </td></tr>\n" .
	         " </form>\n" .
	         "</table>\n" .
	         "</div>\n";
	if( defined( $cookies{'username'} ) )
		{
		$text .= "Logged in as <a href=\"$ScriptName?action=editprefs&" .
		         "originalpage=$page\">" . &get_username . "</a> ";
		}
	else
		{
		$text .= "<a href=\"$ScriptName?action=editprefs&" .
		         "originalpage=$page\">Log in</a> ";
		}
	$text .= "| <a href=\"$ScriptName?action=edit&id=$pagename\">Edit " .
	         "this page</a>";
	my $diff_date = &get_latest_diff_date( $pagename, $datestamp );
	if( $diff_date ne '' )
		{
		$text .= " (last edited <a href=\"$ScriptName?action=diff&" .
		         "id=$pagename&datestamp=$datestamp\">$diff_date</a>)";
		}
	$text .= "\n</body>\n" .
	         "</html>\n";
	return $text;
	}




sub print_page
	{
	my( $pagename ) = shift;
	my( $pagetext,$result );
	$pagetext = &get_file_text( "pages/$pagename" );
	if( $pagetext eq '' )
		{
		return( $DefaultPageText );
		}
	$pagetext = &wiki_page_to_html( $pagetext );
	if( $pagename eq 'RecentChanges' )
		{
		if( ! -e "pages/RecentChanges" )
			{
			open( FILE, ">", "pages/RecentChanges" );
			print( FILE $DefaultPageText );
			close( FILE );
			}
		$pagetext .= &print_recent_changes( $RecentChangesMaxLines );
		}
	return( $pagetext );
	}




sub get_wiki_edit_form
	{
	my( $pagename ) = shift;
	my( $result, $file_text );
	$file_text = &get_file_text( "pages/$pagename" );
	if( $file_text eq '' )
		{
		$file_text = $DefaultPageText;
		}
	$file_text =~ s/</&lt;/g;
	$file_text =~ s/>/&gt;/g;
	$result = "<form action=\"$ScriptName\" method=\"post\">" .
	          "<input type=\"hidden\" name=\"id\" value=\"$pagename\" />" .
	          "<input type=\"hidden\" name=\"action\" value=\"post\" />" .
	          "<textarea name=\"filetext\" rows=\"20\" wrap=\"virtual\" " .
	                    "style=\"width: 100%\">$file_text</textarea><br />" .
	          "<table border=\"0\">\n" .
	          "<tr><td>Summary:</td><td><input type=\"text\" name=\"summary\" " .
	                          "size=\"30\" /></td></tr>" .
	          "<tr><td></td><td><input type=\"submit\" value=\"Save\"> (";
	if( &get_username eq $DefaultUserName )
		{
		$result .= "You are not logged in.";
		}
	else
		{
		$result .= "You are logged in as " . &get_username . ".";
		}
	$result .= ")</td></tr>\n</table></form>\n";
	return $result;
	}




sub log_page_edit
	{
	my( $pagename, $summary, $username ) = ( shift, shift, shift );
	my $currenttime = time;
	my $result = open( LOGFILE, ">>", "log" );
	if( ! $result )
		{
		return;
		}
	print( LOGFILE "$currenttime $pagename $username $summary\n" );
	close( LOGFILE );
	}




sub get_list_of_all_pages
	{
	my( $filename, $text, @pages );
	opendir( DIR, "pages/" );
	$filename = readdir( DIR );
	$filename = readdir( DIR );
	$filename = readdir( DIR );
	while( $filename ne '' )
		{
		if( $filename ne 'temp' )
			{
			push( @pages, $filename );
			}
		$filename = readdir( DIR );
		}
	@pages = sort( @pages );
	foreach $filename (@pages)
		{
		$text .= "<a href=\"$ScriptName?$filename\" " .
		         "class=\"existingpagelink\">$filename</a><br />\n";
		}
	closedir( DIR );
	return $text;
	}




######################
#   Recent Changes   #
######################


sub print_recent_changes
	{
	my( $max_lines ) = shift;
	my( @lines, $line, $result );
	my $result = open( LOGFILE, "<", "log" );
	if( ! $result )
		{
		return '';
		}
	$result = '';
	@lines = <LOGFILE>;
	close( LOGFILE );
	@lines = reverse( @lines );
	while( $#lines >= $max_lines )
		{
		pop( @lines );
		}
	foreach $line (@lines)
		{
		chop( $line );
		$result .= &get_recent_changes_line( $line );
		}
	return $result . "</ul>\n";
	}




sub get_recent_changes_line
	{
	my( $line ) = shift;
	my( $posit, @t, $timestamp, $thetime, $hour, $min, $ampm, $datestamp,
	    $pagename, $author, @months, $year, $result );
	@months = ( 'January', 'February', 'March', 'April', 'May', 'June',
	            'July', 'August', 'September', 'October', 'November',
	            'December' );
	$timestamp = substr( $line, 0, 10 );
	@t = localtime( $timestamp );
	$year = $t[5] + 1900;
	$datestamp = $months[$t[4]] . " $t[3], $year";
	if( $datestamp ne $last_recent_changes_date )
		{
		if( $last_recent_changes_date ne '' )
			{
			$result .= "</ul>\n";
			}
		$result .= "<b>$datestamp</b><br />\n<ul>\n";
		$last_recent_changes_date = $datestamp;
		}
	$hour = $t[2];
	if( $hour == 0 ) { $ampm = 'am'; $hour = 12; }
	elsif( $hour < 12 ) { $ampm = 'am'; }
	elsif( $hour == 12 ) { $ampm = 'pm'; }
	else { $ampm = 'pm'; $hour -= 12; }
	$min = $t[1]; if( length($min) == 1 ) { $min = '0' . $min; }
	$thetime = "$hour:$min $ampm";
	$line = substr( $line, 11 );
	$posit = index( $line, ' ' );
	$pagename = substr( $line, 0, $posit );
	$line = substr( $line, $posit + 1 );
	if( substr( $line, 0, 1 ) ne ' ' )
		{
		$posit = index( $line, ' ' );
		$author = substr( $line, 0, $posit );
		$line = substr( $line, $posit + 1 );
		}
	else
		{
		$author = 'Anonymous';
		$line = substr( $line, 1 );
		}
	$line =~ s/</&lt;/g;
	$line =~ s/>/&gt;/g;
	$line =~ s/&/&amp;/g;
	$result .= "<li> <a href=\"$ScriptName?$pagename\">$pagename</a> - " .
	           $thetime . " - <b>[" . $line . ']</b> . . . . . ' . $author .
	           "</li>\n";
	}




###############################
#   Preferences and Cookies   #
###############################


sub get_preferences_form
	{
	my( $originalpage ) = shift;
	my( $text );
	$text = "<table border=\"0\">\n" .
	        " <form action=\"$ScriptName\" method=\"post\">\n" .
	        " <input type=\"hidden\" name=\"action\" value=\"setprefs\">\n" .
	        " <input type=\"hidden\" name=\"id\" value=\"$originalpage\">\n" .
	        "  <tr><td>Your name:</td>" .
	              "<td><input type=\"text\" name=\"username\" " .
	              "value=\"" . &get_username . "\" size=\"20\"></td></tr>\n" .
	        "  <tr><td></td><td>(Letters and/or numbers only, no " .
	              "spaces)</td></tr>\n" .
	        "  <tr><td><input type=\"submit\" value=\"Save\"></td></tr>\n" .
	        " </form>\n" .
	        "</table>\n";
	return $text;
	}




sub parse_cookies
	{
	my( $cookie_string ) = ( shift, shift );
	my( @values, $query, $name, $value );
	@values = split(/&/, $cookie_string);
	foreach $query (@values)
		{
		($name, $value) = split(/=/, $query);
		$value =~ tr/+/ /;
		$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
		$cookies{$name} = $value;
		}
	}




sub write_cookies_to_browser
	{
	my( $username, $newpage ) = ( shift, shift );
	my $expiration_time = mygmtime( time + 86400 * $CookieSurvivalDays );
	if( $username =~ /^[A-Za-z0-9]+$/ )
		{
		print( "Set-Cookie: username=$username; expires=$expiration_time\n" );
		}
	print( "Location: $ScriptName?$newpage\n\n" );
	exit( 0 );
	}




#############
#   Setup   #
#############


sub setup
	{
	if( ! -d "pages" )
		{
		mkdir( "pages", 0777 );
		mkdir( "diffs", 0777 );
		}
	if( ! -e "pages/HomePage" )
		{
		open( FILE, ">", "pages/HomePage" );
		print( FILE "Welcome to the Wala.\n\n" .
		            "See TextFormattingRules.\n" );
		close( FILE );
		}
	if( ! -e "pages/TextFormattingRules" )
		{
		open( FILE, ">", "pages/TextFormattingRules" );
		print( FILE "Wala has the following '''text formatting rules''':\n\n" .
		            "* Start a line with * to create a ''bulleted list''.\n\n" .
		            "# Start a line with # to create a ''numbered list''.\n\n" .
		            ":Start a line with a : to indent it.\n\n" .
		            " Start a line with a space to display it like source " .
		              "code.\n\n" .
		            "Start a line with a | to create a table:\n" .
		            "|This is|a table\n" .
		            "|specified with|formatting rules.\n\n" .
		            "SmashWordsTogetherLikeSo to create a link to a new " .
		              "page, or use brackets around a [[word]].  Click on " .
		              "the link to create the page.\n\n" .
		            "For links, just type the URL: " .
		              "http://www.stun-software.com/\n\n" .
		            "Alternatively, surround the URL with brackets and put a " .
		              "description after it, separated by a space: " .
		              "[http://www.stun-software.com/ STUN Software]\n\n" .
		            "To include an image, just type the URL of the image.\n" );
		close( FILE );
		}
	}




sub parse_parameters
	{
	my( $query_string ) = shift;
	my( @values, $query, $name, $value );
	@values = split( /&/, $query_string );
	foreach $query (@values)
		{
		($name, $value) = split(/=/, $query);
		$value =~ tr/+/ /;
		$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack( "C", hex($1) )/eg;
		$parameters{$name} = $value;
		}
	$_[0] = $parameters{'id'};
	if( $parameters{'action'} eq 'edit' )
		{
		return &get_wiki_edit_form( $parameters{'id'} );
		}
	elsif( $parameters{'action'} eq 'post' )
		{
		return &write_page( $parameters{'id'}, $parameters{'filetext'},
		                    $parameters{'summary'} );
		}
	elsif( $parameters{'action'} eq 'addtext' )
		{
		return &add_to_page( $parameters{'id'}, $parameters{'text'} );
		}
	elsif( $parameters{'action'} eq 'diff' )
		{
		$_[0] .= ' Diff';
		return &get_diff( $parameters{'id'}, $parameters{'datestamp'} ) . "<hr />\n" . &print_page( $parameters{'id'} );
		}
	elsif( $parameters{'action'} eq 'setprefs' )
		{
		&write_cookies_to_browser( $parameters{'username'}, $parameters{'id'} );
		}
	elsif( $parameters{'action'} eq 'editprefs' )
		{
		$_[0] = 'Preferences';
		return &get_preferences_form( $parameters{'originalpage'} );
		}
	elsif( $parameters{'action'} eq 'index' )
		{
		$_[0] = 'Index of all pages';
		return &get_list_of_all_pages;
		}
	}




############
#   Diff   #
############


sub get_diff
	{
	my( $pagename, $datestamp ) = ( shift,shift );
	my( $text, @lines, $line, $last_type, $added_text, $removed_text );
	$text = &get_file_text( "diffs/$pagename/$datestamp" );
	@lines = split( "\n", $text );
	$added_text = "<b>Added:</b><br />\n" .
	              "<div style=\"background-color: #AAFFAA\">\n";
	$removed_text = "<b>Removed:</b><br />\n" .
	                "<div style=\"background-color: #FFffAA\">\n";
	$text = '';
	foreach $line (@lines)
		{
		if( substr( $line, 0, 1 ) eq '<'  ||  substr( $line, 0, 1 ) eq '>' )
			{
			$text .= substr( $line, 1 ) . "<br />\n";
			}
		elsif( substr( $line, 0, 3 ) eq '---' )
			{
			$text .= "</div>\n$removed_text";
			}
		elsif( index( $line, 'a' ) >= 0  &&  substr( $line, 0, 1 ) ne '\\' )
			{
			$text .= "</div>\n$removed_text";
			}
		elsif( index( $line, 'c' ) >= 0  &&  substr( $line, 0, 1 ) ne '\\' )
			{
			$text .= "</div>\n$added_text";
			}
		}
	return $text . "</div>\n";
	}




sub write_diff
	{
	my( $pagename, $new_text ) = @_;
	my $currtime = time;
	my( $old_text, $the_diff );
	open( FILE, ">", "pages/temp" );
	print( FILE $new_text );
	close( FILE );
	$the_diff = `diff pages/temp pages/$pagename`;
	mkdir( "diffs/$pagename", 0777 );
	open( FILE, ">", "diffs/$pagename/$currtime" );
	print( FILE $the_diff );
	close( FILE );
	}




sub merge_diff
	{
	my( $pagename, $datestamp ) = @_;
	my( $new_text );
	$new_text = `patch -R --dry-run pages/$pagename diffs/$pagename/$datestamp 2> /dev/null`;
	print $new_text;
	}




sub get_latest_diff_date
	{
	my( $pagename ) = shift;
	my @months = ( 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
	               'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' );
	my( $filename, @filenames, @t );
	if( ! -d "diffs/$pagename" )
		{
		return '';
		}
	opendir( DIR, "diffs/$pagename" );
	$filename = readdir( DIR );
	$filename = readdir( DIR );
	$filename = readdir( DIR );
	while( $filename ne '' )
		{
		push( @filenames, $filename );
		$filename = readdir( DIR );
		}
	closedir( DIR );
	sort( @filenames );
	@filenames = reverse( @filenames );
	@t = localtime( $filenames[0] );
	$_[0] = $filenames[0];
	return $months[$t[4]] . ' ' . $t[3] . ', ' . substr( $t[5] + 1900, 2 );
	}




#################
#   Utilities   #
#################


sub get_username
	{
	if( ( ! defined( $cookies{'username'} ) )  ||  $cookies{'username'} eq '' )
		{
		return $DefaultUserName;
		}
	else
		{
		my $name = $cookies{'username'};
		if( index( $name, ";" ) >= 0 )
			{
			my @values = split( ";", $name );
			return $values[0];
			}
		return $name;
		}
	}




sub mygmtime
	{
	my( $etime ) = shift;
	my @months = ( 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
	               'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' );
	my @days = ( 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' );
	my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($etime);
	my $timestr = sprintf( "%3s, %02d-%3s-%4d %02d:%02d:%02d GMT",
	$days[$wday],$mday,$months[$mon],$year+1900,$hour,$min,$sec);
	return $timestr;
	}




sub repeat
# Returns parameter 1, repeated parameter 2 times
	{
	my( $text, $num_iterations ) = (shift,shift);
	my( $index, $result );
	for( $index = 0; $index < $num_iterations; $index++ )
		{
		$result .= $text;
		}
	return $result;
	}




sub get_file_text
	{
	my( $filename ) = (shift);
	my( $result, $pagetext );
	$result = open( FILE, "<", $filename );
	if( ! $result )
		{
		return( '' );
		}
	while( ! eof( FILE ) )
		{
		$pagetext .= <FILE>;
		}
	close( FILE );
	return $pagetext;
	}

1;
