App-Phoebe

 view release on metacpan or  search on metacpan

META.json  view on Meta::CPAN

            "Algorithm::Diff" : "0",
            "Encode::Locale" : "0",
            "File::MimeInfo" : "0",
            "File::MimeInfo::Magic" : "0",
            "File::ReadBackwards" : "0",
            "File::Slurper" : "0",
            "HTTP::Date" : "0",
            "IO::Scalar" : "0",
            "IO::Socket::SSL" : "2.069",
            "IRI" : "0",
            "Modern::Perl" : "1.20180701",
            "Mojolicious" : "9",
            "Net::IDN::Encode" : "0",
            "Net::SSLeay" : "1.9",
            "Text::Wrapper" : "0",
            "URI::Escape" : "0",
            "XML::LibXML" : "0",
            "perl" : "5.026000"
         }
      }
   },

META.yml  view on Meta::CPAN

  Algorithm::Diff: '0'
  Encode::Locale: '0'
  File::MimeInfo: '0'
  File::MimeInfo::Magic: '0'
  File::ReadBackwards: '0'
  File::Slurper: '0'
  HTTP::Date: '0'
  IO::Scalar: '0'
  IO::Socket::SSL: '2.069'
  IRI: '0'
  Modern::Perl: '1.20180701'
  Mojolicious: '9'
  Net::IDN::Encode: '0'
  Net::SSLeay: '1.9'
  Text::Wrapper: '0'
  URI::Escape: '0'
  XML::LibXML: '0'
  perl: '5.026000'
resources:
  repository: https://alexschroeder.ch/cgit/phoebe
version: 4.07

Makefile.PL  view on Meta::CPAN

use ExtUtils::MakeMaker;

os_unsupported if $^O eq 'MSWin32';

