Commit 5e81be1d authored by Luc Didry's avatar Luc Didry

Merge branch 'i.kulgu/lufi-master' into development

parents ad228fd5 dbd31f22
......@@ -13,3 +13,4 @@
* Quentin Pagès (occitan translation)
* Yann Le Brech (htpasswd file support)
* Jéssica Da Cunha (portuguese translation)
* Ilker Kulgu (fix IE11 compatibility, Dutch translation)
......@@ -5,6 +5,7 @@ IT=themes/default/lib/Lufi/I18N/it.po
OC=themes/default/lib/Lufi/I18N/oc.po
CA=themes/default/lib/Lufi/I18N/ca.po
PT=themes/default/lib/Lufi/I18N/pt.po
NL=themes/default/lib/Lufi/I18N/nl.po
XGETTEXT=carton exec local/bin/xgettext.pl
CARTON=carton exec
REAL_LUFI=script/application
......@@ -17,6 +18,7 @@ locales:
$(XGETTEXT) $(EXTRACTDIR) -o $(OC) 2>/dev/null
$(XGETTEXT) $(EXTRACTDIR) -o $(CA) 2>/dev/null
$(XGETTEXT) $(EXTRACTDIR) -o $(PT) 2>/dev/null
$(XGETTEXT) $(EXTRACTDIR) -o $(NL) 2>/dev/null
test:
$(CARTON) $(REAL_LUFI) test
......
......@@ -15,4 +15,5 @@ requires 'Filesys::DfPortable';
requires 'Switch';
requires 'Data::Entropy';
requires 'Net::LDAP';
requires 'Apache::Htpasswd';
\ No newline at end of file
requires 'Crypt::SaltedHash';
requires 'Apache::Htpasswd';
......@@ -65,6 +65,15 @@ DISTRIBUTIONS
requirements:
ExtUtils::MakeMaker 0
perl 5.006
Crypt-SaltedHash-0.09
pathname: G/GS/GSHANK/Crypt-SaltedHash-0.09.tar.gz
provides:
Crypt::SaltedHash 0.09
requirements:
Digest 0
ExtUtils::MakeMaker 6.30
Test::Fatal 0
Test::More 0
DBD-SQLite-1.48
pathname: I/IS/ISHIGAKI/DBD-SQLite-1.48.tar.gz
provides:
......@@ -893,6 +902,18 @@ DISTRIBUTIONS
Text::Balanced 2
if 0
perl 5.005
Test-Fatal-0.014
pathname: R/RJ/RJBS/Test-Fatal-0.014.tar.gz
provides:
Test::Fatal 0.014
requirements:
Carp 0
Exporter 5.57
ExtUtils::MakeMaker 0
Test::Builder 0
Try::Tiny 0.07
strict 0
warnings 0
Test-Script-1.10
pathname: P/PL/PLICEASE/Test-Script-1.10.tar.gz
provides:
......@@ -969,6 +990,18 @@ DISTRIBUTIONS
Time::Zone 2.24
requirements:
ExtUtils::MakeMaker 0
Try-Tiny-0.28
pathname: E/ET/ETHER/Try-Tiny-0.28.tar.gz
provides:
Try::Tiny 0.28
requirements:
Carp 0
Exporter 5.57
ExtUtils::MakeMaker 0
constant 0
perl 5.006
strict 0
warnings 0
URI-1.71
pathname: E/ET/ETHER/URI-1.71.tar.gz
provides:
......
......@@ -15,20 +15,21 @@ sub startup {
my $config = $self->plugin('Config' => {
default => {
provisioning => 100,
provis_step => 5,
length => 10,
token_length => 32,
secrets => ['hfudsifdsih'],
default_delay => 0,
max_delay => 0,
mail => {
provisioning => 100,
provis_step => 5,
length => 10,
token_length => 32,
secrets => ['hfudsifdsih'],
default_delay => 0,
max_delay => 0,
mail => {
how => 'sendmail'
},
mail_sender => 'no-reply@lufi.io',
theme => 'default',
upload_dir => 'files',
session_duration => 3600,
mail_sender => 'no-reply@lufi.io',
theme => 'default',
upload_dir => 'files',
session_duration => 3600,
allow_pwd_on_files => 0,
}
});
......@@ -251,6 +252,16 @@ sub startup {
mkdir($self->config('upload_dir'), 0700) unless (-d $self->config('upload_dir'));
die ('The upload directory ('.$self->config('upload_dir').') is not writable') unless (-w $self->config('upload_dir'));
# SQLite database migration if needed
my $columns = LufiDB::Files->table_info;
my $pwd_col = 0;
foreach my $col (@{$columns}) {
$pwd_col = 1 if $col->{name} eq 'passwd';
}
unless ($pwd_col) {
LufiDB->do('ALTER TABLE files ADD COLUMN passwd TEXT;');
}
# Default layout
$self->defaults(layout => 'default');
......
......@@ -9,6 +9,7 @@ use Lufi::Slice;
use File::Spec::Functions;
use Number::Bytes::Human qw(format_bytes);
use Filesys::DfPortable;
use Crypt::SaltedHash;
sub upload {
my $c = shift;
......@@ -98,6 +99,14 @@ sub upload {
unless (defined $delay) {
$delay = (($json->{delay} > 0 && $json->{delay} <= $c->max_delay) || $c->max_delay == 0) ? $json->{delay} : $c->max_delay;
}
# If we have a password
my $salted_pwd;
if ($c->config('allow_pwd_on_files') && defined($json->{file_pwd}) && $json->{file_pwd} ne '') {
my $csh = Crypt::SaltedHash->new(algorithm => 'SHA-256', salt_len => 8);
$csh->add($json->{file_pwd});
$salted_pwd = $csh->generate();
}
my $creator = $c->ip;
if (defined($c->config('ldap')) || defined($c->config('htpasswd'))) {
......@@ -112,7 +121,8 @@ sub upload {
filename => $json->{name},
filesize => $json->{size},
nbslices => $json->{total},
mod_token => $c->shortener($c->config('token_length'))
mod_token => $c->shortener($c->config('token_length')),
passwd => $salted_pwd
);
$f->write;
}
......@@ -228,29 +238,41 @@ sub download {
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;
# Do we need a password?
my $valid = 1;
if ($c->config('allow_pwd_on_files') && defined($f->{passwd})) {
my $pwd = $json->{file_pwd};
$valid = Crypt::SaltedHash->validate($f->{passwd}, $json->{file_pwd}, 8);
}
if ($valid) {
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;
}
}
} else {
$c->send(encode_json({msg => $c->l('Your password is not valid. Please refresh the page to retry.')}));
}
}
);
......@@ -291,7 +313,8 @@ sub r {
my $f = Lufi::File->new(record => $records[0]);
return $c->render(
template => 'render',
f => $f
f => $f,
file_pwd => ($c->config('allow_pwd_on_files') && defined($records[0]->{passwd}))
);
} else {
return $c->render(
......
......@@ -25,6 +25,7 @@ has 'complete' => 0;
has 'slices' => sub {
return Mojo::Collection->new();
};
has 'passwd';
sub new {
my $c = shift;
......@@ -53,6 +54,7 @@ sub write {
mod_token => $c->mod_token,
nbslices => $c->nbslices,
complete => $c->complete,
passwd => $c->passwd,
);
return $c;
......@@ -89,6 +91,7 @@ sub _slurp {
$c->mod_token($c->record->mod_token) if defined $c->record->mod_token;
$c->nbslices($c->record->nbslices) if defined $c->record->nbslices;
$c->complete($c->record->complete) if defined $c->record->complete;
$c->passwd($c->record->passwd) if defined $c->record->passwd;
my @slices = LufiDB::Slices->select('WHERE short = ? ORDER BY j ASC', $c->short);
......
......@@ -37,7 +37,8 @@ use ORLite {
last_access_at INTEGER,
mod_token TEXT,
nbslices INTEGER,
complete INTEGER)'
complete INTEGER,
passwd TEXT)'
);
$dbh->do(
'CREATE TABLE slices (
......
......@@ -141,6 +141,10 @@
# optional, default is 3600
#session_duration => 3600,
# allow to add a password on files, asked before allowing to download files
# optional, default to 0
#allow_pwd_on_files => 0,
#########################
# Lufi cron jobs settings
#########################
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -6,7 +6,6 @@
color: #888;
}
#files label {
background-color: #5A7BC2;
padding: 6px 0;
color: #FFF;
font-weight: bold;
......@@ -17,7 +16,16 @@
cursor: pointer;
}
#files input {
display:none;
width: 100%;
background: rgba(150,225,150,0.0);
vertical-align: middle;
text-align: center;
padding: 0 50%;
margin-top: -206px;
color: #FFF;
font-weight: bold;
font-size: 200px;
opacity: 0;
}
.progress-info {
text-shadow: initial;
......@@ -86,3 +94,8 @@ a.classic:focus {
.hiddendiv.common {
min-height: 170px;
}
.title-filename {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
......@@ -35,7 +35,7 @@ function base64ToArrayBuffer(base64) {
function addAlert(msg) {
$('#please-wait').remove();
var pbd = $('#pbd');
var pbd = $('.file-progress');
pbd.attr('role', 'alert');
pbd.removeClass('progress');
pbd.html(['<div class="card pink">',
......@@ -53,7 +53,11 @@ function spawnWebsocket(pa) {
var l = $('#loading');
l.html(i18n.loading.replace(/XX1/, (pa + 1)));
window.ws.send('{"part":'+pa+'}');
if ($('#file_pwd').length === 1) {
window.ws.send('{"part":'+pa+', "file_pwd": "'+$('#file_pwd').val()+'"}');
} else {
window.ws.send('{"part":'+pa+'}');
}
};
ws.onclose = function() {
console.log('Connection is closed');
......@@ -70,6 +74,9 @@ function spawnWebsocket(pa) {
if (data.msg !== undefined) {
addAlert(data.msg);
console.log(data.msg);
if ($('#file_pwd').length === 1) {
$('.file-abort').addClass('hide');
}
window.onbeforeunload = null;
} else {
console.log('Getting slice '+(data.part + 1)+' of '+data.total);
......@@ -102,7 +109,11 @@ function spawnWebsocket(pa) {
}
pbd.html(innerHTML.join(''));
window.ws.send('{"ended":true}');
if ($('#file_pwd').length === 1) {
window.ws.send('{"ended":true, "file_pwd": "'+$('#file_pwd').val()+'"}');
} else {
window.ws.send('{"ended":true}');
}
window.onbeforeunload = null;
window.completed = true;
$('#abort').remove();
......@@ -123,7 +134,11 @@ function spawnWebsocket(pa) {
console.log('Error. Retrying to get slice '+(data.part + 1));
window.ws = spawnWebsocket(data.part + 1);
};
window.ws.send('{"part":'+(data.part + 1)+'}');
if ($('#file_pwd').length === 1) {
window.ws.send('{"part":'+(data.part + 1)+', "file_pwd": "'+$('#file_pwd').val()+'"}');
} else {
window.ws.send('{"part":'+(data.part + 1)+'}');
}
}
}
} catch(err) {
......@@ -157,11 +172,26 @@ $(document).ready(function(){
window.completed = false;
if (key !== '=') {
// Set websocket
window.ws = spawnWebsocket(0);
var go = true;
if ($('#file_pwd').length === 1) {
go = false;
$('#go').click(function() {
$('.file-progress, .file-abort').removeClass('hide');
$('#file_pwd').parent().parent().addClass('hide');
// Set websocket
window.ws = spawnWebsocket(0);
// Prevent exiting page before full download
window.onbeforeunload = confirmExit;
});
}
if (go) {
// Set websocket
window.ws = spawnWebsocket(0);
// Prevent exiting page before full download
window.onbeforeunload = confirmExit;
// Prevent exiting page before full download
window.onbeforeunload = confirmExit;
}
} else {
addAlert(i18n.nokey);
}
......
......@@ -145,6 +145,9 @@ function populateFilesTable() {
'</td>',
'<td class="center-align">',
'<a href="', dlink, '" class="classic"><i class="small mdi-action-delete"></i></a>',
'</td>',
'<td class="center-align">',
'<a href="'+baseURL+'m?links=[&quot;'+element.short+'&quot;]" class="classic"><i class="small mdi-communication-email"></i></a>',
'</td>'].join(''));
$('#myfiles').append(tr);
......
......@@ -174,8 +174,12 @@ function sliceAndUpload(randomkey, i, parts, j, delay, del_at_first_view, short)
fr.onloadend = function() {
var sl = $('#parts-'+window.fc);
// Get the binary result
var bin = fr.result;
// Get the binary result, different result in IE browsers (see default.html.ep line 27:48)
if (isIE == true){
var bin = fr.content;
} else {
var bin = fr.result;
}
// Transform it in base64
var b = window.btoa(bin);
......@@ -196,6 +200,12 @@ function sliceAndUpload(randomkey, i, parts, j, delay, del_at_first_view, short)
id: short,
i: i
};
if ($('#file_pwd').length === 1) {
var pwd = $('#file_pwd').val();
if (pwd !== undefined && pwd !== null && pwd !== '') {
data['file_pwd'] = $('#file_pwd').val();
}
}
data = JSON.stringify(data);
console.log('sending slice '+(j + 1)+'/'+parts+' of file '+file.name);
......
......@@ -28,6 +28,7 @@
<th class="center-align"><%= l('Uploaded at') %></th>
<th class="center-align"><%= l('Expires at') %></th>
<th class="center-align"><%= l('Deletion link') %></th>
<th class="center-align"><%= l('Mail') %></th>
</tr>
</thead>
<tbody id="myfiles">
......
......@@ -64,6 +64,14 @@
<label for="first-view"><%= l('Delete at first download?') %></label>
</p>
</div>
% if (config('allow_pwd_on_files')) {
<div class="col s12 m12">
<div class="input-field">
<input type="password" id="file_pwd" placeholder="<%= l('Password') %>" class="validate">
<label for="file_pwd"><%= l('Add a password to file(s)') %></label>
</div>
</div>
% }
</form>
<div id="files">
<h2><%= l('Drop files here') %></h2>
......
......@@ -22,6 +22,32 @@
console.log(i18n.confirmExit);
return i18n.confirmExit;
}
// Is the browser IE?
var isIE = /*@cc_on!@*/false || !!document.documentMode;
// If the browser is IE, add readAsBinaryString function and store the data
if (isIE == true){
if (FileReader.prototype.readAsBinaryString === undefined) {
FileReader.prototype.readAsBinaryString = function (fileData) {
var binary = "";
var pt = this;
var reader = new FileReader();
reader.onload = function (e) {
var bytes = new Uint8Array(reader.result);
var length = bytes.byteLength;
for (var i = 0; i < length; i++) {
binary += String.fromCharCode(bytes[i]);
}
//pt.result - readonly so assign content to another property
pt.content = binary;
$(pt).trigger('onloadend');
}
reader.readAsArrayBuffer(fileData);
}
}
}
% end
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
......
......@@ -67,7 +67,7 @@
var subject = document.getElementById('subject');
var text = document.getElementById('body');
btn.href = 'mailto:'+encodeURIComponent(emails.value)+'?subject='+encodeURIComponent(subject.value)+'&body='+encodeURIComponent(body.value);
btn.href = 'mailto:'+encodeURIComponent(emails.value)+'?subject='+encodeURIComponent(subject.value)+'&body='+encodeURIComponent(text.value);
}
function populateBody() {
var links = [
......
......@@ -10,7 +10,7 @@
</div>
</div>
% } else {
<h2 class="col s12 center-align"><%= stash('f')->filename %></h2>
<h2 class="col s12 center-align title-filename"><%= stash('f')->filename %></h2>
% if (defined(stash('msg'))) {
<div class="col s12">
<div class="card pink">
......@@ -20,7 +20,15 @@
</div>
</div>
% } else {
% if (stash('file_pwd')) {
<div class="col s12">
<div class="input-field">
<input type="password" id="file_pwd" placeholder="<%= l('Password') %>" class="validate">
</div>
<a id="go" class="waves-effect waves-light btn"><%= l('Download') %></a>
</div>
% }
<div class="file-progress col s12<%= (stash('file_pwd')) ? ' hide' : '' %>">
<p id="filesize" data-filesize="<%= stash('f')->filesize %>"></p>
<p id="please-wait"><%= l('Please wait while we are getting your file. We first need to download and decrypt all parts before you can get it.') %></p>
<p id="loading"></p>
......@@ -30,7 +38,7 @@
</div>
</div>
</div>
<div class="col s12">
<div class="file-abort col s12<%= (stash('file_pwd')) ? ' hide' : '' %>">
<a id="abort" class="waves-effect waves-light btn"><%= l('Abort') %></a>
</div>
%= javascript begin
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment