#!/usr/pkg/bin/perl

# germ- let your ideas germinate.
# (c) 2009--11, Wesley Teal, [email protected]
# includes code and comments by [email protected]
#
# distributed under the MIT/X Consortium License, see LICENSE for details

use warnings;
use strict;
use Text::Wrap;
$Text::Wrap::columns=65;
use Fcntl qw(:flock);
use POSIX qw(strftime);

# Nix the PATH and prevent SETUID/SETGID
$ENV{PATH} = "";
$> = $<;
$) = $(;

# === Configurable Variables ===

# The directory used in gopher links, make sure not to end with a "/"
my $base_dir ="/users/USERNAME";

# Full path name to the directory you store your *.post, header.txt, and div.txt
# files in.  Make sure not to end with a "/"
my $dir = "/ftp/pub$base_dir/dat";

# The maximum number of posts you want displayed per phlog page
my $max_posts = 6;

# Permalink text. Shown as a link to individual posts.  It's a good idea to have
# this end with something about comments so that the comment count isn't just a
# random-seeming number sitting next to this link
my $permalink_text = "View Post/Comments";

# This is the text that will be displayed on the main page when you use the
# "--more--" tag in your posts.
my $more_text = "[Continued...]";

# This is printed after every comment that is left on your gopherlog.  Changing
# this after you have comments will mess up comment counts, so it's best to
# change it right away or not at all.  This also divides tags from posts.
my $cmt_div = "---";

# This scripts name, so it can get all self-referential
my $name = "germ.cgi";

# The server that is hosting the script
my $server = "sdf.lonestar.org";

# The port number the server uses; don't change this unless you know its serving
# from a different port
my $port = 70;

# === End Configurable Variables ===

my $txt_string = "\tnull\ttext\t70";
my $ver = "germ 0.4.0";
my $header = "header.txt";
my $div = "div.txt";
my $footer = "i$txt_string\r\nipowered by $ver$txt_string\r\n.\r\n";
my $com_string = "\t$base_dir/$name\t$server\t$port";
my @tag_list;

# check to see if the server is using cgi variables to communicate with germ
# or if its passing information as arguments like Bucktooth
my $query;

if (exists $ENV{QUERY_STRING}) {
   $query = $ENV{QUERY_STRING};
} else {
   $query = join(' ',@ARGV);
}

#get posts
opendir(DH, $dir) || die "3Can't open $dir. $! \r\n";
my @posts = grep(/post$/, readdir(DH));
closedir(DH);
@posts = sort { -M "$dir/$a" <=> -M "$dir/$b" } @posts;

#print main page
if (length($query) == 0) {
   if (($max_posts - 1) > $#posts) {
       $max_posts = $#posts + 1;
   }
   &print_page (@posts[0 .. ($max_posts - 1)]);
   if ($#posts > ($max_posts - 1)) {
       print "1Older Posts\t$base_dir/$name?archive1\t$server\t$port\r\n";
   }

# show individual posts/comments
} elsif ($query =~ /^\w+\.post$/) {
   my $go_string = "\t$base_dir/$name?$query\t$server\t$port";
   my $post = "$query";
   my $cmt = "$post.cmt";
   &print_file ($header);
   &print_file ($div);
   print "1Back to Main Page$com_string\r\n";
   print "i$txt_string\r\n";
   my $date = strftime ("%Y %B %d, %R", localtime((stat ("$dir/$post"))[9]));

   print "i$date$txt_string\r\n";
   print "i$txt_string\r\n";
   &print_file ($post);
   print "i$txt_string\r\n";
   print "7Leave Comment$com_string\r\n";
   $Text::Wrap::separator = "$txt_string\r\n";
   print wrap("i", "i", "To leave comment enter \"$post\" at the prompt followed by a space and then your comment of 950 characters or less."), "$txt_string\r\n";
   $Text::Wrap::separator = "\n";
   if ($tag_list[0]) {
       &print_tags (@tag_list);
   }
   &print_file ($div);
   if (-e "$dir/$cmt") {
       &print_file ("$cmt");
   }

#show archives
} elsif ($query =~ /^archive\d+$/) {
   my $num = $query;
   $num  =~ s/archive//;
   my $min = $max_posts * $num;
   my $max = $max_posts * $num + $max_posts - 1;
   if ($max > $#posts) {
       $max = $#posts;
   }
   &print_page (@posts[$min .. $max]);
   if ($num == 1) {
       print "1Newer Posts\t$base_dir/$name\t$server\t$port\r\n";
   }elsif ($num > 1) {
       my $prev_page = $num - 1;
       print "1Newer Posts\t$base_dir/$name?archive$prev_page\t$server\t$port\r\n";
   }
   if ($#posts > $max) {
       $num += 1;
       print "1Older Posts\t$base_dir/$name?archive$num\t$server\t$port\r\n";
   }

#show files containing a specific tag
} elsif ($query =~ /^tag=(\w+)/) {
   my $tag = $query;
   my @tag_files;
   $tag =~ s/^tag=//;
   $tag =~ s/_/ /g;
   foreach my $file (@posts) {
       open (FH, "< $dir/$file") || print "3Couldn't look for tags in $file! $! $txt_string";
       while (<FH>) {
           if (!/^#tags/i) {
               next;
           } elsif ($. > 2) {
               last;
           } elsif (/\b$tag/i) {
               push (@tag_files, $file);
               last;
           }
       }
       close (FH);
   }
   &print_page (@tag_files);

#get comments
}elsif (($query =~ /^\w+\.post\s[\w[:punct:]]+/) && (length($query) < 950)) {
   my $comment = $query;
   my @s_comment= (split (/ /,$comment));
   my $post;
   if ($s_comment[0] =~ /\w+\.post/) {
       $post = $s_comment[0];
   }
   my $comfile = "$dir/$post.cmt";
   my $tmp = "$comfile.tmp";
   my $go_string = "\t$base_dir/$name?$post\t$server\t$port";

   if (-e "$dir/$post") {
       open(NEW, "> $tmp");
       flock(NEW, LOCK_EX);
       my $date = strftime ("%Y %B %d, %R", localtime((stat ($tmp))[9]));
       print NEW "$date\n";
       print NEW wrap ("", "", @s_comment[1..$#s_comment]), "\n";
       print NEW "\n$cmt_div\n\n";
       if (-e $comfile) {
           open (OLD, "< $comfile");
           flock (OLD, LOCK_EX);
           while (my $line = <OLD>) {
               print NEW $line;
           }
           close OLD;
       }
       close NEW;
       rename ($tmp, $comfile) || die "Can't rename $comfile!";
       chmod 0640, $comfile;
       print "iYour comment has been posted.$txt_string\r\n";
       print "1View your comment$go_string\r\n";
   } else {
       print "3Sorry, $post does not exist.\t$txt_string\r\n";
       print "1Back to Main Page$com_string\r\n";
   }
} elsif (($query =~ /\w+\.post\s[\w[:punct:]]+/) && (length($query) > 950)) {
   print "3Sorry, comment should be less than 950 characters.$txt_string\r\n";
   print "1Back to Main Page$com_string\r\n";

#print error page
} elsif (($query !~ /\w+\.post\s[\w[:punct:]]/) && ($query !~ /archive\d/) && (length($query) > 0)) {
   print "3Oh shoot, something's gone wrong.$txt_string\r\n";
   print "1Back to Main Page$com_string\r\n";
}
print $footer;

#Here there be subroutines

#print individual posts as well as header and div files
sub print_file {
   my $file = "$dir/$_[0]";
   if (-s $file) {
       open (FH, "< $file") || print "3Can't Open $file! $! $txt_string\r\n";
       if ($file =~ /txt$/) {
           while (<FH>) {
               chomp;
               print "i$_$txt_string\r\n";
           }
       } else {
           @tag_list = "";
           while (<FH>) {
               chomp;
               if ($_ =~ s/^#tags\s+//i) {
                   @tag_list = split (/\s*,\s*/, $_);
               } elsif ( $_ =~ /^[0-9ceghisIMT].+?\t/ ) {
                   print "$_\r\n";
               } elsif ($_ =~ /^--more--$/)  {
                   if ($query =~ /^\w+\.post$/) {
                       next;
                   } else {
                       print "i$more_text$txt_string\r\n";
                       last;
                   }
               } else {
                   print "i$_$txt_string\r\n";
               }
           }
        }
        close (FH);
    }
}

#print pages of posts (main page and archive pages).  Counts comments as well.
sub print_page {
   my @print_list = @_;
   &print_file ($header);
   &print_file ($div);
   foreach my $file (@print_list) {
       my $cmt_count = 0;
       my @p_name = split /\//, $file;
       my $p = 0;
       until ($p_name[$p] =~ /post$/) {
           $p++;
       }
       my $go_string = "\t$base_dir/$name?$p_name[$p]\t$server\t$port";
       my $date = strftime ("%Y %B %d, %R", localtime((stat ("$dir/$file"))[9]));

       print "i$date$txt_string\r\n";
       print "i$txt_string\r\n";
       &print_file ($file);
       if (-e "$dir/$file.cmt") {
           open (FH, "< $dir/$file.cmt") || print "3Can't count comments for $file! $! $txt_string\r\n";
           while (<FH>) {
               chomp;
               if ($_ =~ /$cmt_div/) {
                   $cmt_count += 1;
               }
           }
           close (FH);
       }
       print "1$permalink_text($cmt_count)$go_string\r\n";
       if ($tag_list[0]) {
           &print_tags (@tag_list);
       }
       &print_file ($div);
   }
}

#print tag list
sub print_tags {
   my @tags = @_;
   print "i$cmt_div$txt_string\r\n";
   print "iTags:$txt_string\r\n";
   foreach my $tag (@tags) {
       my $tag_link = $tag;
       $tag_link =~ s/\s+/_/g;
       print "1$tag\t$base_dir/$name?tag=$tag_link\t$server\t$port\r\n";
   }
}