92 # |-STORE----------------------------| |
92 # |-STORE----------------------------| |
93 # … |-PATH--------------------------------------| |
93 # … |-PATH--------------------------------------| |
94 if ($ENV{PATH_INFO} =~ |
94 if ($ENV{PATH_INFO} =~ |
95 m{/(?<path>(?<store>(?<view>$rxVIEW)/$rxFILE)/(?<base>.*))}) |
95 m{/(?<path>(?<store>(?<view>$rxVIEW)/$rxFILE)/(?<base>.*))}) |
96 { |
96 { |
97 # use Data::Dumper; |
97 # use Data::Dumper; |
98 # die Dumper \%+; |
98 # die Dumper \%+; |
99 my $view = deslash realpath catfile $ONCE_VAR, $+{view}; |
99 my $view = deslash realpath catfile $ONCE_VAR, $+{view}; |
100 my $store = deslash realpath catfile $ONCE_VAR, $+{store}; |
100 my $store = deslash realpath catfile $ONCE_VAR, $+{store}; |
101 my $file = deslash realpath catfile $ONCE_VAR, $+{path}; |
101 my $file = deslash realpath catfile $ONCE_VAR, $+{path}; |
102 my $base = $+{base}; |
102 my $base = $+{base}; |
103 |
103 |
133 |
133 |
134 -x -w $ONCE_VAR |
134 -x -w $ONCE_VAR |
135 or die "Can't write to $ONCE_VAR: $!\n"; |
135 or die "Can't write to $ONCE_VAR: $!\n"; |
136 |
136 |
137 my ($view, $user_dir) = do { |
137 my ($view, $user_dir) = do { |
|
138 |
138 # view: display name |
139 # view: display name |
139 # anonymous | hans | … |
140 # anonymous | hans | … |
140 # user_dir: the directory name, becomes part of the |
141 # user_dir: the directory name, becomes part of the |
141 # link, later |
142 # link, later |
142 # /var/lib/once/1AaIF9-1KF |
143 # /var/lib/once/1AaIF9-1KF |
149 # to adapt the patterns $rxVIEW and $rxFILE at the beginning of |
150 # to adapt the patterns $rxVIEW and $rxFILE at the beginning of |
150 # the script. |
151 # the script. |
151 # |
152 # |
152 my ($v, $d); |
153 my ($v, $d); |
153 if ($ENV{REMOTE_USER} =~ /^\s*(.+)\s*$/) { |
154 if ($ENV{REMOTE_USER} =~ /^\s*(.+)\s*$/) { |
|
155 |
154 # Known users get a directory name based user name. |
156 # Known users get a directory name based user name. |
155 # Yes, if somebody can guess the user names, they can guess |
157 # Yes, if somebody can guess the user names, they can guess |
156 # the directory names too. But they can't guess the |
158 # the directory names too. But they can't guess the |
157 # completly randomly named files in there. |
159 # completly randomly named files in there. |
158 $d = join '-' => base62(time), md5_base62($1); |
160 $d = join '-' => base62(time), md5_base62($1); |
185 if (length(my $file = param('upload'))) { |
187 if (length(my $file = param('upload'))) { |
186 my $uuid = gen_uuid(); |
188 my $uuid = gen_uuid(); |
187 my ($delete, $expires, $days) = do { |
189 my ($delete, $expires, $days) = do { |
188 my ($d, $e); |
190 my ($d, $e); |
189 my $days = param('expires'); |
191 my $days = param('expires'); |
|
192 |
190 # sanitize expires |
193 # sanitize expires |
191 $days =~ /.*?([+-]?\d+).*/; |
194 $days =~ /.*?([+-]?\d+).*/; |
192 $days = $1 // 10; |
195 $days = $1 // 10; |
193 $e = base62 time + $days * 86400; |
196 $e = base62 time + $days * 86400; |
194 |
197 |
195 if ($days == 0) { $d = 'd' } # at first [d]ownload |
198 if ($days == 0) { $d = 'd' } # at first [d]ownload |
196 elsif ($days < 0) { $d = 'm' } # only [m]anually |
199 elsif ($days < 0) { $d = 'm' } # only [m]anually |
197 else { $d = 'e' } # if expired |
200 else { $d = 'e' } # if expired |
198 ($d, $e, $days); |
201 ($d, $e, $days); |
199 }; |
202 }; |
200 |
203 |
201 # sanitize the filename |
204 # sanitize the filename |
202 my $filename = do { |
205 my $filename = do { |
241 my ($file, $dir) = fileparse($_); |
244 my ($file, $dir) = fileparse($_); |
242 $dir = substr $dir, |
245 $dir = substr $dir, |
243 length $ONCE_VAR; # make it relative to $ONCE_VAR |
246 length $ONCE_VAR; # make it relative to $ONCE_VAR |
244 |
247 |
245 # FIXME: use the rx* patterns from above |
248 # FIXME: use the rx* patterns from above |
246 $dir =~ m{/(?<expires>[a-z\d]{6})-(?<hash>[a-z\d]+)-(?<delete>.)}i or next; |
249 $dir =~ m{/(?<expires>[a-z\d]{6})-(?<hash>[a-z\d]+)-(?<delete>.)}i |
|
250 or next; |
247 my ($hash, $expires, $delete) = @{+}{qw/hash expires delete/}; |
251 my ($hash, $expires, $delete) = @{+}{qw/hash expires delete/}; |
248 if (${expires} <= time and $delete eq 'e') { |
252 if (${expires} <= time and $delete eq 'e') { |
249 /(.*)/; |
253 /(.*)/; |
250 unlink $_ or die "Can't unlik $_: $!\n"; |
254 unlink $_ or die "Can't unlik $_: $!\n"; |
251 rmdir $dir or die "Can't rmdir $dir: $!\n"; |
255 rmdir $dir or die "Can't rmdir $dir: $!\n"; |
359 } |
363 } |
360 |
364 |
361 sub base62 { |
365 sub base62 { |
362 my $n = shift // $_; |
366 my $n = shift // $_; |
363 die 'input is >MAXINT' if $n == $n - 1 or $n == $n + 1; |
367 die 'input is >MAXINT' if $n == $n - 1 or $n == $n + 1; |
364 state $digits = [0..9, 'a'..'z', 'A'..'Z']; |
368 state $digits = [0 .. 9, 'a' .. 'z', 'A' .. 'Z']; |
365 state $base = @$digits; |
369 state $base = @$digits; |
366 my @result; |
370 my @result; |
367 |
371 |
368 for (;$n >= $base; $n = int($n/$base)) { |
372 for (; $n >= $base ; $n = int($n / $base)) { |
369 my $mod = $n % $base; |
373 my $mod = $n % $base; |
370 unshift @result, $digits->[$mod]; |
374 unshift @result, $digits->[$mod]; |
371 } |
375 } |
372 unshift @result, $digits->[$n]; |
376 unshift @result, $digits->[$n]; |
373 join '', @result; |
377 join '', @result; |
374 } |
378 } |
375 |
379 |
376 |
|
377 sub gen_uuid { |
380 sub gen_uuid { |
|
381 |
378 #open my $f, '/dev/urandom' or croak; |
382 #open my $f, '/dev/urandom' or croak; |
379 #read $f, my($_), 128/8; |
383 #read $f, my($_), 128/8; |
380 #/^(.*)$/; |
384 #/^(.*)$/; |
381 #die join '-', map { base62 $_ } unpack 'Q*', $1; |
385 #die join '-', map { base62 $_ } unpack 'Q*', $1; |
382 return base62 int rand(2**64); |
386 return base62 int rand(2**64); |