App-phoebe

 view release on metacpan or  search on metacpan

script/phoebe  view on Meta::CPAN

Known clients:

This repository comes with a Perl script called F<titan> to upload files.
L<https://alexschroeder.ch/cgit/phoebe/plain/titan>

I<Gemini Write> is an extension for the Emacs Gopher and Gemini client
I<Elpher>. L<https://alexschroeder.ch/cgit/gemini-write/>
L<https://thelambdalab.xyz/elpher/>

Gemini & Titan for Bash are two shell functions that allow you to download and
upload files. L<https://alexschroeder.ch/cgit/gemini-titan/about/>

=head2 Editing via the web

The Configuration section of the Phoebe space on I<The Transjovian Council> has
an example config on how to enable editing via the web.

=over

=item * L<https://transjovian.org:1965/phoebe/page/Configuration>

=item * L<gemini://transjovian.org/phoebe/page/Configuration>

=back

=head1 INSTALLATION

Using C<cpan>:

    cpan App::phoebe

Manual install:

    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.

script/phoebe  view on Meta::CPAN

certificates for the hosts. If you want to use just one certificate for all your
hosts, you don't need to do anything else. If you want to use different
certificates for different hosts, you have to specify them all on the command
line. Generally speaking, use C<--host> to specifiy one or more hosts, followed
by both C<--cert_file> and C<--key_file> to specifiy the certificate and key to
use for the hosts.

For example:

    phoebe --host=transjovian.org \
        --cert_file=/var/lib/dehydrated/certs/transjovian.org/cert.pem \
        --key_file=/var/lib/dehydrated/certs/transjovian.org/privkey.pem \
        --host=alexschroeder.ch \
        --cert_file=/var/lib/dehydrated/certs/alexschroeder.ch/cert.pem \
        --key_file=/var/lib/dehydrated/certs/alexschroeder.ch/privkey.pem

=head1 SEE ALSO

As you might have guessed, the system is easy to tinker with, if you know some
Perl. The Transjovian Council has a wiki space dedicated to Phoebe, and it
includes a section with more configuration examples.
See L<gemini://transjovian.org/> or L<https://transjovian.org:1965/>.

=head1 LICENSE

GNU Affero General Public License

=cut

use App::Phoebe qw($log $server host_regex handle_request get_ip_numbers);
use Modern::Perl '2018';
use File::Slurper qw(read_dir);
use Encode qw(decode);
use Encode::Locale;
use IO::Socket::SSL;
use Mojo::IOLoop;
use Getopt::Long;
use Pod::Text;
use utf8;
use B;

# Some of these need to be decoded (hostnames, pagenames).
GetOptions(
  $server,
  'help' => \&help,
  'log_level=s' => sub { $log->level($_[1]), },
  'log_file=s' => sub { $log->path($_[1]) },
  'cert_file=s' => \&host_setup,
  'key_file=s' => \&host_setup,
  'host=s' => \&host_setup,
  'port=i@', # same ports for all hosts!
  'wiki_dir=s',
  'wiki_space=s@' => \&utf8_list_item,
  'wiki_token=s@' => \&utf8_list_item,
  'wiki_page=s@' => \&utf8_list_item,
  'wiki_main_page=s' => \&utf8_item,
  'wiki_mime_type=s@',
  'wiki_page_size_limit=i')
    or die("Error in command line arguments\n");

sub utf8_list_item { my ($key, $value) = @_; push(@{$server->{$key}}, decode(locale => $value)) };
sub utf8_item { my ($key, $value) = @_; $server->{$key} = decode(locale => $value) };

{
  # use a block so that these variables stay local
  my ($cert_file, $key_file, @host);

  sub host_setup {
    my ($opt, $val) = @_;
    if ($opt eq 'host') {
      push @host, decode(locale => $val);
      return;
    };
    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 {



( run in 0.534 second using v1.01-cache-2.11-cpan-ceb78f64989 )