App-phoebe

 view release on metacpan or  search on metacpan

script/phoebe  view on Meta::CPAN

    perl Makefile.PL
    make
    make install

=head2 Dependencies

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

=over

=item * L<Algorithm::Diff>, or C<libalgorithm-diff-xs-perl>

=item * L<File::ReadBackwards>, or C<libfile-readbackwards-perl>

=item * L<File::Slurper>, or C<libfile-slurper-perl>

=item * L<Mojolicious>, or C<libmojolicious-perl>

=item * L<IO::Socket::SSL>, or C<libio-socket-ssl-perl>

=item * L<Modern::Perl>, or C<libmodern-perl-perl>

=item * L<URI::Escape>, or C<liburi-escape-xs-perl>

=item * L<Net::IDN::Encode>, or C<libnet-idn-encode-perl>

=item * L<Encode::Locale>, or C<libencode-locale-perl>

=back

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

The F<update-readme.pl> script I use to generate F<README.md> also requires some
libraries:

=over

=item * L<Pod::Markdown>, or C<libpod-markdown-perl>

=item * L<Text::Slugify>, which has no Debian package, apparently 😭

=back

=head2 Quickstart

I'm going to assume that you're going to create a new user just to be safe.

    sudo adduser --disabled-login --disabled-password phoebe
    sudo su phoebe --shell=/bin/bash
    cd

Now you're in your home directory, F</home/phoebe>. We're going to install
things right here.

    cpan App::phoebe

Start Phoebe. It's going to prompt you for a hostname and create certificates
for you. If in doubt, answer C<localhost>. The certificate and a private key are
stored in the F<cert.pem> and F<key.pem> files, using elliptic curves, valid for
five years, without password protection.

    perl5/bin/phoebe

This starts the server in the foreground. If it aborts, see the
L</Troubleshooting> section below. If it runs, open a second terminal and test
it:

    perl5/bin/gemini gemini://localhost/

You should see a Gemini page starting with the following:

    20 text/gemini; charset=UTF-8
    Welcome to Phoebe!

Success!! 😀 🚀🚀

Let's create a new page using the Titan protocol, from the command line:

    echo "Welcome to the wiki!" > test.txt
    echo "Please be kind." >> test.txt
    perl5/bin/titan --url=titan://localhost/raw/Welcome --token=hello test.txt

You should get a nice redirect message, with an appropriate date.

    30 gemini://localhost:1965/page/Welcome

You can check the page, now (replacing the appropriate date):

    perl5/bin/gemini gemini://localhost:1965/page/Welcome

You should get back a page that starts as follows:

    20 text/gemini; charset=UTF-8
    Welcome to the wiki!
    Please be kind.

Yay! 😁🎉 🚀🚀

If you have a bunch of Gemtext files in a directory, you can upload them all in
one go:

    titan --url=titan://localhost/ --token=hello *.gmi

=head2 Image uploads

OK, how do image uploads work? First, we need to specify which MIME types Phoebe
accepts. The files are going to be served back with that MIME type, so even if
somebody uploads an executable and claim it's an image, other people's clients
will treat it as an image instead of executing it (one hopes!) – so let's start
with a list of common MIME types.

=over

=item * C<image/jpeg> is for photos (usually with the C<jpg> extension)

=item * C<image/png> is for graphics (usually with the C<png> extension)

