#!/usr/bin/perl

# FG Guestbook - guestbook.pl - written in XEmacs (http://www.xemacs.org/)
#
# Version: 3.2 - 29-Apr-2003
# Author & Copyright: Felix Gertz - felix.gertz@epost.de
# FG Guestbook homepage: http://fgguestbook.sourceforge.net
#
# First, please read the README!
# There are the install instructions.
#
# This guestbook is free available, because it stands under the
# GPL (GNU General Public License). See LICENSE for more information.
#
# For all that,you shouldn't remove the copyright notice in the footer_template.
# That would be nice.. :-)
# To remove the copyright notice you can send me an postcard or you can pay a
# little amount. More info is in the readme file..
#
# If you have a question, first read the README again, then read the FAQ on the
# homepage and then feel free to email me.

# use strict;
# use CGI::Carp "fatalsToBrowser";

#----------------------------- BEGIN CONFIGURATION ----------------------------#

# Number of entries you want to see at each page.
# Set to 1000 or so to have all on one page.
my $eppage = 20;

# Length of the visible pagebar.
my $pagebar_length = 11;

# If a word is larger then this value, then it will be wraped.
# A good protection against design killers. ;-)
# Set to 0 to disable.
my $wrap_at = 60;

# Allow posting HTML-code?
# 0 = off , 1 = on
my $allowhtml = 0;

# Make a link from an posted URL?
my $set_link = 1;

# Find and kill duplicates?
my $find_duplicate = 1;

# Check Browserstring, if doesn't match, guestbook will deny display.
# Protection against spambots
my $check_browserstring = 0;
my $allowed_browsers = ['mozilla','lynx','links','googlebot','scooter','fast-webcrawler','w3c_validator','opera'];

# Convert the email to ascii-ordinal-numbers?
# So spambots can't read it.
my $convert_email = 0;

# Enable logging?
my $logging = 1;

# Filtering bad words?
my $badword_filter = 0;

# Word to replace with
my $badword_replace = "CENSORED";

# Use emoticons?
my $use_emoticons = 1;

# URI to the emoticonpath.It can be absolut _or_ relativ..
my $emoticon_path = "emoticons/";

# Force input in the name field?
# That should be.. :)
my $force_name = 1;

# Force the email field?
my $force_email = 0;

# Force the homepage field?
my $force_homepage = 0;

# Force the text field?
my $force_text = 1;

# Error text, if somebody filled not out a forced field.
my $error_text = "<p>Give me a name!</p>\n";

# Enable email notification?
my $send_email_notification = 1;

# The mailprogram
# Most it's ok so..
my $email_program = "/usr/sbin/sendmail";

# Your email
# You must set the '@' with a backslash '\@' !
my $your_email = "thilo\@bitshelter.net";

# Email subject
my $mail_subject = "[Sengaya] New guestbook entry";

# Email message
my $mail_msg = "There is a new guestbook entry!\n";

# Thanx message, if somebody signed the guestbook.
my $thanxmsg = "Thanks for your message!";

# Will be displayed if no homepage was given.
# Leave blank to disable.
my $nohomepage = "";

#------------------------------ END CONFIGURATION -----------------------------#

# That was it!

# ---------- BEGIN MAIN

my $gdata = "gdata/";

my %params = &get_param();

if($params{'cwrite'})
  {
    &check_input($error_text,$gdata,$params{'Name'},$params{'E-Mail'},$params{'Homepage'},$params{'Text'},$force_name,$force_email,$force_homepage,$force_text);
    my $timestring = &get_timestring();
    if(&write_message($gdata,$allowhtml,$set_link,$wrap_at,$params{'Name'},$params{'E-Mail'},$params{'Homepage'},$params{'Text'},$nohomepage,$find_duplicate,$timestring,$convert_email))
      {
	unless(&count_message($gdata)) { &common_error('count_message'); }
	if($logging)
	  { unless(&write_log($gdata,$timestring)) { &common_error('write_log'); } }
	if($send_email_notification)
	  {
	    unless(&send_mail_notification($email_program,$your_email,$mail_subject,$mail_msg))
	      { &common_error('send email notification'); }
	  }
      }
  }

