355 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Perl
		
	
	
	
			
		
		
	
	
			355 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Perl
		
	
	
	
| # vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
 | |
| package Lufi::Controller::Files;
 | |
| use Mojo::Base 'Mojolicious::Controller';
 | |
| use Mojo::JSON qw(encode_json decode_json true false);
 | |
| use Mojo::Util qw(slurp spurt encode decode);
 | |
| use LufiDB;
 | |
| use Lufi::File;
 | |
| use Lufi::Slice;
 | |
| use File::Spec::Functions;
 | |
| use Number::Bytes::Human qw(format_bytes);
 | |
| use Filesys::DfPortable;
 | |
| 
 | |
| sub upload {
 | |
|     my $c = shift;
 | |
| 
 | |
|     if (!defined($c->config('ldap')) || $c->is_user_authenticated) {
 | |
|         $c->inactivity_timeout(30000000);
 | |
| 
 | |
|         $c->app->log->debug('Client connected');
 | |
| 
 | |
|         $c->on(
 | |
|             message => sub {
 | |
|                 my ($ws, $text) = @_;
 | |
| 
 | |
|                 my $begin = time;
 | |
| 
 | |
|                 my ($json) = split('XXMOJOXX', $text, 2);
 | |
|                 $json = encode 'UTF-8', $json;
 | |
|                 $text =~ s/^.*?XXMOJOXX/${json}XXMOJOXX/;
 | |
|                 $json = decode_json $json;
 | |
| 
 | |
|                 $c->app->log->debug('Got message');
 | |
| 
 | |
|                 my $stop = 0;
 | |
| 
 | |
|                 # Check if stop_upload file is present
 | |
|                 if ($c->stop_upload) {
 | |
|                     $stop = 1;
 | |
|                     $c->send(sprintf('{"success": false, "msg":"'.$c->l('Sorry, uploading is disabled.').'", "sent_delay": %d, "i": %d}', $json->{delay}, $json->{i}));
 | |
|                 }
 | |
|                 # Check against max_size
 | |
|                 elsif (defined $c->config('max_file_size')) {
 | |
|                     if ($json->{size} > $c->config('max_file_size')) {
 | |
|                         $stop = 1;
 | |
|                         $c->send(sprintf('{"success": false, "msg":"'.$c->l('Your file is too big: %1 (maximum size allowed: %2)', format_bytes($json->{size}), format_bytes($c->config('max_file_size'))).'", "sent_delay": %d, "i": %d}', $json->{delay}, $json->{i}));
 | |
|                     }
 | |
|                 }
 | |
|                 # Check that we have enough space (multiplying by 2 since it's encrypted, it takes more place that the original file)
 | |
|                 elsif ($json->{part} == 0 && ($json->{size} * 2) >= dfportable($c->config('upload_dir'))->{bavail}) {
 | |
|                     $stop = 1;
 | |
|                     $c->send(sprintf('{"success": false, "msg":"'.$c->l('No enough space available on the server for this file (size: %1).', format_bytes($json->{size})).'", "sent_delay": %d, "i": %d}', $json->{delay}, $json->{i}));
 | |
|                 }
 | |
| 
 | |
|                 unless ($stop) {
 | |
|                     my $f;
 | |
|                     if (defined($json->{id})) {
 | |
|                         my @records = LufiDB::Files->select('WHERE short = ?', $json->{id});
 | |
|                         $f          = Lufi::File->new(record => $records[0]) if scalar @records;
 | |
|                     } else {
 | |
|                         my $delay;
 | |
| 
 | |
|                         if (defined $c->config('delay_for_size')) {
 | |
|                             # Choose delay according to config
 | |
|                             my $delays   = $c->config('delay_for_size');
 | |
|                             my @keys     = sort {$b <=> $a} keys %{$delays};
 | |
|                             for my $key (@keys) {
 | |
|                                 if ($json->{size} >= $key) {
 | |
|                                     $delay = ($json->{delay} < $delays->{$key}) ? $json->{delay} : $delays->{$key};
 | |
|                                     last;
 | |
|                                 }
 | |
|                             }
 | |
|                         }
 | |
|                         # If the file size is lower than the lowest configured size or if there is no delay_for_size setting, we choose the configured max delay
 | |
|                         unless (defined $delay) {
 | |
|                             $delay = ($json->{delay} <= $c->max_delay || $c->max_delay == 0) ? $json->{delay} : $c->max_delay;
 | |
|                         }
 | |
| 
 | |
|                         my $creator = $c->ip;
 | |
|                         if (defined($c->config('ldap'))) {
 | |
|                             $creator = 'User: '.$c->current_user.', IP: '.$creator;
 | |
|                         }
 | |
|                         $f = Lufi::File->new(
 | |
|                             record               => $c->get_empty,
 | |
|                             created_by           => $creator,
 | |
|                             delete_at_first_view => ($json->{del_at_first_view}) ? 1 : 0,
 | |
|                             delete_at_day        => $delay,
 | |
|                             mediatype            => $json->{type},
 | |
|                             filename             => $json->{name},
 | |
|                             filesize             => $json->{size},
 | |
|                             nbslices             => $json->{total},
 | |
|                             mod_token            => $c->shortener($c->config('token_length'))
 | |
|                         );
 | |
|                         $f->write;
 | |
|                     }
 | |
| 
 | |
|                     # This check is just in case we didn't succeed to find a corresponding record
 | |
|                     # It normally can't happen
 | |
|                     if (defined $f) {
 | |
|                         # If we already have a part, it's a resend because the websocket has been broken
 | |
|                         # In this case, we don't need to rewrite the file
 | |
|                         unless ($f->slices->grep(sub { $_->j == $json->{part} })->size) {
 | |
|                             # Create directory
 | |
|                             my $dir = catdir($c->config('upload_dir'), $f->short);
 | |
|                             mkdir($dir, 0700) unless (-d $dir);
 | |
| 
 | |
|                             # Create slice file
 | |
|                             my $file = catfile($dir, $json->{part}.'.part');
 | |
|                             my $s    = Lufi::Slice->new(
 | |
|                                 short => $f->short,
 | |
|                                 j     => $json->{part},
 | |
|                                 path  => $file
 | |
|                             );
 | |
|                             spurt $text, $file;
 | |
|                             push @{$f->slices}, $s;
 | |
|                             $s->write;
 | |
| 
 | |
|                             if (($json->{part} + 1) == $json->{total}) {
 | |
|                                 $f->complete(1);
 | |
|                                 $f->created_at(time);
 | |
|                                 $f->write;
 | |
|                             }
 | |
|                         }
 | |
| 
 | |
|                         $c->provisioning;
 | |
| 
 | |
|                         $ws->send(sprintf('{"success": true, "i": %d, "j": %d, "parts": %d, "short": "%s", "name": "%s", "size": %d, "del_at_first_view": %s, "created_at": %d, "delay": %d, "token": "%s", "sent_delay": %d, "duration": %d}', $json->{i}, $json->{part}, $json->{total}, $f->short, $f->filename, $f->filesize, (($f->delete_at_first_view) ? 'true' : 'false'), $f->created_at, $f->delete_at_day, $f->mod_token, $json->{delay}, time - $begin));
 | |
|                     } else {
 | |
|                         $ws->send(sprintf('{"success": false, "msg":"'.$c->l('The server was unable to find the file record to add your file part to. Please, contact the administrator.').'", "sent_delay": %d, "i": %d}', $json->{delay}, $json->{i}));
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         );
 | |
|         $c->on(
 | |
|             finish => sub {
 | |
|                 $c->app->log->debug('Client disconnected');
 | |
|             }
 | |
|         );
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub download {
 | |
|     my $c     = shift;
 | |
|     my $short = $c->param('short');
 | |
| 
 | |
|     $c->inactivity_timeout(300000);
 | |
|     $c->app->log->debug('Client connected');
 | |
| 
 | |
|     my @records = LufiDB::Files->select('WHERE short = ?', $short);
 | |
| 
 | |
|     # Do we have a file?
 | |
|     if (scalar @records) {
 | |
|         my $record = $records[0];
 | |
|         # Is the file fully uploaded?
 | |
|         if ($record->deleted
 | |
|             || (
 | |
|                 $record->delete_at_day != 0
 | |
|                 && (
 | |
|                     ($record->created_at + $record->delete_at_day * 86400) < time()
 | |
|                 )
 | |
|             )
 | |
|         ) {
 | |
|             unless ($record->deleted) {
 | |
|                 my $f = Lufi::File->new(record => $record);
 | |
|                 $f->delete;
 | |
|             }
 | |
|             $c->on(
 | |
|                 message => sub {
 | |
|                     my ($ws, $json) = @_;
 | |
|                     $c->send('{"success": false, "msg": "'.$c->l('Error: the file existed but was deleted.').'"}');
 | |
|                 }
 | |
|             );
 | |
|         } elsif ($record->complete) {
 | |
|             my $f = Lufi::File->new(record => $record);
 | |
| 
 | |
|             $c->on(
 | |
|                 message => sub {
 | |
|                     my ($ws, $json) = @_;
 | |
|                     $json = decode_json $json;
 | |
|                     if (defined($json->{part})) {
 | |
|                         # Make $num an integer instead of a string
 | |
|                         my $num = $json->{part} + 0;
 | |
| 
 | |
|                         # Get the slice
 | |
|                         my $e    = $f->slices->[$num];
 | |
|                         my $text = slurp $e->path;
 | |
| 
 | |
|                         my ($json2) = split('XXMOJOXX', $text, 2);
 | |
|                         $json2 = decode 'UTF-8', $json2;
 | |
|                         $text =~ s/^.*?XXMOJOXX/${json2}XXMOJOXX/;
 | |
| 
 | |
|                         # Send the slice
 | |
|                         $c->send($text);
 | |
|                     } elsif (defined($json->{ended}) && $json->{ended}) {
 | |
|                         $f->counter($f->counter + 1);
 | |
|                         $f->last_access_at(time);
 | |
| 
 | |
|                         if ($f->delete_at_first_view) {
 | |
|                             $f->delete;
 | |
|                         } else {
 | |
|                             $f->write;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             );
 | |
|             $c->on(
 | |
|                 finish => sub {
 | |
|                     $c->app->log->debug('Client disconnected');
 | |
|                 }
 | |
|             );
 | |
|         } else {
 | |
|             $c->on(
 | |
|                 message => sub {
 | |
|                     my ($ws, $json) = @_;
 | |
|                     $c->send('{"success": false, "msg": "'.$c->l('Error: the file has not been sent entirely.').'"}');
 | |
|                 }
 | |
|             );
 | |
|         }
 | |
|     } else {
 | |
|         $c->send('{"success": false, "msg": "'.$c->l('Error: unable to find the file. Are you sure of your URL?').'"}');
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub r {
 | |
|     my $c     = shift;
 | |
|     my $short = $c->param('short');
 | |
| 
 | |
|     my @records = LufiDB::Files->select('WHERE short = ?', $short);
 | |
|     if (scalar @records) {
 | |
|         my $f   = Lufi::File->new(record => $records[0]);
 | |
|         return $c->render(
 | |
|             template => 'render',
 | |
|             f        => $f
 | |
|         );
 | |
|     } else {
 | |
|         return $c->render(
 | |
|             template => 'render',
 | |
|             msg      => $c->l('Could not find the file. Are you sure of the URL?')
 | |
|         );
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub get_counter {
 | |
|     my $c     = shift;
 | |
|     my $short = $c->param('short');
 | |
|     my $token = $c->param('token');
 | |
| 
 | |
|     if (!defined($c->config('ldap')) || $c->is_user_authenticated) {
 | |
|         my @records = LufiDB::Files->select('WHERE short = ?', $short);
 | |
|         if (scalar(@records)) {
 | |
|             if ($records[0]->mod_token eq $token) {
 | |
|                 return $c->render(
 | |
|                     json => {
 | |
|                         success => true,
 | |
|                         short   => $short,
 | |
|                         counter => $records[0]->counter,
 | |
|                         deleted => ($records[0]->deleted) ? true : false
 | |
|                     }
 | |
|                 );
 | |
|             } else {
 | |
|                 return $c->render(
 | |
|                     json => {
 | |
|                         success => false,
 | |
|                         missing => false,
 | |
|                         short   => $short,
 | |
|                         msg     => $c->l('Unable to get counter for %1. The token is invalid.', $short)
 | |
|                     }
 | |
|                 );
 | |
|             }
 | |
|         } else {
 | |
|             return $c->render(
 | |
|                 json => {
 | |
|                     success => false,
 | |
|                     missing => true,
 | |
|                     short   => $short,
 | |
|                     msg     => $c->l('Unable to get counter for %1. The file does not exists. It will be removed from your localStorage.', $short)
 | |
|                 }
 | |
|             );
 | |
|         }
 | |
|     } else {
 | |
|         return $c->render(
 | |
|             json => {
 | |
|                 success => false,
 | |
|                 missing => false,
 | |
|                 short   => $short,
 | |
|                 msg     => $c->l('Unable to get counter for %1. You are not authenticated.', $short)
 | |
|             }
 | |
|         );
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub delete {
 | |
|     my $c     = shift;
 | |
|     my $short = $c->param('short');
 | |
|     my $token = $c->param('token');
 | |
| 
 | |
|     if (!defined($c->config('ldap')) || $c->is_user_authenticated) {
 | |
|         my @records = LufiDB::Files->select('WHERE short = ? AND mod_token = ?', ($short, $token));
 | |
|         if (scalar(@records)) {
 | |
|             my $f   = Lufi::File->new(record => $records[0]);
 | |
|             my $msg;
 | |
|             if ($f->deleted) {
 | |
|                 $msg = $c->l('The file has already been deleted');
 | |
|             } else {
 | |
|                 $f->delete;
 | |
|                 $msg = $c->l('File deleted');
 | |
|             }
 | |
|             return $c->respond_to(
 | |
|                 json => {
 | |
|                     success => true,
 | |
|                     msg     => $msg
 | |
|                 },
 | |
|                 any => sub {
 | |
|                     $c->render(
 | |
|                         template => 'msg',
 | |
|                         f        => $f,
 | |
|                         msg      => $msg
 | |
|                     );
 | |
|                 }
 | |
|             );
 | |
|         } else {
 | |
|             my $msg = $c->l('Could not find the file. Are you sure of the URL and the token?');
 | |
|             return $c->respond_to(
 | |
|                 json => {
 | |
|                     success => false,
 | |
|                     msg     => $msg
 | |
|                 },
 | |
|                 any => sub {
 | |
|                     $c->render(
 | |
|                         template => 'msg',
 | |
|                         f        => undef,
 | |
|                         msg      => $msg
 | |
|                     );
 | |
|                 }
 | |
|             );
 | |
|         }
 | |
|     } else {
 | |
|         my $msg = $c->l('Could not delete the file. You are not authenticated.');
 | |
|         return $c->respond_to(
 | |
|             json => {
 | |
|                 success => false,
 | |
|                 msg     => $msg
 | |
|             },
 | |
|             any => sub {
 | |
|                 $c->render(
 | |
|                     template => 'msg',
 | |
|                     f        => undef,
 | |
|                     msg      => $msg
 | |
|                 );
 | |
|             }
 | |
|         );
 | |
|     }
 | |
| }
 | |
| 
 | |
| 1;
 |