WriteMakefile(
  NAME             => 'App::Phoebe',
  VERSION_FROM     => 'lib/App/Phoebe.pm',
  ABSTRACT         => 'a Gemini-based wiki',
  AUTHOR           => 'Alex Schroeder',
  LICENSE          => 'agpl_3',
  MIN_PERL_VERSION => '5.26.0', # Modern::Perl '2018'
  EXE_FILES        => [
    'script/phoebe',
    'script/phoebe-ctl',
    'script/gemini',
    'script/gemini-chat',
    'script/titan',
    'script/ijirait',
    'script/spartan',
  ],
  PREREQ_PM => {
    # t/prerequisites.t is serious about all these!
    'Modern::Perl' => 1.20180701, # for '2018'
    'URI::Escape' => 0,
    'Encode::Locale' => 0,
    'Algorithm::Diff' => 0,
    'File::ReadBackwards' => 0,
    'File::Slurper' => 0,
    'Mojolicious' => 9.00,        # removed tls_verify from Mojo::IOLoop::TLS 9.0
    'IO::Socket::SSL' => 2.069,   # optional for Mojo::IOLoop
    'Net::SSLeay' => 1.90,
    'Net::IDN::Encode' => 0,
    'IRI' => 0,                   # for script/gemini

README.md  view on Meta::CPAN

## Dependencies

If you are not using `cpan` or `cpanm` to install Phoebe, you'll need to install
the following dependencies:

- [Algorithm::Diff](https://metacpan.org/pod/Algorithm%3A%3ADiff), or `libalgorithm-diff-xs-perl`
- [File::ReadBackwards](https://metacpan.org/pod/File%3A%3AReadBackwards), or `libfile-readbackwards-perl`
- [File::Slurper](https://metacpan.org/pod/File%3A%3ASlurper), or `libfile-slurper-perl`
- [Mojolicious](https://metacpan.org/pod/Mojolicious), or `libmojolicious-perl`
- [IO::Socket::SSL](https://metacpan.org/pod/IO%3A%3ASocket%3A%3ASSL), or `libio-socket-ssl-perl`
- [Modern::Perl](https://metacpan.org/pod/Modern%3A%3APerl), or `libmodern-perl-perl`
- [URI::Escape](https://metacpan.org/pod/URI%3A%3AEscape), or `liburi-escape-xs-perl`
- [Net::IDN::Encode](https://metacpan.org/pod/Net%3A%3AIDN%3A%3AEncode), or `libnet-idn-encode-perl`
- [Encode::Locale](https://metacpan.org/pod/Encode%3A%3ALocale), or `libencode-locale-perl`

I'm going to be using `curl` and `openssl` in the Quickstart section of
`phoebe`, so you'll need those tools as well. And finally, when people download
their data, the code calls `tar` (available from packages with the same name on
Debian derived systems).

## Installing Perl

lib/App/Phoebe.pm  view on Meta::CPAN

    use App::Phoebe qw(@footer);
    push(@footer, sub { '=> mailto:alex@alexschroeder.ch Mail' });

This prints a very simply footer instead of the usual footer for Gemini, as the
C<footer> function is redefined. At the same time, the C<@footer> array is still
used for the web:

    # tested by t/example-footer2.t
    package App::Phoebe;
    use App::Phoebe::Web;
    use Modern::Perl;
    our (@footer); # HTML only
    push(@footer, sub { '=> https://alexschroeder.ch/wiki/Contact Contact' });
    # footer sub is Gemini only
    no warnings qw(redefine);
    sub footer {
      return "\n" . '—' x 10 . "\n" . '=> mailto:alex@alexschroeder.ch Mail';
    }

This example shows you how to add a new route (a new path served by the wiki).
Instead of just writing "Test" to the page, you could of course run arbitrary
Perl code.

    # tested by t/example-route.t
    our @config = (<<'EOT');
    use App::Phoebe qw(@extensions @main_menu port host_regex success);
    use Modern::Perl;
    push(@main_menu, "=> /do/test Test");
    push(@extensions, \&serve_test);
    sub serve_test {
      my $stream = shift;
      my $url = shift;
      my $hosts = host_regex();
      my $port = port($stream);
      if ($url =~ m!^gemini://($hosts):$port/do/test$!) {
	success($stream, 'text/plain; charset=UTF-8');
	$stream->write("Test\n");

lib/App/Phoebe/BlockFediverse.pm  view on Meta::CPAN

Yeah, we could respond with a error, but fediverse developers aren’t interested
in a new architecture for this problem. They think the issue has been solved.
See L<#4486|https://github.com/tootsuite/mastodon/issues/4486>, “Mastodon can be
used as a DDOS tool.”

=cut

package App::Phoebe::BlockFediverse;
use App::Phoebe qw(@extensions);
use App::Phoebe::Web qw(http_error);
use Modern::Perl;

push(@extensions, \&block_fediverse);

sub block_fediverse {
  my ($stream, $url, $headers) = @_;
  # quit as quickly as possible: return 1 means the request has been handled
  return 0 unless $headers and $headers->{"user-agent"} and $headers->{"user-agent"} =~ m!Mastodon|Friendica|Pleroma!i;
  http_error($stream, "Blocking Fediverse previews");
  return 1;
}

lib/App/Phoebe/Capsules.pm  view on Meta::CPAN

=head1 DESCRIPTION

By default, Phoebe creates a wiki editable by all. With this extension, the
C</capsule> space turns into a special site: if you have a client certificate,
you automatically get an editable capsule with an assigned fantasy name.

Simply add it to your F<config> file. If you are virtual hosting, name the host
or hosts for your capsules.

    package App::Phoebe::Capsules;
    use Modern::Perl;
    our @capsule_hosts = qw(transjovian.org);
    use App::Phoebe::Capsules;

Every client certificate gets assigned a capsule name.

You can provide a link with some documentation, if you want:

    our $capsule_help = '//transjovian.org/phoebe/page/Capsules';

=head1 NO MIME TYPES

lib/App/Phoebe/Chat.pm  view on Meta::CPAN

Then connect with a client that let's you post what you type:

    gemini-chat --cert_file=cert.pem --key_file=key.pem \
      "gemini://localhost/do/chat/say"

=cut

package App::Phoebe::Chat;
use App::Phoebe qw(@extensions @request_handlers $log
		   success result port space host_regex space_regex);
use Modern::Perl '2018';
use Encode qw(decode_utf8 encode_utf8);
use URI::Escape;
use utf8;

# Each chat member is {stream => $stream, host => $host, space => $space, name => $name}
my (@chat_members, @chat_lines);
my $chat_line_limit = 50;

# needs a special handler because the stream never closes
my $spaces = space_regex();

lib/App/Phoebe/Comments.pm  view on Meta::CPAN

There is no configuration. Simply add it to your F<config> file:

    use App::Phoebe::Comments;

=cut

package App::Phoebe::Comments;
use App::Phoebe qw(@footer @extensions $log with_lock port space host_regex space_regex
		   result valid_id valid_token wiki_dir write_page decode_query);
use Encode qw(decode_utf8);
use Modern::Perl;
use URI::Escape;
use File::Slurper qw(read_text);
use utf8;

push(@footer, \&add_comment_link_to_footer);

sub add_comment_link_to_footer {
  my ($stream, $host, $space, $id, $revision, $scheme) = @_;
  # only leave comments on current comment pages
  return "" if $revision;

lib/App/Phoebe/Css.pm  view on Meta::CPAN

The cache control settings make sure that unless explicitly requested by a user
via a reload button, the CSS file is only fetched once per day. That also means
that if you change the CSS file, many users might only see a change after 24h.
That’s the trade-off…

=cut

package App::Phoebe::Css;
use App::Phoebe qw($server $log);
use App::Phoebe::Web;
use Modern::Perl;
use File::Slurper qw(read_text);

no warnings 'redefine';
*App::Phoebe::Web::serve_css_via_http = \&serve_css_via_http;

sub serve_css_via_http {
  my $stream = shift;
  $log->debug("Serving default.css via HTTP");
  $stream->write("HTTP/1.1 200 OK\r\n");
  $stream->write("Content-Type: text/css\r\n");

lib/App/Phoebe/DebugIpNumbers.pm  view on Meta::CPAN

    use App::Phoebe::DebugIpNumbers;

Phoebe tries not to collect visitor data. Logging visitor IP numbers goes
against this. If your aim is detect and block crazy bots by having C<fail2ban>
watch the log files, consider using L<App::Phoebe::SpeedBump> instead.

=cut

package App::Phoebe::DebugIpNumbers;
use App::Phoebe qw($log);
use Modern::Perl;

# We have to override the copy that was imported into the main namespace in the
# start_servers subroutine.
no warnings 'redefine';
*old_handle_request = \&main::handle_request;
*main::handle_request = \&handle_request;

sub handle_request {
  my ($stream) = @_;
  my $address = $stream->handle->peerhost;

lib/App/Phoebe/Favicon.pm  view on Meta::CPAN

F<favicon.svg> in the data directory and served that, only falling back to the
Jupiter planet SVG if no such file can be found. We could cache the content of
the file in the C<$server> hash reference… Well, if somebody writes it, it shall
be merged. 😃

=cut

package App::Phoebe::Favicon;
use App::Phoebe qw(@extensions $log);
use App::Phoebe::Web;
use Modern::Perl;

push(@extensions, \&favicon);

sub favicon {
  my $stream = shift;
  my $request = shift;
  if ($request =~ m!^GET /favicon.ico HTTP/1\.[01]$!) {
    serve_favicon_via_http($stream);
    return 1;
  }

lib/App/Phoebe/Galleries.pm  view on Meta::CPAN

    our $galleries_dir = "/home/alex/alexschroeder.ch/gallery";
    our $galleries_host = "alexschroeder.ch";
    use App::Phoebe::Galleries;

=cut

package App::Phoebe::Galleries;
use App::Phoebe qw(@extensions $log port success result print_link);
use File::Slurper qw(read_dir read_binary read_text);
use Encode qw(decode_utf8 encode_utf8);
use Modern::Perl;
use Mojo::JSON qw(decode_json encode_json);
use Mojo::Util qw(url_unescape);

# galleries

push(@extensions, \&galleries);

our $galleries_dir = "/home/alex/alexschroeder.ch/gallery";
our $galleries_host = "alexschroeder.ch";

lib/App/Phoebe/Gopher.pm  view on Meta::CPAN

    our $gophers_port = 7443; # listen on port 7443 using TLS
    our $gopher_main_page = "Gopher_Welcome";
    use App::Phoebe::Gopher;

Note the C<finger> port in the example. This works, but it's awkward since you
have to finger C<page/alex> instead of C<alex>. In order to make that work, we
need some more code.

    package App::Phoebe::Gopher;
    use App::Phoebe qw(@extensions port $log);
    use Modern::Perl;
    our $gopher_host = "alexschroeder.ch";
    our $gopher_port = [70,79]; # listen on the finger port as well
    our $gophers_port = 7443; # listen on port 7443 using TLS
    our $gopher_main_page = "Gopher_Welcome";
    our @extensions;
    push(@extensions, \&finger);
    sub finger {
      my $stream = shift;
      my $selector = shift;
      my $port = port($stream);

lib/App/Phoebe/Iapetus.pm  view on Meta::CPAN

Make sure your main menu has a link to the login page. The login page allows
people to pick the right certificate without interrupting their uploads.

    => /login Login

=cut

package App::Phoebe::Iapetus;
use App::Phoebe qw($server $log @request_handlers @extensions host_regex space_regex space port result
		   valid_id valid_mime_type valid_size @known_fingerprints process_titan);
use Modern::Perl;
use File::MimeInfo qw(globs);
use Encode qw(decode_utf8);
use URI::Escape;

push(@{$server->{wiki_mime_type}},'text/gemini');
unshift(@request_handlers, '^iapetus://' => \&handle_iapetus);

sub handle_iapetus {
  my $stream = shift;
  my $data = shift;

lib/App/Phoebe/Ijirait.pm  view on Meta::CPAN

    use App::Phoebe::Ijirait;

The help file, if you have one, is F<ijirait-help.gmi> in your wiki data
directory. Feel free to get a copy of
L<gemini://transjovian.org/ijiraq/page/Help>.

=cut

package App::Phoebe::Ijirait;
use App::Phoebe qw(@extensions $log $server @request_handlers success result);
use Modern::Perl;
use Archive::Tar;
use Encode qw(encode_utf8 decode_utf8);
use File::Slurper qw(read_binary write_binary read_text);
use Mojo::JSON qw(decode_json encode_json);
use Mojo::Util qw(gzip);
use List::Util qw(first none any);
use URI::Escape;
use utf8;

# See "load world on startup" for the small world generated if no save file is

lib/App/Phoebe/MokuPona.pm  view on Meta::CPAN


    package App::Phoebe::MokuPona;
    our $dir = "/home/alex/.moku-pona";
    our $host = "alexschroeder.ch";
    use App::Phoebe::MokuPona;

=cut

package App::Phoebe::MokuPona;
use App::Phoebe qw(@extensions $server $log success result port);
use Modern::Perl;
use URI::Escape;
use File::Slurper qw(read_text);
use Encode qw(encode_utf8 decode_utf8);
# moku pona

our $dir  ||= "$ENV{HOME}/.moku-pona";
our $host ||= (keys %{$server->{host}})[0];

push(@extensions, \&mokupona);

lib/App/Phoebe/Oddmuse.pm  view on Meta::CPAN


    use App::Phoebe::Oddmuse;

=cut

package App::Phoebe::Oddmuse;
use App::Phoebe qw(@request_handlers @extensions @main_menu $server $log $full_url_regex
		   success result reserved_regex port gemini_link modified changes diff
		   colourize quote_html bogus_hash print_link decode_query);
use Mojo::UserAgent;
use Modern::Perl;
use MIME::Base64;
use URI::Escape;
use List::Util qw(uniq);
use File::Slurper qw(read_dir read_text write_text);
use Encode qw(encode_utf8 decode_utf8);
use DateTime::Format::ISO8601;
use utf8; # the source contains UTF-8 encoded strings
no warnings 'redefine';

# Oddmuse Wiki

lib/App/Phoebe/Oracle.pm  view on Meta::CPAN


You can only answer questions not your own. You can answer every question just
once (even if you or the question asker deletes your answer, there is no going
back). You can delete your answer. If the question is no longer waiting for
answers, deleting the last answer deletes the question, too.

Simply add it to your F<config> file. If you are virtual hosting, name the host
or hosts for your capsules.

    package App::Phoebe::Oracle;
    use Modern::Perl;
    our @oracle_hosts = qw(transjovian.org);
    use App::Phoebe::Oracle;

If you don't want to use C</oracle> for the game, you can change it:

    our $oracle_space = 'truth';

If you want to change the maximu number of answers that a question may have:

    our $max_answers = 5;

lib/App/Phoebe/RegisteredEditorsOnly.pm  view on Meta::CPAN

created.

Once you have done this and you visit the Phoebe site, it’ll use the client
certificate you provided or it’ll ask you what client certificate to use.

=cut

package App::Phoebe::RegisteredEditorsOnly;
use App::Phoebe qw(@request_handlers @extensions @known_fingerprints $log
		   port host_regex space_regex handle_titan result);
use Modern::Perl;

unshift(@request_handlers, '^titan://' => \&protected_titan);

sub protected_titan {
  my $stream = shift;
  my $data = shift;
  my $hosts = host_regex();
  my $spaces = space_regex();
  my $port = port($stream);
  my $fingerprint = $stream->handle->get_fingerprint();

lib/App/Phoebe/Spartan.pm  view on Meta::CPAN

If you don't do any of the above, you'll get a permission error on startup:
"Mojo::Reactor::Poll: Timer failed: Can't create listen socket: Permission
denied…"

=cut

package App::Phoebe::Spartan;
use App::Phoebe qw($server $log @main_menu get_ip_numbers space host_regex space_regex run_extensions
		   serve_index serve_page serve_raw serve_html serve_history serve_diff save_page
		   blog print_link text);
use Modern::Perl;
use URI::Escape;
use Encode qw(encode_utf8 decode_utf8 decode);
use utf8;
no warnings 'redefine';

our $spartan_port ||= 300;

use Mojo::IOLoop;

# start the loop after configuration (so that the user can change $spartan_port)

lib/App/Phoebe/SpeedBump.pm  view on Meta::CPAN

    | tr [:upper:] [:lower:]

This should give you the fingerprint in the correct format to add to the list
above.

=cut

package App::Phoebe::SpeedBump;
use App::Phoebe qw(@extensions $log $server @known_fingerprints
		   success result port host_regex );
use Modern::Perl;
use File::Slurper qw(read_binary write_binary);
use List::Util qw(sum);
use Mojo::JSON qw(decode_json encode_json);
use Net::IP qw(:PROC);
use Net::DNS qw(rr);

@known_fingerprints = qw(
  sha256$54c0b95dd56aebac1432a3665107d3aec0d4e28fef905020ed6762db49e84ee1);

our $speed_bump_requests ||= 30;

lib/App/Phoebe/TokiPona.pm  view on Meta::CPAN

No further configuration is necessary. Simply add it to your F<config> file:

    use App::Phoebe::TokiPona;

=cut

package App::Phoebe::TokiPona;
use App::Phoebe::Web;
use App::Phoebe qw(@extensions $server $log);
use File::Slurper qw(read_binary);
use Modern::Perl;

push(@extensions, \&toki_pona_font);

sub toki_pona_font {
  my $stream = shift;
  my $request = shift;
  if ($request =~ m!^GET /linja-pona-4.2.woff HTTP/1\.[01]$!) {
    serve_font_via_http($stream);
    return 1;
  }

lib/App/Phoebe/Web.pm  view on Meta::CPAN

package App::Phoebe::Web;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(http_error handle_http_header);
use App::Phoebe qw(@request_handlers port space host_regex space_regex run_extensions text quote_html blog_pages
		   html_page to_html wiki_dir changes all_logs pages rss atom files $server $log @footer
		   space_links diff);
use File::Slurper qw(read_text read_binary);
use Encode qw(encode_utf8 decode_utf8);
use List::Util qw(min);
use Modern::Perl;
use URI::Escape;
use utf8;

unshift(@request_handlers, '^GET .* HTTP/1\.[01]$' => \&handle_http_header);

sub handle_http_header {
  my $stream = shift;
  my $data = shift;
  # $log->debug("Reading HTTP headers");
  my @lines = split(/\r\n/, $data->{buffer}, -1); # including the empty line at the end

lib/App/Phoebe/WebComments.pm  view on Meta::CPAN


    use App::Phoebe::WebComments;

=cut

package App::Phoebe::WebComments;
use App::Phoebe qw(@footer @extensions @request_handlers $server $log port space
		    host_regex space_regex quote_html wiki_dir with_lock
		    bogus_hash to_url);
use App::Phoebe::Web qw(handle_http_header http_error);
use Modern::Perl;
use URI::Escape;
use File::Slurper qw(write_text);
use Encode qw(decode_utf8 encode_utf8);
use File::Slurper qw(read_text);
use utf8;

push(@footer, \&add_comment_web_link_to_footer);

sub add_comment_web_link_to_footer {
  my ($self, $host, $space, $id, $revision, $scheme) = @_;

lib/App/Phoebe/WebDAV.pm  view on Meta::CPAN

use App::Phoebe::Web qw(handle_http_header);
use App::Phoebe qw(@request_handlers @extensions run_extensions $server
		   $log host_regex space_regex space port wiki_dir pages files
		   with_lock bogus_hash);
use File::Slurper qw(read_text write_text read_binary write_binary read_dir);
use HTTP::Date qw(time2str time2isoz);
use Digest::MD5 qw(md5_base64);
use Encode qw(encode_utf8 decode_utf8);
use Mojo::Util qw(b64_decode);
use File::stat;
use Modern::Perl;
use File::MimeInfo::Magic;
use URI::Escape;
use XML::LibXML;
use IO::Scalar;
use utf8;

unshift(@request_handlers, '^(OPTIONS|PROPFIND|PUT|DELETE|COPY|MOVE) .* HTTP/1\.1$' => \&handle_http_header);

# note that the requests handled here must be protected in
# App::Phoebe::RegisteredEditorsOnly!

lib/App/Phoebe/WebEdit.pm  view on Meta::CPAN


=cut

package App::Phoebe::WebEdit;
use App::Phoebe::Web qw(handle_http_header http_error);
use App::Phoebe qw(@footer @extensions @request_handlers @main_menu $server $log
		   port space host_regex space_regex text with_lock wiki_dir
		   bogus_hash to_url quote_html);
use Encode qw(decode_utf8 encode_utf8);
use File::Slurper qw(read_text write_text read_dir);
use Modern::Perl;
use URI::Escape;

unshift(@request_handlers, '^POST .* HTTP/1\.[01]$' => \&handle_http_header);

# edit from the web

push(@footer, \&add_edit_link_to_footer);

sub is_editable {
  my ($space) = @_;

lib/App/Phoebe/WebStaticFiles.pm  view on Meta::CPAN

You still need to add a link to C</do/static> somewhere in your wiki.

=cut

package App::Phoebe::WebStaticFiles;
use App::Phoebe qw(@extensions $log host_regex port);
use App::Phoebe::Web qw(http_error);
use App::Phoebe::StaticFiles qw(%routes mime_type);
use File::Slurper qw(read_text read_binary read_dir);
use Encode qw(encode_utf8 decode_utf8);
use Modern::Perl;
use URI::Escape;

# add a code reference to the list of extensions
push(@extensions, \&static_web_routes);

sub static_web_routes {
  my ($stream, $request, $headers) = @_;
  my $hosts = host_regex();
  my $port = port($stream);
  my ($host, $route, $file);

 view all matches for this distribution
 view release on metacpan -  search on metacpan

( run in 1.769 second using v1.00-cache-2.02-grep-82fe00e-cpan-d29e8ade9f55 )