5 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
8 # Sebastian Zaiser <szcode@arcor.de> (upload status)
11 BEGIN { ($ENV{PERLINIT}||'') =~ /(.+)/s and eval $1 }
15 use Fcntl qw':flock :seek :mode';
17 use Digest::MD5 qw'md5_hex';
21 (our $FEXLIB) = $ENV{FEXLIB} =~ /(.+)/;
27 our $error = 'F*EX upload ERROR';
28 our $head = "$ENV{SERVER_NAME} F*EX upload";
29 our $autodelete = 'YES';
33 our (@locales,@throttle,$bcc,$keep_max,$nomail,$nostore,$overwrite);
34 our (@local_domains,@local_rdomains,@local_hosts,@local_rhosts,);
35 our (@registration_hosts,@demo,@file_link_dirs);
39 our ($spooldir,$durl,$tmpdir,@logdir,$logdir,$docdir,$hostname,$admin,$fra);
40 our ($keep_default,$recipient_quota,$sender_quota,$fex_yourself);
41 our ($sendmail,$mdomain,$fop_auth,$mail_auth,$faillog,$amdl);
42 our ($dkeydir,$ukeydir,$akeydir,$skeydir,$gkeydir,$xkeydir);
44 our $RB; # read POST bytes (total)
50 our $seek = 0; # already sent bytes (from previous upload)
51 our $filesize = 0; # total file size
52 our $fpsize = 0; # file part size (MIME-part)
56 my $rid = ''; # real ID
57 my @header; # HTTP entity header
60 my $muser; # main user fur sub or group user
61 my %specific; # upload specific KEEP and AUTODELETE parameters
63 # load common code, local config: $FEXLIB/fex.ph
64 require "$FEXLIB/fex.pp";
66 # load fup local config
67 our ($info_1,$info_2,$info_login);
69 $locale = $ENV{LOCALE} || 'english';
71 "/var/lib/fex/locale/$locale/lib/fup.pl",
80 &check_camel unless $sid;
82 chdir $spooldir or http_die("$spooldir - $!\n");
86 my $http_client = $ENV{HTTP_USER_AGENT} || '';
87 my $cl = $ENV{X_CONTENT_LENGTH} || $ENV{CONTENT_LENGTH} || 0;
89 $fra .= '/'.$ENV{HTTP_X_FORWARDED_FOR} if $ENV{HTTP_X_FORWARDED_FOR};
91 $from = $to = $id = $file = $fkey = $comment = $command = $bwlimit = '';
92 $filename = $okey = $addto = $replyto = $submit = '';
95 $locale = untaint($ENV{LOCALE}||'');
97 my $ra = $ENV{REMOTE_ADDR}||0;
98 if (@upload_hosts and not ipin($ra,@upload_hosts)) {
100 "Uploads from your host ($ra) are not allowed.",
101 "Contact $ENV{SERVER_ADMIN} for details."
107 &parse_request; # showstatus will not come back!
111 foreach $to (@to) { $to{$to} = 1 }
112 push @to,$addto unless $to{$addto};
113 # user has submitted with [select from your address book] ?
114 # if ($submit and @to == 1) { $addto = '' }
119 if ($from eq $to and $fex_yourself =~ /^no|0$/i) {
120 http_die("fexing to yourself is not allowed");
123 $uid = randstring(8) unless $uid; # upload ID
125 # user requests for forgotten ID
126 $id_forgotten = $id if $id =~ /^"?\?"?$/;
127 if ($from and $id_forgotten and $mail_authid and not ($fop_auth or $nomail)) {
128 &check_status($from);
133 # public recipients? (needs no auth-ID for sender)
134 if ($to and $id and $id eq 'PUBLIC' and @public_recipients) {
137 http_die("missing sender e-mail address");
139 # must use $param{FROM} for checking because $from is expanded with $mdomain
140 unless (checkaddress(despace($param{FROM}))) {
141 http_die("<code>$param{FROM}</code> is not a valid e-mail address");
143 foreach my $to (@to) {
144 unless (grep /^\Q$to\E$/i,@public_recipients) {
145 http_die("<code>$to</code> is not a valid recipient");
148 $restricted = $public = $rid = $id;
151 # anonymous upload from enabled IP?
152 if ($from =~ /^anonymous@/ and
153 @anonymous_upload and ipin($ra,@anonymous_upload)) {
154 $id = $rid = $anonymous = 'anonymous';
155 if ($to =~ /^anonymous/) {
157 $autodelete{$to} = $autodelete = $specific{'autodelete'}||'NO';
159 $nomail = $anonymous;
162 $comment = 'NOMAIL' if $nomail and not $comment;
166 $to = "@to" or http_die("no recipient specified");
167 $from = readlink "$to/\@OKEY/$okey"
168 or http_die("no upload key \"<code>$okey</code>\" - ".
169 "request another one from <code>$to</code>");
170 $from = untaint($from);
173 &check_status($from) if $from;
175 # look for regular sender ID
176 if ($id and $from and not ($public or $anonymous or $okey)) {
177 if (open $from,'<',"$from/\@") {
178 # chomp($rid = <$from> || '');
179 $rid = getline($from);
181 $rid = sidhash($rid,$id);
182 # set time mark for successfull access
184 my $time = untaint(time);
185 utime $time,$time,$from;
189 # if recipient (to) is specified, we have to look for subusers later, too
191 fuplog("ERROR: $spooldir/$from/\@ $error");
192 debuglog("cannot open $spooldir/$from/\@ : $error");
193 faillog("user $from, id $id");
194 http_die("wrong user or auth-ID");
200 if ($from and $id and not ($gkey or $skey or $public or $okey)) {
201 if ($rid and $rid eq $id) {
202 # set akey link for HTTP sessions
203 # (need original id for consistant non-moving akey)
204 if (-d $akeydir and open $idf,'<',"$from/@" and my $id = getline($idf)) {
205 # akey for webbrowser or fexsend special
206 if (not $sid or ($from eq $to and ($comment eq '*')) or $command) {
207 $akey = untaint(md5_hex("$from:$id"));
208 mksymlink("$akeydir/$akey","../$from");
211 $captive = -e "$from/\@CAPTIVE";
213 fuplog("ERROR: wrong auth-ID for $from");
214 debuglog("id sent by user $from=$id, real id=$rid");
215 faillog("user $from, id $id");
216 http_die("Wrong user or auth-ID");
220 # optional $auth_hook() in fup.pl
221 if ($auth_hook and ($akey or $skey or $gkey) and $from and -d $from) {
225 # forward a copy of a file to another recipient
226 if ($akey and $dkey and $command eq 'FORWARD') {
227 my $file = untaint(readlink "$dkeydir/$dkey"||'');
228 http_die("unknown dkey <code>$dkey></code>") unless $file;
234 # modify file parameter
235 if ($akey and $dkey and $command eq 'MODIFY') {
236 my $file = untaint(readlink "$dkeydir/$dkey"||'');
237 http_die("unknown dkey <code>$dkey</code>") unless $file;
243 # copy file from incoming to outgoing spool
244 if ($akey and $dkey and $command eq 'COPY') {
245 unless ($file = readlink "$dkeydir/$dkey") {
246 http_die("No such file with DKEY=$dkey");
248 if ($file =~ m:../(.+)/(.+)/(.+):) {
249 ($to,$from,$file) = ($1,$2,$3);
251 http_die("Bad DKEY $dkey -> $file");
253 unless (-f "$to/$from/$file/data") {
254 http_die("File not found");
256 if (-e "$to/$to/$file/data") {
257 http_die("File $file already exists in your outgoing spool")
258 if (readlink("$to/$to/$file/id")||$to) ne
259 (readlink("$to/$from/$file/id")||$from);
261 mkdirp("$to/$to/$file");
262 link "$to/$from/$file/data","$to/$to/$file/data"
263 or http_die("cannot link to $to/$to/$file/data - $!\n");
264 copy("$to/$from/$file/filename","$to/$to/$file/filename");
265 copy("$to/$from/$file/id","$to/$to/$file/id");
266 open $file,'>',"$to/$to/$file/notify";
268 open $file,'>',"$to/$to/$file/download";
269 print {$file} "$to\n";
271 $dkey = randstring(8);
272 unlink "$to/$to/$file/dkey","$to/$to/$file/keep","$dkeydir/$dkey";
273 symlink "../$to/$to/$file","$dkeydir/$dkey";
274 symlink $dkey,"$to/$to/$file/dkey";
277 "HTTP/1.1 302 Found",
278 "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/rup?akey=$akey&oto=$to&file=$file",
285 # delete file without download
286 if ($akey and $dkey and $command eq 'DELETE') {
287 $del = untaint(readlink "$dkeydir/$dkey"||'');
288 http_die("unknown dkey <code>$dkey</code>") unless $del;
290 $filename = filename($del);
291 if (unlink("$del/data") or unlink("$del/upload")) {
292 if (open F,'>',"$del/error") {
293 printf F "%s has been deleted by %s at %s\n",
294 $filename,$ENV{REMOTE_ADDR},isodate(time);
297 # http_header('200 OK');
298 # print html_header($head);
299 # print "<h3>$filename deleted</h3>\n";
301 "HTTP/1.1 302 Found",
302 "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/fup?akey=$akey&command=LISTRECEIVED",
309 http_header('404 Not Found');
310 print html_header($head);
311 print "<h3>$filename not deleted ($s)</h3>\n";
312 print "<a href=\"/fup?akey=$akey&command=LISTRECEIVED\">continue</a>\n" if $akey;
313 print "</body></html>\n";
319 if (($from and $id and $rid eq $id or $gkey or $skey) and $command) {
321 if ($command eq 'CHECKQUOTA') {
322 http_die("illegal command \"$command\"") if $public or $anonymous;
323 nvt_print('HTTP/1.1 204 OK');
324 # nvt_print("X-SID: $ENV{SID}") if $ENV{SID};
325 ($quota,$du) = check_sender_quota($muser||$from);
326 nvt_print("X-Sender-Quota: $quota $du") if $quota;
327 ($quota,$du) = check_recipient_quota($muser||$from);
328 nvt_print("X-Recipient-Quota: $quota $du") if $quota;
333 if ($command eq 'LISTSETTINGS') {
334 http_die("illegal command \"$command\"") if $public or $anonymous;
335 nvt_print('HTTP/1.1 204 OK');
336 # nvt_print("X-SID: $ENV{SID}") if $ENV{SID};
337 ($quota,$du) = check_sender_quota($muser||$from);
338 nvt_print("X-Sender-Quota: $quota $du") if $quota;
339 ($quota,$du) = check_recipient_quota($muser||$from);
340 nvt_print("X-Recipient-Quota: $quota $du") if $quota;
341 $autodelete = lc(readlink "$from/\@AUTODELETE" || $autodelete);
342 nvt_print("X-Autodelete: $autodelete");
343 $keep = readlink "$from/\@KEEP" || $keep;
344 nvt_print("X-Default-Keep: $keep");
345 $locale = readlink "$from/\@LOCALE" || $default_locale || 'english';
346 nvt_print("X-Default-Locale: $locale");
347 $mime = -e "$from/\@MIME" ? 'yes' : 'no';
348 nvt_print("X-MIME: $mime");
353 if ($command eq 'RENOTIFY') {
354 http_die("illegal command \"$command\"") if $public or $anonymous;
357 # resend notification e-mail
358 $file = readlink("$dkeydir/$dkey")
359 or html_error($error,"illegal DKEY $dkey");
361 $file = untaint($file);
362 unlink "$file/download"; # re-allow download from any ip address
363 notify_locale($dkey,'new');
370 http_header('200 OK');
372 print html_header($head);
374 print "<h3>Files from $from, ",
375 "click on the file name to resend a notification e-mail:</h3>\n",
377 foreach $file (glob "*/$from/*") {
378 next if $file =~ m:/STDFEX$:;
379 next if $file =~ m:(.+?)/: and -l $1;
380 $size = -s "$file/data";
382 $size = int($size/$MB+0.5);
383 $filename = $comment = '';
386 if ($dkey = readlink "$file/dkey") {
389 print "\nto $to :\n";
391 if (open $file,'<',"$file/filename") {
395 if ($filename and length $filename) {
396 $filename = html_quote($filename);
400 if (open $file,'<',"$file/comment") {
401 $comment = untaint(html_quote(getline($file)));
404 my $rkeep = untaint(readlink "$file/keep"||$keep_default)
405 - int((time-mtime("$file/filename"))/$DS);
406 if ($comment =~ /NOMAIL/ or
407 (readlink "$to/\@NOTIFICATION"||'') =~ /^no/i) {
408 printf "%8s MB (%2s d) %s/%s/%s\n",
413 urlencode(basename($file));
415 printf "%8s MB (%2s d) <a href=\"%s\">%s</a>%s %s\n",
418 untaint("/fup?akey=$akey&dkey=$dkey&command=RENOTIFY"),
420 $comment ? qq' "$comment"' : '',
422 " → notification e-mail has been resent" :
429 '<p><a href="/foc?akey=$akey">back to F*EX operation control</a>'
435 if ($command =~ /^LIST(RECEIVED)?$/) {
436 http_die("illegal command \"$command\"") if $public or $anonymous;
438 if ($to and $param{'TO'} eq '*') {
439 http_header('200 OK');
440 print html_header($head);
441 # "(Format: [size] [rest keep time] [filename] [comment])<p>\n",
442 print "<h3>Files from $from:</h3>\n",
444 foreach $file (glob "*/$from/*") {
445 next if $file =~ m:/STDFEX$:;
446 next if $file =~ m:(.+?)/: and -l $1;
447 $size = -s "$file/data";
449 $size = int($size/$MB+0.5);
450 $filename = $comment = '';
453 if ($dkey = readlink "$file/dkey") {
454 # die $file if -s "$file/data" and $file =~ /^$from/;
457 print "\nto $to :\n";
459 if (open $file,'<',"$file/filename") {
463 if ($filename and length $filename) {
464 $filename = html_quote($filename);
468 if (open $file,'<',"$file/comment") {
469 $comment = untaint(html_quote(getline($file)));
472 my $rkeep = untaint(readlink "$file/keep"||$keep_default)
473 - int((time-mtime("$file/filename"))/$DS);
474 printf "%8s MB (%2s d) %s <a href=\"%s\">%s</a>%s\n",
477 stat("$file/download")?'+':'-',
478 untaint("/fup?akey=$akey&dkey=$dkey&command=FORWARD"),
480 $comment?qq( "$comment"):'';
485 '<p><a href="javascript:history.back()">back to F*EX operation control</a>'
489 # list received files
492 http_header('200 OK');
493 print html_header($head);
494 # "(Format: [size] [rest keep time] [URL] [comment])<p>\n",
495 print "<h3>Files for $to (*):</h3>\n",
497 foreach $from (glob "$to/*") {
498 next if $from =~ /[A-Z]/;
501 foreach $file (glob "$to/$from/*") {
502 next if $file =~ /\/STDFEX$/;
503 $filename = $comment = '';
504 $size = -s "$file/data";
506 $size = int($size/$MB+0.5);
507 if ($dkey = readlink "$file/dkey") {
508 print "\nfrom $from :\n" unless $url;
510 $url = "$durl/$dkey/$1";
511 unless (-l "$dkeydir/$dkey") {
512 symlink untaint("../$file"),untaint("$dkeydir/$dkey");
514 if (open $file,'<',"$file/filename") {
518 if ($filename and length $filename) {
519 $filename = html_quote($filename);
523 if (open $file,'<',"$file/comment") {
524 $comment = untaint(html_quote(getline($file)));
525 $comment = ' "'.$comment.'"';
528 my $rkeep = untaint(readlink "$file/keep"||$keep_default)
529 - int((time-mtime("$file/filename"))/$DS);
530 printf "[<a href=\"/fup?akey=%s&dkey=%s&command=DELETE\">delete</a>] ",
532 printf "[<a href=\"/fup?akey=%s&dkey=%s&command=COPY\">forward</a>] ",
534 printf "%8s MB (%2s d) <a href=\"%s\">%s</a>%s\n",
535 $size,$rkeep,$url,$filename,$comment;
541 '(*) Files for other e-mail addresses you own will not be listed here!<p>'
542 '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
549 if ($command eq 'LISTSENT') {
550 http_die("illegal command \"$command\"") if $public or $anonymous;
552 http_header('200 OK');
553 print html_header($head);
554 print "<h2>Download URLs of files you have sent\n";
555 foreach $to (glob "*/$from") {
556 if (@files = glob "$to/*/data") {
558 print "<h3>to <code>$to</code> :</h3>\n";
560 foreach $file (@files) {
562 next if $file =~ /\/STDFEX$/;
563 $dkey = readlink "$file/dkey" or next;
565 print "$ENV{PROTO}://$ENV{HTTP_HOST}/fop/$dkey/$file\n";
572 '<p><a href="javascript:history.back()">back to F*EX operation control</a>'
578 if ($command eq 'FOPLOG') {
579 http_die("illegal command \"$command\"") if $public or $anonymous;
580 if (open my $log,"$logdir/fop.log") {
581 http_header('200 OK');
583 next if /\/STDFEX\s/;
584 if (s:^([^/]+)/$from/:$1 :) {
585 if (s:(\d+)/(\d+)$:$1: and $1 and $1 == $2) {
595 if ($command eq 'RECEIVEDLOG') {
596 http_die("illegal command \"$command\"") if $public or $anonymous;
597 if (open my $log,"$logdir/fup.log") {
598 http_header('200 OK');
600 next if /\sSTDFEX\s/;
613 if ($command eq 'SENDLOG') {
614 http_die("illegal command \"$command\"") if $public or $anonymous;
615 if (open my $log,"$logdir/fup.log") {
616 http_header('200 OK');
618 next if /\sSTDFEX\s/;
619 if (/(\S+\@\S+)/ and $1 eq $from) {
628 if (@to and $command eq 'CHECKRECIPIENT') {
629 http_die("illegal command \"$command\"") if $public or $anonymous;
631 nvt_print('HTTP/1.1 204 OK');
632 nvt_print("X-SID: $sid") if $sid;
633 foreach my $to (@group?@group:@to) {
634 # my $options = sprintf "(autodelete=%s,keep=%s,locale=%s)",
635 # readlink "$to/\@LOCALE"||$locale||$locale{$to}||$default_locale;
636 # my $options = sprintf "(autodelete=%s,keep=%s,locale=%s,notification=%s)",
637 my $options = sprintf "(autodelete=%s,keep=%s,locale=%s)",
638 $autodelete{$to}||$autodelete,
639 $keep{$to}||$keep_default,
640 readlink("$to/\@LOCALE")||$locale{$to}||$default_locale;
641 # readlink("$to/\@NOTIFICATION")||'full';
642 nvt_print("X-Recipient: $to $options");
645 # control back to fexsrv for further HTTP handling
649 if ($file and @to and $command eq 'DELETE') {
650 http_die("illegal command \"$command\"") if $public or $anonymous;
651 foreach (@group?@group:@to) {
653 $to =~ s/:\w+=.*//; # remove options from address
654 $del = "$to/$from/$fkey";
655 # swap to and from for special senders, see fup storage swap!
656 $del = "$from/$to/$fkey" if $from =~ /^(fexmail|anonymous)/;
659 if ($del =~ /\/\./) {
660 http_die("illegal parameter <code>$del</code>");
662 $del = untaint($del);
664 if (unlink("$del/data") or unlink("$del/upload")) {
665 if (open F,'>',"$del/error") {
666 print F "$file has been deleted by $from\n";
669 http_header('200 OK',"X-File: $del");
670 print html_header($head);
671 print "<h3>$file deleted</h3>\n";
673 http_header("404 Not Found");
674 print html_header($head);
675 print "<h3>$file not deleted</h3>\n";
678 printf "<a href=\"/fup?akey=%s&to=%s&command=LISTRECEIVED\">continue</a>\n",
681 print "</body></html>\n";
689 if ($from and $id and $rid eq $id and open my $ipr,"$from/\@UPLOAD_HOSTS") {
694 push @hosts,$_ if /\w/;
697 unless (@hosts and ipin($ra,@hosts)) {
698 http_die("<code>$from</code> is not allowed to upload from IP $ra");
703 if ($from and $id and $rid eq $id and @to and not $flink and not $seek) {
707 ($quota,$du) = check_sender_quota($muser||$from);
708 if ($quota and $du+$cl/$MB > $quota) {
709 http_die("you are overquota");
712 # check recipient quota
713 foreach my $to (@to) {
714 ($quota,$du) = check_recipient_quota($to);
715 if ($quota and $du+$cl/$MB > $quota) {
716 http_die("$to cannot receive files: is overquota");
722 # check recipients restriction
723 if ($id and $id eq $rid and $from and @to and not $public) {
727 # on secure mode "fop authorization" also check if recipient(s) exists
729 if (not $addto and $fop_auth and $id and $id eq $rid and $from and @to) {
730 my ($to_reg,$idf,$subuser);
731 foreach my $to (my @loop = @to) {
732 $to =~ s/:\w+=.*//; # remove options from address
735 if (open $idf,'<',"$to/@") {
736 $to_reg = getline($idf);
740 elsif (open $idf,'<',"$from/\@SUBUSER") {
745 ($subuser) = split ':';
746 if ($subuser eq $to or $subuser eq '*@*'
747 or $subuser =~ /^\*\@(.+)/ and $to =~ /\@\Q$1\E$/i
748 or $subuser =~ /(.+)\@\*$/ and $to =~ /^\Q$1\E\@/i) {
756 http_die("recipient <code>$to</code> is not a registered F*EX full or sub user");
763 if ($to =~ /^@(.+)/) {
765 http_die("server runs in NOMAIL mode - groups ($to) are not allowed");
767 my $gf = "$from/\@GROUP/$1";
768 if (open $gf,'<',$gf) {
771 push @group,$1 if /(.+@.+):/;
780 "HTTP/1.1 302 Found",
781 "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/$redirect?akey=$akey",
788 if ($from and $id and $id eq $rid and $faillog) {
792 # display HTML form and request user data
795 if ($test) { $cgi = $test }
796 else { $cgi = $ENV{SCRIPT_NAME} }
799 # delete old cookies on logout referer
801 if ($logout and my $cookie = $ENV{HTTP_COOKIE}) {
802 while ($cookie =~ s/(\w+key)=\w+//) {
803 push @cookies,"Set-Cookie: $1=x; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT";
807 if (($akey or $skey or $gkey) and $from and -d $from) {
808 # save default locale for this user
809 if (not $locale and ($ENV{HTTP_COOKIE}||'') =~ /\blocale=(\w+)/) {
812 mksymlink("$from/\@LOCALE",$locale) if $locale;
815 http_header('200 OK',@cookies);
816 # print html_header($head,'<img src="/fex_small.gif">');
817 print html_header($head);
819 if ($http_client =~ /(Konqueror|w3m)/) {
823 '<h3>Your client seems to be "$1" which is incompatible with F*EX and will probably not work!</h3>'
824 'We recommend firefox.'
830 # default "fex yourself" setting?
831 if ($from and $id and $id eq $rid and not $addto
832 and not ($gkey or $skey or $okey or $public or $anonymous)
833 and (not @to or "@to" eq $from)
834 and -f "$from/\@FEXYOURSELF")
837 $nomail = 'fexyourself';
840 # ask for recipient address(es)
841 elsif ($from and $id and $id eq $rid and ($addto or not $submit or not @to)
842 and not ($gkey or $skey or $okey or $public or $anonymous))
844 present_locales('/fup');
846 # print "[$addto] [$submit] [@to]<p>\n";
848 @ab = ("<option></option>");
850 # select menu from server address book
851 if (open my $AB,'<',"$from/\@ADDRESS_BOOK") {
854 if (/(\S+)[=\s]+(\S+@[\w.-]+\S*)/) {
858 push @ab,"<option>$_</option>";
866 foreach (glob "$from/\@GROUP/*") {
869 push @ab,"<option>\@$_</option>" unless /~$/;
875 my $ab64 = b64("from=$from&id=$id");
876 # '<form class="uploadform" name="upload"'
878 '<form name="upload"'
881 ' accept-charset="UTF-8"'
882 ' enctype="multipart/form-data">'
883 ' <input type="hidden" name="from" value="$from">'
884 ' <input type="hidden" name="id" value="$id">'
885 ' <table border="1">'
886 ' <tr><td>sender: <td><a href="/foc">$from</a></tr>'
887 ' <tr title="e-mail address or alias"><td>recipient(s):'
888 ' <td><input type="text" name="to" size="96" value="$to"><br>'
892 ' or select from your address book:'
893 ' <select name="addto" size="1">@ab</select>'
895 ' <input type="submit" name="addsubmit" value="add to recipients list">'
903 my $rr = "$from/\@ALLOWED_RECIPIENTS";
904 if (-s $rr and open $rr,'<',$rr) {
906 'You are a restricted user and may only fex to these recipients:<p>'
914 if (/^\@LOCAL_RDOMAINS/) {
915 foreach my $rd (@local_rdomains) {
918 } elsif (/^\@LOCAL_USERS/) {
919 foreach (glob "*/@") {
930 print qq' <input type="submit" name="submit" value="check recipient(s) and continue">';
931 if ($fex_yourself =~ /^yes|1/i) {
932 print qq' or <input type="submit" name="fexyourself" value="fex yourself">'
934 print "\n</form>\n<p>\n";
935 if ($akey and -f "$from/\@" and not $captive ) {
937 '<a href="/foc?akey=$akey">user config & operation control</a>'
941 if ($from eq $admin ) {
944 '<a href="/fac">server config & admin control</a>'
948 if (0 and -f "$docdir/FIX.jar") {
950 if ($public) { print "<a href=\"/fix?from=$from&id=$public&to=$to\">" }
951 elsif ($skey) { print "<a href=\"/fix?skey=$skey&to=$to\">" }
952 elsif ($gkey) { print "<a href=\"/fix?gkey=$gkey&to=$to\">" }
953 else { print "<a href=\"/fix?akey=$akey\">" }
954 print "Alternate Java client</a> (for files > 2 GB or sending of more than one file)\n";
960 'Warning: the recipient must not be a mailing list,'
961 'because after download the file will be no more available!'
963 'Contact <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a> if you want to fex to a mailing list,'
964 'he can allow multiple downloads for specific addresses.'
966 'Use a <a href="/tools.html">F*EX client</a> if you want to send more than one file or resume an interrupted upload.'
975 if ($from and ($id or $okey)) {
976 $to = $group if $group;
977 present_locales($ENV{REQUEST_URI}) if $skey or $gkey or $okey;
978 # " '$ENV{PROTO}://$ENV{HTTP_HOST}/$cgi?showstatus=$uid',"
980 '<script type="text/javascript">'
981 ' function showstatus() {'
982 ' var file = document.forms["upload"].elements["file"].value;'
983 ' if (file == "") return false;'
985 " '/$cgi?showstatus=$uid',"
987 " 'width=700,height=500'"
992 ' function checkupload() {'
993 ' var file = document.forms["upload"].elements["file"].value;'
994 ' if (file == "") { alert("No file selected"); }'
997 ' function reportsize() {'
998 ' var form = document.forms["upload"];'
999 ' var filesize = form.file.files[0].size;'
1000 ' // alert(filesize + " bytes");'
1001 ' form.elements["filesize"].value = filesize;'
1002 ' filesize = filesize.toString();'
1003 ' filesize = filesize.replace(/(\\d)(?=(\\d\\d\\d)+(?!\\d))/g,"\$1,");'
1004 ' document.getElementById("filesize").innerHTML = filesize + " bytes";'
1009 '<form name="upload"'
1012 ' accept-charset="UTF-8"'
1013 ' enctype="multipart/form-data"'
1014 ' onsubmit="return showstatus();">'
1015 ' <input type="hidden" name="uid" value="$uid">'
1016 ' <input type="hidden" name="from" value="$from">'
1017 ' <input type="hidden" name="filesize" value="">'
1021 my $toh = join('<br>',@to);
1023 ' <input type="hidden" name="id" value="$public">'
1024 ' <input type="hidden" name="to" value="$to">'
1025 ' <table border="1">'
1026 ' <tr><td>sender: <td><code>$from</code></tr>'
1027 ' <tr><td>recipient:<td><code>$toh</code></tr>'
1031 ' <input type="hidden" name="okey" value="$okey">'
1032 ' <input type="hidden" name="to" value="$to">'
1033 ' <table border="1">'
1034 ' <tr><td>sender: <td>$from</tr>'
1035 ' <tr><td>recipient:<td>$to</tr>'
1039 ' <input type="hidden" name="skey" value="$skey">'
1040 ' <table border="1">'
1041 ' <tr><td>sender: <td>$from</tr>'
1042 ' <tr><td>recipient:<td>$to</tr>'
1047 ' <input type="hidden" name="gkey" value="$gkey">'
1050 my $toh = "group $group:<ul>";
1051 my $toc = join(',',@group);
1052 foreach my $gm (@group) { $toh .= "<li>$gm" }
1055 ' <input type="hidden" name="id" value="$id">'
1056 ' <table border="1">'
1057 ' <tr><td>sender:<td>$from</tr>'
1058 ' <tr><td>recipient(s):'
1059 ' <td><input type="hidden" name="to" value="$toc">$toh</tr>'
1062 my $toc = join(',',@to);
1063 my $toh = join('<br>',@to);
1065 ' <input type="hidden" name="akey" value="$akey">'
1066 ' <table border="1">'
1067 ' <tr><td>sender:<td><a href="/foc">$from</a></tr>'
1071 ' <tr><td>recipient:'
1072 ' <td><input type="hidden" name="to" value="$toc">$toh</tr>'
1076 ' <tr><td><a href="/fup?akey=$akey&to=$toc">recipient(s)</a>:'
1077 ' <td><input type="hidden" name="to" value="$toc">$toh</tr>'
1082 $autodelete = lc $autodelete;
1083 $keep = $keep_default unless $keep;
1084 my ($quota,$du) = check_sender_quota($muser||$from);
1086 ? "<tr><td>sender quota (used):<td>$quota ($du) MB</tr>"
1089 $bwl = qq'<input type="text" name="bwlimit" size="8" value="$bwlimit"> kB/s';
1091 foreach (@throttle) {
1092 if (/\[?(.+?)\]?:(\d+)$/) {
1095 # throttle ip address?
1096 if ($throttle =~ /^[\w:.-]+$/) {
1097 if (ipin($ra,$throttle)) {
1098 $bwl = qq'<input type="hidden" name="bwlimit" value="$limit"> $limit kB/s';
1102 # throttle e-mail address?
1104 # allow wildcard *, but not regexps
1105 $throttle =~ quotemeta $throttle;
1106 $throttle =~ s/\*/.*/g;
1107 if ($from =~ /^$throttle$/i) {
1108 $bwl = qq'<input type="hidden" name="bwlimit" value="$limit"> $limit kB/s';
1116 $autodelete = $autodelete{$to} if $autodelete{$to};
1120 if (/yes/i) { $adt = 'delete file after download' }
1121 elsif (/no/i) { $adt = 'do not delete file after download' }
1122 elsif (/delay/i) { $adt = 'delete file after download with delay' }
1123 elsif (/^\d+$/) { $adt = "delete file $autodelete days after download" }
1125 $adt .= qq'<input type="hidden" name="autodelete" value="$autodelete">';
1127 my $ctr = my $ktr = '';
1129 $ctr = qq'<em>no notification e-mail will be send</em>';
1131 $ctr = qq'<input type="text" name="comment" size="80" value="$comment">';
1134 $ktr = qq'$keep days<input type="hidden" name="keep" value="$keep">';
1136 $ktr = qq'$keep days<input type="hidden" name="keep" value="$keep">';
1139 ' <tr><td>autodelete:'
1142 ' <tr title="keep file max $keep days, then delete it"><td>keep:'
1146 ' <tr title="optional, full speed if empty"><td>bandwith limit:'
1149 ' <tr title="optional, will be included in notification e-mail"><td>comment:'
1152 ' <tr title="If you want to send more than one file, then put them in a zip or tar archive"><td>file:'
1153 ' <td><input type="file" name="file" size="80" value="$file" onchange="reportsize();">'
1155 ' <tr><td>file size:<td id="filesize"></td></tr>'
1158 ' <input type="submit" value="upload" onclick="checkupload()">'
1162 if ($akey and -f "$from/\@" and not $captive) {
1164 "<a href=\"/foc?akey=$akey\">user config & operation control</a>\n";
1166 if ($from eq $admin ) {
1169 '<a href="/fac">server config & admin control</a>'
1172 if (0 and -f "$docdir/FIX.jar" and not $okey) {
1174 if ($public) { print "<a href=\"/fix?from=$from&id=$public&to=$to\">" }
1175 elsif ($skey) { print "<a href=\"/fix?skey=$skey&to=$to\">" }
1176 elsif ($gkey) { print "<a href=\"/fix?gkey=$gkey&to=$to\">" }
1177 else { print "<a href=\"/fix?akey=$akey&to=$to\">" }
1178 print "Alternate Java client</a> (for files > 2 GB or sending of more than one file)\n";
1182 # printf "<hr><pre>%s</pre>\n",$ENV{HTTP_HEADER};
1183 print "</body></html>\n";
1187 present_locales('/fup');
1189 if ($ENV{REQUEST_METHOD} eq 'POST') {
1191 '<font color="red"><h3>'
1192 ' You have to fill out this form completely to continue.'
1198 '<form action="/fup"'
1200 ' accept-charset="UTF-8"'
1201 ' enctype="multipart/form-data">'
1204 ' <td><input type="text" name="from" size="40" value="$from"></tr>'
1206 ' <td><input type="password" name="id" size="16" value="$id" autocomplete="off"></tr>'
1209 if ($mail_authid and not ($fop_auth or $nomail)) {
1211 # 'If you enter "?" as your auth-ID then it will be sent by e-mail to you.'
1215 ' <input type="checkbox" name="ID_forgotten" value="ID_forgotten">'
1216 ' I have lost my auth-ID! Send it to me by e-mail! '
1217 ' (you must fill out sender field above)'
1221 ' <p><input type="submit" value="check ID and continue"><p>'
1223 if (not $nomail and (
1224 @local_domains and @local_hosts or
1225 @local_rdomains and @local_rhosts or
1229 'You can <a href="/fur">register yourself</a> '
1230 'if you do not have a F*EX account yet.<p>'
1233 if (@anonymous_upload and ipin($ra,@anonymous_upload)) {
1234 my $a = 'anonymous_'.int(rand(999999));
1236 'You may also use <a href="/fup?from=anonymous&to=$a">anonymous upload</a>'
1239 # if (-f "$docdir/sup.html") {
1242 # 'You may also use <a href="/sup.html">simple upload</a>'
1247 print $info_login||$info_1;
1249 if ($debug and $debug>1) {
1250 print "<hr>\n<pre>\n";
1251 foreach $v (sort keys %ENV) {
1252 print "$v = $ENV{$v}\n";
1257 print "</body></html>\n";
1262 if ($from and $file and not @to) {
1263 check_rr($from,$from);
1265 $sup = 'fexyourself';
1266 $keep{$from} = readlink("$from/\@KEEP")||$keep_default;
1269 # all these variables should be defined here, but just to be sure...
1270 http_die("no file specified") unless $file;
1271 http_die("no sender specified") unless $from;
1272 http_die("no recipient specified") unless @to;
1273 unless ($okey and -l "$to/\@OKEY/$okey") {
1274 http_die("no auth-ID specified") unless $id;
1275 unless ($rid eq $id or $gkey or $skey) {
1276 faillog("user $from, id $id");
1277 http_die("wrong auth-ID specified");
1281 &check_status($from);
1284 foreach (@throttle) {
1285 if (/(.+):(\d+)$/) {
1288 if (not $bwlimit or $limit < $bwlimit) {
1289 # throttle ip address?
1290 if ($throttle =~ /^[\d.-]+$/) {
1291 if (ipin($ra,$throttle)) {
1296 # throttle e-mail address?
1298 # allow wildcard *, but not regexps
1299 $throttle =~ quotemeta $throttle;
1300 $throttle =~ s/\*/.*/g;
1301 if ($from =~ /^$throttle$/i) {
1311 # address rewriting for storage (swap sender and recipient), see also fop!
1312 if (not ($skey or $gkey) and $from =~ /^(anonymous|fexmail)/) {
1313 ($from,@to) = ("@to",$from);
1316 if (not $anonymous and $overwrite =~ /^n/i) {
1318 if (-f "$to/$from/$fkey/data") {
1319 http_die("<code>$file</code> already exists for <code>$to</code>");
1324 # additional last check
1325 unless (@group or $gkey or $skey or $public or $okey) {
1327 checkaddress($to) or
1328 http_die("<code>$to</code> is not a valid e-mail address");
1333 $to = join(',',@to);
1335 # file overwriting for anonymous is only possible if his client has the
1336 # download cookie - else request purging
1337 if ($anonymous and not $seek and my $dkey = readlink "$to/$from/$fkey/dkey") {
1338 if ($overwrite =~ /^n/i) {
1339 http_die("<code>$file</code> already exists for <code>$to</code>");
1341 if ($ENV{HTTP_COOKIE} !~ /$dkey/) {
1342 my $purge = "/fop/$dkey/$dkey?purge";
1343 # http_die("$file already exists $dkey:$ENV{HTTP_COOKIE}:");
1344 http_die("<code>$file</code> already exists - <a href=\"$purge\">purge it?!</a>");
1350 $comment = "[$group] $comment";
1352 $comment .= ' (public upload)';
1355 # file data still waits on STDIN ... get it now!
1358 if ($to eq $from and $file eq 'ADDRESS_BOOK') {
1359 unlink "$from/\@ADDRESS_BOOK";
1360 rename "$from/$from/ADDRESS_BOOK/upload","$from/\@ADDRESS_BOOK"
1361 or http_die("cannot save $from/\@ADDRESS_BOOK - $!\n");
1362 http_header('200 OK');
1363 print html_header($head);
1364 print "address book updated",
1371 foreach (@group?@group:@to) {
1373 $to =~ s/:\w+=.*//; # remove options from address
1374 $filed = "$to/$from/$fkey";
1375 $save = "$filed/data";
1376 $upload = "$filed/upload";
1377 $download = "$filed/download";
1378 $dkey{$to} = readlink "$filed/dkey";
1379 $overwrite{$to}++ if -f $save and not -f $download;
1380 unlink $save,$download;
1381 rename $upload,$save or http_die("cannot rename $upload to $save - $!\n");
1384 my $msg = sprintf "%s %s %s %s %s\n",
1385 isodate(time),$dkey{$to},$from,$to,$fkey;
1386 writelog('dkey.log',$msg);
1388 # send notification e-mails if necessary
1389 if (not $nomail and (readlink "$to/\@NOTIFICATION"||'') !~ /^no/i
1390 and ($comment or not $overwrite{$to})) {
1391 notify_locale($dkey{$to},'new');
1392 debuglog("notify $filed [$filename] '$comment'");
1398 $HTTP_HEADER = 'HTTP/1.1 200 OK';
1400 nvt_print($HTTP_HEADER,'Content-Type: text/html','');
1401 exit if $http_client =~ /^fexsend/;
1402 } elsif ($file eq 'STDFEX') {
1403 nvt_print($HTTP_HEADER,'');
1406 nvt_print($HTTP_HEADER);
1407 if ($xkey and not $restricted) {
1408 my $x = "$durl//$xkey";
1410 nvt_print("X-Location: $x");
1413 my $dkey = $dkey{$to};
1415 $cookie = $1 if $ENV{HTTP_COOKIE} =~ /anonymous=([\w:]+)/;
1416 $cookie .= ':'.$dkey if $cookie !~ /$dkey/;
1417 nvt_print("Set-Cookie: anonymous=$cookie");
1418 $keep{$to} = readlink("$to/\@KEEP")||$keep_default;
1420 foreach (@group?@group:@to) {
1422 $to =~ s/:\w+=.*//; # remove options from address
1423 my $file = "$to/$from/$fkey";
1424 # my $options = sprintf "(autodelete=%s,keep=%s,locale=%s,notification=%s)",
1425 my $options = sprintf "(autodelete=%s,keep=%s,locale=%s)",
1426 readlink("$file/autodelete")||$autodelete,
1427 readlink("$file/keep")||readlink("$to/\@KEEP")||$keep_default,
1428 readlink("$to/\@LOCALE")||readlink("$file/locale")||$default_locale;
1429 # readlink("$to/\@NOTIFICATION")||'full';
1430 nvt_print("X-Recipient: $to $options");
1431 nvt_print("X-Location: $durl/$dkey{$to}/$fkey") unless $restricted;
1433 if ($http_client =~ /^(fexsend|schwuppdiwupp)/) {
1437 nvt_print('Content-Type: text/html','');
1442 print html_header($head);
1445 printf "%s (%s MB) received\n",$file,int($ndata/$MB);
1446 } elsif (not $restricted and ($anonymous or $from eq $to)) {
1447 my $size = $ndata<2*1024 ? sprintf "%s B",$ndata:
1448 $ndata<2*$MB ? sprintf "%s kB",int($ndata/1024):
1449 sprintf "%s MB",int($ndata/$MB);
1451 '<code>$file</code> ($size) received and saved<p>'
1452 'Download URL for copy & paste:'
1453 '<h2>$durl/$dkey{$to}/$fkey</h2>'
1454 'Link is valid for $keep{$to} days!<p>'
1457 if ($ndata<2*1024) {
1458 print "<code>$file</code> ($ndata B) received and saved<p>\n";
1459 if (not $boring and not $seek) {
1460 print "Ehh... $ndata <b>BYTES</b>?! You are kidding?<p>\n";
1462 } elsif ($ndata<2*$MB) {
1463 $ndata = int($ndata/1024);
1464 print "<code>$file</code> ($ndata kB) received and saved<p>\n";
1465 if ($ndata<1024 and not ($boring or $seek)) {
1466 print "Using F*EX for less than 1 MB: ",
1467 "ever heard of MIME e-mail? ☺<p>\n";
1470 $ndata = int($ndata/$MB);
1471 print "<code>$file</code> ($ndata MB) received and saved<p>\n";
1476 if ($nomail or $nomail{$to}) {
1478 rmrf("$to/$from/$fkey");
1479 print "<code>$file</code> removed because you are a restricted user ".
1480 "and recipient $to cannot receive e-mail<p>\n";
1483 '$to cannot receive e-mail →'
1484 '<h3><font color="red">'
1485 ' No notification e-mail has been sent to $to!'
1487 'Download URL for copy & paste:'
1490 my $x = "$durl{$to}//$xkey";
1492 print "<h2><code>$x</code></h2>\n";
1494 print "<h2>$durl/$dkey{$to}/$fkey</h2>\n";
1495 print "Link is valid for $keep{$to} days!<p>\n";
1498 } elsif ($overwrite{$to} and not $comment) {
1499 print "(old <code>$file</code> for $to overwritten)<p>\n"
1501 print "$to notified<p>\n"
1508 unlink "$to/\@OKEY/$okey";
1509 } elsif (not $anonymous and not $sup) {
1510 print "<a href=\"/fup?submit=again";
1511 if ($public) { print "&from=$from&to=$to&id=$id" }
1512 elsif ($skey) { print "&skey=$skey" }
1513 elsif ($gkey) { print "&gkey=$gkey" }
1514 elsif ($akey) { print "&akey=$akey&to=$to" }
1515 print "&bwlimit=$bwlimit&autodelete=$autodelete&keep=$keep\">";
1516 print "send another file</a>\n";
1517 if ($http_client !~ /fexsend/ and $http_client =~ /Linux/i) {
1518 print '<p>Hi Linux-user, try ',
1519 '<a href="/FAQ/user.html#Why_should_I_use_a_special_F_EX_client">',
1520 "fexsend</a>! ☺<p>\n";
1522 if ($http_client !~ /fexit/ and $http_client =~ /Windows/i) {
1523 print '<p>Hi Windows-user, try <a href="/fexit.html">fexit</a>! ',
1529 print "</body></html>\n";
1533 # parse GET and POST requests
1538 my $qs = $ENV{QUERY_STRING};
1541 # get JUP parameters from environment (HTTP headers)
1542 while (($k,$v) = each %ENV) {
1543 if ($k =~ s/^FEX_//) {
1548 # decode base64 PATH_INFO to QUERY_STRING
1549 if ($ENV{PATH_INFO} =~ m:^/(\w+=*)$:) {
1551 $qs = sprintf("%s&%s",decode_b64($1),$qs);
1553 $qs = decode_b64($1);
1557 # parse HTTP QUERY_STRING (parameter=value pairs)
1559 foreach (split '&',$qs) {
1560 if (s/^(\w+)=(.*)//) {
1563 # decode URL-encoding
1564 $v =~ s/%([a-f0-9]{2})/chr(hex($1))/gie;
1566 if ($p eq 'AUTODELETE') {
1567 $specific{'autodelete'} = $autodelete = $v;
1569 if ($p eq 'KEEP' and /^\d+$/) {
1570 $specific{'keep'} = $keep = $v;
1572 # if ($p eq 'LOCALE') {
1573 # $specific{'locale'} = $locale = $v;
1579 # HTTP redirect does not work correctly with opera!
1580 # ==> locale handling is now done by fexsrv
1581 if (0 and $locale) {
1583 "HTTP/1.1 302 Found",
1584 "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/fup",
1585 "Set-Cookie: locale=$locale",
1587 'Content-Length: 0',
1598 # check for akey, gkey and skey (from HTTP GET)
1601 if ($ENV{REQUEST_METHOD} eq 'POST' and $cl) {
1602 foreach $sig (keys %SIG) {
1603 if ($sig !~ /^(CHLD|CLD)$/) {
1604 $SIG{$sig} = \&sigexit;
1607 $SIG{PIPE} = 'IGNORE' if $ENV{PROTO} eq 'https'; # stunnel workaround
1608 $SIG{__DIE__} = \&sigdie;
1609 http_die("invalid Content-Length header \"$cl\"") if $cl !~ /^-?\d+$/;
1611 debuglog(sprintf("awaiting %d bytes from %s %s",
1612 $cl,$ENV{REMOTE_ADDR}||'',$ENV{REMOTE_HOST}||''),"\n");
1614 &check_space($cl) if $cl > 0;
1616 $SIG{ALRM} = sub { die "TIMEOUT\n" };
1618 binmode(STDIN,':raw');
1620 if (defined($ENV{FEX_FILENAME})) {
1621 # JUP via HTTP header
1622 $file = $param{'FILE'} = $ENV{FEX_FILENAME};
1623 $fileid = $ENV{FEX_FILEID} || 0;
1624 $fpsize = $ENV{X_CONTENT_LENGTH} || 0;
1626 } elsif ($contentlength) {
1627 # JUP via URL parameter
1628 $fpsize = $contentlength;
1632 if ($ENV{CONTENT_TYPE} =~ /boundary=\"?([\w\-\+\/_]+)/) {
1635 http_die("malformed HTTP POST (no boundary found)");
1638 READPOST: while (&nvt_read) {
1639 # the file itself - *must* be last part of POST!
1640 if (/^Content-Disposition:\s*form-data;\s*name="file";\s*filename="(.+)"/i) {
1642 $file = $param{'FILE'} = $1;
1645 $fileid = $1 if /^X-File-ID:\s*(.+)/;
1646 $fpsize = $1 if /^Content-Length:\s*(\d+)/;
1647 $flink = $1 if /^Content-Location:\s*(\/.+)/;
1650 # STDIN is now at begin of file, will be read later with get_file()
1653 # all other parameters
1654 if (/^Content-Disposition:\s*form-data;\s*name="([a-z]\w*)"/i) {
1656 nvt_skip_to('^\s*$');
1659 NEXTPART: while (&nvt_read) {
1660 last READPOST if /^--\Q$boundary--/;
1661 last NEXTPART if /^--\Q$boundary/;
1667 if (length($file)) {
1668 $file =~ s/%(\d+)/chr($1)/ge;
1669 $file = untaint(strip_path(normalize($file)));
1670 $file =~ s/[\\\/<>]/_/g; # filter out dangerous chars
1671 $file =~ s/^\|//; # filter out dangerous chars
1672 $file =~ s/\|$//; # filter out dangerous chars
1674 $fkey = urlencode($file);
1677 # check for akey, gkey and skey (from HTTP POST)
1683 unless ($skey or $gkey or $okey) {
1684 $from .= '@'.$mdomain if $mdomain and $from !~ /@/;
1685 if ($from ne 'anonymous' and not checkaddress($from)) {
1686 http_die("<code>$from</code> is not a valid e-mail address");
1689 $from = untaint($from);
1692 # collect multiple addresses and check for aliases (not group)
1693 if (@to and "@to" !~ /^@[\w-]+$/
1694 and not ($gkey or $addto or $command =~ /^LIST(RECEIVED)?$/)) {
1696 if ($from and open my $AB,'<',"$from/\@ADDRESS_BOOK") {
1697 my ($alias,$addresses,$autodelete,$locale,$keep);
1701 if (s/^\s*(\S+)[=\s]+(\S+)//) {
1702 ($alias,$addresses) = ($1,$2);
1703 # alias specific options?
1704 $autodelete = $locale = $keep = '';
1705 $autodelete = $1 if /autodelete=(\w+)/;
1706 $locale = $1 if /locale=(\w+)/;
1707 $keep = $1 if /keep=(\d+)/;
1708 foreach my $address (split(",",$addresses)) {
1709 # alias address specific :options?
1710 if ($address =~ s/(.+?):(.+)/$1/) {
1711 my @options = split(':',$2);
1712 $address = expand($address);
1713 foreach (@options) {
1714 if (/^keep=(\d+)$/i) {
1715 $alias_keep{$alias}{$address} = $1
1717 if (/^autodelete=(yes|no|delay)$/i) {
1718 $alias_autodelete{$alias}{$address} = $1
1720 if (/^locale=(\w+)$/i) {
1721 $alias_locale{$alias}{$address} = $1
1725 $address = expand($address);
1727 push @{$ab{$alias}},$address;
1728 $autodelete{$alias} = $autodelete if $autodelete;
1729 $keep{$alias} = $keep if $keep;
1730 $locale{$alias} = $locale if $locale;
1737 # look for recipient's options and eliminate dupes
1739 foreach my $to (my @loop = @to) {
1740 # address book alias?
1741 if ($to !~ /@/ and ($ab{$to} or $to =~ /(.+?):(.+)/ and $ab{$1})) {
1744 $alias =~ s/:(.*)// and @options = split(':',$1);
1746 # alias with :options
1748 foreach my $address (my @loop = @{$ab{$alias}}) {
1749 $to{$address} = $address; # ignore dupes
1750 foreach (@options) {
1751 $keep{$address} = $1 if /^keep=(\d+)$/i;
1752 $autodelete{$address} = $1 if /^autodelete=(yes|no|delay)$/i;
1753 $locale{$address} = $1 if /^locale=(\w+)$/i;
1757 foreach my $address (my @loop = @{$ab{$alias}}) {
1758 $to{$address} = $address; # ignore dupes
1759 unless ($keep{$address}) {
1760 $keep{$address} = $keep{$alias} if $keep{$alias};
1761 if ($specific{'keep'}) {
1762 $keep{$address} = $specific{'keep'}
1763 } elsif (my $keep = $alias_keep{$alias}{$address}) {
1764 $keep{$address} = $keep;
1765 } elsif ($keep{$alias}) {
1766 $keep{$address} = $keep{$alias}
1769 unless ($autodelete{$address}) {
1770 if ($specific{'autodelete'}) {
1771 $autodelete{$address} = $specific{'autodelete'};
1772 } elsif (my $autodelete = $alias_autodelete{$alias}{$address}) {
1773 $autodelete{$address} = $keep;
1774 } elsif ($autodelete{$alias}) {
1775 $autodelete{$address} = $autodelete{$alias};
1777 $autodelete{$address} = readlink "$address/\@AUTODELETE"
1781 unless ($locale{$address}) {
1782 if (my $locale = readlink "$address/\@LOCALE") {
1783 $locale{$address} = $locale;
1784 } elsif ($locale{$alias}) {
1785 $locale{$address} = $locale{$alias};
1786 } elsif ($locale = $alias_locale{$alias}{$address}) {
1787 $locale{$address} = $locale;
1789 $locale{$address} = $::locale ;
1791 $locale{$address} ||= $default_locale || 'english';
1795 # regular address, not an alias
1796 if ($to =~ s/(.+?):(.+)/$1/) {
1797 my @options = split(':',$2);
1799 foreach (@options) {
1800 $keep{$to} = $1 if /^keep=(\d+)$/i;
1801 $autodelete{$to} = $1 if /^autodelete=(yes|no|delay)$/i;
1802 $locale{$to} = $1 if /^locale=(\w+)$/i;
1806 $to{$to} = $to; # ignore dupes
1807 unless ($autodelete{$to}) {
1808 $autodelete{$to} = untaint(readlink("$to/\@AUTODELETE")
1810 if ($specific{'autodelete'}) {
1811 $autodelete{$to} = $specific{'autodelete'};
1814 unless ($keep{$to}) {
1815 $keep{$to} = $keep_default;
1816 $keep{$to} = $keep if $keep;
1817 $keep{$to} = untaint(readlink "$to/\@KEEP") if -l "$to/\@KEEP";
1818 $keep{$to} = $specific{'keep'} if $specific{'keep'};
1821 $autodelete{$to} = 'NO' if $to =~ /$amdl/; # mailing lists, etc
1822 if (-e "$to/\@CAPTIVE") {
1824 $v = readlink "$to/\@AUTODELETE" and $autodelete{$to} = $v;
1825 $v = readlink "$to/\@KEEP" and $keep{$to} = $v;
1830 if (scalar(@to) == 1) {
1832 $keep = $keep{$to} if $keep{$to};
1833 $autodelete = $autodelete{$to} if $autodelete{$to};
1836 # check recipients and eliminate dupes
1839 if ($to eq 'anonymous') {
1842 if ($to =~ /^@(.+)/) {
1843 http_die("You cannot send to more than one group") if @to > 1;
1844 http_die("Group <code>$to</code> does not exist") unless -f "$from/\@GROUP/$1";
1846 if ($skey or $gkey or $okey or checkaddress($to)) {
1847 $to .= '@'.$mdomain if $mdomain and $to !~ /@/;
1848 $to{$to} = untaint($to);
1850 http_die("<code>$to</code> is not a valid e-mail address");
1859 unless (checkforbidden($to)) {
1860 http_die("<code>$to</code> is not allowed");
1866 # show the status progress bar
1869 my ($upload,$data,$sfile,$ukey,$file);
1871 my ($t0,$t1,$t2,$tt,$ts,$tm);
1872 my ($osize,$percent,$npercent);
1875 $wclose = '<p><a href="#" onclick="window.close()">close</a>'."\n".
1876 '</body></html>'."\n";
1877 $ukey = "$ukeydir/$uid";
1878 $upload = "$ukey/upload";
1879 $data = "$ukey/data";
1880 $sfile = "$ukey/size";
1883 $tsize = readlink $sfile and last;
1885 # remark: stupid Internet Explorer *needs* the error represented in this
1886 # asynchronous popup window, because it cannot display the error in the
1887 # main window on HTTP POST!
1888 if (-f $ukey and open $ukey,'<',$ukey or
1889 -f "$ukey/error" and open $ukey,'<',"$ukey/error") {
1892 html_error($error,<$ukey> || 'unknown');
1897 if (defined $tsize and $tsize == 0) {
1898 print "<script type='text/javascript'>window.close()</script>\n";
1903 "no file data received - does your file exist or is it >2GB?")
1905 html_error($error,"file size unknown") unless $tsize =~ /^\d+$/;
1907 http_header('200 OK');
1908 if (open $ukey,'<',"$ukey/filename") {
1913 http_die("no filename?!") unless $file;
1916 if ($ssize<2097152) {
1917 $ssize = sprintf "%d kB",int($ssize/1024);
1919 $ssize = sprintf "%d MB",int($ssize/1048576);
1925 "<h1>Upload Status for<br><code>$file ($ssize)</code></h1>"
1926 '<img src="/action-fex-camel.gif" id="afc">'
1928 "<input type='text' id='percent' style='margin-left:1ex;color:black;background:transparent;border:none;width:32ex;' disabled='true' value='0%'>"
1929 "<div style='border:1px solid black;width:100%;height:20px;'>"
1930 "<div style='float:left;width:0%;background:black;height:20px;' id='bar'>"
1934 # wait for upload file
1936 last if -f $upload or -f $data;
1939 unless (-f $upload or -f $data) {
1940 print "<p><H3>ERROR: no upload received</H3>\n";
1945 $SIG{ALRM} = sub { die "TIMEOUT in showstatus: no (more) data received\n" };
1949 $osize = $percent = $npercent = 0;
1951 for ($percent = 0; $percent<100; sleep(1)) {
1953 $nsize = -s $upload;
1954 if (defined $nsize) {
1955 if ($nsize<$osize) {
1956 print "<p><h3>ABORTED</h3>\n";
1960 if ($nsize>$osize) {
1964 $npercent = int($nsize*100/$tsize);
1965 $showsize = calcsize($tsize,$nsize);
1968 $showsize = calcsize($tsize,$tsize);
1970 # hint: for ISDN (or even slower) links, 5 s tcp delay is minimum
1971 # so, updating more often is contra-productive
1972 if ($t2>$t1+5 or $npercent>$percent) {
1973 $percent = $npercent;
1975 $tm = int(($t2-$t0)/60);
1976 $ts = $t2-$t0-$tm*60;
1977 $tt = sprintf("%d:%02d",$tm,$ts);
1979 "<script type='text/javascript'>"
1980 " document.getElementById('bar').style.width = '$percent%';"
1981 " document.getElementById('percent').value = '$showsize, $tt, $percent %';"
1988 if ($npercent == 100) {
1989 print "<h3>file successfully transferred</h3>\n";
1991 print "<h3>file transfer aborted</h3>\n";
1994 "<script type='text/javascript'>"
1995 " document.getElementById('afc').src='/logo.jpg'"
2004 # get file from post request
2006 my ($to,$filed,$upload,$nupload,$speed,$download);
2011 my $fb = 0; # file bytes
2012 my $ebl = 0; # end boundary length
2016 $ebl = length($boundary)+8; # 8: 2 * CRLF + 2 * "--"
2021 # download already in progress?
2023 $to =~ s/:\w+=.*//; # remove options from address
2024 $filed = "$to/$from/$fkey";
2025 $download = "$filed/download";
2026 if (-f $download and open $download,'>>',$download) {
2027 flock($download,LOCK_EX|LOCK_NB) or
2028 http_die("<code>$filed</code> locked: a download is currently in progress");
2034 $to =~ s/:\w+=.*//; # remove options from address
2035 $filed = "$to/$from/$fkey";
2036 $nupload = "$filed/upload"; # upload for next recipient
2039 # upload already prepared (for first recipient)?
2041 # link upload for next recipient
2042 unless ($upload eq $nupload or
2043 -r $upload and -r $nupload and
2044 (stat $upload)[1] == (stat $nupload)[1])
2047 link $upload,$nupload;
2051 # first recipient => create upload
2054 unlink "$ukeydir/$uid";
2057 http_die("cannot resume on link upload");
2059 &nvt_read and $flink = $_;
2060 if ($flink !~ /^\//) {
2061 http_die("no file link name ($flink)");
2063 $flink = abs_path($flink);
2065 foreach (@file_link_dirs) {
2066 my $dir = abs_path($_);
2067 $fok = $flink if $flink =~ /^\Q$dir\//;
2070 http_die("<code>$flink</code> not allowed for linking");
2072 my @s = stat($flink);
2073 unless (@s and ($s[2] & S_IROTH) and -r $flink) {
2074 http_die("cannot read <code>$flink</code>");
2076 unless (-f $flink and not -l $flink) {
2077 http_die("<code>$flink</code> is not a regular file");
2079 # http_die("DEBUG: flink = $flink");
2082 unless (/^--\Q$boundary--/) {
2083 http_die("found no MIME end boundary in upload ($_)");
2086 symlink untaint($flink),$upload;
2088 unlink $upload if -l $upload;
2089 open $upload,'>>',$upload or http_die("cannot write $upload - $!");
2090 flock($upload,LOCK_EX|LOCK_NB) or
2091 http_die("<code>$file</code> locked: a transfer is already in progress");
2096 # already uploaded file data size
2098 # provide upload ID symlink for showstatus
2099 symlink "../$filed","$ukeydir/$uid";
2103 unlink "$filed/autodelete",
2105 "$filed/restrictions",
2117 unlink "$filed/size" unless $seek;
2119 # showstatus needs file name and size
2120 # fexsend needs full file size (+$seek)
2121 $fh = "$filed/filename";
2122 open $fh,'>',$fh or die "cannot write $fh - $!\n";
2123 print {$fh} $filename;
2125 if ($::filesize > 0 or $cl > 0) {
2126 if ($::filesize > 0) { $filesize = $fpsize || $::filesize }
2127 else { $filesize = $cl-$RB-$ebl+$seek }
2130 if ($::filesize > 0) {
2131 # total file size as reported by POST
2132 mksymlink("$filed/size",$::filesize)
2133 or die "cannot write $filed/size - $!\n";
2135 # file size as counted
2136 mksymlink("$filed/size",$filesize)
2137 or die "cannot write $filed/size - $!\n";
2142 if ($from eq "@to") {
2143 # special "fex yourself"
2144 mksymlink("$filed/autodelete",$specific{'autodelete'}||'NO');
2146 $autodelete{$to} = $autodelete unless $autodelete{$to};
2147 if ($autodelete{$to} =~ /^(DELAY|NO|\d+)$/i) {
2148 mksymlink("$filed/autodelete",$autodelete{$to});
2152 if (my $keep = $keep{$to} || $::keep) {
2153 mksymlink("$filed/keep",$keep);
2155 mksymlink("$filed/id",$fileid) if $fileid;
2156 mksymlink("$filed/ip",$ra) if $ra;
2157 if (my $uurl = $ENV{REQUEST_URL}) {
2158 mksymlink("$filed/uurl",$uurl);
2160 if ($http_client and open $http_client,'>',"$filed/useragent") {
2161 print {$http_client} $http_client,"\n";
2164 if ($_ = readlink "$to/\@LOCALE") {
2165 # mksymlink("$filed/locale",$_);
2166 } elsif ($locale{$to}) {
2167 mksymlink("$filed/locale",$locale{$to});
2168 } elsif ($locale and $locale ne $default_locale) {
2169 mksymlink("$filed/locale",$locale);
2171 if ($replyto and $replyto =~ /.@./) {
2172 mksymlink("$filed/replyto",$replyto);
2175 my $arh = "$from/\@ALLOWED_RHOSTS";
2177 copy($arh,"$filed/restrictions");
2180 if (@header and open $fh,'>',"$filed/header") {
2181 print {$fh} join("\n",@header),"\n";
2185 if ((readlink "$to/\@NOTIFICATION"||'') =~ /^no/i) {
2186 $nomail{$to} = 'NOTIFICATION';
2190 open $fh,'>',"$filed/notify" and close $fh;
2193 if (open $fh,'>',"$filed/comment") {
2194 print {$fh} encode_utf8($comment);
2199 # provide download ID key
2200 unless ($dkey = readlink("$filed/dkey") and -l "$dkeydir/$dkey") {
2201 $dkey = randstring(8);
2202 unlink "$dkeydir/$dkey";
2203 symlink "../$filed","$dkeydir/$dkey"
2204 or http_die("cannot symlink $dkeydir/$dkey ($!)");
2205 unlink "$filed/dkey";
2206 symlink $dkey,"$filed/dkey";
2211 # extra download (XKEY)?
2212 if ($anonymous and $fkey =~ /^afex_\d/ or
2213 $from eq "@to" and $comment =~ s:^//(.*)$:NOMAIL:)
2217 my $x = "$xkeydir/$xkey";
2218 unless (-l $x and readlink($x) eq "../$from/$from/$fkey") {
2220 http_die("extra download key $xkey already exists");
2222 symlink "../$from/$from/$fkey",$x
2223 or http_die("cannot symlink $x - $!\n");
2225 symlink $xkey,"$x/xkey";
2233 # upload link has been already created, no data to read any more
2234 $to = join(',',@to);
2235 fuplog($to,$fkey,0);
2236 debuglog("upload link successfull, dkey=$dkey");
2242 # at last, read (real) file data
2248 # read until EOF, including MIME end boundary
2249 # note: cannot use sysread because of previous buffered read!
2250 while ($n = read(STDIN,$_,$bs)) {
2253 syswrite $upload,$_ unless $nostore;
2256 # size of transferred file, without end boundary
2257 $ndata = untaint($fb-$ebl);
2260 # normal file with known file size
2264 debuglog(sprintf("still awaiting %d+%d = %d bytes",
2265 $fpsize,$ebl,$fpsize+$ebl));
2266 $cl = $RB+$fpsize+$ebl; # recalculate CONTENT_LENGTH
2269 $cl = $RB+$::filesize+$ebl; # recalculate CONTENT_LENGTH
2271 debuglog(sprintf("still awaiting %d-%d = %d bytes",
2274 # read until end boundary, not EOF
2275 while ($RB < $cl-$ebl) {
2277 $b = $bs if $b > $bs;
2278 # max wait for 1 kB/s, but at least 10 s
2279 # $timeout = $b/1024;
2280 # $timeout = 10 if $timeout < 10;
2282 if ($n = read(STDIN,$_,$b)) {
2285 # syswrite is much faster than print
2286 syswrite $upload,$_ unless $nostore;
2289 $tt = (time-$t0) || 1;
2290 while ($RB/$tt/1024 > $bwlimit) {
2300 # read end boundary - F*IX is broken!
2301 if ($ebl and $http_client !~ /F\*IX/) {
2304 unless (/^--\Q$boundary--/) {
2305 http_die("found no MIME end boundary in upload ($_)");
2309 $ndata = untaint($fb);
2315 close $upload; # or die "cannot close $upload - $!\n";;
2318 $tt = (time-$t0) || 1;
2319 mksymlink("$filed/speed",int($fb/1024/$tt));
2323 "No file data received!".
2324 " File name correct?".
2325 " File too big (browser-limit: 2 GB!)?"
2329 $to = join(',',@to);
2334 open $upload,'<',$upload or http_die("internal error - cannot read upload");
2335 seek $upload,$ndata+2,0;
2337 unless (/^--\Q$boundary--/) {
2338 http_die("found no MIME end boundary in upload ($_)");
2341 truncate $upload,$ndata;
2345 # truncate boundary string
2346 # truncate $upload,$ndata+$uss if -s $upload > $ndata+$uss;
2350 fuplog($to,$fkey,$ndata,'(aborted)');
2352 http_die("read $RB bytes, but Content-Length announces $fpsize bytes");
2354 http_die("read $RB bytes, but CONTENT_LENGTH announces $cl bytes");
2358 # multipost, not complete
2359 if ($::filesize > -s $upload) {
2360 http_header('206 Partial OK');
2365 if (-s $upload > ($::filesize||$filesize)) {
2366 fuplog($to,$fkey,$ndata,'(write error: upload > filesize)');
2367 http_die("internal server error while writing file data");
2371 fuplog($to,$fkey,$ndata);
2372 debuglog("upload successfull, dkey=$dkey");
2378 # check recipients restriction
2382 my $rr = "$from/\@ALLOWED_RECIPIENTS";
2383 my ($allowed,$to,$ar,$rd);
2385 if (-s $rr and open $rr,'<',$rr) {
2398 if (/^\@LOCAL_RDOMAINS/) {
2400 foreach (@local_rdomains) {
2402 # allow wildcard *, but not regexps
2404 $rd =~ s/\*/[\\w.-]+/g;
2405 $ar .= '|[^\@]+\@' . $rd;
2408 } elsif (/^\@LOCAL_USERS/ and -s "$to/@") {
2412 # allow wildcard *, but not regexps
2414 $ar =~ s/\\\*/[^@]*/g;
2417 if ($to =~ /^$ar$/i) {
2425 fuplog("ERROR: $from not allowed to fex to $to");
2426 debuglog("$to not in $spooldir/$from/\@ALLOWED_RECIPIENTS");
2427 http_die("You ($from) are not allowed to fex to $to");
2436 # add domain to user if necessary
2441 foreach my $u (my @loop = @users) {
2442 if ($u =~ /^anonymous(_\d+)?$/) {
2443 $u = "$u\@$hostname";
2445 if ($u eq 'nettest') {
2446 if ($mdomain and -d "$u\@$mdomain") {
2448 } elsif (-d "$u\@$hostname") {
2452 if ($u =~ /@/) { push @ua,$u }
2453 elsif ($mdomain) { push @ua,"$u\@$mdomain" }
2454 elsif (-d "$u\@$hostname") { push @ua,"$u\@$hostname" }
2455 else { push @ua,$u }
2458 return wantarray ? @ua : join(',',@ua);
2462 # forward-copy (bounce) an already uploaded file
2465 my ($nfile,$to,$AB);
2466 my ($filename,$keep);
2469 http_die("no file data for <code>$file</code>") unless -f "$file/data";
2471 $keep = $::keep||$keep_default;
2472 if (my $mt = mtime("$file/data")) { $keep += int((time-$mt)/$DS) }
2476 # check recipients restriction
2477 check_rr($from,@to);
2479 # read aliases from address book
2480 if (open $AB,'<',"$from/\@ADDRESS_BOOK") {
2484 if (s/^\s*(\S+)[=\s]+(\S+)//) {
2485 my ($alias,$address) = ($1,$2);
2486 foreach my $address (split(",",$address)) {
2487 $address .= '@'.$mdomain if $mdomain and $address !~ /@/;
2488 push @{$ab{$alias}},$address;
2496 foreach my $to (my @loop = @to) {
2498 foreach my $address (@{$ab{$to}}) {
2499 $to{$address} = $address;
2502 $to .= '@'.$mdomain if $mdomain and $to !~ /@/;
2509 http_header('200 OK');
2510 print html_header($head);
2512 foreach my $to (my @loop = @to) {
2513 $to =~ s/:\w+=.*//; # remove options from address
2515 $nfile =~ s:.*?/:$to/:;
2516 next if $nfile eq $file;
2518 http_die("cannot create directory $nfile") unless -d $nfile;
2519 unlink "$nfile/data",
2522 "$nfile/autodelete",
2524 "$nfile/restrictions",
2533 open $comment,'>',"$nfile/comment";
2534 print {$comment} $comment;
2537 if ($autodelete =~ /^(DELAY|NO|\d+)$/i) {
2538 symlink $autodelete,"$nfile/autodelete";
2540 symlink $keep, "$nfile/keep";
2541 copy("$file/id", "$nfile/id");
2542 copy("$file/ip", "$nfile/ip");
2543 copy("$file/speed", "$nfile/speed");
2544 copy("$file/replyto", "$nfile/replyto");
2545 $filename = copy("$file/filename", "$nfile/filename");
2546 link "$file/data", "$nfile/data"
2547 or die http_die("cannot create $nfile/data - $!");
2548 unless ($dkey = readlink("$nfile/dkey") and -l "$dkeydir/$dkey") {
2549 $dkey = randstring(8);
2550 unlink "$dkeydir/$dkey";
2551 symlink "../$nfile","$dkeydir/$dkey"
2552 or http_die("cannot symlink $dkeydir/$dkey");
2553 unlink "$nfile/dkey";
2554 symlink $dkey,"$nfile/dkey"
2555 or http_die("cannot create $nfile/dkey - $!");
2558 if ($nomail or $nomail{$to}) {
2560 my $url = "$durl/$dkey/".normalize_filename($filename);
2562 'Download-URL for $to:<br>'
2568 notify_locale($dkey,'new');
2569 fuplog($to,urlencode($filename),"(forwarded)");
2572 'File "$filename" copy-forwarded to $to and notified.'
2579 '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
2583 $filename = filename($file);
2584 http_header('200 OK');
2585 print html_header($head);
2587 '<form name="upload"'
2590 ' accept-charset="UTF-8"'
2591 ' enctype="multipart/form-data">'
2592 ' <input type="hidden" name="akey" value="$akey">'
2593 ' <input type="hidden" name="dkey" value="$dkey">'
2594 ' <input type="hidden" name="command" value="FORWARD">'
2595 ' forward a copy of "<code>$filename</code>" to:<br>'
2596 ' <input type="text" name="to" size="80">'
2604 # modify file parameter
2607 my $filename = filename($file);
2608 my $dkey = readlink "$file/$dkey";
2612 http_die("no file data for <code>$file</code>") unless -f "$file/data";
2616 if ($specific{'keep'}) {
2617 mksymlink("$file/keep",$keep);
2618 utime time,time,"$file/filename";
2619 push @parameter,'KEEP';
2621 if ($specific{'autodelete'}) {
2622 mksymlink("$file/autodelete",$autodelete);
2623 push @parameter,'AUTODELETE';
2626 if (open $comment,'>',"$file/comment") {
2627 print {$comment} $comment;
2630 notify_locale($dkey,'new');
2631 push @parameter,'COMMENT';
2633 http_header('200 OK');
2634 print "Parameter ".join(',',@parameter)." modified for $filename for $to\n";
2639 my ($tsize,$nsize) = @_;
2640 if ($tsize<2097152) {
2641 return sprintf "%d kB",int($nsize/1024);
2643 return sprintf "%d MB",int($nsize/1048576);
2648 # set parameter variables
2653 $v = uc(despace($v));
2655 # if ($vv =~ /([<>])/) {
2656 # http_die(sprintf("\"&#%s;\" is not allowed in parameter $v",ord($1)));
2660 if ($v eq 'LOGOUT') {
2662 # skey and gkey are persistant!
2663 $akey = $1 if $ENV{QUERY_STRING} =~ /AKEY:(\w+)/i;
2664 unlink "$akeydir/$akey";
2665 $login = $FEXHOME.'/cgi-bin/login';
2667 $login = readlink $login || 'login';
2669 "HTTP/1.1 302 Found",
2670 "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/$login",
2671 'Content-Length: 0',
2676 "HTTP/1.1 302 Found",
2677 "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/fup",
2678 'Content-Length: 0',
2683 } elsif ($v eq 'LOCALE' and $vv =~ /^(\w+)$/) {
2685 } elsif ($v eq 'REDIRECT' and $vv =~ /^([\w?=]+)$/) {
2687 } elsif ($v eq 'SKEY' and $vv =~ /^([\w:]+)/) {
2690 } elsif ($v eq 'GKEY' and $vv =~ /^([\w:]+)/) {
2691 $gkey = $1 unless $nomail;
2693 } elsif ($v eq 'DKEY' and $vv =~ /^(\w+)/) {
2695 } elsif ($v eq 'AKEY' and $vv =~ /^(\w+)/) {
2697 } elsif ($v eq 'FROM' or $v eq 'USER') {
2698 $from = normalize_email($vv);
2699 $from = untaint(expand($from));
2700 checkchars('from address',$from);
2701 # maybe FROM=SUBUSER !
2702 # checkaddress($from) or http_die("FROM $from is no legal e-mail address");
2703 } elsif ($v eq 'REPLYTO') {
2704 $replyto = normalize_email($vv);
2705 checkchars('replyto address',$replyto);
2706 checkaddress($replyto) or
2707 http_die("REPLYTO $replyto is no legal e-mail address");
2708 } elsif ($v eq 'ADDTO') {
2710 $addto = normalize_email($vv);
2711 } elsif ($v eq 'SUBMIT') {
2712 $submit = decode_utf8(normalize($vv));
2713 } elsif ($v eq 'FEXYOURSELF') {
2716 $specific{'autodelete'} = $autodelete = 'no';
2717 } elsif ($v eq 'TO') {
2718 # extract AUTODELETE and KEEP options
2719 if ($vv =~ s/[\s,]+AUTODELETE=(\w+)//i) {
2720 $specific{'autodelete'} = $autodelete = uc($1);
2722 if ($vv =~ s/[\s,]+KEEP=(\d+)//i) {
2724 $keep = $keep_max if $keep_max and $keep > $keep_max;
2725 $specific{'keep'} = $keep;
2727 $to = normalize(lc($vv));
2728 $to =~ s/[\n\s;,]+/,/g;
2732 unless ($specific{'autodelete'}) {
2733 $specific{'autodelete'} = $autodelete = 'no';
2738 unless ($specific{'autodelete'}) {
2739 $specific{'autodelete'} = $autodelete = 'no';
2744 checkchars('to address',$to);
2745 push @to,split(',',$to);
2746 } elsif ($v eq 'ID') {
2748 checkchars('auth-ID',$id);
2749 } elsif ($v eq 'TCE') {
2750 $test = despace($vv);
2751 } elsif ($v eq 'OKEY' and $vv =~ /^(\w+)$/) {
2754 } elsif ($v eq 'FILEID' and $vv =~ /^(\w+)$/) {
2756 } elsif ($v eq 'CONTENTLENGTH' and $vv =~ /^(\d+)$/) {
2757 $contentlength = $1;
2758 } elsif ($v eq 'FILE' or $v eq 'FILENAME') {
2759 $file = strip_path(normalize($vv));
2760 } elsif ($v eq 'UID' and $vv =~ /^(\w+)$/) {
2762 } elsif ($v eq 'ID_FORGOTTEN') {
2763 $id_forgotten = $vv;
2764 } elsif ($v eq 'SHOWSTATUS' and $vv =~ /^(\w+)$/) {
2765 $showstatus = $uid = $1;
2766 } elsif ($v eq 'COMMENT') {
2767 $comment = decode_utf8(normalize($vv));
2768 $comment =~ s/^\s*!\.!/!SHORTMAIL!/;
2769 $comment =~ s/^!#!/!NOMAIL!/;
2770 $comment =~ s/^!-!/!NOSTORE!/;
2771 $nomail = $comment if $comment =~ /NOMAIL/;
2772 $nostore = $nomail = $comment if $comment =~ /NOSTORE/;
2773 $bcc .= " $from" if $comment =~ s/\s*!bcc!?\s*//i;
2774 # backward compatibility
2775 foreach my $cmd (qw(
2776 DELETE LIST CHECKQUOTA CHECKRECIPIENT RECEIVEDLOG SENDLOG FOPLOG FORWARD
2777 )) { $command = $comment if $comment eq $cmd }
2778 } elsif ($v eq 'COMMAND') {
2779 $command = normalize($vv);
2780 } elsif ($v eq 'BWLIMIT' and $vv =~ /^(\d+)$/) {
2782 } elsif ($v eq 'SEEK' and $vv =~ /^(\d+)$/) {
2784 } elsif ($v eq 'FILESIZE' and $vv =~ /^(\d+)$/) {
2785 $filesize = $1; # complete filesize!
2786 &check_space($filesize-$seek);
2787 } elsif ($v eq 'AUTODELETE' and $vv =~ /^(\w+)$/) {
2788 $specific{'autodelete'} = $autodelete = uc($1);
2789 } elsif ($v eq 'KEEP' and $vv =~ /^(\d+)$/) {
2791 $keep = $keep_max if $keep_max and $keep > $keep_max;
2792 $specific{'keep'} = $keep;
2793 } elsif ($v eq 'TIMEOUT' and $vv =~ /^(\d+)$/) {
2794 $specific{'timeout'} = $timeout = $1;
2800 my ($id,$to,$subuser,$gm,$skey,$gkey,$url,$fup);
2805 $fup =~ s:/fop:/fup:;
2808 if (open $from,'<',"$from/\@") {
2809 $id = getline($from);
2813 $url = "$fup/".b64("from=$from&id=$id");
2814 mail_forgotten($from,qqq(qq(
2815 'Your reqested F*EX auth-ID for $fup?from=$from is:'
2825 foreach my $skey (glob("$skeydir/*")) {
2826 if (-f $skey and open $skey,'<',$skey) {
2829 if (/^(\w+)=(.+)/) {
2830 $subuser = $2 if $1 eq 'from';
2831 $to = $2 if $1 eq 'to';
2836 if ($from and $to and $from eq $subuser) {
2838 mail_forgotten($subuser,qqq(qq(
2839 'Your reqested F*EX login is:'
2848 foreach my $gkey (glob("$gkeydir/*")) {
2849 if (-f $gkey and open $gkey,'<',$gkey) {
2852 if (/^(\w+)=(.+)/) {
2853 $gm = $2 if $1 eq 'from';
2854 $to = $2 if $1 eq 'to';
2859 if ($gm and $to and $from eq $gm) {
2861 mail_forgotten($gm,qqq(qq(
2862 'Your reqested F*EX login is:'
2869 http_die("<code>$from</code> is not a F*EX user on this server");
2873 sub mail_forgotten {
2880 open P,'|-',$sendmail,$user,$bcc or http_die("cannot start sendmail - $!\n");
2884 'Subject: F*EX service $hostname'
2889 close P or http_die("cannot send mail - $!\n");
2890 http_header('200 OK');
2891 print html_header($head);
2892 print "<h3>Mail has been sent to you ($from)</h3>\n";
2893 print "</body></html>\n";
2897 # lookup akey, skey and gkey (full and sub user and group)
2900 if (@to and "@to" ne '_') {
2901 http_die("you cannot mix TO and SKEY URL parameters") if $skey;
2902 http_die("you cannot mix TO and GKEY URL parameters") if $gkey;
2905 # only one key can be valid
2906 $akey = $gkey = '' if $skey;
2907 $akey = $skey = '' if $gkey;
2911 if ($skey =~ s/^MD5H:(.+)/$1/) {
2913 foreach my $s (glob "$skeydir/*") {
2915 if ($skey eq md5_hex($s.$sid)) {
2921 if (open $skey,'<',"$skeydir/$skey") {
2924 if (/^(\w+)=(.+)/) {
2925 $from = $2 if lc($1) eq 'from';
2926 @to = ($muser = $2) if lc($1) eq 'to';
2927 $rid = $id = $2 if lc($1) eq 'id';
2933 http_die("invalid SKEY <code>$skey</code>");
2939 if ($gkey =~ s/^MD5H:(.+)/$1/) {
2941 foreach my $g (glob "$gkeydir/*") {
2943 if ($gkey eq md5_hex($g.$sid)) {
2949 if (open $gkey,'<',"$gkeydir/$gkey") {
2952 if (/^(\w+)=(.+)/) {
2953 $from = $2 if lc($1) eq 'from';
2954 $to = $muser = $2 if lc($1) eq 'to';
2955 $rid = $id = $2 if lc($1) eq 'id';
2956 # $user = $2 if lc($1) eq 'user';
2963 http_die("invalid GKEY <code>$gkey</code>");
2967 if ($akey and not $id) {
2970 # sid is not set with web browser
2971 # akey with sid is set with schwuppdiwupp & co
2972 $idf = "$akeydir/$akey/@";
2974 if (open $idf,'<',$idf and $id = getline($idf)) {
2976 $from = readlink "$akeydir/$akey"
2977 or http_die("internal server error: no $akey symlink");
2979 $from = untaint($from);
2980 if ($akey ne md5_hex("$from:$id")) {
2991 # check if there is enough space on spool
2994 my ($df,$free,$uprq);
2997 if (open $df,"df -k $spooldir|") {
2999 if (/^.+?\s+\d+\s+\d+\s+(\d+)/ and $req/1024 > $1) {
3000 $free = int($1/1024);
3001 $uprq = int($req/$MB);
3002 if (not $nomail and open P,"|$sendmail -t") {
3006 'Subject: F*EX spool out of space'
3008 'F*EX spool $spooldir on $ENV{SERVER_NAME} is out of space.'
3010 'Current free space: $free MB'
3011 'Upload request: $uprq MB'
3015 debuglog("aborting because not enough free space in spool ($free MB)");
3016 http_die("not enough free space for this upload");
3024 # global substitution as a function like in gawk
3039 $msg = sprintf "%s [%s_%s] %s (%s) %s\n",
3040 isodate(time),$$,$ENV{REQUESTCOUNT},$from,$fra,$msg;
3041 writelog($log,$msg);
3055 my $to = join(',',@to);
3057 $SIG{__DIE__} = 'DEFAULT';
3058 foreach (keys %SIG) { $SIG{$_} = 'DEFAULT' }
3060 $msg = @_ ? "@_" : '???';
3063 $msg = sprintf "%s %s (%s) %s %s caught SIGNAL %s %s\n",
3068 encode_Q($file||'-'),
3070 $RB?"(after $RB bytes)":"";
3072 writelog($log,$msg);
3074 if ($sig eq 'DIE') {
3078 die "SIGNAL $msg\n";
3083 sub present_locales {
3085 my @locales = @::locales; # from fex.ph
3090 $url =~ s/locale=\w+&//g;
3096 map { $_ = "$FEXHOME/locale/$_" } @locales;
3098 @locales = glob "$FEXHOME/locale/*";
3103 foreach my $locale (my @loop = @locales) {
3104 if (-x "$locale/cgi-bin/fup") {
3105 $lang = "$locale/lang.html";
3107 if (open $lang,'<',$lang and $lang = getline($lang)) {
3112 print "<a href=\"${url}locale=$locale\">$lang</a> ";
3124 if (open $logo,"$docdir/logo.jpg") {
3125 $camel = md5_hex(<$logo>) eq 'ad8a95bba8dd1a61d70bd38611bc2059';
3127 if ($camel and open $logo,"$docdir/action-fex-camel.gif") {
3128 $camel = md5_hex(<$logo>) eq '1f3d7acc70377496f95c5adddaf4ca7b';
3130 http_die("Missing camel") unless $camel;