]> git.treefish.org Git - fex.git/commitdiff
Original release 20160104 20160104
authorfextracker <fextracker@treefish.org>
Tue, 5 Jan 2016 03:00:03 +0000 (04:00 +0100)
committerfextracker <fextracker@treefish.org>
Tue, 5 Jan 2016 03:00:03 +0000 (04:00 +0100)
2015-12-29: fexsend: added search pattern argument to option -l
2015-12-25: fup: added +/- download flag in LIST command output
2015-12-18: fup: allow AUTODELETE and KEEP parameter for anonymous user and
2015-12-18: sender=recipient
2015-12-01: hint for fexget and fexit in notification email
2015-12-01: fixed bug notification email ignores (sometimes) locale
2015-11-27: fixed bug first line in encrypted notification emails gets lost
2015-11-21: fixed bad UTF8 encoding for french spanish czech galician
2015-11-10: fexget: fixed bug timeout for big files on slow storage
2015-10-14: install: fixed bug missing fex group
2015-10-09: fexsend: better support for reverse proxy (closing connection)
2015-10-06: fexsend: abort if file has been modified while uploading
2015-10-04: fup: when forwarding a file, keep time is calculated for today,
2015-10-04: not for upload day
2015-09-29: fexget: fixed bug no https download
2015-09-26: fex_cleanup: fixed bug send no locale reactivation.txt
2015-09-21: fexget: fixed bug resume download on aborted storage test file
2015-09-21: leads to corrupted file
2015-09-17: changed LIST output formating (more consistent)
2015-09-17: fexget: fixed bug cannot forward a file that was received from myself
2015-09-16: fexsend: more robust fileID (md5sum of metadata)
2015-09-14: fup: show autodelete=no if sender == recipient
2015-09-11: moved comment to top in notification email
2015-09-08: fup,fuc: fixed bug link to F*EX clients tools.html broken (loop)
2015-09-06: fexsend: added ditto-zip for MacOSX
2015-09-02: fexsend: added MacOSX support
2015-09-01: fac: added option -Rl for local users
2015-09-01: fup: added local users restriction option

53 files changed:
bin/fac
bin/fbm
bin/fex_cleanup
bin/fexget
bin/fexsend
bin/fexsrv
bin/fpg [new file with mode: 0755]
bin/l
bin/logwatch
bin/sexsend
cgi-bin/foc
cgi-bin/fop
cgi-bin/fuc
cgi-bin/fup
cgi-bin/fur
cgi-bin/pup
cgi-bin/rup
doc/Changes
doc/SSL
doc/new
doc/newfeatures
doc/version
htdocs/FAQ.html [changed from file to symlink]
htdocs/FAQ/admin.faq
htdocs/FAQ/meta.faq
htdocs/FAQ/misc.faq
htdocs/FAQ/user.faq
htdocs/download/fexget
htdocs/download/fexsend
htdocs/download/sexsend
htdocs/fexit.html [new file with mode: 0644]
htdocs/fexitinstaller [new file with mode: 0644]
htdocs/macfexsend.html [new file with mode: 0644]
htdocs/tools.html
htdocs/version
install
lib/dop
lib/fex.ph
lib/fex.pp
lib/fup.pl
locale/czech/lib/fup.pl
locale/french/htdocs/FAQ/admin.html
locale/french/htdocs/FAQ/all.html
locale/french/htdocs/FAQ/faq.pl
locale/french/htdocs/FAQ/local.html
locale/french/htdocs/FAQ/meta.faq
locale/french/htdocs/FAQ/meta.html
locale/french/htdocs/FAQ/misc.html
locale/french/htdocs/FAQ/user.html
locale/french/lib/fup.pl
locale/galician/lib/fup.pl
locale/italian/lib/fup.pl
locale/translations

diff --git a/bin/fac b/bin/fac
index 1458ef8ce64afc14b6d4bf46a77e3af2fd22116d..c2d59f43b47dadb88b55f592e5d276f6800951ac 100755 (executable)
--- a/bin/fac
+++ b/bin/fac
@@ -57,10 +57,10 @@ die "$0: \$admin not configured in $FEXLIB/fex.ph\n" unless $admin;
 $EDITOR = $ENV{EDITOR} || $ENV{VISUAL} ||
           (-x '/usr/bin/editor' ? '/usr/bin/editor' : 'vi');
 
-$opt_c = $opt_v = $opt_l = $opt_L = $opt_h = $opt_w = $opt_u = $opt_R = 0;
+$opt_c = $opt_v = $opt_l = $opt_L = $opt_h = $opt_w = $opt_u = 0;
 $opt_M = $opt_E = 0;
 $opt_r = $opt_d = $opt_q = $opt_a = $opt_n = $opt_k = $opt_m = '';
-$opt_y = $opt_S = $opt_C = $opt_D = $opt_A = $opt_V = $opt_P = '';
+$opt_y = $opt_S = $opt_C = $opt_D = $opt_A = $opt_V = $opt_P =  $opt_R = '';
 ${'opt_/'} = '';
 
 @__ = @ARGV;
@@ -80,7 +80,7 @@ if (abs_path($spooldir) ne abs_path("$FEXHOME/spool")) {
   warn "WARNING: \$spooldir differs from $FEXHOME/spool !\n";
 }
 
-getopts('hcvlLwuMRE/q:r:d:a:n:k:m:y:S:C:A:V:D:P:') or usage(2);
+getopts('hcvlLwuME/q:r:d:a:n:k:m:y:S:C:A:V:D:P:R:') or usage(2);
 usage(0)   if $opt_h;
 examples() if $opt_E;
 
