mirror of
https://framagit.org/fiat-tux/hat-softwares/lutim.git
synced 2026-03-28 17:42:54 +01:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
438ce5050e | ||
|
|
d67d66d0fd | ||
|
|
cc03ba6d3f | ||
|
|
5b4f56b9f6 | ||
|
|
8b6766f498 | ||
|
|
68518dd85c |
@@ -1,6 +1,12 @@
|
||||
Revision history for Lutim
|
||||
|
||||
0.14.0 ????-??-??
|
||||
0.15.0 ????-??-??
|
||||
- ✨ — Add --nuke option to image command
|
||||
|
||||
0.14.0 2023-12-18
|
||||
- ⬆️ Update dependencies
|
||||
- 💥 BREAKING CHANGE: Use `?_format=json` instead of `?format=json`
|
||||
- 💥 BREAKING CHANGE: Use `?_format=json` instead of terminating the URL with `.json`
|
||||
|
||||
0.13.0 2023-04-26
|
||||
- 💄 — Add Korrigan theme (Nicolas Frandeboeuf)
|
||||
|
||||
1
cpanfile
1
cpanfile
@@ -15,6 +15,7 @@ requires 'Filesys::DiskUsage';
|
||||
requires 'Switch';
|
||||
requires 'Crypt::CBC';
|
||||
requires 'Crypt::Blowfish';
|
||||
requires 'Digest::MD5';
|
||||
requires 'Locale::Maketext';
|
||||
requires 'Locale::Maketext::Extract';
|
||||
requires 'File::MimeInfo';
|
||||
|
||||
3906
cpanfile.snapshot
3906
cpanfile.snapshot
File diff suppressed because it is too large
Load Diff
14
lib/Lutim.pm
14
lib/Lutim.pm
@@ -291,7 +291,7 @@ sub startup {
|
||||
});
|
||||
|
||||
$r->get('/')->
|
||||
over('authorized')->
|
||||
requires('authorized')->
|
||||
to('Image#home')->
|
||||
name('index');
|
||||
$r->get('/')->
|
||||
@@ -330,7 +330,7 @@ sub startup {
|
||||
to('Image#change_lang')->
|
||||
name('lang');
|
||||
|
||||
$r->get('/partial/:file' => sub {
|
||||
$r->get('/partial/<:file>.<:f>' => sub {
|
||||
my $c = shift;
|
||||
$c->render(
|
||||
template => 'partial/'.$c->param('file'),
|
||||
@@ -351,7 +351,7 @@ sub startup {
|
||||
})->name('gallery');
|
||||
|
||||
$r->get('/myfiles')->
|
||||
over('authorized')->
|
||||
requires('authorized')->
|
||||
name('myfiles');
|
||||
$r->get('/myfiles')->
|
||||
to('Authent#index');
|
||||
@@ -369,28 +369,28 @@ sub startup {
|
||||
->name('random');
|
||||
|
||||
$r->post('/')->
|
||||
over('authorized')->
|
||||
requires('authorized')->
|
||||
to('Image#add')->
|
||||
name('add');
|
||||
$r->post('/')->
|
||||
to('Authent#index');
|
||||
|
||||
$r->get('/d/:short/:token')->
|
||||
over('authorized')->
|
||||
requires('authorized')->
|
||||
to('Image#delete')->
|
||||
name('delete');
|
||||
$r->get('/d/:short/:token')->
|
||||
to('Authent#index');
|
||||
|
||||
$r->post('/m/:short/:token')->
|
||||
over('authorized')->
|
||||
requires('authorized')->
|
||||
to('Image#modify')->
|
||||
name('modify');
|
||||
$r->post('/m/:short/:token')->
|
||||
to('Authent#index');
|
||||
|
||||
$r->post('/c')->
|
||||
over('authorized')->
|
||||
requires('authorized')->
|
||||
to('Image#get_counter')->
|
||||
name('counter');
|
||||
$r->post('/c')->
|
||||
|
||||
@@ -46,7 +46,8 @@ sub run {
|
||||
'r|remove=s{1,}' => \my @remove,
|
||||
'y|yes' => \my $yes,
|
||||
'q|quiet' => \my $quiet,
|
||||
's|search=s' => \my $ip
|
||||
's|search=s' => \my $ip,
|
||||
'n|nuke=s' => \my $nuke,
|
||||
;
|
||||
|
||||
if (scalar @info) {
|
||||
@@ -85,9 +86,43 @@ sub run {
|
||||
push @shorts, $e->short;
|
||||
print_infos($e, $csv);
|
||||
});
|
||||
say sprintf('%d matching URLs', $u->size);
|
||||
say sprintf('%d matching images', $u->size);
|
||||
say sprintf("If you want to delete those images, please do:\n carton exec script/lutim image --remove %s", join(' ', @shorts)) if @shorts;
|
||||
}
|
||||
if ($nuke) {
|
||||
my $i = get_short($c, $nuke);
|
||||
if ($i && $i->created_by) {
|
||||
my $u = Lutim::DB::Image->new(app => $c->app)->search_exact_created_by($i->created_by);
|
||||
my @shorts;
|
||||
say sprintf('%d images created by the same IP address (%s) than image %s', $u->size, $i->created_by, $nuke);
|
||||
my $confirm = ($yes) ? 'yes' : undef;
|
||||
unless (defined $confirm) {
|
||||
printf('Are you sure you want to remove those %d images? [N/y] ', $u->size);
|
||||
$confirm = <STDIN>;
|
||||
chomp $confirm;
|
||||
}
|
||||
if ($confirm =~ m/^y(es)?$/i) {
|
||||
$u->each(sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = get_short($c, $e->short);
|
||||
if ($i) {
|
||||
print_infos($i, $csv);
|
||||
if ($i->enabled) {
|
||||
delete_short($c, $i, 1);
|
||||
} else {
|
||||
say sprintf('The image %s is already disabled', $e->short);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
say 'Answer was not "y" or "yes". Aborting deletion.';
|
||||
}
|
||||
} elsif (! $i->created_by) {
|
||||
say sprintf('Image %s does not contain its creator’s IP address.', $nuke);
|
||||
} else {
|
||||
say sprintf('Sorry, can’t find image %s', $nuke);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub get_short {
|
||||
@@ -168,7 +203,7 @@ sub delete_short {
|
||||
|
||||
my $confirm = ($y) ? 'yes' : undef;
|
||||
unless (defined $confirm) {
|
||||
printf('Are you sure you want to remove this image (%s) ? [N/y] ', $i->short);
|
||||
printf('Are you sure you want to remove this image (%s)? [N/y] ', $i->short);
|
||||
$confirm = <STDIN>;
|
||||
chomp $confirm;
|
||||
}
|
||||
@@ -191,6 +226,7 @@ Lutim::Command::image - Manage URL in Lutim's database
|
||||
carton exec script/lutim image --info <short> <short> [--csv] Print infos about the space-separated images (--csv creates a CSV output)
|
||||
carton exec script/lutim image --remove <short> <short> [--yes] [--quiet] Delete the space-separated images (--yes disables confirmation, --quiet disables informations printing)
|
||||
carton exec script/lutim image --search <ip> Print infos about the images uploaded by this IP (database LIKE, may include images uploaded by other IPs)
|
||||
carton exec script/lutim image --nuke <short> Delete the image and all images sent by the same IP address and print infos about the deleted images
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# vim:set sw=4 ts=4 sts=4 expandtab:
|
||||
package Lutim::Controller::Image;
|
||||
use Mojo::Asset::Memory;
|
||||
use Mojo::Base 'Mojolicious::Controller';
|
||||
use Mojo::Util qw(url_escape url_unescape b64_encode encode);
|
||||
use Mojo::Asset::Memory;
|
||||
use Mojo::JSON qw(true false);
|
||||
use Lutim::DB::Image;
|
||||
use DateTime;
|
||||
@@ -487,12 +487,10 @@ sub add {
|
||||
if ($mediatype ne 'image/svg+xml' && $mediatype !~ m#image/(x-)?xcf# && $mediatype ne 'image/webp') {
|
||||
# Remove the EXIF tags
|
||||
my $data = new IO::Scalar \$upload->slurp();
|
||||
my $et = new Image::ExifTool;
|
||||
my $et = Image::ExifTool->new;
|
||||
|
||||
# Use $data in Image::ExifTool object
|
||||
$et->ExtractInfo($data);
|
||||
# Remove all metadata
|
||||
$et->SetNewValue('*', undef);
|
||||
$et->SetNewValue('*');
|
||||
|
||||
# Create a temporary IO::Scalar to write into
|
||||
my $temp;
|
||||
|
||||
@@ -345,6 +345,20 @@ sub to_hash {
|
||||
|
||||
=back
|
||||
|
||||
=head2 search_exact_created_by
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>search_exact_created_by($ip)>
|
||||
|
||||
=item B<Arguments> : an IP address
|
||||
|
||||
=item B<Purpose> : get enabled images that have been uploaded by this IP address
|
||||
|
||||
=item B<Returns> : a Mojo::Collection object containing the matching images as Lutim::DB::Image objects
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
|
||||
@@ -213,6 +213,27 @@ sub search_created_by {
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub search_exact_created_by {
|
||||
my $c = shift;
|
||||
my $ip = shift;
|
||||
|
||||
my @images;
|
||||
|
||||
my $records = $c->app->pg->db->select('lutim', undef, { enabled => 1, created_by => $ip })->hashes;
|
||||
|
||||
$records->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = Lutim::DB::Image->new(app => $c->app);
|
||||
$i->_slurp($e);
|
||||
|
||||
push @images, $i;
|
||||
}
|
||||
);
|
||||
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub _slurp {
|
||||
my $c = shift;
|
||||
my $r = shift;
|
||||
|
||||
@@ -214,6 +214,27 @@ sub search_created_by {
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub search_exact_created_by {
|
||||
my $c = shift;
|
||||
my $ip = shift;
|
||||
|
||||
my @images;
|
||||
|
||||
my $records = $c->app->sqlite->db->select('lutim', undef, { enabled => 1, created_by => $ip })->hashes;
|
||||
|
||||
$records->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = Lutim::DB::Image->new(app => $c->app);
|
||||
$i->_slurp($e);
|
||||
|
||||
push @images, $i;
|
||||
}
|
||||
);
|
||||
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub _slurp {
|
||||
my $c = shift;
|
||||
my $r = shift;
|
||||
|
||||
@@ -8,6 +8,7 @@ use Data::Entropy qw(entropy_source);
|
||||
use DateTime;
|
||||
use Mojo::Util qw(decode);
|
||||
use ISO::639_1;
|
||||
use Digest::MD5 'md5';
|
||||
|
||||
sub register {
|
||||
my ($self, $app) = @_;
|
||||
@@ -258,6 +259,18 @@ sub _is_wm_selected {
|
||||
return ($wm eq $c->config('watermark_default')) ? 'selected="selected"' : '';
|
||||
}
|
||||
|
||||
sub _key_from_key {
|
||||
my $key = shift;
|
||||
|
||||
# Key size for Blowfish is 56
|
||||
my $ks = 56;
|
||||
my $material = md5($key);
|
||||
while (length($material) < $ks) {
|
||||
$material .= md5($material);
|
||||
}
|
||||
return substr($material,0,$ks);
|
||||
}
|
||||
|
||||
sub _crypt {
|
||||
my $c = shift;
|
||||
my $upload = shift;
|
||||
@@ -267,10 +280,12 @@ sub _crypt {
|
||||
my $iv = $c->shortener(8);
|
||||
|
||||
my $cipher = Crypt::CBC->new(
|
||||
-key => $key,
|
||||
-cipher => 'Blowfish',
|
||||
-header => 'none',
|
||||
-iv => $iv
|
||||
-key => _key_from_key($key),
|
||||
-cipher => 'Blowfish',
|
||||
-header => 'none',
|
||||
-literal_key => 1,
|
||||
-pbkdf => 'pbkdf2',
|
||||
-iv => $iv
|
||||
);
|
||||
|
||||
$cipher->start('encrypting');
|
||||
@@ -289,16 +304,18 @@ sub _crypt {
|
||||
|
||||
sub _decrypt {
|
||||
my $c = shift;
|
||||
my $key = shift;
|
||||
my $key = _key_from_key(shift);
|
||||
my $file = shift;
|
||||
my $iv = shift;
|
||||
$iv = 'dupajasi' unless $iv;
|
||||
|
||||
my $cipher = Crypt::CBC->new(
|
||||
-key => $key,
|
||||
-cipher => 'Blowfish',
|
||||
-header => 'none',
|
||||
-iv => $iv
|
||||
-key => $key,
|
||||
-cipher => 'Blowfish',
|
||||
-header => 'none',
|
||||
-literal_key => 1,
|
||||
-pbkdf => 'pbkdf2',
|
||||
-iv => $iv
|
||||
);
|
||||
|
||||
$cipher->start('decrypting');
|
||||
|
||||
6
t/test.t
6
t/test.t
@@ -81,7 +81,7 @@ $t->get_ok('/css/lutim.css')
|
||||
# Instance settings informations
|
||||
$t->get_ok('/infos')
|
||||
->status_is(200)
|
||||
->json_has('image_magick')
|
||||
->json_has('/image_magick')
|
||||
->json_is(
|
||||
'/always_encrypt' => false,
|
||||
'/broadcast_message' => 'test broadcast message',
|
||||
@@ -96,7 +96,7 @@ $t->get_ok('/infos')
|
||||
my $image = Mojo::File->new($Bin, '..', 'themes', 'default', 'public', 'img', 'Lutim.png')->to_string;
|
||||
$t->post_ok('/' => form => { file => { file => $image }, format => 'json' })
|
||||
->status_is(200)
|
||||
->json_has('msg', 'success')
|
||||
->json_has('/msg', '/success')
|
||||
->json_is('/success' => true, '/msg/filename' => 'Lutim.png')
|
||||
->json_like('/msg/short' => qr#[-_a-zA-Z0-9]{8}#, '/msg/real_short' => qr#[-_a-zA-Z0-9]{8}#, '/msg/token' => qr#[-_a-zA-Z0-9]{24}#);
|
||||
|
||||
@@ -138,7 +138,7 @@ my $token = $raw->json('/msg/token');
|
||||
$t->get_ok('/'.$rshort)
|
||||
->status_is(200);
|
||||
|
||||
$t->get_ok('/d/'.$rshort.'/'.$token, form => { format => 'json' })
|
||||
$t->get_ok('/d/'.$rshort.'/'.$token, form => { _format => 'json' })
|
||||
->status_is('200')
|
||||
->json_is(
|
||||
{
|
||||
|
||||
@@ -147,7 +147,7 @@ function delImage(e) {
|
||||
url: '<%= url_for('/') %>d/'+short+'/'+token,
|
||||
method: 'GET',
|
||||
data: {
|
||||
format: 'json'
|
||||
_format: 'json'
|
||||
},
|
||||
success: function(data) {
|
||||
if (data.success) {
|
||||
|
||||
Reference in New Issue
Block a user