This repository has been archived on 2025-02-01. You can view files and clone it, but cannot push or open issues or pull requests.
perl-gopher-server/gopher.pl
2010-09-14 18:04:20 +02:00

218 lines
5.8 KiB
Perl

#!/usr/bin/perl
#####################################
# Thomas Schwery <thomas@schwery.me>
####################################
#
# TODO:
# - Support for a list of tags
# - Default port if failure
# - ...
use strict;
use IO::Socket;
use Fcntl ':mode';
use Privileges::Drop;
use HTML::Parse;
use HTML::FormatText;
use Text::Wrap;
use Date::Parse;
use Date::Format;
use threads;
use Clone qw(clone);
our ($port, $basedir, $server) = @ARGV;
print "Port is " . $port . "\n";
print "Base dir is " . $basedir . "\n";
$SIG{INT} = \&signal_handler_interrupt;
$SIG{PIPE} = \&signal_handler_pipe;
my $socket = new IO::Socket::INET(LocalPort => $port,
Proto => 'tcp',
Listen => '1',
Reuse => 1);
die "Unable to open port: $! \n" unless $socket;
drop_privileges('nobody');
our %tags_entries;
while (1) {
my $listen_socket = $socket->accept();
my $thr = threads->create(\&connection_thread, $listen_socket);
$thr->detach();
}
sub connection_thread {
my ($listen_socket) = @_;
$_ = <$listen_socket>;
s/\r*\n*//g;
s/\t.*//g; # Don't care about the parameters now ...
my $directory = $_;
$directory =~ s/^blog\///;
$directory =~ s/[.\/]//g;
$directory = $basedir . "/" . $directory;
$directory =~ s/\/$//;
unless (-e $directory) {
print("Requested file or directory not readable : $directory \n");
$listen_socket->send("No entry found with this name ...\r\n");
$listen_socket->send(".\r\n");
return 0;
}
if (-f $directory) {
$listen_socket->send(send_blog_entry($directory));
$listen_socket->send(".\r\n");
} else {
my $header = generate_header(".");
my $blog_entries = list_blog_entries($directory);
my $tags_entries = list_entries_by_tag($directory);
$listen_socket->send($header);
$listen_socket->send("i----- Blog entries ---------------\tfake\tfake\t0\r\n");
$listen_socket->send($blog_entries);
$listen_socket->send("i----- Blog entries by tags---------------\tfake\tfake\t0\r\n");
$listen_socket->send($tags_entries);
$listen_socket->send(".\r\n");
}
$listen_socket->close();
return 0;
}
sub generate_header {
my ($directory) = @_;
$directory .= "/" if ($directory);
return "" unless (-T $directory . "header");
my $message = "";
open FILE, $directory . "header";
while (<FILE>) {
s/\r*\n*//g;
$message .= "i" . $_ . "\tfake\tfake\t0\r\n";
}
close FILE;
#$message .= "0About this gopher server\theader\t" . $server . "\t" . $port . "\r\n";
return $message;
}
sub map_content {
my ($dir_entry, $directory) = @_;
$directory .= "/" if ($directory);
my $file_type = -f $directory . $dir_entry ? 0 : 1;
$file_type = 9 if (-f $directory . $dir_entry && -B $directory . $dir_entry);
return $file_type . $dir_entry . "\t" . $dir_entry . "\tlocalhost\t" . $port;
}
sub filter_content {
my ($dir_entry, $blog_dir) = @_;
return 0 unless -r $blog_dir."/".$dir_entry;
return 1 unless (substr($dir_entry,0,1) eq "." || $dir_entry eq "header");
return 0;
}
sub list_blog_entries {
my ($blog_dir) = @_;
unless (opendir(IMD, $blog_dir)) {
return "";
}
my @files = readdir(IMD);
closedir(IMD);
@files = grep filter_content($_, $blog_dir), @files;
@files = map map_blog_entry($_, $blog_dir), @files;
@files = sort sort_entry_line($a,$b), @files;
return join "\r\n", @files;
}
sub list_entries_by_tag {
my $entries;
my %tags_entries_local = %{ clone (\%tags_entries) };
foreach my $tag (sort keys %tags_entries_local) {
$entries .= "i----- " . $tag . " ---------------\tfake\tfake\t0\r\n";
foreach my $entry (@{$tags_entries_local{$tag}}) {
$entry =~ m/^(.*)\/(.*)$/;
my $entry_dir = $1;
my $entry_name = $2;
$entries .= map_blog_entry($entry_name, $entry_dir) . "\r\n";
}
}
return $entries;
}
sub sort_entry_line {
my $a_date = $2 if ($a =~ m/^0(\[.*\]\s)?(.*?)\s*-.*$/);
my $b_date = $2 if ($b =~ m/^0(\[.*\]\s)?(.*?)\s*-.*$/);
return -(str2time($a_date) <=> str2time($b_date));
}
sub read_entry_file {
my ($entry_file) = @_;
my $subject = "";
my $date_file = "";
my $tags = "";
open ENTRY, $entry_file;
while (<ENTRY>) {
s/\r*\n*//g;
$subject = $1 if (m/^Subject:\s*(.*)$/);
$tags = $1 if (m/^Tags:\s*(.*)$/);
$date_file = $1 if (m/^Date:\s*(.*)$/);
}
close ENTRY;
my @tag_list = split ',',$tags;
foreach my $tag (@tag_list) {
push @{ $tags_entries{$tag} }, $entry_file;
}
my $time = str2time($date_file);
my $date = time2str("%o %h %Y", $time);
return "[" . $tags . "] " . $date . " - " . $subject if($tags);
return $date. " - " . $subject;
}
sub map_blog_entry {
my ($entry_file, $entry_dir) = @_;
my $entry_title = read_entry_file($entry_dir . "/" . $entry_file);
return "0" . $entry_title . "\t" . "blog/" . $entry_file . "\t" . $server . "\t" . $port;
}
sub send_blog_entry {
my ($entry_file) = @_;
open FILE, $entry_file;
my $entry_text = "";
while (<FILE>) {
$entry_text .= $_;
}
$entry_text =~ s/\r?\n/<br>/mg;
my $plain_text = HTML::FormatText->new->format(parse_html($entry_text));
$Text::Wrap::columns = 80;
$plain_text = wrap("\t", "", $plain_text);
return $plain_text;
}
sub signal_handler_interrupt {
print "ctrl-c, exiting...\n";
$socket->shutdown(2);
$socket->close();
exit 0;
}
sub signal_handler_pipe {
print "sigpipe, don't care\n";
return 0;
}