App-phoebe
view release on metacpan or search on metacpan
lib/App/Phoebe.pm view on Meta::CPAN
$log->error("Error: $@");
$stream->close_gracefully();
}
sub save_page {
my $stream = shift;
my $host = shift;
my $space = shift;
my $id = shift;
my $type = shift;
my $data = shift;
my $length = shift;
if ($type ne "text/plain") {
if ($length == 0) {
with_lock($stream, $host, $space, sub { delete_file($stream, $host, $space, $id) } );
} else {
with_lock($stream, $host, $space, sub { write_file($stream, $host, $space, $id, $data, $type) } );
}
} elsif ($length == 0) {
with_lock($stream, $host, $space, sub { delete_page($stream, $host, $space, $id) } );
} elsif (utf8::decode($data)) { # decodes in-place and returns success
with_lock($stream, $host, $space, sub { write_page($stream, $host, $space, $id, $data) } );
} else {
$log->debug("The text is invalid UTF-8");
result($stream, "59", "The text is invalid UTF-8");
$stream->close_gracefully();
}
}
# We can't use C<flock> because this defaults to C<fcntl> which means they are
# I<per process>
sub with_lock {
my $stream = shift;
my $host = shift;
my $space = shift;
my $code = shift;
my $count = shift || 0;
my $dir = wiki_dir($host, $space);
my $lock = "$dir/locked";
# remove stale locks
if (-e $lock) {
my $age = time() - modified($lock);
$log->debug("lock is ${age}s old");
rmdir $lock if -e $lock and $age > 5;
}
if (mkdir($lock)) {
$log->debug("Running code with lock $lock");
eval { $code->() }; # protect against exceptions
if ($@) {
$log->error("Unable to run code with locked $lock: $@");
result($stream, "40", "An error occured, unfortunately");
}
rmdir($lock);
$stream->close_gracefully();
} elsif ($count > 25) {
$log->error("Unable to unlock $lock");
result($stream, "40", "The wiki is locked; try again in a few seconds");
$stream->close_gracefully();
} else {
$log->debug("Waiting $count...");
Mojo::IOLoop->timer(0.2 => sub {
with_lock($stream, $host, $space, $code, $count + 1)});
# don't close the stream
}
}
sub write_page {
my $stream = shift;
my $host = shift;
my $space = shift;
my $id = shift;
my $text = shift;
$log->info("Writing page $id");
my $dir = wiki_dir($host, $space);
my $file = "$dir/page/$id.gmi";
my $revision = 0;
if (-e $file) {
my $old = read_text($file);
if ($old eq $text) {
$log->info("$id is unchanged");
result($stream, "30", to_url($stream, $host, $space, "page/$id"));
return;
}
mkdir "$dir/keep" unless -d "$dir/keep";
if (-d "$dir/keep/$id") {
foreach (read_dir("$dir/keep/$id")) {
$revision = $1 if m/^(\d+)\.gmi$/ and $1 > $revision;
}
$revision++;
} else {
mkdir "$dir/keep/$id";
$revision = 1;
}
rename $file, "$dir/keep/$id/$revision.gmi";
} else {
my $index = "$dir/index";
if (not open(my $fh, ">>:encoding(UTF-8)", $index)) {
$log->error("Cannot write index $index: $!");
result($stream, "59", "Unable to write index");
return;
} else {
say $fh $id;
close($fh);
}
}
my $changes = "$dir/changes.log";
if (not open(my $fh, ">>:encoding(UTF-8)", $changes)) {
$log->error("Cannot write log $changes: $!");
result($stream, "59", "Unable to write log");
return;
} else {
my $peerhost = $stream->handle->peerhost;
say $fh join("\x1f", scalar(time), $id, $revision + 1, bogus_hash($peerhost));
close($fh);
}
mkdir "$dir/page" unless -d "$dir/page";
eval { write_text($file, $text) };
if ($@) {
$log->error("Unable to save $id: $@");
result($stream, "59", "Unable to save $id");
} else {
( run in 1.635 second using v1.01-cache-2.11-cpan-39bf76dae61 )