&print_all($gdata,$eppage,$params{'next'},$params{'cwrite'},$thanxmsg,$pagebar_length,$check_browserstring);

# ---------- END MAIN

sub print_all
  {
    my $gdata = $_[0];
    my $eppage = $_[1];
    my $cache = $_[2];
    my $cwrite = $_[3];
    my $thanxmsg = $_[4];
    my $pagebar_length = $_[5];
    my $check_browserstring = $_[6];
    my $pthanxmsg = "<p><b>".$thanxmsg."</b>";
    my $scriptname = $ENV{'SCRIPT_NAME'};
    my $number_of_entries = &get_nb_of_entries($gdata);
    my $sector = $eppage;
    if($cache) { $sector = $cache; }
    my $next = $sector+$eppage;
    my $last = $sector-$eppage;
    my $page = $sector/$eppage;
    my $pagebar = &get_pagebar($eppage,$number_of_entries,$page,$scriptname,$pagebar_length);
    my @data = &get_gdata($gdata,$number_of_entries,$sector,$eppage);
    my @template_cache = &get_data($gdata,'body_template');
    my @template_data = ("");
    my $line;
    my $blback = "";
    my $blnext = "";
    my $elback = "";
    my $elnext = "";

    if($last > 0) { $blback = "<a href=\"".$scriptname."?next=".$last."\">"; $elback = "</a>"; }
    if($sector < $number_of_entries) { $blnext = "<a href=\"".$scriptname."?next=".$next."\">"; $elnext = "</a>"; }
    if($cwrite eq "") { $thanxmsg=""; $pthanxmsg=""; }

    foreach $line(@template_cache)
      {
	$line =~ s/\%PTHANXMSG\%/$pthanxmsg/g;
	$line =~ s/\%THANXMSG\%/$thanxmsg/g;
	$line =~ s/\%NUMBERENTRIES\%/$number_of_entries/g;
	$line =~ s/\%PAGE\%/$page/g;
	$line =~ s/\%PAGEBAR\%/$pagebar/g;
	$line =~ s/\%BEGINLBACK\%/$blback/g;
	$line =~ s/\%ENDLBACK\%/$elback/g;
	$line =~ s/\%BEGINLNEXT\%/$blnext/g;
	$line =~ s/\%ENDLNEXT\%/$elnext/g;
	if($line=~ s/\%DATA\%//g)
	  { push(@template_data,@data); }
	push(@template_data,$line);
      }

    print "Content-type: text/html\n\n";
    print &get_data($gdata,'header_template');

	if($check_browserstring)
    {
       if(&check_if_browser_allowed($allowed_browsers))
	{ print @template_data; }
       else
        { print "<br><br><b><div align=\"center\">Access forbidden!</div></b><br><br>"; }
    }
	else
	{print @template_data;
	}

    print &get_data($gdata,'footer_template');
  }

sub get_param
  {
    my $data;
    my $name;
    my $value;
    my $formularfield;
    my @formularfields = ("");
    my %params;

    if($ENV{'REQUEST_METHOD'} eq 'GET')
      { $data = $ENV{'QUERY_STRING'}; }
    else
      { read(STDIN, $data, $ENV{'CONTENT_LENGTH'}); }

    @formularfields = split(/&/,$data);

    foreach $formularfield (@formularfields)
      {
	($name,$value) = split(/=/, $formularfield);
	$params{$name} = $value;
      }

    return %params;
  }

sub check_input
  {
    my $error_text = $_[0];
    my $gdata = $_[1];
    my $name = $_[2];
    my $email = &string_convert($_[3],0);
    my $homepage = &string_convert($_[4],0);
    my $text = $_[5];
    my $force_name = $_[6];
    my $force_email = $_[7];
    my $force_homepage = $_[8];
    my $force_text = $_[9];

    if((($name eq "") && $force_name) || ((($email eq "") || ($email !~ /\@/) || ($email !~ /\./)) && $force_email) || ((($homepage eq "http://") || ($homepage eq "") || ($homepage !~ /http:\/\//) || ($homepage !~ /\./)) && $force_homepage) || (($text eq "") && $force_text))
      {
	print "Content-type: text/html\n\n";
	print &get_data($gdata,'header_template');
	
	print $error_text;

	if(($name eq "") && $force_name) { print "<br><b>Name</b><br><br>\n"; }
	if((($email eq "") || ($email !~ /\@/) || ($email !~ /\./)) && $force_email) { print "<br><b>E-Mail</b><br><br>\n"; }
	if((($homepage eq "http://") || ($homepage eq "") || ($homepage !~ /http:\/\//) || ($homepage !~ /\./)) && $force_homepage) { print "<br><b>Homepage</b><br><br>\n"; }
	if(($text eq "") && $force_text) { print "<br><b>Text</b><br><br>\n"; }
	print &get_data($gdata,'footer_template');

	die;
      }
  }

sub common_error
  {
    my $catchword = $_[0];

    print "Content-type: text/html\n\n";
    print "<html>\n<head>\n<title>Guestbook error</title>\n</head>\n<body>\n";
    print "An error has accourd!<br>\nMaybe, a file couldn't be read or written?<br>\nOr another user accesses a file in the same time as you.<br><br>\nHere is a catchword:<br>\n";
    print $catchword;
    print "<br>\n</body>\n</html>\n";

    die;
  }

sub count_message
  {
    my $gdata = $_[0];
    my $filename = $gdata."count";
    my $count = &get_nb_of_entries($gdata);

    open(COUNT, ">$filename") || return 0;
    flock(COUNT,1);
    print COUNT $count+1;
    flock(COUNT,8);
    close(COUNT);

    return 1;
  }

sub write_message
  {
    my $gdata = $_[0];
    my $allowhtml = $_[1];
    my $set_link = $_[2];
    my $name = &string_convert($_[4],$allowhtml);
    my $email = &string_convert($_[5],$allowhtml);
    my $homepage = &string_convert($_[6],$allowhtml);
    my $text = &wrap_text($_[7],$_[3]);
    my $nohomepage = $_[8];
    my $find_duplicate = $_[9];
    my $timestring = $_[10];
    my $convert_email = $_[11];
    my $lhomepage;
    my $emailname;
    my $gdata_file = $gdata."data";
    my @template_cache = &get_data($gdata,'write_template');
    my @template_data = ("");
    my @data_old = ("");
    my @data = ("");
    my $line;
    my $badword;

    $text = &string_convert($text,$allowhtml);

    if($badword_filter)
      {
	foreach $badword(&get_data($gdata,'badwords'))
	  {
#	    $badword=~ s/\n//g;
#	    $badword=~ s/\r//g;
            $badword=~ s/\s//g;
	    if($badword ne "") { $text =~ s/$badword/$badword_replace/gi; }
	  }
      }

    if($convert_email && $email) { $email = &convert_to_ord($email); }

    if($email) { $emailname = "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;".$email."\">".$name."</a>"; }
    else { $emailname = $name; }

    unless(($homepage eq "") || ($homepage eq "http://"))
      { $lhomepage = "<a href=\"".$homepage."\" target=\"_blank\">".$homepage."</a>"; }
    else { $lhomepage = $nohomepage; $homepage = $nohomepage; }

    $text =~ s/\n/<br>/g;

    if($set_link)
      {
	$text =~ s/http:\/\/([a-zA-Z_0-9.\-\/\?\=\&\%]*)/<a href=\"http:\/\/$1\" target=\"_blank\">http:\/\/$1<\/a>/g;
        $text =~ s/(([a-zA-Z_0-9.\-]*)\S\@([a-zA-Z0-9\-]*)[^\@]\.([a-zA-Z]*))/<a href=\"mailto:$1\">$1<\/a>/g;
      }

    if($use_emoticons) { $text=&set_emoticons($text,$emoticon_path,$gdata); }

    foreach $line (@template_cache)
      {
	$line =~ s/\%DATE\%/\%ITTIMESTRING\%$timestring/g;
	$line =~ s/\%EMAILNAME\%/$emailname/g;
	$line =~ s/\%EMAIL\%/$email/g;
	$line =~ s/\%NAME\%/$name/g;
	$line =~ s/\%LHOMEPAGE\%/$lhomepage/g;
	$line =~ s/\%HOMEPAGE\%/$homepage/g;
	$line =~ s/\%TEXT\%/$text/g;

	push(@template_data,$line);
      }

    my $nocompare = 0;
    my $count;
    my $same = 0;
    my $duplicate = 0;

    if($find_duplicate)
      {
	@data_old = &get_data($gdata,'data');
	foreach $line (@data_old)
	  {
	    if($line=~s/§!R//g)
	      {
		if($same) { $duplicate=1; }
		else { $nocompare = 1; $same = 1; $count = 2; }
	      }
	    unless($nocompare)
	      {
		if($line !~ m/\%ITTIMESTRING\%/g)
		  {
		    unless($template_data[$count] eq $line) { $same=0; }
		  }
		$count++;
	      }
	    else { $nocompare--; }
	  }
      }

    unless($duplicate)
      {
	@data = &get_data($gdata,'data');
	unshift @template_data,"§!R\n";
	push @template_data,@data;

	unless(open(GDATA,">$gdata_file")) { &common_error('write_message'); }
	flock(GDATA,1);
	print GDATA @template_data;
	flock(GDATA,8);
	close(GDATA);
      }
    else { return 0; }

    return 1;
  }

sub get_timestring
  {
    my $now = localtime(time());
    my @now_time = split(/ +/,$now);
    my $timestring = $now_time[2].".".$now_time[1].".".$now_time[4]." ".$now_time[3];

    return $timestring;
  }

sub string_convert
  {
    my $value = $_[0];
    my $allowhtml = $_[1];

    $value =~ tr/+/ /;
    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
    $value =~ s/§!R//g;


    unless($allowhtml) { $value =~ s/<(.|\n)*>//g; }

    return $value;
  }

sub wrap_text
  {
    my $text = $_[0];
    my $wrap_at = $_[1];
    my $word;
    my @new_word = ("");
    my $wraped_word = "";
    my $new_text = "";
    my $count;

    $text =~ s/<(.|\n)*>//g;

    $text =~ s/\%0D\%0A/\%0D\%0A\+/g;

    foreach $word(split(/\+/,$text))
      {
	$count = 0;
	$new_word[$count] = $word;
	while(length($new_word[$count]) >= $wrap_at)
	  {
	    $new_word[$count+1]=substr($new_word[$count],$wrap_at,(length($new_word[$count])-$wrap_at),"");
	    $wraped_word = $wraped_word.$new_word[$count]."\n";
	    $count++;
	  }
	unless($wraped_word eq "")
	  {
	    $wraped_word = $wraped_word.$new_word[$count];
	    $word = $wraped_word;
	  }

	$new_text = $new_text.$word."+";
      }

    substr($new_text,length($new_text),1,"");

    return $new_text;
  }

sub check_if_browser_allowed
  {
    my $allowed_browsers = $_[0];
    my $browser;
    my $browserstring = $ENV{'HTTP_USER_AGENT'};
    my $allowed = 0;

    foreach $browser (@$allowed_browsers)
	{
	   if($browserstring=~ m/$browser/ig) { $allowed = 1;  }
	}

    return $allowed;
  }

sub convert_to_ord
  {
    my $text = $_[0];

    my $ctext = join ';&#', unpack('C*', $text);
    $ctext = "&#".$ctext.";";

    return $ctext;
  }

sub set_emoticons
  {
    my $text = $_[0];
    my $emoticon_path = $_[1];
    my $gdata = $_[2];
    my $line;
    my $name;
    my $value;
    my $full_path;

    foreach $line(&get_data($gdata,'usedemoticons'))
      {
    	($name,$value) = split(/ /, $line);
	$value=~ s/\s//g;
	$full_path = $emoticon_path.$value;
	if($name) { $text=~ s/\Q$name\E( |<br>|\s)/<img src=\"$full_path\" alt=\"\">$1/gs; }
      }

    return $text;
  }

sub send_mail_notification
  {
    my $mail_program = $_[0];
    my $your_email = $_[1];
    my $mail_subject = $_[2];
    my $mail_msg = $_[3];

    open(MAIL,"|$mail_program -t") || return 0;
    print MAIL "To: $your_email\n";
    print MAIL "From: $your_email\n";
    print MAIL "Subject: $mail_subject\n\n";
    print MAIL $mail_msg;
    close(MAIL);

    return 1;
  }

sub get_nb_of_entries
  {
    my $gdata = $_[0];
    my $filename = $gdata.'count';
    my $count;

    unless(open (COUNT, "<$filename")) { &common_error('get_nb_of_entries'); }
    $count = <COUNT>;
    close(COUNT);
    chomp($count);
    return $count;
  }

sub get_gdata
  {
    my $gdata = $_[0];
    my $nb_of_entries = $_[1];
    my $sector = $_[2];
    my $eppage = $_[3];
    my @data_cache = &get_data($gdata,'data');
    my @data = ("");
    my $lcount = 0;
    my $no;
    my $line;

    foreach $line (@data_cache)
      {
	if($line=~s/§!R//g)
	  {
	    $lcount++; 
	    $no=$nb_of_entries-$lcount+1;
	  }

	$line =~ s/\%NUMBER\%/$no/g;
	$line =~ s/\%ITTIMESTRING\%//g;
	$line =~ s/\%COMMENT\%//g;
	$line =~ s/\%BRCOMMENT\%//g;

	if(($lcount > $sector-$eppage) && ($lcount < $sector+1))
	  { push(@data,$line); }
      }

    return @data;
  }

sub get_data
  {
    my $gdata = $_[0];
    my $name = $_[1];
    my $filename = $gdata.$name;
    my @data = ("");

    unless(open(DATA,"<$filename")) { &common_error($filename); }
    while(<DATA>) { push(@data,$_); }
    close(DATA);

    return @data;
  }

sub get_pagebar
  {
    my $eppage = $_[0];
    my $number_of_entries = $_[1];
    my $page = $_[2];
    my $scriptname = $_[3];
    my $split = $_[4];
    my $pages;
    my $i;
    my $next;
    my $pagebar = "";
    my $space = " ";

    use integer;
    $pages = $number_of_entries/$eppage;
    if($number_of_entries % $eppage) { $pages++; }

    if($split < 3) { $split = 3; }

    unless($split % 2) { $split--; }
    $split=$split/2;

    if(($page-$split > 1) && ($pages > $split+$split+1)) { $pagebar = $pagebar.".. "; }

    for($i=1;$i<=$pages;$i++)
      {
	if($i eq $pages) { $space = ""; }

	if((($i <= $page+$split) && ($i >= $page-$split)) ||  (($page <= $split) && ($i <= $split+$split+1)) || (($page >= $pages-$split) && ($i >= -$split-$split+$pages)))
	  {
	    unless($i eq $page)
	      {
		$next = $eppage*$i;
		$pagebar = $pagebar."<a href=\"".$scriptname."?next=".$next."\">".$i."</a>".$space;
	      }
	    else { $pagebar = $pagebar.$i.$space; }
	  }	
      }

    if(($page+$split < $pages) && ($pages > $split+$split+1)) { $pagebar = $pagebar." .."; }

    return $pagebar;
  }

sub write_log
  {
    my $gdata = $_[0];
    my $timestring = $_[1];
    my $filename = $gdata.'log';
    my $remote_host = $ENV{'REMOTE_HOST'};

    open(LOG,">>$filename") || return 0;
    flock(LOG,1);
    print LOG $timestring," - IP: ",$ENV{'REMOTE_ADDR'};
    unless($remote_host eq "")
      { print LOG " DNS: ",$remote_host; }
    print LOG " BROWSER: ",$ENV{'HTTP_USER_AGENT'},"\n";
    flock(LOG,8);
    close(LOG);

    return 1;
  }
