You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
4.3 KiB
164 lines
4.3 KiB
package LDV::Zerobin; |
|
|
|
use strict; |
|
use warnings; |
|
use utf8; |
|
|
|
use POSIX qw(strftime); |
|
use File::Temp qw(tempfile); |
|
use Mojo::Base 'Mojolicious::Controller'; |
|
use Mojo::Asset::File; |
|
use Mojo::Util qw(b64_encode b64_decode decode encode); |
|
|
|
sub _content_path { |
|
my ($self, $time) = @_; |
|
|
|
return $self->app->home->rel_file("public/zerobin/$time.txt"); |
|
} |
|
|
|
sub _content_save { |
|
my ($self, $time, $content) = @_; |
|
|
|
unless (ref $content) { |
|
my $asset = Mojo::Asset::File->new; |
|
utf8::encode($content); |
|
$asset->add_chunk($content); |
|
$content = $asset; # wrap plaintext to Mojo::Asset |
|
} |
|
my $path = $self->_content_path($time); |
|
$content->move_to($path); |
|
|
|
return 1; |
|
} |
|
|
|
sub _content_load { |
|
my ($self, $time) = @_; |
|
|
|
my $path = $self->_content_path($time); |
|
die("paste content not found\n") unless (-f $path); |
|
my $asset = Mojo::Asset::File->new(path => $path); |
|
$asset->cleanup(0); |
|
my $content = $asset->slurp; |
|
utf8::decode($content); |
|
|
|
return $content; |
|
} |
|
|
|
sub create { |
|
my ($self) = @_; |
|
|
|
$self->access_allowed($self->app->config->{zerobin}->{access}) |
|
or return; |
|
|
|
my @syntax = qw(auto); |
|
my $syntax = [ @{ $self->app->config->{zerobin}->{syntax} } ]; |
|
while (my ($cat, $list) = splice(@{ $syntax }, 0, 2)) { |
|
next unless $cat and ref($list) and ref($list) eq 'ARRAY'; |
|
push @syntax, $self->c($cat => $list); |
|
} |
|
|
|
$self->stash({syntax => \@syntax}); |
|
$self->render; |
|
} |
|
|
|
sub view { |
|
my ($self) = @_; |
|
my $time = $self->stash('time') || 0; |
|
|
|
eval { |
|
my $paste = $self->app->db->select('zerobin', '*', {created => $time})->hash; |
|
die("paste not found\n") unless $paste; |
|
$paste->{data} = $self->_content_load($paste->{created}); |
|
$self->stash({paste => $paste}); 1; |
|
} or do { |
|
chomp $@; |
|
$self->app->log->error($@); |
|
$@ = "internal error" if ($@ =~ m|at \S+ line \d+|oi); |
|
$self->flash({'result' => $@}); |
|
$self->redirect_to("/zerobin"); |
|
return; |
|
}; |
|
|
|
$self->render; |
|
} |
|
|
|
sub save { |
|
my ($self) = @_; |
|
|
|
$self->access_allowed($self->app->config->{zerobin}->{access}) |
|
or return; |
|
|
|
eval { |
|
my $source = $self->req->param('source') || '-'; |
|
my $expire = $self->req->param('expire') || 30; # 30 days or 1 month |
|
my $syntax = $self->req->param('syntax') || ''; |
|
my $paste; |
|
if ($source eq 'form') { |
|
$paste = $self->req->param('paste') || ''; |
|
die("empty paste\n") unless $paste; |
|
} elsif ($source eq 'file') { |
|
require File::MimeInfo::Magic; |
|
my ($fh, $tmpfile) = tempfile(); |
|
my $maxsize = $self->app->config->{zerobin}->{maxsize}; |
|
my $upload = $self->req->upload('file'); |
|
die("empty uploaded file\n") |
|
if $upload->size == 0; |
|
die("uploaded file too large\n") |
|
if $upload->size > $maxsize; |
|
$upload->move_to($tmpfile); |
|
$upload->asset->cleanup(1) |
|
if $upload->asset->SUPER::can('cleanup'); |
|
my $mime = File::MimeInfo::Magic::mimetype($tmpfile); |
|
die("uploaded file not looks like text\n") |
|
unless $mime =~ m{^text/}; |
|
$paste = $upload->asset; |
|
unlink $tmpfile; |
|
} else { |
|
die("unknown 'source'\n"); |
|
} |
|
$expire = 30 unless ($expire =~ m|^\d+$|o); |
|
$syntax = 'auto' unless ($syntax =~ m|^[a-z0-9]+$|oi); |
|
|
|
my $time = time(); |
|
$self->app->db->insert('zerobin', { |
|
created => $time, syntax => $syntax, |
|
expire => $time + ($expire * 86400), |
|
}); |
|
$self->_content_save($time, $paste); |
|
$self->redirect_to("/zerobin/$time"); 1; |
|
} or do { |
|
chomp $@; |
|
$self->app->log->error($@); |
|
$@ = "internal error" if ($@ =~ m|at \S+ line \d+|oi); |
|
$self->flash({'result' => $@}); |
|
$self->redirect_to("/zerobin"); |
|
}; |
|
|
|
$self->rendered; |
|
} |
|
|
|
sub prune { |
|
my ($self) = @_; |
|
|
|
eval { |
|
my @expired = $self->app->db->select('zerobin', '*', {expire => {'<=' => time()}})->hashes; |
|
foreach my $paste (@expired) { |
|
my $path = $self->_content_path($paste->{created}); |
|
$self->app->log->info("Removing expired paste: $path"); |
|
unlink $path if -f $path; |
|
$self->app->db->delete('zerobin', {id => $paste->{id}}); |
|
} |
|
my $msg = sprintf '%d records pruned', scalar @expired; |
|
$self->app->log->info($msg); 1; |
|
} or do { |
|
chomp $@; |
|
$self->app->log->error($@); |
|
$@ = "internal error" if ($@ =~ m|at \S+ line \d+|oi); |
|
$self->stash({result => $@}); |
|
}; |
|
|
|
$self->redirect_to("/zerobin"); |
|
$self->rendered; |
|
} |
|
|
|
1;
|
|
|