Compare commits

...

2 Commits

3 changed files with 41 additions and 24 deletions

View File

@ -4,6 +4,7 @@ Dexter is a Mojolicious webapp that handles the generation of index pages under
## Setup ## Setup
Dexter depends on these Perl modules: Dexter depends on these Perl modules:
* Cwd * Cwd
* Encode
* Readonly * Readonly
* File::Path * File::Path
* File::Spec * File::Spec
@ -35,6 +36,7 @@ Dexter needs to know about two directories you want it to use:
### Other ### Other
* `$MAX_REQUEST_SIZE`: The maximum size of POST requests that Dexter will process. * `$MAX_REQUEST_SIZE`: The maximum size of POST requests that Dexter will process.
* `$FS_ENCODING`: The character encoding that the underlying filesystem uses to store filenames. The default value of `UTF-8` is most likely correct.
## License ## License
Dexter is released under Version 3 of the GNU Affero General Public License (see `LICENSE`). The default font- JetBrainsMono from NerdFonts- is released under Version 1.1 of the SIL Open Font License (see `app/public/.assets/FONT-LICENSE`). Dexter is released under Version 3 of the GNU Affero General Public License (see `LICENSE`). The default font- JetBrainsMono from NerdFonts- is released under Version 1.1 of the SIL Open Font License (see `app/public/.assets/FONT-LICENSE`).

View File

