App-Phoebe

 view release on metacpan or  search on metacpan

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


sub mime_type {
  $_ = shift;
  my $mime = globs($_);
  return $mime if $mime;
  # fallback
  return 'text/gemini' if /\.gmi$/i;
  return 'text/plain' if /\.te?xt$/i;
  return 'text/markdown' if /\.md$/i;
  return 'text/html' if /\.html?$/i;
  return 'image/png' if /\.png$/i;
  return 'image/jpeg' if /\.jpe?g$/i;
  return 'image/gif' if /\.gif$/i;
  return 'application/octet-stream';
}

sub capsule_token_cleanup {
  # only keep tokens created in the last 10 minutes
  my $ts = time - 600;
  @capsule_tokens = grep { $_->[0] > $ts } @capsule_tokens;
}

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

    } else {
      $stream->write(encode_utf8 "# " . gallery_title($dir) . "\n");
    }
    for my $image (@{$data->{data}}) {
      $stream->write("\n");
      $stream->write(encode_utf8 join("\n", grep /\S/, @{$image->{caption}}) . "\n") if $image->{caption};
      gallery_print_link($stream, $host, "Thumbnail", "do/gallery/$dir/" . $image->{thumb}->[0]);
      gallery_print_link($stream, $host, "Image", "do/gallery/$dir/" . $image->{img}->[0]);
    }
    return 1;
  } elsif (my ($file, $extension) = $url =~ m!^gemini://$host(?::$port)?/do/gallery/([^/?]*/(?:thumbs|imgs)/[^/?]*\.(jpe?g|png))$!i) {
    $file = url_unescape $file; # do not decode UTF-8
    my $name = decode_utf8($file);
    if (not -r "$galleries_dir/$file") {
      $stream->write(encode_utf8 "40 Cannot read $name\r\n");
    } else {
      success($stream, $extension =~ /^png$/i ? "image/png" : "image/jpeg");
      $log->info("Serving image $name");
      $stream->write(read_binary("$galleries_dir/$file"));
    }
    return 1;
  }
  return;
}

sub gallery_print_link {
  my $stream = shift;

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

  return 0;
}

# fallback if File::MimeInfo found no data files
sub mime_type {
  $_ = shift;
  return 'text/gemini' if /\.gmi$/i;
  return 'text/plain' if /\.te?xt$/i;
  return 'text/markdown' if /\.md$/i;
  return 'text/html' if /\.html?$/i;
  return 'image/png' if /\.png$/i;
  return 'image/jpeg' if /\.jpe?g$/i;
  return 'image/gif' if /\.gif$/i;
  return 'text/plain'; # this is what phoebe expects
}

# duplicates functionality from registered_editor_login.pl

sub valid_client_cert {
  my $stream = shift;
  my $host = shift;

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

  return substr($child, 0, length($parent)) eq $parent;
}

# cheap MIME type guessing; alternatively, use File::MimeInfo
sub mime_type {
  $_ = shift;
  return 'text/gemini' if /\.gmi$/i;
  return 'text/plain' if /\.te?xt$/i;
  return 'text/markdown' if /\.md$/i;
  return 'text/html' if /\.html?$/i;
  return 'image/png' if /\.png$/i;
  return 'image/jpeg' if /\.jpe?g$/i;
  return 'image/gif' if /\.gif$/i;
  return 'text/plain'; # or application/octet-stream?
}

1;

script/gemini  view on Meta::CPAN

# Start event loop if necessary
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

# Helper
sub extension {
  $_ = shift;
  return 'gmi' if /text\/gemini/;
  return 'txt' if /text\/plain/;
  return 'md' if /text\/markdown/;
  return 'html' if /text\/html/;
  return 'png' if /image\/png/;
  return 'jpg' if /image\/jpeg/;
  return 'gif' if /image\/gif/;
  return 'txt';
}

script/phoebe  view on Meta::CPAN

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)

=item * C<audio/mpeg> is for sound (usually with the C<mp3> extension)

=back

Let's continue using the setup we used for the L</QUICKSTART> section. Restart
the server and allow photos:

    phoebe --wiki_mime_type=image/jpeg

script/phoebe  view on Meta::CPAN

Test it:

    file --mime-type --brief Pictures/Planets/Juno.jpg

The result is the MIME-type we enabled for our wiki:

    image/jpeg

Here's what happens when you're trying to upload an unsupported MIME-type:

    titan --url=titan://localhost:1965/earth.png \
      --token=hello Pictures/Planets/Earth.png

What you get back explains the problem:

    59 This wiki does not allow image/png

