App-phoebe

 view release on metacpan or  search on metacpan

META.json  view on Meta::CPAN

         }
      },
      "runtime" : {
         "requires" : {
            "Algorithm::Diff" : "0",
            "Encode::Locale" : "0",
            "File::ReadBackwards" : "0",
            "File::Slurper" : "0",
            "IO::Socket::SSL" : "2.069",
            "IRI" : "0",
            "Modern::Perl" : "1.20180701",
            "Mojolicious" : "9",
            "Net::IDN::Encode" : "0",
            "Net::SSLeay" : "1.9",
            "URI::Escape" : "0",
            "perl" : "5.026000"
         }
      }
   },
   "release_status" : "stable",
   "resources" : {

META.yml  view on Meta::CPAN

  directory:
    - t
    - inc
requires:
  Algorithm::Diff: '0'
  Encode::Locale: '0'
  File::ReadBackwards: '0'
  File::Slurper: '0'
  IO::Socket::SSL: '2.069'
  IRI: '0'
  Modern::Perl: '1.20180701'
  Mojolicious: '9'
  Net::IDN::Encode: '0'
  Net::SSLeay: '1.9'
  URI::Escape: '0'
  perl: '5.026000'
resources:
  repository: https://alexschroeder.ch/cgit/phoebe
version: 4
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'

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"](#quickstart) instructions,
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).

The `update-readme.pl` script I use to generate `README.md` also requires some

README.md  view on Meta::CPAN


## Tokens per Wiki Space

Per default, there is simply one set of tokens which allows the editing of the
wiki, and all the wiki spaces you defined. If you want to give users a token
just for their space, you can do that, too. Doing this is starting to strain the
command line interface, however, and therefore the following illustrates how to
do more advanced configuration using the config file:

    package App::Phoebe;
    use Modern::Perl;
    our ($server);
    $server->{wiki_space_token}->{alex} = ["*secret*"];

The code above sets up the `wiki_space_token` property. It's a hash reference
where keys are existing wiki spaces and values are array references listing the
valid tokens for that space (in addition to the global tokens that you can set
up using `--wiki_token` which defaults to the token "hello"). Thus, the above
code sets up the token `*secret*` for the `alex` wiki space.

You can use the config file to change the values of other properties as well,
even if these properties are set via the command line.

    package App::Phoebe;
    use Modern::Perl;
    our ($server);
    $server->{wiki_token} = [];

This code simply deactivates the token list. No more tokens!

## Virtual Hosting

Sometimes you want have a machine reachable under different domain names and you
want each domain name to have their own wiki space, automatically. You can do
this by using multiple `--host` options.

README.md  view on Meta::CPAN

      --url=gemini://campaignwiki.org/play/ijirait

You can also use it to stream, i.e. get notified of events in real time:

    ijirait --cert=cert.pem --key=key.pem --stream \
      --url=gemini://campaignwiki.org/play/ijirait/stream

Here are the Debian package names to satisfy the dependencies. Use `cpan` or
`cpanm` to install them.