@@ -260,17 +260,32 @@ if ($opt_d) {
 
 # set user restriction file
 if ($opt_R) {
-  $user = shift or die "usage: $0 -R user\n";
-  $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
-  die "$0: no user $user\n" unless -d "$spooldir/$user";
-  unless (@local_rdomains) {
-    die "$0: no \@local_rdomains in server config\n";
-  }
-  my $rf = "$spooldir/$user/\@ALLOWED_RECIPIENTS";
-  open $rf,'>',$rf or die "$0: cannot open $rf - $!";
-  print {$rf} "\@LOCAL_RDOMAINS\n";
-  close $rf;
-  print "$user restricted\n";
+  if ($opt_R eq 'i') {
+    $user = shift or die "usage: $0 -Ri user\n";
+    $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+    die "$0: no user $user\n" unless -d "$spooldir/$user";
+    unless (@local_rdomains) {
+      die "$0: no \@local_rdomains in server config\n";
+    }
+    my $rf = "$spooldir/$user/\@ALLOWED_RECIPIENTS";
+    open $rf,'>',$rf or die "$0: cannot open $rf - $!";
+    print {$rf} "\@LOCAL_RDOMAINS\n";
+    close $rf;
+    print "$user restricted to internal recipients\n";
+    exit;
+  } elsif ($opt_R eq 'l') {
+    $user = shift or die "usage: $0 -Rl user\n";
+    $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
+    die "$0: no user $user\n" unless -d "$spooldir/$user";
+    my $rf = "$spooldir/$user/\@ALLOWED_RECIPIENTS";
+    open $rf,'>',$rf or die "$0: cannot open $rf - $!";
+    print {$rf} "\@LOCAL_USERS\n";
+    close $rf;
+    print "$user restricted to local recipients\n";
+    exit;
+  } else {
+    usage(2);
+  }
   exit;
 }
 
@@ -862,7 +877,8 @@ $0 -u user auth-ID    # create new user or set new auth-ID
 $0 -/ admin auth-ID   # set new admin and auth-ID
 $0 -q user s:quota    # set new disk quota (MB) for sender user
 $0 -q user r:quota    # set new disk quota (MB) for recipient user
-$0 -R user            # restrict user: only internal recipients allowed
+$0 -Ri user           # restrict user: only internal domain recipients allowed
+$0 -Rl user           # restrict user: only local users as recipients allowed
 $0 -rr user           # edit user recipients restriction
 $0 -ru user           # edit user upload restriction
 $0 -rd user           # edit user download restriction
diff --git a/bin/fbm b/bin/fbm
index f1d71d032e359f1376040951c5ede1ffa16bfbb9..96d2a2253f446e5f70c76de1e360a98b15d75237 100755 (executable)
--- a/bin/fbm
+++ b/bin/fbm
@@ -20,7 +20,7 @@ use constant M => 2**20;
 
 our ($SH,$windoof,$sigpipe,$useragent);
 our ($FEXSERVER);
-our $version = 20150826;
+our $version = 20160104;
 
 # server defaults
 my $server = 'fex.rus.uni-stuttgart.de';
index 1adec4e4fd6f0f269e40976b808e4e0ef64bf0fb..2dabc00b24685e48eea69c711b5252a3cd125702 100755 (executable)
@@ -71,60 +71,60 @@ chdir $spooldir or die "$0: $spooldir - $!\n";
 # clean up regular spool
 opendir $spooldir,'.' or die "$0: $spooldir - $!\n";
 while ($to = readdir $spooldir) {
+  next if $to =~ /^\./;
   next if $to !~ /@/ or $_ = readlink($to) and not /\//;
+  next unless -d $to;
   if (@demo and -f "$to/.demo" and time > lmtime("$to/.demo")+$demo[1]*DS) {
     logdel($to,"demo user $to deleted");
     next;
   }
-  if (-d $to and $to !~ /^\./) {
-    unless (opendir TO,$to) {
-      warn "$0: $spooldir/$to - $!\n";
-      next;
-    }
-    while ($from = readdir TO) {
-      next if $from !~ /@/;
-      if ($from eq '@GROUP') {
-        foreach $group (glob "$to/$from/*") {
-          if (readlink $group and not -f $group) {
-            logdel($group,"$group deleted (master has gone)");
-          }
+  unless (opendir TO,$to) {
+    warn "$0: $spooldir/$to - $!\n";
+    next;
+  }
+  while ($from = readdir TO) {
+    next if $from !~ /@/;
+    if ($from eq '@GROUP') {
+      foreach $group (glob "$to/$from/*") {
+        if (readlink $group and not -f $group) {
+          logdel($group,"$group deleted (master has gone)");
         }
-      } else {
-        if (-d "$to/$from" and $from !~ /^\./) {
-          unless (opendir FROM,"$to/$from") {
-            warn "$0: $spooldir/$to/$from - $!\n";
-            next;
-          }
-          while ($file = readdir FROM) {
-            next if $file eq '.' or $file eq '..';
-            if (-d "$to/$from/$file" and $file !~ /^\./) {
-              cleanup($to,$from,$file);
-              rmdir "$to/$from/$file" unless $opt_d;
-            }
+      }
+    } else {
+      if (-d "$to/$from" and $from !~ /^\./) {
+        unless (opendir FROM,"$to/$from") {
+          warn "$0: $spooldir/$to/$from - $!\n";
+          next;
+        }
+        while ($file = readdir FROM) {
+          next if $file eq '.' or $file eq '..';
+          if (-d "$to/$from/$file" and $file !~ /^\./) {
+            cleanup($to,$from,$file);
+            rmdir "$to/$from/$file" unless $opt_d;
           }
-          closedir FROM;
-          rmdir "$to/$from" unless $opt_d;
         }
+        closedir FROM;
+        rmdir "$to/$from" unless $opt_d;
       }
     }
-    closedir TO;
-    unless (-f "$to/\@PERSISTENT" or $to eq $admin) {
-      @glob = glob "$to/*/* $to/\@MAINUSER/* $to/\@GROUP/*";
-      unless (@glob or -f "$to/\@") {
-        logdel($to,"$to deleted");
-      }
-      $user = $to;
-      if ($login_check and -l "$user/.login") {
-        my $lc = &$login_check(readlink("$user/.login"));
-        if ($lc) {
-          if (-f "$user/\@~" and not "$user/@") {
-            rename "$user/\@~","$user/@" unless $opt_d;
-            logv("$user reanimated (login_check)");
-          }
-        } else {
-          rename "$user/@","$user/\@~" unless $opt_d;
-          logv("$user deactivated (login_check)");
+  }
+  closedir TO;
+  unless (-f "$to/\@PERSISTENT" or $to eq $admin) {
+    @glob = glob "$to/*/* $to/\@MAINUSER/* $to/\@GROUP/*";
+    unless (@glob or -f "$to/\@") {
+      logdel($to,"$to deleted");
+    }
+    $user = $to;
+    if ($login_check and -l "$user/.login") {
+      my $lc = &$login_check(readlink("$user/.login"));
+      if ($lc) {
+        if (-f "$user/\@~" and not "$user/@") {
+          rename "$user/\@~","$user/@" unless $opt_d;
+          logv("$user reanimated (login_check)");
         }
+      } else {
+        rename "$user/@","$user/\@~" unless $opt_d;
+        logv("$user deactivated (login_check)");
       }
     }
   }
@@ -350,7 +350,7 @@ if ($account_expire and $account_expire =~ /^(\d+)/) {
 
       if (time > lmtime($user)+$expire*DS) {
         # print "$spooldir/$user\n";
-        my $locale = readlink "$user/\@LOCALE";
+        local $locale = readlink "$user/\@LOCALE";
         $locale = 'english' unless $locale and $reactivation{$locale};
         &{$reactivation{$locale}}($expire,$user);
         sleep 1;
index 8e001196d8cc13b1f0d4fc24c16b0f1c01268625..c375cea05b269d3ddc4ea08472185e560c11f853 100755 (executable)
@@ -30,9 +30,10 @@ our $SH;
 our ($fexhome,$idf,$tmpdir,$windoof,$useragent);
 our ($xv,%autoview);
 our $bs = 2**16; # blocksize for tcp-reading and writing file
-our $version = 20150826;
+our $version = 20160104;
 our $CTYPE = 'ISO-8859-1';
 our $fexsend = $ENV{FEXSEND} || 'fexsend';
+our $DEBUG = $ENV{DEBUG};
 
 my %SSL = (SSL_version => 'TLSv1');
 my $sigpipe;
@@ -57,6 +58,14 @@ if ($Config{osname} =~ /^mswin/i) {
   $SSL{SSL_verify_mode} = 0;
   chdir $ENV{USERPROFILE}.'\Desktop';
   # open XX,'>XXXXXX';close XX;
+} elsif ($Config{osname} =~ /^darwin/i or $ENV{MACOS}) {
+  $0 =~ s:(.*)/:: and $ENV{PATH} .= ":$1";
+  $fexhome = $ENV{FEXHOME} || $ENV{HOME}.'/.fex';
+  $tmpdir = $ENV{FEXTMP} || $ENV{TMPDIR} || "$fexhome/tmp";
+  $idf = "$fexhome/id";
+  $_ = `sw_vers -productVersion 2>/dev/null`||'';
+  chomp;
+  $useragent = "fexget-$version (MacOS $_)";
 } else {
   $0 =~ s:(.*)/:: and $ENV{PATH} .= ":$1";
   $fexhome = $ENV{FEXHOME} || $ENV{HOME}.'/.fex';
@@ -233,7 +242,7 @@ if ($opt_a) {
   }
 }
 
-my ($file,%files,$download,$server,$port,$fop);
+my ($file,%files,$download,$server,$port,$fop,$https);
 
 if ($opt_f) {
   unless ($ENV{FEXID} or -f $ENV{HOME}.'/.fex/id') {
@@ -271,6 +280,7 @@ URL: foreach my $url (@ARGV) {
   }
 
   if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/.*fop/\S+)}) {
+    $https  = $1;
     $server = $2;
     $port   = $4 || ($1?443:80);
     $fop    = $5;
@@ -358,6 +368,7 @@ URL: foreach my $url (@ARGV) {
 
     if ($ENV{DISPLAY} and $download =~ /\.(gif|jpg|png|tiff?)$/i) {
       # see also mimeopen and xdg-mime
+      # http://unix.stackexchange.com/questions/144047/how-does-xdg-open-do-its-work
       if (my $xv = $xv || pathsearch('xv') || pathsearch('xdg-open')) {
         printf "run \"%s %s\" [Yn] ? ",basename($xv),basename($download);
         $_ = <STDIN>||'';
@@ -367,10 +378,10 @@ URL: foreach my $url (@ARGV) {
     }
 
     if ($download =~ /$atype/) {
-      if    ($download =~ /\.(tgz|tar.gz)$/)  { extract('tar tvzf','tar xvzf') }
-      elsif ($download =~ /\.tar$/)           { extract('tar tvf','tar xvf') }
-      elsif ($download =~ /\.zip$/i)          { extract('unzip -l','unzip') }
-      elsif ($download =~ /\.7z$/i)           { extract('7z l','7z x') }
+      if    ($download =~ /\.(tgz|tar.gz)$/) { extract('tar tvzf','tar xvzf') }
+      elsif ($download =~ /\.tar$/)          { extract('tar tvf','tar xvf') }
+      elsif ($download =~ /\.zip$/i)         { extract('unzip -l','unzip') }
+      elsif ($download =~ /\.7z$/i)          { extract('7z l','7z x') }
       else { die "$0: unknown archive \"$download\"\n" }
       if ($? == 0) {
         unlink $download;
@@ -388,7 +399,7 @@ sub extract {
   my $l = shift;
   my $x = shift;
   my $d = $download;
-  my $xd = '.';
+  my $xd = '';
   local $_;
 
   if (-t and not $windoof) {
@@ -396,9 +407,10 @@ sub extract {
     system(split(' ',$l),$download);
     $d =~ s:.*/:./:;
     $d =~ s/\.[^.]+$//;
+    $d =~ s:/*$:/:;
     for (;;) {
       $xd = inquire("extract to directory (Ctrl-C to keep archive): ",$d);
-      last if $xd =~ s:^(\./*)*!?$:./:;
+      last if $xd =~ s:^(\./*)*!?$::;
       if ($xd eq '-') {
         print "keeping $download\n";
         exit;
@@ -420,8 +432,9 @@ sub extract {
       last;
     }
   }
-  print "extracting to $xd :\n";
+  print "extracting to $xd :\n" if $xd;
   system(split(' ',$x),$download);
+  print "extracted to $xd\n" if $xd;
 }
 
 sub del {
@@ -453,7 +466,7 @@ sub del {
 sub forward {
   my $url = shift;
   my ($server,$port);
-  my ($uri,$dkey,$list,$cmd,$n);
+  my ($uri,$dkey,$list,$cmd,$n,$copy);
   my @r;
 
   if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/fop/.+)}) {
@@ -474,7 +487,13 @@ sub forward {
   die "$0: no reply from fex server $server\n" unless $_;
   warn "<-- $_" if $opt_v;
 
-  unless (/^HTTP.*200/) {
+  if (/^HTTP.*already exists/) {
+    if ($uri =~ m:/fop/(\w+)/:) {
+      $dkey = $1;
+    }
+  } elsif (/^HTTP.*200/) {
+    # ok!
+  } else {
     s/^HTTP.... \d+ //;
     die "$0: $_";
   }
@@ -486,8 +505,7 @@ sub forward {
     warn "<-- $_" if $opt_v;
   }
 
-  $cmd = 'fexsend -l >/dev/null 2>&1';
-  print "$cmd\n" if $opt_v;
+  print "fexsend -l\n" if $opt_v;
   system 'fexsend -l >/dev/null 2>&1';
   $list = $ENV{HOME}.'/.fex/tmp/fexlist';
   open $list,$list or die "$0: cannot open $list - $!\n";
@@ -563,14 +581,18 @@ sub download {
       $pipe = $download = $opt_s;
     } elsif (-p $opt_s or -c $opt_s) {
       $download = $opt_s;
+      $nocheck = 'pipe or character device';
     } else {
       $download = $file.'.tmp';
       $seek = -s $download || 0;
     }
   } else {
     # ask server for real file name
-    serverconnect($server, $port);
-    sendheader("$server:$port","HEAD $proxy_prefix$fop HTTP/1.1","User-Agent: $useragent");
+    sendheader(
+      "$server:$port",
+      "HEAD $proxy_prefix$fop HTTP/1.1",
+      "User-Agent: $useragent"
+    );
     my $reply = $_ = <$SH>;
     unless (defined $_ and /\w/) {
       die "$0: no response from server\n";
@@ -644,28 +666,34 @@ sub download {
       }
     }
     if ($checkstorage and not $nocheck) {
-      $t0 = time;
+      my $t0 = my $t1 = my $t2 = time;
       my $n = 0;
+      my $buf = '.' x M;
+      my $storagetest = $file.'.test';
+      my $error = "$0: cannot write \"$storagetest\"";
+      open $storagetest,'>',$storagetest or die "$error - $!\n";
       print STDERR "checking storage...\r";
-      $buf = '.' x M;
-      while (-s $download < $checkstorage) {
-        syswrite X,$buf or do {
-          unlink $download;
-          die "\n$0: cannot write $download - $!\n";
+      while (-s $storagetest < $checkstorage) {
+        syswrite $storagetest,$buf or do {
+          unlink $storagetest;
+          die "\n$error - $!\n";
         };
         $n++;
-        print STDERR "checking storage... ".$n." MB\r";
+        $t2 = int(time);
+        if ($t2 > $t1) {
+          print STDERR "checking storage... ".$n." MB\r";
+          $t1 = $t2;
+        }
       }
-      close X or do {
-        unlink $download;
-        die "\n$0: cannot write $download - $!\n";
+      close $storagetest or do {
+        unlink $storagetest;
+        die "\n$error - $!\n";
       };
       print STDERR "checking storage... ".$n." MB ok!\n";
-      unlink $download;
-      if (time-$t0 < 25) {
-        open X,'>',$download or die "$0: cannot write to \"$download\" - $!\n";
-      } else {
+      unlink $storagetest;
+      if (time-$t0 > 25) {
         # retry after timeout
+        serverconnect($server,$port);
         return(download($server,$port,$fop,'nocheck'))
       }
     }
@@ -806,13 +834,6 @@ sub pathsearch {
 }
 
 
-sub quote {
-  local $_ = shift;
-  s/([^\w¡-ÿ_%\/=~:.,-])/\\$1/g;
-  return $_;
-}
-
-
 {
   my $tty;
 
@@ -830,10 +851,11 @@ sub quote {
 
       if (defined(&TIOCSTI) and $tty and open($tty,'>',$tty)) {
         print $prompt;
+        # push default answer into keyboard buffer
         foreach my $a (split("",$default)) { ioctl($tty,&TIOCSTI,$a) }
         chomp($_ = <STDIN>||'');
       } else {
-        $prompt =~ s/([\?:=]\s*)/ [$default]$1/ or $prompt .= " [$default]";
+        $prompt =~ s/([\?:=]\s*)/ [$default]$1/ or $prompt .= " [$default] ";
         print $prompt;
         chomp($_ = <STDIN>||'');
         $_ = $default unless length;
@@ -915,15 +937,9 @@ sub serverconnect {
   my $connect = "CONNECT $server:$port HTTP/1.1";
   local $_;
 
-  if ($opt_v and $port == 443 and %SSL) {
-    foreach my $v (keys %SSL) {
-      printf "%s => %s\n",$v,$SSL{$v};
-    }
-  }
-
   if ($proxy) {
     tcpconnect(split(':',$proxy));
-    if ($port == 443) {
+    if ($https) {
       printf "--> %s\n",$connect if $opt_v;
       nvtsend($connect,"");
       $_ = <$SH>;
@@ -932,14 +948,13 @@ sub serverconnect {
       unless (/^HTTP.1.. 200/) {
         die "$0: proxy error : $_";
       }
-      eval "use IO::Socket::SSL";
-      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      &enable_ssl;
       $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
     }
   } else {
     tcpconnect($server,$port);
   }
-#  if ($port == 443 and $opt_v) {
+#  if ($https and $opt_v) {
 #    printf "%s\n",$SH->get_cipher();
 #  }
 }
@@ -954,10 +969,9 @@ sub tcpconnect {
     undef $SH;
   }
 
-  if ($port == 443) {
+  if ($https) {
     # eval "use IO::Socket::SSL qw(debug3)";
-    eval "use IO::Socket::SSL";
-    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    &enable_ssl;
     $SH = IO::Socket::SSL->new(
       PeerAddr => $server,
       PeerPort => $port,
@@ -974,6 +988,7 @@ sub tcpconnect {
 
   if ($SH) {
     autoflush $SH 1;
+    binmode $SH;
   } else {
     die "$0: cannot connect $server:$port - $@\n";
   }
@@ -982,6 +997,18 @@ sub tcpconnect {
 }
 
 
+sub enable_ssl {
+  eval "use IO::Socket::SSL";
+  die "$0: cannot load IO::Socket::SSL\n" if $@;
+  eval '$SSL{SSL_verify_mode} = 0 if Net::SSLeay::SSLeay() <= 9470143';
+  if ($opt_v) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
+}
+
+
 sub sendheader {
   my $sp = shift;
   my @head = @_;
@@ -1018,6 +1045,18 @@ sub nvtsend {
 }
 
 
+sub quote {
+  local $_ = shift;
+  s/([^\w\@\/%^,.=+_:+-])/\\$1/g;
+  return $_;
+}
+
+
+sub debug {
+  print "## DEBUG: @_\n" if $DEBUG;
+}
+
+
 # from MIME::Base64::Perl
 sub encode_b64 {
   my $res = "";
index e746b669a958495748effb2b305c448a94ec211b..7e498dce61f81160a31b8cd70732665eeb7dc1d1 100755 (executable)
@@ -31,15 +31,15 @@ eval 'use Net::INET6Glue::INET_is_INET6';
 
 $| = 1;
 
-our ($SH,$fexhome,$idf,$tmpdir,$windoof,$useragent,$editor,$nomail);
+our ($SH,$fexhome,$idf,$tmpdir,$windoof,$macos,$useragent,$editor,$nomail);
 our ($anonymous,$public);
 our ($tpid,$frecipient);
 our ($FEXID,$FEXXX,$HOME);
 our (%alias);
 our $chunksize = 0;
-our $version = 20150826;
+our $version = 20160104;
 our $_0 = $0;
-our $DEBUG;
+our $DEBUG = $ENV{DEBUG};
 
 my %SSL = (SSL_version => 'TLSv1');
 my $sigpipe;
@@ -54,18 +54,31 @@ if ($Config{osname} =~ /^mswin/i) {
   $useragent = sprintf("fexsend-$version (%s %s)",
                        $Config{osname},$Config{archname});
   $SSL{SSL_verify_mode} = 0;
+} elsif ($Config{osname} =~ /^darwin/i or $ENV{MACOS}) {
+  $macos = $Config{osname};
+  # http://stackoverflow.com/questions/989349/running-a-command-in-a-new-mac-os-x-terminal-window
+  $HOME = (getpwuid($<))[7]||$ENV{HOME};
+  $fexhome = $HOME.'/.fex';
+  $tmpdir = $ENV{FEXTMP} || $ENV{TMPDIR} || "$fexhome/tmp";
+  $tmpdir =~ s:/$::;
+  $idf = "$fexhome/id";
+  chmod 0600,$idf;
+  $editor = $ENV{EDITOR} || 'open -W -n -e';
+  $_ = `sw_vers -productVersion 2>/dev/null`||'';
+  chomp;
+  $useragent = "fexsend-$version (MacOS $_)";
 } else {
   $0 =~ s:.*/::;
   $HOME = (getpwuid($<))[7]||$ENV{HOME};
   $fexhome = $HOME.'/.fex';
   $tmpdir = $ENV{FEXTMP} || "$fexhome/tmp";
   $idf = "$fexhome/id";
+  chmod 0600,$idf;
   $editor = $ENV{EDITOR} || 'vi';
   $_ = `(lsb_release -d||uname -a)2>/dev/null`||'';
   chomp;
   s/^Description:\s+//;
   $useragent = "fexsend-$version ($_)";
-  chmod 0600,$idf;
 }
 
 if (-f ($_ = '/etc/fex/config.pl')) {
@@ -88,7 +101,7 @@ my $features = '';
 my $timeout = 30;      # server timeout
 my $fexlist = "$tmpdir/fexlist";
 my ($usage,$hints);
-my $xx = $0 =~ /^xx/;
+my $xx = $0 =~ /\bxx$/;
 
 if ($xx) {
   $usage = "usage: send file(s):               xx [:slot] file...\n".
@@ -104,6 +117,7 @@ if ($xx) {
   $usage = <<EOD;
 usage: $0 [options] file(s) [@] recipient(s)
    or: $0 [special options]
+   or: $0 -l [recipient-regexp]
    or: $0 -f \# recipient(s)
    or: $0 -x \# [-C -k -D -K -S]
 options: -v           verbose mode
@@ -111,7 +125,7 @@ options: -v           verbose mode
          -c           compress file with gzip
          -g           encrypt file with gpg
          -m limit     limit throughput (kB/s)
-         -i tag       use ID data [tag] from ID file
+         -i account   use ID data [account] from ID file
          -C comment   add comment to notification e-mail
          -k max       keep file max days on fex server
          -D           delay auto-delete after download
@@ -120,19 +134,19 @@ options: -v           verbose mode
          -o           overwrite mode, do not resume
          -a archive   put files in archive (.zip .7z .tar .tgz)
          -s stream    read data from pipe and upload it with stream name
-special options: -I      initialize ID file or show ID
-                 -I tag  add alternate ID data (secondary logins) to ID file
-                 -l      list sent files numbered (# needed for -f -x -d -N)
-                 -f \#    forward already uploaded file to another recipient
-                 -x \#    modify options -C -k -D -K for already uploaded file
-                 -d \#    delete file on fex server
-                 -N \#    resend notification e-mail
-                 -Q      check quotas
-                 -A      edit server address book (aliases)
-                 -S      show server/user settings and auth-ID
-                 -H      show hints, examples and more options
-                 -V      show version
-                 (\# is a file number, see output from $0 -l)
+special options: -I          initialize ID file or show ID
+                 -I account  add alternate ID data (secondary logins) to ID file
+                 -l          list sent files numbers (# needed for -f -x -d -N)
+                 -f \#        forward already uploaded file to another recipient
+                 -x \#        use -C -k -D -K for already uploaded file
+                 -d \#        delete file on fex server
+                 -N \#        resend notification e-mail
+                 -Q          check quotas
+                 -A          edit server address book (aliases)
+                 -S          show server/user settings and auth-ID
+                 -H          show hints, examples and more options
+                 -V          show version
+                 (# is a file number, see output from $0 -l)
 examples: $0 visualization.mpg framstag\@rus.uni-stuttgart.de
           $0 -a images.zip *.jpg webmaster\@flupp.org,metoo
           lshw | $0 -s hardware.list admin\@flupp.org
@@ -217,7 +231,9 @@ whereas archive types zip and 7z need a temporary archive file on local disk.
 With option -s you can send any data coming from a pipe (STDIN) as a file
 without wasting local disc space.
 
-With option -X you can specify any parameter, e.g.: -X autodelete=yes
+With option -X you can specify any URL parameter, e.g.: 
+fexsend -X autodelete=yes ...
+fexsend -X 'autodelete=no&locale=german' ...
 
 For HTTPS you can set the environment variables:
 SSLVERIFY=1                 # activate server identity verification
@@ -305,6 +321,9 @@ if ($xx) {
   $_ = "$fexhome/config.pl"; require if -f;
   getopts('hvIm:') or die $usage;
 } else {
+  if ($macos and not @ARGV) {
+    &ask_file;
+  }
   $opt_h = $opt_v = $opt_m = $opt_c = $opt_k = $opt_d = $opt_l = $opt_I = 0;
   $opt_H = $opt_K = $opt_D = $opt_R = $opt_M = $opt_L = $opt_Q = $opt_A = 0;
   $opt_x = $opt_o = $opt_g = $opt_V = $opt_U = $opt_F = $opt_n = $opt_q = 0;
@@ -571,7 +590,7 @@ elsif ($opt_z or $opt_Z or ${'opt_!'})      { &get_log }
 elsif ($opt_A)                         { edit_address_book($from) }
 elsif (${'opt_@'})                     { &show_address_book }
 elsif ($opt_d and $anonymous)          { &purge }
-elsif ($opt_d and $ARGV[-1] =~ /^\d+$/)        { &delete }
+elsif ($opt_d and $ARGV[-1] =~ /^\d+$/)        { &delete_file_number }
 else                                   { &send_fex }
 
 exit;
@@ -697,6 +716,7 @@ sub show_id {
   my ($fexcgi,$from,$id);
   if (open $idf,$idf) {
     $fexcgi = <$idf>;
+    # $fexcgi = <$idf> if $fexcgi =~ /^\[.+\]/;
     $from   = <$idf>;
     $id     = <$idf>;
     while (<$idf>) {
@@ -745,6 +765,7 @@ sub register {
   sendheader("$fs:$port","GET $proxy_prefix/fur?user=$mail&verify=no HTTP/1.1");
   http_response();
 
+  # header
   while (<$SH>) {
     s/\r//;
     printf "<-- $_"if $opt_v;
@@ -779,10 +800,365 @@ sub register {
 }
 
 
+# menu for MacOS users
+sub menu {
+  my $key;
+  my $new;
+  local $_;
+  
+  system 'clear';
+  print "\n";
+  print "fexsend-$version\n";
+
+  for (;;) {
+    if (open $idf,$idf) {
+      $fexcgi = getline($idf) and
+      $from   = getline($idf) and
+      $id     = getline($idf);
+      close $idf;
+      last if $id;
+    }
+    &set_ID;
+  }
+
+  print "\n";
+  print "$from on $fexcgi\n";
+  print "\n";
+  
+  for (;;) {
+    print "\n";
+    print "[s]  send a file or directory\n";
+    print "[u]  update fexsend\n";
+    print "[l]  change login data (user, server, auth-ID)\n";
+    print "[h]  help\n";
+    print "[q]  quit\n";
+    print "\n";
+    print "your choice: ";
+    $key = ReadKey(0);
+    if ($key eq 'q') { 
+      print "$key\n";
+      print "\n";
+      print "Type [Cmd]W to close this window.\n";
+      exit;
+    }
+    if ($key eq 'h') { 
+      print "$key\n";
+      print 
+        "\n".
+        "With fexsend you can send files of any size to any e-mail address.\n".
+        "\n".
+        "At the recipient or file prompt [RETURN] brings you to this option menu.\n".
+        "\n".
+        "To send more than one file:\n".
+        "When you enter * at the file prompt, you will be first asked for an archive name\n".
+        "and then you can drag+drop multiple files.\n".
+        "\n".
+        "Do not forget to terminate each input line with [RETURN].\n".
+        "\n".
+        "See http://fex.rus.uni-stuttgart.de/ for more informations.\n";
+      next;
+    }
+    if ($key eq 'u') { 
+      print "$key\n";
+      if ($0 =~ m:(^/client/|/sw/):) {
+        print "\n";
+        print "use swupdate to update fexsend!\n";
+        next;
+      }
+      $new = $0.'.new';
+      system "curl http://fex.belwue.de/download/fexsend>".quote($new);
+      chmod 0755,$new;
+      system qw'perl -c',$new;
+      if ($? == 0) {
+        rename $new,$0;
+        exec $0;
+      } else {
+        print "\n";
+        print "cannot install new fexsend\n";
+      }
+      next;
+    }
+    if ($key eq 'l') { 
+      print "$key\n";
+      system 'clear';
+      &set_ID;
+      next;
+    }
+    if ($key eq 's' or $key eq "\n") { 
+      print "s\n";
+      &ask_file;
+      next;
+    }
+  }
+  exit;
+}
+
+
+# for MacOS
+sub ask_file {
+  my ($file,$comment,$recipient,$archive,$size,$cmd,$key);
+  my @files;
+  my $qfiles;
+  local $_;
+  
+  system 'clear';
+  
+  &set_ID unless -s $idf;
+
+  print "\n";
+  print "Enter [RETURN] after each input line.\n";
+  print "\n";
+
+  for (;;) {
+    print "Recipient(s): ";
+    $recipient = <STDIN>;
+    chomp $recipient;
+    $recipient =~ s/^\s+//;
+    $recipient =~ s/\s+$//;
+    $recipient =~ s/[\s;,]+/,/g;
+    &menu unless $recipient;
+    last if $recipient =~ /\w/ or $recipient eq '.';
+  }
+
+  for (;;) {
+    print "\n";
+    print "Drag a file into this window or hit [RETURN] ";
+    print $archive ? "to continue.\n" : "for menu options.\n";
+    print "File to send: ";
+    $file = <STDIN>||'';
+    chomp $file;
+    $file =~ s/^\s+//;
+    $file =~ s/ $// if $file !~ /\\ $/;
+    &menu unless $file or $archive;
+    if ($file eq '*') {
+      print "Archive name: ";
+      $archive = <STDIN>||'';
+      chomp $archive;
+      next unless $archive;
+      $archive =~ s/^\s+//g;
+      $archive =~ s/\s+$//g;
+      $archive =~ s/[^\w=.+-]/_/g;
+      next;
+    }
+    if ($file) {
+      unless (-e $file) {
+        $file =~ s/\\\\/\000/g;
+        $file =~ s/\\//g;
+        $file =~ s/\000/\\/g;
+      }
+      unless (-r $file) {
+        print "\"$file\" is not readable\n";
+        next;
+      }
+      my $qf = quote($file);
+      if (`du -ms $qf` =~ /^(\d+)/) {
+        $size += $1;
+        printf "%d MB\n",$1;
+      }
+      if ($archive) {
+        push @files,$file;
+        next;
+      }
+    }
+    if ($archive) {
+      next unless @files;
+      $qfiles = join(' ',map(quote($_),@files));
+      if ($size < 2048) {
+        $archive .= '.zip';
+      } else {
+        $archive .= '.tar';
+      }
+    }
+    print "\n";
+    print "Comment: ";
+    $comment = <STDIN>||'';
+    chomp $comment;
+    print "\n";
+    if ($comment =~ s/^:\s*-/-/) {
+      $cmd = quote($0)." $comment ";
+      if ($archive) {
+        $cmd .= '-a '.quote($archive).' '.$qfiles;
+      } else {
+        $cmd .= quote($file);
+      }
+      $cmd .= ' '.quote($recipient);
+      print $cmd,"\n";
+      system $cmd;
+    } else {
+      print quote($0)." -C '$comment' ";
+      if ($archive) {
+        printf "-a %s %s %s\n",quote($archive),$qfiles,$recipient;
+        system $0,'-C',$comment,'-a',$archive,@files,$recipient;
+      } else {
+        printf "%s %s\n",quote($file),$recipient;
+        system $0,'-C',$comment,$file,$recipient;
+      }
+    }
+    print "\n";
+    print "[s]  send another file to $recipient\n";
+    print "[n]  send another file to another recipient\n";
+    print "[q]  quit\n";
+    print "\n";
+    print "your choice: ";
+    for (;;) {
+      $key = ReadKey(0);
+      &ask_file if $key eq 'n';
+      if ($key eq 's' or $key eq "\n") {
+        print "s\n";
+        last;
+      }
+      if ($key eq 'q') {
+        print "$key\n";
+        exit;
+      }
+    }
+    $file = $comment = $archive = '';
+    @files = ();
+  }
+}
+
+
+sub set_ID {
+  my ($server,$port,$user,$logo);
+  local $_;
+  
+  print "\n";
+  for (;;) {
+    print "F*EX server URL: ";
+    $server = <STDIN>;
+    $server =~ s/[\s\n]//g;
+    if ($server =~ s:/fup/(\w+)$::) {
+      $_ = decode_b64($1);
+      if (/(from|user)=(.+)&id=(.+)/) {
+        $user = $2;
+        $id = $3;
+      }
+    }
+    $server =~ s:/fup.*::;
+    $server =~ s:/+$::;
+    next if $server !~ /\w/;
+    if ($server =~ s/^https:..// or $server =~ /:443/) {
+      $server =~ s/:.*//;
+      $port = 443;
+      eval "use IO::Socket::SSL";
+      if ($@) {
+        print "\nno perl SSL modules installed - cannot use https\n\n";
+        next;
+      }
+      $SH = IO::Socket::SSL->new(
+        PeerAddr => $server,
+        PeerPort => $port,
+        Proto    => 'tcp',
+        %SSL
+      );
+    } else {
+      $server =~ s:^http.//::;
+      if ($server =~ s/:(\d+)//) {
+        $port = $1;
+      } else {
+        $port = 80;
+      }
+      $SH = IO::Socket::INET->new(
+        PeerAddr => $server,
+        PeerPort => $port,
+        Proto    => 'tcp',
+      );
+    }
+    unless ($SH) {
+      print "\ncannot connect to $server:$port - $!\n\n";
+      next;
+    }
+    sendheader(
+      "$server:$port",
+      "GET /logo.jpg HTTP/1.0",
+      "User-Agent: $useragent",
+      "Connection: close",
+    );
+    $_ = <$SH>||'';
+    unless (/HTTP.1.1 200/) {
+      print "\nbad server reply: $_\n";
+      next;
+    }
+    while (<$SH>) { last if /^\s*$/ }
+    local $/;
+    $logo = <$SH>||'';
+    close $SH;
+    if (length $logo < 9999) {
+      print "\n$server is not a F*EX server!\n\n";
+      next;
+    }
+    open $logo,">$tmpdir/fex.jpg";
+    print {$logo} $logo;
+    close $logo;
+    last;
+  }
+  
+  for (;;) {
+    last if $user;
+    print "Your login (e-mail address): ";
+    $user = <STDIN>;
+    $user =~ s/[\s\n]//g;
+    if ($user !~ /.@[\w.-]+$/) {
+      print "\"$user\" is not a valid e-mail address!\n";
+      next;
+    }
+  }
+  
+  for (;;) {
+    last if $id;
+    print "Your auth-ID for this account: ";
+    $id = <STDIN>;
+    $id =~ s/[\s\n]//g;
+  }
+  
+  open $idf,'>',$idf or die "$0: cannot write to $idf - $!\n";
+  print {$idf} "$server\n",
+               "$user\n",
+               "$id\n";
+  close $idf;
+  print "\n";
+  print "Login data written to $idf\n\n";
+  print "fexing test file to $user:\n\n";
+  system "$0 -o -M -C test $tmpdir/fex.jpg $user";
+  print "\n";
+  if ($? != 0) {
+    print "fexsend failed, login data is invalid, try again\n";
+    &set_ID;
+  } else {
+    print "fexsend test succeeded!\n";
+    sleep 3;
+  }
+}
+
+
+# read one key from terminal in raw mode
+sub ReadKey {
+  my $key;
+  local $SIG{INT} = sub { stty('reset'); exit };
+  
+  stty('raw');
+  # loop necessary for ESXi support
+  while (not defined $key) {
+    $key = getc(STDIN);
+  }
+  stty('reset');
+  return $key;
+}
+
+
+sub stty {
+  if (shift eq 'raw') {
+    system qw'stty -echo -icanon eol',"\001";
+  } else {
+    system qw'stty echo icanon eol',"\000";
+  }
+}
+
+
 sub send_xx {
   my $transferfile = shift;
   my $file = '';
-  my (@r,@tar);
+  my (@r,@tar,$dir);
 
   $SIG{PIPE} = $SIG{INT} = sub {
     unlink $transferfile;
@@ -800,7 +1176,6 @@ sub send_xx {
       print "making tar transfer file $transferfile :\n";
       # single file? then add this directly
       if (scalar @ARGV == 1) {
-        my ($dir,$file);
         # strip path if not ending with /
         if ($ARGV[0] =~ m:(.+)/(.+): and $2 !~ m:/$:) {
           ($dir,$file) = ($1,$2);
@@ -947,49 +1322,54 @@ sub query_settings {
 # list spool
 sub list {
   my (@r,$r);
-  my ($data,$dkey,$n);
+  my ($data,$dkey);
+  my $n = 0;
+  my $s = 1;
+  my $a = shift @ARGV || '.';
   local $_;
 
   female_mode("list spooled files?") if $opt_F;
 
-  if ($opt_l and $n = shift @ARGV and $n =~ /^\d+$/) {
-    open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
-    while (<$fexlist>) {
-      if (/^\s*(\d+)\) (\w+) (.+)/ and $1 eq $n) {
-        serverconnect($server,$port) unless $SH;
-        sendheader(
-          "$server:$port",
-          "GET $proxy_prefix/fop/$2/$2?LIST HTTP/1.1",
-          "User-Agent: $useragent",
-        );
-        $_ = <$SH>||'';
-        s/\r//;
-        print "<-- $_" if $opt_v;
-        if (/^HTTP.* 200/) {
+  if ($opt_l) {
+    if ($a =~ /^\d+$/) {
+      open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+      while (<$fexlist>) {
+        if (/^\s*(\d+)\) (\w+) (.+)/ and $1 eq $a) {
+          serverconnect($server,$port) unless $SH;
+          sendheader(
+            "$server:$port",
+            "GET $proxy_prefix/fop/$2/$2?LIST HTTP/1.1",
+            "User-Agent: $useragent",
+          );
+          $_ = <$SH>||'';
+          s/\r//;
           print "<-- $_" if $opt_v;
-          while (<$SH>) {
-            s/\r//;
-            if (/^\n/) {
-              print;
-              print while <$SH>;
+          if (/^HTTP.* 200/) {
+            print "<-- $_" if $opt_v;
+            while (<$SH>) {
+              s/\r//;
+              if (/^\n/) {
+                print;
+                print while <$SH>;
+              }
             }
+          } elsif (s:HTTP/[\d\. ]+::) {
+            die "$0: server response: $_";
+          } else {
+            die "$0: no response from fex server $server\n";
           }
-        } elsif (s:HTTP/[\d\. ]+::) {
-          die "$0: server response: $_";
-        } else {
-          die "$0: no response from fex server $server\n";
+          exit;
         }
-        exit;
       }
+      die "$0: file \#$a not found in fexlist\n";
     }
-    die "$0: file \#$n not found in fexlist\n";
-  } else {
-    @r = formdatapost(
-      from     => $from,
-      to       => $opt_l ? '*' : $from,
-      command  => $opt_C,
-    );
   }
+   
+  @r = formdatapost(
+    from       => $from,
+    to         => $opt_l ? '*' : $from,
+    command    => $opt_C,
+  );
   die "$0: no response from fex server $server\n" unless @r;
   $_ = shift @r;
   unless (/^HTTP.* 200/) {
@@ -1011,12 +1391,13 @@ sub list {
       s/&amp;/&/g;
       s/&quot;/\"/g;
       s/&lt;/</g;
-      if (/^(to .* :)/) {
-        print "\n$1\n";
-        print {$fexlist} "\n$1\n";
+      if (/^(to (.+) :)/) { 
+        $s = $2 =~ /$a/;
+        print "\n$_\n" if $s;
+        print {$fexlist} "\n$_\n";
       } elsif (m/(\d+) MB (.+)/) {
         $n++;
-        printf "%4s) %8d MB %s\n","#$n",$1,$2;
+        printf "%4s) %8d MB %s\n","#$n",$1,$2 if $s;
         printf {$fexlist} "%3d) %s %s\n",$n,$dkey,$2;
       }
     }
@@ -1092,12 +1473,12 @@ sub purge {
 }
 
 
-sub delete {
+sub delete_file_number {
   my ($to,$file);
 
   while (@ARGV) {
     $opt_d = shift @ARGV;
-    die "$usage: $0 -d #\n" if $opt_d !~ /^\d+$/;
+    die "usage: $0 -d #\n" if $opt_d !~ /^\d+$/;
 
     open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
     while (<$fexlist>) {
@@ -1139,6 +1520,37 @@ sub delete {
 }
 
 
+sub delete_file {
+  my ($from,$to,$file) = @_;
+  local $_;
+
+  unless ($SH) {
+    serverconnect($server,$port);
+    query_sid($server,$port) unless $anonymous;
+  }
+  
+  $file = urlencode($file);
+  sendheader(
+    "$server:$port",
+    "GET $proxy_prefix/fop/$to/$from/$file?id=$sid&DELETE HTTP/1.1",
+    "User-Agent: $useragent",
+  );
+  
+  while (<$SH>) {
+    s/\r//;
+    printf "<-- $_"if $opt_v;
+    last if /^\s*$/;
+  }
+}
+
+
+sub urlencode {
+  local $_ = shift;
+  s/([^_=:,;<>()+.\w\-])/'%'.uc(unpack("H2",$1))/ge;
+  return $_;
+}
+
+
 sub send_fex {
   my @to;
   my $file = '';
@@ -1194,7 +1606,7 @@ sub send_fex {
 
   if ($anonymous) {
     my $aok;
-    sendheader("$server:$port","OPTIONS FEX HTTP/1.1");
+    sendheader("$server:$port","OPTIONS /FEX HTTP/1.1");
     $_ = <$SH>||'';
     s/\r//;
     die "$0: no response from fex server $server\n" unless $_;
@@ -1287,10 +1699,21 @@ sub send_fex {
   }
 
   if (@ARGV > 1 and not ($opt_a or $opt_s or $opt_d)) {
-    print "Archive name (name.tar, name.tgz or name.zip) or [ENTER] to send file for file:\n";
+    print "Archive name (name.tar, name.tgz or name.zip) or [RETURN] to send file for file:\n";
     $opt_a = <STDIN>;
     $opt_a =~ s/^\s+//;
     $opt_a =~ s/\s+$//;
+    $opt_a =~ s/\//_/g;
+  }
+
+  if ($macos and not $opt_a and -d "@ARGV") {
+    my $dir = "@ARGV";
+    my $qdir = quote($dir);
+    if (`du -s $qdir` =~ /^(\d+)/ and $1 < 2**21) {
+      $opt_a = "$dir.zip";
+    } else {
+      $opt_a = "$dir.tar";
+    }
   }
 
   if ($opt_s) {
@@ -1317,7 +1740,7 @@ sub send_fex {
       $opt_a =~ s:.*/::g;
     }
     foreach my $file (@ARGV) {
-      die "$0: cannot read $file\n" unless -l $file or -r $file;
+      die "$0: cannot read \"$file\"\n" unless -l $file or -r $file;
     }
     $opt_a .= ".$atype" if $opt_a !~ /\.$atype$/;
     $transferfile = "$tmpdir/$opt_a";
@@ -1329,6 +1752,10 @@ sub send_fex {
         # else        { system(qw'7z a -tzip -mm=copy',$transferfile,@ARGV) }
         system(qw'7z a -tzip',$transferfile,@ARGV);
         @files = ($transferfile);
+      } elsif ($macos and scalar(@ARGV) == 1) {
+        ## ditto-zip is now handled by formdatapost()
+        system 'true';
+        @files = ($opt_a);
       } else {
         # zip archives must be < 2 GB, so split as necessary
         @files = zipsplit($transferfile,@ARGV);
@@ -1358,6 +1785,7 @@ sub send_fex {
       } else {
         ## tar is now handled by formdatapost()
         # system(qw'tar cvf',$transferfile,@ARGV);
+        system 'true';
         @files = ($opt_a);
       }
     } elsif ($atype eq 'tgz') {
@@ -1403,12 +1831,12 @@ sub send_fex {
       unless ($opt_d) {
         unless (-f $file) {
           if (-e $file) {
-            die "$0: $file is not a regular file, try option -a\n"
+            die "$0: \"$file\" is not a regular file, try option -a\n"
           } else {
-            die "$0: $file does not exist\n";
+            die "$0: \"$file\" does not exist\n";
           }
         }
-        die "$0: cannot read $file\n" unless -r $file;
+        die "$0: cannot read \"$file\"\n" unless -r $file;
       }
       push @files,$file;
     }
@@ -1418,7 +1846,7 @@ sub send_fex {
     foreach my $file (@files) {
       my @s = stat($file);
       unless (@s and ($s[2] & S_IROTH) and -r $file) {
-        die "$0: $file is not world readable\n";
+        die "$0: \"$file\" is not world readable\n";
       }
     }
   }
@@ -1426,7 +1854,7 @@ sub send_fex {
   foreach my $file (@files) {
     sleep 1;    # do not overrun server!
     unless (-s $file or $opt_d or $opt_a or $opt_s) {
-      die "$0: cannot send empty file $file\n";
+      die "$0: cannot send empty file \"$file\"\n";
     }
     female_mode("send file $file?") if $opt_F;
     @r = formdatapost(
@@ -1443,15 +1871,32 @@ sub send_fex {
     if (not @r or not grep /\w/,@r) {
       die "$0: no response from server\n";
     }
+    next if "@r" eq '0'; # already transfered
     if (($r) = grep /^ERROR:/,@r) {
       if ($anonymous and $r =~ /purge it/) {
         die "$0: file is already on server for $to - use another anonymous recipent\n";
+      } elsif ($r =~ /timeout/i) {
+        close $SH;
+        retry("timed out");
       } else {
         $r =~ s/.*?:\s*//;
         $r =~ s/<.+?>//g;
         die "$0: server error: $r\n";
       }
     }
+    unless ($opt_d) {
+      if (scalar(@r) == 1) {
+        die "$0: server error: @r\n";
+      } else {
+        if ($r[0] !~ /HTTP.1.. 2/) {
+          if ($r[0] =~ /HTTP.[\s\d.]+(.+)/) {
+            die "$0: server error: $1\n";
+          } else {
+            die "$0: server error:\n".join("\n",@r)."\n";
+          }
+        }
+      }
+    }
     if (($r) = grep /<h3>\Q$file/,@r) {
       $r =~ s/<.+?>//g;
       print "$r\n";
@@ -1459,7 +1904,8 @@ sub send_fex {
     if ($opt_a !~ /^afex_\d+\.tar$/ and $file !~ /afex_\d+\.tar$/) {
       # print grep({s/^(X-Recipient:.*\((.+)\))/Parameters: $2\n/i} @r);
       my $nonot = 0;
-      my ($recipient,$location);
+      my $recipient = '';
+      my $location = '';
       foreach (@r) {
         if (/^(X-)?(Recipient.*)/i) {
           $recipient = $2;
@@ -1468,23 +1914,13 @@ sub send_fex {
         }
         if (/^(X-)?(Location.*)/i) {
           $location = $2;
-          if ($from eq $to or $from =~ /^\Q$to\E@/i
-              or $nomail or $anonymous or $nonot) {
-            print "$recipient\n";
-            print "$location\n";
-          }
         }
       }
-      unless ($opt_d or $location) {
-        if (scalar(@r) == 1) {
-          die "$0: server error: @r\n";
-        } else {
-          if ($r[0] !~ /HTTP.1.. 2/ and $r[0] =~ /HTTP.[\s\d.]+(.+)/) {
-            die "$0: server error: $1\n";
-          } else {
-            die "$0: server error:\n".join("\n",@r)."\n";
-          }
-        }
+      if ($from eq $to or $from =~ /^\Q$to\E@/i
+          or $nomail or $anonymous or $nonot) 
+      {
+        print "$recipient\n" if $recipient;
+        print "$location\n"  if $location;
       }
     }
   }
@@ -1518,10 +1954,10 @@ sub forward {
 
   open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
   while (<$fexlist>) {
-    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_f) {
+    if (/^\s*(\d+)\) (\w+) .\s*\d+ d. ([+-] )?(.+)/ and $1 eq $opt_f) {
       $n = $1;
       $dkey = $2;
-      $file = $3;
+      $file = $4;
       if ($file =~ s/ "(.*)"$//) {
         $opt_C ||= $1 if $1 ne 'NOMAIL';
       }
@@ -1572,7 +2008,7 @@ sub renotify {
 
   open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
   while (<$fexlist>) {
-    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_N) {
+    if (/^\s*(\d+)\) (\w+) .\s*\d+ d. (.+)/ and $1 eq $opt_N) {
       $n = $1;
       $dkey = $2;
       last;
@@ -1628,7 +2064,7 @@ sub modify {
 
   open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
   while (<$fexlist>) {
-    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_x) {
+    if (/^\s*(\d+)\) (\w+) .\s*\d+ d. (.+)/ and $1 eq $opt_x) {
       $n = $1;
       $dkey = $2;
       $file = $3;
@@ -1738,7 +2174,7 @@ sub get_xx {
 
 sub formdatapost {
   my %P = @_;
-  my ($boundary,$filename,$filesize,$length,$buf,$file,$fpsize,$resume,$seek);
+  my ($boundary,$filename,$length,$buf,$file,$fpsize,$resume,$seek);
   my ($flink);
   my (@hh,@hb,@r,@pv,$to);
   my ($bytes,$t,$bt);
@@ -1746,9 +2182,11 @@ sub formdatapost {
   my $bs = 2**16;        # blocksize for reading and sending file
   my $fileid = int(time);
   my $chunk = 0;
+  my $filesize = 0;
   my $connection = '';
   my $pct = '';
-  my ($tar,$aname,$atype,$tarlist,$tarerror,$location,$transferfile);
+  my $dittodir = '.';
+  my ($tar,$ditto,$aname,$atype,$list,$error,$location,$transferfile);
   local $_;
 
   if (defined($file = $P{file})) {
@@ -1771,7 +2209,7 @@ sub formdatapost {
       $of =~ s/([^_\w\.\-])/\\$1/g;
       shelldo("gzip <$if>$of");
       $filesize = -s $transferfile;
-      die "$0: cannot gzip $file\n" unless $filesize;
+      die "$0: cannot gzip \"$file\"\n" unless $filesize;
       $file = $transferfile;
     }
 
@@ -1779,12 +2217,12 @@ sub formdatapost {
     if (not $windoof and $opt_a and $file =~ /(.+)\.(tar|tgz)$/) {
       $aname = $1;
       $atype = $2;
-      $tarlist  = "$tmpdir/$aname.list";
-      $tarerror = "$tmpdir/$aname.error";
+      $list  = "$tmpdir/$aname.list";
+      $error = "$tmpdir/$aname.error";
       $tar = 'tar -cv';
       $tar .= 'z' if $atype eq 'tgz';
       if (`tar --help 2>/dev/null` =~ /--index-file/) {
-        $tar .= " --index-file=$tarlist -f-";
+        $tar .= " --index-file=$list -f-";
       } else {
         $tar .= " -f-";
       }
@@ -1794,12 +2232,10 @@ sub formdatapost {
         }
       }
       foreach (@ARGV) {
-        $file = $_;
-        $file =~ s/([^\w\-\@\#%,.=+~_:])/\\$1/g;
-        $tar .= ' '.$file;
+        $tar .= ' '.quote($_);
       }
       # print "calculating archive size... ";
-      open $tar,"$tar 2>$tarerror|" or die "$0: cannot run tar - $!\n";
+      open $tar,"$tar 2>$error|" or die "$0: cannot run tar - $!\n";
       $t0 = int(time) if -t STDOUT;
       while ($b = read $tar,$_,$bs) {
         $filesize += $b;
@@ -1814,12 +2250,12 @@ sub formdatapost {
       printf "Archive size: %d MB\n",int($filesize/M) if -t STDOUT;
       unless (close $tar) {
         $_ = '';
-        if (open $tarerror,$tarerror) {
+        if (open $error,$error) {
           local $/;
-          $_ = <$tarerror>;
-          close $tarerror;
+          $_ = <$error>;
+          close $error;
         }
-        unlink $tarlist,$tarerror;
+        unlink $list,$error;
         die "$0: tar error:\n$_";
       }
       $file = "$aname.$atype";
@@ -1827,6 +2263,58 @@ sub formdatapost {
       undef $SH; # force reconnect (timeout!)
     }
 
+    # special file: ditto-zip-on-the-fly
+    # ditto: Can't archive multiple sources
+    elsif ($macos and $opt_a and $file =~ /(.+)\.(zip)$/ and scalar(@ARGV) == 1) {
+      $aname = $1;
+      $atype = $2;
+      $list  = "$tmpdir/$aname.list";
+      $error = "$tmpdir/$aname.error";
+      $ditto = 'ditto -c -k --sequesterRsrc --keepParent';
+      if (-d "@ARGV" and "@ARGV" =~ m:^(.+)/(.+):) {
+        $dittodir = $1;
+        $file = $2;
+        $file =~ s/([^\w\-\@\#%,.=+_:])/\\$1/g;
+        $ditto .= ' '.$file;
+      } else {
+        foreach (@ARGV) {
+          $file = $_;
+          $file =~ s/([^\w\-\@\#%,.=+_:])/\\$1/g;
+          $ditto .= ' '.$file;
+        }
+      }
+      # print "calculating archive size... ";
+      debug("cd $dittodir;$ditto -");
+      open $ditto,"cd $dittodir;$ditto - 2>$error|" 
+        or die "$0: cannot run ditto - $!\n";
+      $t0 = int(time) if -t STDOUT;
+      while ($b = read $ditto,$_,$bs) {
+        $filesize += $b;
+        if ($t0) {
+          $t1 = int(time);
+          if ($t1>$t0) {
+            printf "Archive size: %d MB\r",int($filesize/M);
+            $t0 = $t1;
+          }
+        }
+      }
+      printf "Archive size: %d MB\n",int($filesize/M) if -t STDOUT;
+      unless (close $ditto) {
+        $_ = '';
+        if (-s $error and open $error,$error) {
+          local $/;
+          $_ = <$error>;
+          close $error;
+        }
+        unlink $list,$error;
+        die "$0: ditto-zip error:\n$_";
+      }
+      unlink $list,$error;
+      $file = "$aname.$atype";
+      $filename = encode_utf8($file);
+      undef $SH; # force reconnect (timeout!)
+    }
+
     # single file
     else {
       $filename = encode_utf8(${'opt_='} || $file);
@@ -1840,7 +2328,7 @@ sub formdatapost {
       if ($opt_d) {
         $filesize = 0;
       } elsif (not $opt_g and not $opt_s) {
-        $filesize = -s $file or die "$0: $file is empty or not readable\n";
+        $filesize = -s $file or die "$0: \"$file\" is empty or not readable\n";
       }
     }
 
@@ -1880,20 +2368,28 @@ sub formdatapost {
 
   $P{id} = $sid; # ugly hack!
 
+  $filename =~ s/\\/_/g; # \ is a illegal character for fexsrv
+
   # ask server if this file has been already sent
-  if ($file and not $xx and not
-      ($opt_s or $opt_g or $opt_o or $opt_d or $opt_l or $opt_L or ${'opt_/'}))
-  {
-    ($seek,$location) = query_file($server,$port,$frecipient||$P{to},$P{from},
-                                   $P{id},$filename,$fileid);
-    if ($filesize == $seek) {
-      print "Location: $location\n" if $location and $nomail;
-      warn "$0: $file has been already transferred\n";
-      return $file;
-    } elsif ($seek and $seek < $filesize) {
-      $resume = " (resuming at byte $seek)";
-    } elsif ($filesize <= $seek) {
-      $seek = 0;
+  if ($file and not $xx) {
+    if (not $opt_d and $opt_o) {
+      # delete before overwrite
+      delete_file($from,$to,$filename);
+      serverconnect($server,$port);
+      query_sid($server,$port) unless $anonymous;
+      $P{id} = $sid; # ugly hack!
+    } elsif (not($opt_s or $opt_g or $opt_d or $opt_l or $opt_L or ${'opt_/'})) {
+      ($seek,$location) = query_file($server,$port,
+        $frecipient||$P{to},$P{from},$P{id},$filename,$fileid);
+      if ($filesize == $seek) {
+        print "Location: $location\n" if $location and $nomail;
+        warn "$0: $file has been already transferred\n";
+        return 0;
+      } elsif ($seek and $seek < $filesize) {
+        $resume = " (resuming at byte $seek)";
+      } elsif ($filesize <= $seek) {
+        $seek = 0;
+      }
     }
     if ($proxy) {
       sleep 1;    # do not overrun proxy
@@ -1935,7 +2431,8 @@ sub formdatapost {
       push @hb,"--$boundary";
       push @hb,"Content-Disposition: form-data; name=\"$name\"";
       push @hb,"";
-      push @hb,encode_utf8($P{$v});
+      # push @hb,encode_utf8($P{$v});
+      push @hb,$P{$v};
     }
   }
 
@@ -2027,10 +2524,10 @@ sub formdatapost {
           $tpid = fork();
           if (defined $tpid and $tpid == 0) {
             sleep 1;
-            if (open $tarlist,$tarlist) {
-              # print "\n$tar|\n"; system "ls -l $tarlist";
-              while ($tarlist) {
-                while (<$tarlist>) {
+            if (open $list,$list) {
+              # print "\n$tar|\n"; system "ls -l $list";
+              while ($list) {
+                while (<$list>) {
                   print ' 'x(length($file)+40),"\r",$_;
                 }
                 sleep 1;
@@ -2044,13 +2541,19 @@ sub formdatapost {
           print "Fast forward to byte $seek (resuming)\n";
           readahead($file,$seek);
         }
+      } elsif ($ditto) {
+        $ditto =~ s/ditto/ditto -V/;
+        open $file,"cd $dittodir;$ditto -|" or die "$0: cannot run ditto - $!\n";
+        if ($seek) {
+          print "Fast forward to byte $seek (resuming)\n";
+          readahead($file,$seek);
+        }
       } else {
         if ($opt_g) {
-          my $fileq = $file;
-          $fileq =~ s/([^\w\-\@\#%,.=+~_:])/\\$1/g;
+          my $fileq = quote($file);
           open $file,"gpg -e -r $to <$fileq|" or die "$0: cannot run gpg - $!\n";
         } else {
-          open $file,$file or die "$0: cannot read $file - $!\n";
+          open $file,$file or die "$0: cannot read \"$file\" - $!\n";
           seek $file,$seek,0;
         }
         binmode $file;
@@ -2072,7 +2575,11 @@ sub formdatapost {
         alarm(0);
         $bytes += $b;
         if ($filesize > 0 and $bytes+$seek > $filesize) {
-          die "$0: $file filesize has grown while uploading\n";
+          if ($tpid) {
+            kill 9,$tpid;
+            unlink $list;
+          }
+          die "$0: \"$file\" filesize has grown while uploading\n";
         }
         $bt += $b;
         $t2 = time;
@@ -2121,12 +2628,26 @@ sub formdatapost {
       if ($tpid) {
         sleep 2;
         kill 9,$tpid;
-        unlink $tarlist;
+        unlink $list;
       }
-
+      
+      if ($fileid =~ /[a-z]/ and not ($opt_s or $opt_g)) {
+        if ($opt_a) {
+          if ($fileid ne md5_hex(fmd(@ARGV))) {
+            print "\n" unless $opt_q;
+            die "$0: files have been modified while uploading\n";
+          }
+        } else {
+          if ($fileid ne fileid($file)) {
+            print "\n" unless $opt_q;
+            die "$0: file has been modified while uploading\n";
+          }
+        }
+      }
+      
       unless ($opt_q) {
         if (not $chunksize and $bytes+$seek < $filesize) {
-          die "$0: $file filesize has shrunk while uploading\n";
+          die "$0: \"$file\" filesize has shrunk while uploading\n";
         }
 
         if ($seek or $chunksize and $chunksize < $filesize) {
@@ -2270,7 +2791,7 @@ sub zipsplit {
       $size = -s $file;
       if ($size > 2147480000) {
         unlink @zipfiles;
-        die "$0: $file too big for zip\n";
+        die "$0: \"$file\" too big for zip\n";
       }
       if ($zsize + $size > 2147000000) {
         push @zipfiles,zip($zipbase.'_'.$n.'.zip',@files);
@@ -2336,7 +2857,7 @@ sub query_file {
   my $seek = 0;
   my $qfileid = '';
   my ($head,$location);
-  my ($response,$fexsrv);
+  my ($response,$fexsrv,$cc);
   local $_;
 
   $to =~ s/,.*//;
@@ -2377,11 +2898,17 @@ sub query_file {
     if (/^X-File-ID:\s+(.+)/)          { $qfileid = $1 }
     if (/^X-Features:\s+(.+)/)         { $features = $1 }
     if (/^X-Location:\s+(.+)/)         { $location = $1 }
+    if (/^Connection: close/)           { $cc = $_ }
   }
 
   # return true seek only if file is identified
   $seek = 0 if $qfileid and $qfileid ne $fileid;
 
+  if ($cc) {
+    serverconnect($server,$port);
+    $sid = $id;
+  }
+
   return ($seek,$location);
 }
 
@@ -2409,7 +2936,7 @@ sub edit_address_book {
   print {$ab} $AB{ADDRESS_BOOK};
   close $ab;
 
-  system $editor,$ab;
+  system "$editor $ab";
   exit unless -s $ab;
 
   $opt_o = $opt_A;
@@ -2510,14 +3037,12 @@ sub query_sid {
 
   $sid = $id;
 
-  if ($port eq 443) {
+  if ($port eq 443 or $proxy) {
     return if $features;    # early return if we know enough
-    $req = "OPTIONS FEX HTTP/1.1";
-  } elsif ($proxy) {
-    return if $features;    # early return if we know enough
-    $req = "GET $proxy_prefix/SID HTTP/1.1";
+    $req = "OPTIONS /FEX HTTP/1.1";
+    $req = "HEAD / HTTP/1.1";
   } else {
-    $req = "GET SID HTTP/1.1";
+    $req = "GET /SID HTTP/1.1";
   }
 
   sendheader("$server:$port",$req,"User-Agent: $useragent");
@@ -2529,16 +3054,44 @@ sub query_sid {
   s/\r//;
   print "<-- $_" if $opt_v;
 
-  if (/^HTTP.* [25]0[01] /) {
+  if ($req =~ /OPTIONS/ and /^HTTP.* 502 /) {
+    # (reverse) proxy error
+    close $SH;
+    serverconnect($server,$port);
+    $req = "GET /SID HTTP/1.0";
+    sendheader("$server:$port",$req,"User-Agent: $useragent");
+    $_ = <$SH>;
+    unless (defined $_ and /\w/) {
+      print "\n" if $opt_v;
+      die "$0: no response from server\n";
+    }
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    while (<$SH>) {
+      s/\r//;
+      print "<-- $_" if $opt_v;
+      $features = $1 if /^X-Features: (.+)/;
+      $timeout = $1  if /^X-Timeout: (\d+)/;
+      last if /^\n/;
+    }
+    close $SH;
+    serverconnect($server,$port);
+  } elsif (/^HTTP.* [25]0[01] /) {
     if (not $proxy and $port ne 443 and /^HTTP.* 201 (.+)/) {
       $sid = 'MD5H:'.md5_hex($id.$1);
     }
+    my $cc;
     while (<$SH>) {
       s/\r//;
       print "<-- $_" if $opt_v;
       $features = $1 if /^X-Features: (.+)/;
       $timeout = $1  if /^X-Timeout: (\d+)/;
-      last if /^\n/;
+      $cc = $_       if /^Connection: close/;
+      last           if /^\n/;
+    }
+    if ($cc) {
+      serverconnect($server,$port);
+      $sid = $id;
     }
   } elsif (/^HTTP.* 301 /) {
     while (<$SH>) { last if /Location/ }
@@ -2588,15 +3141,15 @@ sub xxget {
 
   die "$0: no Content-Length in server-reply\n" unless $cl;
 
-  open F,">$save" or die "$0: cannot write to $save - $!\n";
-  binmode F;
+  open $save,">$save" or die "$0: cannot write to $save - $!\n";
+  binmode $save;
 
   $t0 = $t1 = int(time);
   $tso = '';
 
   while ($b = read($SH,$_,$bs)) {
     $B += $b;
-    print F;
+    print {$save} $_;
     if (int(time) > $t1) {
       $t1 = int(time);
       $ts = ts($B,$cl);
@@ -2609,7 +3162,7 @@ sub xxget {
   }
 
   print STDERR ts($B,$cl),"\n";
-  close F;
+  close $save;
 }
 
 
@@ -2745,10 +3298,16 @@ sub readahead {
 }
 
 
-# fileid is inode and mtime
 sub fileid {
-  my @s = stat(shift);
-  return @s ? $s[1].$s[9] : int(time);
+  my $file = shift;
+  my @s = stat($file);
+  
+  if (@s) {
+    return md5_hex($file.$s[0].$s[1].$s[7].$s[9]);
+  } else {
+    warn "$0: $file - $!\n";
+    return int(time);
+  }
 }
 
 
@@ -2768,7 +3327,7 @@ sub get_mutt_alias {
       s/.*\s+//;
       s/[<>]//g;
       if (/,/) {
-        warn "$0: ignoring mutt multi-alias $to = $alias\n";
+        warn "$0: ignoring mutt multi-alias $to = $_\n";
         last;
       }
       if (/@/) {
@@ -2783,7 +3342,7 @@ sub get_mutt_alias {
 }
 
 
-# collect file meta data (filename, inode, mtime)
+# collect (hashed) file meta data
 sub fmd {
   my @files = @_;
   my ($file,$dir);
@@ -2796,7 +3355,7 @@ sub fmd {
         while (defined ($file = readdir($dir))) {
           next if $file eq '..';
           if ($file eq '.') {
-            $fmd .= $file.fileid($dir);
+            $fmd .= fileid($dir);
           } else {
             $fmd .= fmd("$dir/$file");
           }
@@ -2804,7 +3363,7 @@ sub fmd {
         closedir $dir;
       }
     } else {
-      $fmd .= $file.fileid($file);
+      $fmd .= fileid($file);
     }
   }
 
@@ -2862,8 +3421,8 @@ sub http_response {
   unless (defined $_ and /\w/) {
     die "$0: no response from server\n";
   }
-  print "<-- $_\n" if $opt_v;
   s/\r?\n//;
+  print "<-- $_\n" if $opt_v;
   # CGI fatalsToBrowser
   if (/^HTTP.* 500/) {
     @r = <$SH> unless @r;
@@ -2883,7 +3442,6 @@ sub http_response {
     die "$0: server error: $error\n";
   }
 
-  print "<-- $_\n" if $opt_v;
   return $_;
 }
 
@@ -2901,12 +3459,11 @@ sub update {
   local $/;
 
   open $0,$0 or die "cannot read $0 - $!\n";
-  $_ = <$0>;
+  $cfc = <$0>;
   close $0;
-  s/.*\n$cfb\n//s;
-  $cfc = $_;
+  $cfc =~ s/.*\n$cfb\n//s;
 
-  foreach my $p (qw(fexget sexsend)) {
+  foreach my $p (qw'fexget sexsend') {
     open $p,$p or die "cannot read $p - $!\n";
     $_ = <$p>;
     close $p;
@@ -2917,7 +3474,7 @@ sub update {
     close $p;
   }
 
-  exec "l $0 fexget sexsend";
+  exec "l fexsend fexget sexsend";
   exit;
 }
 
@@ -3039,6 +3596,7 @@ sub tcpconnect {
 
   if ($SH) {
     autoflush $SH 1;
+    binmode $SH;
   } else {
     die "$0: cannot connect $server:$port - $@\n";
   }
@@ -3095,6 +3653,18 @@ sub nvtsend {
 }
 
 
+sub quote {
+  local $_ = shift;
+  s/([^\w\@\/%^,.=+_:+-])/\\$1/g;
+  return $_;
+}
+
+
+sub debug {
+  print "## DEBUG: @_\n" if $DEBUG;
+}
+
+
 # from MIME::Base64::Perl
 sub encode_b64 {
   my $res = "";
index 6a3e80e73641aa0c91eaf2e2d66bd127177fb6f6..e89b6f9bd578d62186219d255d076dd2c37ceb9d 100755 (executable)
@@ -91,7 +91,7 @@ foreach my $lib (
 
 # import from fex.pp
 our ($hostname,$debug,$timeout,$max_error,$max_error_handler);
-our ($spooldir,@logdir,$docdir,$xkeydir,$lockdir);
+our ($spooldir,@logdir,$docdir,$xkeydir,$akeydir,$lockdir);
 our ($force_https,$default_locale,$bs,$MB,$adlm);
 our (@locales);
 
@@ -247,15 +247,16 @@ REQUEST: while (*STDIN) {
       http_error(413);
     }
 
-    if (/^(GET \/|X-Forwarded-For|User-Agent)/i) {
+    if (/^(GET \/|\S*Forwarded|\S*Client-IP|\S*Coming-From|User-Agent)/i) {
       $hid .= $_."\n";
     }
 
     # reverse-proxy?
+    # (only IPv4 support!)
     if ($reverse_proxy_ip and $reverse_proxy_ip eq $ra and
-       /^X-Forwarded-For: ([\d.]+)/
+       /^\S*(Forwarded|Client-IP|Coming-From)\S*: ([\d.]+)/i
     ) {
-      $ENV{REMOTE_ADDR} = $ra = $1;
+      $ENV{REMOTE_ADDR} = $ra = $2;
       $ENV{REMOTE_HOST} = $rh = gethostbyaddr(inet_aton($ra),AF_INET) || '';
       $ENV{HTTP_HOST} = $hostname;
       if ($ENV{PROTO} eq 'https') { $port = 443 }
@@ -312,7 +313,7 @@ REQUEST: while (*STDIN) {
     }
   }
 
-  if ($request =~ /^OPTIONS FEX HTTP\/[\d\.]+$/i) {
+  if ($request =~ /^OPTIONS \/?FEX HTTP\/[\d\.]+$/i) {
     fexlog($connect,@log);
     nvt_print(
       "HTTP/1.1 201 OK",
@@ -499,6 +500,22 @@ REQUEST: while (*STDIN) {
     $ENV{LOCALE} = $locale = $default_locale;
   }
 
+  # for dynamic HTML documents
+  if ($ENV{HTTP_COOKIE} =~ /akey=(\w+)/) {
+    my $akey = $1;
+    my ($user,$id);
+    if ($user = readlink "$akeydir/$akey") {
+      $user =~ s:.*/::;
+      $user = untaint($user);
+      if ($id = slurp("$spooldir/$user/@")) {
+        chomp $id;
+        $ENV{AKEY} = $akey;
+        $ENV{USER} = $user;
+        $ENV{ID}   = $id;
+      }
+    }
+  }
+
   # check for name based virtual host
   $vhost = vhost($ENV{'HTTP_HOST'});
 
diff --git a/bin/fpg b/bin/fpg
new file mode 100755 (executable)
index 0000000..be610fe
--- /dev/null
+++ b/bin/fpg
@@ -0,0 +1,338 @@
+#!/usr/bin/perl -w
+#
+# Programname:                 fpg - Frams' Perl grep
+# Author:                      framstag@rus.uni-stuttgart.de
+# Copyright:                   GPL
+#
+# History:
+#   2003-02-27 Framstag                initial version
+#   2003-02-28 Framstag                added exit status
+#   2007-03-09 Framstag                added option -Q
+#   2007-06-01 Framstag                added options -s and -c 
+#                               and changed default output mode
+#   2007-06-03 Framstag                added ReadLine-support
+#   2007-08-31 Framstag                added option -x
+#   2008-02-06 Framstag                added implicit gunzip
+#                              -F ==> -R, new -F option
+#   2008-10-07 Framstag                added option -p
+#                               -n ==> -S, new -n option
+#   2008-10-14 Framstag                added option -M
+#   2008-11-23 Framstag                added option -~
+
+use Getopt::Std;
+use Term::ReadLine;
+use locale;
+
+sub usage {
+  die <<EOD
+usage: $0 [options] 'EXP' [file...]
+   or: $0 [options] -Q file...
+options: -r        recursively scan through directories   
+         -i        ignore case
+        -v        print only lines that do NOT match
+        -s        verbose scanning/searching
+        -n        prefix with line number
+        -l        list filenames only
+        -L        list filenames only that do NOT match
+        -p        show paragraphs, not lines (multiline record separator)
+        -o        show only matched strings, not whole lines
+        -M        mail-mode: search and show complete mails from mbox files
+        -c        print (count) only number of matches (NOT LINES!)
+        -F        EXP is a string, not a Perl regular expression
+        -e        EXP is any perl code which returns TRUE/FALSE
+        -S \#      minimum string length \# for binary files, default: 4
+        -C \#      \# lines of context
+        -R 'RS'   record separator, default: newline (\\n) if not set -p
+        -x 'exp'  extra regexp for highlighting (not used for searching)
+        -X 'exp'  exclude files (filename matching this regexp) when searching
+         -~        search in backup files *~ #*#, too
+        -Q        query-loop-prompt for search expression (with readline)
+arguments: EXP     is a Perl regular expression
+           file... can be one or more files, even binary or compressed ones
+EOD
+#examples: $0 -r 'from.*STDIN' *
+#          $0 -e 'length>30 and not /\\w/' script
+#See "perldoc perlre" for help on regular expressions.
+}
+
+$0 =~ s:.*/::;
+$| = 1;
+
+$maxlen = 0;
+
+$opt_i = $opt_r = $opt_v = $opt_l = $opt_h = $opt_e = $opt_n = $opt_o = 0;
+$opt_s = $opt_c = $opt_Q = $opt_F = $opt_p = $opt_M = $opt_C = $opt_S = 0;
+${'opt_~'} = 0;
+$opt_S = 4;
+$opt_x = $opt_X = '';
+$opt_R = "\n";
+
+usage() if !getopts('hirvlLFMopscQen~S:R:C:x:X:') or $opt_h and not @ARGV;
+
+unless ($opt_Q) {
+  $exp = shift or usage();
+}
+
+if ($opt_C and ($opt_l or $opt_L or $opt_s or $opt_v or $opt_p or $opt_M)) {
+  die "$0: cannot mix option -C with any of -l -L -s -v -p -M\n";
+}
+
+if ($opt_M and ($opt_l or $opt_L or $opt_s or $opt_v or $opt_p or $opt_C)) {
+  die "$0: cannot mix option -M with any of -l -L -s -v -p -C\n";
+}
+
+if ($opt_o and ($opt_v or $opt_l or $opt_L or $opt_c or $opt_F or $opt_C)) {
+  die "$0: cannot mix option -E with any of -l -L -v -c -C -F\n";
+}
+
+$opt_XX = 0;
+if (not ${'opt_~'}) {
+  @bfiles = grep(/~$|^#.*#$/,@ARGV);
+  if (@bfiles and 
+      (grep(/[^~]$/,@ARGV) or grep(/(^|\/)#[^\/]*#$/,@ARGV))) {
+    $opt_XX = 1;
+    warn "$0: ignoring @bfiles\n"; # unless $opt_r;
+  }
+}
+
+if (-t STDOUT) {
+  $B = "\033[1m";
+  $N = "\033[m";
+} else {
+  $B = $N = '';
+}
+
+if ($opt_p) { $/ = '' }
+else        { $/ = $opt_R }
+#else        { eval '$/ = "'.$opt_R.'"' }
+  
+$opt_h = 1 if not $opt_r and @ARGV < 2;
+
+if ($opt_Q) {
+  $q = new Term::ReadLine $0;
+  $q->ornaments(0) unless $ENV{PERL_RL};
+  for (;;) {
+    $exp = $q->readline("$B\nsearch-expression:$N ");
+    last unless $exp;
+    &scan;
+  }
+} else {
+  &scan;
+}
+
+exit ($found?0:1);
+
+sub scan {
+  $egrep = '';
+  if ($opt_e) {
+    eval "\$egrep =  sub { $exp }";
+  } else {
+    $exp =~ s/([\@\$\%\^\&\*\(\)\+\[\]\{\}\\\|\.\?])/\\$1/g if $opt_F;
+    $exp = '(?i)'.$exp if $opt_i;
+    $exp = '(?s)'.$exp if $opt_p or $opt_R;
+    #? $exp =~ s/\.\*\*/[.\n]*/g;
+  }
+  
+  $found = 0;
+  
+  if (@ARGV) {
+    foreach $file (@ARGV) {
+      next if $opt_X  and $file =~ /$opt_X/;
+      next if $opt_XX and ($file =~ /~$/ or $file =~ m{(^|/)#[^/]*#$});
+      my $error = ''; open $file,$file or $error = $!; close $file;
+      if ($error) {
+        warn "$0: cannot read file $file - $error\n";
+        next;
+      }
+      unless (-f $file or -d $file or -c $file or -S $file or -p $file) {
+        warn "$0: ignoring special file $file\n";
+        next;
+      }
+      $maxlen = length $file if $maxlen < length $file;
+      # printf "%s\r",substr("scanning $file".(" " x 255),0,$maxlen+9) if -t STDOUT;
+      # print  $B."scanning $file\n".$N if -t STDOUT and not $opt_l||$opt_L;
+      if ($opt_r and -d $file) {
+        $found += grepd($file);
+        next;
+      }
+      # next if -z $file; # Achtung: special files unter /proc sind "empty" !
+      # $type = `file -L $file`;
+      # if ($type =~ /text/i and open F,$file or open F,"strings $file|") {
+      $fileq = quotemeta $file;
+      if (-T $file) {
+        open $file,$file;
+        # warn "$file\n";
+      } else {
+        if ($file =~ /\.bz2$/) {
+          open $file,"bunzip2 <$fileq|";
+          # warn "gunzip <$file|\n";
+        } elsif ($file =~ /\.gz$/) {
+          open $file,"gunzip <$fileq|";
+          # warn "gunzip <$file|\n";
+        } else {
+          open $file,"strings -a -n $opt_S $fileq|";
+          # warn "strings -n $opt_S $file|\n";
+        }
+      }
+      if (fileno $file) {
+        $found += grepf($file,$file);
+        close $file;
+      } else {
+        warn "$0: cannot open $file - $!\n";
+        next;
+      }
+    }
+    # print " " x ($maxlen+9),"\r" if -t STDOUT;
+  } else {
+    $found = grepf(STDIN);
+  }
+}
+
+sub grepd {
+  my $dir = shift;
+  my $file;
+  my $found = 0;
+  
+  opendir $dir,$dir or return;
+  while (defined($file = readdir $dir)) {
+    next if $file eq '.' or $file eq '..';
+    if (not ${'opt_~'} and $file =~ /~$|^#[^\/]*#$/) {
+      # warn "$0: ignoring $dir/$file\n";
+      next;
+    }
+    $file = "$dir/$file";
+    next unless -r $file;
+    if (-d $file and not -l $file) {
+      $found += grepd($file);
+      next;
+    }
+    next unless -f $file or -c $file or -S $file or -p $file or -z $file;
+    $fileq = quotemeta $file;
+    if (-T $file and open $file,$file or 
+        open $file,"strings -a -n $opt_S $fileq|") {
+      $found += grepf($file,$file);
+      close $file;
+    }
+  }
+  closedir $dir;
+  return $found;
+}
+
+
+sub grepf {
+  my $F = shift;
+  my $file = shift;
+  my $found = 0;
+  my ($n,$l,$c);
+  
+  warn $B."scanning $file".$N."\n" if -t STDOUT and $opt_s;
+  
+  while (<$F>) {
+    $_ .= "\n" unless /\n$/;
+    if ($opt_M) {
+      if ($mail and (/^From / or eof $F)) {
+        my $__ = $_;
+        $_ = $mail;
+        $mail = $__;
+      } else {
+        $mail .= $_;
+        next;
+      }
+    }
+    $l++;
+    $n = 0;
+    if ($opt_C) {
+      for (my $i=$opt_C;$i;$i--) {
+        $C{$i} = $C{$i-1} if defined $C{$i-1};
+      }
+      $C{0} = [$l,$_];
+    }
+    if ($opt_e) {
+      if ($opt_v) { 
+        next if &$egrep;
+      } else { 
+        unless (&$egrep) {
+          if ($opt_C and $c) {
+            print "$l:" if $opt_n;
+            print;
+            $L{$l} = $l;
+            $c--;
+          }
+          next;
+        }
+      }
+      $n++;
+    } else {
+      if ($opt_v) {
+        # print ">>>$_" if $opt_i and /$exp/oi or /$exp/o;
+        if ($opt_Q) {
+          next if /$exp/m;
+        } else {
+          next if /$exp/om;
+        }
+        $n++;
+      } else {
+        if ($opt_c) {
+          if ($opt_Q) { $n++ while /$exp/mg }
+          else       { $n++ while /$exp/omg }
+        } else {
+          if ($opt_o) { 
+            my $m = '';
+            while (s/($exp)//) {
+              $n++;
+              $m .= "$1\n";
+            }
+            $_ = $m;
+          } elsif ($opt_Q) { 
+            $n += s/($exp)/$B$1$N/mg;
+          } else { 
+            $n += s/($exp)/$B$1$N/omg;
+          }
+        }
+      }
+    }
+    unless ($n) {
+      if ($opt_C and $c) {
+        print "$l:" if $opt_n;
+        print;
+        $L{$l} = $l;
+        $c--;
+      }
+      next;
+    }
+    $found += $n;
+    # print " " x ($maxlen+9),"\r" if -t STDOUT and $found==1;
+    next if $opt_c;
+    last if $opt_l or $opt_L;
+    if ($file and not $opt_s) {
+      print "\n$B$file$N:\n";
+      $file = '';
+    }
+    if ($opt_x and $n) {
+      if ($opt_i) { s/($opt_x)/$B$1$N/ogi } 
+      else        { s/($opt_x)/$B$1$N/og }
+    }
+    for (my $i=$opt_C;$i;$i--) {
+      if (defined $C{$i}) {
+        my ($ln,$ls) = @{$C{$i}};
+        unless (defined $L{$ln}) {
+          $L{$ln} = $ln;
+          print "$ln:" if $opt_n;
+          print $ls;
+        }
+      }
+    }
+    print "$l:" if $opt_n;
+    print;
+    $L{$l} = $l;
+    $c = $opt_C;
+  }
+  
+  if ($opt_c) {
+    print "$file:" if @ARGV>1;
+    print "$found\n";
+  } else {
+    print "$file\n" if $opt_l and $found or $opt_L and not $found;
+  }
+  return $found;
+}
diff --git a/bin/l b/bin/l
index 1389931a5e070cdcdf82f03ef9fb9c14b53d55bb..c7d7667baeedfb7844b7b81992a73c16de5669c9 100755 (executable)
--- a/bin/l
+++ b/bin/l
@@ -1,10 +1,10 @@
 #!/usr/bin/perl -w
 #
-# l / ll / lf / llf -  substitute of the classic ls command
+# l / ll / lf / llf -  better replacement of the classic ls command
 #
 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
 #
-# Copyright: GNU General Public License 
+# Copyright: Perl Artistic License 
 
 use Cwd qw'abs_path';
 use File::Basename;
@@ -22,31 +22,24 @@ $ENV{LC_CTYPE} = 'C';
 # parse CLI arguments
 $opt_l = $opt_i = $opt_t = $opt_s = $opt_a = $opt_r = $opt_d = $opt_n = 0;
 $opt_L = $opt_N = $opt_c = $opt_u = $opt_S = $opt_R = $opt_z = $opt_h = 0;
-$opt_U = $opt_x = 0;
-${'opt_*'} = ${'opt_?'} = 0;
+$opt_U = $opt_x = $opt_E = 0;
+${'opt_*'} = 0;
 $opt_m = $opt_f = $opt_F = $opt_D = '';
-&usage if !getopts('hdnlLNitcuarsxUSRz*?m:f:D:F:') || $opt_h;
+getopts('hdnlLNitcuarsxUSREz*m:f:D:F:') or usage(1);
+usage(0) if $opt_h;
 $opt_z = 1 unless $opt_R;
 $opt_l = 1                            if $0 eq 'll';
 $opt_l = $opt_i = $opt_a = $opt_S = 1 if $0 eq 'lll';
+&examples if $opt_E;
 if ($0 eq 'lf' or $0 eq 'llf') {
-  unless ($opt_F) {
-    $opt_F = shift;
-    unless (length $opt_F) {
-      print "find regexp: ";
-      chomp($opt_F = <STDIN>||'');
-    }
-  }
-  $opt_l = $0 if $0 eq 'llf';
-  $opt_F = '.' unless length $opt_F;
-  $opt_R = $opt_F;
+  $opt_F ||= shift or usage(1);
+  $opt_R ||= scalar(@ARGV) || ($opt_F eq '.');
+  $opt_l ||= $0 eq 'llf';
 }
 
 $postsort = $opt_t||$opt_s;
 $postproc = $postsort||$opt_z;
 
-&examples if ${'opt_?'};
-
 # mark for squeeze operation
 $z = $opt_z ? "\0" : '';
 
@@ -551,14 +544,17 @@ sub fmatch {
 
 
 sub usage {
+  my $status = shift;
   my $opts = '[-lastcuidnrzLRxNS*] [-f format] [-D X:Y]';
+  local *OUT = $status ? *STDERR : *STDOUT;
+  
   if ($0 ne 'lf') { 
-    print "usage: $0 $opts [-F regexp] [file...]\n";
+    print OUT "usage: $0 $opts [-F regexp] [file...]\n";
   }
   $opts =~ s/R//;
-  print "usage: lf $opts regexp [directory...]\n";
-  print <<EOD;
-options: -l  long list
+  print OUT "usage: lf $opts regexp [directory...]\n";
+  print OUT <<EOD;
+options: -l  long list (implicit if called 'll')
          -a  list also .* files
          -s  sort by size
          -t  sort by time
@@ -576,16 +572,16 @@ options: -l  long list
          -F  find files matching case insensitive regexp
          -N  show only normal (regular) files
          -S  print statistics summary at end
-         -*  list plain file names (without masking \\)
+         -*  list plain file names (without \\ masking)
         -f  user defined format output, format characters are:
             m=mode, u=user, g=group, s=size, l=hard links count, i=inode
             n=name only, d=date, a=access+modification+inodechange dates
          -D  list only files newer than X and older than Y 
              XY format: NUMBER[smhd] (s=seconds, m=minutes, h=hours, d=days)
              XY format: YYYY-MM-DD (Y=year, M=month, D=day)
-         -?  show examples
+         -E  show examples
 EOD
-  exit 2;
+  exit $status;
 }
 
 sub examples {
@@ -597,8 +593,8 @@ l -*f mus        # list files native names with format: mode+user+size
 l -D 10d:        # list files newer than 10 days
 ll               # list files long format (equal to: l -l)
 lll              # list files extra long format (equal to: l -liS)
-lf 'status.*mp3' # list files recursive matching regexp (equal to: l -RF)
-lf sda3 /dev     # list devices matching sda3 (equal to: l -RF sd3 /dev)                                                       
+lf 'status.*mp3' # list files matching regexp (equal to: l -F 'status.*mp3')
+lf sda1 /dev     # list devices matching sda1 (equal to: l -RF sda1 /dev)
 EOD
   exit;
 }
index 733673ce2d6b99abd586737a51706cf5893033b7..d5d21955618b8a592eb9134de17a6236830ca1ac 100755 (executable)
@@ -44,6 +44,7 @@ $ignore = join('|',qw(
   GET./browserconfig\.xml
   User-Agent:.*(Webnote|FeedFetcher|\w+bot|bot/|Website.Watcher|crawler|spider|searchme|Yandex|Slurp|ScoutJet|findlinks|urlmon|nagios)
   User-Agent:.fnb.*quak
+  User-Agent:.Google.favicon
   From:.*(msnbot|yandex|googlebot|webcrawler)
   Referer:.*sex.*stream
   Referer:.*stream.*sex
@@ -64,16 +65,20 @@ $ignore = join('|',qw(
   DNT:
   Via:
   profile:
+  Upgrade-Insecure-Requests:
   if-modified-since
   Surrogate-Capability
   Proxy-Authorization
   http\.
+  Device-Stock
   NOKIA_
   GPRS
   X-Proxy-ID
   X-Moz
   X.Wap
   X-FH
+  X-FB
+  X-WS
   X-Nokia
   X-UCBrowser
   X-NSN
@@ -88,6 +93,9 @@ $ignore = join('|',qw(
   X-Country
   X-ClickOnceSupport
   X-Newrelic
+  X-IMForwards
+  X-Clearswift
+  X-MDS
   .*:\s*$
 );
 
@@ -166,7 +174,7 @@ sub read_debug_log {
     sleep 1;
     @log = `ls -rt $logdir[0]/.debug/*_${pid}.$cgi 2>/dev/null`;
     if ($log = $log[-1] and open $log,$log) {
-      binmode($log,":encoding(UTF-8)");
+      binmode($log,":encoding(UTF-8)");
       while (<$log>) {
         s/\r//;
         if (/^Content-Disposition:.*name="FILE".*filename="(.+)"/i) {
index ff3f1ed5d367d8cd5e12c3bb12f82dc7e203f212..d9fe821de487cd5e3733877c9009560286081c1d 100755 (executable)
@@ -19,7 +19,8 @@ use constant M => 2**20;
 
 eval 'use Net::INET6Glue::INET_is_INET6';
 
-our $version = 20150826;
+our $version = 20160104;
+our $DEBUG = $ENV{DEBUG};
 
 my %SSL = (SSL_version => 'TLSv1');
 my $sigpipe;
@@ -142,6 +143,8 @@ if ($0 eq 'sexxx') {
   }
 
 } elsif ($0 eq 'sexget' or $0 eq 'fuckme') {
+
+  $opt_g = 0;
   getopts('hgvVdu:') or die $usage;
   die $usage if $opt_h;
 
@@ -175,7 +178,7 @@ if ($0 eq 'sexxx') {
 
 } else { # sexsend
 
-  $opt_g = 1;
+  $opt_g = 0;
   getopts('hguvqVTt:') or die $usage;
   die $usage if $opt_h;
 
@@ -605,15 +608,9 @@ sub serverconnect {
   my $connect = "CONNECT $server:$port HTTP/1.1";
   local $_;
 
-  if ($opt_v and $port == 443 and %SSL) {
-    foreach my $v (keys %SSL) {
-      printf "%s => %s\n",$v,$SSL{$v};
-    }
-  }
-
   if ($proxy) {
     tcpconnect(split(':',$proxy));
-    if ($port == 443) {
+    if ($https) {
       printf "--> %s\n",$connect if $opt_v;
       nvtsend($connect,"");
       $_ = <$SH>;
@@ -622,14 +619,13 @@ sub serverconnect {
       unless (/^HTTP.1.. 200/) {
         die "$0: proxy error : $_";
       }
-      eval "use IO::Socket::SSL";
-      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      &enable_ssl;
       $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
     }
   } else {
     tcpconnect($server,$port);
   }
-#  if ($port == 443 and $opt_v) {
+#  if ($https and $opt_v) {
 #    printf "%s\n",$SH->get_cipher();
 #  }
 }
@@ -644,10 +640,9 @@ sub tcpconnect {
     undef $SH;
   }
 
-  if ($port == 443) {
+  if ($https) {
     # eval "use IO::Socket::SSL qw(debug3)";
-    eval "use IO::Socket::SSL";
-    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    &enable_ssl;
     $SH = IO::Socket::SSL->new(
       PeerAddr => $server,
       PeerPort => $port,
@@ -664,6 +659,7 @@ sub tcpconnect {
 
   if ($SH) {
     autoflush $SH 1;
+    binmode $SH;
   } else {
     die "$0: cannot connect $server:$port - $@\n";
   }
@@ -672,6 +668,18 @@ sub tcpconnect {
 }
 
 
+sub enable_ssl {
+  eval "use IO::Socket::SSL";
+  die "$0: cannot load IO::Socket::SSL\n" if $@;
+  eval '$SSL{SSL_verify_mode} = 0 if Net::SSLeay::SSLeay() <= 9470143';
+  if ($opt_v) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
+}
+
+
 sub sendheader {
   my $sp = shift;
   my @head = @_;
@@ -708,6 +716,18 @@ sub nvtsend {
 }
 
 
+sub quote {
+  local $_ = shift;
+  s/([^\w\@\/%^,.=+_:+-])/\\$1/g;
+  return $_;
+}
+
+
+sub debug {
+  print "## DEBUG: @_\n" if $DEBUG;
+}
+
+
 # from MIME::Base64::Perl
 sub encode_b64 {
   my $res = "";
index daf83f3b3039778fdd6adc6fb7ed1222283d39c8..7a180a65c1b899d0cb81befe1f7fc5912361dbab 100755 (executable)
@@ -7,6 +7,7 @@
 
 BEGIN { ($ENV{PERLINIT}||'') =~ /(.+)/s and eval $1 }
 
+use utf8;
 use Fcntl      qw(:flock);
 use Digest::MD5        qw(md5_hex);
 
@@ -85,7 +86,15 @@ if ($user and $id) {
   # authorized login URL
   my $url = "$ENV{PROTO}://$ENV{HTTP_HOST}/fup/".b64("from=$user&id=$id");
   pq(qq(
-    '<h2>for user <a href="$url">$user</a></h2>'
+    '<script>'
+    '  function show_user() { return(alert('
+    '       "server:\\t$ENV{HTTP_HOST}\\n"+'
+    '       "user:\\t$user\\n"+'
+    '       "auth-ID:\\t$id\\n"+'
+    '       "URL:\\t\\t$url"'
+    '  ));}'
+    '</script>'
+    '<h2>for user <a href="#" onclick="show_user();" title="click to see account data">$user</a></h2>'
     '<table>'
   ));
   ($quota,$du) = check_sender_quota($user);
@@ -119,7 +128,7 @@ if ($user and $id) {
     '  <input type="hidden" name="user" value="$user">'
     '  <input type="hidden" name="id"   value="$id">'
     '  <script>function show_id() {return(alert("auth-ID: $id"));}</script>'
-    '  Change your <a href="" onclick="show_id();" title="$id">auth-ID</a> to'
+    '  Change your <a href="#" onclick="show_id();" title="$id">auth-ID</a> to'
     '  <input type="text"   name="nid"  size="16">'
     '  <input type="submit" value="remember it!">'
   ));
index fb37261f075b9b62a17c7b9869418f4c5828eebd..dec3abc0fe9c45192e62166f78962e9984a92feb 100755 (executable)
@@ -7,6 +7,7 @@
 
 BEGIN { ($ENV{PERLINIT}||'') =~ /(.+)/s and eval $1 }
 
+use utf8;
 use Fcntl              qw':flock :seek';
 use Cwd                        qw'abs_path';
 use File::Basename;
@@ -552,19 +553,18 @@ if (-f $data) {
       and not($dkey and ($ENV{HTTP_COOKIE}||'') =~ /dkey=$dkey/)
       and open $file,'<',"$file/download")
   {
-    $_ = <$file> || '';
+    my $d1 = <$file> || ''; # first download
+    chomp $d1;
     close $file;
-    chomp;
     if ($ra) {
       # allow downloads from same ip
-      $_ = '' if /\Q$ra/;
+      $d1 = '' if $d1 =~ /\Q$ra/;
       # allow downloads from sender ip
-      $_ = '' if (readlink("$file/ip")||'') eq $ra;
+      $d1 = '' if (readlink("$file/ip")||'') eq $ra;
     }
-    if ($_) {
-      s/(.+) ([\w.:]+)$/by $2 at $1/;
+    if ($d1 and $d1 =~ s/(.+) ([\w.:]+)$/$2 at $1/) {
       $file = filename($file);
-      http_die("$file has already been downloaded $_");
+      http_die("$file has already been downloaded by $d1");
     }
   }
   $sb = sendfile($file,$seek,$stop);
index 661c897367a7418beabb0ec97bf7b57a693a3a3f..4d7ee3c043f1f0bb143b8ca6efeae8d90c7c74a9 100755 (executable)
@@ -8,6 +8,7 @@
 
 BEGIN { ($ENV{PERLINIT}||'') =~ /(.+)/s and eval $1 }
 
+use utf8;
 use Fcntl      qw(:flock);
 use Digest::MD5        qw(md5_hex);
 
@@ -74,7 +75,7 @@ foreach my $v (keys %PARAM) {
     $v =~ /^group$/i           ? $group        = checkchars('group',$vv):
     $v =~ /^ab$/i              ? $ab           = $vv:
     $v =~ /^gm$/i              ? $gm           = $vv:
-    $v =~ /^show$/i            ? $tools        = checkchars('parameter',$vv):
+    $v =~ /^show$/i            ? $show         = checkchars('parameter',$vv):
   $ESAC;
 }
 
@@ -87,6 +88,30 @@ $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
 
 $nomail = $comment if $comment =~ /NOMAIL|!#!/;
 
+if ($show and $show eq 'tools') {
+  nvt_print(
+    "HTTP/1.1 302 Found",
+    "Location: /tools.html",
+    'Expires: 0',
+    'Content-Length: 0',
+    ''
+  );
+  &reexec;
+  
+  if (open $tools,"$docdir/tools.html") {
+    while (<$tools>) {
+      while (/\$([\w_]+)\$/) {
+        my $var = $1;
+        my $env = $ENV{$var} || '';
+        s/\$$var\$/$env/g;
+      };
+      print;
+    }
+  }
+  exit;
+}
+
+
 if ($akey) {
 
   # sid is not set with web browser
@@ -154,7 +179,7 @@ if ($user and $id) {
 
 # empty POST? ==> back to foc
 if ($ENV{REQUEST_METHOD} eq 'POST' and not
-    ($subuser or $notify or $nid or $ssid or $group or $ab or $gm or $tools
+    ($subuser or $notify or $nid or $ssid or $group or $ab or $gm
      or $disclaimer or $encryption or $pubkey))
 {
   nvt_print(
@@ -181,31 +206,6 @@ if ($gm and not $group) {
   exit;
 }
 
-if ($tools) {
-  pq(qq(
-    'To use one of the following F*EX clients you must configure them after'
-    'download:'
-    '<p>'
-    '<table border=1>'
-    '  <tr><th align=left>F*EX server:<td><code>$ENV{PROTO}://$ENV{HTTP_HOST}</code></tr>'
-    '  <tr><th align=left>Proxy:<td>(your web proxy address, may be empty)</tr>'
-    '  <tr><th align=left>User:<td><code>$user</code></tr>'
-    '  <tr><th align=left>Auth-ID:<td><code>$id</code></tr>'
-    '</table>'
-  ));
-  if (open $tools,"$docdir/tools.html") {
-    while (<$tools>) {
-      while (/\$([\w_]+)\$/) {
-        my $var = $1;
-        my $env = $ENV{$var} || '';
-        s/\$$var\$/$env/g;
-      };
-      print;
-    }
-  }
-  exit;
-}
-
 if ($group) {
   &handle_group;
 }
@@ -924,7 +924,7 @@ sub notify_groupmember {
     ''
     'to upload files to F*EX group "$group"'
     ''
-    'See http://$ENV{HTTP_HOST}/ for more information about F*EX.'
+    'See http://$ENV{HTTP_HOST}/index.html for more information about F*EX.'
     ''
     'Questions? ==> F*EX admin: $admin'
   ));
index 97624a081db94e732440bc30c4ac116d63234a1d..7d222f9a3b9b14445914dc0b9e93a4f40ef72e2b 100755 (executable)
@@ -10,6 +10,7 @@
 
 BEGIN { ($ENV{PERLINIT}||'') =~ /(.+)/s and eval $1 }
 
+use utf8;
 use Encode;
 use Fcntl              qw':flock :seek :mode';
 use IO::Handle;
@@ -151,7 +152,7 @@ if ($from =~ /^anonymous@/ and
   $id = $rid = $anonymous = 'anonymous';
   if ($to =~ /^anonymous/) {
     @to = ($to);
-    $autodelete{$to} = $autodelete = 'NO';
+    $autodelete{$to} = $autodelete = $specific{'autodelete'}||'NO';
   }
   $nomail = $anonymous;
 }
@@ -199,10 +200,9 @@ if ($from and $id and not ($gkey or $skey or $public or $okey)) {
     # set akey link for HTTP sessions
     # (need original id for consistant non-moving akey)
     if (-d $akeydir and open $idf,'<',"$from/@" and my $id = getline($idf)) {
-      $akey = untaint(md5_hex("$from:$id"));
-      mksymlink("$akeydir/$akey","../$from");
-      # show URL from fexsend
-      if ($from eq $to and $comment eq '*') {
+      # akey for webbrowser or fexsend special
+      if (not $sid or ($from eq $to and ($comment eq '*')) or $command) {
+        $akey = untaint(md5_hex("$from:$id"));
         mksymlink("$akeydir/$akey","../$from");
       }
     }
@@ -403,14 +403,14 @@ if (($from and $id and $rid eq $id or $gkey or $skey) and $command) {
                     - int((time-mtime("$file/filename"))/$DS);
         if ($comment =~ /NOMAIL/ or
            (readlink "$to/\@NOTIFICATION"||'') =~ /^no/i) {
-          printf "%8s MB [%s d] %s/%s/%s\n",
+          printf "%8s MB (%2s d) %s/%s/%s\n",
                  $size,
                  $rkeep,
                  $durl,
                  $dkey,
                  urlencode(basename($file));
         } else {
-          printf "%8s MB [%s d] <a href=\"%s\">%s</a>%s %s\n",
+          printf "%8s MB (%2s d) <a href=\"%s\">%s</a>%s %s\n",
                  $size,
                  $rkeep,
                  untaint("/fup?akey=$akey&dkey=$dkey&command=RENOTIFY"),
@@ -469,9 +469,10 @@ if (($from and $id and $rid eq $id or $gkey or $skey) and $command) {
           }
           my $rkeep = untaint(readlink "$file/keep"||$keep_default)
                       - int((time-mtime("$file/filename"))/$DS);
-          printf "%8s MB [%s d] <a href=\"%s\">%s</a>%s\n",
+          printf "%8s MB (%2s d) %s <a href=\"%s\">%s</a>%s\n",
                  $size,
                  $rkeep,
+                 stat("$file/download")?'+':'-',
                  untaint("/fup?akey=$akey&dkey=$dkey&command=FORWARD"),
                  $filename,
                  $comment?qq( "$comment"):'';
@@ -528,7 +529,7 @@ if (($from and $id and $rid eq $id or $gkey or $skey) and $command) {
                    $akey,$dkey;
             printf "[<a href=\"/fup?akey=%s&dkey=%s&command=COPY\">forward</a>] ",
                    $akey,$dkey;
-            printf "%8s MB (%s d) <a href=\"%s\">%s</a>%s\n",
+            printf "%8s MB (%2s d) <a href=\"%s\">%s</a>%s\n",
                    $size,$rkeep,$url,$filename,$comment;
           }
         }
@@ -796,7 +797,7 @@ unless ($file) {
   my @cookies;
   if ($logout and my $cookie = $ENV{HTTP_COOKIE}) {
     while ($cookie =~ s/(\w+key)=\w+//) {
-      push @cookies,"Set-Cookie: $1=; Max-Age=0; Discard";
+      push @cookies,"Set-Cookie: $1=x; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT";
     }
   }
 
@@ -876,7 +877,7 @@ unless ($file) {
       '  <input type="hidden" name="from" value="$from">'
       '  <input type="hidden" name="id"   value="$id">'
       '  <table border="1">'
-      '    <tr><td>sender:   <td><a href="/fup/$ab64">$from</a></tr>'
+      '    <tr><td>sender:   <td><a href="/foc">$from</a></tr>'
       '    <tr title="e-mail address or alias"><td>recipient(s):'
       '        <td><input type="text" name="to" size="96" value="$to"><br>'
     ));
@@ -908,6 +909,11 @@ unless ($file) {
           foreach my $rd (@local_rdomains) {
             print "*\@$rd\n";
           }
+        } elsif (/^\@LOCAL_USERS/) {
+          foreach (glob "*/@") {
+            s:/.::;
+            print "$_\n";
+          }
         } else {
           print "$_\n";
         }
@@ -948,14 +954,16 @@ unless ($file) {
     pq(qq(
       '<p><hr><p>'
       '<b>'
-      'Warning: the recipient must not be a mailing list, because after'
-      'download the file will be no more available!'
+      'Warning: the recipient must not be a mailing list,'
+      'because after download the file will be no more available!'
       '</b><br>'
-      'Contact <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a>'
-      'if you want to fex to a mailing list,'
+      'Contact <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a> if you want to fex to a mailing list,'
       'he can allow multiple downloads for specific addresses.'
+      '<p>'
+      'Use a <a href="/tools.html">F*EX client</a> if you want to send more than one file or resume an interrupted upload.'
       '</body></html>'
-    ));
+      '<p>
+   ));
     exit;
   }
 
@@ -1052,7 +1060,7 @@ unless ($file) {
       pq(qq(
         '  <input type="hidden" name="akey" value="$akey">'
         '  <table border="1">'
-        '    <tr><td>sender:<td>$from</tr>'
+        '    <tr><td>sender:<td><a href="/foc">$from</a></tr>'
       ));
       if ($anonymous) {
         pq(qq(
@@ -1185,7 +1193,7 @@ unless ($file) {
   pq(qq(
     '<form action="/fup"'
     '      method="post"'
-    '      accept-charset="ISO-8859-1"'
+    '      accept-charset="UTF-8"'
     '      enctype="multipart/form-data">'
     '  <table>'
     '    <tr><td>sender:'
@@ -1538,11 +1546,18 @@ sub parse_request {
   # parse HTTP QUERY_STRING (parameter=value pairs)
   if ($qs) {
     foreach (split '&',$qs) {
-      if (s/^(\w+)=//) {
-        my $x = $1;
+      if (s/^(\w+)=(.*)//) {
+        my $p = uc($1);
+        my $v = $2;
         # decode URL-encoding
-        s/%([a-f0-9]{2})/chr(hex($1))/gie;
-        setparam($x,$_);
+        $v =~ s/%([a-f0-9]{2})/chr(hex($1))/gie;
+        setparam($p,$v);
+        if ($p eq 'AUTODELETE') {
+          $specific{'autodelete'} = $autodelete = $v;
+        }
+        if ($p eq 'KEEP' and /^\d+$/) {
+          $specific{'keep'} = $keep = $v;
+        }
       }
     }
   }
@@ -1680,9 +1695,9 @@ sub parse_request {
           foreach my $address (split(",",$address)) {
             $address .= '@'.$mdomain if $mdomain and $address !~ /@/;
             push @{$ab{$alias}},$address;
-            $autodelete{$alias} = $autodelete;
-            $keep{$alias}       = $keep;
-            $locale{$alias}     = $locale;
+            $autodelete{$alias} = $autodelete if $autodelete;
+            $keep{$alias}       = $keep       if $keep;
+            $locale{$alias}     = $locale     if $locale;
           }
         }
       }
@@ -1710,7 +1725,7 @@ sub parse_request {
           } elsif ($locale{$to}) {
             $locale{$address} = $locale{$to};
           } else {
-            $locale{$address} = $locale ;
+            $locale{$address} = $::locale ;
           }
           unless ($locale{$address}) {
             $locale{$address} = $default_locale || 'english';
@@ -2056,7 +2071,7 @@ sub get_file {
 
       if ($from eq "@to") {
         # special "fex yourself"
-        mksymlink("$filed/autodelete",'NO');
+        mksymlink("$filed/autodelete",$specific{'autodelete'}||'NO');
       } else {
         $autodelete{$to} = $autodelete unless $autodelete{$to};
         if ($autodelete{$to} =~ /^(DELAY|NO|\d+)$/i) {
@@ -2320,6 +2335,9 @@ sub check_rr {
             $ar .= '|[^\@]+\@' . $rd;
           }
           $ar .= ')';
+        } elsif (/^\@LOCAL_USERS/ and -s "$to/@") {
+          $allowed = 1;
+          last;
         } else {
           # allow wildcard *, but not regexps
           $ar = quotemeta $_;
@@ -2375,11 +2393,14 @@ sub expand {
 sub forward {
   my $file = shift;
   my ($nfile,$to,$AB);
-  my ($filename);
+  my ($filename,$keep);
   my (%to);
 
   http_die("no file data for <code>$file</code>") unless -f "$file/data";
 
+  $keep = $::keep||$keep_default;
+  if (my $mt = mtime("$file/data")) { $keep += int((time-$mt)/$DS) }
+
   if (@to) {
 
     # check recipients restriction
@@ -2413,11 +2434,11 @@ sub forward {
       }
     }
 
+    @to = keys %to;
+    
     http_header('200 OK');
     print html_header($head);
 
-    @to = keys %to;
-
     foreach my $to (my @loop = @to) {
       $to =~ s/:\w+=.*//; # remove options from address
       $nfile = $file;
@@ -2444,15 +2465,15 @@ sub forward {
         close $comment;
       }
       if ($autodelete =~ /^(DELAY|NO|\d+)$/i) {
-        symlink($autodelete,"$nfile/autodelete");
-      }
-      symlink($keep||$keep_default,         "$nfile/keep");
-                    copy("$file/id",        "$nfile/id");
-                    copy("$file/ip",        "$nfile/ip");
-                    copy("$file/speed",     "$nfile/speed");
-                    copy("$file/replyto",   "$nfile/replyto");
-      $filename   = copy("$file/filename",  "$nfile/filename");
-      link               "$file/data",      "$nfile/data"
+        symlink $autodelete,"$nfile/autodelete";
+      }
+      symlink          $keep,             "$nfile/keep";
+                  copy("$file/id",        "$nfile/id");
+                  copy("$file/ip",        "$nfile/ip");
+                  copy("$file/speed",     "$nfile/speed");
+                  copy("$file/replyto",   "$nfile/replyto");
+      $filename = copy("$file/filename",  "$nfile/filename");
+      link             "$file/data",      "$nfile/data"
         or die http_die("cannot create $nfile/data - $!");
       unless ($dkey = readlink("$nfile/dkey") and -l "$dkeydir/$dkey") {
         $dkey = randstring(8);
@@ -2622,6 +2643,7 @@ sub setparam {
   } elsif ($v eq 'FEXYOURSELF') {
     $submit = $vv;
     @to = ($from);
+    $specific{'autodelete'} = $autodelete = 'no';
   } elsif ($v eq 'TO') {
     # extract AUTODELETE and KEEP options
     if ($vv =~ s/[\s,]+AUTODELETE=(\w+)//i) {
@@ -2637,9 +2659,15 @@ sub setparam {
     if ($from) {
       if ($to eq '.') {
         $to = $from;
+         unless ($specific{'autodelete'}) {
+           $specific{'autodelete'} = $autodelete = 'no';
+         }
       }
       if ($to eq '//') {
         $to = $from;
+        unless ($specific{'autodelete'}) {
+          $specific{'autodelete'} = $autodelete = 'no';
+        }
         $comment = '//';
       }
     }
index ffccca601e2fa09bfac8477905f75ce326ac4062..5db49c46db9e0c05b42a9d21bdb1ab8cbcb5b096 100755 (executable)
@@ -7,6 +7,7 @@
 
 BEGIN { ($ENV{PERLINIT}||'') =~ /(.+)/s and eval $1 }
 
+use utf8;
 use Fcntl      qw(:flock :seek :mode);
 
 # import from fex.ph
index 0d0050947eee696ba31c588b9a74f7e662a3f856..a9449956b245693dc20adefcf7658bcf6a8c6451 100755 (executable)
@@ -7,6 +7,8 @@
 
 BEGIN { ($ENV{PERLINIT}||'') =~ /(.+)/s and eval $1 }
 
+use utf8;
+
 # add fex lib
 (our $FEXLIB) = $ENV{FEXLIB} =~ /(.+)/;
 die "$0: no $FEXLIB\n" unless -d $FEXLIB;
index 5b2d4e0619805b2c2a31555aa3123b2d26a561be..0a4d0b033a95efe90d7c90af5f55ff0d965f0fed 100755 (executable)
@@ -7,6 +7,7 @@
 
 BEGIN { ($ENV{PERLINIT}||'') =~ /(.+)/s and eval $1 }
 
+use utf8;
 use Fcntl      qw(:flock :seek :mode);
 use Digest::MD5        qw(md5_hex);
 
index 29809c05c7e48e56c0cf7cd0f37df293b4da6fcf..e95c66848a17212ee6681b54a54df61850e00f90 100644 (file)
@@ -1,13 +1,41 @@
+2015-12-29 fexsend: added search pattern argument to option -l
+2015-12-25 fup: added +/- download flag in LIST command output
+2015-12-18 fup: allow AUTODELETE and KEEP parameter for anonymous user and
+                sender=recipient
+2015-12-01 hint for fexget and fexit in notification email
+2015-12-01 fixed bug notification email ignores (sometimes) locale
+2015-11-27 fixed bug first line in encrypted notification emails gets lost
+2015-11-21 fixed bad UTF8 encoding for french spanish czech galician
+2015-11-10 fexget: fixed bug timeout for big files on slow storage
+2015-10-14 install: fixed bug missing fex group
+2015-10-09 fexsend: better support for reverse proxy (closing connection)
+2015-10-06 fexsend: abort if file has been modified while uploading
+2015-10-04 fup: when forwarding a file, keep time is calculated for today,
+                not for upload day
+2015-09-29 fexget: fixed bug no https download
+2015-09-26 fex_cleanup: fixed bug send no locale reactivation.txt
+2015-09-21 fexget: fixed bug resume download on aborted storage test file
+                   leads to corrupted file
+2015-09-17 changed LIST output formating (more consistent)
+2015-09-17 fexget: fixed bug cannot forward a file that was received from myself
+2015-09-16 fexsend: more robust fileID (md5sum of metadata)
+2015-09-14 fup: show autodelete=no if sender == recipient
+2015-09-11 moved comment to top in notification email
+2015-09-08 fup,fuc: fixed bug link to F*EX clients tools.html broken (loop)
+2015-09-06 fexsend: added ditto-zip for MacOSX
+2015-09-02 fexsend: added MacOSX support
+2015-09-01 fac: added option -Rl for local users
+2015-09-01 fup: added local users restriction option
 2015-08-26 fur: fixed bug no registration possible
 2015-08-25 fup: fixed bug uninitialized value when called by sup.html
-           fac: option -q quota=0 means use default quota
+2015-08-25 fac: option -q quota=0 means use default quota
 2015-08-24 better detection of UTF8 in comment
 2015-08-14 fixed bug "Wide character in print at (...)/fex.pp" in function pq()
 2015-07-29 install: fixed various bugs
 2015-07-15 dop: symbolic links generate a HTTP 302 (temporarily redirection) 
                 instead of a HTTP 301 (permanently redirection) response
 2015-06-16 fexsend: fixed bug hangs with https
-           new fex.ph config variable @mailing_lists
+2015-06-16 new fex.ph config variable @mailing_lists
 2015-06-15 fup: always display fur link, if @local_domains is defined
 2015-06-10 fexsrv: fixed warning with https and SIGCHLD
 2015-05-16 fexsrv/dop: added active and passive redirect support
@@ -24,7 +52,7 @@
 2015-03-08 fup: fixed bug uninitialized value $address if alias address is 
                 used twice
 2015-03-07 disallow email addresses starting with "-"
-           fex_cleanup: do not terminate on sendmail error
+2015-03-07 fex_cleanup: do not terminate on sendmail error
 2015-03-01 no file name in email subject if notification is encrypted
 2015-02-28 fexsrv: restrict HTTP header to 64 kB ($bs) and POST (not fup) to 
                    128 MB
 2015-02-18 fuc: fixed bug no gpg usage help
 2015-02-17 fexsend: check SSLeay version and adjust SSL_verify_mode
 2015-02-16 fup: save upload URL in spool
-           in notification+reminder emails use same protocol for download URL
+2015-02-16 in notification+reminder emails use same protocol for download URL
            like in upload
 2015-02-08 rup: fixed various bugs (not working at all)
 2015-02-05 fup: fixed bug cannot send to groups 
 2015-01-27 fup: set autodelete=no if sender == recipient
                 (use case: provide download link for mailing lists)
-           new fex.ph config variable $fex_yourself (default yes)
+2015-01-27 new fex.ph config variable $fex_yourself (default yes)
 2015-01-25 fexsend: fixed bug cannot forward a file name with "&"
 2015-01-21 main user is always first member of a new group
-           substituted CGI::Carp with web error handler via PERLINIT environment
+2015-01-21 substituted CGI::Carp with web error handler via PERLINIT environment
 2015-01-17 new fex.ph config variable $mail_authid (default yes)
 2015-01-16 fixed bug no notfication for still existing file (overwrite)
 2015-01-15 fixed bug no locale reminder notfication
-           fixed bug wrong result for recipients with NOTIFICATION=no
+2015-01-15 fixed bug wrong result for recipients with NOTIFICATION=no
 2015-01-13 fexsend: added option -N resend notification email
-           resending notification email deletes download ip restriction
-           fup: fixed bug sending to groups broken
+2015-01-13 resending notification email deletes download ip restriction
+2015-01-13 fup: fixed bug sending to groups broken
 2015-01-10 fexsend: added option -S show server/user settings
-           fup: added command LISTSETTINGS
+2015-01-10 fup: added command LISTSETTINGS
 2015-01-09 foc: added save-or-display (MIME) option for download
 2015-01-04 fexsend: fixed bug dies too early on multiple files and one
                     file has been already transfered
                                    DEFAULT:!3DES:!MD5
 2014-12-24 fexget,fexsend,sexsend: evaluate environment variables SSLVERIFY
                                    SSLVERSION SSLCAPATH SSLCAFILE SSLCIPHERLIST
-           fexget,fexsend,sexsend: use TLS, not SSL
+2014-12-24 fexget,fexsend,sexsend: use TLS, not SSL
 2014-12-23 fexsend: $HOME/.fex/config with $opt_* and %alias variables
-           fexget: $HOME/.fex/config with $opt_* and %autoview variables
+2014-12-23 fexget: $HOME/.fex/config with $opt_* and %autoview variables
 2014-12-19 fur: fixed bug race condition with fex_cleanup (external->internal)
 2014-12-17 install/update: fixed bug some spool files are owned by user root
 2014-12-16 fexsrv: fixed bug handling of User-Agent FDM
 2014-12-09 added l ll lf to distribution
-           fexwall: also mail to sub and group users
+2014-12-09 fexwall: also mail to sub and group users
 2014-12-03 fup: remove file after upload if restricted user has set NOMAIL
-           fup: fixed bug wrong message "user notified" if NOMAIL
+2014-12-03 fup: fixed bug wrong message "user notified" if NOMAIL
 2014-12-02 fup: also check recipient restrictions on command CHECKRECIPIENT
 2014-11-24 fexget: autoview gif jpg png tif after download
 2014-11-20 count unfinished upload size into quota, too
-           fixed bug wrong quota calculation on SysV UNIX like Solaris
+2014-11-20 fixed bug wrong quota calculation on SysV UNIX like Solaris
 2014-11-18 fexsend: added environment variables SSLVERIFY SSLCAPATH SSLCAFILE
 2014-11-18 dop: added HTTP basic authentication for htdoc directory with 
                 .htauth file
 2014-11-14 ignore @forbidden_recipients if $SPOOL/$USER exists
            (admin has created user)
 2014-11-11 fup: fixed bug groups from other users in address book selection
-           fup: added useragent to $SPOOL/$TO/$FROM/$FILE/
+2014-11-11 fup: added useragent to $SPOOL/$TO/$FROM/$FILE/
 2014-11-10 fup: present locales in recipient query form, too
 2014-11-07 FAQ: added text anchor URLs
 2014-11-03 added missing fexget fexsend sexget sexsend for tools.html
 2014-09-01 fup: upload status bar waits longer, until $timeout
 2014-08-27 fex_cleanup: use wget for new release dedection
 2014-08-18 fexsend: workaround for stunnel bug (options -s and -g)
-           fex_cleanup,fexsend: always restrict permissions on fexsend id-file
+2014-08-18 fex_cleanup,fexsend: always restrict permissions on fexsend id-file
 2014-08-16 fac: added options -P and -E (more examples)
 2014-08-15 install: fixed bug wrong owner in spool
-           fex_cleanup: do not terminate on error, but print warning
+2014-08-15 fex_cleanup: do not terminate on error, but print warning
 2014-08-13 fexsrv,fexsend,fexget: reenabled IPv6 support
 2014-08-10 fac: added option -/ to set new admin
 2014-08-08 moved $admin_pw from fex.ph to auth-ID for user $admin
-           fex_cleanup: fixed bug in notify_newrelease
-           fac(CGI): switched from HTTP basic authorization to auth-ID/akey
-           fexsend: always use CHECKRECIPIENT, not only for aliases
+2014-08-08 fex_cleanup: fixed bug in notify_newrelease
+2014-08-08 fac(CGI): switched from HTTP basic authorization to auth-ID/akey
+2014-08-08 fexsend: always use CHECKRECIPIENT, not only for aliases
 2014-08-07 dop: generate on-the-fly gzipped documents if requested
 2014-08-06 fixed bug install script dysfunctional (permission, hostname)
 2014-07-25 reenabled vhost support
 2013-09-23 fexsend: fixed bug option -c sends uncompressed file
 2013-09-19 sub and group users have the same quota amount like their main user
 2013-09-18 fixed bug permission denied for locale htdocs
-           anonymous user now with hostname domain instead of mail domain
+2013-09-18 anonymous user now with hostname domain instead of mail domain
 2013-09-17 dop: set locale cookie, too
 2013-09-15 foc: added "Change the disclaimer" option
 2013-09-12 fup: expand domainless address with server hostname if such a user 
                 exists (needed for fbm/nettest)
 2013-09-09 new FAQ design (questions first, then Q+A)
 2013-09-04 fac: modify $hostname when vhost fex.ph is created
-           fuc: recognize comment=NOMAIL
+2013-09-04 fuc: recognize comment=NOMAIL
 2013-08-26 fexsend: always show download URL if recipient is "."
 2013-08-20 fup,fop: fixed bug no DELETE and RESUME for fexmail and anonymous 
                     users (because of storage swap)
 2013-08-18 sexsend: fixed bug data corruption when using https
-           fop,fup: fixed bug bad file locking when using multiple recipients
-           fexsend: fixed bug hangs on server error when sending archive
+2013-08-18 fop,fup: fixed bug bad file locking when using multiple recipients
+2013-08-18 fexsend: fixed bug hangs on server error when sending archive
 2013-08-17 fixed bug $sender_from ignored
-           fup,foc: added notification email resending on user request
+2013-08-17 fup,foc: added notification email resending on user request
 2013-08-16 fac: fixed bug wrong output order for option -l
 2013-08-14 fop: fixed bug no multiple downloads for fexmail
-           fac: added option -M for resending notification emails
+2013-08-14 fac: added option -M for resending notification emails
 2013-08-09 afex: fixed bug ID for input
 2013-08-06 fexsrv: always includes "Server: fexsrv" in HTTP reply
            fexsend: terminates if no fexsrv HTTP reply 
 2013-07-18 fup,fexsend: use header Content-Location instead of Content-Type for
                         file linking
 2013-07-15 fup: modifying keep references actual time, not upload date
-           fexsend: fixed bug no feedback on option -x -k (modify keep)
+2013-07-15 fexsend: fixed bug no feedback on option -x -k (modify keep)
 2013-07-13 fup: fixed bug user specific keep and autodelete defaults are ignored
 2013-07-12 fup,fop: added file link support
-           fexsend: added option -/ for file linking
+2013-07-12 fexsend: added option -/ for file linking
 2013-07-09 fexget: added option -P proxy:port
 2013-06-28 new all-in-one FAQ
 2013-06-27 fup: to/from storage swap for fexmail and anonymous users
-           fup: anonymous recipient with random number
+2013-06-27 fup: anonymous recipient with random number
 2013-06-26 fop: allow multiple downloads from same ip
-           fup,fac: extended "fex yourself" support
-           added sup.html
+2013-06-26 fup,fac: extended "fex yourself" support
+2013-06-26 added sup.html
 2013-06-22 fop,fexsend: Location output for fexmail for already transfered files
 2013-06-19 fexget: fixed bug cannot download MIME file
 2013-06-15 fex.ph: new config variable $notify_newrelease checks
                 (with hooks in fop,fop.fuc,foc,fur)
 2013-06-03 fuc: fixed bug user can modify his auth-ID to an illegal value
 2013-05-30 install: force creation of $admin_pw
-           fex_cleanup: fixed bug wrong fexadmin fexid for reactivation emails
+2013-05-30 fex_cleanup: fixed bug wrong fexadmin fexid for reactivation emails
 2013-05-25 added afex and asex to distribution
 2013-05-23 dop: fixed bug no output on file.stream 
 2013-05-22 install script installs as user fex (and not as root)
 2013-05-19 security patch: config variable @local_rhosts restricts download of
                            files from new external users to specific ip ranges
-           use <from> and <to> syntax in notification email header
-           added support for $max_fail_handler()
+2013-05-19 use <from> and <to> syntax in notification email header
+2013-05-19 added support for $max_fail_handler()
 2013-05-18 fac: fixed bug option -rd does not work
-           fac: added option -R
+2013-05-18 fac: added option -R
 2013-05-16 fop: fexmail support (multiple downloads allowed)
 2013-05-09 fup: fixed bug anonymous only works if $mdomain is defined
-           fop: fixed bug anonymous only works if recipient host is in
+2013-05-09 fop: fixed bug anonymous only works if recipient host is in
                 @anonymous_upload list 
 2013-05-07 fup: fixed bug multiple Location HTTP headers generate an error with
                 some web browsers 
 2013-04-05 fexsend: fixed bug server timeout when sending huge ZIPs
 2013-02-20 fac: added -m maintenance mode
 2013-02-17 fup: fixed bug stored comment in spool not in UTF8
-           fop: fixed bug file deletion also deletes fop.log
+2013-02-17 fop: fixed bug file deletion also deletes fop.log
 2013-02-16 fur,fex.ph: allow "*" for local domains self registration
-           fex.ph: new config variable @registration_hosts
-           fex.ph: new config variable @admin_hosts
+2013-02-16 fex.ph: new config variable @registration_hosts
+                   new config variable @admin_hosts
 2013-02-11 fur: fixed bug insecure dependency with exuser
 2013-01-31 receiving of reminder emails is user configurable
 2013-01-24 fup: decode UTF8 comment
 2012-11-08 fop: fixed bug cannot use "?" in file name with fexsend
 2012-11-07 fixed security bug restricted user can redirect files
 2012-11-06 fup: show download-URL after upload if sender = recipient
-           fup,fop,fac: added user up/download IP restriction by admin
+2012-11-06 fup,fop,fac: added user up/download IP restriction by admin
 2012-11-05 added HTTP Strict Transport Security (HSTS) if $force_https is set
-           fixed bug afex accessible via xkey from everywhere
+2012-11-05 fixed bug afex accessible via xkey from everywhere
 2012-11-02 fup: fixed bug one time upload URL gives "no recipient specified" 
                 error
 2012-11-01 fup: fixed bug public upload always gives error
 2012-10-16 fop,fup: added afex support
-           fup: accept recipients . and //
+2012-10-16 fup: accept recipients . and //
 2012-10-15 sex,sexsend: added anonymous mode (no auth-ID necessary)
 2012-10-14 fop,fup: added support for fexsend anonymous mode
 2012-10-11 fex.ph: added optional config variable $overwrite
 2012-10-10 fup: anonymous upload with non-anonymous recipient
 2012-09-30 fup: fixed bug groups not working any more (NOMAIL)
 2012-09-19 fup: logout functions respects login CGI (or symlink)
-           foc: detailed/brief notification mail configuration
+2012-09-19 foc: detailed/brief notification mail configuration
 2012-09-18 fexsend: added options -. and -n
-           fup: added shortmail option in comment
+2012-09-18 fup: added shortmail option in comment
 2012-09-17 added mailmode configuration option in fex.ph
-           fup: shows download-URL if NOMAIL
+2012-09-17 fup: shows download-URL if NOMAIL
 2012-09-15 sex: public URL parameter may be in base64 format, too
 2012-09-10 dop: added more security checks
 2012-09-01 dop: added streaming document output
 2012-08-30 sex: fixed bug second receiving client corrupts the stream
 2012-08-26 Changed licence from AGPL to Perl Artistic
 2012-08-21 schwuppdiwupp: error handling on network failures for Windows
-           schwuppdiwupp: removed Tk::FileSelect for Windows
+                          removed Tk::FileSelect for Windows
 2012-07-11 fop: fixed bug no multiple downloads for anonymous uploads
 2012-07-10 fixed French, Spanish and Czech localization (code syntax) bugs
 2012-07-09 fup: anonymous upload with modifyable keep option and multiple
 2012-07-05 fexsrv: added camel easteregg
 2012-07-02 fup: added optional anonymous upload with fex.ph variable
                 @anonymous_upload 
-           fup: fixed bug throttle 0 configuration is ignored
+2012-07-02 fup: fixed bug throttle 0 configuration is ignored
 2012-07-01 fexsend: optional argument '@' as files/recipients separator
-           fexsend: fixed bug notification email for recipient '.'
+                    fixed bug notification email for recipient '.'
 2012-06-21 dop: inside HTML documents: #include "file" 
 2012-06-06 fexget: new option -+
 2012-06-05 fexsend: new option -+
 2012-06-01 fup: show transfered size instead of total size in status window
 2012-05-04 fexsrv: added bunny easteregg
 2012-05-02 fexsrv: allow HTTP 1.0 with Range header (wget!)
-           fexsrv: disallow negative value in Range (client signed int bug)
+                   disallow negative value in Range (client signed int bug)
 2012-04-26 Changed licence from GPL to AGPL
 2012-04-07 foc: check new address book for syntax errors
 2012-04-06 foc: added comment field for new subuser information
 2012-04-04 removed F*IX because of too many bugs and no maintainer any more
 2012-03-05 fup: fixed bug shell wildcards in recipient address are expanded to
                 known users from spool
-           fex.ph: added optional config variable @locales
+2012-03-05 fex.ph: added optional config variable @locales
 2012-03-01 dop: delivers MIME type text/plain if "?!" is appended to URL
 2012-02-20 foc: show auth-ID after click on link
 2012-02-07 fop: MIME-type text/html is no longer possible for security reasons
 2012-02-04 added optional french localization
 2012-02-03 fixed bug 0.0.0.0 not recognized as ip address
-          fex.ph: added optional config variable $keep_max
+2012-02-03 fex.ph: added optional config variable $keep_max
 2012-02-02 HTTP parameter filtering to prevent cross-site scripting attacks
 2012-02-01 config variable @throttle may also contains ip addresses
 2012-01-25 pup: locale selection in native language, default autodelete=no
-           fup: "send another file" with same keep and autodelete parameters
+2012-01-25 fup: "send another file" with same keep and autodelete parameters
 2012-01-17 fixed bug reactivation.txt in czech instead english
 2012-01-06 fup: fixed bug show wrong remaining keep days
 2012-01-02 fup.pl: fixed bug bad FAQ link
 2011-09-07 fac(CGI): fixed bug infinitve loop in watch logfile
 2011-09-06 fup,fac,fur: new additional login URL type: 
                         http://FEXSERVER/fup/B64ID
-           fup: show "or select from address book" only if there are entries
+2011-09-06 fup: show "or select from address book" only if there are entries
 2011-09-05 fexsrv: fixed bug locale cookie not fetched on http://cgi?parameter
 2011-09-01 rup: fixed bug cannot find files (no more SID in akeys directory)
 2011-08-30 fex.ph: new config variable @forbidden_recipients
 2011-08-29 fexsend: accept file number for delete option -d, too
-           dop: fixed bug no text document output if external file command is 
+2011-08-29 dop: fixed bug no text document output if external file command is 
                 non-GNU
 2011-08-26 added one time upload OKEY
 2011-08-13 fex_cleanup: fixed bug comment missing in reminder email
 2011-08-11 dop: #if ... #else ... #elseif ... #endif inside HTML documents
-           dop: show HTML sourcecode if "!" is appended to URL
+                show HTML sourcecode if "!" is appended to URL
 2011-08-10 fex_cleanup: delete obsolete users, too, via fex.ph $account_expire
            new FAQ design (with Javascript/CSS)
 2011-08-09 fup: show error on invalid SKEY or GKEY
-           fuc: fixed bug subuser and groupuser not lowercase forced
-           address book may also contain option locale=<languange>
+2011-08-09 fuc: fixed bug subuser and groupuser not lowercase forced
+2011-08-09 address book may also contain option locale=<languange>
 2011-08-08 fex_cleanup: auto-expire user accounts with fex.ph variable 
                         $account_expire
-           fexsend,fup: allow forward with locale
-           dop: extra security check: files from lib and spool are not allowed
+2011-08-08 fexsend,fup: allow forward with locale
+2011-08-08 dop: extra security check: files from lib and spool are not allowed
 2011-08-07 fup: subusers and groupusers can also select a locale
-           fup: if user selects a locale login, save it as default locale
+2011-08-07 fup: if user selects a locale login, save it as default locale
                 (does not affect fexsend and schwuppdiwupp)
-           notification emails come in default locale
+2011-08-07 notification emails come in default locale
 2011-08-03 fexsend: fixed bug uninitialized value when using chunked mode
-           fexsend: added undocumented option -F female mode
+                    added undocumented option -F female mode
 2011-07-31 fexsend: added option -s streaming data
-           fup: accept streaming data
+2011-07-31 fup: accept streaming data
 2011-07-30 fexsend: -a tar archives no longer use a intermediate transferfile,
                     but send via pipe (streaming)
-           fexsend: fixed bug no resume on -a archives
-           fexsend: always ask server if file already has been uploaded
-           fup: more information on F*EX clients download and configuration
+2011-07-30 fexsend: fixed bug no resume on -a archives
+2011-07-30 fexsend: always ask server if file already has been uploaded
+2011-07-30 fup: more information on F*EX clients download and configuration
 2011-07-27 fup: if comment contains "!bcc!" then sender will get a bcc 
                 of notification email
 2011-07-26 fexget: added option -X do not extract archive file
-           fexget: added option -a get all files
-           fexsrv: fixed bug uninitialized value when using a reverse proxy
-           fex_cleanup: fixed bug notify reminder email not localized
+2011-07-26 fexget: added option -a get all files
+2011-07-26 fexsrv: fixed bug uninitialized value when using a reverse proxy
+2011-07-26 fex_cleanup: fixed bug notify reminder email not localized
 2011-07-22 fac(CGI): fixed bug displaying < and & in logfiles
-           fac(CGI): added getting error.log
-           fop: allow multiple downloads from any client if sender = recipient
+2011-07-22 fac(CGI): added getting error.log
+2011-07-22 fop: allow multiple downloads from any client if sender = recipient
 2011-07-16 added doc/reverse_proxy 
 2011-07-14 added optional czech localization
 2011-07-01 FAQ.html reformated
 2011-06-17 fixed bug $bcc is ignored
 2011-06-16 fexsend,fexget: better reverse proxy support 
                            (always send Host header)
-           added optional galician localization
+2011-06-16 added optional galician localization
 2011-06-15 fup: fixed bug always keep_default days in notification email
 2011-06-14 fexsend: transparent proxy detection (and support)
-           fixed bug $docdir ignored
+2011-06-14 fixed bug $docdir ignored
 2011-06-10 fex.ph: new config variable $bcc for notification emails
 2011-06-09 set Reply-To in all notification emails
-           fup: fixed security bug everyone can upload files with empty auth-ID
+2011-06-09 fup: fixed security bug everyone can upload files with empty auth-ID
 2011-06-05 fup: fixed bug insecure dependency in printf on "forward file"
 2011-06-03 fup,fop: added throttle bandwith limit option (fex.ph)
 2011-06-02 fup: added bandwith limit option
 2011-06-01 added PID and request-number to the logs
-           fex_cleanup: fixed bug no expire with AUTODELETE=NO
+2011-06-01 fex_cleanup: fixed bug no expire with AUTODELETE=NO
 2011-05-31 support for FEXLIB /usr/local/share/fex/lib /usr/share/fex/lib
 2011-05-30 fup,fexsend: added option -x to modify file parameters
 2011-05-29 fup,fexsend: forward files with new comment and keep time
-           rup: add mdomain to addresses without domain
+2011-05-29 rup: add mdomain to addresses without domain
 2011-05-18 fup: fixed bug restricted users can forward files to anybody
 2011-05-17 fixed bug access problems with AKEYs: now use SID instead of SIP
 2011-05-11 added helper script mksgkeys (regenerates missing SKEYs and GKEYs)
 2011-05-10 fex_cleanup: cleanup ADDRESS_BOOK file upload
-           fex.ph,fex_cleanup: AUTODELETE=NUMBER ==> 
+2011-05-10 fex.ph,fex_cleanup: AUTODELETE=NUMBER ==> 
                                delete file on next NUMBER day after download
 2011-05-09 fac,fup: added user specific autodelete default
-           fac,fex_cleanup: added user specific keep default
+2011-05-09 fac,fex_cleanup: added user specific keep default
 2011-04-27 fexsend: fixed bug archiv.zip not working on Windows
-           dop: added index function for htdoc directory with .htindex file
+2011-04-27 dop: added index function for htdoc directory with .htindex file
 2011-04-25 fexsend: better proxy support (non-persistent connections)
 2011-04-24 xx: better ESXi support (heuristic guessing of tar format)
 2011-04-22 sexsend: base64 support for $FEXID and $FEXXX
 2011-02-24 dop: evaluate <<perl-code>> inside html documents
 2011-02-21 fexsend,fup: added option fexsend -U show authorized (login) URL
 2011-02-18 do not modify download URL protocol if $dkey is set in fex.ph
-           fac(CGI): fixed bug uninitialized value $server
-           URLs in notification emails are derived from config variable $durl
+2011-02-18 fac(CGI): fixed bug uninitialized value $server
+2011-02-18 URLs in notification emails are derived from config variable $durl
 2011-02-17 fup: fixed bug access denied with SKEY
 2011-02-08 fup,fop,fuc: fixed bug access problems with sip in AKEYs
-           fup: fixed bug no notification email for multiple recipients
+2011-02-08 fup: fixed bug no notification email for multiple recipients
 2011-02-07 fexsend,fop: do not send same file (filename and mtime) twice
 2011-02-06 fup: fixed bug no notification email after first failed upload
 2011-01-31 schwuppdiwupp: added ISO-8859-1 support
 2011-01-30 schwuppdiwupp: added running camel
 2011-01-28 schwuppdiwupp: added chunksize to proxy options
-           schwuppdiwupp: fixed bug timeout when using address book
-           fexsend: fixed bug chunksize 0
+2011-01-28 schwuppdiwupp: fixed bug timeout when using address book
+2011-01-28 fexsend: fixed bug chunksize 0
 2011-01-27 schwuppdiwupp: added advanced preferences Proxy and TMPDIR
 2011-01-26 fex_cleanup: fixed bug uninitialized value in debuglog
-           fex_cleanup: added option -v
-           fexsend,fexget,sexsend: added option -V show version
-           schwuppdiwupp: added drag&drop support for windows
-           schwuppdiwupp: added 7zG support
+2011-01-26 fex_cleanup: added option -v
+2011-01-26 fexsend,fexget,sexsend: added option -V show version
+2011-01-26 schwuppdiwupp: added drag&drop support for windows
+2011-01-26 schwuppdiwupp: added 7zG support
 2011-01-25 fuc: fixed bug cannot delete all subusers
-           schwuppdiwupp: fixed bug 7-zip not found
-           schwuppdiwupp: added drive letters in directory selection
+2011-01-25 schwuppdiwupp: fixed bug 7-zip not found
+2011-01-25 schwuppdiwupp: added drive letters in directory selection
 2011-01-24 fop: IE bug workaround to store *.exe files
 2011-01-18 schwuppdiwupp: added tar, zip and 7z container
 2011-01-17 fexsend: fixed bug option -l not working with https URL
 2011-01-16 fup: added 7zip hint in notification emails
 2011-01-13 schwuppdiwupp: fixed bug no transfer at all when comment is set
 2011-01-12 rup: added logging
-           rup: wrong recipient cannot download file, but will get an error
-           fac(CGI): fixed bug $server not declared
+2011-01-12 rup: wrong recipient cannot download file, but will get an error
+2011-01-12 fac(CGI): fixed bug $server not declared
 2011-01-07 fexget: keep file permission in overwrite mode
 2011-01-04 fex_cleanup: fixed bug autodelete after partial download
 2010-12-26 fex_cleanup: fixed bug too early expire for forwarded files
 2010-12-10 new config variable @public_recipients for new CGI pup 
-           (public upload) - upload without auth-ID
+2010-12-10 (public upload) - upload without auth-ID
 2010-12-09 fex.ph,fur: new config variable @local_rdomain for self-
                        registration of restricted external-to-internal users
 2010-12-08 fup,foc: no access to foc for restricted users
 2010-12-02 fup: if there is a cgi-bin/login it will be called on "logout"
 2010-11-24 fex.ph: new config variable $default_locale
-           dop: auto-expires every document (to prevent browser caching)
+2010-11-24 dop: auto-expires every document (to prevent browser caching)
 2010-11-16 fexsrv: better error handling if CGI is not executable
 2010-11-09 new SKEYs and GKEYs, because old ones could be not unique
            access for subuser only with SKEY
            access for groupuser only with GKEY
 2010-11-07 fup,fuc: added GKEY
-           fexsend: SKEY or GKEY URLs can be recipients, too
+2010-11-07 fexsend: SKEY or GKEY URLs can be recipients, too
 2010-11-04 fexget: added (hidden) option -K
 2010-11-03 fexsend: fixed bug proxy usage failed
 2010-11-02 fop: fixed bug corrupted download with Internet Explorer 
 2010-10-25 fop,fup: better locking: no uploading is possible while a
                     download is in progress for the same file
 2010-10-24 fix,fop: fixed bug subuser not working (SKEY problem)
-           xx: added locking
+2010-10-24 xx: added locking
 2010-10-23 xx,fop: added xx :slot option (multiple storage slots)
 2010-10-20 fup,fop,fexsend: fexsend for subuser with SKEY
 2010-10-19 fup: expires *KEY cookies on logout
 2010-09-27 fup: fixed bug missing Content-Type in upload status report
 2010-09-20 fexsrv: IPv6 http support
 2010-09-15 fac(CGI): fixed bug cannot delete and (re)create user
-           fac(CGI): fixed bug cannot create user who was a recipient
+2010-09-15 fac(CGI): fixed bug cannot create user who was a recipient
 2010-09-12 fexsend,fop: fixed bug resuming upload does not work with alias
-           fup: fixed bug resuming upload handles autodelete and keep
+2010-09-12 fup: fixed bug resuming upload handles autodelete and keep
                 parameters incorrectly
 2010-09-07 fac(CGI): fixed bug not working if there is no user at all
 2010-09-05 fop: extra parameter keep=days
 2010-08-31 install: fixed bug wrong ownership for spool files
 2010-08-25 perl 5.8 required
-           fexget: -s can write to named pipe or character special file
+2010-08-25 fexget: -s can write to named pipe or character special file
 2010-08-21 fop: $limited_download checks dkey cookie instead of client IP
 2009-08-20 removed mma
 2010-08-18 fex_cleanup: fixed bug not expiring
 2010-08-17 fac: fixed bug accept users without domain
-           install: fixed bug empty $admin_pw
+2010-08-17 install: fixed bug empty $admin_pw
 2010-08-15 fex.ph: optional fix address $sender_from (instead of F*EX user) in
                    notification email From 
 2010-08-14 added optional spanish localization
 2010-08-12 fup: speedup 90%
-           fop: speedup 20%
-           fop: better fexget compatibility 
+2010-08-12 fop: speedup 20%
+2010-08-12 fop: better fexget compatibility 
                 (close connection after file delivery)
 2010-08-11 fop: fixed IE download bug (missing header separating line)
-           fop: fixed 1 min delay bug on AUTODELETE=YES
+2010-08-11 fop: fixed 1 min delay bug on AUTODELETE=YES
 2010-08-08 sex: support for compressed streams
-           sex,sexsend: removed unneccesary text mode (option -t)
-           sex,sexsend: speedup factor 5
-           added sexxx
+2010-08-08 sex,sexsend: removed unneccesary text mode (option -t)
+2010-08-08 sex,sexsend: speedup factor 5
+2010-08-08 added sexxx
 2010-08-06 sex: fixed various bugs in client and server
-           fac(CGI): fixed bug AKEY not working
+2010-08-06 fac(CGI): fixed bug AKEY not working
 2010-08-03 xx: no user inquiry for postprocessing if output is a pipe
 2010-08-02 added optional german localization
 2010-07-31 separated subusers in extra file $SPOOL/$USER/@SUBUSER
 2010-07-25 fop: log also aborted downloads
 2010-07-23 added fac CGI
 2010-07-18 fexsrv,fup,fexsend: extra XKEY download with short // URL
-           fexsend: fixed bug CHECKRECIPIENT not working
+2010-07-18 fexsend: fixed bug CHECKRECIPIENT not working
 2010-07-16 added cookie support (for AKEY and SKEY)
-           fup: fixed bug showstatus window too small for close button
+2010-07-16 fup: fixed bug showstatus window too small for close button
 2010-07-13 schwuppdiwupp: added CHECKRECIPIENT
 2010-07-12 fop,xx: allow several concurrent downloads of STDFEX
 2010-07-10 fop: workaround for stupid IE download bug
 2010-07-01 fexsrv,fex.ph: new config variable $force_https
 2010-06-29 fop: new config variable $limited_download with default NO 
                 => allow multiple downloads through proxy farm (varying IPs)
-           fop: note every successful download in spool file "download"
+2010-06-29 fop: note every successful download in spool file "download"
 2010-06-25 fexget: fixed bug download status info update too often
 2010-06-23 fur: better sendmail clone compatibility: 
                 use space instead of comma as address separator
 2010-06-19 fexget: new option -o overwrite mode
-           fexget: use ./$file.tmp for downloading instead of $HOME/.fex/tmp/
+2010-06-19 fexget: use ./$file.tmp for downloading instead of $HOME/.fex/tmp/
 2010-06-16 schwuppdiwupp: edit and select address book entries
 2010-06-15 rup: fixed bug case sensitive recipient address
 2010-06-12 fop: send X-File-ID on HEAD request, too
-           fexget: added support of X-File-ID
+2010-06-12 fexget: added support of X-File-ID
 2010-06-11 schwuppdiwupp: (chunked) multi-POST for proxy with 4 GB limit
-           schwuppdiwupp: X-File-ID support
+2010-06-11 schwuppdiwupp: X-File-ID support
 2010-06-08 fup,fexsend: (chunked) multi-POST for proxy with 4 GB limit
 2010-06-06 fup,fop,fexsend: protocol extension X-File-ID (contains mtime
                             of file) is the successor of X-Size for more 
 2010-05-27 fexsend: added option -b bounce (copy-forward)
 2010-05-26 fup,foc: added copy-forward feature
 2010-05-20 fexsend: fixed bug uninitialized value with option -@
-           fur: fixed bug $main::admin not declared
+2010-05-20 fur: fixed bug $main::admin not declared
 2010-05-17 fexsend: added option -H for hints
-           fexsend: added option -A for edit server address book
+2010-05-17 fexsend: added option -A for edit server address book
 2010-05-16 fexsend: added HTTPS proxy support
-           fup: fixed bug uninitialized value (line 1059)
+2010-05-16 fup: fixed bug uninitialized value (line 1059)
 2010-05-13 fup: fixed bug ignored KEEP and AUTODELETE options for groups
 2010-05-12 fup: fixed bug ignored autodelete option from ADDRESSBOOK
 2010-04-30 fup: fixed bug uninitialized value with CHECKRECIPIENT
-           fexsend: no SID for https
+2010-04-30 fexsend: no SID for https
 2010-04-28 fup: fixed bug case sensitiv group addresses
 2010-04-27 fexsend: fixed bug ignored server address book options
-           fexsend: displays recipients and options before starting post
-           fup: fixed bug ignored server address book autodelete option
+2010-04-27 fexsend: displays recipients and options before starting post
+2010-04-27 fup: fixed bug ignored server address book autodelete option
 2010-04-26 fexsrv: log all HTTP headers (no more ignore list)
 2010-04-25 fexsrv: accept HTTP header with continuation lines
 2010-04-22 fex.pp: added htdocs/header.html support
-           fex.ph: added variable @H1_extra organization link and logo
+2010-04-22 fex.ph: added variable @H1_extra organization link and logo
 2010-04-20 fexsrv,fexsend: HTTP header X-Timeout (info server->client)
-           fexsrv: logging with locking
+2010-04-20 fexsrv: logging with locking
 2010-04-19 fexsend: removed broken option -A and replaced it with more
                     flexible feature "." for recipient address
-           fexsend: fixed bug dies if sender is subuser (ADDRESS_BOOK error)
-           fup: fixed bug no COMMENT in notification email
-           dop: fixed bug error output with non GNU file command
+2010-04-19 fexsend: fixed bug dies if sender is subuser (ADDRESS_BOOK error)
+2010-04-19 fup: fixed bug no COMMENT in notification email
+2010-04-19 dop: fixed bug error output with non GNU file command
 2010-04-17 fexsend,fexget: added option -i for alternative accounts or servers
 2010-04-12 fexsend: new verbose output format --> <--
 2010-04-11 fexsend: added option -Q quota query
 2010-04-09 fup,fac: added quota support
 2010-03-25 fup: fixed bug "Insecure dependency" when using AKEY parameter
                 (eg: sending a second file)
-           fup_template.html: fixed bug upload status window always shows
+2010-03-25 fup_template.html: fixed bug upload status window always shows
                               "ERROR: no file data received"
 2010-03-24 fexsend: fixed bug dies if there is no server address book
 2010-03-22 FIX.jar: fixed bug interpret HTTP response "200 OK" as error.
 2010-03-20 fup,fop: set mtime on user directory for last successfull access
 2010-03-19 fexsend: fixed bug abort on short address if there is no server
                     address book
-           fex_cleanup: better cleanup for dkeys directory
+2010-03-19 fex_cleanup: better cleanup for dkeys directory
 2010-03-18 fup: fixed bug cannot DELETE with group recipient
 2010-03-17 fup: fixed bug wrong success message on aborted uploads
-           fop: fixed bug cannot handle @group names
+2010-03-17 fop: fixed bug cannot handle @group names
 2010-03-16 fup,fuc: fixed bug mixed case in F*EX group names and addresses
 2010-03-14 fex.pp: do not send notification emails on empty files
-          fup,fuc: added F*EX groups
+2010-03-14    fup,fuc: added F*EX groups
 2010-03-12 fup,fop: fixed bug case sensitiv FROM and TO addresses
 2010-03-05 fup: fixed bug aliases are not accepted with fop_auth
 2010-03-04 fexsrv: use CGI login if it exists as start-page
 2010-02-26 fexsend: first check server address book, then mutt aliases
-           fop: do not terminate session after ADDRESSBOOK request
+2010-02-26 fop: do not terminate session after ADDRESSBOOK request
 2010-02-18 fexget: fixed bug always append existing file, ask for overwriting
 2010-02-08 fexsrv: fixed bug uninitialized value in substitution (line 229)
-           fex.pp: better qmail compatibility (space separated addresses)
+2010-02-08 fex.pp: better qmail compatibility (space separated addresses)
 2010-02-07 fac: fixed bug uninitialized $EDITOR environment variable
 2009-12-28 fup,fop,fexsend: protocol extension X-Size for more reliable
                             resume function (checks size of file)
 2009-08-12 fup: fixed bug "send another file" for subusers
 2009-08-10 fexsend: fixed bug timeout on big archives
 2009-07-27 to and from addresses in spool are now always localpart@domain,
-           install contains automatic spool converter
-           fup: fixed bug short aliases address list mismatch
+2009-07-27 install contains automatic spool converter
+2009-07-27 fup: fixed bug short aliases address list mismatch
 2009-07-24 fup,fex_cleanup: fixed bug delete all files for multiple
                             recipients after any download
-           fexget: fixed bug delete local file before download
+2009-07-24 fexget: fixed bug delete local file before download
 2009-07-20 fup: added autodelete and keep hack for HTML form
 2009-07-18 fup: fixed bug leading . in file directory name
-           fup: added CHECKRECIPIENT support
-           fup: code cleanup, new 3-stage user interface
-           fexsend: added CHECKRECIPIENT feature
+2009-07-18 fup: added CHECKRECIPIENT support
+2009-07-18 fup: code cleanup, new 3-stage user interface
+2009-07-18 fexsend: added CHECKRECIPIENT feature
 2009-07-17 fexget: fixed bug wrong UTF8 handling
 2009-07-16 fop: fixed bug sending wrong file size if TO or FROM has
                 uppercase chars ==> resuming upload did not work
 2009-07-11 fup: can select more than one address from address book
 2009-07-08 fup,fex.pp: fixed bug wrong download URLs
 2009-07-07 new spool directory layout $TO/$FROM/urlencode($FILENAME)
-           fup: be more restrictive in accepting (illegal) parameters values
-           fup,fuc: subuser access key name is now SKEY (KEY is depreciated)
-           rup: new HTML layout, fixed bug in file select box
+2009-07-07 fup: be more restrictive in accepting (illegal) parameters values
+2009-07-07 fup,fuc: subuser access key name is now SKEY (KEY is depreciated)
+2009-07-07 rup: new HTML layout, fixed bug in file select box
 2009-07-06 fup: substitute all control characters in file name and comment 
                 with "_"
 2009-07-02 better install script, guesses IP
            $TO/$FROM/$FILE --> $TO/$FROM/md5h($FILENAME)
            to avoid filename collisions
 2009-06-28 added mailman authorization mma
-           better address-book integration in fup
+2009-06-28 fup: better address-book integration
 2009-06-26 FIX.jar: fixed several bugs, now working with Windows Vista, too
 2009-06-25 added fup_template.html as an example for customizing upload page
 2009-06-22 fup,fexsend,fexget: LIST also shows COMMENT
 2009-05-17 fup: check if there is enough free space in spool
 2009-04-07 new perl based install; requires server IP for xinetd binding
 2009-03-25 fexget: fixed bug saving failed if on other partition then FEXHOME
-           fexget: fixed bug calculated wrong transfer rate
-           fexget: changed default answers to more secure values 
+2009-03-25 fexget: fixed bug calculated wrong transfer rate
+2009-03-25 fexget: changed default answers to more secure values 
 2009-03-24 fexsend: new option -l for listing sent files
-           fup: support for listing sent files
-           fex.pp: default charset is now UTF-8 in HTTP reply
+2009-03-24 fup: support for listing sent files
+2009-03-24 fex.pp: default charset is now UTF-8 in HTTP reply
 2009-03-16 fur: fixed bug no lower case transformation for user and domain
 2009-03-05 fop: fixed bug no parallel download possible on multiple recipients
 2009-03-03 dop: send Last-Modified HTTP header (java needs it)
 2009-02-22 fop: fixed bug download failed without FROM parameter
 2009-02-20 test for /usr/lib/sendmail and /usr/sbin/sendmail
 2009-02-18 fop: fixed bug file size query for alias recipient
-           fexget: added option -a to get address-book from server
+2009-02-18 fexget: added option -a to get address-book from server
 2009-02-17 fup,fuc: better linking
 2009-02-14 fup: first send notification emails, then send HTTP 200 OK to client
-           fup: accept ADDRESS_BOOK as upload
+2009-02-14 fup: accept ADDRESS_BOOK as upload
 2009-02-13 fup,foc,fuc: added ADDRESS_BOOK support
-           added fix and FIX.jar (Java applet client)
+2009-02-13 added fix and FIX.jar (Java applet client)
 2009-02-11 fop: fixed bug file size request with multiple $to gives always 0
                 (no upload resume possible with multiple recipients)
-           fop: check for valid recipient address (in file path)
+2009-02-11 fop: check for valid recipient address (in file path)
                 ==> early abort possible when client uses illegal address for 
                     upload (resume-HEAD-request)
 2009-02-10 fur: catch errors from sendmail(clone) and save them to $log
 2009-01-31 fexsrv: fixed bug handling of missing trailing / in doc requests
 2009-01-30 rup: fixed bug wrong download URL in notification email
 2009-01-26 fexsend: archive format 7z and zip with default compression
-           fup,fuc,foc,rup: link to F*EX start page in top header
+2009-01-26 fup,fuc,foc,rup: link to F*EX start page in top header
 2009-01-21 fuc: URL for subusers with KEY parameter
-           fup.fuc,foc: fixed bug wrong AKEY lookup
+2009-01-21 fup.fuc,foc: fixed bug wrong AKEY lookup
 2009-01-20 fexsrv: better handling of URLs with trailing / (==> index.html)
-           fop: fixed bug endless loop with fop_auth mode
+2009-01-20 fop: fixed bug endless loop with fop_auth mode
 2009-01-13 fup,fop: support for MIME-file types
-           fexsend: added option -M for MIME-file to be displayed in webbrowser
+2009-01-13 fexsend: added option -M for MIME-file to be displayed in webbrowser
                     on download
 2009-01-04 fup: increase minimum timeout to 10 s
 2008-12-26 fup: do not allow re-upload (overwrite) if file is in download
 2008-12-21 fup: fixed bug removing old autodelete and error files failed
 2008-12-20 added logwatch
 2008-12-18 fexget: fixed bug responsiveness on slow links 
-           fexget: fixed bug save file name for archives
+2008-12-18 fexget: fixed bug save file name for archives
 2008-12-12 fexget: better responsiveness on slow links (modem, ISDN)
-           fup: added warning for incompatible clients (konqueror, etc)
+2008-12-12 fup: added warning for incompatible clients (konqueror, etc)
 2008-12-11 fexsend: allow comments in ID file
 2008-12-03 fup,fex.pp: fixed bug UTF-8 subject in notfication email
 2008-12-02 fexsend: better responsiveness on slow links (modem, ISDN)
-          fop: send UTF-8 filename in HTTP header Content-Disposition
-           fexget: save original filename (parse HTTP header)
+2008-12-02    fop: send UTF-8 filename in HTTP header Content-Disposition
+2008-12-02 fexget: save original filename (parse HTTP header)
 2008-11-28 fexserv: added special FlashGet (download sucker) brake
-          html error messages now with HTTP_HOST und server-time info
+2008-11-28    html error messages now with HTTP_HOST und server-time info
 2008-11-27 added htdocs/version and htdocs/tools.html
-           added fexsend, fexget, sexsend, sexget to htdocs/download
-           dop: fixed bug symlink of symlink leads to hangup
-           fop: teergrub download managers and other suckers
+2008-11-27 added fexsend, fexget, sexsend, sexget to htdocs/download
+2008-11-27 dop: fixed bug symlink of symlink leads to hangup
+2008-11-27 fop: teergrub download managers and other suckers
 2008-11-26 fop: with URL parameter ?KEEP file can be downloaded more than once
-           fexget: added option -k for keep on server
+2008-11-26 fexget: added option -k for keep on server
 2008-11-24 fex_cleanup: fixed bug $autodelete not defined
-           fexget: added HTTPS/SSL support
+2008-11-24 fexget: added HTTPS/SSL support
 2008-11-22 fexsrv: reject requests with IP hostnames in HTTP Host header
 2008-11-21 fex.ph,fop: $autodelete="DELAY" allows file download many times
                        (but only from same IP and until next fex_cleanup run)
-           fup,fop: fixed bug options keep and delete autodelay do not
+2008-11-21 fup,fop: fixed bug options keep and delete autodelay do not
                     work with spool on NFS
 2008-11-20 fexsend: added HTTPS/SSL support
-           fex.ph: added config variable $autodelete
-           fup: fixed bug subuser cannot send files
+2008-11-20 fex.ph: added config variable $autodelete
+2008-11-20 fup: fixed bug subuser cannot send files
 2008-11-19 use md5-hash of $from:$id instead of URL parameters FROM=$from&ID=$id
-           fac: set correct exit status
+2008-11-19 fac: set correct exit status
 2008-11-16 fup: fixed bug DELETE not working 
-           install: do not overwrite lib/fup.pl (perhaps contains site config)
+2008-11-16 install: do not overwrite lib/fup.pl (perhaps contains site config)
 2008-11-15 fex_cleanup: clean up $SPOOL/.ukeys/, too
 2008-11-14 fup: show "user config" link only after authorization
 2008-11-13 foc,fuc,fup: quick sub-user creation with auto-notification.
 2008-10-29 fup: do not require HTTP authorization if request already
                 contains ID (methode used by xx)
 2008-10-28 fex.pp: fixed bug $warning not defined
-           fup: fixed bug do not allow subuser in fop_auth mode
+2008-10-28 fup: fixed bug do not allow subuser in fop_auth mode
 2008-10-27 install: do not overwrite existing htdoc/index.html
-           fup: fixed bug resend (SEEK) leads to HTTP error 666
+2008-10-27 fup: fixed bug resend (SEEK) leads to HTTP error 666
 2008-10-26 fexsrv: accept HTTP request with absolute URLs (http://...), too
 2008-10-23 fexsrv: fixed bug continue connect logfile entry
 2008-10-17 fexsrv: fixed bug keep_alive with HTTP/1.0
-           fexsrv: fixed bug wrong warning in debug mode with empty line
+2008-10-17 fexsrv: fixed bug wrong warning in debug mode with empty line
 2008-10-07 fexsrv: moved TIMEOUT message to debug.log
 2008-10-06 dop: fixed bug opening file (did not deliver any file!)
-           dop: implemented HTTP keep-alive (delivering more than one
+2008-10-06 dop: implemented HTTP keep-alive (delivering more than one
                 document per session)
 2008-10-04 if config variable $fop_auth is set, download requires
-           authentication and upload is restricted to registered users
+2008-10-04 authentication and upload is restricted to registered users
 2008-10-02 dop: declare exectuable scripts as application/octet-stream
-           fup: added link to Windows client schwuppdiwupp.exe
+2008-10-02 fup: added link to Windows client schwuppdiwupp.exe
 2008-09-29 write upload speed to upload directory
 2008-09-17 fac: fixed bug locating FEXLIB
 2008-09-12 fup: added config lib/fup.pl
 2008-08-31 added fur (F*EX User (auto-) Registration)
 2008-08-25 will die when no hostname is available
 2008-08-21 added fexsend to htdocs/download
-           fup: added ID mail sendback option
+2008-08-21 fup: added ID mail sendback option
 2008-08-20 fac: added -l option
 2008-08-19 fexsrv: fixed bug SSL handling
 2008-08-15 fup,fuc,fexsrv: dynamic protocol detection (HTTP/HTTPS)
 2008-08-14 fup: fixed bug login possible with wrong login data (but no upload)
 2008-08-13 fup: showstatus terminates immediately when empty file was uploaded
-           fup: showstatus shows error message on illegal recipient address
+2008-08-13 fup: showstatus shows error message on illegal recipient address
                 or when no file was uploaded 
                 (nececessary for stupid Internet Explorer!)
 2008-08-11 splitted debugfiles with time stamp in filename 
-           fex_cleanup: clean up aborted uploads, .ukeys/ and .debug/, too
-           fexsend,fexget: allow more than one file (with all options)
+2008-08-11 fex_cleanup: clean up aborted uploads, .ukeys/ and .debug/, too
+2008-08-11 fexsend,fexget: allow more than one file (with all options)
 2008-08-08 fup: eliminate superfluous newlines in logfile on error handling
-           changed bareword filehandles to indirect filehandles
+2008-08-08 changed bareword filehandles to indirect filehandles
 2008-08-06 fup: decode %NUMBERs in file names from POST
 2008-08-02 fexsend: bug fix -A option and argument handling
 2008-08-01 fup: regular users can change the recipient in the upload form,
                 sub users can not
 2008-07-31 fup: fixed bug internet explorer not showing upload status window
-           fup: fixed bug with id / special id / real id mixup
-           fuc: nearly complete rewrite, better user interface
+2008-07-31 fup: fixed bug with id / special id / real id mixup
+2008-07-31 fuc: nearly complete rewrite, better user interface
 2008-07-30 fup: fixed bug when account is a symlink
-           fup: fixed bug in authentication of subusers
+2008-07-30 fup: fixed bug in authentication of subusers
 2008-07-03 fop: workaround for Internet Explorer download bug
 2008-07-02 fup,fop: switched default charset from ISO-8859-1 to UTF-8
-           fup: uid for showstatus synchronization
+2008-07-02 fup: uid for showstatus synchronization
 2008-06-21 fexget: downloading without wget, file number as argument
 2008-06-20 fexget,fop: added DELETE option
-           fexsend: send more files in one run
+2008-06-20 fexsend: send more files in one run
 2008-05-30 added missing sex to distribution
-           fexsend: added -A archiv to yourself option
+2008-05-30 fexsend: added -A archiv to yourself option
 2008-05-28 fup: fixed bug in LIST and DELETE commands
 2008-05-27 fexsrv: correct HTTP redirect on missing trailing / in URL
 2008-05-26 sex,sexsend: better public mode
 2008-05-24 sex,sexsend: added text mode option
 2008-05-23 added missing foc and rup to distribution
 2008-05-20 fexsend: fixed bug in list parsing (-l option)
-          dop: fixed bug in file type determining on symbolic links
+2008-05-20    dop: fixed bug in file type determining on symbolic links
 2008-05-15 fexsrv,dop: fixed bug in HTTP keep_alive multi-requests
 2008-05-02 fexsrv,dop: support for HTTP keep_alive multi-requests
-           fexsrv: more robust header parsing (ignore superfluous spaces)
+2008-05-02 fexsrv: more robust header parsing (ignore superfluous spaces)
 2008-04-28 added support for HTTP keep_alive
-          fexsrv: added SID (session ID) support
-          fexsend: encrypt ID with MD5 and SID
+2008-04-28    fexsrv: added SID (session ID) support
+2008-04-28    fexsend: encrypt ID with MD5 and SID
 2008-04-20 added foc and rup
-          fop: return apropriate error message when file has been (auto)deleted
+2008-04-20    fop: return apropriate error message when file has been (auto)deleted
                 or is expired; error message is kept 3*keep_default days
 2008-04-19 install: do not overwrite old fex.ph, but create fex.ph_new instead
 2008-04-18 fup: fixed bug filename with path in notification email
 2008-04-16 fexsrv,fop,dop: implemented HTTP HEAD
 2008-04-14 renamed cgilaunch to fexsrv
-           fup: do not send notify-mail if file already exists (overwrite mode)
-           fup: do not accept file if authentication fails
-           fup,fop,fexsend: new secure download URL scheme with random dkey
+2008-04-14 fup: do not send notify-mail if file already exists (overwrite mode)
+2008-04-14 fup: do not accept file if authentication fails
+2008-04-14 fup,fop,fexsend: new secure download URL scheme with random dkey
 2008-04-11 fup: fixed bug in upload bar with 8-bit file names
-           fex_cleanup: fixed bug not removing aborted uploads
+2008-04-11 fex_cleanup: fixed bug not removing aborted uploads
 2008-04-10 added F*EX camel logo
 2008-04-09 added dop (generic document output)
-          install: better infos
+2008-04-09    install: better infos
 2008-04-08 renamed confusing ID to auth-ID (request by chris@citecs.de)
-           fuc: fixed bug with more than 1 sub-user
+2008-04-08 fuc: fixed bug with more than 1 sub-user
 2008-04-07 fup: readded keep parameter (code got lost sometime?)
-          fup: added sender restriction (ALLOWED_RECIPIENTS)
-          fac: added restriction option -r and delete user option -d
+2008-04-07 fup: added sender restriction (ALLOWED_RECIPIENTS)
+2008-04-07 fac: added restriction option -r and delete user option -d
 2008-04-06 fup: use Net::DNS instead of external host command
-          fup: more debuglog, fixed wrong error messages
-          added doc/concept doc/FAQ 
+2008-04-06 fup: more debuglog, fixed wrong error messages
+2008-04-06 added doc/concept doc/FAQ 
 2008-04-02 install: better error handling (patch by chris@citecs.de)
-           more docs and improved logging
+2008-04-02 more docs and improved logging
 2008-04-01 cgilaunch: fixed bug in determing REMOTE_HOST when using stunnel
-          fexget: added -s streaming option
-           sex,sexsend: added public mode
+2008-04-01 fexget: added -s streaming option
+2008-04-01 sex,sexsend: added public mode
 2008-03-31 changed project name to F*EX because of name collision with
           http://freshmeat.net/projects/fex/
-          added sex, sexsend and sexget to distribution
+2008-03-31 added sex, sexsend and sexget to distribution
 2008-03-28 xx: changed syntax, now compatible to zz
-          added zz to distribution
+2008-03-28 added zz to distribution
 2008-03-27 fup: fixed bug in mail address verification
 2008-03-24 fup,fexsend: show transfer rate in kB if filesize < 2 MB
-          fup: code-cleanup, more comments
-           fex.pp: umask 077
+2008-03-24 fup: code-cleanup, more comments
+2008-03-24 fex.pp: umask 077
 2008-03-23 fup: fixed bug in using multiple recipients
 2008-03-22 first public release
 2007-01-27 first file fexed via fex.rus.uni-stuttgart.de
+2007-01-15 first file fexed via wwwtest6.belwue.de
 2006-11-?? first code
diff --git a/doc/SSL b/doc/SSL
index d0e95f62f6a4e039d4c976182078fce03c1d532a..14f5abd089aa03d448c8c4a75b1ee8d23cdbc7b4 100644 (file)
--- a/doc/SSL
+++ b/doc/SSL
@@ -3,6 +3,7 @@
 # execute this as root!
 
 # Redhat : stunnel-4 does not work! you need to install stunnel-5
+# Debian : stunnel-5.06 does not work! you need to install stunnel-5.18
 
 mkdir /home/fex/etc
 cd /home/fex/etc/
@@ -43,7 +44,7 @@ service fexs
         type            = unlisted
         protocol        = tcp
         port            = 443
-        cps             = 5 10
+        cps             = 10 2
         user            = fex
         groups          = yes
         server          = $stunnel
diff --git a/doc/new b/doc/new
index 568080d556810f9ea5f2693c91bc8459084c6958..c0ae6f2107149d32689b0af208a00a2458192d49 100644 (file)
--- a/doc/new
+++ b/doc/new
@@ -2,12 +2,6 @@ New release on http://fex.belwue.de/fex.html
 
 Important changes:
 
-- moved to new distribution site fex.belwue.de
-
-- autodelete=no if sender == recipient
-
-- no file name in email subject if notification is encrypted
-
-- added active and passive redirect support for standard HTTP documents
+- added fexit (Windows client) and macfexsend (MacOS client) support
 
 - fixed various bugs
index d28f95a832bcdd53e0303c454e4e43ea020b2084..57e383fb4f0a67a5aa5e1179fc3787849dc40d8a 100644 (file)
@@ -1,6 +1,14 @@
 New features for users
 ----------------------
 
+2016-01-04:
+
+- new Windows client fexit
+
+- new MacOS client macfexsend
+
+
+
 2015-01-12:
 
 - user configuration: save-or-display (MIME) for download
index 056cc38939a7cccbb512423df0e632965548470c..366b2e90488653aa0f7e52f554bd3c37a9fda461 100644 (file)
@@ -1 +1 @@
-fex-20150826
+fex-20160104
deleted file mode 100644 (file)
index a0966457f9f3c8217371e44dca51636c0e3900ef..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<html>
-<head><title>F*EX FAQ</title></head>
-<body>
-
-## <pre>
-## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
-## </pre>
-
-<< require "./faq.pl" or print $! >>
-
-</body>
-</html>
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..b9014e9f4408e737987b9e6b59e9bc2399be85c9
--- /dev/null
@@ -0,0 +1 @@
+FAQ/index.html
\ No newline at end of file
index 8ab089c681d9c25bf239d796e3da772be717b52f..4c442ca84caf8acb9c7a5105ccbf01b307e1a02c 100644 (file)
@@ -1,7 +1,7 @@
 Q: I cannot install a web server like fexsrv, because I have no root permissions. Is there a pure-CGI-version of F*EX which runs with an apache web server?
 A: F*EX is hard bound to fexsrv for several reasons (performance, file size limit, session concept, etc) and cannot be run as CGI under apache. But you might have a look at
 
-   * http://gpl.univ-avignon.fr/filez/
+   * https://github.com/FileZ/FileZ
    * http://freshmeat.net/projects/eventh/
    * http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html (German only!) 
 
index bc232d78db853481c32d75a41844f7b75a40d591..8a5f3a2e00512c1994d8b34ecd1cc760caeb0c0c 100644 (file)
@@ -11,25 +11,25 @@ A: * They have a file size limit of 2 GB or even less.
    * It is unknown how long they will exist - DropLoad and ALLPeers already have terminated their business.
    
 Q: Why name "F*EX" and not shortly "FEX"?
-A: At publication time there was already an (older) program named "FEX" listed on freshmeat.net.
+A: At publication time there was already a program named "FEX" listed on freshmeat.net.
 
 Q: Who is the author?
 A: The main author is Ulli Horlacher <framstag@rus.uni-stuttgart.de><br>
-   But there are also a lot of contributors.
+   But there are also a lot of contributors all around the world.
 
 Q: Why a camel as the logo?
 A: The logo was inspired by the Perl camel, but it is based on a Steiff plush camel, which rides with us on our racing tandem.
    The logo was drawn by my stoker Beate.
-   http://fex.belwue.de/Vortrag/tosa.html
+   http://fex.rus.uni-stuttgart.de/Vortrag/tosa.html
 
-Q: What do I need to install F*EX?
+Q: What do I need to install a F*EX server?
 A: A UNIX or Windows server with a DNS entry, smtp for outgoing email and one open and free incoming tcp port.
    You must have administrative rights (UNIX: root) on this server and a basic understanding of UNIX and networking.
 
 Q: What means DNS and smtp? What is a tcp port?
 A: Do not install F*EX. It is beyond your horizon.
 
-Q: Does F*EX support IPv6?
+Q: Does F*EX support IPv6 and SSL/TLS?
 A: Yes.
 
 Q: Can I run F*EX on Windows?
@@ -43,14 +43,14 @@ Q: I do not want to install a F*EX server of my own, but where can I use it?
 A: Contact <fex@nepustil.net> http://www.nepustil.net/ for F*EX hosting.
 
 Q: The F*EX server is all in Perl?! Isn't Perl too slow for this job?
-A: fex.belwue.de runs on a PC and F*EX is able to handle uploads with more than 300 MB/s.
+A: F*EX is able to handle uploads with more than 300 MB/s on an office PC.
    Try this with an ordinary webserver like Apache!
 
 Q: Which licence does F*EX have? And why?
 A: Perl Artistic free software with a special anti-military clause: 
    http://fex.belwue.de/doc/Licence
    "I want peace on earth and goodwill towards men"
-   http://www.youtube.com/watch?v=JHU0HinVhYc
+   https://www.youtube.com/watch?v=JHU0HinVhYc https://en.wikipedia.org/wiki/Sneakers_%281992_film%29
 
 Q: Is there a F*EX mailing list?
 A: https://listserv.uni-stuttgart.de/mailman/listinfo/fex
@@ -58,6 +58,12 @@ A: https://listserv.uni-stuttgart.de/mailman/listinfo/fex
 Q: Where can I get commercial support for F*EX?
 A: Contact <fex@nepustil.net> http://www.nepustil.net/
 
+Q: How big is F*EX?
+A: Server: 400 kB, 11000 lines of code
+   Clients: 180 kB, 5500 lines of code
+   Documentation: 130 kB
+   Localizations: 250 kB
+   
 Q: Who else is using F*EX?
 A: For example:
    * German Aerospace Center http://fex.dlr.de
@@ -66,6 +72,8 @@ A: For example:
    * Swiss National Supercomputing Centre http://fex.cscs.ch
    * Centre National de la Recherche Scientifique (French National Center for Scientific Research) http://bigfiles.cnrs-gif.fr
    * Institut Pasteur http://dl.pasteur.fr
+   * Justus Liebig University http://fex.hrz.uni-giessen.de
+   * Fiat Chrysler https://fex.fiatitem.com/
    * Palo Alto Research Center (Xerox PARC) http://parcftp.parc.com
    * Baden-Württembergs extended LAN http://fex.belwue.de
    * Deutsche Kinemathek Museum f&uuml;r Film und Fernsehen http://upload.deutsche-kinemathek.de
index ddd52511c8b82df8b2b8349690c67348b87de44f..050902d3ac93bbddb451cb851704d7f0d23bfb58 100644 (file)
@@ -4,8 +4,8 @@ A: Contact the author <framstag@rus.uni-stuttgart.de>
    Requested features are:
    <ul>
      <li>testers for MacOS, AIX and other UNIXes
-     <li>a new maintainer for the Java client F*IX
      <li>an Android or iOS client
+     <li>HTML5/websocket support
      <li>a F*EX plugin for thunderbird or outlook
          see thunderbird's filelink
          https://support.mozillamessaging.com/en-US/kb/filelink-large-attachments
index b98c1d24998aa0fcd8e0bbd4fbfb7fa3816abfe9..641886a40004dc02ca84f9e6c57f487fd9932bdf 100644 (file)
@@ -76,6 +76,10 @@ Q: Sending as a F*EX user is easy, but how to receive files from others, outside
 A: Register them as your subusers, create a F*EX group or a one-time upload key with "user config & operation control"
    See also http://fex.belwue.de/usecases/foreign.html
 
+Q: I am not a user of your site. How can I send a file to a registered user?
+A: See question above: you must ask a regular user to register you as his subuser.
+   You will then get a specific upload URL from him.
+
 Q: Sometimes I can download a file more than once, especially when I repeat it quickly. Is the autodelete feature buggy?
 A: The F*EX server has a grace time of 1 minute after first sucessfully download in which the file is still available. This is necessary because of some stupid "download managers" which request the file several times at once. Otherwise they would report an error to the user.
 
@@ -96,6 +100,9 @@ Q: I forgot to download a file. Now it is expired. How can I obtain it neverthel
 A: An expired file is definitively deleted. Even the admin cannot restore it.
    You must re-request it from the sender.
 
+Q: When I try to download a file again, I get the error message: "file has been autodeleted after download". Can you restore it?
+A: No. You must re-request it from the sender.
+
 Q: I have sent a second file with the same name, but the recpient has not received a second notification email. Why?
 A: A file with the same name to the same recpient overwrites the first one if it is still there (no download so far).
    A second notification email of the same file(name) is not suggestive.
@@ -107,6 +114,9 @@ Q: How can I suppress the automatic notification email?
 A: Use "!#!" as comment, then no notification email will be sent.
    Of course you then have to inform the recipient manually.
 
+Q: Can I delete a file without downloading?
+A: Add "?DELETE" to your download URL.
+
 Q: Can I get a copy of the notification email?
 A: Add "!bcc!" to the comment field on upload.
 
index 8e001196d8cc13b1f0d4fc24c16b0f1c01268625..c375cea05b269d3ddc4ea08472185e560c11f853 100755 (executable)
@@ -30,9 +30,10 @@ our $SH;
 our ($fexhome,$idf,$tmpdir,$windoof,$useragent);
 our ($xv,%autoview);
 our $bs = 2**16; # blocksize for tcp-reading and writing file
-our $version = 20150826;
+our $version = 20160104;
 our $CTYPE = 'ISO-8859-1';
 our $fexsend = $ENV{FEXSEND} || 'fexsend';
+our $DEBUG = $ENV{DEBUG};
 
 my %SSL = (SSL_version => 'TLSv1');
 my $sigpipe;
@@ -57,6 +58,14 @@ if ($Config{osname} =~ /^mswin/i) {
   $SSL{SSL_verify_mode} = 0;
   chdir $ENV{USERPROFILE}.'\Desktop';
   # open XX,'>XXXXXX';close XX;
+} elsif ($Config{osname} =~ /^darwin/i or $ENV{MACOS}) {
+  $0 =~ s:(.*)/:: and $ENV{PATH} .= ":$1";
+  $fexhome = $ENV{FEXHOME} || $ENV{HOME}.'/.fex';
+  $tmpdir = $ENV{FEXTMP} || $ENV{TMPDIR} || "$fexhome/tmp";
+  $idf = "$fexhome/id";
+  $_ = `sw_vers -productVersion 2>/dev/null`||'';
+  chomp;
+  $useragent = "fexget-$version (MacOS $_)";
 } else {
   $0 =~ s:(.*)/:: and $ENV{PATH} .= ":$1";
   $fexhome = $ENV{FEXHOME} || $ENV{HOME}.'/.fex';
@@ -233,7 +242,7 @@ if ($opt_a) {
   }
 }
 
-my ($file,%files,$download,$server,$port,$fop);
+my ($file,%files,$download,$server,$port,$fop,$https);
 
 if ($opt_f) {
   unless ($ENV{FEXID} or -f $ENV{HOME}.'/.fex/id') {
@@ -271,6 +280,7 @@ URL: foreach my $url (@ARGV) {
   }
 
   if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/.*fop/\S+)}) {
+    $https  = $1;
     $server = $2;
     $port   = $4 || ($1?443:80);
     $fop    = $5;
@@ -358,6 +368,7 @@ URL: foreach my $url (@ARGV) {
 
     if ($ENV{DISPLAY} and $download =~ /\.(gif|jpg|png|tiff?)$/i) {
       # see also mimeopen and xdg-mime
+      # http://unix.stackexchange.com/questions/144047/how-does-xdg-open-do-its-work
       if (my $xv = $xv || pathsearch('xv') || pathsearch('xdg-open')) {
         printf "run \"%s %s\" [Yn] ? ",basename($xv),basename($download);
         $_ = <STDIN>||'';
@@ -367,10 +378,10 @@ URL: foreach my $url (@ARGV) {
     }
 
     if ($download =~ /$atype/) {
-      if    ($download =~ /\.(tgz|tar.gz)$/)  { extract('tar tvzf','tar xvzf') }
-      elsif ($download =~ /\.tar$/)           { extract('tar tvf','tar xvf') }
-      elsif ($download =~ /\.zip$/i)          { extract('unzip -l','unzip') }
-      elsif ($download =~ /\.7z$/i)           { extract('7z l','7z x') }
+      if    ($download =~ /\.(tgz|tar.gz)$/) { extract('tar tvzf','tar xvzf') }
+      elsif ($download =~ /\.tar$/)          { extract('tar tvf','tar xvf') }
+      elsif ($download =~ /\.zip$/i)         { extract('unzip -l','unzip') }
+      elsif ($download =~ /\.7z$/i)          { extract('7z l','7z x') }
       else { die "$0: unknown archive \"$download\"\n" }
       if ($? == 0) {
         unlink $download;
@@ -388,7 +399,7 @@ sub extract {
   my $l = shift;
   my $x = shift;
   my $d = $download;
-  my $xd = '.';
+  my $xd = '';
   local $_;
 
   if (-t and not $windoof) {
@@ -396,9 +407,10 @@ sub extract {
     system(split(' ',$l),$download);
     $d =~ s:.*/:./:;
     $d =~ s/\.[^.]+$//;
+    $d =~ s:/*$:/:;
     for (;;) {
       $xd = inquire("extract to directory (Ctrl-C to keep archive): ",$d);
-      last if $xd =~ s:^(\./*)*!?$:./:;
+      last if $xd =~ s:^(\./*)*!?$::;
       if ($xd eq '-') {
         print "keeping $download\n";
         exit;
@@ -420,8 +432,9 @@ sub extract {
       last;
     }
   }
-  print "extracting to $xd :\n";
+  print "extracting to $xd :\n" if $xd;
   system(split(' ',$x),$download);
+  print "extracted to $xd\n" if $xd;
 }
 
 sub del {
@@ -453,7 +466,7 @@ sub del {
 sub forward {
   my $url = shift;
   my ($server,$port);
-  my ($uri,$dkey,$list,$cmd,$n);
+  my ($uri,$dkey,$list,$cmd,$n,$copy);
   my @r;
 
   if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/fop/.+)}) {
@@ -474,7 +487,13 @@ sub forward {
   die "$0: no reply from fex server $server\n" unless $_;
   warn "<-- $_" if $opt_v;
 
-  unless (/^HTTP.*200/) {
+  if (/^HTTP.*already exists/) {
+    if ($uri =~ m:/fop/(\w+)/:) {
+      $dkey = $1;
+    }
+  } elsif (/^HTTP.*200/) {
+    # ok!
+  } else {
     s/^HTTP.... \d+ //;
     die "$0: $_";
   }
@@ -486,8 +505,7 @@ sub forward {
     warn "<-- $_" if $opt_v;
   }
 
-  $cmd = 'fexsend -l >/dev/null 2>&1';
-  print "$cmd\n" if $opt_v;
+  print "fexsend -l\n" if $opt_v;
   system 'fexsend -l >/dev/null 2>&1';
   $list = $ENV{HOME}.'/.fex/tmp/fexlist';
   open $list,$list or die "$0: cannot open $list - $!\n";
@@ -563,14 +581,18 @@ sub download {
       $pipe = $download = $opt_s;
     } elsif (-p $opt_s or -c $opt_s) {
       $download = $opt_s;
+      $nocheck = 'pipe or character device';
     } else {
       $download = $file.'.tmp';
       $seek = -s $download || 0;
     }
   } else {
     # ask server for real file name
-    serverconnect($server, $port);
-    sendheader("$server:$port","HEAD $proxy_prefix$fop HTTP/1.1","User-Agent: $useragent");
+    sendheader(
+      "$server:$port",
+      "HEAD $proxy_prefix$fop HTTP/1.1",
+      "User-Agent: $useragent"
+    );
     my $reply = $_ = <$SH>;
     unless (defined $_ and /\w/) {
       die "$0: no response from server\n";
@@ -644,28 +666,34 @@ sub download {
       }
     }
     if ($checkstorage and not $nocheck) {
-      $t0 = time;
+      my $t0 = my $t1 = my $t2 = time;
       my $n = 0;
+      my $buf = '.' x M;
+      my $storagetest = $file.'.test';
+      my $error = "$0: cannot write \"$storagetest\"";
+      open $storagetest,'>',$storagetest or die "$error - $!\n";
       print STDERR "checking storage...\r";
-      $buf = '.' x M;
-      while (-s $download < $checkstorage) {
-        syswrite X,$buf or do {
-          unlink $download;
-          die "\n$0: cannot write $download - $!\n";
+      while (-s $storagetest < $checkstorage) {
+        syswrite $storagetest,$buf or do {
+          unlink $storagetest;
+          die "\n$error - $!\n";
         };
         $n++;
-        print STDERR "checking storage... ".$n." MB\r";
+        $t2 = int(time);
+        if ($t2 > $t1) {
+          print STDERR "checking storage... ".$n." MB\r";
+          $t1 = $t2;
+        }
       }
-      close X or do {
-        unlink $download;
-        die "\n$0: cannot write $download - $!\n";
+      close $storagetest or do {
+        unlink $storagetest;
+        die "\n$error - $!\n";
       };
       print STDERR "checking storage... ".$n." MB ok!\n";
-      unlink $download;
-      if (time-$t0 < 25) {
-        open X,'>',$download or die "$0: cannot write to \"$download\" - $!\n";
-      } else {
+      unlink $storagetest;
+      if (time-$t0 > 25) {
         # retry after timeout
+        serverconnect($server,$port);
         return(download($server,$port,$fop,'nocheck'))
       }
     }
@@ -806,13 +834,6 @@ sub pathsearch {
 }
 
 
-sub quote {
-  local $_ = shift;
-  s/([^\w¡-ÿ_%\/=~:.,-])/\\$1/g;
-  return $_;
-}
-
-
 {
   my $tty;
 
@@ -830,10 +851,11 @@ sub quote {
 
       if (defined(&TIOCSTI) and $tty and open($tty,'>',$tty)) {
         print $prompt;
+        # push default answer into keyboard buffer
         foreach my $a (split("",$default)) { ioctl($tty,&TIOCSTI,$a) }
         chomp($_ = <STDIN>||'');
       } else {
-        $prompt =~ s/([\?:=]\s*)/ [$default]$1/ or $prompt .= " [$default]";
+        $prompt =~ s/([\?:=]\s*)/ [$default]$1/ or $prompt .= " [$default] ";
         print $prompt;
         chomp($_ = <STDIN>||'');
         $_ = $default unless length;
@@ -915,15 +937,9 @@ sub serverconnect {
   my $connect = "CONNECT $server:$port HTTP/1.1";
   local $_;
 
-  if ($opt_v and $port == 443 and %SSL) {
-    foreach my $v (keys %SSL) {
-      printf "%s => %s\n",$v,$SSL{$v};
-    }
-  }
-
   if ($proxy) {
     tcpconnect(split(':',$proxy));
-    if ($port == 443) {
+    if ($https) {
       printf "--> %s\n",$connect if $opt_v;
       nvtsend($connect,"");
       $_ = <$SH>;
@@ -932,14 +948,13 @@ sub serverconnect {
       unless (/^HTTP.1.. 200/) {
         die "$0: proxy error : $_";
       }
-      eval "use IO::Socket::SSL";
-      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      &enable_ssl;
       $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
     }
   } else {
     tcpconnect($server,$port);
   }
-#  if ($port == 443 and $opt_v) {
+#  if ($https and $opt_v) {
 #    printf "%s\n",$SH->get_cipher();
 #  }
 }
@@ -954,10 +969,9 @@ sub tcpconnect {
     undef $SH;
   }
 
-  if ($port == 443) {
+  if ($https) {
     # eval "use IO::Socket::SSL qw(debug3)";
-    eval "use IO::Socket::SSL";
-    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    &enable_ssl;
     $SH = IO::Socket::SSL->new(
       PeerAddr => $server,
       PeerPort => $port,
@@ -974,6 +988,7 @@ sub tcpconnect {
 
   if ($SH) {
     autoflush $SH 1;
+    binmode $SH;
   } else {
     die "$0: cannot connect $server:$port - $@\n";
   }
@@ -982,6 +997,18 @@ sub tcpconnect {
 }
 
 
+sub enable_ssl {
+  eval "use IO::Socket::SSL";
+  die "$0: cannot load IO::Socket::SSL\n" if $@;
+  eval '$SSL{SSL_verify_mode} = 0 if Net::SSLeay::SSLeay() <= 9470143';
+  if ($opt_v) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
+}
+
+
 sub sendheader {
   my $sp = shift;
   my @head = @_;
@@ -1018,6 +1045,18 @@ sub nvtsend {
 }
 
 
+sub quote {
+  local $_ = shift;
+  s/([^\w\@\/%^,.=+_:+-])/\\$1/g;
+  return $_;
+}
+
+
+sub debug {
+  print "## DEBUG: @_\n" if $DEBUG;
+}
+
+
 # from MIME::Base64::Perl
 sub encode_b64 {
   my $res = "";
index e746b669a958495748effb2b305c448a94ec211b..7e498dce61f81160a31b8cd70732665eeb7dc1d1 100755 (executable)
@@ -31,15 +31,15 @@ eval 'use Net::INET6Glue::INET_is_INET6';
 
 $| = 1;
 
-our ($SH,$fexhome,$idf,$tmpdir,$windoof,$useragent,$editor,$nomail);
+our ($SH,$fexhome,$idf,$tmpdir,$windoof,$macos,$useragent,$editor,$nomail);
 our ($anonymous,$public);
 our ($tpid,$frecipient);
 our ($FEXID,$FEXXX,$HOME);
 our (%alias);
 our $chunksize = 0;
-our $version = 20150826;
+our $version = 20160104;
 our $_0 = $0;
-our $DEBUG;
+our $DEBUG = $ENV{DEBUG};
 
 my %SSL = (SSL_version => 'TLSv1');
 my $sigpipe;
@@ -54,18 +54,31 @@ if ($Config{osname} =~ /^mswin/i) {
   $useragent = sprintf("fexsend-$version (%s %s)",
                        $Config{osname},$Config{archname});
   $SSL{SSL_verify_mode} = 0;
+} elsif ($Config{osname} =~ /^darwin/i or $ENV{MACOS}) {
+  $macos = $Config{osname};
+  # http://stackoverflow.com/questions/989349/running-a-command-in-a-new-mac-os-x-terminal-window
+  $HOME = (getpwuid($<))[7]||$ENV{HOME};
+  $fexhome = $HOME.'/.fex';
+  $tmpdir = $ENV{FEXTMP} || $ENV{TMPDIR} || "$fexhome/tmp";
+  $tmpdir =~ s:/$::;
+  $idf = "$fexhome/id";
+  chmod 0600,$idf;
+  $editor = $ENV{EDITOR} || 'open -W -n -e';
+  $_ = `sw_vers -productVersion 2>/dev/null`||'';
+  chomp;
+  $useragent = "fexsend-$version (MacOS $_)";
 } else {
   $0 =~ s:.*/::;
   $HOME = (getpwuid($<))[7]||$ENV{HOME};
   $fexhome = $HOME.'/.fex';
   $tmpdir = $ENV{FEXTMP} || "$fexhome/tmp";
   $idf = "$fexhome/id";
+  chmod 0600,$idf;
   $editor = $ENV{EDITOR} || 'vi';
   $_ = `(lsb_release -d||uname -a)2>/dev/null`||'';
   chomp;
   s/^Description:\s+//;
   $useragent = "fexsend-$version ($_)";
-  chmod 0600,$idf;
 }
 
 if (-f ($_ = '/etc/fex/config.pl')) {
@@ -88,7 +101,7 @@ my $features = '';
 my $timeout = 30;      # server timeout
 my $fexlist = "$tmpdir/fexlist";
 my ($usage,$hints);
-my $xx = $0 =~ /^xx/;
+my $xx = $0 =~ /\bxx$/;
 
 if ($xx) {
   $usage = "usage: send file(s):               xx [:slot] file...\n".
@@ -104,6 +117,7 @@ if ($xx) {
   $usage = <<EOD;
 usage: $0 [options] file(s) [@] recipient(s)
    or: $0 [special options]
+   or: $0 -l [recipient-regexp]
    or: $0 -f \# recipient(s)
    or: $0 -x \# [-C -k -D -K -S]
 options: -v           verbose mode
@@ -111,7 +125,7 @@ options: -v           verbose mode
          -c           compress file with gzip
          -g           encrypt file with gpg
          -m limit     limit throughput (kB/s)
-         -i tag       use ID data [tag] from ID file
+         -i account   use ID data [account] from ID file
          -C comment   add comment to notification e-mail
          -k max       keep file max days on fex server
          -D           delay auto-delete after download
@@ -120,19 +134,19 @@ options: -v           verbose mode
          -o           overwrite mode, do not resume
          -a archive   put files in archive (.zip .7z .tar .tgz)
          -s stream    read data from pipe and upload it with stream name
-special options: -I      initialize ID file or show ID
-                 -I tag  add alternate ID data (secondary logins) to ID file
-                 -l      list sent files numbered (# needed for -f -x -d -N)
-                 -f \#    forward already uploaded file to another recipient
-                 -x \#    modify options -C -k -D -K for already uploaded file
-                 -d \#    delete file on fex server
-                 -N \#    resend notification e-mail
-                 -Q      check quotas
-                 -A      edit server address book (aliases)
-                 -S      show server/user settings and auth-ID
-                 -H      show hints, examples and more options
-                 -V      show version
-                 (\# is a file number, see output from $0 -l)
+special options: -I          initialize ID file or show ID
+                 -I account  add alternate ID data (secondary logins) to ID file
+                 -l          list sent files numbers (# needed for -f -x -d -N)
+                 -f \#        forward already uploaded file to another recipient
+                 -x \#        use -C -k -D -K for already uploaded file
+                 -d \#        delete file on fex server
+                 -N \#        resend notification e-mail
+                 -Q          check quotas
+                 -A          edit server address book (aliases)
+                 -S          show server/user settings and auth-ID
+                 -H          show hints, examples and more options
+                 -V          show version
+                 (# is a file number, see output from $0 -l)
 examples: $0 visualization.mpg framstag\@rus.uni-stuttgart.de
           $0 -a images.zip *.jpg webmaster\@flupp.org,metoo
           lshw | $0 -s hardware.list admin\@flupp.org
@@ -217,7 +231,9 @@ whereas archive types zip and 7z need a temporary archive file on local disk.
 With option -s you can send any data coming from a pipe (STDIN) as a file
 without wasting local disc space.
 
-With option -X you can specify any parameter, e.g.: -X autodelete=yes
+With option -X you can specify any URL parameter, e.g.: 
+fexsend -X autodelete=yes ...
+fexsend -X 'autodelete=no&locale=german' ...
 
 For HTTPS you can set the environment variables:
 SSLVERIFY=1                 # activate server identity verification
@@ -305,6 +321,9 @@ if ($xx) {
   $_ = "$fexhome/config.pl"; require if -f;
   getopts('hvIm:') or die $usage;
 } else {
+  if ($macos and not @ARGV) {
+    &ask_file;
+  }
   $opt_h = $opt_v = $opt_m = $opt_c = $opt_k = $opt_d = $opt_l = $opt_I = 0;
   $opt_H = $opt_K = $opt_D = $opt_R = $opt_M = $opt_L = $opt_Q = $opt_A = 0;
   $opt_x = $opt_o = $opt_g = $opt_V = $opt_U = $opt_F = $opt_n = $opt_q = 0;
@@ -571,7 +590,7 @@ elsif ($opt_z or $opt_Z or ${'opt_!'})      { &get_log }
 elsif ($opt_A)                         { edit_address_book($from) }
 elsif (${'opt_@'})                     { &show_address_book }
 elsif ($opt_d and $anonymous)          { &purge }
-elsif ($opt_d and $ARGV[-1] =~ /^\d+$/)        { &delete }
+elsif ($opt_d and $ARGV[-1] =~ /^\d+$/)        { &delete_file_number }
 else                                   { &send_fex }
 
 exit;
@@ -697,6 +716,7 @@ sub show_id {
   my ($fexcgi,$from,$id);
   if (open $idf,$idf) {
     $fexcgi = <$idf>;
+    # $fexcgi = <$idf> if $fexcgi =~ /^\[.+\]/;
     $from   = <$idf>;
     $id     = <$idf>;
     while (<$idf>) {
@@ -745,6 +765,7 @@ sub register {
   sendheader("$fs:$port","GET $proxy_prefix/fur?user=$mail&verify=no HTTP/1.1");
   http_response();
 
+  # header
   while (<$SH>) {
     s/\r//;
     printf "<-- $_"if $opt_v;
@@ -779,10 +800,365 @@ sub register {
 }
 
 
+# menu for MacOS users
+sub menu {
+  my $key;
+  my $new;
+  local $_;
+  
+  system 'clear';
+  print "\n";
+  print "fexsend-$version\n";
+
+  for (;;) {
+    if (open $idf,$idf) {
+      $fexcgi = getline($idf) and
+      $from   = getline($idf) and
+      $id     = getline($idf);
+      close $idf;
+      last if $id;
+    }
+    &set_ID;
+  }
+
+  print "\n";
+  print "$from on $fexcgi\n";
+  print "\n";
+  
+  for (;;) {
+    print "\n";
+    print "[s]  send a file or directory\n";
+    print "[u]  update fexsend\n";
+    print "[l]  change login data (user, server, auth-ID)\n";
+    print "[h]  help\n";
+    print "[q]  quit\n";
+    print "\n";
+    print "your choice: ";
+    $key = ReadKey(0);
+    if ($key eq 'q') { 
+      print "$key\n";
+      print "\n";
+      print "Type [Cmd]W to close this window.\n";
+      exit;
+    }
+    if ($key eq 'h') { 
+      print "$key\n";
+      print 
+        "\n".
+        "With fexsend you can send files of any size to any e-mail address.\n".
+        "\n".
+        "At the recipient or file prompt [RETURN] brings you to this option menu.\n".
+        "\n".
+        "To send more than one file:\n".
+        "When you enter * at the file prompt, you will be first asked for an archive name\n".
+        "and then you can drag+drop multiple files.\n".
+        "\n".
+        "Do not forget to terminate each input line with [RETURN].\n".
+        "\n".
+        "See http://fex.rus.uni-stuttgart.de/ for more informations.\n";
+      next;
+    }
+    if ($key eq 'u') { 
+      print "$key\n";
+      if ($0 =~ m:(^/client/|/sw/):) {
+        print "\n";
+        print "use swupdate to update fexsend!\n";
+        next;
+      }
+      $new = $0.'.new';
+      system "curl http://fex.belwue.de/download/fexsend>".quote($new);
+      chmod 0755,$new;
+      system qw'perl -c',$new;
+      if ($? == 0) {
+        rename $new,$0;
+        exec $0;
+      } else {
+        print "\n";
+        print "cannot install new fexsend\n";
+      }
+      next;
+    }
+    if ($key eq 'l') { 
+      print "$key\n";
+      system 'clear';
+      &set_ID;
+      next;
+    }
+    if ($key eq 's' or $key eq "\n") { 
+      print "s\n";
+      &ask_file;
+      next;
+    }
+  }
+  exit;
+}
+
+
+# for MacOS
+sub ask_file {
+  my ($file,$comment,$recipient,$archive,$size,$cmd,$key);
+  my @files;
+  my $qfiles;
+  local $_;
+  
+  system 'clear';
+  
+  &set_ID unless -s $idf;
+
+  print "\n";
+  print "Enter [RETURN] after each input line.\n";
+  print "\n";
+
+  for (;;) {
+    print "Recipient(s): ";
+    $recipient = <STDIN>;
+    chomp $recipient;
+    $recipient =~ s/^\s+//;
+    $recipient =~ s/\s+$//;
+    $recipient =~ s/[\s;,]+/,/g;
+    &menu unless $recipient;
+    last if $recipient =~ /\w/ or $recipient eq '.';
+  }
+
+  for (;;) {
+    print "\n";
+    print "Drag a file into this window or hit [RETURN] ";
+    print $archive ? "to continue.\n" : "for menu options.\n";
+    print "File to send: ";
+    $file = <STDIN>||'';
+    chomp $file;
+    $file =~ s/^\s+//;
+    $file =~ s/ $// if $file !~ /\\ $/;
+    &menu unless $file or $archive;
+    if ($file eq '*') {
+      print "Archive name: ";
+      $archive = <STDIN>||'';
+      chomp $archive;
+      next unless $archive;
+      $archive =~ s/^\s+//g;
+      $archive =~ s/\s+$//g;
+      $archive =~ s/[^\w=.+-]/_/g;
+      next;
+    }
+    if ($file) {
+      unless (-e $file) {
+        $file =~ s/\\\\/\000/g;
+        $file =~ s/\\//g;
+        $file =~ s/\000/\\/g;
+      }
+      unless (-r $file) {
+        print "\"$file\" is not readable\n";
+        next;
+      }
+      my $qf = quote($file);
+      if (`du -ms $qf` =~ /^(\d+)/) {
+        $size += $1;
+        printf "%d MB\n",$1;
+      }
+      if ($archive) {
+        push @files,$file;
+        next;
+      }
+    }
+    if ($archive) {
+      next unless @files;
+      $qfiles = join(' ',map(quote($_),@files));
+      if ($size < 2048) {
+        $archive .= '.zip';
+      } else {
+        $archive .= '.tar';
+      }
+    }
+    print "\n";
+    print "Comment: ";
+    $comment = <STDIN>||'';
+    chomp $comment;
+    print "\n";
+    if ($comment =~ s/^:\s*-/-/) {
+      $cmd = quote($0)." $comment ";
+      if ($archive) {
+        $cmd .= '-a '.quote($archive).' '.$qfiles;
+      } else {
+        $cmd .= quote($file);
+      }
+      $cmd .= ' '.quote($recipient);
+      print $cmd,"\n";
+      system $cmd;
+    } else {
+      print quote($0)." -C '$comment' ";
+      if ($archive) {
+        printf "-a %s %s %s\n",quote($archive),$qfiles,$recipient;
+        system $0,'-C',$comment,'-a',$archive,@files,$recipient;
+      } else {
+        printf "%s %s\n",quote($file),$recipient;
+        system $0,'-C',$comment,$file,$recipient;
+      }
+    }
+    print "\n";
+    print "[s]  send another file to $recipient\n";
+    print "[n]  send another file to another recipient\n";
+    print "[q]  quit\n";
+    print "\n";
+    print "your choice: ";
+    for (;;) {
+      $key = ReadKey(0);
+      &ask_file if $key eq 'n';
+      if ($key eq 's' or $key eq "\n") {
+        print "s\n";
+        last;
+      }
+      if ($key eq 'q') {
+        print "$key\n";
+        exit;
+      }
+    }
+    $file = $comment = $archive = '';
+    @files = ();
+  }
+}
+
+
+sub set_ID {
+  my ($server,$port,$user,$logo);
+  local $_;
+  
+  print "\n";
+  for (;;) {
+    print "F*EX server URL: ";
+    $server = <STDIN>;
+    $server =~ s/[\s\n]//g;
+    if ($server =~ s:/fup/(\w+)$::) {
+      $_ = decode_b64($1);
+      if (/(from|user)=(.+)&id=(.+)/) {
+        $user = $2;
+        $id = $3;
+      }
+    }
+    $server =~ s:/fup.*::;
+    $server =~ s:/+$::;
+    next if $server !~ /\w/;
+    if ($server =~ s/^https:..// or $server =~ /:443/) {
+      $server =~ s/:.*//;
+      $port = 443;
+      eval "use IO::Socket::SSL";
+      if ($@) {
+        print "\nno perl SSL modules installed - cannot use https\n\n";
+        next;
+      }
+      $SH = IO::Socket::SSL->new(
+        PeerAddr => $server,
+        PeerPort => $port,
+        Proto    => 'tcp',
+        %SSL
+      );
+    } else {
+      $server =~ s:^http.//::;
+      if ($server =~ s/:(\d+)//) {
+        $port = $1;
+      } else {
+        $port = 80;
+      }
+      $SH = IO::Socket::INET->new(
+        PeerAddr => $server,
+        PeerPort => $port,
+        Proto    => 'tcp',
+      );
+    }
+    unless ($SH) {
+      print "\ncannot connect to $server:$port - $!\n\n";
+      next;
+    }
+    sendheader(
+      "$server:$port",
+      "GET /logo.jpg HTTP/1.0",
+      "User-Agent: $useragent",
+      "Connection: close",
+    );
+    $_ = <$SH>||'';
+    unless (/HTTP.1.1 200/) {
+      print "\nbad server reply: $_\n";
+      next;
+    }
+    while (<$SH>) { last if /^\s*$/ }
+    local $/;
+    $logo = <$SH>||'';
+    close $SH;
+    if (length $logo < 9999) {
+      print "\n$server is not a F*EX server!\n\n";
+      next;
+    }
+    open $logo,">$tmpdir/fex.jpg";
+    print {$logo} $logo;
+    close $logo;
+    last;
+  }
+  
+  for (;;) {
+    last if $user;
+    print "Your login (e-mail address): ";
+    $user = <STDIN>;
+    $user =~ s/[\s\n]//g;
+    if ($user !~ /.@[\w.-]+$/) {
+      print "\"$user\" is not a valid e-mail address!\n";
+      next;
+    }
+  }
+  
+  for (;;) {
+    last if $id;
+    print "Your auth-ID for this account: ";
+    $id = <STDIN>;
+    $id =~ s/[\s\n]//g;
+  }
+  
+  open $idf,'>',$idf or die "$0: cannot write to $idf - $!\n";
+  print {$idf} "$server\n",
+               "$user\n",
+               "$id\n";
+  close $idf;
+  print "\n";
+  print "Login data written to $idf\n\n";
+  print "fexing test file to $user:\n\n";
+  system "$0 -o -M -C test $tmpdir/fex.jpg $user";
+  print "\n";
+  if ($? != 0) {
+    print "fexsend failed, login data is invalid, try again\n";
+    &set_ID;
+  } else {
+    print "fexsend test succeeded!\n";
+    sleep 3;
+  }
+}
+
+
+# read one key from terminal in raw mode
+sub ReadKey {
+  my $key;
+  local $SIG{INT} = sub { stty('reset'); exit };
+  
+  stty('raw');
+  # loop necessary for ESXi support
+  while (not defined $key) {
+    $key = getc(STDIN);
+  }
+  stty('reset');
+  return $key;
+}
+
+
+sub stty {
+  if (shift eq 'raw') {
+    system qw'stty -echo -icanon eol',"\001";
+  } else {
+    system qw'stty echo icanon eol',"\000";
+  }
+}
+
+
 sub send_xx {
   my $transferfile = shift;
   my $file = '';
-  my (@r,@tar);
+  my (@r,@tar,$dir);
 
   $SIG{PIPE} = $SIG{INT} = sub {
     unlink $transferfile;
@@ -800,7 +1176,6 @@ sub send_xx {
       print "making tar transfer file $transferfile :\n";
       # single file? then add this directly
       if (scalar @ARGV == 1) {
-        my ($dir,$file);
         # strip path if not ending with /
         if ($ARGV[0] =~ m:(.+)/(.+): and $2 !~ m:/$:) {
           ($dir,$file) = ($1,$2);
@@ -947,49 +1322,54 @@ sub query_settings {
 # list spool
 sub list {
   my (@r,$r);
-  my ($data,$dkey,$n);
+  my ($data,$dkey);
+  my $n = 0;
+  my $s = 1;
+  my $a = shift @ARGV || '.';
   local $_;
 
   female_mode("list spooled files?") if $opt_F;
 
-  if ($opt_l and $n = shift @ARGV and $n =~ /^\d+$/) {
-    open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
-    while (<$fexlist>) {
-      if (/^\s*(\d+)\) (\w+) (.+)/ and $1 eq $n) {
-        serverconnect($server,$port) unless $SH;
-        sendheader(
-          "$server:$port",
-          "GET $proxy_prefix/fop/$2/$2?LIST HTTP/1.1",
-          "User-Agent: $useragent",
-        );
-        $_ = <$SH>||'';
-        s/\r//;
-        print "<-- $_" if $opt_v;
-        if (/^HTTP.* 200/) {
+  if ($opt_l) {
+    if ($a =~ /^\d+$/) {
+      open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+      while (<$fexlist>) {
+        if (/^\s*(\d+)\) (\w+) (.+)/ and $1 eq $a) {
+          serverconnect($server,$port) unless $SH;
+          sendheader(
+            "$server:$port",
+            "GET $proxy_prefix/fop/$2/$2?LIST HTTP/1.1",
+            "User-Agent: $useragent",
+          );
+          $_ = <$SH>||'';
+          s/\r//;
           print "<-- $_" if $opt_v;
-          while (<$SH>) {
-            s/\r//;
-            if (/^\n/) {
-              print;
-              print while <$SH>;
+          if (/^HTTP.* 200/) {
+            print "<-- $_" if $opt_v;
+            while (<$SH>) {
+              s/\r//;
+              if (/^\n/) {
+                print;
+                print while <$SH>;
+              }
             }
+          } elsif (s:HTTP/[\d\. ]+::) {
+            die "$0: server response: $_";
+          } else {
+            die "$0: no response from fex server $server\n";
           }
-        } elsif (s:HTTP/[\d\. ]+::) {
-          die "$0: server response: $_";
-        } else {
-          die "$0: no response from fex server $server\n";
+          exit;
         }
-        exit;
       }
+      die "$0: file \#$a not found in fexlist\n";
     }
-    die "$0: file \#$n not found in fexlist\n";
-  } else {
-    @r = formdatapost(
-      from     => $from,
-      to       => $opt_l ? '*' : $from,
-      command  => $opt_C,
-    );
   }
+   
+  @r = formdatapost(
+    from       => $from,
+    to         => $opt_l ? '*' : $from,
+    command    => $opt_C,
+  );
   die "$0: no response from fex server $server\n" unless @r;
   $_ = shift @r;
   unless (/^HTTP.* 200/) {
@@ -1011,12 +1391,13 @@ sub list {
       s/&amp;/&/g;
       s/&quot;/\"/g;
       s/&lt;/</g;
-      if (/^(to .* :)/) {
-        print "\n$1\n";
-        print {$fexlist} "\n$1\n";
+      if (/^(to (.+) :)/) { 
+        $s = $2 =~ /$a/;
+        print "\n$_\n" if $s;
+        print {$fexlist} "\n$_\n";
       } elsif (m/(\d+) MB (.+)/) {
         $n++;
-        printf "%4s) %8d MB %s\n","#$n",$1,$2;
+        printf "%4s) %8d MB %s\n","#$n",$1,$2 if $s;
         printf {$fexlist} "%3d) %s %s\n",$n,$dkey,$2;
       }
     }
@@ -1092,12 +1473,12 @@ sub purge {
 }
 
 
-sub delete {
+sub delete_file_number {
   my ($to,$file);
 
   while (@ARGV) {
     $opt_d = shift @ARGV;
-    die "$usage: $0 -d #\n" if $opt_d !~ /^\d+$/;
+    die "usage: $0 -d #\n" if $opt_d !~ /^\d+$/;
 
     open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
     while (<$fexlist>) {
@@ -1139,6 +1520,37 @@ sub delete {
 }
 
 
+sub delete_file {
+  my ($from,$to,$file) = @_;
+  local $_;
+
+  unless ($SH) {
+    serverconnect($server,$port);
+    query_sid($server,$port) unless $anonymous;
+  }
+  
+  $file = urlencode($file);
+  sendheader(
+    "$server:$port",
+    "GET $proxy_prefix/fop/$to/$from/$file?id=$sid&DELETE HTTP/1.1",
+    "User-Agent: $useragent",
+  );
+  
+  while (<$SH>) {
+    s/\r//;
+    printf "<-- $_"if $opt_v;
+    last if /^\s*$/;
+  }
+}
+
+
+sub urlencode {
+  local $_ = shift;
+  s/([^_=:,;<>()+.\w\-])/'%'.uc(unpack("H2",$1))/ge;
+  return $_;
+}
+
+
 sub send_fex {
   my @to;
   my $file = '';
@@ -1194,7 +1606,7 @@ sub send_fex {
 
   if ($anonymous) {
     my $aok;
-    sendheader("$server:$port","OPTIONS FEX HTTP/1.1");
+    sendheader("$server:$port","OPTIONS /FEX HTTP/1.1");
     $_ = <$SH>||'';
     s/\r//;
     die "$0: no response from fex server $server\n" unless $_;
@@ -1287,10 +1699,21 @@ sub send_fex {
   }
 
   if (@ARGV > 1 and not ($opt_a or $opt_s or $opt_d)) {
-    print "Archive name (name.tar, name.tgz or name.zip) or [ENTER] to send file for file:\n";
+    print "Archive name (name.tar, name.tgz or name.zip) or [RETURN] to send file for file:\n";
     $opt_a = <STDIN>;
     $opt_a =~ s/^\s+//;
     $opt_a =~ s/\s+$//;
+    $opt_a =~ s/\//_/g;
+  }
+
+  if ($macos and not $opt_a and -d "@ARGV") {
+    my $dir = "@ARGV";
+    my $qdir = quote($dir);
+    if (`du -s $qdir` =~ /^(\d+)/ and $1 < 2**21) {
+      $opt_a = "$dir.zip";
+    } else {
+      $opt_a = "$dir.tar";
+    }
   }
 
   if ($opt_s) {
@@ -1317,7 +1740,7 @@ sub send_fex {
       $opt_a =~ s:.*/::g;
     }
     foreach my $file (@ARGV) {
-      die "$0: cannot read $file\n" unless -l $file or -r $file;
+      die "$0: cannot read \"$file\"\n" unless -l $file or -r $file;
     }
     $opt_a .= ".$atype" if $opt_a !~ /\.$atype$/;
     $transferfile = "$tmpdir/$opt_a";
@@ -1329,6 +1752,10 @@ sub send_fex {
         # else        { system(qw'7z a -tzip -mm=copy',$transferfile,@ARGV) }
         system(qw'7z a -tzip',$transferfile,@ARGV);
         @files = ($transferfile);
+      } elsif ($macos and scalar(@ARGV) == 1) {
+        ## ditto-zip is now handled by formdatapost()
+        system 'true';
+        @files = ($opt_a);
       } else {
         # zip archives must be < 2 GB, so split as necessary
         @files = zipsplit($transferfile,@ARGV);
@@ -1358,6 +1785,7 @@ sub send_fex {
       } else {
         ## tar is now handled by formdatapost()
         # system(qw'tar cvf',$transferfile,@ARGV);
+        system 'true';
         @files = ($opt_a);
       }
     } elsif ($atype eq 'tgz') {
@@ -1403,12 +1831,12 @@ sub send_fex {
       unless ($opt_d) {
         unless (-f $file) {
           if (-e $file) {
-            die "$0: $file is not a regular file, try option -a\n"
+            die "$0: \"$file\" is not a regular file, try option -a\n"
           } else {
-            die "$0: $file does not exist\n";
+            die "$0: \"$file\" does not exist\n";
           }
         }
-        die "$0: cannot read $file\n" unless -r $file;
+        die "$0: cannot read \"$file\"\n" unless -r $file;
       }
       push @files,$file;
     }
@@ -1418,7 +1846,7 @@ sub send_fex {
     foreach my $file (@files) {
       my @s = stat($file);
       unless (@s and ($s[2] & S_IROTH) and -r $file) {
-        die "$0: $file is not world readable\n";
+        die "$0: \"$file\" is not world readable\n";
       }
     }
   }
@@ -1426,7 +1854,7 @@ sub send_fex {
   foreach my $file (@files) {
     sleep 1;    # do not overrun server!
     unless (-s $file or $opt_d or $opt_a or $opt_s) {
-      die "$0: cannot send empty file $file\n";
+      die "$0: cannot send empty file \"$file\"\n";
     }
     female_mode("send file $file?") if $opt_F;
     @r = formdatapost(
@@ -1443,15 +1871,32 @@ sub send_fex {
     if (not @r or not grep /\w/,@r) {
       die "$0: no response from server\n";
     }
+    next if "@r" eq '0'; # already transfered
     if (($r) = grep /^ERROR:/,@r) {
       if ($anonymous and $r =~ /purge it/) {
         die "$0: file is already on server for $to - use another anonymous recipent\n";
+      } elsif ($r =~ /timeout/i) {
+        close $SH;
+        retry("timed out");
       } else {
         $r =~ s/.*?:\s*//;
         $r =~ s/<.+?>//g;
         die "$0: server error: $r\n";
       }
     }
+    unless ($opt_d) {
+      if (scalar(@r) == 1) {
+        die "$0: server error: @r\n";
+      } else {
+        if ($r[0] !~ /HTTP.1.. 2/) {
+          if ($r[0] =~ /HTTP.[\s\d.]+(.+)/) {
+            die "$0: server error: $1\n";
+          } else {
+            die "$0: server error:\n".join("\n",@r)."\n";
+          }
+        }
+      }
+    }
     if (($r) = grep /<h3>\Q$file/,@r) {
       $r =~ s/<.+?>//g;
       print "$r\n";
@@ -1459,7 +1904,8 @@ sub send_fex {
     if ($opt_a !~ /^afex_\d+\.tar$/ and $file !~ /afex_\d+\.tar$/) {
       # print grep({s/^(X-Recipient:.*\((.+)\))/Parameters: $2\n/i} @r);
       my $nonot = 0;
-      my ($recipient,$location);
+      my $recipient = '';
+      my $location = '';
       foreach (@r) {
         if (/^(X-)?(Recipient.*)/i) {
           $recipient = $2;
@@ -1468,23 +1914,13 @@ sub send_fex {
         }
         if (/^(X-)?(Location.*)/i) {
           $location = $2;
-          if ($from eq $to or $from =~ /^\Q$to\E@/i
-              or $nomail or $anonymous or $nonot) {
-            print "$recipient\n";
-            print "$location\n";
-          }
         }
       }
-      unless ($opt_d or $location) {
-        if (scalar(@r) == 1) {
-          die "$0: server error: @r\n";
-        } else {
-          if ($r[0] !~ /HTTP.1.. 2/ and $r[0] =~ /HTTP.[\s\d.]+(.+)/) {
-            die "$0: server error: $1\n";
-          } else {
-            die "$0: server error:\n".join("\n",@r)."\n";
-          }
-        }
+      if ($from eq $to or $from =~ /^\Q$to\E@/i
+          or $nomail or $anonymous or $nonot) 
+      {
+        print "$recipient\n" if $recipient;
+        print "$location\n"  if $location;
       }
     }
   }
@@ -1518,10 +1954,10 @@ sub forward {
 
   open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
   while (<$fexlist>) {
-    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_f) {
+    if (/^\s*(\d+)\) (\w+) .\s*\d+ d. ([+-] )?(.+)/ and $1 eq $opt_f) {
       $n = $1;
       $dkey = $2;
-      $file = $3;
+      $file = $4;
       if ($file =~ s/ "(.*)"$//) {
         $opt_C ||= $1 if $1 ne 'NOMAIL';
       }
@@ -1572,7 +2008,7 @@ sub renotify {
 
   open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
   while (<$fexlist>) {
-    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_N) {
+    if (/^\s*(\d+)\) (\w+) .\s*\d+ d. (.+)/ and $1 eq $opt_N) {
       $n = $1;
       $dkey = $2;
       last;
@@ -1628,7 +2064,7 @@ sub modify {
 
   open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
   while (<$fexlist>) {
-    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_x) {
+    if (/^\s*(\d+)\) (\w+) .\s*\d+ d. (.+)/ and $1 eq $opt_x) {
       $n = $1;
       $dkey = $2;
       $file = $3;
@@ -1738,7 +2174,7 @@ sub get_xx {
 
 sub formdatapost {
   my %P = @_;
-  my ($boundary,$filename,$filesize,$length,$buf,$file,$fpsize,$resume,$seek);
+  my ($boundary,$filename,$length,$buf,$file,$fpsize,$resume,$seek);
   my ($flink);
   my (@hh,@hb,@r,@pv,$to);
   my ($bytes,$t,$bt);
@@ -1746,9 +2182,11 @@ sub formdatapost {
   my $bs = 2**16;        # blocksize for reading and sending file
   my $fileid = int(time);
   my $chunk = 0;
+  my $filesize = 0;
   my $connection = '';
   my $pct = '';
-  my ($tar,$aname,$atype,$tarlist,$tarerror,$location,$transferfile);
+  my $dittodir = '.';
+  my ($tar,$ditto,$aname,$atype,$list,$error,$location,$transferfile);
   local $_;
 
   if (defined($file = $P{file})) {
@@ -1771,7 +2209,7 @@ sub formdatapost {
       $of =~ s/([^_\w\.\-])/\\$1/g;
       shelldo("gzip <$if>$of");
       $filesize = -s $transferfile;
-      die "$0: cannot gzip $file\n" unless $filesize;
+      die "$0: cannot gzip \"$file\"\n" unless $filesize;
       $file = $transferfile;
     }
 
@@ -1779,12 +2217,12 @@ sub formdatapost {
     if (not $windoof and $opt_a and $file =~ /(.+)\.(tar|tgz)$/) {
       $aname = $1;
       $atype = $2;
-      $tarlist  = "$tmpdir/$aname.list";
-      $tarerror = "$tmpdir/$aname.error";
+      $list  = "$tmpdir/$aname.list";
+      $error = "$tmpdir/$aname.error";
       $tar = 'tar -cv';
       $tar .= 'z' if $atype eq 'tgz';
       if (`tar --help 2>/dev/null` =~ /--index-file/) {
-        $tar .= " --index-file=$tarlist -f-";
+        $tar .= " --index-file=$list -f-";
       } else {
         $tar .= " -f-";
       }
@@ -1794,12 +2232,10 @@ sub formdatapost {
         }
       }
       foreach (@ARGV) {
-        $file = $_;
-        $file =~ s/([^\w\-\@\#%,.=+~_:])/\\$1/g;
-        $tar .= ' '.$file;
+        $tar .= ' '.quote($_);
       }
       # print "calculating archive size... ";
-      open $tar,"$tar 2>$tarerror|" or die "$0: cannot run tar - $!\n";
+      open $tar,"$tar 2>$error|" or die "$0: cannot run tar - $!\n";
       $t0 = int(time) if -t STDOUT;
       while ($b = read $tar,$_,$bs) {
         $filesize += $b;
@@ -1814,12 +2250,12 @@ sub formdatapost {
       printf "Archive size: %d MB\n",int($filesize/M) if -t STDOUT;
       unless (close $tar) {
         $_ = '';
-        if (open $tarerror,$tarerror) {
+        if (open $error,$error) {
           local $/;
-          $_ = <$tarerror>;
-          close $tarerror;
+          $_ = <$error>;
+          close $error;
         }
-        unlink $tarlist,$tarerror;
+        unlink $list,$error;
         die "$0: tar error:\n$_";
       }
       $file = "$aname.$atype";
@@ -1827,6 +2263,58 @@ sub formdatapost {
       undef $SH; # force reconnect (timeout!)
     }
 
+    # special file: ditto-zip-on-the-fly
+    # ditto: Can't archive multiple sources
+    elsif ($macos and $opt_a and $file =~ /(.+)\.(zip)$/ and scalar(@ARGV) == 1) {
+      $aname = $1;
+      $atype = $2;
+      $list  = "$tmpdir/$aname.list";
+      $error = "$tmpdir/$aname.error";
+      $ditto = 'ditto -c -k --sequesterRsrc --keepParent';
+      if (-d "@ARGV" and "@ARGV" =~ m:^(.+)/(.+):) {
+        $dittodir = $1;
+        $file = $2;
+        $file =~ s/([^\w\-\@\#%,.=+_:])/\\$1/g;
+        $ditto .= ' '.$file;
+      } else {
+        foreach (@ARGV) {
+          $file = $_;
+          $file =~ s/([^\w\-\@\#%,.=+_:])/\\$1/g;
+          $ditto .= ' '.$file;
+        }
+      }
+      # print "calculating archive size... ";
+      debug("cd $dittodir;$ditto -");
+      open $ditto,"cd $dittodir;$ditto - 2>$error|" 
+        or die "$0: cannot run ditto - $!\n";
+      $t0 = int(time) if -t STDOUT;
+      while ($b = read $ditto,$_,$bs) {
+        $filesize += $b;
+        if ($t0) {
+          $t1 = int(time);
+          if ($t1>$t0) {
+            printf "Archive size: %d MB\r",int($filesize/M);
+            $t0 = $t1;
+          }
+        }
+      }
+      printf "Archive size: %d MB\n",int($filesize/M) if -t STDOUT;
+      unless (close $ditto) {
+        $_ = '';
+        if (-s $error and open $error,$error) {
+          local $/;
+          $_ = <$error>;
+          close $error;
+        }
+        unlink $list,$error;
+        die "$0: ditto-zip error:\n$_";
+      }
+      unlink $list,$error;
+      $file = "$aname.$atype";
+      $filename = encode_utf8($file);
+      undef $SH; # force reconnect (timeout!)
+    }
+
     # single file
     else {
       $filename = encode_utf8(${'opt_='} || $file);
@@ -1840,7 +2328,7 @@ sub formdatapost {
       if ($opt_d) {
         $filesize = 0;
       } elsif (not $opt_g and not $opt_s) {
-        $filesize = -s $file or die "$0: $file is empty or not readable\n";
+        $filesize = -s $file or die "$0: \"$file\" is empty or not readable\n";
       }
     }
 
@@ -1880,20 +2368,28 @@ sub formdatapost {
 
   $P{id} = $sid; # ugly hack!
 
+  $filename =~ s/\\/_/g; # \ is a illegal character for fexsrv
+
   # ask server if this file has been already sent
-  if ($file and not $xx and not
-      ($opt_s or $opt_g or $opt_o or $opt_d or $opt_l or $opt_L or ${'opt_/'}))
-  {
-    ($seek,$location) = query_file($server,$port,$frecipient||$P{to},$P{from},
-                                   $P{id},$filename,$fileid);
-    if ($filesize == $seek) {
-      print "Location: $location\n" if $location and $nomail;
-      warn "$0: $file has been already transferred\n";
-      return $file;
-    } elsif ($seek and $seek < $filesize) {
-      $resume = " (resuming at byte $seek)";
-    } elsif ($filesize <= $seek) {
-      $seek = 0;
+  if ($file and not $xx) {
+    if (not $opt_d and $opt_o) {
+      # delete before overwrite
+      delete_file($from,$to,$filename);
+      serverconnect($server,$port);
+      query_sid($server,$port) unless $anonymous;
+      $P{id} = $sid; # ugly hack!
+    } elsif (not($opt_s or $opt_g or $opt_d or $opt_l or $opt_L or ${'opt_/'})) {
+      ($seek,$location) = query_file($server,$port,
+        $frecipient||$P{to},$P{from},$P{id},$filename,$fileid);
+      if ($filesize == $seek) {
+        print "Location: $location\n" if $location and $nomail;
+        warn "$0: $file has been already transferred\n";
+        return 0;
+      } elsif ($seek and $seek < $filesize) {
+        $resume = " (resuming at byte $seek)";
+      } elsif ($filesize <= $seek) {
+        $seek = 0;
+      }
     }
     if ($proxy) {
       sleep 1;    # do not overrun proxy
@@ -1935,7 +2431,8 @@ sub formdatapost {
       push @hb,"--$boundary";
       push @hb,"Content-Disposition: form-data; name=\"$name\"";
       push @hb,"";
-      push @hb,encode_utf8($P{$v});
+      # push @hb,encode_utf8($P{$v});
+      push @hb,$P{$v};
     }
   }
 
@@ -2027,10 +2524,10 @@ sub formdatapost {
           $tpid = fork();
           if (defined $tpid and $tpid == 0) {
             sleep 1;
-            if (open $tarlist,$tarlist) {
-              # print "\n$tar|\n"; system "ls -l $tarlist";
-              while ($tarlist) {
-                while (<$tarlist>) {
+            if (open $list,$list) {
+              # print "\n$tar|\n"; system "ls -l $list";
+              while ($list) {
+                while (<$list>) {
                   print ' 'x(length($file)+40),"\r",$_;
                 }
                 sleep 1;
@@ -2044,13 +2541,19 @@ sub formdatapost {
           print "Fast forward to byte $seek (resuming)\n";
           readahead($file,$seek);
         }
+      } elsif ($ditto) {
+        $ditto =~ s/ditto/ditto -V/;
+        open $file,"cd $dittodir;$ditto -|" or die "$0: cannot run ditto - $!\n";
+        if ($seek) {
+          print "Fast forward to byte $seek (resuming)\n";
+          readahead($file,$seek);
+        }
       } else {
         if ($opt_g) {
-          my $fileq = $file;
-          $fileq =~ s/([^\w\-\@\#%,.=+~_:])/\\$1/g;
+          my $fileq = quote($file);
           open $file,"gpg -e -r $to <$fileq|" or die "$0: cannot run gpg - $!\n";
         } else {
-          open $file,$file or die "$0: cannot read $file - $!\n";
+          open $file,$file or die "$0: cannot read \"$file\" - $!\n";
           seek $file,$seek,0;
         }
         binmode $file;
@@ -2072,7 +2575,11 @@ sub formdatapost {
         alarm(0);
         $bytes += $b;
         if ($filesize > 0 and $bytes+$seek > $filesize) {
-          die "$0: $file filesize has grown while uploading\n";
+          if ($tpid) {
+            kill 9,$tpid;
+            unlink $list;
+          }
+          die "$0: \"$file\" filesize has grown while uploading\n";
         }
         $bt += $b;
         $t2 = time;
@@ -2121,12 +2628,26 @@ sub formdatapost {
       if ($tpid) {
         sleep 2;
         kill 9,$tpid;
-        unlink $tarlist;
+        unlink $list;
       }
-
+      
+      if ($fileid =~ /[a-z]/ and not ($opt_s or $opt_g)) {
+        if ($opt_a) {
+          if ($fileid ne md5_hex(fmd(@ARGV))) {
+            print "\n" unless $opt_q;
+            die "$0: files have been modified while uploading\n";
+          }
+        } else {
+          if ($fileid ne fileid($file)) {
+            print "\n" unless $opt_q;
+            die "$0: file has been modified while uploading\n";
+          }
+        }
+      }
+      
       unless ($opt_q) {
         if (not $chunksize and $bytes+$seek < $filesize) {
-          die "$0: $file filesize has shrunk while uploading\n";
+          die "$0: \"$file\" filesize has shrunk while uploading\n";
         }
 
         if ($seek or $chunksize and $chunksize < $filesize) {
@@ -2270,7 +2791,7 @@ sub zipsplit {
       $size = -s $file;
       if ($size > 2147480000) {
         unlink @zipfiles;
-        die "$0: $file too big for zip\n";
+        die "$0: \"$file\" too big for zip\n";
       }
       if ($zsize + $size > 2147000000) {
         push @zipfiles,zip($zipbase.'_'.$n.'.zip',@files);
@@ -2336,7 +2857,7 @@ sub query_file {
   my $seek = 0;
   my $qfileid = '';
   my ($head,$location);
-  my ($response,$fexsrv);
+  my ($response,$fexsrv,$cc);
   local $_;
 
   $to =~ s/,.*//;
@@ -2377,11 +2898,17 @@ sub query_file {
     if (/^X-File-ID:\s+(.+)/)          { $qfileid = $1 }
     if (/^X-Features:\s+(.+)/)         { $features = $1 }
     if (/^X-Location:\s+(.+)/)         { $location = $1 }
+    if (/^Connection: close/)           { $cc = $_ }
   }
 
   # return true seek only if file is identified
   $seek = 0 if $qfileid and $qfileid ne $fileid;
 
+  if ($cc) {
+    serverconnect($server,$port);
+    $sid = $id;
+  }
+
   return ($seek,$location);
 }
 
@@ -2409,7 +2936,7 @@ sub edit_address_book {
   print {$ab} $AB{ADDRESS_BOOK};
   close $ab;
 
-  system $editor,$ab;
+  system "$editor $ab";
   exit unless -s $ab;
 
   $opt_o = $opt_A;
@@ -2510,14 +3037,12 @@ sub query_sid {
 
   $sid = $id;
 
-  if ($port eq 443) {
+  if ($port eq 443 or $proxy) {
     return if $features;    # early return if we know enough
-    $req = "OPTIONS FEX HTTP/1.1";
-  } elsif ($proxy) {
-    return if $features;    # early return if we know enough
-    $req = "GET $proxy_prefix/SID HTTP/1.1";
+    $req = "OPTIONS /FEX HTTP/1.1";
+    $req = "HEAD / HTTP/1.1";
   } else {
-    $req = "GET SID HTTP/1.1";
+    $req = "GET /SID HTTP/1.1";
   }
 
   sendheader("$server:$port",$req,"User-Agent: $useragent");
@@ -2529,16 +3054,44 @@ sub query_sid {
   s/\r//;
   print "<-- $_" if $opt_v;
 
-  if (/^HTTP.* [25]0[01] /) {
+  if ($req =~ /OPTIONS/ and /^HTTP.* 502 /) {
+    # (reverse) proxy error
+    close $SH;
+    serverconnect($server,$port);
+    $req = "GET /SID HTTP/1.0";
+    sendheader("$server:$port",$req,"User-Agent: $useragent");
+    $_ = <$SH>;
+    unless (defined $_ and /\w/) {
+      print "\n" if $opt_v;
+      die "$0: no response from server\n";
+    }
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    while (<$SH>) {
+      s/\r//;
+      print "<-- $_" if $opt_v;
+      $features = $1 if /^X-Features: (.+)/;
+      $timeout = $1  if /^X-Timeout: (\d+)/;
+      last if /^\n/;
+    }
+    close $SH;
+    serverconnect($server,$port);
+  } elsif (/^HTTP.* [25]0[01] /) {
     if (not $proxy and $port ne 443 and /^HTTP.* 201 (.+)/) {
       $sid = 'MD5H:'.md5_hex($id.$1);
     }
+    my $cc;
     while (<$SH>) {
       s/\r//;
       print "<-- $_" if $opt_v;
       $features = $1 if /^X-Features: (.+)/;
       $timeout = $1  if /^X-Timeout: (\d+)/;
-      last if /^\n/;
+      $cc = $_       if /^Connection: close/;
+      last           if /^\n/;
+    }
+    if ($cc) {
+      serverconnect($server,$port);
+      $sid = $id;
     }
   } elsif (/^HTTP.* 301 /) {
     while (<$SH>) { last if /Location/ }
@@ -2588,15 +3141,15 @@ sub xxget {
 
   die "$0: no Content-Length in server-reply\n" unless $cl;
 
-  open F,">$save" or die "$0: cannot write to $save - $!\n";
-  binmode F;
+  open $save,">$save" or die "$0: cannot write to $save - $!\n";
+  binmode $save;
 
   $t0 = $t1 = int(time);
   $tso = '';
 
   while ($b = read($SH,$_,$bs)) {
     $B += $b;
-    print F;
+    print {$save} $_;
     if (int(time) > $t1) {
       $t1 = int(time);
       $ts = ts($B,$cl);
@@ -2609,7 +3162,7 @@ sub xxget {
   }
 
   print STDERR ts($B,$cl),"\n";
-  close F;
+  close $save;
 }
 
 
@@ -2745,10 +3298,16 @@ sub readahead {
 }
 
 
-# fileid is inode and mtime
 sub fileid {
-  my @s = stat(shift);
-  return @s ? $s[1].$s[9] : int(time);
+  my $file = shift;
+  my @s = stat($file);
+  
+  if (@s) {
+    return md5_hex($file.$s[0].$s[1].$s[7].$s[9]);
+  } else {
+    warn "$0: $file - $!\n";
+    return int(time);
+  }
 }
 
 
@@ -2768,7 +3327,7 @@ sub get_mutt_alias {
       s/.*\s+//;
       s/[<>]//g;
       if (/,/) {
-        warn "$0: ignoring mutt multi-alias $to = $alias\n";
+        warn "$0: ignoring mutt multi-alias $to = $_\n";
         last;
       }
       if (/@/) {
@@ -2783,7 +3342,7 @@ sub get_mutt_alias {
 }
 
 
-# collect file meta data (filename, inode, mtime)
+# collect (hashed) file meta data
 sub fmd {
   my @files = @_;
   my ($file,$dir);
@@ -2796,7 +3355,7 @@ sub fmd {
         while (defined ($file = readdir($dir))) {
           next if $file eq '..';
           if ($file eq '.') {
-            $fmd .= $file.fileid($dir);
+            $fmd .= fileid($dir);
           } else {
             $fmd .= fmd("$dir/$file");
           }
@@ -2804,7 +3363,7 @@ sub fmd {
         closedir $dir;
       }
     } else {
-      $fmd .= $file.fileid($file);
+      $fmd .= fileid($file);
     }
   }
 
@@ -2862,8 +3421,8 @@ sub http_response {
   unless (defined $_ and /\w/) {
     die "$0: no response from server\n";
   }
-  print "<-- $_\n" if $opt_v;
   s/\r?\n//;
+  print "<-- $_\n" if $opt_v;
   # CGI fatalsToBrowser
   if (/^HTTP.* 500/) {
     @r = <$SH> unless @r;
@@ -2883,7 +3442,6 @@ sub http_response {
     die "$0: server error: $error\n";
   }
 
-  print "<-- $_\n" if $opt_v;
   return $_;
 }
 
@@ -2901,12 +3459,11 @@ sub update {
   local $/;
 
   open $0,$0 or die "cannot read $0 - $!\n";
-  $_ = <$0>;
+  $cfc = <$0>;
   close $0;
-  s/.*\n$cfb\n//s;
-  $cfc = $_;
+  $cfc =~ s/.*\n$cfb\n//s;
 
-  foreach my $p (qw(fexget sexsend)) {
+  foreach my $p (qw'fexget sexsend') {
     open $p,$p or die "cannot read $p - $!\n";
     $_ = <$p>;
     close $p;
@@ -2917,7 +3474,7 @@ sub update {
     close $p;
   }
 
-  exec "l $0 fexget sexsend";
+  exec "l fexsend fexget sexsend";
   exit;
 }
 
@@ -3039,6 +3596,7 @@ sub tcpconnect {
 
   if ($SH) {
     autoflush $SH 1;
+    binmode $SH;
   } else {
     die "$0: cannot connect $server:$port - $@\n";
   }
@@ -3095,6 +3653,18 @@ sub nvtsend {
 }
 
 
+sub quote {
+  local $_ = shift;
+  s/([^\w\@\/%^,.=+_:+-])/\\$1/g;
+  return $_;
+}
+
+
+sub debug {
+  print "## DEBUG: @_\n" if $DEBUG;
+}
+
+
 # from MIME::Base64::Perl
 sub encode_b64 {
   my $res = "";
index ff3f1ed5d367d8cd5e12c3bb12f82dc7e203f212..d9fe821de487cd5e3733877c9009560286081c1d 100755 (executable)
@@ -19,7 +19,8 @@ use constant M => 2**20;
 
 eval 'use Net::INET6Glue::INET_is_INET6';
 
-our $version = 20150826;
+our $version = 20160104;
+our $DEBUG = $ENV{DEBUG};
 
 my %SSL = (SSL_version => 'TLSv1');
 my $sigpipe;
@@ -142,6 +143,8 @@ if ($0 eq 'sexxx') {
   }
 
 } elsif ($0 eq 'sexget' or $0 eq 'fuckme') {
+
+  $opt_g = 0;
   getopts('hgvVdu:') or die $usage;
   die $usage if $opt_h;
 
@@ -175,7 +178,7 @@ if ($0 eq 'sexxx') {
 
 } else { # sexsend
 
-  $opt_g = 1;
+  $opt_g = 0;
   getopts('hguvqVTt:') or die $usage;
   die $usage if $opt_h;
 
@@ -605,15 +608,9 @@ sub serverconnect {
   my $connect = "CONNECT $server:$port HTTP/1.1";
   local $_;
 
-  if ($opt_v and $port == 443 and %SSL) {
-    foreach my $v (keys %SSL) {
-      printf "%s => %s\n",$v,$SSL{$v};
-    }
-  }
-
   if ($proxy) {
     tcpconnect(split(':',$proxy));
-    if ($port == 443) {
+    if ($https) {
       printf "--> %s\n",$connect if $opt_v;
       nvtsend($connect,"");
       $_ = <$SH>;
@@ -622,14 +619,13 @@ sub serverconnect {
       unless (/^HTTP.1.. 200/) {
         die "$0: proxy error : $_";
       }
-      eval "use IO::Socket::SSL";
-      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      &enable_ssl;
       $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
     }
   } else {
     tcpconnect($server,$port);
   }
-#  if ($port == 443 and $opt_v) {
+#  if ($https and $opt_v) {
 #    printf "%s\n",$SH->get_cipher();
 #  }
 }
@@ -644,10 +640,9 @@ sub tcpconnect {
     undef $SH;
   }
 
-  if ($port == 443) {
+  if ($https) {
     # eval "use IO::Socket::SSL qw(debug3)";
-    eval "use IO::Socket::SSL";
-    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    &enable_ssl;
     $SH = IO::Socket::SSL->new(
       PeerAddr => $server,
       PeerPort => $port,
@@ -664,6 +659,7 @@ sub tcpconnect {
 
   if ($SH) {
     autoflush $SH 1;
+    binmode $SH;
   } else {
     die "$0: cannot connect $server:$port - $@\n";
   }
@@ -672,6 +668,18 @@ sub tcpconnect {
 }
 
 
+sub enable_ssl {
+  eval "use IO::Socket::SSL";
+  die "$0: cannot load IO::Socket::SSL\n" if $@;
+  eval '$SSL{SSL_verify_mode} = 0 if Net::SSLeay::SSLeay() <= 9470143';
+  if ($opt_v) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
+}
+
+
 sub sendheader {
   my $sp = shift;
   my @head = @_;
@@ -708,6 +716,18 @@ sub nvtsend {
 }
 
 
+sub quote {
+  local $_ = shift;
+  s/([^\w\@\/%^,.=+_:+-])/\\$1/g;
+  return $_;
+}
+
+
+sub debug {
+  print "## DEBUG: @_\n" if $DEBUG;
+}
+
+
 # from MIME::Base64::Perl
 sub encode_b64 {
   my $res = "";
diff --git a/htdocs/fexit.html b/htdocs/fexit.html
new file mode 100644 (file)
index 0000000..3bbfeb0
--- /dev/null
@@ -0,0 +1,44 @@
+<html>
+<head>
+  <title>fexit</title>
+  <style>
+    pre {
+      padding: 8px;
+      background: #eef;
+      border: thin dashed #0076A3;
+      font-family: courier;
+    }
+  </style>
+</head>
+<body>
+
+<<$ENV{FEXIT} = 'http://fex.belwue.de/download/fexit.exe';'';>>
+
+<h1><a href="$FEXIT$">fexit</a> for Windows</h1>
+
+<a href="$FEXIT$">fexit</a> is a <a href="/index.html">F*EX</a>
+client for sending files of any size to any e-mail address.<br>
+<a href="$FEXIT$">fexit</a> can also send directories or
+download files and resume the upload or download after link failures,
+which your webbrowser cannot do.
+<p>
+You can start <a href="$FEXIT$">fexit</a> via Windows explorer or
+via command console (cmd).<br>
+You can also drag files or directories to the fexit icon with the Windows
+explorer.
+<p>
+#if $ENV{ID}
+When you run <a href="$FEXIT$">fexit</a> for the first time you are 
+asked for the "F*EX server URL". You can enter your personal URL:
+<p>
+<code>
+<<"$ENV{PROTO}://$ENV{HTTP_HOST}/fup/".b64("from=$ENV{USER}&id=$ENV{ID}")>>
+</code>
+<p>
+(use your mouse for copy+paste)
+<p>
+<<require "./fexitinstaller";''>>
+#endif
+##<pre><<foreach $v (keys %ENV) { printf "%s=%s\n",$v,$ENV{$v} }>>
+</body>
+</html>
diff --git a/htdocs/fexitinstaller b/htdocs/fexitinstaller
new file mode 100644 (file)
index 0000000..3054e57
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/perl -w
+
+$user = $ENV{USER};
+$id = $ENV{ID};
+$url = "$ENV{PROTO}://$ENV{HTTP_HOST}";
+$fi = 'fexitinstaller.cmd';
+$fe = 'http://fex.belwue.de/download/fexit.exe';
+$ps = '%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe';
+$cmd = <<EOD;
+mkdir "%USERPROFILE%\\fex"
+cd "%USERPROFILE%\\fex"
+if not exist id (
+  echo $url>id
+  echo $user>>id
+  echo $id>>id
+)
+cd "%USERPROFILE%\\Desktop"
+$ps -command "& { (New-Object Net.WebClient).DownloadFile('$fe','fexit.exe') }"
+\@explorer "%USERPROFILE%\\Desktop"
+\@set /p x="press [ENTER]"
+EOD
+$cmd =~ s/\n/\r\n/g;
+
+if (chdir "$spooldir/$user" and open $fi,'>',$fi) {
+  print {$fi} $cmd;
+  close $fi;
+  system "$FEXHOME/bin/fexsend ".
+         "-oKq -C 'fexit for your Windows desktop' $fi $user >/dev/null";
+  if ($? == 0) {
+    print "<p>\n";
+    print "<h3>A fexit installer has been sent to you. Check your email.</h3>\n";
+  }
+  unlink $fi;
+}
diff --git a/htdocs/macfexsend.html b/htdocs/macfexsend.html
new file mode 100644 (file)
index 0000000..2c17c78
--- /dev/null
@@ -0,0 +1,65 @@
+<html>
+<head>
+  <title>fexsend for Mac</title>
+  <style>
+    pre {
+      padding: 8px;
+      background: #eef;
+      border: thin dashed #0076A3;
+      font-family: courier;
+    }
+  </style>
+</head>
+<body>
+
+<h1>fexsend for Apple Macintosh OS X</h1>
+
+fexsend is a <a href="/index.html">F*EX</a> client for sending files of
+any size to any e-mail address.<br>
+fexsend can also send directories and resume the upload
+after a link failure, which your webbrowser cannot do.
+
+<h3>To install fexsend:</h3>
+<ol>
+<li> start a "Terminal":<br>
+     go to <mark>Finder</mark>, press &#8679;&#8984;U to open the Utility 
+     Application folder and double-click <mark>Terminal.app</mark> with left 
+     mouse button
+<p>
+<li> copy this code into the Terminal window:
+     <pre>curl http://fex.belwue.de/download/fexsend.command|bash</pre>
+     Do not forget to enter [RETURN]
+     <p>
+     You can use your mouse for copying:
+     <p>
+     <ol>
+       <li>move your mouse cursor with hold left mouse button over the
+           code obove (the code will be marked)
+       <li>press &#8984;C
+       <li>move your mouse cursor into the Terminal window
+       <li>click left mouse button
+       <li>press &#8984;V
+     </ol>
+</ol>
+
+<h3>To run fexsend:</h3>
+<ol>
+  <li> in <mark>Finder</mark> click <mark>Desktop</mark> with left mouse button
+  <li> double-click <mark>fexsend.command</mark> with left mouse button
+</ol>
+<p>
+<<
+  if ($ENV{ID}) {
+    my $url = "$ENV{PROTO}://$ENV{HTTP_HOST}/fup/"
+            . b64("from=$ENV{USER}&id=$ENV{ID}");
+    qq(
+      When asked for "F*EX server URL" enter:
+      <p>
+      <code>$url</code>
+      <p>
+      (again, use your mouse for copy+paste)
+    );
+  }
+>>
+</body>
+</html>
index 2f417443d8bc38921f1d4344754a81f1b3de76db..84e377c178550a2899b03aaa7c19bb3619bd1d40 100644 (file)
@@ -4,29 +4,49 @@
 <center></center>
 <h1> <a href="/">F*EX</a> tools</h1>
 
-<<$ENV{TA}='http://fex.belwue.de';''>>
+<<$ENV{TA} = 'http://fex.belwue.de';'';>>
 
+#if $ENV{AKEY}
+To use one of the following F*EX clients you must configure them.
+See <a href="/foc">user config</a> for your account data.
+<p>
+#endif
+
+<h3>UNIX:</h3>
 <table border=1>
 <tr><td><a href="/download/fexsend">fexsend</a>
-    <td>UNIX CLI client for sending files (with many 
+    <td>client for sending files (with many 
         <a href="$TA$/fstools/fexsend.html">
         additional features</a>)</tr>
 <tr><td><a href="/download/fexget">fexget</a>
-    <td>UNIX CLI client for receiving files (with many 
+    <td>client for receiving files (with many 
         <a href="$TA$/fstools/fexget.html">
         additional features</a>)</tr>
-<tr><td><a href="$TA$/download/fexget.exe">fexget</a>
-    <td>Windows CLI client for receiving files
 <tr><td><a href="/download/sex.tar">sexsend, sexget</a>
-    <td>UNIX CLI clients for sending and receiving streams</tr>
+    <td>clients for sending and receiving streams</tr>
+</table>
+<p>
+<h3>Windows:</h3>
+<table border=1>
+<!--
 <tr><td><a href="$TA$/download/schwuppdiwupp.exe">schwuppdiwupp</a>
-    <td>Windows GUI client for sending files</tr>
-<tr><td><a href="$TA$/download/macschwupp.tar">schwuppdiwupp</a>
-    <td>Macintosh GUI client for sending files</tr>
+    <td>GUI client for sending files</tr>
+<tr><td><a href="$TA$/download/fexget.exe">fexget</a>
+    <td>client for receiving files
+-->
+<tr><td><a href="/fexit.html">fexit</a>
+    <td>client for sending and receiving files
+</table>
+<p>
+<h3>Macintosh:</h3>
+<table border=1>
+<tr><td><a href="/macfexsend.html">fexsend.command</a>
+    <td>client for sending files and directories</tr>
 </table>
 <p>
-In opposite to most web browsers all these clients can handle files
-greater than 2 GB and are able to resume interrupted up/downloads.
+In opposite to many web browsers all these clients can handle files
+or directories greater than 2 GB and are able to resume interrupted
+up/downloads.
 <p>
 Hint for UNIX users: 
 <pre>  wget -qO- http://$HTTP_HOST$/xx.tar | tar xvf -</pre>
@@ -41,5 +61,13 @@ also installs the client programs for
   print "http://fex.rus.uni-stuttgart.de" unless -s "$docdir$a";
   print "$a\">anonymous usage</a>";
 >>
+## <hr>
+## <pre>
+## <<
+##  #while (($v,$vv) = each %ENV) { printf "%s = %s\n",$v,$vv if $vv !~ /\n/ }
+##  #foreach $v (qw'AKEY USER ID') { printf "$v = %s\n",$ENV{$v} }
+##  '';
+## >>
+## </pre>
 </BODY>
 </HTML>
index 056cc38939a7cccbb512423df0e632965548470c..366b2e90488653aa0f7e52f554bd3c37a9fda461 100644 (file)
@@ -1 +1 @@
-fex-20150826
+fex-20160104
diff --git a/install b/install
index 6f0bb62a6e556f1cbf05ec1b357c41e398e1f2fe..87dc2feffb7fbbf1931025d4e5e369640c5c90b1 100755 (executable)
--- a/install
+++ b/install
@@ -123,7 +123,14 @@ print "prerequisites checked, ok\n";
 
 unless (getpwnam('fex')) {
   print "creating user fex\n";
-  system 'useradd -s /bin/bash -c "File EXchange" -m fex';
+  system 'groupadd -g 80 fex 2>/dev/null || groupadd fex';
+  my @g = getgrnam('fex') or die "$0: cannot groupadd fex\n";
+  my $gid = $g[2];
+  if (getpwuid($gid)) {
+    system "useradd -s /bin/bash -c 'File EXchange' -g $gid -m fex"
+  } else {
+    system "useradd -s /bin/bash -c 'File EXchange' -u $gid -g $gid -m fex"
+  }
   exit $? if $?;
 }
 
@@ -213,6 +220,10 @@ if (-d "$FEXHOME/spool") {
   mkdir "$FEXHOME/spool",0700 or die "cannot mkdir $FEXHOME/spool - $!\n";
   mkdir "$FEXHOME/spool/.error",0700;
 }
+foreach my $dir (qw'.dkeys .ukeys .akeys .skeys .gkeys .xkeys .locks') {
+  mkdir "$FEXHOME/spool/$dir",0700;
+}
+  
 chownr('fex',"$FEXHOME/spool/.");
 
 # fex-VM?
@@ -316,6 +327,7 @@ if (@locales = glob "locale/*/lib/fup.pl") {
       chownr('fex',"$FEXHOME/locale/$locale");
       $hl = "$FEXHOME/htdocs/locale/$locale";
       symlink "$FEXHOME/locale/$locale/htdocs",$hl unless -l $hl;
+      chownr('fex',"$FEXHOME/htdocs/locale/$locale");
     } else { 
       push @nlocales,"./install $1\n";
     }
diff --git a/lib/dop b/lib/dop
index 20df28e46951c794ec602ca306372f2ec26eead9..b41dbc1e074d9425e4936f1bf4ed1858eb1ea86c 100755 (executable)
--- a/lib/dop
+++ b/lib/dop
@@ -12,6 +12,7 @@ use CGI::Carp qw(fatalsToBrowser);
 use Fcntl      qw(:flock :seek :mode);
 use POSIX      qw(strftime locale_h);
 use Cwd        qw(getcwd abs_path);
+use utf8;
 
 # import from fex.pp
 our ($bs,$tmpdir,@doc_dirs);
@@ -98,9 +99,10 @@ sub http_output {
     @files = ("$1.tar");
     open $file,'-|',qw'gzip -c',@files or http_error(503);
   } elsif ($file =~ /(.+)\.(tar|tgz|zip)$/ and
-           @s = lstat($streamfile = "$1.stream") and $s[4] == $<)
+           @s = lstat($streamfile = "$1.stream") and
+           ($s[4] == $< or $s[4] == 0))
   {
-    # streaming file (only if it is owned by user fex)
+    # streaming file
     chdir dirname($file);
     security_check($file);
     if (-l $streamfile and readlink($streamfile) =~ /^:(.+):$/) {
index 585e291df2dd82041395501c183cc9cce545cefe..8c3ee12d50077274013c96106a34317715ffaa37 100644 (file)
@@ -34,7 +34,7 @@ $mailmode = 'AUTO';
 ## optional: your mail domain
 ## if set it will be used as domain for every user without domain
 ## local_user ==> local_user@$mdomain
-## if not set, addresses without domains produce an error
+## if not set, addresses without domain produce an error
 # $mdomain = 'MY.MAIL.DOMAIN';
 # $admin = 'fexmaster@'.$mdomain;
 
index c6f0562d4328e1a892cf299938321739eeca256d..be911d2fd9f70b67263bec01bd85b4b92d41dfc0 100644 (file)
@@ -1,6 +1,7 @@
 #  -*- perl -*-
 
 use 5.008;
+use utf8;
 use Fcntl              qw':flock :seek :mode';
 use IO::Handle;
 use IPC::Open3;
@@ -115,13 +116,13 @@ $keep = $keep_default ||= $keep || 5;
 $fra = $ENV{REMOTE_ADDR} || '';
 $sid = $ENV{SID} || '';
 
-mkdirp($dkeydir = "$spooldir/.dkeys"); # download keys
-mkdirp($ukeydir = "$spooldir/.ukeys"); # upload keys
-mkdirp($akeydir = "$spooldir/.akeys"); # authentification keys
-mkdirp($skeydir = "$spooldir/.skeys"); # subuser authentification keys
-mkdirp($gkeydir = "$spooldir/.gkeys"); # group authentification keys
-mkdirp($xkeydir = "$spooldir/.xkeys"); # extra download keys
-mkdirp($lockdir = "$spooldir/.locks"); # download lock files
+$dkeydir = "$spooldir/.dkeys"; # download keys
+$ukeydir = "$spooldir/.ukeys"; # upload keys
+$akeydir = "$spooldir/.akeys"; # authentification keys
+$skeydir = "$spooldir/.skeys"; # subuser authentification keys
+$gkeydir = "$spooldir/.gkeys"; # group authentification keys
+$xkeydir = "$spooldir/.xkeys"; # extra download keys
+$lockdir = "$spooldir/.locks"; # download lock files
 
 if (my $ra = $ENV{REMOTE_ADDR} and $max_fail) {
   mkdirp("$spooldir/.fail");
@@ -283,8 +284,9 @@ sub http_header {
     nvt_print("Strict-Transport-Security: max-age=2851200; preload");
   }
   if ($use_cookies) {
+    $akey = md5_hex("$from:$id") if $id and $from;
     if ($akey) {
-      nvt_print("Set-Cookie: akey=$akey; Max-Age=9999; Discard");
+      nvt_print("Set-Cookie: akey=$akey; path=/; Max-Age=9999; Discard");
     }
     # if ($skey) {
     #   nvt_print("Set-Cookie: skey=$skey; Max-Age=9999; Discard");
@@ -871,12 +873,15 @@ sub debuglog {
     $debuglog = sprintf("%s/%s_%s_%s.%s",
                         $ddir,time,$$,$ENV{REQUESTCOUNT}||0,$prg);
     $debuglog =~ s/\s/_/g;
+    # http://perldoc.perl.org/perlunifaq.html#What-is-a-%22wide-character%22%3f
     # open $debuglog,'>>:encoding(UTF-8)',$debuglog or return;
     open $debuglog,'>>',$debuglog or return;
+    # binmode($debuglog,":utf8");
     autoflush $debuglog 1;
     # printf {$debuglog} "\n### %s ###\n",isodate(time);
   }
   while ($_ = shift @_) {
+    $_ = encode_utf8($_) if utf8::is_utf8($_);
     s/\n*$/\n/;
     s/<.+?>//g; # remove HTML
     print {$debuglog} $_;
@@ -948,11 +953,11 @@ sub qqq {
   my $q = "[\'\"]"; # quote delimiter chars " and '
 
   # remove first newline and look for default indention
-  s/^((\d+)?)?\n//;
+  s/^((\d+)?)?\n//;
   $i = ' ' x ($2||0);
 
   # remove trailing spaces at end
-  s/[ \t]*?$//;
+  s/[ \t]*?$//;
 
   @s = split "\n";
 
@@ -1292,7 +1297,7 @@ sub gpg_encrypt {
     "    -a -e -r $bcc -r $to"
   ) or return;
 
-  print {$po} $plain;
+  print {$po} "\n",$plain,"\n";
   close $po;
 
   $enc .= $_ while <$pi>;
@@ -1439,14 +1444,17 @@ sub notify {
   $data = "$dkeydir/$P{dkey}/data";
   $size = $bytes = -s $data;
   return unless $size;
-  if ($nowarning) {
-    $warning = '';
-  } else {
-    $warning =
-      "Please avoid download with Internet Explorer, ".
-      "because it has too many bugs.\n".
-      "We recommend Firefox or wget.";
-  }
+  $warning =
+    "We recommend fexget or fexit for download,\n".
+    "because these clients can resume the download after an interruption.\n".
+    "See $proto://$hostname/tools.html";
+  # if ($nowarning) {
+  #   $warning = '';
+  # } else {
+  #   $warning =
+  #     "Please avoid download with Internet Explorer, ".
+  #     "because it has too many bugs.\n\n";
+  # }
   if ($filename =~ /\.(tar|zip|7z|arj|rar)$/) {
     $warning .= "\n\n".
       "$filename is a container file.\n".
@@ -1543,17 +1551,16 @@ sub notify {
         or http_die("cannot start sendmail - $!");
     }
   }
+  $comment .= "\n" if $comment;
   if ($comment =~ s/^!(shortmail|\.)!\s*//i
-    or (readlink "$to/\@NOTIFICATION"||'') =~ /short/i
+    or (readlink("$to/\@NOTIFICATION")||'') =~ /short/i
   ) {
     $body = qqq(qq(
       '$comment'
-      ''
       '$download'
       '$size'
     ));
   } else {
-    $comment = "Comment: $comment\n" if $comment;
     $disclaimer = slurp("$from/\@DISCLAIMER") || qqq(qq(
       '$warning'
       ''
@@ -1564,6 +1571,7 @@ sub notify {
     ));
     $disclaimer .= "\n" . $::disclaimer if $::disclaimer;
     $body = qqq(qq(
+      '$comment'
       '$from has uploaded the file'
       '  "$filename"'
       '($size) for $receiver. Use'
@@ -1571,7 +1579,6 @@ sub notify {
       '$download'
       'to download this file within $days.'
       ''
-      '$comment'
       '$autodelete'
       ''
       '$disclaimer'
@@ -1617,21 +1624,23 @@ sub notify {
 sub reactivation {
   my ($expire,$user) = @_;
   my $fexsend = "$FEXHOME/bin/fexsend";
+  my $reactivation = "$FEXLIB/reactivation.txt";
 
   return if $nomail;
 
   if (-x $fexsend) {
+    if ($locale) {
+      my $lr = "$FEXHOME/locale/$locale/lib/reactivation.txt";
+      $reactivation = $lr if -f $lr and -s $lr;
+    }
     $fexsend .= " -M -D -k 30 -C"
                ." 'Your F*EX account has been inactive for $expire days,"
                ." you must download this file to reactivate it."
                ." Otherwise your account will be deleted.'"
-               ." $FEXLIB/reactivation.txt $user";
+               ." $reactivation $user";
     # on error show STDOUT and STDERR
-    system "$fexsend >/dev/null 2>&1";
-    if ($?) {
-      warn "$fexsend\n";
-      system $fexsend;
-    }
+    my $fo = `$fexsend 2>&1`;
+    warn $fexsend.'\n'.$fo if $?;
   } else {
     warn "$0: cannot execute $fexsend for reactivation()\n";
   }
index 25ec8e1badc91f3df0279fc9cb7474298a2189d6..24eccf795067f25b44775dbb4985dfe2b1186a50 100644 (file)
@@ -24,9 +24,9 @@ After submission you will see an upload progress bar
 (if you have javascript enabled and popups allowed).
 <p>
 <em>NOTE: Many web browsers cannot upload files > 2 GB!</em><br>
-If your file is larger you have to use a special <a href="/fuc?show=tools">F*EX client</a>
+If your file is larger you have to use a special <a href="/tools.html">F*EX client</a>
 or Firefox or Google Chrome which have no size limit.<br>
-You also need a <a href="/fuc?show=tools">F*EX client</a> for resuming interrupted uploads. Your web browser cannot do this.
+You also need a <a href="/tools.html">F*EX client</a> for resuming interrupted uploads. Your web browser cannot do this.
 <p>
 If you want to send more than one file, then put them in a zip or tar archive, 
 e.g. with <a href="http://www.7-zip.org/download.html">7-Zip</a>.
index 9bf5dd990e25ee1eebf2baa200b77a93f0b494b0..82a7a5e3c837e2454670bb614bdf2b30353e094a 100644 (file)
@@ -1,5 +1,7 @@
 # config for F*EX CGI fup
 
+use utf8;
+
 $info_1 = $info_login = <<EOD;
 <p><hr><p>
 <a href="/">F*EX (File EXchange)</a>
index a0966457f9f3c8217371e44dca51636c0e3900ef..a3b32d43a522b546a6ec27c7e6fc689ccefef328 100644 (file)
@@ -1,11 +1,18 @@
 <html>
-<head><title>F*EX FAQ</title></head>
+<head>
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+  <title>F*EX FAQ</title>
+</head>
 <body>
 
 ## <pre>
 ## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
 ## </pre>
 
+Ce document est périmé. Merci de consulter
+la <a href="/FAQ/meta.html?locale=french">FAQ anglaise</a>
+<p>
+
 << require "./faq.pl" or print $! >>
 
 </body>
index a0966457f9f3c8217371e44dca51636c0e3900ef..a3b32d43a522b546a6ec27c7e6fc689ccefef328 100644 (file)
@@ -1,11 +1,18 @@
 <html>
-<head><title>F*EX FAQ</title></head>
+<head>
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+  <title>F*EX FAQ</title>
+</head>
 <body>
 
 ## <pre>
 ## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
 ## </pre>
 
+Ce document est périmé. Merci de consulter
+la <a href="/FAQ/meta.html?locale=french">FAQ anglaise</a>
+<p>
+
 << require "./faq.pl" or print $! >>
 
 </body>
index 777054998c95c89ca2f1fccde12b41d88b4523bf..ba09ba8ed121f1acdd100e6f0648c2e7fc7ec5fd 100644 (file)
@@ -1,3 +1,4 @@
+use utf8;
 package FAQ;
 
 my ($faq,$var,$env,$q,$a,$c,$s,$t,$n);
@@ -9,11 +10,11 @@ my @sections = qw'Meta User Admin Misc';
 
 print "<style type=text/css><!-- h2,h3 {font-weight:normal} --></style>\n";
 
-print '<h1><a name="top" href="/index.html">F*EX</a> ',
-      " Frequently Asked Questions</h1>\n";
+print '<h1><a name="top" href="/index.html">F*EX</a> ';
+printf "Frequently Asked Questions: %s</h1>\n",ucfirst($faq);
 
 if ($faq ne 'local') {
-  print "<h3>\n";
+  print "<h3>Sections: ";
   foreach $s (@sections,'All') {
     if ($s =~ /$faq/i) {
       print "<b>$s</b>\n";
@@ -24,6 +25,7 @@ if ($faq ne 'local') {
   print "</h3>\n";
 }
 
+print "<p><hr><p>\n";
 print "<table>\n";
 
 foreach my $faq (@faq) {
@@ -40,8 +42,10 @@ foreach my $faq (@faq) {
     };
     ($q,$a) = split /A:\s*/;
     $q =~ s/[\s\n]+$//;
+    $q =~ s/^\s+//;
     $q =~ s! (/\w[\S]+/[\S]+)! <code>$1</code>!g;
     $a =~ s/[\s\n]+$/\n/;
+    $a =~ s/^\s+//;
     while ($a =~ s/^(\s*)\*/$1<ul>\n$1<li>/m) { 
       while ($a =~ s/(<li>.*\n\s*)\*/$1<li>/g) {}
       $a =~ s:(.*\n)(\s*)(<li>[^\n]+\n):$1$2$3$2</ul>\n:s
@@ -53,8 +57,8 @@ foreach my $faq (@faq) {
 #    $a =~ s/^\s*<br>\s*//mg;
     $a =~ s/<([^\s<>\@]+\@[\w.-]+)>/<a href="mailto:$1">&lt;$1><\/a>/g;
     $a =~ s! (/\w[\S]+/[\S]+)! <code>$1</code>!g;
-    $a =~ s!(https?://[\w-]+\.[^\s<>]+)!<a href="$1">[$1]</a>!g or
-    $a =~ s!(https?://[^\s<>]+)!<code>$1</code></a>!g;
+    $a =~ s!(https?://[\w-]+\.[^\s<>()]+)!<a href="$1">[$1]</a>!g or
+    $a =~ s!(https?://[^\s<>()]+)!<code>$1</code></a>!g;
     push @{$Q{$faq}},$q;
     push @{$A{$faq}},$a;
   }
@@ -71,12 +75,18 @@ foreach $s (sections($faq)) {
   $t = $s if $faq eq 'all';
 
   for ($n = 0; $n < scalar(@{$Q{$c}}); $n++) {
-    printf "<tr><th align=left>%s Q%d:<td> <a href=\"#%s%d\">%s</tr>\n",
-           $s,$n+1,$t,$n+1,${Q{$c}[$n]};
+    $q = ${Q{$c}[$n]};
+    $qa = anchor($q);
+    printf '<tr valign=top><th align=left>'.
+           '<a href="#%s%d" style="text-decoration: none">'.
+           '<font color="black">%s&nbsp;Q%d</a>:'.
+           '<td><a href="#%s">%s</a></tr>'."\n",
+           $t,$n+1,$s,$n+1,$qa,$q;
   }
 }
 
 print "</table>\n";
+print "<p><hr><p>\n";
 
 foreach $s (sections($faq)) {
 
@@ -86,14 +96,19 @@ foreach $s (sections($faq)) {
   $t = $s if $faq eq 'all';
 
   for ($n = 0; $n < scalar(@{$Q{$c}}); $n++) {
-    print "<p><hr><p>\n";
+    $q = ${Q{$c}[$n]};
+    $qa = anchor($q);
+    print "<p>\n";
     print "<table>\n";
-    printf "<tr><th><a name=\"%s%d\">%s&nbsp;Q%d:</a><td>%s</tr>\n",
-           $t,$n+1,$s,$n+1,${Q{$c}[$n]};
+    printf "<tr valign=top><th>".
+           "<a name=\"%s%d\">%s&nbsp;Q%d:</a>".
+           "<a name=\"%s\"></a>".
+           "<td><b>%s</b></tr>\n",
+           $t,$n+1,$s,$n+1,$qa,$q;
     printf "<tr valign=top><th>%s&nbsp;A%d:<td>\n%s</tr>\n",
            $s,$n+1,${A{$c}[$n]};
-    print "<tr><th>[<a href=\"#top\">Top</a>]<td></tr>\n";
     print "</table>\n";
+    print "[<a href=\"#top\">&uarr;&nbsp;Questions</a>]\n";
   }
 }
 
@@ -117,3 +132,12 @@ sub pre {
   s/\s+$//;
   return "<pre>$_</pre>\n";
 }
+
+sub anchor {
+  local $_ = shift;
+  s/<.+?>//g;
+  s/\(.+?\)//g;
+  s/\W/_/g;
+  s/_+$//;
+  return $_;
+}
index a0966457f9f3c8217371e44dca51636c0e3900ef..a3b32d43a522b546a6ec27c7e6fc689ccefef328 100644 (file)
@@ -1,11 +1,18 @@
 <html>
-<head><title>F*EX FAQ</title></head>
+<head>
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+  <title>F*EX FAQ</title>
+</head>
 <body>
 
 ## <pre>
 ## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
 ## </pre>
 
+Ce document est périmé. Merci de consulter
+la <a href="/FAQ/meta.html?locale=french">FAQ anglaise</a>
+<p>
+
 << require "./faq.pl" or print $! >>
 
 </body>
index 53b91531f73e47f3111e4b897d88fa9007bd7fca..0a191726509ec352d1c7a521991420fc10f5fe4e 100644 (file)
@@ -26,7 +26,7 @@ Q: Perl n'est-il pas trop lent pour faire tout ça ?
 A: fex.rus.uni-stuttgart.de tourne sur un ordinateur de bureau et est capable de gérer des uploads
    à plus de 300 MB/s. Essayer donc ça avec un serveur web généraliste comme Apache !
 
-Q: De quoi ais-je besoin de pour installer F*EX ?
+Q: De quoi ais-je besoin pour installer F*EX ?
 A: D'une machine UNIX avec une entrée DNS et un service smtp fonctionnel. Vous devez être root sur la machine.
 
 Q: Qu'est-ce que DNS et smtp ?
@@ -35,7 +35,7 @@ A: N'installez pas F*EX. Ce n'est pas fait pour vous.
 Q: Qui est l'auteur ?
 A: Ulli Horlacher framstag@rus.uni-stuttgart.de
 
-Q: Quel est la licence de F*EX ?
+Q: Quelle est la licence de F*EX ?
 A: Perl Artistic free software
 
 Q: Est-ce qu'il y a une mailing list F*EX ?
index a0966457f9f3c8217371e44dca51636c0e3900ef..a3b32d43a522b546a6ec27c7e6fc689ccefef328 100644 (file)
@@ -1,11 +1,18 @@
 <html>
-<head><title>F*EX FAQ</title></head>
+<head>
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+  <title>F*EX FAQ</title>
+</head>
 <body>
 
 ## <pre>
 ## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
 ## </pre>
 
+Ce document est périmé. Merci de consulter
+la <a href="/FAQ/meta.html?locale=french">FAQ anglaise</a>
+<p>
+
 << require "./faq.pl" or print $! >>
 
 </body>
index a0966457f9f3c8217371e44dca51636c0e3900ef..a3b32d43a522b546a6ec27c7e6fc689ccefef328 100644 (file)
@@ -1,11 +1,18 @@
 <html>
-<head><title>F*EX FAQ</title></head>
+<head>
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+  <title>F*EX FAQ</title>
+</head>
 <body>
 
 ## <pre>
 ## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
 ## </pre>
 
+Ce document est périmé. Merci de consulter
+la <a href="/FAQ/meta.html?locale=french">FAQ anglaise</a>
+<p>
+
 << require "./faq.pl" or print $! >>
 
 </body>
index a0966457f9f3c8217371e44dca51636c0e3900ef..a3b32d43a522b546a6ec27c7e6fc689ccefef328 100644 (file)
@@ -1,11 +1,18 @@
 <html>
-<head><title>F*EX FAQ</title></head>
+<head>
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+  <title>F*EX FAQ</title>
+</head>
 <body>
 
 ## <pre>
 ## << while (($v,$vv) = each %ENV) { print "$v = $vv\n" } >>
 ## </pre>
 
+Ce document est périmé. Merci de consulter
+la <a href="/FAQ/meta.html?locale=french">FAQ anglaise</a>
+<p>
+
 << require "./faq.pl" or print $! >>
 
 </body>
index fb72a49d829eac022580a813da36ee494c4ba15c..62c0be32b6c92f5bbdf2e331a627f61bde610588 100644 (file)
@@ -1,5 +1,7 @@
 # config for F*EX CGI fup
 
+use utf8;
+
 $info_1 = $info_login = <<EOD;
 <p><hr><p>
 <a href="/">F*EX (File EXchange)</a>
index fb730ce0c5f744ca9a74277827513f9946da5f54..a20a1b3ff6ed6b2c9dc780e7a280365e2fef2029 100644 (file)
@@ -1,5 +1,7 @@
 # config for F*EX CGI fup
 
+use utf8;
+
 $info_1 = $info_login = <<EOD;
 <p><hr><p>
 <a href="/">F*EX (File EXchange)</a>
index 7c44cd48dab4ce76b2216b242fb80301fe8237a9..4002667e1b4892a26bf9f00727dcf8e689cc50d5 100644 (file)
@@ -1,5 +1,7 @@
 # configurazione per F*EX CGI fup
 
+use utf8;
+
 $info_1 = $info_login = <<EOD;
 <p><hr><p>
 <a href="/">F*EX (File EXchange)</a>
index 0b1b6cfef508004a27456b3b17eda7cab90938b7..0aed2d0d3e9fe5a4a4c1fb236804d3bec39158f2 100644 (file)
@@ -7,6 +7,15 @@
 # czech:       Michal Simunek <michal.simunek@gmail.com>
 # french:      Jean-Baptiste Denis <jbd@jbdenis.net>
 
+'english'
+'german'
+'swabian'
+'spanish'
+'galician'
+'italian'
+'czech'
+'french'
+
 F*EX operation control ERROR
 F*EX Bedienungssteuerungs-Fehler
 F*EX Fehler bei dr Bedienongsschdeuerong
@@ -232,14 +241,14 @@ Cambia il disclaimer <a/> affinchè venga spedito assieme all'e-mail di notifica
 Změnit odesílání zřeknutí se odpovědnosti</a> u správy s upozorněním
 Changer la clause de non-responsabilité</a> à envoyer avec le message de notification
 
-Change your <a href="" onclick="show_id();" title="$id">auth-ID</a> to
-&Auml;ndern Sie Ihre <a href="" onclick="show_id();" title="$id">auth-ID</a> in
-&Auml;ndr die <a href="" onclick="show_id();" title="$id">auth-ID</a> noch
-Cambie su <a href="" onclick="show_id();" title="$id">auth-ID</a> en
-Cambie o seu <a href="" onclick="show_id();" title="$id">auth-ID</a> en
-Cambia il tuo <a href="" onclick="show_id();" title="$id">auth-ID</a> in
-Změnit si ověřovací ID <a href="" onclick="show_id();" title="$id">auth-ID</a> na
-Changer votre <a href="" onclick="show_id();" title="$id">auth-ID</a> pour
+Change your <a href="#" onclick="show_id();" title="$id">auth-ID</a> to
+&Auml;ndern Sie Ihre <a href="#" onclick="show_id();" title="$id">auth-ID</a> in
+&Auml;ndr die <a href="#" onclick="show_id();" title="$id">auth-ID</a> noch
+Cambie su <a href="#" onclick="show_id();" title="$id">auth-ID</a> en
+Cambie o seu <a href="#" onclick="show_id();" title="$id">auth-ID</a> en
+Cambia il tuo <a href="#" onclick="show_id();" title="$id">auth-ID</a> in
+Změnit si ověřovací ID <a href="#" onclick="show_id();" title="$id">auth-ID</a> na
+Changer votre <a href="#" onclick="show_id();" title="$id">auth-ID</a> pour
 
 remember it
 merken
@@ -351,7 +360,7 @@ Les emails de notifications sont maintenant au format simple
 
 Downloads will now be saved
 Downloads werden nun gespeichert
-Downloads werdet ab jetzt gschpeichert
+Ronderlads werdet ab jetzt gschpeichert
 Descargas están guardadas
 Downloads will now be saved
 I downloads saranno ora salvati
@@ -360,7 +369,7 @@ Les téléchargements vont maintenant être sauvés
 
 Downloads will now be displayed (if possible)
 Downloads werden nun angezeigt (wenn m&ouml;glich)
-Downloads werdet ab jetzt ozeigt (wenns ghot)
+Ronderlads werdet ab jetzt ozeigt (wenns ghot)
 Descargas están indicadas ahora (si posible)
 Downloads will now be displayed (if possible)
 I downloads saranno mostrati (se possibile)
@@ -448,14 +457,14 @@ value="continua"
 value="pokračovat"
 value="continuer"
 
->User:
->Benutzer:
->Benutzr:
->Usuario:
->Usuario:
->Utente:
->Uživatel:
->Utilisateur:
+#>User:
+#>Benutzer:
+#>Benutzr:
+#>Usuario:
+#>Usuario:
+#>Utente:
+#>Uživatel:
+#>Utilisateur:
 
 user config ERROR
 ERROR Benutzer-Einstellungen
@@ -862,6 +871,15 @@ Vedi http://$ENV{HTTP_HOST}/index.html per ulteriori informazioni
 Více informací o svém účtu získáte na http://$ENV{HTTP_HOST}/index.html
 Voir http://$ENV{HTTP_HOST}/index.html pour plus d'informations sur
 
+See $proto:
+Siehe $proto:
+Guck uff $proto:
+Vea $proto:
+V&eacute;xase $proto:
+Vedi $proto:
+Více $proto:
+Voir $proto:
+
 Questions? ==> F*EX admin: $admin
 Fragen? ==> Kontaktieren Sie den F*EX Administrator: $admin
 Froga? ==> Belaeschtig ruhig dr F*EX Adminischdrator: $admin
@@ -898,14 +916,14 @@ Un compte F*EX a été créé pour vous sur
 'Používání
 'Utilisez
 
-See http://$ENV{HTTP_HOST}/ for more information about
-Siehe http://$ENV{HTTP_HOST}/ fuer mehr Informationen ueber
+See http://$ENV{HTTP_HOST}/index.html for more information about
+Siehe http://$ENV{HTTP_HOST}/index.html fuer mehr Informationen ueber
 Guck halt uff http://$ENV{HTTP_HOST}/index.html wenn De meh wisse willsch ieber
-Vea http://$ENV{HTTP_HOST}/ para obtener m&aacute;s informaci&oacute;n sobre
-V&eacute;xase http://$ENV{HTTP_HOST}/ para obter m&aacute;s informaci&oacute;n sobre
-Vedi http://$ENV{HTTP_HOST}/ per ulteriori informazioni
-Informace, jak používat F*EX, naleznete na http://$ENV{HTTP_HOST}/
-Voir http://$ENV{HTTP_HOST}/ pour plus d'informations sur
+Vea http://$ENV{HTTP_HOST}/index.html para obtener m&aacute;s informaci&oacute;n sobre
+V&eacute;xase http://$ENV{HTTP_HOST}/index.html para obter m&aacute;s informaci&oacute;n sobre
+Vedi http://$ENV{HTTP_HOST}/index.html per ulteriori informazioni
+Informace, jak používat F*EX, naleznete na http://$ENV{HTTP_HOST}/index.html
+Voir http://$ENV{HTTP_HOST}/index.html pour plus d'informations sur
 
 $notify not found in $gf
 $notify nicht gefunden in $gf
@@ -988,15 +1006,6 @@ Edita il gruppo F*EX
 Upravit F*EX skupinu
 Éditez le groupe F*EX
 
-A F*EX group is similar to a mailing list, but for files:
-Eine F*EX-Gruppe ist einem E-Mail-Verteiler &auml;hnlich, hier geht es jedoch um Daten-Verteilung:
-A F*EX-Grupp isch so was wie en E-Mail-Verdoiler, allerdengs werde do Date verdoilt:
-Un grupo F*EX es similar a una lista de correo, pero para ficheros:
-Un grupo F*EX &eacute; semellante a unha lista de correo, pero para ficheiros:
-Un gruppo F*EX e' simile ad una lista di distribuzione, ma per i file:
-F*EX skupina je podobná poštovní konferenci s tím rozdílem, že se odesílají soubory:
-Un groupe F*EX est similaire à une liste de diffusion (mailing-list), mais pour des fichiers
-
 When a member fexes a file to this list, 
 Wenn ein Mitglied eine Datei f&uuml;r die Gruppe bereitstellt, 
 Wenn oi Mitglied a Datei fier die reschdlich Grupp nuffl&auml;d
@@ -1071,7 +1080,7 @@ effacer le fichier après un délai à la suite du téléchargement
 
 delete file $autodelete days after download
 L&ouml;sche Datei $autodelete Tage nach dem Download
-L&ouml;sch Datei $autodelete Dag nochm ronderlada
+L&ouml;sch Datei $autodelete Dag nochm Ronderlada
 borrar archivo $autodelete dias despues del descargar
 delete file $autodelete days after download
 cancella file $autodelete giorni dopo il download
@@ -1093,7 +1102,7 @@ Dei nochgfrogte F*EX auth-ID fir $fup?from=$from isch:
 Su auth-ID de F*EX solicitada para $fup?from=$from es:
 O seu auth-ID de F*EX solicitado para $fup?from=$from &eacute;:
 Il tuo auth-ID di F*EX che hai richiesto per $fup?from=$from e':
-Požadované ověřovací ID pro $fup?from=$from je:
+Vaše požadované F*EX ověřovací ID pro $fup?from=$from je:
 Votre auth-ID F*EX pour $fup?from=$from est:
 
 Or use:
@@ -1102,7 +1111,7 @@ Odr du nemsch:
 O use:
 Or use:
 Oppure usa
-Nebo pou¾ijte:
+Nebo použijte:
 Ou utilisez:
 
 Your reqested F*EX login is:
@@ -1255,7 +1264,7 @@ Fichiers pour $to (*):
 >reenviar<
 >forward<
 >prosegui<
->pøedat<
+>přeposlat<
 >faire suivre<
 
 Files for other e-mail addresses you own will not be listed here!
@@ -1330,15 +1339,6 @@ Il vostro client sembra essere "$1", che e' incompatibile con F*EX e probabilmen
 Zřejmě používáte prohlížeč "$1", který není kompatibilní s F*EX a pravděpodobně nebude správně fungovat
 Votre client semble être "$1" qui est incompatible avec F*EX et ne va sans doute pas fonctionner
 
-We recommend firefox
-Wir empfehlen \"Firefox\"
-Mir empfehled \"Firefox\"
-Recomendamos firefox
-Recomendamos firefox
-Raccomandiamo firefox
-Doporučujeme používat Firefox
-Nous recommandons firefox
-
 sender:
 Absender:
 Absendr:
@@ -1479,55 +1479,55 @@ Alternativer Java Client</a> (f&uuml;r Dateien gr&ouml;&szlig;er als 2 GB oder z
 Alternativer Java Client</a> (fier Dateia wo gr&ouml;&szlig;er send als 2 GB oder zum Sende von meh als oiner Datei)
 Cliente java alternativo</a> (par ficheros > 2 GB o env&iacute;o de m&aacute;s de un fichero)
 Cliente java alternativo</a> (para ficheiros > 2 GB ou env&iacute;o de m&aacute;s dun fichero)
-Client java alternativo</a> (per file > 2 GB o per spedizioni di piu' di un file)
+Client java alternativo</a> (per file > 2 GB o per spedizioni di più di un file)
 Alternativní Java klient</a> (pro soubory větší než 2 GB či pro odesílání více než jednoho souboru)
 Client Java alternatif</a> (pour les fichiers > 2 GB ou envoyer plusieurs fichiers d'un coup)
 
-Warning: the recipient must not be a mailing list, because after
-Warnung: die Empf&auml;ngeradresse darf keine Mailingliste sein, weil nach dem
-Obacht: die Empf&auml;ngeradress darf koi Mailinglischt sei, weil nochm
-Warning: the recipient must not be a mailing list, because after
-Warning: the recipient must not be a mailing list, because after
-Warning: the recipient must not be a mailing list, because after
-Warning: the recipient must not be a mailing list, because after
-Warning: the recipient must not be a mailing list, because after
-
-download the file will be no more available
-Download wird die Datei nicht mehr verf&uuml;gbar sein
-Ronderlada isch die Datei nemme verf&uuml;gbar
-download the file will be no more available
-download the file will be no more available
-download the file will be no more available
-download the file will be no more available
-download the file will be no more available
-
-Contact <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a>
-Kontaktieren Sie den <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a>
-Frog dr <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a>
-Contact <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a>
-Contact <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a>
-Contact <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a>
-Contact <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a>
-Contact <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a>
-
-if you want to fex to a mailing list
-wenn Sie an eine Mailingliste fexen wollen
-wenn Du an a Mailinglischt fexa wilsch
-if you want to fex to a mailing list
-if you want to fex to a mailing list
-if you want to fex to a mailing list
-if you want to fex to a mailing list
-if you want to fex to a mailing list
+Warning: the recipient must not be a mailing list
+Warnung: die Empf&auml;ngeradresse darf keine Mailingliste sein
+Obacht: die Empf&auml;ngeradress darf koi Mailinglischt sei
+Aviso: el destinatario no debe ser una lista de correo
+Warning: the recipient must not be a mailing list
+Attenzione: il destinatario non deve essere una lista di distribuzione
+Upozornění: příjemce nemůže být poštovní konference
+Attention: le destinataire ne peut pas être une liste de diffusion
+
+because after download the file will be no more available
+weil nach dem Download wird die Datei nicht mehr verf&uuml;gbar sein
+weil nochm Ronderlada isch die Datei nemme verf&uuml;gbar
+porque tras la descarga del fichero ya no estar&aacute; disponible
+because after download the file will be no more available
+siccome dopo il download il file non sarà più disponibile
+protože soubor již nebude po stažení dostupný
+parce qu'après le téléchargement, le fichier ne sera plus disponible
+
+Contact <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a> if you want to fex to a mailing list
+Kontaktieren Sie den <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a> wenn Sie an eine Mailingliste fexen wollen
+Frog dr <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a> wenn Du an a Mailinglischt fexa wilsch
+Contacte con <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a> si desea hacer un env&iacute;o a una lista de correo
+Contact <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a> if you want to fex to a mailing list
+Contatta <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster se vuoi fexare ad una lista di distribuzione
+Chcete-li poslat soubor do poštovní konference, kontaktujte <a href="mailto:$ENV{SERVER_ADMIN}">fexmastera</a>
+Contacter <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a> si vous voulez fexer à une liste de diffusion
 
 he can allow multiple downloads for specific addresses
 er kann f&uuml;r bestimmte Adressen einen mehrfachen Download freischalten
 der ko fir beschtemmte Adressa a mehrfaches Ronderlada erlauba
+el puede permitir m&uacute;ltipes descargas para una direcci&oacute,nespec&iacute;fica
 he can allow multiple downloads for specific addresses
-he can allow multiple downloads for specific addresses
-he can allow multiple downloads for specific addresses
-he can allow multiple downloads for specific addresses
-he can allow multiple downloads for specific addresses
-
+egli puo' permettere download multipli per indirizzi specifici
+ten může umožnit vícenásobná stahování pro určité adresy
+il peut autoriser plusieurs téléchargements pour des adresses spécifiques
+
+Use a <a href="/tools.html">F*EX client</a> if you want to send more than one file or resume an interrupted upload
+Verwenden Sie einen <a href="/tools.html">F*EX client</a> wenn Sie mehr als eine Datei versenden wollen oder einen abgebrochenen Upload wiederaufnehmen wollen
+Nemm an <a href="/tools.html">F*EX client</a> wenn du meh als oi Datei verschicka willsch oder an abbrochona Nufflad wiederaufnehma willsch
+Use un <a href="/tools.html">cliente F*EX</a> si desea enviar m&aacute;s de un fichero o continuar una subida interrumpida
+Use a <a href="/tools.html">F*EX client</a> if you want to send more than one file or resume an interrupted upload
+Usa <a href="/tools.html">F*EX client</a> se vuoi spedire più di un file o riesumare un upload interrotto
+Pokud chcete odeslat více než jeden soubor, nebo pokračovat v přerušeném nahrávání, použijte <a href="/tools.html">klienta pro F*EX</a>
+Utilisez un <a href="/tools.html">client F*EX</a> si vous voulez envoyer plus d'un fichier ou poursuivre un upload interrompue
+      
 You have to fill out this form completely to continue
 Sie m&uuml;ssen dieses Formular komplett ausf&uuml;llen um fortzufahren
 Du musch des Formular ganz ausff&uuml;lla sonsch kosch ned weitrmacha
@@ -1632,7 +1632,7 @@ Wenn Sie mehr als eine Datei senden m&ouml;chten, dann erzeugen Sie ein zip oder
 Wenn De meh als oi Datei verschigge willsch, no mach a zip oddr a tar Archiv
 Si desea enviar m&aacute;s de un fichero p&oacute;ngalos en un archivo zip
 De querer enviar m&aacute;s dun fichero p&oacute;&ntilde;aos nun arquivo zip
-Se vuoi spedire piu' di un file, mettili in un archivio zip o tar
+Se vuoi spedire più di un file, mettili in un archivio zip o tar
 Chcete-li odeslat více jak jeden soubor, zkomprimujte jej do zip nebo tar archivu
 Si vous voulez envoyer plus d'un fichier, mettez les dans un zip ou une archive tar
 
@@ -2302,18 +2302,45 @@ perche' ha troppi bug
 protože má příliš mnoho chyb
 parce qu'il a beaucoup trop de problèmes
 
-We recommend Firefox or wget
-Wir empfehlen Ihnen Firefox oder wget
-Nemm lieber Firefox oder wget
-Recomendamos Firefox o wget
-Recomendámoslle Firefox ou wget
-Raccomandiamo di usare Firefox o wget
-Doporučujeme použít Firefox či wget
-Nous reconmmandons Firefox ou wget
+We recommend
+Wir empfehlen
+Mir empfehled
+Recomendamos
+Recomendamos
+Raccomandiamo
+Doporučujeme používat
+Nous recommandons
+
+#We recommend Firefox or wget
+#Wir empfehlen Ihnen Firefox oder wget
+#Nemm lieber Firefox oder wget
+#Recomendamos Firefox o wget
+#Recomendámoslle Firefox ou wget
+#Raccomandiamo di usare Firefox o wget
+#Doporučujeme použít Firefox či wget
+#Nous reconmmandons Firefox ou wget
+
+We recommend fexget or fexit for download
+Wir empfehlen fexget oder fexit fuer den Download
+Mir empfehlad fexget odr fexit firs Ronderlada
+Recomendamos fexget o fexit para la descarga
+We recommend fexget or fexit for download
+Raccomandiamo fexget o fexit per il download
+Pro stahování doporučujeme fexget nebo fexit,
+Nous recommandons fexget ou fexit pour un téléchargement
+
+because these clients can resume the download after an interruption
+weil diese Programme einen unterbrochenen Download wiederaufnehmen koennen
+weil Du mit dene Denger an abbrochana Ronderlad wiederaufnehma kosch
+porque estos clientes pueden continuar una descarga que ha sido interrumpida
+because these clients can resume the download after an interruption
+dato che questi clients possono riesumare il download dopo una interruzione
+protože tito klienti umožňují pokračovat ve stahování po přerušení
+parce que ces clients peuvent poursuivre un téléchargement après une interruption
 
 After download (or view with a web browser!), 
 Nach Download (oder Ansicht mit dem Web Browser!) 
-Nachm Ronderlada (oddr wenn Du se mit Deim Webbrowser oguckt hosch!) 
+Nochm Ronderlada (odr wenn Du's mitm Webbrowser oguckt hosch!) 
 Tras la descarga,
 Tras a descarga,
 Dopo il download (o dopo essere stato visto con un browser WEB!)
@@ -2383,41 +2410,32 @@ $receiver = 'te
 $receiver = 'vy
 $receiver = 'vous
 
-Comment:
-Kommentar:
-Kommendar:
-Comentario:
-Comentario:
-Commento:
-Komentář:
-Commentaire:
-
-" day"
-" Tag"
-" Dag"
-" día"
-" día"
-" giorno"
-" den"
-" jour"
-
-" days"
-" Tagen"
-" Dag"
-" días"
-" días"
-" giorni"
-" dnů"
-" jours"
-
-> days<
-> Tagen<
-> Dag<
-> días<
-> días<
-> giorni<
-> dnů<
-> jours<
+ day"
+ Tag"
+ Dag"
+ día"
+ día"
+ giorno"
+ den"
+ jour"
+
+ days"
+ Tagen"
+ Dag"
+ días"
+ días"
+ giorni"
+ dnů"
+ jours"
+
+ days<
+ Tagen<
+ Dag<
+ días<
+ días<
+ giorni<
+ dnů<
+ jours<
 
 has uploaded the file
 hat die Datei
@@ -2439,7 +2457,7 @@ pour $receiver. Utilisez
 
 to download this file within $days
 um die Datei innerhalb von $days herunterzuladen
-damit Du dui Datei ennerhalb von $days ronderlade kosch
+damit Du dui Datei ennerhalb von $days ronderlada kosch
 para descargar este fichero antes de $days
 para descargar este fichero antes de $days
 per scaricare questo file entro $days
@@ -2671,41 +2689,41 @@ cliccare qui con il tasto destro del mouse e selezionare "salva come"
 zde klikněte pravým tlačítkem myši a vyberte "Uložit jako"
 cliquez ici avec le bouton droit de la souris et sélectionner "save as"
 
-Meta questions
-Meta Fragen
-Meta Froga
-Meta Preguntas
-Meta Preguntas
-Meta Domande
-Všeobecné Máte otázky
-Questions générales
-
-User questions
-Benutzer Fragen
-Benutzr Froga
-Usario Preguntas
-Usuario Preguntas
-Utente Domande
-Uživatel Máte otázky
-Questions utilisateurs
-
-Admin questions
-Administratoren Fragen
-Adminischdrator Froga
-Admin Preguntas
-Admin Preguntas
-Amministratore Domande
-Správce Máte otázky
-Questions administrateur
-
-Misc questions
-Sonstige Fragen
-Vermischte Froga
-Misc Preguntas
-Misc Preguntas
-Misc Domande
-Misc Máte otázky
-Questions diverses
+#Meta questions
+#Meta Fragen
+#Meta Froga
+#Meta Preguntas
+#Meta Preguntas
+#Meta Domande
+#Všeobecné Máte otázky
+#Questions générales
+
+#User questions
+#Benutzer Fragen
+#Benutzr Froga
+#Usario Preguntas
+#Usuario Preguntas
+#Utente Domande
+#Uživatel Máte otázky
+#Questions utilisateurs
+
+#Admin questions
+#Administratoren Fragen
+#Adminischdrator Froga
+#Admin Preguntas
+#Admin Preguntas
+#Amministratore Domande
+#Správce Máte otázky
+#Questions administrateur
+
+#Misc questions
+#Sonstige Fragen
+#Vermischte Froga
+#Misc Preguntas
+#Misc Preguntas
+#Misc Domande
+#Misc Máte otázky
+#Questions diverses
 
 Your F*EX account has been inactive for $expire days
 Ihr F*EX Account ist seit $expire Tagen inaktiv