2017-12-27 15:13:58 +01:00
|
|
|
|
#!/usr/bin/env perl
|
2019-05-04 18:33:36 +02:00
|
|
|
|
# Copyright (C) 2017–2019 Alex Schroeder <alex@gnu.org>
|
2017-12-27 09:45:43 +01:00
|
|
|
|
|
|
|
|
|
|
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
2018-01-01 16:14:59 +01:00
|
|
|
|
package OddMuse;
|
2017-12-27 09:45:43 +01:00
|
|
|
|
use strict;
|
|
|
|
|
|
use 5.10.0;
|
2018-01-09 17:59:24 +01:00
|
|
|
|
use base qw(Net::Server::Fork); # any personality will do
|
2017-12-31 14:32:39 +01:00
|
|
|
|
use MIME::Base64;
|
2018-02-01 23:22:01 +01:00
|
|
|
|
use Text::Wrap;
|
2018-02-07 14:37:34 +01:00
|
|
|
|
use List::Util qw(first);
|
2018-07-16 09:37:39 +02:00
|
|
|
|
use Socket;
|
2017-12-30 17:26:41 +01:00
|
|
|
|
|
2018-09-10 11:29:56 +02:00
|
|
|
|
our($RunCGI, $DataDir, %IndexHash, @IndexList, $IndexFile, $TagFile, $q,
|
|
|
|
|
|
%Page, $OpenPageName, $MaxPost, $ShowEdits, %Locks, $CommentsPattern,
|
|
|
|
|
|
$CommentsPrefix, $EditAllowed, $NoEditFile, $SiteName, $ScriptName,
|
2018-09-10 12:15:02 +02:00
|
|
|
|
$Now, %RecentVisitors, $SurgeProtectionTime, $SurgeProtectionViews,
|
|
|
|
|
|
$SurgeProtection);
|
2017-12-30 17:26:41 +01:00
|
|
|
|
|
2018-02-13 09:30:42 +01:00
|
|
|
|
my $external_image_path = '/home/alex/alexschroeder.ch/pics/';
|
|
|
|
|
|
|
2018-01-29 11:41:15 +01:00
|
|
|
|
# Sadly, we need this information before doing anything else
|
2018-01-21 19:44:07 +01:00
|
|
|
|
my %args = (proto => 'ssl');
|
|
|
|
|
|
for (grep(/--wiki_(key|cert)_file=/, @ARGV)) {
|
|
|
|
|
|
$args{SSL_cert_file} = $1 if /--wiki_cert_file=(.*)/;
|
|
|
|
|
|
$args{SSL_key_file} = $1 if /--wiki_key_file=(.*)/;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($args{SSL_cert_file} and not $args{SSL_key_file}
|
|
|
|
|
|
or not $args{SSL_cert_file} and $args{SSL_key_file}) {
|
|
|
|
|
|
die "I must have both --wiki_key_file and --wiki_cert_file\n";
|
|
|
|
|
|
} elsif ($args{SSL_cert_file} and $args{SSL_key_file}) {
|
|
|
|
|
|
OddMuse->run(%args);
|
2018-01-19 12:21:40 +01:00
|
|
|
|
} else {
|
|
|
|
|
|
OddMuse->run;
|
|
|
|
|
|
}
|
2018-01-09 17:59:24 +01:00
|
|
|
|
|
|
|
|
|
|
sub options {
|
|
|
|
|
|
my $self = shift;
|
|
|
|
|
|
my $prop = $self->{'server'};
|
|
|
|
|
|
my $template = shift;
|
|
|
|
|
|
|
|
|
|
|
|
# setup options in the parent classes
|
|
|
|
|
|
$self->SUPER::options($template);
|
|
|
|
|
|
|
|
|
|
|
|
# add a single value option
|
|
|
|
|
|
$prop->{wiki} ||= undef;
|
|
|
|
|
|
$template->{wiki} = \$prop->{wiki};
|
|
|
|
|
|
|
|
|
|
|
|
$prop->{wiki_dir} ||= undef;
|
|
|
|
|
|
$template->{wiki_dir} = \$prop->{wiki_dir};
|
|
|
|
|
|
|
|
|
|
|
|
$prop->{wiki_pages} ||= [];
|
|
|
|
|
|
$template->{wiki_pages} = $prop->{wiki_pages};
|
2018-01-19 12:21:40 +01:00
|
|
|
|
|
2018-02-07 14:37:34 +01:00
|
|
|
|
$prop->{menu} ||= [];
|
|
|
|
|
|
$template->{menu} = $prop->{menu};
|
|
|
|
|
|
|
|
|
|
|
|
$prop->{menu_file} ||= [];
|
|
|
|
|
|
$template->{menu_file} = $prop->{menu_file};
|
|
|
|
|
|
|
2018-01-19 12:21:40 +01:00
|
|
|
|
# $prop->{wiki_pem_file} ||= undef;
|
|
|
|
|
|
# $template->{wiki_pem_file} = $prop->{wiki_pem_file};
|
2018-01-09 17:59:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub post_configure_hook {
|
|
|
|
|
|
my $self = shift;
|
|
|
|
|
|
$self->write_help if $ARGV[0] eq '--help';
|
|
|
|
|
|
|
|
|
|
|
|
$DataDir = $self->{server}->{wiki_dir} || $ENV{WikiDataDir} || '/tmp/oddmuse';
|
2018-09-10 11:29:56 +02:00
|
|
|
|
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->log(3, "PID $$");
|
|
|
|
|
|
$self->log(3, "Host " . ("@{$self->{server}->{host}}" || "*"));
|
|
|
|
|
|
$self->log(3, "Port @{$self->{server}->{port}}");
|
2019-03-09 23:41:20 +01:00
|
|
|
|
|
|
|
|
|
|
# Note: if you use sudo to run gopher-server.pl, these options might not work!
|
|
|
|
|
|
$self->log(4, "--wikir_dir says $self->{server}->{wiki_dir}\n");
|
|
|
|
|
|
$self->log(4, "\$WikiDataDir says $ENV{WikiDataDir}\n");
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->log(3, "Wiki data dir is $DataDir\n");
|
|
|
|
|
|
|
|
|
|
|
|
$RunCGI = 0;
|
|
|
|
|
|
my $wiki = $self->{server}->{wiki} || "./wiki.pl";
|
|
|
|
|
|
$self->log(1, "Running $wiki\n");
|
|
|
|
|
|
unless (my $return = do $wiki) {
|
|
|
|
|
|
$self->log(1, "couldn't parse wiki library $wiki: $@") if $@;
|
|
|
|
|
|
$self->log(1, "couldn't do wiki library $wiki: $!") unless defined $return;
|
|
|
|
|
|
$self->log(1, "couldn't run wiki library $wiki") unless $return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# make sure search is sorted newest first because NewTagFiltered resorts
|
|
|
|
|
|
*OldGopherFiltered = \&Filtered;
|
|
|
|
|
|
*Filtered = \&NewGopherFiltered;
|
2018-07-16 09:37:39 +02:00
|
|
|
|
*ReportError = sub {
|
|
|
|
|
|
my ($error, $status, $log, @html) = @_;
|
|
|
|
|
|
$self->print_error("Error: $error");
|
|
|
|
|
|
map { ReleaseLockDir($_); } keys %Locks;
|
|
|
|
|
|
exit 2;
|
|
|
|
|
|
};
|
2018-01-09 17:59:24 +01:00
|
|
|
|
}
|
2017-12-30 17:26:41 +01:00
|
|
|
|
|
2018-01-01 16:14:59 +01:00
|
|
|
|
my $usage = << 'EOT';
|
2017-12-27 11:11:06 +01:00
|
|
|
|
This server serves a wiki as a gopher site.
|
2017-12-27 09:45:43 +01:00
|
|
|
|
|
2018-01-09 17:59:24 +01:00
|
|
|
|
It implements Net::Server and thus all the options available to
|
|
|
|
|
|
Net::Server are also available here. Additional options are available:
|
|
|
|
|
|
|
|
|
|
|
|
wiki - this is the path to the Oddmuse script
|
|
|
|
|
|
wiki_dir - this is the path to the Oddmuse data directory
|
|
|
|
|
|
wiki_pages - this is a page to show on the entry menu
|
2018-02-07 14:37:34 +01:00
|
|
|
|
menu - this is the description of a gopher menu to prepend
|
|
|
|
|
|
menu_file - this is the filename of the gopher menu to prepend
|
2018-01-21 19:44:07 +01:00
|
|
|
|
wiki_cert_file - the filename containing a certificate in PEM format
|
|
|
|
|
|
wiki_key_file - the filename containing a private key in PEM format
|
2018-01-09 17:59:24 +01:00
|
|
|
|
|
|
|
|
|
|
For many of the options, more information can be had in the Net::Server
|
|
|
|
|
|
documentation. This is important if you want to daemonize the server. You'll
|
|
|
|
|
|
need to use --pid_file so that you can stop it using a script, --setsid to
|
2019-03-09 23:41:20 +01:00
|
|
|
|
daemonize it, --log_file to write keep logs, and you'll need to set the user or
|
2018-01-09 17:59:24 +01:00
|
|
|
|
group using --user or --group such that the server has write access to the data
|
|
|
|
|
|
directory.
|
2018-01-01 16:14:59 +01:00
|
|
|
|
|
2018-01-09 17:59:24 +01:00
|
|
|
|
For testing purposes, you can start with the following:
|
|
|
|
|
|
|
|
|
|
|
|
--port=7070
|
2018-01-02 11:55:46 +01:00
|
|
|
|
The port to listen to, defaults to a random port.
|
2018-01-09 17:59:24 +01:00
|
|
|
|
--log_level=4
|
|
|
|
|
|
The log level to use, defaults to 2.
|
|
|
|
|
|
--wiki_dir=/var/oddmuse
|
|
|
|
|
|
The wiki directory, defaults to the value of the "WikiDataDir" environment
|
|
|
|
|
|
variable or "/tmp/oddmuse".
|
2018-01-01 16:14:59 +01:00
|
|
|
|
--wiki_lib=/home/alex/src/oddmuse/wiki.pl
|
2018-01-09 17:59:24 +01:00
|
|
|
|
The Oddmuse main script, defaults to "./wiki.pl".
|
2018-01-01 16:14:59 +01:00
|
|
|
|
--wiki_pages=SiteMap
|
|
|
|
|
|
This adds a page to the main index. Can be used multiple times.
|
|
|
|
|
|
--help
|
|
|
|
|
|
Prints this message.
|
|
|
|
|
|
|
2017-12-27 11:11:06 +01:00
|
|
|
|
Example invocation:
|
2017-12-27 09:45:43 +01:00
|
|
|
|
|
2017-12-27 15:13:58 +01:00
|
|
|
|
/home/alex/src/oddmuse/stuff/gopher-server.pl \
|
2018-01-01 16:14:59 +01:00
|
|
|
|
--port=7070 \
|
2017-12-27 15:13:58 +01:00
|
|
|
|
--wiki=/home/alex/src/oddmuse/wiki.pl \
|
2018-01-09 17:59:24 +01:00
|
|
|
|
--pid_file=/tmp/oddmuse/gopher.pid \
|
2017-12-27 15:26:32 +01:00
|
|
|
|
--wiki_dir=/tmp/oddmuse \
|
|
|
|
|
|
--wiki_pages=Homepage \
|
2018-01-09 17:59:24 +01:00
|
|
|
|
--wiki_pages=Gopher
|
2017-12-27 11:11:06 +01:00
|
|
|
|
|
|
|
|
|
|
Run the script and test it:
|
|
|
|
|
|
|
2018-01-02 11:55:46 +01:00
|
|
|
|
echo | nc localhost 7070
|
2017-12-27 11:11:06 +01:00
|
|
|
|
lynx gopher://localhost:7070
|
2017-12-27 09:45:43 +01:00
|
|
|
|
|
2018-01-21 19:44:07 +01:00
|
|
|
|
If you want to use SSL, you need to provide PEM files containing certificate and
|
|
|
|
|
|
private key. To create self-signed files, for example:
|
2018-01-19 12:21:40 +01:00
|
|
|
|
|
|
|
|
|
|
openssl req -new -x509 -days 365 -nodes -out \
|
2018-01-21 19:44:07 +01:00
|
|
|
|
gopher-server-cert.pem -keyout gopher-server-key.pem
|
2018-01-19 12:21:40 +01:00
|
|
|
|
|
|
|
|
|
|
Make sure the common name you provide matches your domain name!
|
|
|
|
|
|
|
2018-02-07 14:37:34 +01:00
|
|
|
|
Note that parameters should not contain spaces. Thus:
|
|
|
|
|
|
|
|
|
|
|
|
/home/alex/src/oddmuse/stuff/gopher-server.pl \
|
|
|
|
|
|
--port=7070 \
|
|
|
|
|
|
--log_level=3 \
|
|
|
|
|
|
--wiki=/home/alex/src/oddmuse/wiki.pl \
|
|
|
|
|
|
--wiki_dir=/home/alex/alexschroeder \
|
|
|
|
|
|
--menu=Moku_Pona_Updates \
|
|
|
|
|
|
--menu_file=~/.moku-pona/updates.txt \
|
|
|
|
|
|
--menu=Moku_Pona_Sites \
|
|
|
|
|
|
--menu_file=~/.moku-pona/sites.txt
|
|
|
|
|
|
|
2017-12-27 09:45:43 +01:00
|
|
|
|
EOT
|
2018-01-01 16:14:59 +01:00
|
|
|
|
|
|
|
|
|
|
run();
|
|
|
|
|
|
|
|
|
|
|
|
sub NewGopherFiltered {
|
|
|
|
|
|
my @pages = OldGopherFiltered(@_);
|
|
|
|
|
|
@pages = sort newest_first @pages;
|
|
|
|
|
|
return @pages;
|
2017-12-27 09:45:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-04 12:15:04 +02:00
|
|
|
|
sub normal_to_free {
|
|
|
|
|
|
my $title = shift;
|
|
|
|
|
|
$title =~ s/_/ /g;
|
|
|
|
|
|
return $title;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-05-04 18:33:36 +02:00
|
|
|
|
sub free_to_normal {
|
|
|
|
|
|
my $title = shift;
|
|
|
|
|
|
$title =~ s/^ +//g;
|
|
|
|
|
|
$title =~ s/ +$//g;
|
|
|
|
|
|
$title =~ s/ +/_/g;
|
|
|
|
|
|
return $title;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-02 12:42:29 +01:00
|
|
|
|
sub print_text {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2018-01-02 12:42:29 +01:00
|
|
|
|
my $text = shift;
|
2018-01-09 17:59:24 +01:00
|
|
|
|
print($text); # bytes
|
2018-01-02 12:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-04 13:40:03 +01:00
|
|
|
|
sub print_menu {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2018-01-04 13:40:03 +01:00
|
|
|
|
my $display = shift;
|
2018-01-15 13:30:19 +01:00
|
|
|
|
my $selector = shift;
|
2018-01-15 14:38:50 +01:00
|
|
|
|
my $host = shift
|
|
|
|
|
|
|| $self->{server}->{host}->[0]
|
|
|
|
|
|
|| $self->{server}->{sockaddr};
|
|
|
|
|
|
my $port = shift
|
|
|
|
|
|
|| $self->{server}->{port}->[0]
|
|
|
|
|
|
|| $self->{server}->{sockport};
|
2018-02-12 14:45:39 +01:00
|
|
|
|
my $encoded = shift;
|
2018-01-15 13:30:19 +01:00
|
|
|
|
|
2018-02-12 14:45:39 +01:00
|
|
|
|
$selector = join('/', map { UrlEncode($_) } split(/\//, $selector)) unless $encoded;
|
2018-01-15 14:38:50 +01:00
|
|
|
|
$self->print_text(join("\t", $display, $selector, $host, $port)
|
2018-01-09 21:13:21 +01:00
|
|
|
|
. "\r\n");
|
2018-01-04 13:40:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-05 23:56:51 +01:00
|
|
|
|
sub print_info {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2018-01-05 23:56:51 +01:00
|
|
|
|
my $info = shift;
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_menu("i$info", "");
|
2018-01-05 23:56:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-06 21:58:03 +01:00
|
|
|
|
sub print_error {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2018-01-06 21:58:03 +01:00
|
|
|
|
my $error = shift;
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_menu("3$error", "");
|
2018-01-06 21:58:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-28 12:16:25 +01:00
|
|
|
|
sub serve_main_menu {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2018-02-01 23:22:01 +01:00
|
|
|
|
my $more = shift;
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->log(3, "Serving main menu");
|
|
|
|
|
|
$self->print_info("Welcome to the Gopher version of this wiki.");
|
2018-02-01 23:22:01 +01:00
|
|
|
|
$self->print_info("");
|
|
|
|
|
|
|
|
|
|
|
|
$self->print_info("Phlog:");
|
|
|
|
|
|
my @pages = sort { $b cmp $a } grep(/^\d\d\d\d-\d\d-\d\d/, @IndexList);
|
2018-09-10 11:29:56 +02:00
|
|
|
|
# we should check for pages marked for deletion!
|
2018-02-01 23:22:01 +01:00
|
|
|
|
for my $id (@pages[0..9]) {
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("1" . normal_to_free($id), free_to_normal($id) . "/menu");
|
2018-02-01 23:22:01 +01:00
|
|
|
|
}
|
|
|
|
|
|
$self->print_menu("1" . "More...", "do/more");
|
|
|
|
|
|
$self->print_info("");
|
2017-12-28 21:40:21 +01:00
|
|
|
|
|
2018-01-09 17:59:24 +01:00
|
|
|
|
for my $id (@{$self->{server}->{wiki_pages}}) {
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("1" . normal_to_free($id), free_to_normal($id) . "/menu");
|
2017-12-28 12:16:25 +01:00
|
|
|
|
}
|
2017-12-28 21:40:21 +01:00
|
|
|
|
|
2018-02-07 14:37:34 +01:00
|
|
|
|
for my $id (@{$self->{server}->{menu}}) {
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("1" . normal_to_free($id), "map/" . free_to_normal($id));
|
2018-02-07 14:37:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_menu("1" . "Recent Changes", "do/rc");
|
2018-02-18 00:03:29 +01:00
|
|
|
|
$self->print_menu("0" . "Gopher RSS", "do/rss");
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_menu("7" . "Find matching page titles", "do/match");
|
|
|
|
|
|
$self->print_menu("7" . "Full text search", "do/search");
|
|
|
|
|
|
$self->print_menu("1" . "Index of all pages", "do/index");
|
2017-12-28 21:40:21 +01:00
|
|
|
|
|
2018-01-01 16:14:59 +01:00
|
|
|
|
if ($TagFile) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_menu("1" . "Index of all tags", "do/tags");
|
2017-12-28 21:40:21 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-03 14:53:30 +01:00
|
|
|
|
if ($EditAllowed and not IsFile($NoEditFile)) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_menu("w" . "New page", "do/new");
|
2018-01-03 14:53:30 +01:00
|
|
|
|
}
|
2018-02-01 23:22:01 +01:00
|
|
|
|
}
|
2018-01-03 14:53:30 +01:00
|
|
|
|
|
2018-02-01 23:22:01 +01:00
|
|
|
|
sub serve_phlog_archive {
|
|
|
|
|
|
my $self = shift;
|
|
|
|
|
|
$self->log(3, "Serving phlog archive");
|
2018-01-01 16:14:59 +01:00
|
|
|
|
my @pages = sort { $b cmp $a } grep(/^\d\d\d\d-\d\d-\d\d/, @IndexList);
|
2017-12-29 15:18:55 +01:00
|
|
|
|
for my $id (@pages) {
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("1" . normal_to_free($id), free_to_normal($id) . "/menu");
|
2017-12-28 21:40:21 +01:00
|
|
|
|
}
|
2017-12-28 12:16:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub serve_index {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
|
|
|
|
|
$self->log(3, "Serving index of all pages");
|
2018-01-01 16:14:59 +01:00
|
|
|
|
for my $id (sort newest_first @IndexList) {
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("1" . normal_to_free($id), free_to_normal($id) . "/menu");
|
2017-12-28 12:16:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-29 11:37:56 +01:00
|
|
|
|
sub serve_match {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-29 11:37:56 +01:00
|
|
|
|
my $match = shift;
|
2018-01-29 11:41:55 +01:00
|
|
|
|
$self->log(3, "Serving pages matching " . UrlEncode($match));
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_info("Use a regular expression to match page titles.");
|
|
|
|
|
|
$self->print_info("Spaces in page titles are underlines, '_'.");
|
2018-01-01 16:14:59 +01:00
|
|
|
|
for my $id (sort newest_first grep(/$match/i, @IndexList)) {
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu( "1" . normal_to_free($id), free_to_normal($id) . "/menu");
|
2017-12-29 11:37:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub serve_search {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-29 11:37:56 +01:00
|
|
|
|
my $str = shift;
|
2018-01-29 11:41:55 +01:00
|
|
|
|
$self->log(3, "Serving search result for " . UrlEncode($str));
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_info("Use regular expressions separated by spaces.");
|
2018-01-01 16:14:59 +01:00
|
|
|
|
SearchTitleAndBody($str, sub {
|
2017-12-29 11:37:56 +01:00
|
|
|
|
my $id = shift;
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("1" . normal_to_free($id), free_to_normal($id) . "/menu");
|
2017-12-29 11:37:56 +01:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-28 21:40:21 +01:00
|
|
|
|
sub serve_tags {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
|
|
|
|
|
$self->log(3, "Serving tag cloud");
|
2017-12-28 21:40:21 +01:00
|
|
|
|
# open the DB file
|
2018-01-01 16:14:59 +01:00
|
|
|
|
my %h = TagReadHash();
|
2017-12-28 21:40:21 +01:00
|
|
|
|
my %count = ();
|
|
|
|
|
|
foreach my $tag (grep !/^_/, keys %h) {
|
|
|
|
|
|
$count{$tag} = @{$h{$tag}};
|
|
|
|
|
|
}
|
|
|
|
|
|
foreach my $id (sort { $count{$b} <=> $count{$a} } keys %count) {
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("1" . normal_to_free($id), free_to_normal($id) . "/tag");
|
2017-12-28 21:40:21 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-29 15:18:55 +01:00
|
|
|
|
sub serve_rc {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2018-01-01 16:14:59 +01:00
|
|
|
|
my $showedit = $ShowEdits = shift;
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->log(3, "Serving recent changes"
|
2018-01-01 16:14:59 +01:00
|
|
|
|
. ($showedit ? " including minor changes" : ""));
|
2017-12-30 17:26:41 +01:00
|
|
|
|
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_info("Recent Changes");
|
2017-12-29 15:28:10 +01:00
|
|
|
|
if ($showedit) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_menu("1" . "Skip minor edits", "do/rc");
|
2017-12-29 15:28:10 +01:00
|
|
|
|
} else {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_menu("1" . "Show minor edits", "do/rc/showedits");
|
2017-12-29 15:28:10 +01:00
|
|
|
|
}
|
2017-12-29 23:20:47 +01:00
|
|
|
|
|
2018-01-01 16:14:59 +01:00
|
|
|
|
ProcessRcLines(
|
2017-12-29 15:18:55 +01:00
|
|
|
|
sub {
|
|
|
|
|
|
my $date = shift;
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_info("");
|
|
|
|
|
|
$self->print_info("$date");
|
|
|
|
|
|
$self->print_info("");
|
2017-12-29 15:18:55 +01:00
|
|
|
|
},
|
|
|
|
|
|
sub {
|
2018-01-03 08:53:55 +01:00
|
|
|
|
my($id, $ts, $author_host, $username, $summary, $minor, $revision,
|
2017-12-29 15:18:55 +01:00
|
|
|
|
$languages, $cluster, $last) = @_;
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("1" . normal_to_free($id), free_to_normal($id) . "/menu");
|
2018-02-01 23:22:01 +01:00
|
|
|
|
for my $line (split(/\n/, wrap(' ', ' ', $summary))) {
|
|
|
|
|
|
$self->print_info($line);
|
|
|
|
|
|
}
|
2017-12-29 15:18:55 +01:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-18 00:03:29 +01:00
|
|
|
|
sub serve_rss {
|
|
|
|
|
|
my $self = shift;
|
|
|
|
|
|
$self->log(3, "Serving Gopher RSS");
|
|
|
|
|
|
my $host = shift
|
|
|
|
|
|
|| $self->{server}->{host}->[0]
|
|
|
|
|
|
|| $self->{server}->{sockaddr};
|
|
|
|
|
|
my $port = shift
|
|
|
|
|
|
|| $self->{server}->{port}->[0]
|
|
|
|
|
|
|| $self->{server}->{sockport};
|
|
|
|
|
|
my $gopher = "gopher://$host:$port/"; # use gophers for TLS?
|
|
|
|
|
|
local $ScriptName = $gopher;
|
|
|
|
|
|
my $rss = GetRcRss();
|
|
|
|
|
|
$rss =~ s!$ScriptName\?action=rss!${gopher}1do/rss!g;
|
|
|
|
|
|
$rss =~ s!$ScriptName\?action=history;id=([^[:space:]<]*)!${gopher}1$1/history!g;
|
|
|
|
|
|
$rss =~ s!$ScriptName/([^[:space:]<]*)!${gopher}0$1!g;
|
|
|
|
|
|
$rss =~ s!<wiki:diff>.*</wiki:diff>\n!!g;
|
|
|
|
|
|
print $rss;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-07 14:37:34 +01:00
|
|
|
|
sub serve_map {
|
|
|
|
|
|
my $self = shift;
|
|
|
|
|
|
my $id = shift;
|
|
|
|
|
|
$self->log(3, "Serving map " . UrlEncode($id));
|
|
|
|
|
|
my @menu = @{$self->{server}->{menu}};
|
|
|
|
|
|
my $i = first { $id eq $menu[$_] } 0..$#menu;
|
|
|
|
|
|
my $file = $self->{server}->{menu_file}->[$i];
|
|
|
|
|
|
if (-f $file and open(my $fh, '<:encoding(UTF-8)', $file)) {
|
|
|
|
|
|
local $/ = undef;
|
|
|
|
|
|
my $text = <$fh>;
|
|
|
|
|
|
$self->log(4, "Map has " . length($text) . " characters");
|
|
|
|
|
|
$self->print_text($text);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$self->log(1, "Error reading $file");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-05 10:57:53 +01:00
|
|
|
|
sub serve_page_comment_link {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2018-01-05 10:57:53 +01:00
|
|
|
|
my $id = shift;
|
|
|
|
|
|
my $revision = shift;
|
|
|
|
|
|
if (not $revision and $CommentsPattern) {
|
|
|
|
|
|
if ($id =~ /$CommentsPattern/) {
|
|
|
|
|
|
my $original = $1;
|
2018-01-05 11:05:43 +01:00
|
|
|
|
# sometimes we are on a comment page and cannot derive the original
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_menu("1" . "Back to the original page",
|
2018-01-05 11:05:43 +01:00
|
|
|
|
"$original/menu") if $original;
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("w" . "Add a comment", free_to_normal($id) . "/append/text");
|
2018-01-05 10:57:53 +01:00
|
|
|
|
} else {
|
2019-05-04 18:33:36 +02:00
|
|
|
|
my $comments = free_to_normal($CommentsPrefix . $id);
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_menu("1" . "Comments on this page", "$comments/menu");
|
2018-01-05 10:57:53 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub serve_page_history_link {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2018-01-05 10:57:53 +01:00
|
|
|
|
my $id = shift;
|
|
|
|
|
|
my $revision = shift;
|
|
|
|
|
|
if (not $revision) {
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("1" . "Page History", free_to_normal($id) . "/history");
|
2018-01-05 10:57:53 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-28 19:09:11 +01:00
|
|
|
|
sub serve_file_page_menu {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-28 19:09:11 +01:00
|
|
|
|
my $id = shift;
|
|
|
|
|
|
my $type = shift;
|
2017-12-29 23:20:47 +01:00
|
|
|
|
my $revision = shift;
|
2017-12-28 19:09:11 +01:00
|
|
|
|
my $code = substr($type, 0, 6) eq 'image/' ? 'I' : '9';
|
2018-01-29 11:41:55 +01:00
|
|
|
|
$self->log(3, "Serving file page menu for " . UrlEncode($id));
|
2018-05-04 12:15:04 +02:00
|
|
|
|
$self->print_menu($code . normal_to_free($id)
|
2019-05-04 18:33:36 +02:00
|
|
|
|
. ($revision ? "/$revision" : ""), free_to_normal($id));
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_page_comment_link($id, $revision);
|
|
|
|
|
|
$self->serve_page_history_link($id, $revision);
|
2017-12-28 19:09:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub serve_text_page_menu {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-28 12:16:25 +01:00
|
|
|
|
my $id = shift;
|
2017-12-30 17:26:41 +01:00
|
|
|
|
my $page = shift;
|
2017-12-29 23:20:47 +01:00
|
|
|
|
my $revision = shift;
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->log(3, "Serving text page menu for $id"
|
2018-01-01 16:14:59 +01:00
|
|
|
|
. ($revision ? "/$revision" : ""));
|
2017-12-28 21:40:21 +01:00
|
|
|
|
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_info("The text of this page:");
|
2018-05-04 12:15:04 +02:00
|
|
|
|
$self->print_menu("0" . normal_to_free($id),
|
2019-05-04 18:33:36 +02:00
|
|
|
|
free_to_normal($id) . ($revision ? "/$revision" : ""));
|
2018-05-04 12:15:04 +02:00
|
|
|
|
$self->print_menu("h" . normal_to_free($id),
|
2019-05-04 18:33:36 +02:00
|
|
|
|
free_to_normal($id) . ($revision ? "/$revision" : "") . "/html");
|
2018-05-04 12:15:04 +02:00
|
|
|
|
$self->print_menu("w" . "Replace " . normal_to_free($id),
|
2019-05-04 18:33:36 +02:00
|
|
|
|
free_to_normal($id) . "/write/text");
|
2018-01-05 10:53:22 +01:00
|
|
|
|
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_page_comment_link($id, $revision);
|
|
|
|
|
|
$self->serve_page_history_link($id, $revision);
|
2018-01-06 00:29:32 +01:00
|
|
|
|
|
2018-01-15 14:38:50 +01:00
|
|
|
|
my $first = 1;
|
2018-08-10 20:45:11 +02:00
|
|
|
|
while ($page->{text} =~ /
|
|
|
|
|
|
\[\[ (?<title>[^\]|]*) (?:\|(?<text>[^\]]*))? \]\]
|
|
|
|
|
|
| \[ (?<url>https?:\/\/\S+) \s+ (?<text>[^\]]*) \]
|
2019-06-20 14:27:32 +02:00
|
|
|
|
| (?<url>https?:\/\/\S+)
|
2018-08-10 20:45:11 +02:00
|
|
|
|
| \[ (?<text>[^\]]*) \] \( (?<url>https?:\/\/\S+) \)
|
2019-06-20 14:27:32 +02:00
|
|
|
|
| \[ gophers?:\/\/ (?<hostname>[^:\/]*) (?::(?<port>\d+))?
|
|
|
|
|
|
(?:\/(?<type>\d)? (?<selector>\S+))? \]
|
2019-06-20 12:31:31 +02:00
|
|
|
|
| \[ gophers?:\/\/ (?<hostname>[^:\/]*) (?::(?<port>\d+))?
|
2019-06-20 12:35:04 +02:00
|
|
|
|
(?:\/(?<type>\d)? (?<selector>\S+))?
|
2019-06-20 14:27:32 +02:00
|
|
|
|
\s+ (?<text>[^\]]+) \]
|
2018-08-10 20:45:11 +02:00
|
|
|
|
| \[ (?<text>[^\]]+) \]
|
2019-06-20 12:31:31 +02:00
|
|
|
|
\( gophers?:\/\/ (?<hostname>[^:\/]*) (?::(?<port>\d+))?
|
2019-06-20 12:35:04 +02:00
|
|
|
|
(?:\/(?<type>\d)? (?<selector>\S+))? \)
|
2018-08-10 20:45:11 +02:00
|
|
|
|
/xg) {
|
2019-06-20 14:27:32 +02:00
|
|
|
|
# remember $type can be "0" and thus "false" -- use // and defined instead!
|
2018-08-10 20:45:11 +02:00
|
|
|
|
my ($title, $text, $url, $hostname,
|
|
|
|
|
|
$port, $type, $selector)
|
|
|
|
|
|
= ($+{title}, $+{text}, $+{url}, $+{hostname},
|
2019-06-20 14:27:32 +02:00
|
|
|
|
$+{port}||70, $+{type}//1, $+{selector});
|
2019-06-24 12:08:36 +02:00
|
|
|
|
$title =~ s/\n/ /g;
|
|
|
|
|
|
$text =~ s/\n/ /g;
|
2018-03-01 09:00:00 +01:00
|
|
|
|
if ($first) {
|
2018-01-15 14:38:50 +01:00
|
|
|
|
$self->print_info("");
|
2018-05-04 12:15:04 +02:00
|
|
|
|
$self->print_info("Links leaving " . normal_to_free($id) . ":");
|
2018-01-15 14:38:50 +01:00
|
|
|
|
$first = 0;
|
|
|
|
|
|
}
|
2019-06-20 14:27:32 +02:00
|
|
|
|
if ($hostname and $text) {
|
2018-02-12 14:45:39 +01:00
|
|
|
|
$self->print_text(join("\t", $type . $text, $selector, $hostname, $port) . "\r\n");
|
2019-06-20 14:27:32 +02:00
|
|
|
|
} elsif ($hostname and $selector) {
|
|
|
|
|
|
$self->print_text(join("\t", "$type$hostname:$port/$type$selector", $selector, $hostname, $port) . "\r\n");
|
|
|
|
|
|
} elsif ($hostname) {
|
|
|
|
|
|
$self->print_text(join("\t", "1$hostname:$port", $selector, $hostname, $port) . "\r\n");
|
|
|
|
|
|
} elsif ($url and $text) {
|
2018-02-12 14:45:39 +01:00
|
|
|
|
$self->print_menu("h$text", "URL:" . $url, undef, undef, 1);
|
2019-06-20 14:27:32 +02:00
|
|
|
|
} elsif ($url) {
|
|
|
|
|
|
$self->print_menu("h$url", "URL:" . $url, undef, undef, 1);
|
2018-02-12 14:45:39 +01:00
|
|
|
|
} elsif ($title and substr($title, 0, 4) eq 'tag:') {
|
|
|
|
|
|
$self->print_menu("1" . ($text||substr($title, 4)),
|
2019-05-04 18:33:36 +02:00
|
|
|
|
free_to_normal(substr($title, 4)) . "/tag");
|
2018-02-13 09:30:42 +01:00
|
|
|
|
} elsif ($title =~ s!^image[/a-z]* external:!pics/!) {
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("I" . $text||$title, $title); # do not normalize space
|
2018-02-12 14:45:39 +01:00
|
|
|
|
} elsif ($title) {
|
2018-03-17 11:27:00 +01:00
|
|
|
|
$title =~ s!^image[/a-z]*:!!i;
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("1" . ($text||$title), free_to_normal($title) . "/menu");
|
2018-02-12 14:45:39 +01:00
|
|
|
|
}
|
2018-01-15 14:38:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$first = 1;
|
|
|
|
|
|
while ($page->{text} =~ /\[https?:\/\/gopher\.floodgap\.com\/gopher\/gw\?a=gopher%3a%2f%2f(.*?)(?:%3a(\d+))?%2f(.)(\S+)\s+([^\]]+)\]/gi) {
|
|
|
|
|
|
my ($hostname, $port, $type, $selector, $text) = ($1, $2||"70", $3, $4, $5);
|
|
|
|
|
|
if ($first) {
|
|
|
|
|
|
$self->print_info("");
|
|
|
|
|
|
$self->print_info("Gopher links (via Floodgap):");
|
|
|
|
|
|
$first = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
$selector =~ s/%([0-9a-f][0-9a-f])/chr(hex($1))/eig; # url unescape
|
|
|
|
|
|
$self->print_text(join("\t", $type . $text, $selector, $hostname, $port)
|
|
|
|
|
|
. "\r\n");
|
|
|
|
|
|
}
|
2018-09-10 11:29:56 +02:00
|
|
|
|
|
2018-01-01 16:14:59 +01:00
|
|
|
|
if ($page->{text} =~ m/<journal search tag:(\S+)>\s*/) {
|
2017-12-28 21:40:21 +01:00
|
|
|
|
my $tag = $1;
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_info("");
|
|
|
|
|
|
$self->serve_tag_list($tag);
|
2017-12-28 21:40:21 +01:00
|
|
|
|
}
|
2017-12-28 12:16:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-29 23:20:47 +01:00
|
|
|
|
sub serve_page_history {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-29 23:20:47 +01:00
|
|
|
|
my $id = shift;
|
2018-01-29 11:41:55 +01:00
|
|
|
|
$self->log(3, "Serving history of " . UrlEncode($id));
|
2018-01-01 16:14:59 +01:00
|
|
|
|
OpenPage($id);
|
2017-12-30 17:26:41 +01:00
|
|
|
|
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("1" . normal_to_free($id) . " (current)", free_to_normal($id) . "/menu");
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_info(CalcTime($Page{ts})
|
2018-03-17 11:27:00 +01:00
|
|
|
|
. " by " . GetAuthor($Page{username})
|
2018-01-01 16:14:59 +01:00
|
|
|
|
. ($Page{summary} ? ": $Page{summary}" : "")
|
2018-01-06 21:58:03 +01:00
|
|
|
|
. ($Page{minor} ? " (minor)" : ""));
|
2018-01-01 16:14:59 +01:00
|
|
|
|
|
|
|
|
|
|
foreach my $revision (GetKeepRevisions($OpenPageName)) {
|
|
|
|
|
|
my $keep = GetKeptRevision($revision);
|
2018-05-04 12:15:04 +02:00
|
|
|
|
$self->print_menu("1" . normal_to_free($id) . " ($keep->{revision})",
|
2019-05-04 18:33:36 +02:00
|
|
|
|
free_to_normal($id) . "/$keep->{revision}/menu");
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_info(CalcTime($keep->{ts})
|
2018-03-17 11:27:00 +01:00
|
|
|
|
. " by " . GetAuthor($keep->{username})
|
2017-12-29 23:20:47 +01:00
|
|
|
|
. ($keep->{summary} ? ": $keep->{summary}" : "")
|
2018-01-06 21:58:03 +01:00
|
|
|
|
. ($keep->{minor} ? " (minor)" : ""));
|
2017-12-29 23:20:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-30 17:26:41 +01:00
|
|
|
|
sub get_page {
|
2017-12-29 23:20:47 +01:00
|
|
|
|
my $id = shift;
|
|
|
|
|
|
my $revision = shift;
|
2017-12-30 17:26:41 +01:00
|
|
|
|
my $page;
|
2018-01-01 16:14:59 +01:00
|
|
|
|
|
2017-12-30 17:26:41 +01:00
|
|
|
|
if ($revision) {
|
2018-01-01 16:14:59 +01:00
|
|
|
|
$OpenPageName = $id;
|
|
|
|
|
|
$page = GetKeptRevision($revision);
|
2017-12-29 23:20:47 +01:00
|
|
|
|
} else {
|
2018-01-01 16:14:59 +01:00
|
|
|
|
OpenPage($id);
|
|
|
|
|
|
$page = \%Page;
|
2017-12-29 23:20:47 +01:00
|
|
|
|
}
|
2017-12-30 17:26:41 +01:00
|
|
|
|
|
|
|
|
|
|
return $page;
|
2017-12-29 23:20:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-28 19:09:11 +01:00
|
|
|
|
sub serve_page_menu {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-28 12:16:25 +01:00
|
|
|
|
my $id = shift;
|
2017-12-30 17:26:41 +01:00
|
|
|
|
my $revision = shift;
|
|
|
|
|
|
my $page = get_page($id, $revision);
|
|
|
|
|
|
|
2018-01-01 16:14:59 +01:00
|
|
|
|
if (my ($type) = TextIsFile($page->{text})) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_file_page_menu($id, $type, $revision);
|
2017-12-28 19:09:11 +01:00
|
|
|
|
} else {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_text_page_menu($id, $page, $revision);
|
2017-12-30 17:26:41 +01:00
|
|
|
|
}
|
2017-12-28 19:09:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub serve_file_page {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-28 19:09:11 +01:00
|
|
|
|
my $id = shift;
|
2017-12-30 17:26:41 +01:00
|
|
|
|
my $page = shift;
|
2018-01-29 11:41:55 +01:00
|
|
|
|
$self->log(3, "Serving " . UrlEncode($id) . " as file");
|
2018-01-08 10:44:23 +01:00
|
|
|
|
my ($encoded) = $page->{text} =~ /^[^\n]*\n(.*)/s;
|
2018-01-29 11:41:55 +01:00
|
|
|
|
$self->log(4, UrlEncode($id) . " has " . length($encoded)
|
|
|
|
|
|
. " bytes of MIME encoded data");
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $data = decode_base64($encoded);
|
2018-01-29 11:41:55 +01:00
|
|
|
|
$self->log(4, UrlEncode($id) . " has " . length($data)
|
|
|
|
|
|
. " bytes of binary data");
|
2018-01-09 17:59:24 +01:00
|
|
|
|
binmode(STDOUT, ":raw");
|
|
|
|
|
|
print($data);
|
2017-12-28 19:09:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub serve_text_page {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-28 19:09:11 +01:00
|
|
|
|
my $id = shift;
|
2017-12-30 17:26:41 +01:00
|
|
|
|
my $page = shift;
|
|
|
|
|
|
my $text = $page->{text};
|
2018-10-07 15:39:51 +02:00
|
|
|
|
$text =~ s/^\./../mg;
|
|
|
|
|
|
$text =~ s/\[\[tag:([^]]+)\]\]/'#' . join('_', split(' ', $1))/mge;
|
2018-01-29 11:41:55 +01:00
|
|
|
|
$self->log(3, "Serving " . UrlEncode($id) . " as " . length($text)
|
|
|
|
|
|
. " bytes of text");
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_text($text);
|
2017-12-28 12:16:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-28 19:09:11 +01:00
|
|
|
|
sub serve_page {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-28 19:09:11 +01:00
|
|
|
|
my $id = shift;
|
2017-12-30 17:26:41 +01:00
|
|
|
|
my $revision = shift;
|
|
|
|
|
|
my $page = get_page($id, $revision);
|
2018-01-01 16:14:59 +01:00
|
|
|
|
if (my ($type) = TextIsFile($page->{text})) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_file_page($id, $page);
|
2017-12-28 19:09:11 +01:00
|
|
|
|
} else {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_text_page($id, $page);
|
2017-12-28 19:09:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub serve_page_html {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-28 19:09:11 +01:00
|
|
|
|
my $id = shift;
|
2017-12-30 17:26:41 +01:00
|
|
|
|
my $revision = shift;
|
|
|
|
|
|
my $page = get_page($id, $revision);
|
|
|
|
|
|
|
2018-01-29 11:41:15 +01:00
|
|
|
|
$self->log(3, "Serving " . UrlEncode($id) . " as HTML");
|
|
|
|
|
|
|
2018-05-04 12:15:04 +02:00
|
|
|
|
my $title = normal_to_free($id);
|
2018-01-29 11:41:15 +01:00
|
|
|
|
print GetHtmlHeader(Ts('%s:', $SiteName) . ' ' . UnWiki($title), $id);
|
|
|
|
|
|
print GetHeaderDiv($id, $title);
|
|
|
|
|
|
print $q->start_div({-class=>'wrapper'});
|
|
|
|
|
|
|
2017-12-30 17:26:41 +01:00
|
|
|
|
if ($revision) {
|
2018-01-04 13:40:03 +01:00
|
|
|
|
# no locking of the file, no updating of the cache
|
2018-01-29 11:41:15 +01:00
|
|
|
|
PrintWikiToHTML($page->{text});
|
2017-12-30 17:26:41 +01:00
|
|
|
|
} else {
|
2018-01-29 11:41:15 +01:00
|
|
|
|
PrintPageHtml();
|
2017-12-30 17:26:41 +01:00
|
|
|
|
}
|
2018-01-29 11:41:15 +01:00
|
|
|
|
PrintFooter($id, $revision);
|
2017-12-28 19:09:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-12 14:45:39 +01:00
|
|
|
|
sub serve_redirect {
|
|
|
|
|
|
my $self = shift;
|
|
|
|
|
|
my $url = shift;
|
|
|
|
|
|
print qq{<!DOCTYPE HTML>
|
|
|
|
|
|
<html lang="en-US">
|
|
|
|
|
|
<head>
|
|
|
|
|
|
<meta http-equiv="refresh" content="0; url=$url">
|
|
|
|
|
|
<title>Redirection</title>
|
|
|
|
|
|
</head>
|
|
|
|
|
|
<body>
|
|
|
|
|
|
If you are not redirected automatically, follow this <a href='$url'>link</a>.
|
|
|
|
|
|
</body>
|
|
|
|
|
|
</html>
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-13 09:30:42 +01:00
|
|
|
|
sub serve_image {
|
|
|
|
|
|
my $self = shift;
|
|
|
|
|
|
my $pic = shift;
|
|
|
|
|
|
my $file = $external_image_path . $pic;
|
|
|
|
|
|
# no tricks
|
|
|
|
|
|
if ($file !~ /\.\./ and $file !~ /\/\//
|
|
|
|
|
|
and -f $file and open(my $fh, "<", $file)) {
|
|
|
|
|
|
local $/ = undef;
|
|
|
|
|
|
my $data = <$fh>;
|
|
|
|
|
|
$self->log(4, $pic . " has " . length($data)
|
|
|
|
|
|
. " bytes of binary data");
|
|
|
|
|
|
binmode(STDOUT, ":raw");
|
|
|
|
|
|
print($data);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$self->log(1, "Error reading $file: $!");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-28 13:15:44 +01:00
|
|
|
|
sub newest_first {
|
|
|
|
|
|
my ($A, $B) = ($a, $b);
|
|
|
|
|
|
if ($A =~ /^\d\d\d\d-\d\d-\d\d/ and $B =~ /^\d\d\d\d-\d\d-\d\d/) {
|
|
|
|
|
|
return $B cmp $A;
|
|
|
|
|
|
}
|
|
|
|
|
|
$A cmp $B;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-28 21:40:21 +01:00
|
|
|
|
sub serve_tag_list {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-28 12:48:34 +01:00
|
|
|
|
my $tag = shift;
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_info("Search result for tag $tag:");
|
2018-01-01 16:14:59 +01:00
|
|
|
|
for my $id (sort newest_first TagFind($tag)) {
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("1" . normal_to_free($id), free_to_normal($id) . "/menu");
|
2017-12-28 12:48:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-28 21:40:21 +01:00
|
|
|
|
sub serve_tag {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-28 21:40:21 +01:00
|
|
|
|
my $tag = shift;
|
2018-01-29 11:41:55 +01:00
|
|
|
|
$self->log(3, "Serving tag " . UrlEncode($tag));
|
2018-01-01 16:14:59 +01:00
|
|
|
|
if ($IndexHash{$tag}) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_info("This page is about the tag $tag.");
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("1" . normal_to_free($tag), free_to_normal($tag) . "/menu");
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_info("");
|
2017-12-28 21:40:21 +01:00
|
|
|
|
}
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_tag_list($tag);
|
2017-12-28 21:40:21 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-03 22:45:31 +01:00
|
|
|
|
sub serve_error {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-28 12:16:25 +01:00
|
|
|
|
my $id = shift;
|
2018-01-03 22:45:31 +01:00
|
|
|
|
my $error = shift;
|
2018-01-29 11:41:55 +01:00
|
|
|
|
$self->log(3, "Error ('" . UrlEncode($id) . "'): $error");
|
|
|
|
|
|
$self->print_error("Error ('" . UrlEncode($id) . "'): $error");
|
2017-12-28 12:16:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-30 20:42:24 +01:00
|
|
|
|
sub write_help {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2018-01-06 21:58:03 +01:00
|
|
|
|
my @lines = split(/\n/, <<"EOF");
|
|
|
|
|
|
This is how your document should start:
|
|
|
|
|
|
```
|
|
|
|
|
|
username: Alex Schroeder
|
|
|
|
|
|
summary: typo fixed
|
|
|
|
|
|
```
|
|
|
|
|
|
This is the text of your document.
|
|
|
|
|
|
Just write whatever.
|
|
|
|
|
|
|
|
|
|
|
|
Note the space after the colon for metadata fields.
|
|
|
|
|
|
More metadata fields are allowed:
|
|
|
|
|
|
`minor` is 1 if this is a minor edit. The default is 0.
|
2018-01-01 16:14:59 +01:00
|
|
|
|
EOF
|
2018-01-06 21:58:03 +01:00
|
|
|
|
for my $line (@lines) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_info($line);
|
2018-01-06 21:58:03 +01:00
|
|
|
|
}
|
2017-12-30 20:42:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub write_page_ok {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2018-01-05 10:45:21 +01:00
|
|
|
|
my $id = shift;
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->print_info("Page was saved.");
|
2019-05-04 18:33:36 +02:00
|
|
|
|
$self->print_menu("1" . normal_to_free($id), free_to_normal($id) . "/menu");
|
2018-01-01 16:14:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub write_page_error {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2018-01-01 16:14:59 +01:00
|
|
|
|
my $error = shift;
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->log(4, "Not saved: $error");
|
|
|
|
|
|
$self->print_error("Page was not saved: $error");
|
2018-01-01 16:14:59 +01:00
|
|
|
|
map { ReleaseLockDir($_); } keys %Locks;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub write_data {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2018-01-01 16:14:59 +01:00
|
|
|
|
my $id = shift;
|
|
|
|
|
|
my $data = shift;
|
2018-01-03 23:21:24 +01:00
|
|
|
|
my $param = shift||'text';
|
|
|
|
|
|
SetParam($param, $data);
|
2018-01-06 21:58:03 +01:00
|
|
|
|
my $error;
|
2018-01-01 16:14:59 +01:00
|
|
|
|
eval {
|
2018-01-06 21:58:03 +01:00
|
|
|
|
local *ReBrowsePage = sub {};
|
|
|
|
|
|
local *ReportError = sub { $error = shift };
|
2018-01-01 16:14:59 +01:00
|
|
|
|
DoPost($id);
|
|
|
|
|
|
};
|
2018-01-06 21:58:03 +01:00
|
|
|
|
if ($error) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->write_page_error($error);
|
2018-01-06 21:58:03 +01:00
|
|
|
|
} else {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->write_page_ok($id);
|
2018-01-06 21:58:03 +01:00
|
|
|
|
}
|
2017-12-30 20:42:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-31 14:32:39 +01:00
|
|
|
|
sub write_file_page {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-31 14:32:39 +01:00
|
|
|
|
my $id = shift;
|
2018-01-01 16:14:59 +01:00
|
|
|
|
my $data = shift;
|
2017-12-31 14:32:39 +01:00
|
|
|
|
my $type = shift || 'application/octet-stream';
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->write_page_error("page title is missing") unless $id;
|
2018-01-29 11:41:55 +01:00
|
|
|
|
$self->log(3, "Posting " . length($data) . " bytes of $type to page "
|
|
|
|
|
|
. UrlEncode($id));
|
2017-12-31 14:32:39 +01:00
|
|
|
|
# no metadata
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->write_data($id, "#FILE $type\n" . encode_base64($data));
|
2017-12-31 14:32:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-03 23:21:24 +01:00
|
|
|
|
sub write_text {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2017-12-30 20:42:24 +01:00
|
|
|
|
my $id = shift;
|
2018-01-01 16:14:59 +01:00
|
|
|
|
my $data = shift;
|
2018-01-03 23:21:24 +01:00
|
|
|
|
my $param = shift;
|
|
|
|
|
|
|
2018-01-01 16:14:59 +01:00
|
|
|
|
utf8::decode($data);
|
2017-12-30 20:42:24 +01:00
|
|
|
|
|
2018-01-01 16:14:59 +01:00
|
|
|
|
my ($lead, $meta, $text) = split(/^```\s*(?:meta)?\n/m, $data, 3);
|
2018-01-03 18:46:23 +01:00
|
|
|
|
|
2018-01-06 21:58:03 +01:00
|
|
|
|
if (not $lead and $meta) {
|
2017-12-30 20:42:24 +01:00
|
|
|
|
while ($meta =~ /^([a-z-]+): (.*)/mg) {
|
|
|
|
|
|
if ($1 eq 'minor' and $2) {
|
2018-01-01 16:14:59 +01:00
|
|
|
|
SetParam('recent_edit', 'on'); # legacy UseMod parameter name
|
2017-12-30 20:42:24 +01:00
|
|
|
|
} else {
|
2018-01-01 16:14:59 +01:00
|
|
|
|
SetParam($1, $2);
|
2018-01-03 18:46:23 +01:00
|
|
|
|
if ($1 eq "title") {
|
|
|
|
|
|
$id = $2;
|
|
|
|
|
|
}
|
2017-12-30 20:42:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->log(3, ($param eq 'text' ? "Posting" : "Appending")
|
2018-01-06 21:58:03 +01:00
|
|
|
|
. " " . length($text) . " characters (with metadata) to page $id");
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->write_data($id, $text, $param);
|
2017-12-30 20:42:24 +01:00
|
|
|
|
} else {
|
|
|
|
|
|
# no meta data
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->log(3, ($param eq 'text' ? "Posting" : "Appending")
|
2018-01-06 21:58:03 +01:00
|
|
|
|
. " " . length($data) . " characters to page $id") if $id;
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->write_data($id, $data, $param);
|
2017-12-30 20:42:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-03 23:21:24 +01:00
|
|
|
|
sub write_text_page {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
|
|
|
|
|
$self->write_text(@_, 'text');
|
2018-01-03 23:21:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub append_text_page {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
|
|
|
|
|
$self->write_text(@_, 'aftertext');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub read_file {
|
|
|
|
|
|
my $self = shift;
|
|
|
|
|
|
my $length = shift;
|
|
|
|
|
|
$length = $MaxPost if $length > $MaxPost;
|
|
|
|
|
|
local $/ = \$length;
|
|
|
|
|
|
my $buf .= <STDIN>;
|
|
|
|
|
|
$self->log(4, "Received " . length($buf) . " bytes (max is $MaxPost)");
|
|
|
|
|
|
return $buf;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub read_text {
|
|
|
|
|
|
my $self = shift;
|
|
|
|
|
|
my $buf;
|
|
|
|
|
|
while (1) {
|
|
|
|
|
|
my $line = <STDIN>;
|
|
|
|
|
|
if (length($line) == 0) {
|
|
|
|
|
|
sleep(1); # wait for input
|
|
|
|
|
|
next;
|
|
|
|
|
|
}
|
|
|
|
|
|
last if $line =~ /^.\r?\n/m;
|
|
|
|
|
|
$buf .= $line;
|
|
|
|
|
|
if (length($buf) > $MaxPost) {
|
|
|
|
|
|
$buf = substr($buf, 0, $MaxPost);
|
|
|
|
|
|
last;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
$self->log(4, "Received " . length($buf) . " bytes (max is $MaxPost)");
|
|
|
|
|
|
utf8::decode($buf);
|
|
|
|
|
|
$self->log(4, "Received " . length($buf) . " characters");
|
|
|
|
|
|
return $buf;
|
2018-01-03 23:21:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-07-16 21:34:11 +02:00
|
|
|
|
sub allow_deny_hook {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $self = shift;
|
2018-07-16 21:34:11 +02:00
|
|
|
|
my $client = shift;
|
|
|
|
|
|
|
2018-07-19 00:10:06 +02:00
|
|
|
|
# clear cookie, read config file
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$q = undef;
|
|
|
|
|
|
Init();
|
2018-01-08 13:45:54 +01:00
|
|
|
|
|
2018-07-19 00:10:06 +02:00
|
|
|
|
# don't do surge protection if we're testing
|
|
|
|
|
|
return 1 unless $SurgeProtection;
|
|
|
|
|
|
|
|
|
|
|
|
# get the client IP number
|
|
|
|
|
|
my $peeraddr = $self->{server}->{'peeraddr'};
|
|
|
|
|
|
|
2018-07-16 21:34:11 +02:00
|
|
|
|
# implement standard surge protection using Oddmuse tools but without using
|
|
|
|
|
|
# ReportError and all that
|
|
|
|
|
|
$self->log(4, "Adding visitor $peeraddr");
|
|
|
|
|
|
ReadRecentVisitors();
|
|
|
|
|
|
AddRecentVisitor($peeraddr);
|
|
|
|
|
|
if (RequestLockDir('visitors')) { # not fatal
|
|
|
|
|
|
WriteRecentVisitors();
|
|
|
|
|
|
ReleaseLockDir('visitors');
|
|
|
|
|
|
my @entries = @{$RecentVisitors{$peeraddr}};
|
|
|
|
|
|
my $ts = $entries[$SurgeProtectionViews];
|
|
|
|
|
|
if (($Now - $ts) < $SurgeProtectionTime) {
|
|
|
|
|
|
$self->log(2, "Too many requests by $peeraddr");
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub process_request {
|
|
|
|
|
|
my $self = shift;
|
2018-07-16 09:37:39 +02:00
|
|
|
|
|
2018-01-09 17:59:24 +01:00
|
|
|
|
# refresh list of pages
|
|
|
|
|
|
if (IsFile($IndexFile) and ReadIndex()) {
|
|
|
|
|
|
# we're good
|
|
|
|
|
|
} else {
|
|
|
|
|
|
RefreshIndex();
|
|
|
|
|
|
}
|
2018-01-06 00:29:32 +01:00
|
|
|
|
|
2018-01-09 17:59:24 +01:00
|
|
|
|
eval {
|
|
|
|
|
|
local $SIG{'ALRM'} = sub {
|
|
|
|
|
|
$self->log(1, "Timeout!");
|
|
|
|
|
|
die "Timed Out!\n";
|
|
|
|
|
|
};
|
|
|
|
|
|
alarm(10); # timeout
|
|
|
|
|
|
my $selector = <STDIN>; # no loop
|
2018-01-15 13:30:19 +01:00
|
|
|
|
$selector = UrlDecode($selector); # assuming URL-encoded UTF-8
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$selector =~ s/\s+$//g; # no trailing whitespace
|
2018-01-01 16:14:59 +01:00
|
|
|
|
|
2018-02-18 00:03:41 +01:00
|
|
|
|
if (not $selector or $selector eq "/") {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_main_menu();
|
2018-02-01 23:22:01 +01:00
|
|
|
|
} elsif ($selector eq "do/more") {
|
|
|
|
|
|
$self->serve_phlog_archive();
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif ($selector eq "do/index") {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_index();
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif (substr($selector, 0, 9) eq "do/match\t") {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_match(substr($selector, 9));
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif (substr($selector, 0, 10) eq "do/search\t") {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_search(substr($selector, 10));
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif ($selector eq "do/tags") {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_tags();
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif ($selector eq "do/rc") {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_rc(0);
|
2018-02-18 00:03:29 +01:00
|
|
|
|
} elsif ($selector eq "do/rss") {
|
|
|
|
|
|
$self->serve_rss(0);
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif ($selector eq "do/rc/showedits") {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_rc(1);
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif ($selector eq "do/new") {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $data = $self->read_text();
|
|
|
|
|
|
$self->write_text_page(undef, $data);
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif ($selector =~ m!^([^/]*)/(\d+)/menu$!) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_page_menu($1, $2);
|
2018-02-07 14:37:34 +01:00
|
|
|
|
} elsif ($selector =~ m!^map/(.*)!) {
|
|
|
|
|
|
$self->serve_map($1);
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif (substr($selector, -5) eq '/menu') {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_page_menu(substr($selector, 0, -5));
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif ($selector =~ m!^([^/]*)/tag$!) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_tag($1);
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif ($selector =~ m!^([^/]*)(?:/(\d+))?/html!) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_page_html($1, $2);
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif ($selector =~ m!^([^/]*)/history$!) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_page_history($1);
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif ($selector =~ m!^([^/]*)/write/text$!) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $data = $self->read_text();
|
|
|
|
|
|
$self->write_text_page($1, $data);
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif ($selector =~ m!^([^/]*)/append/text$!) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $data = $self->read_text();
|
|
|
|
|
|
$self->append_text_page($1, $data);
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif ($selector =~ m!^([^/]*)(?:/([a-z]+/[-a-z]+))?/write/file(?:\t(\d+))?$!) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
my $data = $self->read_file($3);
|
|
|
|
|
|
$self->write_file_page($1, $data, $2);
|
2018-01-06 14:52:58 +01:00
|
|
|
|
} elsif ($selector =~ m!^([^/]*)(?:/(\d+))?(?:/text)?$!) {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_page($1, $2);
|
2018-02-12 14:45:39 +01:00
|
|
|
|
} elsif ($selector =~ m!^URL:(.*)!i) {
|
|
|
|
|
|
$self->serve_redirect(UrlDecode($1));
|
2018-02-13 09:30:42 +01:00
|
|
|
|
} elsif ($selector =~ m!^pics/(.*)!i) {
|
|
|
|
|
|
$self->serve_image(UrlDecode($1));
|
2017-12-28 11:07:03 +01:00
|
|
|
|
} else {
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->serve_error($selector, ValidId($selector)||'Cause unknown');
|
2017-12-27 09:45:43 +01:00
|
|
|
|
}
|
2017-12-28 21:40:21 +01:00
|
|
|
|
|
2018-01-09 17:59:24 +01:00
|
|
|
|
$self->log(4, "Done");
|
|
|
|
|
|
}
|
2017-12-27 09:45:43 +01:00
|
|
|
|
}
|