- [Modern::Perl](https://metacpan.org/pod/Modern%3A%3APerl) from `libmodern-perl-perl`
- [Mojo::IOLoop](https://metacpan.org/pod/Mojo%3A%3AIOLoop) from `libmojolicious-perl`
- [Term::ReadLine::Gnu](https://metacpan.org/pod/Term%3A%3AReadLine%3A%3AGnu) from `libterm-readline-gnu-perl`
- [URI::Escape::XS](https://metacpan.org/pod/URI%3A%3AEscape%3A%3AXS) from `liburi-escape-xs-perl`
- [Encode::Locale](https://metacpan.org/pod/Encode%3A%3ALocale) from `libencode-locale-perl`
- [Text::Wrapper](https://metacpan.org/pod/Text%3A%3AWrapper) from `libtext-wrapper-perl`

# phoebe-ctl

This script helps you maintain your Phoebe installation.

README.md  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
`footer` function is redefined. At the same time, the `@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");

README.md  view on Meta::CPAN

    EOT

This example also shows how to redefine existing code in your config file
without the warning "Subroutine … redefined".

Here's a more elaborate example to add a new action the main menu and a handler
for it, for Gemini only:

    # tested by t/example-new-action.t
    package App::Phoebe;
    use Modern::Perl;
    our (@extensions, @main_menu);
    push(@main_menu, "=> gemini://localhost/do/test Test");
    push(@extensions, \&serve_test);
    sub serve_test {
      my $stream = shift;
      my $url = shift;
      my $headers = shift;
      my $host = host_regex();
      my $port = port($stream);
      if ($url =~ m!^gemini://($host)(?::$port)?/do/test$!) {

README.md  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 `finger` port in the example. This works, but it's awkward since you
have to finger `page/alex` instead of `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.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.pm  view on Meta::CPAN

    EOT

This example also shows how to redefine existing code in your config file
without the warning "Subroutine … redefined".

Here's a more elaborate example to add a new action the main menu and a handler
for it, for Gemini only:

    # tested by t/example-new-action.t
    package App::Phoebe;
    use Modern::Perl;
    our (@extensions, @main_menu);
    push(@main_menu, "=> gemini://localhost/do/test Test");
    push(@extensions, \&serve_test);
    sub serve_test {
      my $stream = shift;
      my $url = shift;
      my $headers = shift;
      my $host = host_regex();
      my $port = port($stream);
      if ($url =~ m!^gemini://($host)(?::$port)?/do/test$!) {

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

	$stream->write("Test\n");
	return 1;
      }
      return;
    }
    1;

=cut

package App::Phoebe;
use Modern::Perl '2018';
use File::Slurper qw(read_text read_binary read_lines read_dir write_text write_binary);
use Encode qw(encode_utf8 decode_utf8);
use Net::IDN::Encode qw(domain_to_ascii);
use Socket qw(:addrinfo SOCK_RAW);
use List::Util qw(first min any);
use File::ReadBackwards;
use Algorithm::Diff;
use URI::Escape;
use Mojo::IOLoop;
use Mojo::Log;

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/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);
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(encode_utf8);
use Modern::Perl;
use Mojo::JSON qw(decode_json encode_json);

# galleries

push(@extensions, \&galleries);

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

sub gallery_title {

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/Gopher.pm  view on Meta::CPAN

      }
      return 0;
    }
    use App::Phoebe::Gopher;

=cut

package App::Phoebe::Gopher;
use App::Phoebe qw(get_ip_numbers $log $server @extensions port space pages blog_pages
		   space_regex reserved_regex run_extensions text search);
use Modern::Perl;
use URI::Escape;
use List::Util qw(min);
use Encode qw(encode_utf8 decode_utf8);
use Text::Wrapper;
use utf8;

our $gopher_header = "iPhlog:\n"; # must start with 'i'
our $gopher_port ||= 70;
our $gophers_port = [];
our $gopher_host;

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

    use App::Phoebe::HeapDump;

Once have restarted the server, L<gemini://localhost/do/heap-dump> will write a
heap dump to its wiki data directory. See L<Devel::MAT::UserGuide> for more.

=cut

package App::Phoebe::HeapDump;
use App::Phoebe qw(@extensions $server $log @known_fingerprints
		   port host_regex space_regex success result);
use Modern::Perl;
use Devel::MAT::Dumper;

our @known_fingerprints;

# order is important: we must be able to reset the stats for tests
push(@extensions, \&heap_dump);

sub heap_dump {
  my $stream = shift;
  my $url = shift;

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

serve just one of them:

    package App::Phoebe::Ijirait;
    our $host = "campaignwiki.org";
    use App::Phoebe::Ijirait;

=cut

package App::Phoebe::Ijirait;
use App::Phoebe qw(@extensions $log $server @request_handlers success result);
use Modern::Perl;
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 List::Util qw(first);
use Graph::Easy;
use URI::Escape;
use utf8;

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

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);
use Mojo::UserAgent;
use Modern::Perl;
use MIME::Base64;
use URI::Escape;
use List::Util qw(uniq);
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/PageHeadings.pm  view on Meta::CPAN


Beware the limitations:

The code doesn’t do the same for requests over the web.

=cut

package App::Phoebe::PageHeadings;
use App::Phoebe qw(@extensions $server $log port host_regex space_regex success
		   blog_pages text print_link footer);
use Modern::Perl;
use List::Util qw(min);
use Encode qw(encode_utf8);

# Blogging where the first level headers in the page takes precedence over the
# filename.

push(@extensions, \&serve_minimal_main_menu);

# We want to serve a different main page if no page was specified.
sub serve_minimal_main_menu {

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


If you allow editing via the web using L<App::Phoebe::WebEdit>, then those are
not affected, since these edits use HTTP instead of Titan. Thus, people can
still edit pages. B<This is probably not what you want!>

=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 Text::Wrapper;
use utf8;
no warnings 'redefine';

our $spartan_port ||= 300;

use Mojo::IOLoop;

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;
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_lines 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/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/Wikipedia.pm  view on Meta::CPAN

    use App::Phoebe::Wikipedia;

You can also use L<App::Phoebe::Web> in which case web requests will get
redirected to the actual Wikipedia.

=cut

package App::Phoebe::Wikipedia;
use App::Phoebe qw($log @extensions $full_url_regex success result gemini_link);
use URI::Escape;
use Modern::Perl;
use MediaWiki::API;
use Text::SpanningTable;
use List::Util qw(sum min max);
use Encode;

our $host;

# Wikipedia

push(@extensions, \&wikipedia);

script/gemini  view on Meta::CPAN


=head2 Client Certificates

You can provide a certificate and a key file:

        gemini --cert_file=cert.pem --key_file=key.pem \
          gemini://campaignwiki.org/play/ijirait

=cut

use Modern::Perl '2018';
use Mojo::IOLoop;
use Pod::Text;
use Getopt::Long;
use Encode::Locale qw(decode_argv);
use Encode qw(decode decode_utf8 encode_utf8);
use Net::IDN::Encode qw(:all);
use URI::Escape;
use IRI;

my $cert;

script/gemini-chat  view on Meta::CPAN


To generate your client certificate for 100 days and using “Alex” as your common
name:

    openssl req -new -x509 -newkey ec -subj "/CN=Alex" \
      -pkeyopt ec_paramgen_curve:prime256v1 -days 100 \
      -nodes -out cert.pem -keyout key.pem

=cut

use Modern::Perl '2018';
use Mojo::IOLoop;
use Pod::Text;
use Getopt::Long;
use Term::ReadLine;
use Encode::Locale qw(decode_argv);
use Encode qw(decode_utf8 encode_utf8);
use Net::IDN::Encode qw(:all);
use URI::Escape;
use IRI;

script/ijirait  view on Meta::CPAN

You can also use it to stream, i.e. get notified of events in real time:

    ijirait --cert=cert.pem --key=key.pem --stream \
      --url=gemini://campaignwiki.org/play/ijirait/stream

Here are the Debian package names to satisfy the dependencies. Use C<cpan> or
C<cpanm> to install them.

=over

=item L<Modern::Perl> from C<libmodern-perl-perl>

=item L<Mojo::IOLoop> from C<libmojolicious-perl>

=item L<Term::ReadLine::Gnu> from C<libterm-readline-gnu-perl>

=item L<URI::Escape> from C<liburi-escape-xs-perl>

=item L<Encode::Locale> from C<libencode-locale-perl>

=item L<Text::Wrapper> from C<libtext-wrapper-perl>

=back

=cut

use Modern::Perl '2018';
use Mojo::IOLoop;
use Pod::Text;
use Getopt::Long;
use Term::ReadLine; # install Term::ReadLine::Gnu
use Term::ANSIColor qw(colorstrip colored);
use URI::Escape qw(uri_escape uri_unescape);
use Encode::Locale;
use Encode qw(decode_utf8 encode_utf8 decode encode);
use Text::Wrapper;
use File::Slurper qw(read_text write_text);



( run in 0.509 second using v1.01-cache-2.11-cpan-4d50c553e7e )