# 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 to_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(encode_json( { success => false, msg => $c->l('Sorry, uploading is disabled.'), sent_delay => $json->{delay}, i => $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(encode_json( { 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 => $json->{delay}, i => $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(encode_json( { success => false, msg => $c->l('No enough space available on the server for this file (size: %1).', format_bytes($json->{size})), sent_delay => $json->{delay}, i => $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(to_json( { success => true, i => $json->{i}, j => $json->{part}, parts => $json->{total}, short => $f->short, name => $f->filename, size => $f->filesize, del_at_first_view => (($f->delete_at_first_view) ? true : false), created_at => $f->created_at, delay => $f->delete_at_day, token => $f->mod_token, sent_delay => $json->{delay}, duration => time - $begin } )); } else { $ws->send(encode_json( { 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 => $json->{delay}, i => $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(encode_json( { 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(encode_json( { success => false, msg => $c->l('Error: the file has not been sent entirely.') } )); } ); } } else { $c->send(encode_json( { 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;