In order to allow such graphics as well, you need to restart Phoebe:

    phoebe --wiki_mime_type=image/jpeg --wiki_mime_type=image/png

Except that in my case, the image is too big:

    59 This wiki does not allow more than 100000 bytes per page

I could scale it down before I upload the image, using C<convert> (which is part
of ImageMagick):

    convert -scale 20% Pictures/Planets/Earth.png earth-small.png

Try again:

    titan --url=titan://localhost:1965/earth.png \
      --token=hello earth-small.png

Alternatively, you can increase the size limit using the
C<--wiki_page_size_limit> option, but you need to restart Phoebe:

    phoebe --wiki_page_size_limit=10000000 \
      --wiki_mime_type=image/jpeg --wiki_mime_type=image/png

Now you can upload about 10MB…

=head1 USING SYSTEMD

Systemd is going to handle daemonisation for us. There's more documentation
available online.
L<https://www.freedesktop.org/software/systemd/man/systemd.service.html>.

Basically, this is the template for our service, assuming that you created a

script/phoebe  view on Meta::CPAN

     --host=toki.transjovian.org \
     --host=vault.transjovian.org \
     --host=communitywiki.org \
     --host=alexschroeder.ch \
     --host=next.oddmuse.org \
     --host=emacswiki.org \
     --cert_file=/home/alex/phoebe/cert.pem \
     --key_file=/home/alex/phoebe/key.pem \
     --wiki_main_page=Welcome \
     --wiki_page=About \
     --wiki_mime_type=image/png \
     --wiki_mime_type=image/jpeg \
     --wiki_mime_type=audio/mpeg \
     --wiki_space=transjovian.org/test \
     --wiki_space=transjovian.org/phoebe \
     --wiki_space=transjovian.org/anthe \
     --wiki_space=transjovian.org/gemini \
     --wiki_space=transjovian.org/titan

=head2 Certificates and File Permission

t/Galleries.t  view on Meta::CPAN

require './t/test.pl';

# variables set by test.pl
our $base;
our $dir;

