a lot of changes, hoo boy.

made the expiry check interval configurable (expire_every in config.yml)
fixed a bug where an expired paste could still be viewed for a short period after expiring in some situations
consolidated the paste fetching and parameter validation functions somewhat
fixed some bugs with expiry handling
hopefully haven't broken anything, changes are untested
This commit is contained in:
Matthew Connelly 2015-11-14 04:39:28 +00:00
parent 986ea029ee
commit e73864fcea
2 changed files with 164 additions and 157 deletions

View File

@ -7,6 +7,9 @@ locale: "en_GB"
logger: "console"
log: "debug"
# Time in seconds between each paste expiry check
expire_every: 600
template: "template_toolkit"
engines:
template:
@ -24,8 +27,8 @@ plugins:
options:
sqlite_unicode: 1
# Comment out for production
show_errors: 1
# Uncomment for debugging
#show_errors: 1
# Sets the expiration for posts
# Options can be any combination of weeks, days, hours, minutes, seconds
@ -34,6 +37,7 @@ expiration:
# Languages supported by Pygments
# TODO: perhaps swap this out with something in App::WerePaste::Util::PygmentsBridge so we can dynamically load supported languages
languages:
- Programming languages:
- ActionScript:

View File

@ -12,7 +12,7 @@ use Data::UUID;
my $lastexpunge = 0;
sub DeploySchema {
# need to find a way to handle this that doesn't shit errors everywhere
# TODO: figure out how to ensure schema is deployed without producing "table already exists" errors when it is already deployed
eval {schema->deploy};
}
sub DateTimeToQueryable {
@ -22,39 +22,47 @@ sub DateTimeToQueryable {
}
sub ExpirationToDate {
my $expire = shift;
$expire = $expire ? { split ':', $expire } : undef;
$expire = $expire ? { split ':', $expire } : config->{default_expire};
return undef if $expire and $expire->{never};
return DateTimeToQueryable(%{ $expire || config->{default_expire} });
return DateTimeToQueryable(%{ $expire });
}
sub GetUUID {
my $uuid = Data::UUID->new->create_str;
$uuid =~ s/\-//g;
return lc $uuid;
}
sub CheckExpired {
return unless time > ($lastexpunge+900); #expunge once every 15 mins
sub CheckExpiry {
return unless time > ($lastexpunge+config->{expire_every});
$lastexpunge = time;
schema->resultset('Paste')->search({expiration => { '<' => DateTimeToQueryable() }})->delete_all;
}
sub ValidateParams {
my $params = shift;
if($params->{id}) {
return undef unless lc($params->{id}) =~ /^[a-f0-9]*$/;
return 1;
}
return undef unless $params->{code};
#TODO: Allow all 'word' characters rather than just a-zA-Z0-9, expanded grammar
## Presently this is limited so people can't do anything nasty.
return undef unless $params->{title} =~ /^[a-zA-Z0-9\.\-_ @\(\)]{0,255}$/;
return undef unless $params->{lang} =~ /^[a-z0-9\.\-\+# ]{0,40}$/;
return undef unless $params->{expiration} =~ /^([a-z]+:[0-9]+)(:[a-z]+:[0-9]+)*$/ or not $params->{expiration};
return 1;
}
sub GetPaste {
my $id = shift;
return schema->resultset('Paste')->single({ id => $id }) or return undef;
my $id = shift; $id = lc $id;
return undef unless $id =~ /^[a-f0-9]*$/;
#This got a bit messy, required because otherwise there are scenarios where an expired paste may still be viewed
return schema->resultset('Paste')->single({
-and => [
{ id => $id },
-or => [
{ expiration => { '>=' => DateTimeToQueryable() }},
{ expiration => undef }
]
]}) or return undef;
}
sub SubmitPaste {
my $params = shift;
my ($lang,$html) = PygmentsHighlight(lang => $params->{lang}, code => $params->{code});
#TODO: maybe figure out a nicer way of doing this, presently the UUID namespace changes with every app start
my $id = GetUUID();
my $result = schema->resultset('Paste')->create({
id => $id,
@ -66,31 +74,26 @@ sub SubmitPaste {
}) or return undef;
return $id;
}
sub ValidateAndGet {
my $params = shift;
ValidateParams($params) or return undef;
return GetPaste(lc $params->{id}) or return undef;
}
# Startup
DeploySchema();
# Hooks
hook 'before' => sub { CheckExpired(); };
hook 'before' => sub { CheckExpiry(); };
# Routes
#get
get '/' => sub { template 'index.tt'; };
get '/:id' => sub { my $paste=ValidateAndGet(scalar params('route')) or pass; template 'show.tt', { paste => $paste };};
get '/:id/copy' => sub { my $paste=ValidateAndGet(scalar params('route')) or pass; template 'index.tt', { paste => $paste }; };
get '/:id/raw' => sub { my $paste=ValidateAndGet(scalar params('route')) or pass; content_type 'text/plain'; return $paste->code; };
get '/:id' => sub { my $paste=GetPaste(scalar params 'route') or pass; template 'show.tt', { paste => $paste }; };
get '/:id/copy' => sub { my $paste=GetPaste(scalar params 'route') or pass; template 'index.tt', { paste => $paste }; };
get '/:id/raw' => sub { my $paste=GetPaste(scalar params 'route') or pass; content_type 'text/plain'; return $paste->code; };
#post
post '/' => sub {
my $p = params('body');
my $p = params 'body';
ValidateParams($p) or return send_error('Submitted paste is not valid. Check your post title and language, and try again.', 400);
my $id = SubmitPaste($p) or return redirect '/503.html';
return redirect "/$id";
};
#default
any qr/.*/ => sub { return send_error('Page gone',404); };
any qr/.*/ => sub { return send_error('What you seek cannot be found here.', 404); };
1;
__END__