script/phoebe  view on Meta::CPAN

    die "$val does not exist\n" unless -f $val;
    if ($opt eq 'cert_file') { $cert_file = $val }
    elsif ($opt eq 'key_file') { $key_file = $val }
    if ($cert_file and $key_file) {
      if (not @host) {
	$server->{host}->{'localhost'} = 1;
	$server->{cert_file}->{'localhost'} = $cert_file;
	$server->{key_file}->{'localhost'} = $key_file;
      }
      for (@host) {
	$server->{host}->{$_} = 1;
	$server->{cert_file}->{$_} = $cert_file;
	$server->{key_file}->{$_} = $key_file;
      }
      $cert_file = $key_file = undef;
      @host = ();
    }
  }

  # if, at the end, there is a left-over
  if ($cert_file or $key_file) {
    die "I must have both --key_file and --cert_file\n";
  }

  # let's see if we need to generate certificates
  my $default_certs = 0;

  # if, at the end, we have some hosts but no certs and keys
  for (@host) {
    $default_certs = 1;
    $server->{host}->{$_} = 1;
    $server->{cert_file}->{$_} = 'cert.pem';
    $server->{key_file}->{$_} = 'key.pem';
  }

  # if, at the end, we had no hosts at all, the default still needs cert and key
  if (not keys %{$server->{host}}) {
    $default_certs = 1;
    $server->{host}->{localhost} = 1;
    $server->{cert_file}->{localhost} = 'cert.pem';
    $server->{key_file}->{localhost} = 'key.pem';
  }

  # if the certs don't exist, generate them
  if ($default_certs
      and (not -f 'cert.pem'
	   or not -f 'key.pem')) {
    generate_certificates();
  }
}

sub generate_certificates {
  say "The default certificate (and key) files are missing.";
  say "Do you want to create them right now?";
  say "The certificate uses eliptic curves and is valid for five years.";
  say "If so, please provide your hostname (e.g. localhost).";
  say "If not, just press Enter.";
  local $SIG{'ALRM'} = sub {
    die "Timed out!\n";
  };
  alarm(30); # timeout for the following prompt
  my $hostname = <STDIN>;
  alarm(0);  # done, no more alarm
  chomp $hostname;
  die "The hostname may not contain any whitespace\n" if $hostname =~ /\s/;
  my $cmd = qq(openssl req -new -x509 -newkey ec -subj "/CN=$hostname" )
      . qq(-pkeyopt ec_paramgen_curve:prime256v1 -days 1825 -nodes -out cert.pem -keyout key.pem);
  if ($hostname) {
    say $cmd;
    system($cmd) == 0
      or die "openssl failed: $?";
  }
}

sub help {
  my $parser = Pod::Text->new();
  $parser->parse_file($0);
  exit;
}

sub verify_fingerprint {
  my ($ok, $ctx_store, $certname, $error, $cert, $depth) = @_;
  return 1;
}

# defaults
$server->{port} ||= [1965];
$server->{wiki_token} ||= ['hello'];
$server->{wiki_space} ||= [];
$server->{wiki_mime_type} ||= [];
$server->{wiki_dir} ||= $ENV{PHOEBE_DATA_DIR} || './wiki';
$server->{wiki_page} ||= [];
$server->{wiki_main_page} ||= '';
$server->{wiki_page_size_limit} ||= 100000;

configure();

# Reconfigure if we get SIGHUP (via kill -s SIGHUP $pid, for example)
$SIG{HUP} = sub { Mojo::IOLoop->next_tick(\&configure) };

start_servers();

sub configure {
  # config file with extra code; restart server if you change it
  my $dir = $server->{wiki_dir};
  my @config;
  push(@config, map { "$dir/conf.d/$_" } grep(/\.p[lm]$/, read_dir("$dir/conf.d"))) if -d "$dir/conf.d";
  # allow override of config files in conf.d
  push(@config, "$dir/config") if -f "$dir/config";
  for my $config (@config) {
    $log->info("Running $config");
    $log->error("$config cannot be read") unless -r $config;
    $log->warn("$config did not return a true value") unless do $config;
    $log->error("$@") if $@;
    $log->error("$!") if $!;
  }
  # summarize config results
  $log->info("PID: $$");
  $log->info("Host: " . join(" ", keys %{$server->{host}}));
  $log->info("Port: @{$server->{port}}");
    $log->info("Space: @{$server->{wiki_space}}");



( run in 0.572 second using v1.01-cache-2.11-cpan-0b5f733616e )