@ -4,6 +4,9 @@ use Module::Installed::Tiny qw(module_installed);
use Mojolicious::Lite -signatures; use Mojolicious::Lite -signatures;
use Encode::Locale;
use Encode;
use Readonly; use Readonly;
use Cwd; use Cwd;
@ -34,6 +37,7 @@ Readonly::Hash my %MEANING_OF_HTTP_CODE => (
Readonly my $ROOT_DIRECTORY => 'public'; Readonly my $ROOT_DIRECTORY => 'public';
Readonly my $SOCKET_DIRECTORY => '/var/www/run'; Readonly my $SOCKET_DIRECTORY => '/var/www/run';
Readonly my $TEMP_DIRECTORY => '/tmp/dexter'; Readonly my $TEMP_DIRECTORY => '/tmp/dexter';
Readonly my $FS_ENCODING => 'UTF-8';
Readonly my $MAX_REQUEST_SIZE => $GIGABYTE; Readonly my $MAX_REQUEST_SIZE => $GIGABYTE;
mkdir $TEMP_DIRECTORY; mkdir $TEMP_DIRECTORY;
@ -339,7 +343,7 @@ sub make_size_human_readable ($size) {
sub user_save_file_to_path ($user, $file, $path) { sub user_save_file_to_path ($user, $file, $path) {
my ( $code, $message ) = check_user_can_create_file($user, $path); my ( $code, $message ) = check_user_can_create_file($user, $path);
( $code, $message ) = check_dexter_can_edit_file($path); ( $code, $message ) = check_dexter_can_create_file($path);
return $code, $message if $code != 200; return $code, $message if $code != 200;
my $path_string = $ROOT_DIRECTORY . $path->to_route; my $path_string = $ROOT_DIRECTORY . $path->to_route;
@ -351,7 +355,7 @@ sub user_save_file_to_path ($user, $file, $path) {
sub user_mkdir_at_path ($user, $path) { sub user_mkdir_at_path ($user, $path) {
my ( $code, $message ) = check_user_can_create_file($user, $path); my ( $code, $message ) = check_user_can_create_file($user, $path);
( $code, $message ) = check_dexter_can_edit_file($path); ( $code, $message ) = check_dexter_can_create_file($path);
return $code, $message if $code != 200; return $code, $message if $code != 200;
my $path_string = $ROOT_DIRECTORY . $path->to_route; my $path_string = $ROOT_DIRECTORY . $path->to_route;
@ -388,7 +392,7 @@ sub user_move_path_to_path ($user, $path, $new_path) {
return $code, $message if $code != 200; return $code, $message if $code != 200;
( $code, $message ) = check_user_can_create_file($user, $new_path); ( $code, $message ) = check_user_can_create_file($user, $new_path);
( $code, $message ) = check_dexter_can_edit_file($new_path); ( $code, $message ) = check_dexter_can_create_file($new_path);
return $code, $message if $code != 200; return $code, $message if $code != 200;
my $path_string = $ROOT_DIRECTORY . $path->to_route; my $path_string = $ROOT_DIRECTORY . $path->to_route;
@ -469,21 +473,22 @@ sub get_file_with_name ($name) {
} }
my %file = ( my %file = (
name => $name, name => $name,
device => $device, name_dec => Encode::decode($FS_ENCODING, $name),
inode => $inode, device => $device,
mode => $mode, inode => $inode,
nlink => $nlink, mode => $mode,
uid => $uid, nlink => $nlink,
gid => $gid, uid => $uid,
rdev => $rdev, gid => $gid,
size => $size, rdev => $rdev,
atime => $atime, size => $size,
mtime => $mtime, atime => $atime,
ctime => $ctime, mtime => $mtime,
blksize => $blksize, ctime => $ctime,
blocks => $blocks, blksize => $blksize,
type => $type, blocks => $blocks,
type => $type,
); );
return \%file; return \%file;
@ -620,6 +625,16 @@ sub check_dexter_can_edit_file ($path) {
return 200, ''; return 200, '';
} }
sub check_dexter_can_create_file ($path) {
my $path_string = $path->clone->trailing_slash(0)->to_dir->to_route;
if ( not -w $ROOT_DIRECTORY . $path_string ) {
return 500, "Dexter cannot create the file at '$path_string'!";
}
return 200, '';
}
if ( module_installed('OpenBSD::Unveil') ) { if ( module_installed('OpenBSD::Unveil') ) {

View File

@ -2,14 +2,14 @@
<html lang="en_US"> <html lang="en_US">
<head> <head>
<title>Index of <%= $c->req->url->path->to_string %></title> <title>Index of <%= $c->req->url->path->to_route %></title>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="stylesheet" href="/.assets/style.css" /> <link rel="stylesheet" href="/.assets/style.css" />
<script src="/.assets/main.js"></script> <script src="/.assets/main.js"></script>
</head> </head>
<body> <body>
<header> <header>
<h1>Index of <%= $c->req->url->path->to_string %></h1> <h1>Index of <%= $c->req->url->path->to_route %></h1>
</header> </header>
<article> <article>
<table> <table>
@ -64,8 +64,8 @@
% foreach my $file (@$FILES) { % foreach my $file (@$FILES) {
<tr> <tr>
<td class="left"> <td class="left">
<a href="<%= $file->{name} %>"> <a href="<%= $file->{name_dec} %>">
<%= $file->{name} %> <%= $file->{name_dec} %>
</a> </a>
<span class="buttons"> <span class="buttons">
% if ( permission_available_at_path('MOVE', $file->{name}) ) { % if ( permission_available_at_path('MOVE', $file->{name}) ) {
@ -73,7 +73,7 @@
<a class="button" onclick="file_move(this)">&nbsp;✎&nbsp;</a> <a class="button" onclick="file_move(this)">&nbsp;✎&nbsp;</a>
<form action="./" method="post"> <form action="./" method="post">
<input type="hidden" name="intent" value="move" /> <input type="hidden" name="intent" value="move" />
<input type="hidden" name="target_path" value="<%= $file->{name} %>" /> <input type="hidden" name="target_path" value="<%= $file->{name_dec} %>" />
<input type="hidden" name="dest_path" value="" /> <input type="hidden" name="dest_path" value="" />
</form> </form>
</span> </span>
@ -83,7 +83,7 @@
<a class="button" onclick="file_delete(this)">&nbsp;✖&nbsp;</a> <a class="button" onclick="file_delete(this)">&nbsp;✖&nbsp;</a>
<form action="./" method="post"> <form action="./" method="post">
<input type="hidden" name="intent" value="delete" /> <input type="hidden" name="intent" value="delete" />
<input type="hidden" name="target_path" value="<%= $file->{name} %>" /> <input type="hidden" name="target_path" value="<%= $file->{name_dec} %>" />
</form> </form>
</span> </span>
% } % }