# setup
mkdir("$dir/galleries");
mkdir("$dir/galleries/one");
write_text("$dir/galleries/one/data.json", <<EOT);
{"data":[{"blur":"blurs\/P3111203.jpg","caption":["Grapsus grapsus atop a marine iguana",""],"date":"2020-03-11 16:54","img":["imgs\/P3111203.jpg",[1600,1200]],"original":"P3111203.JPG","sha256":"0192dd7efd4b19e404cadbd3e797355836ee142cdb2eece87b5a23...
EOT
mkdir("$dir/galleries/one/thumbs");
write_text("$dir/galleries/one/thumbs/P3111203.jpg", "TEST");
write_text("$dir/galleries/one/thumbs/hëad space.jpg", "TËST");

my $page = query_gemini("$base/do/gallery");
like($page, qr/^20/, "Galleries main page");
like($page, qr/^# Galleries/m, "Main title");
like($page, qr/^=> $base\/do\/gallery\/one One/m, "Link to album");

t/Oddmuse.t  view on Meta::CPAN

$page = App::Phoebe::Oddmuse::oddmuse_gemini_text(undef, $host, "", qq{
This is a list.

* first
- second

And this is the end.});
like($page, qr(^This is a list\.\n\n\* first\n\* second\n\nAnd this is the end\.$)m, "list");

$page = App::Phoebe::Oddmuse::oddmuse_gemini_text(undef, $host, "", qq{
[[image:https://alexschroeder.ch/podcast/logo-small.png]]
x});
like($page, qr(^=> https://alexschroeder.ch/podcast/logo-small.png https://alexschroeder.ch/podcast/logo-small.png \(image\)\n)m, "image 1a");
like($page, qr(^x$)m, "image x");

$page = App::Phoebe::Oddmuse::oddmuse_gemini_text(undef, $host, "", qq{
[[image:https://alexschroeder.ch/podcast/logo-small.png|Halberds and Helmets]]
x});
like($page, qr(^=> https://alexschroeder.ch/podcast/logo-small.png Halberds and Helmets \(image\)\n)m, "image 2a");
like($page, qr(^x$)m, "image x");

$page = App::Phoebe::Oddmuse::oddmuse_gemini_text(undef, $host, "", qq{
[[image:https://alexschroeder.ch/podcast/logo-small.png|Halberds and Helmets|Halberds and Helmets Podcast]]
x});
like($page, qr(^=> https://alexschroeder.ch/podcast/logo-small.png Halberds and Helmets \(image\)\n)m, "image 3a1");
like($page, qr(^=> gemini://localhost:1965/page/Halberds_and_Helmets_Podcast Halberds and Helmets \(follow-up\)\n)m, "image 3a2");
like($page, qr(^x$)m, "image x");

$page = App::Phoebe::Oddmuse::oddmuse_gemini_text(undef, $host, "", qq{
[[image/right:https://alexschroeder.ch/podcast/logo-small.png]]
x});
like($page, qr(^=> https://alexschroeder.ch/podcast/logo-small.png https://alexschroeder.ch/podcast/logo-small.png \(image\)\n)m, "image 1b");
like($page, qr(^x$)m, "image x");

$page = App::Phoebe::Oddmuse::oddmuse_gemini_text(undef, $host, "", qq{
[[image/right:https://alexschroeder.ch/podcast/logo-small.png|Halberds and Helmets]]
x});
like($page, qr(^=> https://alexschroeder.ch/podcast/logo-small.png Halberds and Helmets \(image\)\n)m, "image 2b");
like($page, qr(^x$)m, "image x");

$page = App::Phoebe::Oddmuse::oddmuse_gemini_text(undef, $host, "", qq{
[[image/right:https://alexschroeder.ch/podcast/logo-small.png|Halberds and Helmets|Halberds and Helmets Podcast]]
x});
like($page, qr(^=> https://alexschroeder.ch/podcast/logo-small.png Halberds and Helmets \(image\)\n)m, "image 3b1");
like($page, qr(^=> gemini://localhost:1965/page/Halberds_and_Helmets_Podcast Halberds and Helmets \(follow-up\)\n)m, "image 3b");
like($page, qr(^x$)m, "image x");

done_testing();

t/basics.t  view on Meta::CPAN


$page = query_gemini("$base/page/Bread");
like($page, qr/^20 text\/gemini; charset=UTF-8\r\n# Bread\n$haiku/, "Bread haiku saved");

# upload image

my $data = read_binary("t/alex.jpg");
my $size = length($data);
$page = query_gemini("$titan/raw/Alex;size=$size;token=hello", $data);
like($page, qr/^59 The text is invalid UTF-8/, "Upload image without MIME type");
$page = query_gemini("$titan/raw/Alex;size=$size;mime=image/png;token=hello", $data);
like($page, qr/^59 This wiki does not allow image\/png/, "Upload image with wrong MIME type");
$page = query_gemini("$base/page/Alex");
like($page, qr/This page does not yet exist/, "Save of unsupported MIME type failed");

$page = query_gemini("$titan/raw/Alex;size=$size;mime=image/jpeg;token=hello", $data);
like($page, qr/^30 $base\/file\/Alex\r/, "Upload image");

# fake creation of some files for the blog

for (qw(2017-12-25 2017-12-26 2017-12-27)) {
  write_text("$dir/page/$_.gmi", "yo");

t/oddmuse-wiki.pl  view on Meta::CPAN

our $PageCluster = '';              # name of cluster page, eg. 'Cluster' to enable
our $InterWikiMoniker = '';        	# InterWiki prefix for this wiki for RSS
our $SiteDescription  = '';        	# RSS Description of this wiki
our $RssStrip = '^\d\d\d\d-\d\d-\d\d_'; # Regexp to strip from feed item titles
our $RssImageUrl      = $LogoUrl;  	# URL to image to associate with your RSS feed
our $RssRights        = '';        	# Copyright notice for RSS, usually an URL to the appropriate text
our $RssExclude       = 'RssExclude'; # name of the page that lists pages to be excluded from the feed
our $RssCacheHours    =  1;        	# How many hours to cache remote RSS files
our $RssStyleSheet    = '';        	# External style sheet for RSS files
our $UploadAllowed    =  0;        	# 1 = yes, 0 = administrators only
our @UploadTypes = ('image/jpeg', 'image/png'); # MIME types allowed, all allowed if empty list
our $EmbedWiki         = 0;        	# 1 = no headers/footers
our $FooterNote       = '';        	# HTML for bottom of every page
our $EditNote         = '';        	# HTML notice above buttons on edit page
our $TopLinkBar        = 1;        	# 0 = goto bar both at the top and bottom; 1 = top, 2 = bottom
our $TopSearchForm     = 1;         # 0 = search form both at the top and bottom; 1 = top, 2 = bottom
our $MatchingPages     = 0;         # 1 = search page content and page titles
our @UserGotoBarPages = ();        	# List of pagenames
our $UserGotoBar      = '';        	# HTML added to end of goto bar
our $CommentsPrefix   = '';        	# prefix for comment pages, eg. 'Comments_on_' to enable
our $CommentsPattern = undef;      	# regex used to match comment pages

t/oddmuse-wiki.pl  view on Meta::CPAN

our $JournalLimit    = 200;        	# how many pages can be collected in one go?
our $PageNameLimit   = 120;        	# max length of page name in bytes
$DocumentHeader = "<!DOCTYPE html>\n<html>";
our @MyFooters = (\&GetCommentForm, \&WrapperEnd, \&DefaultFooter);
# Checkboxes at the end of the index.
our @IndexOptions = ();
# Display short comments below the GotoBar for special days
# Example: %SpecialDays = ('1-1' => 'New Year', '1-2' => 'Next Day');
our %SpecialDays = ();
# Replace regular expressions with inlined images
# Example: %Smilies = (":-?D(?=\\W)" => '/pics/grin.png');
our %Smilies = ();
# Detect page languages when saving edits
# Example: %Languages = ('de' => '\b(der|die|das|und|oder)\b');
our %Languages = ();
our @KnownLocks = qw(main diff index merge visitors); # locks to remove
our $LockExpiration = 60; # How long before expirable locks are expired
our %LockExpires = (diff=>1, index=>1, merge=>1, visitors=>1); # locks to expire after some time
our %LockCleaners = (); # What to do if a job under a lock gets a signal like SIGINT. e.g. 'diff' => \&CleanDiff
our %CookieParameters = (username=>'', pwd=>'', homepage=>'', theme=>'', css=>'', msg=>'', lang=>'', embed=>$EmbedWiki,
		     toplinkbar=>$TopLinkBar, topsearchform=>$TopSearchForm, matchingpages=>$MatchingPages, );

t/oddmuse-wiki.pl  view on Meta::CPAN

  # Intersites must start with uppercase letter to avoid confusion with URLs.
  $InterSitePattern = '[A-Z\x{0080}-\x{fffd}]+[A-Za-z\x{0080}-\x{fffd}]+';
  $InterLinkPattern = "($InterSitePattern:[-a-zA-Z0-9\x{0080}-\x{fffd}_=!?#\$\@~`\%&*+\\/:;.,]*[-a-zA-Z0-9\x{0080}-\x{fffd}_=#\$\@~`\%&*+\\/])$QDelim";
  $FreeInterLinkPattern = "($InterSitePattern:[-a-zA-Z0-9\x{0080}-\x{fffd}_=!?#\$\@~`\%&*+\\/:;.,()' ]+)"; # plus space and other characters, and no restrictions on the end of the pattern
  $UrlProtocols = 'https?|ftp|afs|news|nntp|mid|cid|mailto|wais|prospero|telnet|gophers?|irc|feed';
  $UrlProtocols .= '|file' if $NetworkFile;
  my $UrlChars = '[-a-zA-Z0-9/@=+$_~*.,;:?!\'"()&#%]'; # see RFC 2396
  my $EndChars = '[-a-zA-Z0-9/@=+$_~*]'; # no punctuation at the end of the url.
  $UrlPattern = "((?:$UrlProtocols):$UrlChars+$EndChars)";
  $FullUrlPattern="((?:$UrlProtocols):$UrlChars+)"; # when used in square brackets
  $ImageExtensions = '(gif|jpg|jpeg|png|bmp|svg)';
}

sub Clean {
  my $block = shift;
  return 0 unless defined($block); # "0" must print
  return 1 if $block eq '';        # '' is the result of a dirty rule
  $Fragment .= $block;
  return 1;
}

t/oddmuse-wiki.pl  view on Meta::CPAN

  return if $OpenPageName eq $id;
  if ($IndexHash{$id}) {
    my $file = GetPageFile($id);
    open(my $FILE, '<:encoding(UTF-8)', encode_utf8($file))
      or ReportError(Ts('Cannot open %s', GetPageFile($id))
		     . ": $!", '500 INTERNAL SERVER ERROR');
    while (defined($_ = <$FILE>) and $_ !~ /^text: /) {
    }          # read lines until we get to the text key
    close $FILE;
    return unless length($_) > 6;
    return TextIsFile(substr($_, 6)); # pass "#FILE image/png\n" to the test
  }
}

sub SearchTitleAndBody {
  my ($regex, $func, @args) = @_;
  my @found;
  my $lang = GetParam('lang', '');
  foreach my $id (Filtered($regex, AllPagesList())) {
    my $name = NormalToFree($id);
    my ($text) = PageIsUploadedFile($id); # set to mime-type if this is an uploaded file



( run in 1.597 second using v1.01-cache-2.11-cpan-df04353d9ac )