[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#776646: unblock: fex/20150120-2



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

Please unblock package fex

Version 20150120-2 fixes a security race condition where the cleanup
script would delete a state of a freshly registered user (if
registration is allowed) granting full user privileges instead of
restricted ones (if so configured).

Also the version 20150120-2 fixes that the fex-utils by default don't
connect to SSLv3 any more (and other SSL parameters are configurable).

Attached is the (quite large) diff against the version in testing
(20140917-2) which however has multiple copies of the same update (in
every cli client) and does contain upstreams new copy of the cli tools
in htdocs/download (as well as 3 scripts that upstream needs yet we
don't ship in the deb). Stripping the diff down to what we need gives
more or less this:
 bin/fac                                  |   56 ++
 bin/fbm                                  |   58 ++
 bin/fex_cleanup                          |   55 ++
 bin/fexget                               |  400 +++++++++++++++-----
 bin/fexsend                              |  614 ++++++++++++++++++++++---------
 bin/fexsrv                               |   19 
 bin/fexwall                              |   37 +
 bin/logwatch                             |   67 ++-
 bin/sexget                               |  211 +++++++++-
 bin/sexsend                              |  211 +++++++++-
 bin/sexxx                                |  211 +++++++++-
 bin/xx                                   |  614 ++++++++++++++++++++++---------
 cgi-bin/fac                              |    2 
 cgi-bin/foc                              |   14 
 cgi-bin/fop                              |    6 
 cgi-bin/fuc                              |   35 +
 cgi-bin/fup                              |  347 +++++++++--------
 cgi-bin/fur                              |   29 +
 cgi-bin/rup                              |   38 -
 cgi-bin/sex                              |   16 
 debian/changelog                         |   29 +
 debian/control                           |    6 
 debian/fex.lintian-overrides             |    3 
 debian/fex.postinst                      |    4 
 debian/htdocs.md5/20150120-2             |   26 +
 debian/patches/03_fexget_search_ca.patch |  123 ++++++
 debian/patches/series                    |    1 
 debian/rules                             |    2 
 doc/Changes                              |   47 ++
 doc/Contribs                             |    5 
 doc/SSL                                  |    4 
 doc/concept                              |   45 +-
 doc/installation                         |    7 
 doc/newfeatures                          |   21 +
 doc/version                              |    2 
 htdocs/FAQ/admin.faq                     |   11 
 htdocs/FAQ/faq.pl                        |   31 +
 htdocs/FAQ/meta.faq                      |    4 
 htdocs/FAQ/user.faq                      |   40 +-
 htdocs/features.html                     |    1 
 htdocs/version                           |    2 
 lib/dop                                  |   56 ++
 lib/fex.ph                               |    5 
 lib/fex.pp                               |  112 ++++-
 lib/fup.pl                               |    6 
 locale/czech/htdocs/FAQ.html             |  271 -------------
 locale/german/htdocs/FAQ.html            |    2 
 locale/italian/htdocs/FAQ.html           |    3 
 locale/spanish/htdocs/FAQ.html           |    6 
 locale/translations                      |  255 ++++++++----
 50 files changed, 3001 insertions(+), 1169 deletions(-)

unblock fex/20150120-2

TIA!

Best,
Kilian
diff -Nru fex-20140917/bin/afex fex-20150120/bin/afex
--- fex-20140917/bin/afex	2013-08-29 09:40:16.000000000 +0200
+++ fex-20150120/bin/afex	2014-12-24 01:17:15.000000000 +0100
@@ -4,7 +4,7 @@
 #
 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
 #
-# Copyright: Perl Artistic
+# Perl Artistic Licence
 
 $fexserver = '';
 
diff -Nru fex-20140917/bin/asex fex-20150120/bin/asex
--- fex-20140917/bin/asex	2013-08-29 09:40:16.000000000 +0200
+++ fex-20150120/bin/asex	2014-12-24 01:17:15.000000000 +0100
@@ -4,7 +4,7 @@
 #
 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
 #
-# Copyright: Perl Artistic
+# Perl Artistic Licence
 
 $fexserver = '';
 
diff -Nru fex-20140917/bin/fac fex-20150120/bin/fac
--- fex-20140917/bin/fac	2014-08-27 18:51:44.000000000 +0200
+++ fex-20150120/bin/fac	2014-12-08 16:02:19.000000000 +0100
@@ -57,8 +57,8 @@
 $EDITOR = $ENV{EDITOR} || $ENV{VISUAL} ||
           (-x '/usr/bin/editor' ? '/usr/bin/editor' : 'vi');
 
-$opt_c = $opt_v = $opt_l = $opt_h = $opt_w = $opt_u = $opt_R = $opt_M = 0;
-$opt_E = 0;
+$opt_c = $opt_v = $opt_l = $opt_L = $opt_h = $opt_w = $opt_u = $opt_R = 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_/'} = '';
@@ -80,9 +80,9 @@
   warn "WARNING: \$spooldir differs from $FEXHOME/spool !\n";
 }
   
-getopts('hcvlwuMRE/q:r:d:a:n:k:m:y:S:C:A:V:D:P:') or usage(2);
-usage(0)  if $opt_h;
-example() if $opt_E;
+getopts('hcvlLwuMRE/q:r:d:a:n:k:m:y:S:C:A:V:D:P:') or usage(2);
+usage(0)   if $opt_h;
+examples() if $opt_E;
 
 if (${'opt_/'}) {
   my $admin = shift;
@@ -155,7 +155,7 @@
   exit;
 }
 
-# resend notification e-mails
+# list files or resend notification e-mails
 if ($opt_M) {
   my ($mtime,$comment,$file,$keep);
   local $_;
@@ -220,12 +220,40 @@
   exit;
 }
 
+# list files detailed
+if ($opt_L) {
+  my $filter = shift;
+  my ($comment,$file,$keep,$old,$size,$download);
+  local $_;
+  
+  foreach $file (glob "*/*/*/data") {
+    next if $file =~ m:(.+?)/: and -l $1;
+    $size = -s $file or next;
+    $file =~ s:/data$::;
+    next if $filter and $file !~ /$filter/;
+    $comment = slurp("$file/comment")||'';
+    $dkey = readlink("$file/dkey")||'';
+    $keep = readlink("$file/keep")||$keep_default;
+    $old = int((time-mtime("$file/data"))/60/60/24);
+    $download = join(' & ',split("\n",(slurp("$file/download")||'')));
+    print "\n$file\n";
+    printf "  comment: %s\n",decode_utf8($comment);
+    printf "  size: %s\n",d3($size); 
+    printf "  sender ip: %s\n",readlink("$file/ip")||'';
+    printf "  expire in: %s days\n",$keep-$old;
+    printf "  upload speed: %s kB/s\n",readlink("$file/speed")||0;
+    printf "  URL: $durl/$dkey/%3\$s\n",split "/",$file;
+    printf "  download: %s\n",$download;
+  }
+  exit;
+}
+
 # delete user 
 if ($opt_d) {
   $idf = "$spooldir/$opt_d/\@";
   die "$0: no such user $opt_d\n" unless -f $idf;
   unlink $idf or die "$0: cannot remove $idf - $!\n";
-  unlink "$spooldir/$opt_d/\@ALLOWED_RECIPIENTS";
+  foreach $rf (glob "$spooldir/$opt_d/\@*") { unlink $rf }
   print "$opt_d deleted\n";
   exit;
 }
@@ -809,6 +837,13 @@
 }
 
 
+sub d3 {
+  local $_ = shift;
+  while (s/(\d)(\d\d\d\b)/$1,$2/) {};
+  return $_;
+}
+
+
 sub usage {
   my $port = '';
   my $proto = 'http';
@@ -839,14 +874,15 @@
 $0 -y user [yn]       # set user "fex yourself" web default (yes,no)
 $0 -S fup             # file upload statistics
 $0 -S fop             # file download statistics
-$0 -A alias:hostname  # add new virtual server
 $0 -v                 # show server config
 $0 -c                 # edit server config
+$0 -w                 # watch fexsrv.log (continously)
 $0 -l                 # list pending files with download URLs
+$0 -L [filter]        # list pending files in detail
 $0 -M                 # list pending files with TO/FROM/FILE
 $0 -M TO/FROM/FILE    # resend notification email
-$0 -w                 # watch fexsrv.log (continously)
 $0 -m "reason"        # enter maintenance mode (reason "exit" to leave)
+$0 -A alias:hostname  # add new virtual server
 $0 -V virtualhost ... # operations on virtualhost (alias or hostname)
 $0 -E                 # show usage examples
 EOD
@@ -856,7 +892,7 @@
   exit shift;
 }
 
-sub example {
+sub examples {
   $0 =~ s:.*/::;
   print <<EOD;
 create new user:
diff -Nru fex-20140917/bin/fbm fex-20150120/bin/fbm
--- fex-20140917/bin/fbm	2013-09-12 18:10:08.000000000 +0200
+++ fex-20150120/bin/fbm	2014-12-02 13:34:20.000000000 +0100
@@ -20,7 +20,7 @@
 
 our ($SH,$windoof,$sigpipe,$useragent);
 our ($FEXSERVER);
-our $version = 20140917;
+our $version = 20150120;
 
 # server defaults
 my $server = 'fex.rus.uni-stuttgart.de';
@@ -150,9 +150,59 @@
   serverconnect($server,$port);
     
   $boundary = randstring(48);
-  $filename = 'test_'.int(time*1000);
+  $P{command} = 'CHECKRECIPIENT';
   
-  # send HTTP POST variables
+  # HTTP POST variables
+  @pv = qw'from to id command';
+  foreach my $v (@pv) {
+    if ($P{$v}) {
+      my $name = uc($v);
+      push @hb,"--$boundary";
+      push @hb,"Content-Disposition: form-data; name=\"$name\"";
+      push @hb,"";
+      push @hb,$P{$v};
+    }
+  }
+  push @hb,"--$boundary--";
+
+  $length = length(join('',@hb)) + scalar(@hb)*2 + $mb*M;
+
+  # HTTP header
+  push @hh,"POST $proxy_prefix/fup HTTP/1.1";
+  push @hh,"Host: $server:$port";
+  push @hh,"User-Agent: $useragent";
+  push @hh,"Content-Length: $length";
+  push @hh,"Content-Type: multipart/form-data; boundary=$boundary";
+  push @hh,"Connection: close";
+  push @hh,'';
+
+  if ($opt_v) {
+    printf "--> $_\n" foreach (@hh,@hb);
+  }
+
+  nvtsend(@hh,@hb) or die "$0: server has closed the connection\n";
+
+  while (<$SH>) {
+    s/[\r\n]+//;
+    print "<-- $_\n" if $opt_v;
+    push @r,$_;
+    last if /^$/;
+  }
+
+  unless (@r and $r[0] =~ / 204 /) {
+    $_ = $r[0] || '';
+    s/^HTTP.[.\d\s]+//;
+    die "$0: server error: $_\n";
+  }
+
+  @hh = (); # HTTP header
+  @hb = (); # HTTP body
+  @r = ();
+  $filename = 'test_'.int(time*1000);
+
+  serverconnect($server,$port);
+
+  # HTTP POST variables
   @pv = qw'from to id keep autodelete comment filesize';
   foreach my $v (@pv) {
     if ($P{$v}) {
@@ -164,7 +214,7 @@
     }
   }
   
-  # at last, POST the file
+  # at last, the file
   push @hb,"--$boundary";
   push @hb,"Content-Disposition: form-data; name=\"FILE\"; filename=\"$filename\"";
   push @hb,"Content-Type: application/octet-stream";
diff -Nru fex-20140917/bin/fex_cleanup fex-20150120/bin/fex_cleanup
--- fex-20140917/bin/fex_cleanup	2014-09-14 01:20:17.000000000 +0200
+++ fex-20150120/bin/fex_cleanup	2014-12-19 00:16:27.000000000 +0100
@@ -106,9 +106,24 @@
       }
     }
     closedir TO;
-    @glob = glob "$to/*/* $to/\@MAINUSER/* $to/\@GROUP/*";
-    unless (@glob or -f "$to/\@") {
-      logdel($to,"$to deleted");
+    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("$isodate $user reanimated (login_check)");
+          }
+        } else {
+          rename "$user/@","$user/\@~" unless $opt_d;
+          logv("$user deactivated (login_check)");
+        }
+      }
     }
   }
 }
@@ -173,8 +188,9 @@
   while ($file = readdir D) {
     if (-f $file) {
       $mtime = mtime($file);
-      if ($mtime and $today > 5*$keep_default*DS+$mtime) {
-        logdel($file,".error/$file deleted");
+      if ($mtime and $today > 10*$keep_default*DS+$mtime) {
+        if ($opt_d) { print "unlink .error/$file\n" }
+        else        { logdel($file,".error/$file deleted") }
       }
     }
   }
@@ -328,8 +344,8 @@
       next if -e "$user/\@PERSISTENT";
       next if $user !~ /@/ or -l $user;
       next if $user =~ /^(fexmaster|fexmail)/ or $user eq $admin;
-      next if $login_check and &$login_check(readlink("$user/.login"));
-      
+      next if -l "$user/.login";
+
       if (time > mtime($user)+$expire*DS) {
         # print "$spooldir/$user\n";
         my $locale = readlink "$user/\@LOCALE";
@@ -347,10 +363,11 @@
 # vhosts
 exit if $opt_V;
 if (%vhost) {
-  foreach $vhost (values %vhost) {
-    if (-f "$vhost/lib/fex.ph") {
-      warn "run $0 on $vhost/spool :\n" if -t or $opt_v;
-      my $cmd = "FEXLIB=$vhost/lib $_0 -V @_ARGV";
+  foreach $vhost (keys %vhost) {
+    my $fexlib = $vhost{$vhost}.'/lib';
+    if (-f "$fexlib/fex.ph") {
+      warn "run $0 for $vhost :\n" if -t or $opt_v;
+      my $cmd = "HTTP_HOST=$vhost FEXLIB=$fexlib $_0 -V @_ARGV";
       if ($opt_d) { print "$cmd\n" }
       else        { system $cmd }
     }
@@ -508,6 +525,7 @@
       );
       open $notify,'>',$notify;
       close $notify;
+      print "sent reminder for $file\n" if -t or $opt_v;
     }
   }
 }
@@ -540,8 +558,7 @@
     print "$msg\n";
   } else {
     if ($status = rmrf($file)) {
-      print L "$isodate $msg\n";
-      print   "$msg\n" if -t or $opt_v;
+      logv($msg);
     } else {
       print L "$isodate $file DEL FAILED : $!\n";
       warn     "$file DEL FAILED : $!\n" if -t or $opt_v;
@@ -551,12 +568,20 @@
   return $status;
 }
 
+
+sub logv {
+  my $msg = shift;
+  print L "$isodate $msg\n" unless $opt_d;
+  print "$msg\n" if -t or $opt_v;
+}
+
+
 sub verbose {
   local $_;
   if ($opt_v) {
     while ($_ = shift @_) {
-      s/\n//;
-      print "$_\n";
+      s/\n*$/\n/;
+      print;
     }
   }
 }
diff -Nru fex-20140917/bin/fexget fex-20150120/bin/fexget
--- fex-20140917/bin/fexget	2014-08-15 14:31:24.000000000 +0200
+++ fex-20150120/bin/fexget	2015-01-19 13:59:57.000000000 +0100
@@ -6,7 +6,7 @@
 #
 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
 #
-# Copyright: Perl Artistic
+# Perl Artistic Licence
 
 use 5.006;
 use strict qw'vars subs';
@@ -14,6 +14,7 @@
 use POSIX;
 use Encode;
 use Getopt::Std;
+use File::Basename;
 use Socket;
 use IO::Handle;
 use IO::Socket::INET;
@@ -23,13 +24,19 @@
 
 eval 'use Net::INET6Glue::INET_is_INET6';
 
+$| = 1;
+
 our $SH;
 our ($fexhome,$idf,$tmpdir,$windoof,$useragent);
+our ($xv,%autoview);
 our $bs = 2**16; # blocksize for tcp-reading and writing file
-our $version = 20140917;
+our $version = 20150120;
 our $CTYPE = 'ISO-8859-1';
 our $fexsend = $ENV{FEXSEND} || 'fexsend';
 
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
 # inquire default character set
 # cannot use "use I18N::Langinfo" because of no windows support!
 eval {
@@ -39,8 +46,6 @@
   $CTYPE = langinfo(CODESET());
 };
 
-$version = mtime($0) unless $version;
-
 if ($Config{osname} =~ /^mswin/i) {
   $windoof = $Config{osname};
   $ENV{HOME} = $ENV{USERPROFILE};
@@ -49,6 +54,7 @@
   $idf = "$fexhome/id";
   $useragent = sprintf("fexget-$version (%s %s)",
                        $Config{osname},$Config{archname});
+  $SSL{SSL_verify_mode} = 0;
   chdir $ENV{USERPROFILE}.'\Desktop';
   # open XX,'>XXXXXX';close XX;
 } else {
@@ -62,8 +68,9 @@
   $useragent = "fexget-$version ($_)";
 }
 
-$| = 1;
-
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
 
 my $usage = <<EOD;
 usage: $0 [-v] [-m limit] [-s filename] [-o] [-k] [-X] [-P proxy:port] F*EX-URL(s)
@@ -71,21 +78,49 @@
    or: $0 [-v] -f F*EX-URL(s) e-mail-address
    or: $0 [-v] -a
    or: $0 -l [-i tag]
+   or: $0 -H
 options: -v verbose mode
          -m limit kB/s
          -s save to filename (-s- means: write to STDOUT/pipe)
          -o overwrite existing file
   	 -k keep on server after download
-  	 -X do not extract archive files
+  	 -X do not extract archive files or autoview file
   	 -d delete without download
   	 -f forward a file to another recipient
          -a get all files (implies -X)
   	 -l list files on server
          -i tag alternate server/account, see: $fexsend -h
          -P use Proxy for connection to the F*EX server
+         -H show hints and examples
 argument: F*EX-URL may be file number (see: $0 -l)
 EOD
 
+my $hints = <<'EOD';
+When you download a file with extension .jpg .gif .png or .tif an image viewer
+will be started. This can be xv or xdg-open.
+In $HOME/.fex/config.pl you can set your prefered autoview applications:
+
+%autoview = (
+  '\.(gif|jpg|png|tiff?)' => 'my_prefered_image_viewer',
+  '\.(avi|mp4|mov)'       => 'vlc -f',
+  '\.pdf'                 => 'evince',
+);
+
+For HTTPS you can set the environment variables:
+SSLVERIFY=1                 # activate server identity verification
+SSLVERSION=TLSv1            # this is the default
+SSLCAPATH=/etc/ssl/certs    # path to trusted (root) certificates
+SSLCAFILE=/etc/ssl/cert.pem # file with trusted (root) certificates
+SSLCIPHERLIST=HIGH:!3DES    # see http://www.openssl.org/docs/apps/ciphers.html
+
+You can set these environment variables also in $HOME/.fex/config.pl, as well as
+the $opt_* variables, e.g.:
+  
+$ENV{SSLVERSION} = 'TLSv1';
+${'opt_+'} = 1;
+$opt_m = 200;
+EOD
+
 if ($windoof and not @ARGV and not $ENV{PROMPT}) {
   # restart with cmd.exe to have mouse cut+paste
   my $cmd = "cmd /k \"$0\"";
@@ -100,12 +135,13 @@
 my $chunksize;
 
 our ($opt_h,$opt_v,$opt_l,$opt_d,$opt_m,$opt_z,$opt_K,$opt_o,$opt_a);
-our ($opt_s,$opt_k,$opt_i,$opt_V,$opt_X,$opt_f,$opt_P,$opt_L);
+our ($opt_s,$opt_k,$opt_i,$opt_V,$opt_X,$opt_f,$opt_P,$opt_L,$opt_H);
 $opt_m = $opt_h = $opt_v = $opt_l = $opt_d = $opt_K = $opt_o = $opt_a = 0;
-$opt_V = $opt_X = $opt_f = $opt_L = 0;
+$opt_V = $opt_X = $opt_f = $opt_L = $opt_H = 0;
 ${'opt_+'} = 0;
 $opt_s = $opt_k = $opt_i = $opt_P = '';
-getopts('hvVlLdkzoaXf+m:s:i:K:P:') or die $usage;
+$_ = "$fexhome/config.pl"; require if -f;
+getopts('hvVHlLdkzoaXf+m:s:i:K:P:') or die $usage;
 $opt_k = '?KEEP' if $opt_k;
 
 if ($opt_m =~ /(\d+)/) {
@@ -116,6 +152,53 @@
 
 print "Version: $version\n" if $opt_V;
 die $usage                  if $opt_h;
+if ($opt_H) {
+  print $hints;
+  exit;
+}
+
+# set SSL/TLS options
+$SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+foreach my $opt (qw(
+  SSL_version
+  SSL_cipher_list 
+  SSL_verify_mode 
+  SSL_ca_path 
+  SSL_ca_file)
+) {
+  my $env = uc($opt);
+  $env =~ s/_//g;
+  $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+}
+
+if ($SSL{SSL_verify_mode}) {
+  &search_ca;
+  unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+    die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+  }
+} elsif (defined($SSL{SSL_verify_mode})) {
+  # user has set SSLVERIFY=0 !
+} else {
+  &search_ca;
+  $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
 
 my $ffl = "$tmpdir/fexget"; 		# F*EX files list (cache)
 
@@ -203,7 +286,7 @@
   }
 }
 
-foreach my $url (@ARGV) {
+URL: foreach my $url (@ARGV) {
 
   # do not overrun server
   sleep 1 if $fop;
@@ -303,16 +386,38 @@
     }
   }
 
-  if (not $opt_X and $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') }
-    else { die "$0: unknown archive \"$download\"\n" }
-    if ($? == 0) {
-      unlink $download;
-    } else {
-      die "$0: keeping \"$download\"\n";
+  unless ($opt_X) {
+    
+    foreach my $a (keys %autoview) {
+      if ($download =~ /$a$/i and $autoview{$a}) {
+        printf "run \"%s %s\" [Yn] ? ",$autoview{$a},basename($download);
+        $_ = <STDIN>||'';
+        system sprintf("%s %s",$autoview{$a},quote($download)) if /^y|^$/i;
+        next URL;
+      }
+    }
+    
+    if ($ENV{DISPLAY} and $download =~ /\.(gif|jpg|png|tiff?)$/i) {
+      # see also mimeopen and xdg-mime
+      if (my $xv = $xv || pathsearch('xv') || pathsearch('xdg-open')) {
+        printf "run \"%s %s\" [Yn] ? ",basename($xv),basename($download);
+        $_ = <STDIN>||'';
+        system $xv,$download if /^y|^$/i;
+        next URL;
+      }
+    }
+  
+    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') }
+      else { die "$0: unknown archive \"$download\"\n" }
+      if ($? == 0) {
+        unlink $download;
+      } else {
+        die "$0: keeping \"$download\"\n";
+      }
     }
   }
 
@@ -716,29 +821,6 @@
 }
 
 
-# set up tcp/ip connection
-sub tcpconnect {
-  my ($server,$port) = @_;
-
-  if ($port == 443) {
-    eval "use IO::Socket::SSL";
-    $SH = IO::Socket::SSL->new(
-      PeerAddr => $server,
-      PeerPort => $port,
-      Proto    => 'tcp',
-    );
-  } else {
-    $SH = IO::Socket::INET->new(
-      PeerAddr => $server,
-      PeerPort => $port,
-      Proto    => 'tcp',
-    );
-  }
-  die "cannot connect $server:$port - $@\n" unless $SH;
-  warn "TCPCONNECT to $server:$port\n" if $opt_v;
-}
-
-
 sub locale {
   my $string = shift;
 
@@ -756,21 +838,59 @@
 }
 
 
-sub sendheader {
-  my $sp = shift;
-  my @head = @_;
-  my $head;
-
-  push @head,"Host: $sp";
-
-  foreach $head (@head) {
-    warn "--> $head\n" if $opt_v;
-    print {$SH} $head,"\r\n";
+sub pathsearch {
+  my $prg = shift;
+  
+  foreach my $dir (split(':',$ENV{PATH})) {
+    return "$dir/$prg" if -x "$dir/$prg";
   }
-  warn "-->\n" if $opt_v;
-  print {$SH} "\r\n";
 }
 
+    
+sub quote {
+  local $_ = shift;
+  s/([^\w¡-ÿ_%\/=~:.,-])/\\$1/g;
+  return $_;
+}
+
+    
+{
+  my $tty;
+
+  sub inquire {
+    my $prompt = shift;
+    my $default = shift;
+    local $| = 1;
+    local $_;
+
+    if (defined $default) {
+      unless ($tty) {
+        chomp($tty = `tty 2>/dev/null`);
+        eval { local $^W; require "sys/ioctl.ph"; };
+      }
+
+      if (defined(&TIOCSTI) and $tty and open($tty,'>',$tty)) {
+        print $prompt;
+        foreach my $a (split("",$default)) { ioctl($tty,&TIOCSTI,$a) } 
+        chomp($_ = <STDIN>||'');
+      } else {
+        $prompt =~ s/([\?:=]\s*)/ [$default]$1/ or $prompt .= " [$default]";
+        print $prompt;
+        chomp($_ = <STDIN>||'');
+        $_ = $default unless length;
+      }
+    } else {
+      print $prompt;
+      chomp($_ = <STDIN>||'');
+    }
+
+    return $_;
+  }    
+}    
+
+
+### common functions ###
+
 
 sub mtime {
   my @d = localtime((stat shift)[9]);
@@ -784,11 +904,64 @@
   return $_;
 }
 
+
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
 sub serverconnect {
   my ($server,$port) = @_;
   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) {
@@ -802,23 +975,78 @@
       }
       eval "use IO::Socket::SSL";
       die "$0: cannot load IO::Socket::SSL\n" if $@;
-      $SH = IO::Socket::SSL->start_SSL($SH);
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
     }
   } else {
     tcpconnect($server,$port);
   }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
 }
 
-my $sigpipe;
 
 sub nvtsend {
   local $SIG{PIPE} = sub { $sigpipe = "@_" };
-
+  
   $sigpipe = '';
-
+  
   die "$0: internal error: no active network handle\n" unless $SH;
   die "$0: remote host has closed the link\n" unless $SH->connected;
-
+  
   foreach my $line (@_) {
     print {$SH} $line,"\r\n";
     if ($sigpipe) {
@@ -826,39 +1054,21 @@
       return 0;
     }
   }
+  
+  return 1;
 }
 
 
-{
-  my $tty;
-
-  sub inquire {
-    my $prompt = shift;
-    my $default = shift;
-    local $| = 1;
-    local $_;
-
-    if (defined $default) {
-      unless ($tty) {
-        chomp($tty = `tty 2>/dev/null`);
-        eval { local $^W; require "sys/ioctl.ph"; };
-      }
-
-      if (defined(&TIOCSTI) and $tty and open($tty,'>',$tty)) {
-        print $prompt;
-        foreach my $a (split("",$default)) { ioctl($tty,&TIOCSTI,$a) } 
-        chomp($_ = <STDIN>||'');
-      } else {
-        $prompt =~ s/([\?:=]\s*)/ [$default]$1/ or $prompt .= " [$default]";
-        print $prompt;
-        chomp($_ = <STDIN>||'');
-        $_ = $default unless length;
-      }
-    } else {
-      print $prompt;
-      chomp($_ = <STDIN>||'');
-    }
-
-    return $_;
-  }    
-}    
+# from MIME::Base64::Perl
+sub encode_b64 {
+  my $res = "";
+  my $eol = "\n";
+  my $padding;
+  
+  pos($_[0]) = 0;
+  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+  $res =~ tr|` -_|AA-Za-z0-9+/|;
+  $padding = (3-length($_[0])%3)%3;
+  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+  return $res;
+}
diff -Nru fex-20140917/bin/fexsend fex-20150120/bin/fexsend
--- fex-20140917/bin/fexsend	2014-09-05 09:03:47.000000000 +0200
+++ fex-20150120/bin/fexsend	2015-01-16 15:52:53.000000000 +0100
@@ -6,7 +6,7 @@
 #
 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
 #
-# Copyright: Perl Artistic
+# Perl Artistic Licence
 
 use 5.006;
 use strict qw'vars subs';
@@ -27,17 +27,23 @@
 
 eval 'use Net::INET6Glue::INET_is_INET6';
 
-our ($SH,$fexhome,$idf,$tmpdir,$windoof,$sigpipe,$useragent,$editor,$nomail);
+&update if "@ARGV" eq 'UPDATE';
+
+$| = 1;
+
+our ($SH,$fexhome,$idf,$tmpdir,$windoof,$useragent,$editor,$nomail);
 our ($anonymous,$public);
-our ($tpid);
+our ($tpid,$frecipient);
 our ($FEXID,$FEXXX,$HOME);
+our (%alias);
 our $chunksize = 0;
-our $version = 20140917;
+our $version = 20150120;
 our $_0 = $0;
 our $DEBUG;
-  
-$version ||= mtime($0);
-  
+
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
 if ($Config{osname} =~ /^mswin/i) {
   $windoof = $Config{osname};
   $HOME = $ENV{USERPROFILE};
@@ -47,6 +53,7 @@
   $editor = $ENV{EDITOR} || 'notepad.exe';
   $useragent = sprintf("fexsend-$version (%s %s)",
                        $Config{osname},$Config{archname});
+  $SSL{SSL_verify_mode} = 0;
 } else {
   $0 =~ s:.*/::;
   $HOME = (getpwuid($<))[7]||$ENV{HOME};
@@ -61,7 +68,9 @@
   chmod 0600,$idf;
 }
 
-$| = 1;
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
 
 my $from = '';
 my $to = '';
@@ -79,7 +88,6 @@
 my $timeout = 30; 	# server timeout
 my $fexlist = "$tmpdir/fexlist";
 my ($usage,$hints);
-
 my $xx = $0 =~ /^xx/;
 
 if ($xx) {
@@ -97,7 +105,7 @@
 usage: $0 [options] file(s) [@] recipient(s)
    or: $0 [special options]
    or: $0 -f \# recipient(s)
-   or: $0 -x \# [-C -k -D -K]
+   or: $0 -x \# [-C -k -D -K -S]
 options: -v           verbose mode
          -d           delete file on fex server
          -c           compress file
@@ -114,13 +122,14 @@
          -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)
+                 -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)
-                 -U      show authorized URL
+                 -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)
@@ -132,7 +141,7 @@
 #         -R FEX mail  self-register your e-mail address at FEX server
 
   $hints = <<EOD;
-$0 Hints and more options:
+$0 hints and more options:
   
 usage: $0 [options] file recipient(s)
 
@@ -167,7 +176,7 @@
 If you want a Bcc of the notification e-mail then add '!bcc!' to the comment:
 fexsend -C '!bcc! for me and you' ...
 
-Special options:
+Additional special options:
 
   -. sends a short instead of a detailed notification e-mail
   -/ does not upload the file, but tells the server to link it
@@ -177,6 +186,7 @@
   -q is quiet mode
   -r ADDRESS sets e-mail Reply-To ADDRESS
   -F activates female mode
+  -U show authorized URL
   -+ is an undocumented feature - test it :-)
     
 To manage your subuser and groups or forward or redirect files, use a 
@@ -204,6 +214,13 @@
 without wasting local disc space.
  
 With option -X you can specify any parameter, e.g.: -X autodelete=yes
+
+For HTTPS you can set the environment variables:
+SSLVERIFY=1                 # activate server identity verification
+SSLVERSION=TLSv1            # this is the default
+SSLCAPATH=/etc/ssl/certs    # path to trusted (root) certificates
+SSLCAFILE=/etc/ssl/cert.pem # file with trusted (root) certificates
+SSLCIPHERLIST=HIGH:!3DES    # see http://www.openssl.org/docs/apps/ciphers.html
   
 Partner program xx is an internet clipboard. See: xx -h
   
@@ -218,7 +235,27 @@
   FEXID="FEXSERVER USER AUTHID" $0 file recipient
 Example:
   FEXID="fex.flupp.org gaga\@flupp.org blubb" $0 big.file framstag\@rus.uni-stuttgart.de
-    
+
+You can define aliases (and optional fexsend options) in \$HOME/.fex/config.pl:
+  %alias = (
+    'alias1' => 'user1\@domain1.org',
+    'alias2' => 'user2\@domain2.org',
+    'both'   => 'user1\@domain1.org,user2\@domain2.org',
+    'extra'  => 'extra\@special.net:-i other -K -k 30',
+  );
+
+fexsend also respects aliases in $HOME/.mutt/aliases
+The alias priority is (descending):
+\$HOME/.fex/config.pl
+\$HOME/.mutt/aliases 
+fexserver address book  
+
+In \$HOME/.fex/config.pl you can also set the SSL* environment variables and the
+\$opt_* variables, e.g.:
+  
+\$ENV{SSLVERSION} = 'TLSv1';
+\${'opt_+'} = 1;
+\$opt_m = 200;
 EOD
 }
 
@@ -255,22 +292,25 @@
 our ($opt_q,$opt_h,$opt_H,$opt_v,$opt_m,$opt_c,$opt_k,$opt_d,$opt_l,$opt_I,
      $opt_K,$opt_D,$opt_u,$opt_f,$opt_a,$opt_C,$opt_R,$opt_M,$opt_L,$opt_Q,
      $opt_A,$opt_i,$opt_z,$opt_Z,$opt_b,$opt_P,$opt_x,$opt_X,$opt_V,$opt_U,
-     $opt_s,$opt_o,$opt_g,$opt_F,$opt_n,$opt_r);
+     $opt_s,$opt_o,$opt_g,$opt_F,$opt_n,$opt_r,$opt_S,$opt_N);
 
 if ($xx) {
   $opt_q = 1 if @ARGV and $ARGV[-1] eq '--' and pop @ARGV or not -t STDOUT;
   $opt_h = $opt_v = $opt_m = $opt_I = 0;
   $opt_X = '';
+  $_ = "$fexhome/config.pl"; require if -f;
   getopts('hvIm:') or die $usage;
 } else {
   $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;
+  $opt_S = $opt_N = 0;
   ${'opt_@'} = ${'opt_!'} = ${'opt_+'} = ${'opt_.'} = ${'opt_/'} = 0;
   ${'opt_='} = ${'opt_#'} = '';
   $opt_u = $opt_f = $opt_a = $opt_C = $opt_i = $opt_b = $opt_P = $opt_X = '';
   $opt_s = $opt_r = '';
-  getopts('hHvcdognVDKlILUARWMFzZqQ@!+./r:m:k:u:f:a:s:C:i:b:P:x:X:=:#:') 
+  $_ = "$fexhome/config.pl"; require if -f;
+  getopts('hHvcdognVDKlILUARWMFzZqQS@!+./r:m:k:u:f:a:s:C:i:b:P:x:X:N:=:#:') 
     or die $usage;
 
   if ($opt_H) {
@@ -307,16 +347,12 @@
     die "$0: you cannot use both options -I and -R\n";
   }
 
-  if ($opt_R) {
-    &register;
-    exit;
-  }
-
   # $opt_C is COMMENT command in F*EX protocol
   $opt_C =    
     ($opt_d)		? 'DELETE':
     ($opt_l or $opt_L)	? 'LIST':
     ($opt_Q)		? 'CHECKQUOTA':
+    ($opt_S)		? 'LISTSETTINGS':
     ($opt_Z)		? 'RECEIVEDLOG':
     ($opt_z)		? 'SENDLOG':
     (${'opt_!'})	? 'FOPLOG':
@@ -328,12 +364,21 @@
   $opt_D;
 }
 
+&get_ssl_env;
+
 if ($opt_h) {
   female_mode("show help?") if $opt_F;
   print $usage;
   exit;
 }
 
+
+if ($opt_R) {
+  &register;
+  exit;
+}
+
+
 die $usage if $opt_m and $opt_m !~ /^\d+/;
 
 if ($opt_P) { 
@@ -492,7 +537,7 @@
 
 &inquire if $windoof and not @ARGV and not
             ($opt_l or $opt_L or $opt_Q or $opt_A or $opt_U or $opt_I or
-             $opt_f or $opt_x);
+             $opt_f or $opt_x or $opt_N);
 
 if (${'opt_.'}) {
   $opt_C = "!SHORTMAIL! $opt_C";
@@ -503,20 +548,18 @@
 }
 
 unless ($skey or $gkey or $anonymous) {
-  if ($opt_v) {
-    if ($FEXID) {
-      warn "ID data from \$FEXID: $fexcgi $from $id\n";
-    } elsif (-f $idf) {
-      warn "ID data from $idf: $fexcgi $from $id\n";
-    }
-  }
-  warn "Server/User: $fexcgi/$from\n" unless $opt_q;
+  if (not $opt_q and (
+    $opt_f||$opt_x||$opt_Q||$opt_l||$opt_L||$opt_U||$opt_z||$opt_Z||$opt_A
+    ||$opt_d||${'opt_!'}||${'opt_@'})
+  ) { warn "Server/User: $fexcgi/$from\n" }
 }
 
 if    ($opt_V and not @ARGV)    	{ exit }
 if    ($opt_f) 				{ &forward } 
 elsif ($opt_x) 				{ &modify } 
+elsif ($opt_N) 				{ &renotify } 
 elsif ($opt_Q) 				{ &query_quotas } 
+elsif ($opt_S) 				{ &query_settings } 
 elsif ($opt_l or $opt_L)		{ &list } 
 elsif ($opt_U)				{ &show_URL } 
 elsif ($opt_z or $opt_Z or ${'opt_!'})	{ &get_log } 
@@ -817,7 +860,51 @@
     from	=> $from,
     to		=> $from,
     id		=> $sid,
-    comment	=> $opt_C, 
+    command	=> $opt_C, 
+  );
+  die "$0: no response from fex server $server\n" unless @r;
+  $_ = shift @r;
+  unless (/^HTTP.* 2/) {
+    s:HTTP/[\d\. ]+::;
+    die "$0: server response: $_\n";
+  }
+  if (($_) = grep(/^X-Sender-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "sender quota (used): $1 ($2) MB\n";
+  } else {
+    print "sender quota: unlimited\n";
+  }
+  if (($_) = grep(/^X-Recipient-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "recipient quota (used): $1 ($2) MB\n";
+  } else {
+    print "recipient quota: unlimited\n";
+  }
+}
+
+
+sub query_settings {
+  my (@r,$r);
+  local $_;
+
+  female_mode("query settings?") if $opt_F;
+
+  if ($FEXID) {
+    print "ID data from \$FEXID\n";
+  } elsif (-f $idf) {
+    print "ID data from $idf\n";
+  } else {
+    die "$0: found no ID\n";
+  }
+  print "server: $fexcgi\n";
+  print "user: $from\n";
+  print "auth-ID: $id\n";
+  print "login URL: ";
+  &show_URL;
+  
+  @r = formdatapost(
+    from	=> $from,
+    to		=> $from,
+    id		=> $sid,
+    command	=> $opt_C, 
   );
   die "$0: no response from fex server $server\n" unless @r;
   $_ = shift @r;
@@ -825,6 +912,18 @@
     s:HTTP/[\d\. ]+::;
     die "$0: server response: $_\n";
   }
+  if (($_) = grep(/^X-Autodelete/,@r) and /:\s+(\w+)/) {
+    print "autodelete: $1\n";
+  }
+  if (($_) = grep(/^X-Default-Keep/,@r) and /(\d+)/) {
+    print "default keep: $1 days\n";
+  }
+  if (($_) = grep(/^X-Default-Locale/,@r) and /:\s+(\w+)/) {
+    print "default locale: $1\n";
+  }
+  if (($_) = grep(/^X-MIME/,@r) and /:\s+(\w+)/) {
+    print "display file with browser: $1\n";
+  }
   if (($_) = grep(/^X-Sender-Quota/,@r) and /(\d+)\s+(\d+)/) {
     print "sender quota (used): $1 ($2) MB\n";
   } else {
@@ -934,7 +1033,6 @@
 
 sub show_URL {
   printf "%s/fup/%s\n",$fexcgi,encode_b64("from=$from&id=$id");
-  exit;
 }
 
 
@@ -946,7 +1044,7 @@
     from	=> $from,
     to		=> $from,
     id		=> $sid,
-    comment	=> $opt_C, 
+    command	=> $opt_C, 
   );
   die "$0: no response from fex server $server\n" unless @r;
   $_ = shift @r;
@@ -1135,8 +1233,22 @@
         query_sid($server,$port);
       }
       foreach $to (@to) {
+        # alias in local config?
+        if ($alias{$to}) {
+          if ($alias{$to} =~ /(.+?):(.+)/) {
+            my $ato = $1;
+            my $opt = $2;
+            my @argv = @_ARGV;
+            pop @argv;
+            # special extra upload
+            system $0,split(/\s/,$opt),@argv,$ato;
+            $to = '';
+          } else {
+            $to = $alias{$to};
+          }
+        }
         # alias in server address book?
-        if ($AB{$to}) {  
+        elsif ($AB{$to}) {  
           # do not substitute alias with expanded addresses because then 
           # keep and autodelete options from address book will get lost
           # $to = $AB{$to};
@@ -1169,7 +1281,8 @@
       }
     }
   
-    $to = join(',',@to);
+    $to = join(',',grep /./,@to) or exit;
+    warn "Server/User: $fexcgi/$from\n" unless $opt_q;
   
     if (
       not $skey and not $gkey
@@ -1356,10 +1469,21 @@
     }
     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);
       foreach (@r) {
-        if ($from eq $to or $from =~ /^\Q$to\E@/i or $nomail or $anonymous) {
-          print "$1\n" if /^X-(Recipient.*)/i;
-          print "$2\n" if /^(X-)?(Location:.*)/i;
+        if (/^(X-)?(Recipient.*)/i) {
+          $recipient = $2;
+          if (/notification=no/i) { $nonot = 1 }
+          else                    { $nonot = 0 }
+        }
+        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";
+          }
         }
       }
     }
@@ -1440,6 +1564,61 @@
 }
 
 
+sub renotify {
+  my (@r);
+  my ($to,$n,$dkey,$file,$req,$recipient);
+  local $_;
+
+  die $usage if @ARGV;
+
+  open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+  while (<$fexlist>) {
+    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_N) {
+      $n = $1;
+      $dkey = $2;
+      last;
+    }
+  }
+  close $fexlist;
+  
+  unless ($n) {
+    die "$0: file #$opt_N not found in fexlist\n";
+  }
+
+  female_mode("resend notification for file #$opt_N?") if $opt_F;
+
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  $req = "GET $proxy_prefix/fup?"
+        ."from=$from&ID=$sid&dkey=$dkey&command=RENOTIFY"
+        ." HTTP/1.1";
+  sendheader("$server:$port",$req);
+  http_response();
+  while (<$SH>) {
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    last if /^\s*$/;
+    if (/^X-Notify: (.+)\/(.+)\/(.+)/) {
+      $recipient = $1;
+      $file = $3;
+    }
+  }
+  
+  if ($file) {
+    print "notification e-mail for $file has been resent to $recipient\n";
+  } else {
+    if ($opt_v) {
+      die "$0: server failed\n";
+    } else {
+      die "$0: server failed, rerun command with option -v\n";
+    }
+  }
+  
+  exit;
+}
+
+
 sub modify {
   my (@r);
   my ($n,$dkey,$file,$req);
@@ -1702,11 +1881,12 @@
   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,$P{to},$P{from},$P{id},
-                                   $filename,$fileid);
+    ($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;
-      die "$0: $file has been already transferred\n";
+      warn "$0: $file has been already transferred\n";
+      return $file;
     } elsif ($seek and $seek < $filesize) {
       $resume = " (resuming at byte $seek)";
     } elsif ($filesize <= $seek) {
@@ -2063,7 +2243,7 @@
   $zipbase =~ s/\.zip$//;
   map { s/([^_\w\+\-\.])/\\$1/g } @files;
 
-  open my $ff,"find @files -type f|" or die "$0: cannot search for @_ - $!\n";
+  open my $ff,"find @files|" or die "$0: cannot search for @_ - $!\n";
   @files = ();
 
   zipfile: for (;;) {
@@ -2075,7 +2255,8 @@
     $zsize = 0;
     while ($file = <$ff>) {
       chomp $file;
-      next if -l $file or not -f $file;
+      # next if -l $file or not -f $file;
+      next unless -f $file;
       $size = -s $file;
       if ($size > 2147480000) {
         unlink @zipfiles;
@@ -2114,7 +2295,10 @@
   }
   print $cmd,"\n" if $opt_v;
   open $cmd,"|$cmd" or die "$0: cannot create $zip - $!\n";
-  foreach (@_) { print {$cmd} $_."\n" }
+  foreach (@_) { 
+    print {$cmd} $_."\n";
+    print "  $_\n" if $opt_v;
+  }
   close $cmd or die "$0: zip failed - $!\n";
 
   return $zip;
@@ -2137,67 +2321,6 @@
 }
 
 
-sub serverconnect {
-  my ($server,$port) = @_;
-  my $connect = "CONNECT $server:$port HTTP/1.1";
-  local $_;
-  
-  if ($proxy) {
-    tcpconnect(split(':',$proxy));
-    if ($port == 443) {
-      printf "--> %s\n",$connect if $opt_v;
-      nvtsend($connect,"");
-      $_ = <$SH>;
-      s/\r//;
-      printf "<-- $_"if $opt_v;
-      unless (/^HTTP.1.. 200/) {
-        die "$0: proxy error : $_";
-      }
-      eval "use IO::Socket::SSL";
-      die "$0: cannot load IO::Socket::SSL\n" if $@;
-      $SH = IO::Socket::SSL->start_SSL($SH);
-    }
-  } else {
-    tcpconnect($server,$port);
-  }
-}
-
-
-# set up tcp/ip connection
-sub tcpconnect {
-  my ($server,$port) = @_;
-  
-  if ($SH) {
-    close $SH;
-    undef $SH;
-  }
-  
-  if ($port == 443) {
-    eval "use IO::Socket::SSL";
-    die "$0: cannot load IO::Socket::SSL\n" if $@;
-    $SH = IO::Socket::SSL->new(
-      PeerAddr => $server,
-      PeerPort => $port,
-      Proto    => 'tcp',
-    );
-  } else {
-    $SH = IO::Socket::INET->new(
-      PeerAddr => $server,
-      PeerPort => $port,
-      Proto    => 'tcp',
-    );
-  }
-  
-  if ($SH) {
-    autoflush $SH 1;
-  } else {
-    die "$0: cannot connect $server:$port - $@\n";
-  }
-  
-  print "TCPCONNECT to $server:$port\n" if $opt_v;
-}
-
-
 sub query_file {
   my ($server,$port,$to,$from,$id,$filename,$fileid) = @_;
   my $seek = 0;
@@ -2480,42 +2603,6 @@
 }
 
 
-sub sendheader {
-  my $sp = shift;
-  my @head = @_;
-  my $head;
-  
-  push @head,"Host: $sp";
-  
-  foreach $head (@head) {
-    print "--> $head\n" if $opt_v;
-    print {$SH} $head,"\r\n";
-  }
-  print "-->\n" if $opt_v;
-  print {$SH} "\r\n";
-}
-
-
-sub nvtsend {
-  local $SIG{PIPE} = sub { $sigpipe = "@_" };
-  
-  $sigpipe = '';
-  
-  die "$0: internal error: no active network handle\n" unless $SH;
-  die "$0: remote host has closed the link\n" unless $SH->connected;
-  
-  foreach my $line (@_) {
-    print {$SH} $line,"\r\n";
-    if ($sigpipe) {
-      undef $SH;
-      return 0;
-    }
-  }
-  
-  return 1;
-}
-
-
 # transfer status
 sub ts {
   my ($b,$tb) = @_;
@@ -2568,6 +2655,7 @@
         s/autodelete=\w+/autodelete=$opt_D/ if $opt_D;
         s/keep=\d+/keep=$opt_k/             if $opt_k;
         print;
+        $frecipient ||= (split)[1];
       }
     }
   } else {
@@ -2624,12 +2712,6 @@
 }
 
 
-sub mtime {
-  my @d = localtime((stat shift)[9]);
-  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
-}
-
-
 # emulate seek on a pipe
 sub readahead {
   my $fh = shift; # filehandle
@@ -2706,21 +2788,6 @@
 }
 
 
-# from MIME::Base64::Perl
-sub encode_b64 {
-  my $res = "";
-  my $eol = "\n";
-  my $padding;
-  
-  pos($_[0]) = 0;
-  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
-  $res =~ tr|` -_|AA-Za-z0-9+/|;
-  $padding = (3-length($_[0])%3)%3;
-  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
-  return $res;
-}
-
-
 sub female_mode {
   local $_;
   if (open my $tty,'/dev/tty') {
@@ -2742,6 +2809,7 @@
 sub http_response {
   local $_ = shift || <$SH>;
   my @r = @_;
+  my $error;
 
   $_ = <$SH> unless $_;
   unless (defined $_ and /\w/) {
@@ -2755,8 +2823,13 @@
     die "$0: server error: $_\n@r\n";
   }
   unless (/^HTTP.* 200/) {
-    s/HTTP.[\s\d.]+//;
-    die "$0: server error: $_\n";
+    $error = $_;
+    $error =~ s/HTTP.[\s\d.]+//;
+    if ($opt_v) {
+      print "<-- $_";
+      print "<-- $_" while <$SH>;
+    }
+    die "$0: server error: $error\n";
   }
 
   print "<-- $_\n" if $opt_v;
@@ -2764,6 +2837,48 @@
 }
 
 
+sub ws {
+  local $_ = shift;
+  return split;
+}
+
+
+sub update {
+  my $cfb = '### common functions ###';
+  my $cfc;
+  
+  local $/;
+  
+  open $0,$0 or die "cannot read $0 - $!\n";
+  $_ = <$0>;
+  close $0;
+  s/.*\n$cfb\n//s;
+  $cfc = $_;
+  
+  foreach my $p (qw(fexget sexsend)) {
+    open $p,$p or die "cannot read $p - $!\n";
+    $_ = <$p>;
+    close $p;
+    s/\n$cfb.*/\n$cfb\n$cfc/s;
+    system "vv -s $p";
+    open $p,'>',$p or die "cannot write $p - $!\n";
+    print {$p} $_;
+    close $p;
+  }
+
+  exec "l $0 fexget sexsend";
+  exit;
+}
+
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
 sub urldecode {
   local $_ = shift;
   s/\%([a-f\d]{2})/chr(hex($1))/ige;
@@ -2771,7 +2886,170 @@
 }
 
 
-sub ws {
-  local $_ = shift;
-  return split;
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  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) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
+sub encode_b64 {
+  my $res = "";
+  my $eol = "\n";
+  my $padding;
+  
+  pos($_[0]) = 0;
+  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+  $res =~ tr|` -_|AA-Za-z0-9+/|;
+  $padding = (3-length($_[0])%3)%3;
+  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+  return $res;
 }
diff -Nru fex-20140917/bin/fexsrv fex-20150120/bin/fexsrv
--- fex-20140917/bin/fexsrv	2014-09-16 11:32:41.000000000 +0200
+++ fex-20150120/bin/fexsrv	2014-12-16 15:20:32.000000000 +0100
@@ -77,11 +77,6 @@
   SID CHECKRECIPIENT GROUPS QUOTA FILEID MULTIPOST XKEY FILEQUERY FILESTREAM
   JUP NOSTORE AXEL FEXMAIL FILELINK
 ));
-  
-# ignore bullshit requests
-my $ignore = join('|',(
-  '^GET /w00tw00t\.at\.ISC\.SANS',
-));
 
 $port = 0;
 
@@ -219,11 +214,6 @@
       if ($ENV{PROTO} eq 'https') { $port = 443 }
       else                        { $port = 80 }
     }
-
-    if (/$ignore/) {
-      fexlog($connect,@log,'IGNORED');
-      exit;
-    }
   }
 
   exit unless @header;
@@ -241,6 +231,7 @@
   $request = shift @header;
   if ($request !~ /^(GET|HEAD|POST|OPTIONS).*HTTP\/\d\.\d$/i) {
     fexlog($connect,$request,"DISCONNECT: no HTTP request");
+    badlog("no HTTP request: $request");
     exit;
   }
   
@@ -341,7 +332,7 @@
       exit;
     }
 
-    if (/^User-Agent: (FDM)/ and $header =~ /\nRange:/) {
+    if ($header =~ /\nRange:/ and /^User-Agent: (FDM)/) {
       disconnect($1,"499 Download Manager $1 Not Supported",30);
     }
     
@@ -776,7 +767,7 @@
 
 sub badlog {
   my $request = shift;
-  my $n = 1;
+  my @n;
   my $ed = "$spooldir/.error";
   local $_;
   
@@ -792,10 +783,10 @@
     if (open $ra,"+>>$ed/$ra") {
       flock($ra,LOCK_EX);
       seek $ra,0,SEEK_SET;
-      $n++ while <$ra>;
+      @n = <$ra>;
       printf {$ra} "%s %s\n",isodate(time),$request;
       close $ra;
-      &$max_error_handler($ra,$n) if $n > $max_error;
+      &$max_error_handler($ra,@n) if scalar(@n) > $max_error;
     }
   }
 }
diff -Nru fex-20140917/bin/fexwall fex-20150120/bin/fexwall
--- fex-20140917/bin/fexwall	2014-07-24 15:45:39.000000000 +0200
+++ fex-20150120/bin/fexwall	2015-01-15 09:36:20.000000000 +0100
@@ -62,12 +62,43 @@
   close $sig;
 }
 
+local $/ = "\n";
+
 chdir $spooldir or die "$0: $spooldir - $!\n";
 
-@users = grep { s:/@:: } glob("*/@");
+# @users = grep { chomp;s:/@:: } glob("*/@");
+foreach $user (glob("*@*")) {
+  if (-f "$user/@" and (readlink "$user/\@NOTIFICATION"||'') !~ /no/i) {
+    push @users,$user;
+  }
+}
+
+foreach $group (glob "*/\@GROUP/*") {
+  if (open $group,$group) {
+    while (<$group>) {
+      s/#.*//;
+      s/:.*\n//;
+      push @users,$_ if /@/;
+    }
+    close $group;
+  }
+}
+    
+foreach $subuser (glob "*/\@SUBUSER") {
+  if (open $subuser,$subuser) {
+    while (<$subuser>) {
+      s/#.*//;
+      s/:.*\n//;
+      push @users,$_ if /@/;
+    }
+    close $subuser;
+  }
+}
+    
 # @users = qw'framstag@fex';
 die "$0: no users found\n" unless @users or grep /@/,@users;
 push @users,$bcc;
+@users = uniq(@users);
 
 open $sendmail,'|-',$sendmail,@users or die "$0: $sendmail - $!\n";
 
@@ -82,6 +113,10 @@
 print "mail sent to:\n",map { "$_\n" } @users;
 exit;
 
+sub uniq {
+  my %x;
+  grep !$x{$_}++,@_;
+}
 
 sub usage {
   print "usage: $0 \"SUBJECT\" < mail.text\n";
diff -Nru fex-20140917/bin/l fex-20150120/bin/l
--- fex-20140917/bin/l	1970-01-01 01:00:00.000000000 +0100
+++ fex-20150120/bin/l	2014-12-09 12:31:43.000000000 +0100
@@ -0,0 +1,597 @@
+#!/usr/bin/perl -w
+#
+# l / ll / lf / llf -  substitute of the classic ls command
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Copyright: GNU General Public License 
+
+use Cwd qw'abs_path';
+use File::Basename;
+use Getopt::Std;
+
+# the name of the game
+$0 =~ s:.*/::;
+
+$ENV{LC_CTYPE} = 'C';
+
+# unshift @ARGV,split /\s+/,$ENV{'l_opt'} if $ENV{'l_opt'};
+
+@ARGV = grep { chomp } <STDIN> if "@ARGV" eq '-';
+
+# 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 = 0;
+${'opt_*'} = ${'opt_?'} = 0;
+$opt_m = $opt_f = $opt_F = $opt_D = '';
+&usage if !getopts('hdnlLNitcuarsUSRz*?m:f:D:F:') || $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';
+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;
+}
+
+$postsort = $opt_t||$opt_s;
+$postproc = $postsort||$opt_z;
+
+&examples if ${'opt_?'};
+
+# mark for squeeze operation
+$z = $opt_z ? "\0" : '';
+
+# default sorting methode
+if    ($opt_U) { $lcsort = sub { return @_ } }
+elsif ($opt_r) { $lcsort = sub { sort { lc $b cmp lc $a } @_ } }
+else           { $lcsort = sub { sort { lc $a cmp lc $b } @_ } }
+
+# default: list only files not beginning with a dot
+unless ($opt_m) {
+  if ($opt_a) { $opt_m = '.' }
+  else        { $opt_m = '^[^\.]' }
+}
+
+$older = $newer = 0;
+
+if ($opt_D) {
+  if ($opt_D =~ /:(\d+)([mhd])/) {
+    $older = $1;
+    my $z = $2 || 's';
+    if    ($z =~ /m/) { $older *= 60 }
+    elsif ($z =~ /h/) { $older *= 60*60 }
+    elsif ($z =~ /d/) { $older *= 60*60*24 }
+  } elsif ($opt_D =~ /:(\d\d\d\d-\d\d-\d\d)$/) {
+    $older = $1;
+  }
+  if ($opt_D =~ /(\d+)([mhd]):/) {
+    $newer = $1;
+    my $z = $2 || 's';
+    if    ($z =~ /m/) { $newer *= 60 }
+    elsif ($z =~ /h/) { $newer *= 60*60 }
+    elsif ($z =~ /d/) { $newer *= 60*60*24 }
+  } elsif ($opt_D =~ /^(\d\d\d\d-\d\d-\d\d):/) {
+    $newer = $1;
+  }
+}
+  
+# preselect date field number
+if    ($opt_c) { $sdf = 'c' }
+elsif ($opt_u) { $sdf = 'a' }
+else           { $sdf = 'm' }
+
+# any arguments?
+if (@ARGV) { @ARGV = &$lcsort(@ARGV) }
+else       { @ARGV = &getfiles('.') }
+
+# build files list
+&collect(@ARGV);
+
+# post process files list?
+# remark: if no postprocessing, files list has been already printed in list()
+if (@LIST && $postproc) {
+
+  # on -t or -s option sort list on date or size
+  # and then strip of leading sorting pre-string
+  @LIST = grep { s/.{21}// } reverse sort @LIST if $postsort;
+
+  # squeeze size field (= remove unnecessary spaces)
+  if ($opt_z and not $opt_f) {
+    $opt_z = '%'.$opt_z.'s ';
+    @LIST = grep { s/\0 *([,\d\.\-]+) /sprintf($opt_z,$1)/e } @LIST;
+  }
+  
+  @LIST = reverse @LIST if $opt_r;
+
+  if (not ($opt_t or $opt_U) and grep /^d[rR-][wW-][xX-]/,@LIST) {
+    foreach (@LIST) { print if /^d/ }
+    foreach (@LIST) { print unless /^d/ }
+  } else { 
+    print @LIST;
+  }
+}
+
+# print statistics summary?
+if ($opt_S && $SS) {
+  print "$SS file(s):";
+  printf " r=%d (%s Bytes)",$SS{'-'},&d3($Ss) if $SS{'-'};
+  delete $SS{'-'};
+  foreach my $type (qw(l d c b p s ?)) { 
+    printf " %s=%d",$type,$SS{$type} if $SS{$type};
+    delete $SS{$type};
+  }
+  foreach my $type (keys %SS) { printf " %s=%d",$type,$SS{$type} }
+  print "\n";
+}
+
+exit ($found ? 0 : 1);
+
+
+# collect files and build file lists
+# 
+# INPUT: filenames
+#
+# GLOBAL: @LIST
+sub collect {
+  my @files = @_;
+  my $f;
+
+  # loop over all argument files/directories
+  foreach $f (@files) {
+  
+    # skip jed and emacs backup files
+    # next if $f =~ /~$/ and not $opt_a and not $opt_l;
+    
+    # recursive?
+    if ($opt_R) {
+
+      # list single file
+      if ($opt_L) {
+        unless (-e $f) {
+          warn "$0: dangling symlink $f\n";
+          next;
+        }
+        $f = abs_path($f);
+      }
+      list($f);
+
+      # traverse real subdirs
+      if (-d $f and not -l $f) { 
+        $f =~ s:/*$:/:;
+        collect(getfiles($f));
+      }
+      
+    } else {
+  
+      # suppress trailing / on -d option
+      $f =~ s:/$:: if $opt_d;
+    
+      # on trailing / list subdirs, too
+      if ($f =~ m:/$:) { &list(&getfiles($f)) }
+      elsif ($f eq '') { &list('/') }
+      else {
+        if ($opt_L) {
+          unless (-e $f) {
+            warn "$0: dangling symlink $f\n";
+            next;
+          }
+          $f = abs_path($f);
+        }
+        list($f);
+      }
+      
+    }
+  }
+}
+
+
+# list file(s)
+#
+# INPUT: filenames
+#
+# GLOBAL: @LIST (filenames-list)
+sub list {
+  my @files = @_;
+  my ($file,$line,$linkname,$inode,$links,$size,$mode,$uid,$gid,$date,%dates);
+  my ($day);
+
+  foreach $file (@files) {
+
+    next if $opt_F and not fmatch($file);
+    next if $opt_N and (not -f $file or -l $file);
+
+    # get file information
+    # if ($opt_L and stat $file or not $opt_L and lstat $file) {
+    if (lstat $file) {
+      ($linkname,$inode,$links,$size,$mode,$uid,$gid,$date,%dates) = &info($file);
+    } elsif ($! eq "Permission denied") {
+      $linkname = $file;
+      $inode = $links = $size = $uid = $gid = '?';
+      $mode = $opt_l ? '?---------' : '?---';
+      $date = '????-??-?? ??:??:??';
+      %dates = ('m' => 0, 'a' => 0, 'c' => 0);
+    } else {
+      warn "$0: ".quote($file)." - $!\n";
+      next;
+    }
+
+    $day = $date;
+    $day =~ s/\s.*//;
+    
+    if ($older) {
+      next if $older =~ /-/ and $day gt $older;
+      next if $older !~ /-/ and $dates{m} > time-$older;
+    }
+    if ($newer) {
+      next if $newer =~ /-/ and $day lt $newer;
+      next if $newer !~ /-/ and $dates{m} < time-$newer;
+    }
+    
+    if (defined $linkname) {
+
+      # prepend sorting string
+      $line = '';
+      $line = sprintf '%21s',$date if $opt_t;
+      $line = sprintf '%21s',$size if $opt_s;
+
+      unless ($opt_n) {
+        $uid = substr($uid,0,8);
+        $gid = substr($gid,0,8);
+      }
+
+      # user defined format?
+      if ($opt_f) {
+        foreach my $i (split '',$opt_f) {
+	  if ($opt_n) {
+	    $i =~ tr/AD/ad/;
+	    if    ($i eq 'm') { $line .= sprintf '%06o ',  $mode }
+	    elsif ($i eq 'u') { $line .= sprintf '%6d ',   $uid }
+	    elsif ($i eq 'g') { $line .= sprintf '%6d ',   $gid }
+	    elsif ($i eq 's') { $line .= sprintf "$z%16s ",$size }
+	    elsif ($i eq 'l') { $line .= sprintf '%3s ',   $links }
+	    elsif ($i eq 'i') { $line .= sprintf '%14s ',  $inode }
+	    elsif ($i eq 'd') { $line .= sprintf '%10s ',  $date }
+	    elsif ($i eq 'a') { $line .= sprintf '%10s %10s %10s ', 
+	                                 $dates{'a'},$dates{'m'},$dates{'c'} }
+	  } else {
+	    if    ($i eq 'm') { $line .= $mode.' ' }
+	    elsif ($i eq 'u') { $line .= sprintf '%-8s ',  $uid }
+	    elsif ($i eq 'g') { $line .= sprintf '%-8s ',  $gid }
+	    elsif ($i eq 's') { $line .= sprintf "$z%19s ",$size }
+	    elsif ($i eq 'l') { $line .= sprintf '%3s ',   $links }
+	    elsif ($i eq 'i') { $line .= sprintf '%14s ',  $inode }
+	    elsif ($i eq 'd') { $line .= $date.' ' }
+	    elsif ($i eq 'D') { $line .= $date.' ' }
+	    elsif ($i eq 'a') { $line .= &isodate($dates{'a'}).' '.
+	                                 &isodate($dates{'m'}).' '.
+	                                 &isodate($dates{'c'}).' ' }
+	    elsif ($i eq 'A') { $line .= &isodate($dates{'a'}).' '.
+	                                 &isodate($dates{'m'}).' '.
+	                                 &isodate($dates{'c'}).' ' }
+	  }
+	}
+	
+      # predefined formats
+      } else {
+      
+	if ($opt_n) {
+          if ($opt_l) { $line .= sprintf "%06o %6d %6d $z%15s %10d ",
+	                                 $mode,$uid,$gid,$size,$date }
+          else        { $line .= sprintf "%06o $z%15s %10d ",
+	                                 $mode,$size,$date }
+	} else {
+          if ($opt_l) { $line .= sprintf "%s %-8s %-8s $z%19s %s ",
+	                                 $mode,$uid,$gid,$size,$date }
+          else        { $line .= sprintf "%s $z%19s %s ",
+	                                 $mode,$size,substr($date,0,-3) }
+        }
+	
+	if ($opt_i)   { $line .= sprintf '%3s %10s ',$links,$inode }
+      }
+
+      $line .= $linkname."\n";
+      
+      if ($postproc) { 
+        push @LIST,$line;
+      } else { 
+        $line =~ s/\0//;
+        print $line;
+      }
+      $found++;
+      
+    } else {
+      lstat $file;
+      warn "$0: cannot get dir-info for ".quote($file)." - $!\n";
+    }
+    
+  }
+}
+
+# get file information
+#
+# INPUT: file name
+#
+# OUTPUT: filename with linkname, inode, hard link count, size, mode string, 
+#         UID, GID, isodate
+sub info {
+  my $file = shift;
+  my ($linkname,$links,$mode,$bmode,$uid,$gid,$date,%dates,@stat);
+  my $size = '-';
+  my $inode = '?';
+  my @rwx = qw/--- --x -w- -wx r-- r-x rw- rwx/;
+  my $type;
+
+  if ($opt_L) { @stat = stat $file }
+  else        { @stat = lstat $file }
+  
+  if (@stat) {
+  
+    $inode = $stat[1];
+    $bmode = $stat[2];
+    $links = $stat[3];
+    %dates = ('m' => $stat[9], 
+              'a' => $stat[8], 
+	      'c' => $stat[10]);
+
+    if ($opt_n) {
+      $uid  = $stat[4];
+      $gid  = $stat[5];
+      $date = $dates{$sdf};
+    } else {
+      $uid  = getpwuid($stat[4]) || $stat[4];
+      $gid  = getgrgid($stat[5]) || $stat[5];
+      $date = &isodate($dates{$sdf});
+    }
+    
+    if    (-f _)	    { $type = '-'; $size = $stat[7]; }
+    elsif (!$opt_L && -l _) { $type = 'l'; }
+    elsif (-d _)            { $type = 'd'; }
+    elsif (-c _)            { $type = 'c'; $size = &nodes($stat[6]); }
+    elsif (-b _)            { $type = 'b'; $size = &nodes($stat[6]); }
+    elsif (-p _)            { $type = 'p'; }
+    elsif (-S _)            { $type = 's'; }
+    else                    { $type = '?'; }
+
+    if ($opt_n) {
+      $mode = $stat[2];
+      $size = $stat[7] if $size eq '-';
+    } else {
+      if ($opt_l) {
+        $mode = $rwx[$bmode & 7];
+        $bmode >>= 3;
+        $mode = $rwx[$bmode & 7] . $mode;
+        $bmode >>= 3;
+        $mode = $rwx[$bmode & 7] . $mode;
+        substr($mode,2,1) =~ tr/-x/Ss/ if -u _;
+        substr($mode,5,1) =~ tr/-x/Ss/ if -g _;
+        substr($mode,8,1) =~ tr/-x/Tt/ if -k _;
+        $mode = $type.$mode;
+      } else {
+        # with short list display only effektive file access modes
+        $mode = $type 
+	        . (-r _ ? 'R' : '-')
+                . (-w _ ? 'W' : '-')
+                . (-x _ ? 'X' : '-');
+        substr($mode,2,1) =~ tr/-x/Ss/ if -u _ or -g _;
+        substr($mode,3,1) =~ tr/-x/Tt/ if -k _;
+      }
+    }
+
+  # fall back to ls command if perl lstat failed
+  } else {
+    if ($opt_L) {
+      return;
+    } else {
+      ($mode,$links,$uid,$gid,$size) = split /\s+/,`ls -ld $file 2>/dev/null`;
+      return undef unless defined $mode;
+      $type = substr($mode,0,1);
+      # for (my $i=0;$i<3;$i++) { push @dates,'????-??-?? ??:??:??' }
+      # $date = `gfind $dir -maxdepth 1 -name $file -printf '%Ty-%Tm-%Td %TT\n'`;
+    }
+  }
+
+  # summarize statistics
+  if ($opt_S) {
+    $SS++;
+    $SS{$type}++;
+    $Ss += $size if $type eq '-';
+  }
+
+  $size = &d3($size);
+  
+  # determine longest size field
+  if ($opt_z) {
+    my $x = length $size;
+    $opt_z = $x if $x>$opt_z;
+  }
+  $linkname = ${'opt_*'} ? $file : quote($file) ;
+  if ($type eq 'l' and $opt_f !~ /n/) {
+    my $link = readlink($file);
+    if (defined $link) {
+      $linkname .= ' -> ' . (${'opt_*'} ? $link : quote($link));
+    }
+  }
+  $mode =~ s/\+$//;
+  #$mode .= ' ' unless $mode =~ /\+$/;
+  
+  return ($linkname,$inode,$links,$size,$mode,$uid,$gid,$date,%dates);
+}
+
+
+# reformat integer into 3-digit doted format
+# (when non-numerical mode is set)
+#
+# INPUT: integer or '-'
+#
+# OUTPUT: d3-string
+sub d3 {
+  local $_ = shift;
+  if ($opt_n) { s/-/0/ }
+  else        { while (s/(\d)(\d\d\d\b)/$1,$2/) {} }
+  return $_;
+}
+
+
+# get all files matching pattern $opt_m
+#
+# INPUT: directory to scan
+#
+# OUTPUT: files which match (sorted, directories first)
+sub getfiles {
+  my $dir = shift;
+  my @files = ();
+  my @dirs = ();
+  my $f;
+
+  if (opendir D,$dir) {
+    $dir = '' if $dir eq '.';
+    while (defined($f = readdir D)) { 
+    
+      # skip . and .. pseudo-subdirs
+      next if $f =~ m:(^|/)\.\.?/*$:;
+      # skip ONTAP snapshot dir
+      next if $f =~ m:(^|/)\.snapshot/*$:;
+      
+
+      # skip jed and emacs backup files
+      # next if $f =~ /~$/ and not $opt_a and not $opt_l;
+      
+      if ($f =~ /$opt_m/) {
+        my $x = $dir.$f;
+        if (not -l $x and -d $x and not ($opt_R or $postsort or $opt_U)) { 
+          push @dirs,$x;
+        } else { 
+          push @files,$x;
+        }
+      }
+    }
+    closedir D;
+    unless ($postsort) {
+      @files = &$lcsort(@files);
+      @dirs  = &$lcsort(@dirs);
+    }
+  } else {
+    warn "$0: cannot read $dir : $!\n";
+  }
+  
+  return (@dirs,@files);
+}
+
+
+# reformat integer to string node
+#
+# INPUT: integer node
+#
+# OUTPUT: string node
+sub nodes {
+  my $rdev = shift;
+  return sprintf("%03d,%03d", ($rdev >> 8) & 255, $rdev & 255);
+}
+
+
+# reformat timetick to ISO date string
+#
+# INPUT: timetick
+#
+# OUTPUT: ISO date string
+sub isodate {
+  my @d = localtime shift;
+  return sprintf('%d-%02d-%02d %02d:%02d:%02d',
+                 $d[5]+1900,$d[4]+1,$d[3],$d[2],$d[1],$d[0]);
+}
+
+
+# quote file name to printable name and escape shell meta chars
+#
+# INPUT: original file name
+#
+# OUTPUT: printable file name
+sub quote {
+  local $_ = shift;
+  my $mc = '\'\[\]\\\\ `"$?&<>$*()|{};';
+  
+  unless (defined $_) {
+    die "@_";
+    @x = caller;
+    die "@x";
+  }
+  if (s/[\000-\037\200-\237\241-\250]/?/g or /\'/) {
+    s/([$mc])/\\$1/g;
+    s/^~/\\~/;
+# } elsif (/[$mc]/ or -d and /:/) {
+  } elsif (/[$mc]/) {
+    $_ = "'$_'";
+  }
+  return $_;
+}
+
+
+sub fmatch {
+  my $file = shift;
+  my $link = readlink($file)||'';
+
+  return $file if basename($file) =~ /$opt_F/i;
+  return $link if basename($link) =~ /$opt_F/i;
+}
+
+
+sub usage {
+  my $opts = '[-lastcuidnrzLRNS*] [-f format] [-D X:Y]';
+  if ($0 ne 'lf') { 
+    print "usage: $0 $opts [-F regexp] [file...]\n";
+  }
+  $opts =~ s/R//;
+  print "usage: lf $opts regexp [directory...]\n";
+  print <<EOD;
+options: -l  long list
+         -a  list also .* files
+         -s  sort by size
+         -t  sort by time
+         -U  sort by nothing (original i-node order)
+         -c  list status change time instead of modification time
+         -u  list last access time instead of modification time
+         -i  list also inode and hard links numbers
+         -d  do not list contents of diretories
+         -n  numerical output
+         -r  reverse list
+         -z  squeeze size field (slows down output)
+         -L  derefernce symbolic links
+         -R  recursive into subdirs
+         -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 \\)
+	 -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
+EOD
+  exit 2;
+}
+
+sub examples {
+  print <<EOD;
+l *.c            # list files ending with .c
+l -la            # list all files in long format
+l -Rrs           # list files recursive reverse sorted by size 
+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)                                                       
+EOD
+  exit;
+}
diff -Nru fex-20140917/bin/lf fex-20150120/bin/lf
--- fex-20140917/bin/lf	1970-01-01 01:00:00.000000000 +0100
+++ fex-20150120/bin/lf	2014-12-09 12:31:43.000000000 +0100
@@ -0,0 +1,597 @@
+#!/usr/bin/perl -w
+#
+# l / ll / lf / llf -  substitute of the classic ls command
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Copyright: GNU General Public License 
+
+use Cwd qw'abs_path';
+use File::Basename;
+use Getopt::Std;
+
+# the name of the game
+$0 =~ s:.*/::;
+
+$ENV{LC_CTYPE} = 'C';
+
+# unshift @ARGV,split /\s+/,$ENV{'l_opt'} if $ENV{'l_opt'};
+
+@ARGV = grep { chomp } <STDIN> if "@ARGV" eq '-';
+
+# 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 = 0;
+${'opt_*'} = ${'opt_?'} = 0;
+$opt_m = $opt_f = $opt_F = $opt_D = '';
+&usage if !getopts('hdnlLNitcuarsUSRz*?m:f:D:F:') || $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';
+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;
+}
+
+$postsort = $opt_t||$opt_s;
+$postproc = $postsort||$opt_z;
+
+&examples if ${'opt_?'};
+
+# mark for squeeze operation
+$z = $opt_z ? "\0" : '';
+
+# default sorting methode
+if    ($opt_U) { $lcsort = sub { return @_ } }
+elsif ($opt_r) { $lcsort = sub { sort { lc $b cmp lc $a } @_ } }
+else           { $lcsort = sub { sort { lc $a cmp lc $b } @_ } }
+
+# default: list only files not beginning with a dot
+unless ($opt_m) {
+  if ($opt_a) { $opt_m = '.' }
+  else        { $opt_m = '^[^\.]' }
+}
+
+$older = $newer = 0;
+
+if ($opt_D) {
+  if ($opt_D =~ /:(\d+)([mhd])/) {
+    $older = $1;
+    my $z = $2 || 's';
+    if    ($z =~ /m/) { $older *= 60 }
+    elsif ($z =~ /h/) { $older *= 60*60 }
+    elsif ($z =~ /d/) { $older *= 60*60*24 }
+  } elsif ($opt_D =~ /:(\d\d\d\d-\d\d-\d\d)$/) {
+    $older = $1;
+  }
+  if ($opt_D =~ /(\d+)([mhd]):/) {
+    $newer = $1;
+    my $z = $2 || 's';
+    if    ($z =~ /m/) { $newer *= 60 }
+    elsif ($z =~ /h/) { $newer *= 60*60 }
+    elsif ($z =~ /d/) { $newer *= 60*60*24 }
+  } elsif ($opt_D =~ /^(\d\d\d\d-\d\d-\d\d):/) {
+    $newer = $1;
+  }
+}
+  
+# preselect date field number
+if    ($opt_c) { $sdf = 'c' }
+elsif ($opt_u) { $sdf = 'a' }
+else           { $sdf = 'm' }
+
+# any arguments?
+if (@ARGV) { @ARGV = &$lcsort(@ARGV) }
+else       { @ARGV = &getfiles('.') }
+
+# build files list
+&collect(@ARGV);
+
+# post process files list?
+# remark: if no postprocessing, files list has been already printed in list()
+if (@LIST && $postproc) {
+
+  # on -t or -s option sort list on date or size
+  # and then strip of leading sorting pre-string
+  @LIST = grep { s/.{21}// } reverse sort @LIST if $postsort;
+
+  # squeeze size field (= remove unnecessary spaces)
+  if ($opt_z and not $opt_f) {
+    $opt_z = '%'.$opt_z.'s ';
+    @LIST = grep { s/\0 *([,\d\.\-]+) /sprintf($opt_z,$1)/e } @LIST;
+  }
+  
+  @LIST = reverse @LIST if $opt_r;
+
+  if (not ($opt_t or $opt_U) and grep /^d[rR-][wW-][xX-]/,@LIST) {
+    foreach (@LIST) { print if /^d/ }
+    foreach (@LIST) { print unless /^d/ }
+  } else { 
+    print @LIST;
+  }
+}
+
+# print statistics summary?
+if ($opt_S && $SS) {
+  print "$SS file(s):";
+  printf " r=%d (%s Bytes)",$SS{'-'},&d3($Ss) if $SS{'-'};
+  delete $SS{'-'};
+  foreach my $type (qw(l d c b p s ?)) { 
+    printf " %s=%d",$type,$SS{$type} if $SS{$type};
+    delete $SS{$type};
+  }
+  foreach my $type (keys %SS) { printf " %s=%d",$type,$SS{$type} }
+  print "\n";
+}
+
+exit ($found ? 0 : 1);
+
+
+# collect files and build file lists
+# 
+# INPUT: filenames
+#
+# GLOBAL: @LIST
+sub collect {
+  my @files = @_;
+  my $f;
+
+  # loop over all argument files/directories
+  foreach $f (@files) {
+  
+    # skip jed and emacs backup files
+    # next if $f =~ /~$/ and not $opt_a and not $opt_l;
+    
+    # recursive?
+    if ($opt_R) {
+
+      # list single file
+      if ($opt_L) {
+        unless (-e $f) {
+          warn "$0: dangling symlink $f\n";
+          next;
+        }
+        $f = abs_path($f);
+      }
+      list($f);
+
+      # traverse real subdirs
+      if (-d $f and not -l $f) { 
+        $f =~ s:/*$:/:;
+        collect(getfiles($f));
+      }
+      
+    } else {
+  
+      # suppress trailing / on -d option
+      $f =~ s:/$:: if $opt_d;
+    
+      # on trailing / list subdirs, too
+      if ($f =~ m:/$:) { &list(&getfiles($f)) }
+      elsif ($f eq '') { &list('/') }
+      else {
+        if ($opt_L) {
+          unless (-e $f) {
+            warn "$0: dangling symlink $f\n";
+            next;
+          }
+          $f = abs_path($f);
+        }
+        list($f);
+      }
+      
+    }
+  }
+}
+
+
+# list file(s)
+#
+# INPUT: filenames
+#
+# GLOBAL: @LIST (filenames-list)
+sub list {
+  my @files = @_;
+  my ($file,$line,$linkname,$inode,$links,$size,$mode,$uid,$gid,$date,%dates);
+  my ($day);
+
+  foreach $file (@files) {
+
+    next if $opt_F and not fmatch($file);
+    next if $opt_N and (not -f $file or -l $file);
+
+    # get file information
+    # if ($opt_L and stat $file or not $opt_L and lstat $file) {
+    if (lstat $file) {
+      ($linkname,$inode,$links,$size,$mode,$uid,$gid,$date,%dates) = &info($file);
+    } elsif ($! eq "Permission denied") {
+      $linkname = $file;
+      $inode = $links = $size = $uid = $gid = '?';
+      $mode = $opt_l ? '?---------' : '?---';
+      $date = '????-??-?? ??:??:??';
+      %dates = ('m' => 0, 'a' => 0, 'c' => 0);
+    } else {
+      warn "$0: ".quote($file)." - $!\n";
+      next;
+    }
+
+    $day = $date;
+    $day =~ s/\s.*//;
+    
+    if ($older) {
+      next if $older =~ /-/ and $day gt $older;
+      next if $older !~ /-/ and $dates{m} > time-$older;
+    }
+    if ($newer) {
+      next if $newer =~ /-/ and $day lt $newer;
+      next if $newer !~ /-/ and $dates{m} < time-$newer;
+    }
+    
+    if (defined $linkname) {
+
+      # prepend sorting string
+      $line = '';
+      $line = sprintf '%21s',$date if $opt_t;
+      $line = sprintf '%21s',$size if $opt_s;
+
+      unless ($opt_n) {
+        $uid = substr($uid,0,8);
+        $gid = substr($gid,0,8);
+      }
+
+      # user defined format?
+      if ($opt_f) {
+        foreach my $i (split '',$opt_f) {
+	  if ($opt_n) {
+	    $i =~ tr/AD/ad/;
+	    if    ($i eq 'm') { $line .= sprintf '%06o ',  $mode }
+	    elsif ($i eq 'u') { $line .= sprintf '%6d ',   $uid }
+	    elsif ($i eq 'g') { $line .= sprintf '%6d ',   $gid }
+	    elsif ($i eq 's') { $line .= sprintf "$z%16s ",$size }
+	    elsif ($i eq 'l') { $line .= sprintf '%3s ',   $links }
+	    elsif ($i eq 'i') { $line .= sprintf '%14s ',  $inode }
+	    elsif ($i eq 'd') { $line .= sprintf '%10s ',  $date }
+	    elsif ($i eq 'a') { $line .= sprintf '%10s %10s %10s ', 
+	                                 $dates{'a'},$dates{'m'},$dates{'c'} }
+	  } else {
+	    if    ($i eq 'm') { $line .= $mode.' ' }
+	    elsif ($i eq 'u') { $line .= sprintf '%-8s ',  $uid }
+	    elsif ($i eq 'g') { $line .= sprintf '%-8s ',  $gid }
+	    elsif ($i eq 's') { $line .= sprintf "$z%19s ",$size }
+	    elsif ($i eq 'l') { $line .= sprintf '%3s ',   $links }
+	    elsif ($i eq 'i') { $line .= sprintf '%14s ',  $inode }
+	    elsif ($i eq 'd') { $line .= $date.' ' }
+	    elsif ($i eq 'D') { $line .= $date.' ' }
+	    elsif ($i eq 'a') { $line .= &isodate($dates{'a'}).' '.
+	                                 &isodate($dates{'m'}).' '.
+	                                 &isodate($dates{'c'}).' ' }
+	    elsif ($i eq 'A') { $line .= &isodate($dates{'a'}).' '.
+	                                 &isodate($dates{'m'}).' '.
+	                                 &isodate($dates{'c'}).' ' }
+	  }
+	}
+	
+      # predefined formats
+      } else {
+      
+	if ($opt_n) {
+          if ($opt_l) { $line .= sprintf "%06o %6d %6d $z%15s %10d ",
+	                                 $mode,$uid,$gid,$size,$date }
+          else        { $line .= sprintf "%06o $z%15s %10d ",
+	                                 $mode,$size,$date }
+	} else {
+          if ($opt_l) { $line .= sprintf "%s %-8s %-8s $z%19s %s ",
+	                                 $mode,$uid,$gid,$size,$date }
+          else        { $line .= sprintf "%s $z%19s %s ",
+	                                 $mode,$size,substr($date,0,-3) }
+        }
+	
+	if ($opt_i)   { $line .= sprintf '%3s %10s ',$links,$inode }
+      }
+
+      $line .= $linkname."\n";
+      
+      if ($postproc) { 
+        push @LIST,$line;
+      } else { 
+        $line =~ s/\0//;
+        print $line;
+      }
+      $found++;
+      
+    } else {
+      lstat $file;
+      warn "$0: cannot get dir-info for ".quote($file)." - $!\n";
+    }
+    
+  }
+}
+
+# get file information
+#
+# INPUT: file name
+#
+# OUTPUT: filename with linkname, inode, hard link count, size, mode string, 
+#         UID, GID, isodate
+sub info {
+  my $file = shift;
+  my ($linkname,$links,$mode,$bmode,$uid,$gid,$date,%dates,@stat);
+  my $size = '-';
+  my $inode = '?';
+  my @rwx = qw/--- --x -w- -wx r-- r-x rw- rwx/;
+  my $type;
+
+  if ($opt_L) { @stat = stat $file }
+  else        { @stat = lstat $file }
+  
+  if (@stat) {
+  
+    $inode = $stat[1];
+    $bmode = $stat[2];
+    $links = $stat[3];
+    %dates = ('m' => $stat[9], 
+              'a' => $stat[8], 
+	      'c' => $stat[10]);
+
+    if ($opt_n) {
+      $uid  = $stat[4];
+      $gid  = $stat[5];
+      $date = $dates{$sdf};
+    } else {
+      $uid  = getpwuid($stat[4]) || $stat[4];
+      $gid  = getgrgid($stat[5]) || $stat[5];
+      $date = &isodate($dates{$sdf});
+    }
+    
+    if    (-f _)	    { $type = '-'; $size = $stat[7]; }
+    elsif (!$opt_L && -l _) { $type = 'l'; }
+    elsif (-d _)            { $type = 'd'; }
+    elsif (-c _)            { $type = 'c'; $size = &nodes($stat[6]); }
+    elsif (-b _)            { $type = 'b'; $size = &nodes($stat[6]); }
+    elsif (-p _)            { $type = 'p'; }
+    elsif (-S _)            { $type = 's'; }
+    else                    { $type = '?'; }
+
+    if ($opt_n) {
+      $mode = $stat[2];
+      $size = $stat[7] if $size eq '-';
+    } else {
+      if ($opt_l) {
+        $mode = $rwx[$bmode & 7];
+        $bmode >>= 3;
+        $mode = $rwx[$bmode & 7] . $mode;
+        $bmode >>= 3;
+        $mode = $rwx[$bmode & 7] . $mode;
+        substr($mode,2,1) =~ tr/-x/Ss/ if -u _;
+        substr($mode,5,1) =~ tr/-x/Ss/ if -g _;
+        substr($mode,8,1) =~ tr/-x/Tt/ if -k _;
+        $mode = $type.$mode;
+      } else {
+        # with short list display only effektive file access modes
+        $mode = $type 
+	        . (-r _ ? 'R' : '-')
+                . (-w _ ? 'W' : '-')
+                . (-x _ ? 'X' : '-');
+        substr($mode,2,1) =~ tr/-x/Ss/ if -u _ or -g _;
+        substr($mode,3,1) =~ tr/-x/Tt/ if -k _;
+      }
+    }
+
+  # fall back to ls command if perl lstat failed
+  } else {
+    if ($opt_L) {
+      return;
+    } else {
+      ($mode,$links,$uid,$gid,$size) = split /\s+/,`ls -ld $file 2>/dev/null`;
+      return undef unless defined $mode;
+      $type = substr($mode,0,1);
+      # for (my $i=0;$i<3;$i++) { push @dates,'????-??-?? ??:??:??' }
+      # $date = `gfind $dir -maxdepth 1 -name $file -printf '%Ty-%Tm-%Td %TT\n'`;
+    }
+  }
+
+  # summarize statistics
+  if ($opt_S) {
+    $SS++;
+    $SS{$type}++;
+    $Ss += $size if $type eq '-';
+  }
+
+  $size = &d3($size);
+  
+  # determine longest size field
+  if ($opt_z) {
+    my $x = length $size;
+    $opt_z = $x if $x>$opt_z;
+  }
+  $linkname = ${'opt_*'} ? $file : quote($file) ;
+  if ($type eq 'l' and $opt_f !~ /n/) {
+    my $link = readlink($file);
+    if (defined $link) {
+      $linkname .= ' -> ' . (${'opt_*'} ? $link : quote($link));
+    }
+  }
+  $mode =~ s/\+$//;
+  #$mode .= ' ' unless $mode =~ /\+$/;
+  
+  return ($linkname,$inode,$links,$size,$mode,$uid,$gid,$date,%dates);
+}
+
+
+# reformat integer into 3-digit doted format
+# (when non-numerical mode is set)
+#
+# INPUT: integer or '-'
+#
+# OUTPUT: d3-string
+sub d3 {
+  local $_ = shift;
+  if ($opt_n) { s/-/0/ }
+  else        { while (s/(\d)(\d\d\d\b)/$1,$2/) {} }
+  return $_;
+}
+
+
+# get all files matching pattern $opt_m
+#
+# INPUT: directory to scan
+#
+# OUTPUT: files which match (sorted, directories first)
+sub getfiles {
+  my $dir = shift;
+  my @files = ();
+  my @dirs = ();
+  my $f;
+
+  if (opendir D,$dir) {
+    $dir = '' if $dir eq '.';
+    while (defined($f = readdir D)) { 
+    
+      # skip . and .. pseudo-subdirs
+      next if $f =~ m:(^|/)\.\.?/*$:;
+      # skip ONTAP snapshot dir
+      next if $f =~ m:(^|/)\.snapshot/*$:;
+      
+
+      # skip jed and emacs backup files
+      # next if $f =~ /~$/ and not $opt_a and not $opt_l;
+      
+      if ($f =~ /$opt_m/) {
+        my $x = $dir.$f;
+        if (not -l $x and -d $x and not ($opt_R or $postsort or $opt_U)) { 
+          push @dirs,$x;
+        } else { 
+          push @files,$x;
+        }
+      }
+    }
+    closedir D;
+    unless ($postsort) {
+      @files = &$lcsort(@files);
+      @dirs  = &$lcsort(@dirs);
+    }
+  } else {
+    warn "$0: cannot read $dir : $!\n";
+  }
+  
+  return (@dirs,@files);
+}
+
+
+# reformat integer to string node
+#
+# INPUT: integer node
+#
+# OUTPUT: string node
+sub nodes {
+  my $rdev = shift;
+  return sprintf("%03d,%03d", ($rdev >> 8) & 255, $rdev & 255);
+}
+
+
+# reformat timetick to ISO date string
+#
+# INPUT: timetick
+#
+# OUTPUT: ISO date string
+sub isodate {
+  my @d = localtime shift;
+  return sprintf('%d-%02d-%02d %02d:%02d:%02d',
+                 $d[5]+1900,$d[4]+1,$d[3],$d[2],$d[1],$d[0]);
+}
+
+
+# quote file name to printable name and escape shell meta chars
+#
+# INPUT: original file name
+#
+# OUTPUT: printable file name
+sub quote {
+  local $_ = shift;
+  my $mc = '\'\[\]\\\\ `"$?&<>$*()|{};';
+  
+  unless (defined $_) {
+    die "@_";
+    @x = caller;
+    die "@x";
+  }
+  if (s/[\000-\037\200-\237\241-\250]/?/g or /\'/) {
+    s/([$mc])/\\$1/g;
+    s/^~/\\~/;
+# } elsif (/[$mc]/ or -d and /:/) {
+  } elsif (/[$mc]/) {
+    $_ = "'$_'";
+  }
+  return $_;
+}
+
+
+sub fmatch {
+  my $file = shift;
+  my $link = readlink($file)||'';
+
+  return $file if basename($file) =~ /$opt_F/i;
+  return $link if basename($link) =~ /$opt_F/i;
+}
+
+
+sub usage {
+  my $opts = '[-lastcuidnrzLRNS*] [-f format] [-D X:Y]';
+  if ($0 ne 'lf') { 
+    print "usage: $0 $opts [-F regexp] [file...]\n";
+  }
+  $opts =~ s/R//;
+  print "usage: lf $opts regexp [directory...]\n";
+  print <<EOD;
+options: -l  long list
+         -a  list also .* files
+         -s  sort by size
+         -t  sort by time
+         -U  sort by nothing (original i-node order)
+         -c  list status change time instead of modification time
+         -u  list last access time instead of modification time
+         -i  list also inode and hard links numbers
+         -d  do not list contents of diretories
+         -n  numerical output
+         -r  reverse list
+         -z  squeeze size field (slows down output)
+         -L  derefernce symbolic links
+         -R  recursive into subdirs
+         -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 \\)
+	 -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
+EOD
+  exit 2;
+}
+
+sub examples {
+  print <<EOD;
+l *.c            # list files ending with .c
+l -la            # list all files in long format
+l -Rrs           # list files recursive reverse sorted by size 
+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)                                                       
+EOD
+  exit;
+}
diff -Nru fex-20140917/bin/ll fex-20150120/bin/ll
--- fex-20140917/bin/ll	1970-01-01 01:00:00.000000000 +0100
+++ fex-20150120/bin/ll	2014-12-09 12:31:43.000000000 +0100
@@ -0,0 +1,597 @@
+#!/usr/bin/perl -w
+#
+# l / ll / lf / llf -  substitute of the classic ls command
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Copyright: GNU General Public License 
+
+use Cwd qw'abs_path';
+use File::Basename;
+use Getopt::Std;
+
+# the name of the game
+$0 =~ s:.*/::;
+
+$ENV{LC_CTYPE} = 'C';
+
+# unshift @ARGV,split /\s+/,$ENV{'l_opt'} if $ENV{'l_opt'};
+
+@ARGV = grep { chomp } <STDIN> if "@ARGV" eq '-';
+
+# 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 = 0;
+${'opt_*'} = ${'opt_?'} = 0;
+$opt_m = $opt_f = $opt_F = $opt_D = '';
+&usage if !getopts('hdnlLNitcuarsUSRz*?m:f:D:F:') || $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';
+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;
+}
+
+$postsort = $opt_t||$opt_s;
+$postproc = $postsort||$opt_z;
+
+&examples if ${'opt_?'};
+
+# mark for squeeze operation
+$z = $opt_z ? "\0" : '';
+
+# default sorting methode
+if    ($opt_U) { $lcsort = sub { return @_ } }
+elsif ($opt_r) { $lcsort = sub { sort { lc $b cmp lc $a } @_ } }
+else           { $lcsort = sub { sort { lc $a cmp lc $b } @_ } }
+
+# default: list only files not beginning with a dot
+unless ($opt_m) {
+  if ($opt_a) { $opt_m = '.' }
+  else        { $opt_m = '^[^\.]' }
+}
+
+$older = $newer = 0;
+
+if ($opt_D) {
+  if ($opt_D =~ /:(\d+)([mhd])/) {
+    $older = $1;
+    my $z = $2 || 's';
+    if    ($z =~ /m/) { $older *= 60 }
+    elsif ($z =~ /h/) { $older *= 60*60 }
+    elsif ($z =~ /d/) { $older *= 60*60*24 }
+  } elsif ($opt_D =~ /:(\d\d\d\d-\d\d-\d\d)$/) {
+    $older = $1;
+  }
+  if ($opt_D =~ /(\d+)([mhd]):/) {
+    $newer = $1;
+    my $z = $2 || 's';
+    if    ($z =~ /m/) { $newer *= 60 }
+    elsif ($z =~ /h/) { $newer *= 60*60 }
+    elsif ($z =~ /d/) { $newer *= 60*60*24 }
+  } elsif ($opt_D =~ /^(\d\d\d\d-\d\d-\d\d):/) {
+    $newer = $1;
+  }
+}
+  
+# preselect date field number
+if    ($opt_c) { $sdf = 'c' }
+elsif ($opt_u) { $sdf = 'a' }
+else           { $sdf = 'm' }
+
+# any arguments?
+if (@ARGV) { @ARGV = &$lcsort(@ARGV) }
+else       { @ARGV = &getfiles('.') }
+
+# build files list
+&collect(@ARGV);
+
+# post process files list?
+# remark: if no postprocessing, files list has been already printed in list()
+if (@LIST && $postproc) {
+
+  # on -t or -s option sort list on date or size
+  # and then strip of leading sorting pre-string
+  @LIST = grep { s/.{21}// } reverse sort @LIST if $postsort;
+
+  # squeeze size field (= remove unnecessary spaces)
+  if ($opt_z and not $opt_f) {
+    $opt_z = '%'.$opt_z.'s ';
+    @LIST = grep { s/\0 *([,\d\.\-]+) /sprintf($opt_z,$1)/e } @LIST;
+  }
+  
+  @LIST = reverse @LIST if $opt_r;
+
+  if (not ($opt_t or $opt_U) and grep /^d[rR-][wW-][xX-]/,@LIST) {
+    foreach (@LIST) { print if /^d/ }
+    foreach (@LIST) { print unless /^d/ }
+  } else { 
+    print @LIST;
+  }
+}
+
+# print statistics summary?
+if ($opt_S && $SS) {
+  print "$SS file(s):";
+  printf " r=%d (%s Bytes)",$SS{'-'},&d3($Ss) if $SS{'-'};
+  delete $SS{'-'};
+  foreach my $type (qw(l d c b p s ?)) { 
+    printf " %s=%d",$type,$SS{$type} if $SS{$type};
+    delete $SS{$type};
+  }
+  foreach my $type (keys %SS) { printf " %s=%d",$type,$SS{$type} }
+  print "\n";
+}
+
+exit ($found ? 0 : 1);
+
+
+# collect files and build file lists
+# 
+# INPUT: filenames
+#
+# GLOBAL: @LIST
+sub collect {
+  my @files = @_;
+  my $f;
+
+  # loop over all argument files/directories
+  foreach $f (@files) {
+  
+    # skip jed and emacs backup files
+    # next if $f =~ /~$/ and not $opt_a and not $opt_l;
+    
+    # recursive?
+    if ($opt_R) {
+
+      # list single file
+      if ($opt_L) {
+        unless (-e $f) {
+          warn "$0: dangling symlink $f\n";
+          next;
+        }
+        $f = abs_path($f);
+      }
+      list($f);
+
+      # traverse real subdirs
+      if (-d $f and not -l $f) { 
+        $f =~ s:/*$:/:;
+        collect(getfiles($f));
+      }
+      
+    } else {
+  
+      # suppress trailing / on -d option
+      $f =~ s:/$:: if $opt_d;
+    
+      # on trailing / list subdirs, too
+      if ($f =~ m:/$:) { &list(&getfiles($f)) }
+      elsif ($f eq '') { &list('/') }
+      else {
+        if ($opt_L) {
+          unless (-e $f) {
+            warn "$0: dangling symlink $f\n";
+            next;
+          }
+          $f = abs_path($f);
+        }
+        list($f);
+      }
+      
+    }
+  }
+}
+
+
+# list file(s)
+#
+# INPUT: filenames
+#
+# GLOBAL: @LIST (filenames-list)
+sub list {
+  my @files = @_;
+  my ($file,$line,$linkname,$inode,$links,$size,$mode,$uid,$gid,$date,%dates);
+  my ($day);
+
+  foreach $file (@files) {
+
+    next if $opt_F and not fmatch($file);
+    next if $opt_N and (not -f $file or -l $file);
+
+    # get file information
+    # if ($opt_L and stat $file or not $opt_L and lstat $file) {
+    if (lstat $file) {
+      ($linkname,$inode,$links,$size,$mode,$uid,$gid,$date,%dates) = &info($file);
+    } elsif ($! eq "Permission denied") {
+      $linkname = $file;
+      $inode = $links = $size = $uid = $gid = '?';
+      $mode = $opt_l ? '?---------' : '?---';
+      $date = '????-??-?? ??:??:??';
+      %dates = ('m' => 0, 'a' => 0, 'c' => 0);
+    } else {
+      warn "$0: ".quote($file)." - $!\n";
+      next;
+    }
+
+    $day = $date;
+    $day =~ s/\s.*//;
+    
+    if ($older) {
+      next if $older =~ /-/ and $day gt $older;
+      next if $older !~ /-/ and $dates{m} > time-$older;
+    }
+    if ($newer) {
+      next if $newer =~ /-/ and $day lt $newer;
+      next if $newer !~ /-/ and $dates{m} < time-$newer;
+    }
+    
+    if (defined $linkname) {
+
+      # prepend sorting string
+      $line = '';
+      $line = sprintf '%21s',$date if $opt_t;
+      $line = sprintf '%21s',$size if $opt_s;
+
+      unless ($opt_n) {
+        $uid = substr($uid,0,8);
+        $gid = substr($gid,0,8);
+      }
+
+      # user defined format?
+      if ($opt_f) {
+        foreach my $i (split '',$opt_f) {
+	  if ($opt_n) {
+	    $i =~ tr/AD/ad/;
+	    if    ($i eq 'm') { $line .= sprintf '%06o ',  $mode }
+	    elsif ($i eq 'u') { $line .= sprintf '%6d ',   $uid }
+	    elsif ($i eq 'g') { $line .= sprintf '%6d ',   $gid }
+	    elsif ($i eq 's') { $line .= sprintf "$z%16s ",$size }
+	    elsif ($i eq 'l') { $line .= sprintf '%3s ',   $links }
+	    elsif ($i eq 'i') { $line .= sprintf '%14s ',  $inode }
+	    elsif ($i eq 'd') { $line .= sprintf '%10s ',  $date }
+	    elsif ($i eq 'a') { $line .= sprintf '%10s %10s %10s ', 
+	                                 $dates{'a'},$dates{'m'},$dates{'c'} }
+	  } else {
+	    if    ($i eq 'm') { $line .= $mode.' ' }
+	    elsif ($i eq 'u') { $line .= sprintf '%-8s ',  $uid }
+	    elsif ($i eq 'g') { $line .= sprintf '%-8s ',  $gid }
+	    elsif ($i eq 's') { $line .= sprintf "$z%19s ",$size }
+	    elsif ($i eq 'l') { $line .= sprintf '%3s ',   $links }
+	    elsif ($i eq 'i') { $line .= sprintf '%14s ',  $inode }
+	    elsif ($i eq 'd') { $line .= $date.' ' }
+	    elsif ($i eq 'D') { $line .= $date.' ' }
+	    elsif ($i eq 'a') { $line .= &isodate($dates{'a'}).' '.
+	                                 &isodate($dates{'m'}).' '.
+	                                 &isodate($dates{'c'}).' ' }
+	    elsif ($i eq 'A') { $line .= &isodate($dates{'a'}).' '.
+	                                 &isodate($dates{'m'}).' '.
+	                                 &isodate($dates{'c'}).' ' }
+	  }
+	}
+	
+      # predefined formats
+      } else {
+      
+	if ($opt_n) {
+          if ($opt_l) { $line .= sprintf "%06o %6d %6d $z%15s %10d ",
+	                                 $mode,$uid,$gid,$size,$date }
+          else        { $line .= sprintf "%06o $z%15s %10d ",
+	                                 $mode,$size,$date }
+	} else {
+          if ($opt_l) { $line .= sprintf "%s %-8s %-8s $z%19s %s ",
+	                                 $mode,$uid,$gid,$size,$date }
+          else        { $line .= sprintf "%s $z%19s %s ",
+	                                 $mode,$size,substr($date,0,-3) }
+        }
+	
+	if ($opt_i)   { $line .= sprintf '%3s %10s ',$links,$inode }
+      }
+
+      $line .= $linkname."\n";
+      
+      if ($postproc) { 
+        push @LIST,$line;
+      } else { 
+        $line =~ s/\0//;
+        print $line;
+      }
+      $found++;
+      
+    } else {
+      lstat $file;
+      warn "$0: cannot get dir-info for ".quote($file)." - $!\n";
+    }
+    
+  }
+}
+
+# get file information
+#
+# INPUT: file name
+#
+# OUTPUT: filename with linkname, inode, hard link count, size, mode string, 
+#         UID, GID, isodate
+sub info {
+  my $file = shift;
+  my ($linkname,$links,$mode,$bmode,$uid,$gid,$date,%dates,@stat);
+  my $size = '-';
+  my $inode = '?';
+  my @rwx = qw/--- --x -w- -wx r-- r-x rw- rwx/;
+  my $type;
+
+  if ($opt_L) { @stat = stat $file }
+  else        { @stat = lstat $file }
+  
+  if (@stat) {
+  
+    $inode = $stat[1];
+    $bmode = $stat[2];
+    $links = $stat[3];
+    %dates = ('m' => $stat[9], 
+              'a' => $stat[8], 
+	      'c' => $stat[10]);
+
+    if ($opt_n) {
+      $uid  = $stat[4];
+      $gid  = $stat[5];
+      $date = $dates{$sdf};
+    } else {
+      $uid  = getpwuid($stat[4]) || $stat[4];
+      $gid  = getgrgid($stat[5]) || $stat[5];
+      $date = &isodate($dates{$sdf});
+    }
+    
+    if    (-f _)	    { $type = '-'; $size = $stat[7]; }
+    elsif (!$opt_L && -l _) { $type = 'l'; }
+    elsif (-d _)            { $type = 'd'; }
+    elsif (-c _)            { $type = 'c'; $size = &nodes($stat[6]); }
+    elsif (-b _)            { $type = 'b'; $size = &nodes($stat[6]); }
+    elsif (-p _)            { $type = 'p'; }
+    elsif (-S _)            { $type = 's'; }
+    else                    { $type = '?'; }
+
+    if ($opt_n) {
+      $mode = $stat[2];
+      $size = $stat[7] if $size eq '-';
+    } else {
+      if ($opt_l) {
+        $mode = $rwx[$bmode & 7];
+        $bmode >>= 3;
+        $mode = $rwx[$bmode & 7] . $mode;
+        $bmode >>= 3;
+        $mode = $rwx[$bmode & 7] . $mode;
+        substr($mode,2,1) =~ tr/-x/Ss/ if -u _;
+        substr($mode,5,1) =~ tr/-x/Ss/ if -g _;
+        substr($mode,8,1) =~ tr/-x/Tt/ if -k _;
+        $mode = $type.$mode;
+      } else {
+        # with short list display only effektive file access modes
+        $mode = $type 
+	        . (-r _ ? 'R' : '-')
+                . (-w _ ? 'W' : '-')
+                . (-x _ ? 'X' : '-');
+        substr($mode,2,1) =~ tr/-x/Ss/ if -u _ or -g _;
+        substr($mode,3,1) =~ tr/-x/Tt/ if -k _;
+      }
+    }
+
+  # fall back to ls command if perl lstat failed
+  } else {
+    if ($opt_L) {
+      return;
+    } else {
+      ($mode,$links,$uid,$gid,$size) = split /\s+/,`ls -ld $file 2>/dev/null`;
+      return undef unless defined $mode;
+      $type = substr($mode,0,1);
+      # for (my $i=0;$i<3;$i++) { push @dates,'????-??-?? ??:??:??' }
+      # $date = `gfind $dir -maxdepth 1 -name $file -printf '%Ty-%Tm-%Td %TT\n'`;
+    }
+  }
+
+  # summarize statistics
+  if ($opt_S) {
+    $SS++;
+    $SS{$type}++;
+    $Ss += $size if $type eq '-';
+  }
+
+  $size = &d3($size);
+  
+  # determine longest size field
+  if ($opt_z) {
+    my $x = length $size;
+    $opt_z = $x if $x>$opt_z;
+  }
+  $linkname = ${'opt_*'} ? $file : quote($file) ;
+  if ($type eq 'l' and $opt_f !~ /n/) {
+    my $link = readlink($file);
+    if (defined $link) {
+      $linkname .= ' -> ' . (${'opt_*'} ? $link : quote($link));
+    }
+  }
+  $mode =~ s/\+$//;
+  #$mode .= ' ' unless $mode =~ /\+$/;
+  
+  return ($linkname,$inode,$links,$size,$mode,$uid,$gid,$date,%dates);
+}
+
+
+# reformat integer into 3-digit doted format
+# (when non-numerical mode is set)
+#
+# INPUT: integer or '-'
+#
+# OUTPUT: d3-string
+sub d3 {
+  local $_ = shift;
+  if ($opt_n) { s/-/0/ }
+  else        { while (s/(\d)(\d\d\d\b)/$1,$2/) {} }
+  return $_;
+}
+
+
+# get all files matching pattern $opt_m
+#
+# INPUT: directory to scan
+#
+# OUTPUT: files which match (sorted, directories first)
+sub getfiles {
+  my $dir = shift;
+  my @files = ();
+  my @dirs = ();
+  my $f;
+
+  if (opendir D,$dir) {
+    $dir = '' if $dir eq '.';
+    while (defined($f = readdir D)) { 
+    
+      # skip . and .. pseudo-subdirs
+      next if $f =~ m:(^|/)\.\.?/*$:;
+      # skip ONTAP snapshot dir
+      next if $f =~ m:(^|/)\.snapshot/*$:;
+      
+
+      # skip jed and emacs backup files
+      # next if $f =~ /~$/ and not $opt_a and not $opt_l;
+      
+      if ($f =~ /$opt_m/) {
+        my $x = $dir.$f;
+        if (not -l $x and -d $x and not ($opt_R or $postsort or $opt_U)) { 
+          push @dirs,$x;
+        } else { 
+          push @files,$x;
+        }
+      }
+    }
+    closedir D;
+    unless ($postsort) {
+      @files = &$lcsort(@files);
+      @dirs  = &$lcsort(@dirs);
+    }
+  } else {
+    warn "$0: cannot read $dir : $!\n";
+  }
+  
+  return (@dirs,@files);
+}
+
+
+# reformat integer to string node
+#
+# INPUT: integer node
+#
+# OUTPUT: string node
+sub nodes {
+  my $rdev = shift;
+  return sprintf("%03d,%03d", ($rdev >> 8) & 255, $rdev & 255);
+}
+
+
+# reformat timetick to ISO date string
+#
+# INPUT: timetick
+#
+# OUTPUT: ISO date string
+sub isodate {
+  my @d = localtime shift;
+  return sprintf('%d-%02d-%02d %02d:%02d:%02d',
+                 $d[5]+1900,$d[4]+1,$d[3],$d[2],$d[1],$d[0]);
+}
+
+
+# quote file name to printable name and escape shell meta chars
+#
+# INPUT: original file name
+#
+# OUTPUT: printable file name
+sub quote {
+  local $_ = shift;
+  my $mc = '\'\[\]\\\\ `"$?&<>$*()|{};';
+  
+  unless (defined $_) {
+    die "@_";
+    @x = caller;
+    die "@x";
+  }
+  if (s/[\000-\037\200-\237\241-\250]/?/g or /\'/) {
+    s/([$mc])/\\$1/g;
+    s/^~/\\~/;
+# } elsif (/[$mc]/ or -d and /:/) {
+  } elsif (/[$mc]/) {
+    $_ = "'$_'";
+  }
+  return $_;
+}
+
+
+sub fmatch {
+  my $file = shift;
+  my $link = readlink($file)||'';
+
+  return $file if basename($file) =~ /$opt_F/i;
+  return $link if basename($link) =~ /$opt_F/i;
+}
+
+
+sub usage {
+  my $opts = '[-lastcuidnrzLRNS*] [-f format] [-D X:Y]';
+  if ($0 ne 'lf') { 
+    print "usage: $0 $opts [-F regexp] [file...]\n";
+  }
+  $opts =~ s/R//;
+  print "usage: lf $opts regexp [directory...]\n";
+  print <<EOD;
+options: -l  long list
+         -a  list also .* files
+         -s  sort by size
+         -t  sort by time
+         -U  sort by nothing (original i-node order)
+         -c  list status change time instead of modification time
+         -u  list last access time instead of modification time
+         -i  list also inode and hard links numbers
+         -d  do not list contents of diretories
+         -n  numerical output
+         -r  reverse list
+         -z  squeeze size field (slows down output)
+         -L  derefernce symbolic links
+         -R  recursive into subdirs
+         -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 \\)
+	 -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
+EOD
+  exit 2;
+}
+
+sub examples {
+  print <<EOD;
+l *.c            # list files ending with .c
+l -la            # list all files in long format
+l -Rrs           # list files recursive reverse sorted by size 
+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)                                                       
+EOD
+  exit;
+}
diff -Nru fex-20140917/bin/logwatch fex-20150120/bin/logwatch
--- fex-20140917/bin/logwatch	2014-09-12 17:00:29.000000000 +0200
+++ fex-20150120/bin/logwatch	2014-12-12 17:15:45.000000000 +0100
@@ -35,13 +35,15 @@
   GET.*(favicon|robots\.txt)
   GET./organization\.gif
   GET./small_logo\.jpg
+  GET./logo\.jpg
+  GET./action-fex-camel\.gif
+  GET./fup\?showstatus
   GET./FAQ/faq\.css
   GET./FAQ/jquery\.js
   GET./10+.B
   GET.*Arrow\.gif
   GET./apple-touch
-  User-Agent:.*(Webnote|FeedFetcher|\w+bot|bot/|Website.Watcher|crawler|spider|searchme|Yandex|Slurp|ScoutJet|findlinks|urlmon)
-  User-Agent:.ichiro
+  User-Agent:.*(Webnote|FeedFetcher|\w+bot|bot/|Website.Watcher|crawler|spider|searchme|Yandex|Slurp|ScoutJet|findlinks|urlmon|nagios)
   User-Agent:.fnb.*quak
   From:.*(msnbot|yandex|googlebot|webcrawler)
   Referer:.*sex.*stream
@@ -69,6 +71,7 @@
   http\.
   NOKIA_
   GPRS
+  X-Proxy-ID
   X-Moz
   X.Wap
   X-FH
@@ -97,7 +100,7 @@
 } else {
   *L = *STDIN;
 }
-binmode(L,":encoding(UTF-8)");
+# binmode(L,":encoding(UTF-8)");
 
 for (;;) {
   while (<L>) {
@@ -116,7 +119,11 @@
     }
     s/[\s\n]*$/\n\n/;
     print or exit;
-    if (m:\nGET /fop/(\w+)/:) {
+    $from = '';
+    if (m:\nGET /fup/(\w{40,}):) {
+      $_ = decode_b64($1);
+      printf "  FROM=\"%s\"\n\n",$1 if /from=([\w\@.-]+)/;
+    } elsif (m:\nGET /fop/(\w+)/:) {
       $dkey = $1;
       my $ddir = "$spooldir/.dkeys/$dkey";
       $_ = readlink $ddir or next;
@@ -125,21 +132,23 @@
       printf "  TO=\"%s\"\n",$to;
       $cgi = '';
       if ($comment = slurp("$ddir/comment")) {
-        printf "  COMMENT=\"%s\"\n",decode_utf8($comment)||'';
+        printf "  COMMENT=\"%s\"\n",decode_utf8($comment,0)||'';
       }
       if (not -f "$ddir/data" and $_ = slurp("$ddir/error")) {
         s/\n.*//s;
         print "  ERROR=\"$_\"\n";
       }
-      elsif ($size = readlink("$ddir/size")) {
+      elsif ($size = -s "$ddir/data") {
         printf "  SIZE=%s MB\n",int($size/1024/1024);
       }
-      print "\n" if $_;
+      print "\n";
+    } elsif (m:\nGET /fup.*skey=(\w+):) {
+      read_skey($1);
+      print "\n";
     }
   }
   sleep 1;
   if ($debug and $pid and $cgi) {
-    sleep 1;
     &read_debug_log;
     $pid = $cgi = '';
   };
@@ -149,28 +158,54 @@
 sub read_debug_log {
   my (@log,$log);
   local $/ = "\n";
+  local $_;
+  local $^W;
   # no warnings "all";
-  
-  if (@log = glob "$logdir/.debug/*_$pid.$cgi") {
-    $log = pop @log;
-    if (open $log,$log) {
-      binmode($log,":encoding(UTF-8)");
+
+  for (1..2) {
+    sleep 1;
+    @log = `ls -rt $logdir/.debug/*_${pid}.$cgi 2>/dev/null`;
+    if ($log = $log[-1] and open $log,$log) {
+      # binmode($log,":encoding(UTF-8)");
       while (<$log>) {
         s/\r//;
         if (/^Content-Disposition:.*name="FILE".*filename="(.+)"/i) {
           print "  FILE=\"$1\"\n";
         } elsif (/^Content-Disposition:.*name="(\w+)"/i) {
-          $p = $1;
+          my $p = uc($1);
           $_ = <$log>;
-          $v = <$log>;
+          my $v = <$log>||'';
           $v =~ s/[\r\n]+//;
-          printf "  %s=\"%s\"\n",$p,decode_utf8($v) if $v;
+          printf "  %s=\"%s\"\n",$p,decode_utf8($v,0)||$v if $v;
+          read_akey($v) if $p eq 'AKEY';
+          read_skey($v) if $p eq 'SKEY';
         } elsif (/^(Param|Exp): (\w+=".+")/) {
           print "  $2\n";
         }
       }
       close $log;
       print "\n";
+      return;
+    }
+  }
+}
+
+sub read_akey {
+  my $akey = "$spooldir/.akeys/" . shift;
+  if (my $user = readlink($akey)) {
+    $user =~ s:../::;
+    printf "  USER=\"%s\"\n",$user;
+  }
+}
+
+
+sub read_skey {
+  my $skey = "$spooldir/.skeys/" . shift;
+  if (open $skey,$skey) {
+    while (<$skey>) {
+      printf "  FROM=\"%s\"\n",$1 if /from=(.+)/;
+      printf "  TO=\"%s\"\n",$1   if /to=(.+)/;
     }
+    close $skey;
   }
 }
diff -Nru fex-20140917/bin/sexget fex-20150120/bin/sexget
--- fex-20140917/bin/sexget	2014-07-26 16:43:02.000000000 +0200
+++ fex-20150120/bin/sexget	2015-01-19 13:59:57.000000000 +0100
@@ -4,7 +4,7 @@
 #
 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
 #
-# Copyright: Perl Artistic
+# Perl Artistic Licence
 
 # sexsend / sexget / sexxx
 
@@ -19,9 +19,14 @@
 
 eval 'use Net::INET6Glue::INET_is_INET6';
 
-our $version = 20140917;
+our $version = 20150120;
 
-$version = mtime($0) unless $version;
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
 
 $0 =~ s:.*/::;
 $| = 1;
@@ -30,6 +35,7 @@
 $usage = 
   "usage: ... | $0 [options] [SEX-URL/]recipient [stream]\n".
   "options: -v           verbose mode\n".
+  "         -g           show transfer rate\n".
   "         -V           show version\n".
   "         -t timeout   timeout in s (waiting for recipient)\n".
   "special: recipient may be \"public\" or \"anonymous\" or \".\"\n".
@@ -39,8 +45,8 @@
 if ($0 eq 'sexget' or $0 eq 'fuckme') {
   $usage = 
     "usage: $0 [options] [[SEX-URL/]user:ID] [stream]\n".
-    "options: -g           show transfer rate\n".
-    "         -v           verbose mode\n".
+    "options: -v           verbose mode\n".
+    "         -g           show transfer rate\n".
     "         -V           show version\n".
     "arguments: user:ID    use this user & ID\n".
     "                      (ID may be \"public\" or user:ID may be \"anonymous\")\n".
@@ -53,8 +59,8 @@
   $usage = 
     "usage: $0 [-v] [-g] [-c] [-u [SEX-URL/]user] [-s stream] [files...]\n".
     "usage: $0 [-v] [-g]      [-u [SEX-URL/]user] [-s stream] | ...\n".
-    "options: -g               show transfer rate\n".
-    "         -v               verbose mode\n".
+    "options: -v               verbose mode\n".
+    "         -g               show transfer rate\n".
     "         -q               quiet mode\n".
     "         -c               compress files\n".
     "         -u SEX-URL/user  SEX-URL and user (default: use FEXID/FEXXX)\n".
@@ -93,6 +99,8 @@
 $opt_h = $opt_v = $opt_V = $opt_q = 0;
 $opt_u = $opt_s = $opt_c = $opt_t = '';
 
+$_ = "$fexhome/config.pl"; require if -f;
+
 if ($0 eq 'sexxx') {
   
   # xx server URL, user and auth-ID
@@ -206,6 +214,8 @@
   
 }
 
+&get_ssl_env;
+
 $fexcgi =~ s(^http://)()i;
 $fexcgi =~ s(/fup.*)();
 $server = $fexcgi;
@@ -223,12 +233,18 @@
 # connect(SH,sockaddr_in($port,$iaddr)) or die "$0: connect $!\n";
 # warn "connecting $server:$port user=$user\n";
 if ($port == 443) {
+  if ($opt_v and %SSL) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
   eval "use IO::Socket::SSL";
   die "$0: cannot load IO::Socket::SSL\n" if $@;
   $SH = IO::Socket::SSL->new(                                                  
     PeerAddr => $server,                                                       
     PeerPort => $port,                                                         
-    Proto    => 'tcp',                                                         
+    Proto    => 'tcp',
+    %SSL
   );                                                                           
 } else {                                                                       
   $SH = IO::Socket::INET->new(
@@ -499,11 +515,6 @@
   return $line;
 }
 
-sub mtime {
-  my @d = localtime((stat shift)[9]);
-  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
-}
-
 # from MIME::Base64::Perl
 sub decode_b64 {
   local $_ = shift;
@@ -527,12 +538,182 @@
 }
 
 
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
+sub urldecode {
+  local $_ = shift;
+  s/\%([a-f\d]{2})/chr(hex($1))/ige;
+  return $_;
+}
+
+
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  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) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
 sub encode_b64 {
   my $res = "";
-  my $eol = $_[1];
+  my $eol = "\n";
   my $padding;
   
-  $eol = "\n" unless defined $eol;
   pos($_[0]) = 0;
   $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
   $res =~ tr|` -_|AA-Za-z0-9+/|;
diff -Nru fex-20140917/bin/sexsend fex-20150120/bin/sexsend
--- fex-20140917/bin/sexsend	2014-07-26 16:43:02.000000000 +0200
+++ fex-20150120/bin/sexsend	2015-01-19 13:59:57.000000000 +0100
@@ -4,7 +4,7 @@
 #
 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
 #
-# Copyright: Perl Artistic
+# Perl Artistic Licence
 
 # sexsend / sexget / sexxx
 
@@ -19,9 +19,14 @@
 
 eval 'use Net::INET6Glue::INET_is_INET6';
 
-our $version = 20140917;
+our $version = 20150120;
 
-$version = mtime($0) unless $version;
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
 
 $0 =~ s:.*/::;
 $| = 1;
@@ -30,6 +35,7 @@
 $usage = 
   "usage: ... | $0 [options] [SEX-URL/]recipient [stream]\n".
   "options: -v           verbose mode\n".
+  "         -g           show transfer rate\n".
   "         -V           show version\n".
   "         -t timeout   timeout in s (waiting for recipient)\n".
   "special: recipient may be \"public\" or \"anonymous\" or \".\"\n".
@@ -39,8 +45,8 @@
 if ($0 eq 'sexget' or $0 eq 'fuckme') {
   $usage = 
     "usage: $0 [options] [[SEX-URL/]user:ID] [stream]\n".
-    "options: -g           show transfer rate\n".
-    "         -v           verbose mode\n".
+    "options: -v           verbose mode\n".
+    "         -g           show transfer rate\n".
     "         -V           show version\n".
     "arguments: user:ID    use this user & ID\n".
     "                      (ID may be \"public\" or user:ID may be \"anonymous\")\n".
@@ -53,8 +59,8 @@
   $usage = 
     "usage: $0 [-v] [-g] [-c] [-u [SEX-URL/]user] [-s stream] [files...]\n".
     "usage: $0 [-v] [-g]      [-u [SEX-URL/]user] [-s stream] | ...\n".
-    "options: -g               show transfer rate\n".
-    "         -v               verbose mode\n".
+    "options: -v               verbose mode\n".
+    "         -g               show transfer rate\n".
     "         -q               quiet mode\n".
     "         -c               compress files\n".
     "         -u SEX-URL/user  SEX-URL and user (default: use FEXID/FEXXX)\n".
@@ -93,6 +99,8 @@
 $opt_h = $opt_v = $opt_V = $opt_q = 0;
 $opt_u = $opt_s = $opt_c = $opt_t = '';
 
+$_ = "$fexhome/config.pl"; require if -f;
+
 if ($0 eq 'sexxx') {
   
   # xx server URL, user and auth-ID
@@ -206,6 +214,8 @@
   
 }
 
+&get_ssl_env;
+
 $fexcgi =~ s(^http://)()i;
 $fexcgi =~ s(/fup.*)();
 $server = $fexcgi;
@@ -223,12 +233,18 @@
 # connect(SH,sockaddr_in($port,$iaddr)) or die "$0: connect $!\n";
 # warn "connecting $server:$port user=$user\n";
 if ($port == 443) {
+  if ($opt_v and %SSL) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
   eval "use IO::Socket::SSL";
   die "$0: cannot load IO::Socket::SSL\n" if $@;
   $SH = IO::Socket::SSL->new(                                                  
     PeerAddr => $server,                                                       
     PeerPort => $port,                                                         
-    Proto    => 'tcp',                                                         
+    Proto    => 'tcp',
+    %SSL
   );                                                                           
 } else {                                                                       
   $SH = IO::Socket::INET->new(
@@ -499,11 +515,6 @@
   return $line;
 }
 
-sub mtime {
-  my @d = localtime((stat shift)[9]);
-  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
-}
-
 # from MIME::Base64::Perl
 sub decode_b64 {
   local $_ = shift;
@@ -527,12 +538,182 @@
 }
 
 
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
+sub urldecode {
+  local $_ = shift;
+  s/\%([a-f\d]{2})/chr(hex($1))/ige;
+  return $_;
+}
+
+
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  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) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
 sub encode_b64 {
   my $res = "";
-  my $eol = $_[1];
+  my $eol = "\n";
   my $padding;
   
-  $eol = "\n" unless defined $eol;
   pos($_[0]) = 0;
   $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
   $res =~ tr|` -_|AA-Za-z0-9+/|;
diff -Nru fex-20140917/bin/sexxx fex-20150120/bin/sexxx
--- fex-20140917/bin/sexxx	2014-07-26 16:43:02.000000000 +0200
+++ fex-20150120/bin/sexxx	2015-01-19 13:59:57.000000000 +0100
@@ -4,7 +4,7 @@
 #
 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
 #
-# Copyright: Perl Artistic
+# Perl Artistic Licence
 
 # sexsend / sexget / sexxx
 
@@ -19,9 +19,14 @@
 
 eval 'use Net::INET6Glue::INET_is_INET6';
 
-our $version = 20140917;
+our $version = 20150120;
 
-$version = mtime($0) unless $version;
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
 
 $0 =~ s:.*/::;
 $| = 1;
@@ -30,6 +35,7 @@
 $usage = 
   "usage: ... | $0 [options] [SEX-URL/]recipient [stream]\n".
   "options: -v           verbose mode\n".
+  "         -g           show transfer rate\n".
   "         -V           show version\n".
   "         -t timeout   timeout in s (waiting for recipient)\n".
   "special: recipient may be \"public\" or \"anonymous\" or \".\"\n".
@@ -39,8 +45,8 @@
 if ($0 eq 'sexget' or $0 eq 'fuckme') {
   $usage = 
     "usage: $0 [options] [[SEX-URL/]user:ID] [stream]\n".
-    "options: -g           show transfer rate\n".
-    "         -v           verbose mode\n".
+    "options: -v           verbose mode\n".
+    "         -g           show transfer rate\n".
     "         -V           show version\n".
     "arguments: user:ID    use this user & ID\n".
     "                      (ID may be \"public\" or user:ID may be \"anonymous\")\n".
@@ -53,8 +59,8 @@
   $usage = 
     "usage: $0 [-v] [-g] [-c] [-u [SEX-URL/]user] [-s stream] [files...]\n".
     "usage: $0 [-v] [-g]      [-u [SEX-URL/]user] [-s stream] | ...\n".
-    "options: -g               show transfer rate\n".
-    "         -v               verbose mode\n".
+    "options: -v               verbose mode\n".
+    "         -g               show transfer rate\n".
     "         -q               quiet mode\n".
     "         -c               compress files\n".
     "         -u SEX-URL/user  SEX-URL and user (default: use FEXID/FEXXX)\n".
@@ -93,6 +99,8 @@
 $opt_h = $opt_v = $opt_V = $opt_q = 0;
 $opt_u = $opt_s = $opt_c = $opt_t = '';
 
+$_ = "$fexhome/config.pl"; require if -f;
+
 if ($0 eq 'sexxx') {
   
   # xx server URL, user and auth-ID
@@ -206,6 +214,8 @@
   
 }
 
+&get_ssl_env;
+
 $fexcgi =~ s(^http://)()i;
 $fexcgi =~ s(/fup.*)();
 $server = $fexcgi;
@@ -223,12 +233,18 @@
 # connect(SH,sockaddr_in($port,$iaddr)) or die "$0: connect $!\n";
 # warn "connecting $server:$port user=$user\n";
 if ($port == 443) {
+  if ($opt_v and %SSL) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
   eval "use IO::Socket::SSL";
   die "$0: cannot load IO::Socket::SSL\n" if $@;
   $SH = IO::Socket::SSL->new(                                                  
     PeerAddr => $server,                                                       
     PeerPort => $port,                                                         
-    Proto    => 'tcp',                                                         
+    Proto    => 'tcp',
+    %SSL
   );                                                                           
 } else {                                                                       
   $SH = IO::Socket::INET->new(
@@ -499,11 +515,6 @@
   return $line;
 }
 
-sub mtime {
-  my @d = localtime((stat shift)[9]);
-  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
-}
-
 # from MIME::Base64::Perl
 sub decode_b64 {
   local $_ = shift;
@@ -527,12 +538,182 @@
 }
 
 
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
+sub urldecode {
+  local $_ = shift;
+  s/\%([a-f\d]{2})/chr(hex($1))/ige;
+  return $_;
+}
+
+
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  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) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
 sub encode_b64 {
   my $res = "";
-  my $eol = $_[1];
+  my $eol = "\n";
   my $padding;
   
-  $eol = "\n" unless defined $eol;
   pos($_[0]) = 0;
   $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
   $res =~ tr|` -_|AA-Za-z0-9+/|;
diff -Nru fex-20140917/bin/xx fex-20150120/bin/xx
--- fex-20140917/bin/xx	2014-09-05 09:03:47.000000000 +0200
+++ fex-20150120/bin/xx	2015-01-16 15:52:53.000000000 +0100
@@ -6,7 +6,7 @@
 #
 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
 #
-# Copyright: Perl Artistic
+# Perl Artistic Licence
 
 use 5.006;
 use strict qw'vars subs';
@@ -27,17 +27,23 @@
 
 eval 'use Net::INET6Glue::INET_is_INET6';
 
-our ($SH,$fexhome,$idf,$tmpdir,$windoof,$sigpipe,$useragent,$editor,$nomail);
+&update if "@ARGV" eq 'UPDATE';
+
+$| = 1;
+
+our ($SH,$fexhome,$idf,$tmpdir,$windoof,$useragent,$editor,$nomail);
 our ($anonymous,$public);
-our ($tpid);
+our ($tpid,$frecipient);
 our ($FEXID,$FEXXX,$HOME);
+our (%alias);
 our $chunksize = 0;
-our $version = 20140917;
+our $version = 20150120;
 our $_0 = $0;
 our $DEBUG;
-  
-$version ||= mtime($0);
-  
+
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
 if ($Config{osname} =~ /^mswin/i) {
   $windoof = $Config{osname};
   $HOME = $ENV{USERPROFILE};
@@ -47,6 +53,7 @@
   $editor = $ENV{EDITOR} || 'notepad.exe';
   $useragent = sprintf("fexsend-$version (%s %s)",
                        $Config{osname},$Config{archname});
+  $SSL{SSL_verify_mode} = 0;
 } else {
   $0 =~ s:.*/::;
   $HOME = (getpwuid($<))[7]||$ENV{HOME};
@@ -61,7 +68,9 @@
   chmod 0600,$idf;
 }
 
-$| = 1;
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
 
 my $from = '';
 my $to = '';
@@ -79,7 +88,6 @@
 my $timeout = 30; 	# server timeout
 my $fexlist = "$tmpdir/fexlist";
 my ($usage,$hints);
-
 my $xx = $0 =~ /^xx/;
 
 if ($xx) {
@@ -97,7 +105,7 @@
 usage: $0 [options] file(s) [@] recipient(s)
    or: $0 [special options]
    or: $0 -f \# recipient(s)
-   or: $0 -x \# [-C -k -D -K]
+   or: $0 -x \# [-C -k -D -K -S]
 options: -v           verbose mode
          -d           delete file on fex server
          -c           compress file
@@ -114,13 +122,14 @@
          -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)
+                 -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)
-                 -U      show authorized URL
+                 -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)
@@ -132,7 +141,7 @@
 #         -R FEX mail  self-register your e-mail address at FEX server
 
   $hints = <<EOD;
-$0 Hints and more options:
+$0 hints and more options:
   
 usage: $0 [options] file recipient(s)
 
@@ -167,7 +176,7 @@
 If you want a Bcc of the notification e-mail then add '!bcc!' to the comment:
 fexsend -C '!bcc! for me and you' ...
 
-Special options:
+Additional special options:
 
   -. sends a short instead of a detailed notification e-mail
   -/ does not upload the file, but tells the server to link it
@@ -177,6 +186,7 @@
   -q is quiet mode
   -r ADDRESS sets e-mail Reply-To ADDRESS
   -F activates female mode
+  -U show authorized URL
   -+ is an undocumented feature - test it :-)
     
 To manage your subuser and groups or forward or redirect files, use a 
@@ -204,6 +214,13 @@
 without wasting local disc space.
  
 With option -X you can specify any parameter, e.g.: -X autodelete=yes
+
+For HTTPS you can set the environment variables:
+SSLVERIFY=1                 # activate server identity verification
+SSLVERSION=TLSv1            # this is the default
+SSLCAPATH=/etc/ssl/certs    # path to trusted (root) certificates
+SSLCAFILE=/etc/ssl/cert.pem # file with trusted (root) certificates
+SSLCIPHERLIST=HIGH:!3DES    # see http://www.openssl.org/docs/apps/ciphers.html
   
 Partner program xx is an internet clipboard. See: xx -h
   
@@ -218,7 +235,27 @@
   FEXID="FEXSERVER USER AUTHID" $0 file recipient
 Example:
   FEXID="fex.flupp.org gaga\@flupp.org blubb" $0 big.file framstag\@rus.uni-stuttgart.de
-    
+
+You can define aliases (and optional fexsend options) in \$HOME/.fex/config.pl:
+  %alias = (
+    'alias1' => 'user1\@domain1.org',
+    'alias2' => 'user2\@domain2.org',
+    'both'   => 'user1\@domain1.org,user2\@domain2.org',
+    'extra'  => 'extra\@special.net:-i other -K -k 30',
+  );
+
+fexsend also respects aliases in $HOME/.mutt/aliases
+The alias priority is (descending):
+\$HOME/.fex/config.pl
+\$HOME/.mutt/aliases 
+fexserver address book  
+
+In \$HOME/.fex/config.pl you can also set the SSL* environment variables and the
+\$opt_* variables, e.g.:
+  
+\$ENV{SSLVERSION} = 'TLSv1';
+\${'opt_+'} = 1;
+\$opt_m = 200;
 EOD
 }
 
@@ -255,22 +292,25 @@
 our ($opt_q,$opt_h,$opt_H,$opt_v,$opt_m,$opt_c,$opt_k,$opt_d,$opt_l,$opt_I,
      $opt_K,$opt_D,$opt_u,$opt_f,$opt_a,$opt_C,$opt_R,$opt_M,$opt_L,$opt_Q,
      $opt_A,$opt_i,$opt_z,$opt_Z,$opt_b,$opt_P,$opt_x,$opt_X,$opt_V,$opt_U,
-     $opt_s,$opt_o,$opt_g,$opt_F,$opt_n,$opt_r);
+     $opt_s,$opt_o,$opt_g,$opt_F,$opt_n,$opt_r,$opt_S,$opt_N);
 
 if ($xx) {
   $opt_q = 1 if @ARGV and $ARGV[-1] eq '--' and pop @ARGV or not -t STDOUT;
   $opt_h = $opt_v = $opt_m = $opt_I = 0;
   $opt_X = '';
+  $_ = "$fexhome/config.pl"; require if -f;
   getopts('hvIm:') or die $usage;
 } else {
   $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;
+  $opt_S = $opt_N = 0;
   ${'opt_@'} = ${'opt_!'} = ${'opt_+'} = ${'opt_.'} = ${'opt_/'} = 0;
   ${'opt_='} = ${'opt_#'} = '';
   $opt_u = $opt_f = $opt_a = $opt_C = $opt_i = $opt_b = $opt_P = $opt_X = '';
   $opt_s = $opt_r = '';
-  getopts('hHvcdognVDKlILUARWMFzZqQ@!+./r:m:k:u:f:a:s:C:i:b:P:x:X:=:#:') 
+  $_ = "$fexhome/config.pl"; require if -f;
+  getopts('hHvcdognVDKlILUARWMFzZqQS@!+./r:m:k:u:f:a:s:C:i:b:P:x:X:N:=:#:') 
     or die $usage;
 
   if ($opt_H) {
@@ -307,16 +347,12 @@
     die "$0: you cannot use both options -I and -R\n";
   }
 
-  if ($opt_R) {
-    &register;
-    exit;
-  }
-
   # $opt_C is COMMENT command in F*EX protocol
   $opt_C =    
     ($opt_d)		? 'DELETE':
     ($opt_l or $opt_L)	? 'LIST':
     ($opt_Q)		? 'CHECKQUOTA':
+    ($opt_S)		? 'LISTSETTINGS':
     ($opt_Z)		? 'RECEIVEDLOG':
     ($opt_z)		? 'SENDLOG':
     (${'opt_!'})	? 'FOPLOG':
@@ -328,12 +364,21 @@
   $opt_D;
 }
 
+&get_ssl_env;
+
 if ($opt_h) {
   female_mode("show help?") if $opt_F;
   print $usage;
   exit;
 }
 
+
+if ($opt_R) {
+  &register;
+  exit;
+}
+
+
 die $usage if $opt_m and $opt_m !~ /^\d+/;
 
 if ($opt_P) { 
@@ -492,7 +537,7 @@
 
 &inquire if $windoof and not @ARGV and not
             ($opt_l or $opt_L or $opt_Q or $opt_A or $opt_U or $opt_I or
-             $opt_f or $opt_x);
+             $opt_f or $opt_x or $opt_N);
 
 if (${'opt_.'}) {
   $opt_C = "!SHORTMAIL! $opt_C";
@@ -503,20 +548,18 @@
 }
 
 unless ($skey or $gkey or $anonymous) {
-  if ($opt_v) {
-    if ($FEXID) {
-      warn "ID data from \$FEXID: $fexcgi $from $id\n";
-    } elsif (-f $idf) {
-      warn "ID data from $idf: $fexcgi $from $id\n";
-    }
-  }
-  warn "Server/User: $fexcgi/$from\n" unless $opt_q;
+  if (not $opt_q and (
+    $opt_f||$opt_x||$opt_Q||$opt_l||$opt_L||$opt_U||$opt_z||$opt_Z||$opt_A
+    ||$opt_d||${'opt_!'}||${'opt_@'})
+  ) { warn "Server/User: $fexcgi/$from\n" }
 }
 
 if    ($opt_V and not @ARGV)    	{ exit }
 if    ($opt_f) 				{ &forward } 
 elsif ($opt_x) 				{ &modify } 
+elsif ($opt_N) 				{ &renotify } 
 elsif ($opt_Q) 				{ &query_quotas } 
+elsif ($opt_S) 				{ &query_settings } 
 elsif ($opt_l or $opt_L)		{ &list } 
 elsif ($opt_U)				{ &show_URL } 
 elsif ($opt_z or $opt_Z or ${'opt_!'})	{ &get_log } 
@@ -817,7 +860,51 @@
     from	=> $from,
     to		=> $from,
     id		=> $sid,
-    comment	=> $opt_C, 
+    command	=> $opt_C, 
+  );
+  die "$0: no response from fex server $server\n" unless @r;
+  $_ = shift @r;
+  unless (/^HTTP.* 2/) {
+    s:HTTP/[\d\. ]+::;
+    die "$0: server response: $_\n";
+  }
+  if (($_) = grep(/^X-Sender-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "sender quota (used): $1 ($2) MB\n";
+  } else {
+    print "sender quota: unlimited\n";
+  }
+  if (($_) = grep(/^X-Recipient-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "recipient quota (used): $1 ($2) MB\n";
+  } else {
+    print "recipient quota: unlimited\n";
+  }
+}
+
+
+sub query_settings {
+  my (@r,$r);
+  local $_;
+
+  female_mode("query settings?") if $opt_F;
+
+  if ($FEXID) {
+    print "ID data from \$FEXID\n";
+  } elsif (-f $idf) {
+    print "ID data from $idf\n";
+  } else {
+    die "$0: found no ID\n";
+  }
+  print "server: $fexcgi\n";
+  print "user: $from\n";
+  print "auth-ID: $id\n";
+  print "login URL: ";
+  &show_URL;
+  
+  @r = formdatapost(
+    from	=> $from,
+    to		=> $from,
+    id		=> $sid,
+    command	=> $opt_C, 
   );
   die "$0: no response from fex server $server\n" unless @r;
   $_ = shift @r;
@@ -825,6 +912,18 @@
     s:HTTP/[\d\. ]+::;
     die "$0: server response: $_\n";
   }
+  if (($_) = grep(/^X-Autodelete/,@r) and /:\s+(\w+)/) {
+    print "autodelete: $1\n";
+  }
+  if (($_) = grep(/^X-Default-Keep/,@r) and /(\d+)/) {
+    print "default keep: $1 days\n";
+  }
+  if (($_) = grep(/^X-Default-Locale/,@r) and /:\s+(\w+)/) {
+    print "default locale: $1\n";
+  }
+  if (($_) = grep(/^X-MIME/,@r) and /:\s+(\w+)/) {
+    print "display file with browser: $1\n";
+  }
   if (($_) = grep(/^X-Sender-Quota/,@r) and /(\d+)\s+(\d+)/) {
     print "sender quota (used): $1 ($2) MB\n";
   } else {
@@ -934,7 +1033,6 @@
 
 sub show_URL {
   printf "%s/fup/%s\n",$fexcgi,encode_b64("from=$from&id=$id");
-  exit;
 }
 
 
@@ -946,7 +1044,7 @@
     from	=> $from,
     to		=> $from,
     id		=> $sid,
-    comment	=> $opt_C, 
+    command	=> $opt_C, 
   );
   die "$0: no response from fex server $server\n" unless @r;
   $_ = shift @r;
@@ -1135,8 +1233,22 @@
         query_sid($server,$port);
       }
       foreach $to (@to) {
+        # alias in local config?
+        if ($alias{$to}) {
+          if ($alias{$to} =~ /(.+?):(.+)/) {
+            my $ato = $1;
+            my $opt = $2;
+            my @argv = @_ARGV;
+            pop @argv;
+            # special extra upload
+            system $0,split(/\s/,$opt),@argv,$ato;
+            $to = '';
+          } else {
+            $to = $alias{$to};
+          }
+        }
         # alias in server address book?
-        if ($AB{$to}) {  
+        elsif ($AB{$to}) {  
           # do not substitute alias with expanded addresses because then 
           # keep and autodelete options from address book will get lost
           # $to = $AB{$to};
@@ -1169,7 +1281,8 @@
       }
     }
   
-    $to = join(',',@to);
+    $to = join(',',grep /./,@to) or exit;
+    warn "Server/User: $fexcgi/$from\n" unless $opt_q;
   
     if (
       not $skey and not $gkey
@@ -1356,10 +1469,21 @@
     }
     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);
       foreach (@r) {
-        if ($from eq $to or $from =~ /^\Q$to\E@/i or $nomail or $anonymous) {
-          print "$1\n" if /^X-(Recipient.*)/i;
-          print "$2\n" if /^(X-)?(Location:.*)/i;
+        if (/^(X-)?(Recipient.*)/i) {
+          $recipient = $2;
+          if (/notification=no/i) { $nonot = 1 }
+          else                    { $nonot = 0 }
+        }
+        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";
+          }
         }
       }
     }
@@ -1440,6 +1564,61 @@
 }
 
 
+sub renotify {
+  my (@r);
+  my ($to,$n,$dkey,$file,$req,$recipient);
+  local $_;
+
+  die $usage if @ARGV;
+
+  open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+  while (<$fexlist>) {
+    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_N) {
+      $n = $1;
+      $dkey = $2;
+      last;
+    }
+  }
+  close $fexlist;
+  
+  unless ($n) {
+    die "$0: file #$opt_N not found in fexlist\n";
+  }
+
+  female_mode("resend notification for file #$opt_N?") if $opt_F;
+
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  $req = "GET $proxy_prefix/fup?"
+        ."from=$from&ID=$sid&dkey=$dkey&command=RENOTIFY"
+        ." HTTP/1.1";
+  sendheader("$server:$port",$req);
+  http_response();
+  while (<$SH>) {
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    last if /^\s*$/;
+    if (/^X-Notify: (.+)\/(.+)\/(.+)/) {
+      $recipient = $1;
+      $file = $3;
+    }
+  }
+  
+  if ($file) {
+    print "notification e-mail for $file has been resent to $recipient\n";
+  } else {
+    if ($opt_v) {
+      die "$0: server failed\n";
+    } else {
+      die "$0: server failed, rerun command with option -v\n";
+    }
+  }
+  
+  exit;
+}
+
+
 sub modify {
   my (@r);
   my ($n,$dkey,$file,$req);
@@ -1702,11 +1881,12 @@
   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,$P{to},$P{from},$P{id},
-                                   $filename,$fileid);
+    ($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;
-      die "$0: $file has been already transferred\n";
+      warn "$0: $file has been already transferred\n";
+      return $file;
     } elsif ($seek and $seek < $filesize) {
       $resume = " (resuming at byte $seek)";
     } elsif ($filesize <= $seek) {
@@ -2063,7 +2243,7 @@
   $zipbase =~ s/\.zip$//;
   map { s/([^_\w\+\-\.])/\\$1/g } @files;
 
-  open my $ff,"find @files -type f|" or die "$0: cannot search for @_ - $!\n";
+  open my $ff,"find @files|" or die "$0: cannot search for @_ - $!\n";
   @files = ();
 
   zipfile: for (;;) {
@@ -2075,7 +2255,8 @@
     $zsize = 0;
     while ($file = <$ff>) {
       chomp $file;
-      next if -l $file or not -f $file;
+      # next if -l $file or not -f $file;
+      next unless -f $file;
       $size = -s $file;
       if ($size > 2147480000) {
         unlink @zipfiles;
@@ -2114,7 +2295,10 @@
   }
   print $cmd,"\n" if $opt_v;
   open $cmd,"|$cmd" or die "$0: cannot create $zip - $!\n";
-  foreach (@_) { print {$cmd} $_."\n" }
+  foreach (@_) { 
+    print {$cmd} $_."\n";
+    print "  $_\n" if $opt_v;
+  }
   close $cmd or die "$0: zip failed - $!\n";
 
   return $zip;
@@ -2137,67 +2321,6 @@
 }
 
 
-sub serverconnect {
-  my ($server,$port) = @_;
-  my $connect = "CONNECT $server:$port HTTP/1.1";
-  local $_;
-  
-  if ($proxy) {
-    tcpconnect(split(':',$proxy));
-    if ($port == 443) {
-      printf "--> %s\n",$connect if $opt_v;
-      nvtsend($connect,"");
-      $_ = <$SH>;
-      s/\r//;
-      printf "<-- $_"if $opt_v;
-      unless (/^HTTP.1.. 200/) {
-        die "$0: proxy error : $_";
-      }
-      eval "use IO::Socket::SSL";
-      die "$0: cannot load IO::Socket::SSL\n" if $@;
-      $SH = IO::Socket::SSL->start_SSL($SH);
-    }
-  } else {
-    tcpconnect($server,$port);
-  }
-}
-
-
-# set up tcp/ip connection
-sub tcpconnect {
-  my ($server,$port) = @_;
-  
-  if ($SH) {
-    close $SH;
-    undef $SH;
-  }
-  
-  if ($port == 443) {
-    eval "use IO::Socket::SSL";
-    die "$0: cannot load IO::Socket::SSL\n" if $@;
-    $SH = IO::Socket::SSL->new(
-      PeerAddr => $server,
-      PeerPort => $port,
-      Proto    => 'tcp',
-    );
-  } else {
-    $SH = IO::Socket::INET->new(
-      PeerAddr => $server,
-      PeerPort => $port,
-      Proto    => 'tcp',
-    );
-  }
-  
-  if ($SH) {
-    autoflush $SH 1;
-  } else {
-    die "$0: cannot connect $server:$port - $@\n";
-  }
-  
-  print "TCPCONNECT to $server:$port\n" if $opt_v;
-}
-
-
 sub query_file {
   my ($server,$port,$to,$from,$id,$filename,$fileid) = @_;
   my $seek = 0;
@@ -2480,42 +2603,6 @@
 }
 
 
-sub sendheader {
-  my $sp = shift;
-  my @head = @_;
-  my $head;
-  
-  push @head,"Host: $sp";
-  
-  foreach $head (@head) {
-    print "--> $head\n" if $opt_v;
-    print {$SH} $head,"\r\n";
-  }
-  print "-->\n" if $opt_v;
-  print {$SH} "\r\n";
-}
-
-
-sub nvtsend {
-  local $SIG{PIPE} = sub { $sigpipe = "@_" };
-  
-  $sigpipe = '';
-  
-  die "$0: internal error: no active network handle\n" unless $SH;
-  die "$0: remote host has closed the link\n" unless $SH->connected;
-  
-  foreach my $line (@_) {
-    print {$SH} $line,"\r\n";
-    if ($sigpipe) {
-      undef $SH;
-      return 0;
-    }
-  }
-  
-  return 1;
-}
-
-
 # transfer status
 sub ts {
   my ($b,$tb) = @_;
@@ -2568,6 +2655,7 @@
         s/autodelete=\w+/autodelete=$opt_D/ if $opt_D;
         s/keep=\d+/keep=$opt_k/             if $opt_k;
         print;
+        $frecipient ||= (split)[1];
       }
     }
   } else {
@@ -2624,12 +2712,6 @@
 }
 
 
-sub mtime {
-  my @d = localtime((stat shift)[9]);
-  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
-}
-
-
 # emulate seek on a pipe
 sub readahead {
   my $fh = shift; # filehandle
@@ -2706,21 +2788,6 @@
 }
 
 
-# from MIME::Base64::Perl
-sub encode_b64 {
-  my $res = "";
-  my $eol = "\n";
-  my $padding;
-  
-  pos($_[0]) = 0;
-  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
-  $res =~ tr|` -_|AA-Za-z0-9+/|;
-  $padding = (3-length($_[0])%3)%3;
-  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
-  return $res;
-}
-
-
 sub female_mode {
   local $_;
   if (open my $tty,'/dev/tty') {
@@ -2742,6 +2809,7 @@
 sub http_response {
   local $_ = shift || <$SH>;
   my @r = @_;
+  my $error;
 
   $_ = <$SH> unless $_;
   unless (defined $_ and /\w/) {
@@ -2755,8 +2823,13 @@
     die "$0: server error: $_\n@r\n";
   }
   unless (/^HTTP.* 200/) {
-    s/HTTP.[\s\d.]+//;
-    die "$0: server error: $_\n";
+    $error = $_;
+    $error =~ s/HTTP.[\s\d.]+//;
+    if ($opt_v) {
+      print "<-- $_";
+      print "<-- $_" while <$SH>;
+    }
+    die "$0: server error: $error\n";
   }
 
   print "<-- $_\n" if $opt_v;
@@ -2764,6 +2837,48 @@
 }
 
 
+sub ws {
+  local $_ = shift;
+  return split;
+}
+
+
+sub update {
+  my $cfb = '### common functions ###';
+  my $cfc;
+  
+  local $/;
+  
+  open $0,$0 or die "cannot read $0 - $!\n";
+  $_ = <$0>;
+  close $0;
+  s/.*\n$cfb\n//s;
+  $cfc = $_;
+  
+  foreach my $p (qw(fexget sexsend)) {
+    open $p,$p or die "cannot read $p - $!\n";
+    $_ = <$p>;
+    close $p;
+    s/\n$cfb.*/\n$cfb\n$cfc/s;
+    system "vv -s $p";
+    open $p,'>',$p or die "cannot write $p - $!\n";
+    print {$p} $_;
+    close $p;
+  }
+
+  exec "l $0 fexget sexsend";
+  exit;
+}
+
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
 sub urldecode {
   local $_ = shift;
   s/\%([a-f\d]{2})/chr(hex($1))/ige;
@@ -2771,7 +2886,170 @@
 }
 
 
-sub ws {
-  local $_ = shift;
-  return split;
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  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) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
+sub encode_b64 {
+  my $res = "";
+  my $eol = "\n";
+  my $padding;
+  
+  pos($_[0]) = 0;
+  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+  $res =~ tr|` -_|AA-Za-z0-9+/|;
+  $padding = (3-length($_[0])%3)%3;
+  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+  return $res;
 }
diff -Nru fex-20140917/cgi-bin/fac fex-20150120/cgi-bin/fac
--- fex-20140917/cgi-bin/fac	2014-08-18 12:32:32.000000000 +0200
+++ fex-20150120/cgi-bin/fac	2015-01-17 00:17:28.000000000 +0100
@@ -816,7 +816,7 @@
     }
 
     $user .= '@'.$mdomain if $mdomain and $user !~ /@/;
-    open my $mail,"|$sendmail -f '$admin' '$user' '$bcc'"
+    open my $mail,'|-',$sendmail,'-f',$admin,$user,$bcc
 	or http_die("cannot start sendmail - $!\n");
     pq($mail,qq(
         'From: $admin'
diff -Nru fex-20140917/cgi-bin/foc fex-20150120/cgi-bin/foc
--- fex-20140917/cgi-bin/foc	2014-05-26 14:08:41.000000000 +0200
+++ fex-20150120/cgi-bin/foc	2015-01-11 11:58:09.000000000 +0100
@@ -10,6 +10,9 @@
 use Fcntl 	qw(:flock);
 use Digest::MD5	qw(md5_hex);
 
+$CGI::LIST_CONTEXT_WARN = 0;
+$CGI::LIST_CONTEXT_WARN = 0;
+
 # add fex lib
 ($FEXLIB) = $ENV{FEXLIB} =~ /(.+)/;
 die "$0: no $FEXLIB\n" unless -d $FEXLIB;
@@ -208,6 +211,17 @@
       '  Get <a href="/fuc?reminder=no&akey=$akey">no reminder</a> notification e-mails (current setting: <em>send reminders</em>).'
     ));
   }
+  if (-e "$user/\@MIME") {
+    pq(qq(
+      '  <p><hr><p>'
+      '  <a href="/fuc?mime=no&akey=$akey">Save</a> files after download (current setting: <em>display</em>).'
+    ));
+  } else {
+    pq(qq(
+      '  <p><hr><p>'
+      '  <a href="/fuc?mime=yes&akey=$akey">Display</a> files when downloading with web browser (current setting: <em>save</em>).'
+    ));
+  }
   pq(qq(
     '  <p><hr><p>'
     '  <a href="/fup?akey=$akey">Back to fup (upload page)</a>'
diff -Nru fex-20140917/cgi-bin/fop fex-20150120/cgi-bin/fop
--- fex-20140917/cgi-bin/fop	2014-09-11 23:54:12.000000000 +0200
+++ fex-20150120/cgi-bin/fop	2015-01-13 14:03:50.000000000 +0100
@@ -307,7 +307,7 @@
     }
       
   }
-
+  
   if ($qs =~ /\&?KEEP=(\d+)/i) {
     $keep = $1;
     $filename = filename($file);
@@ -692,8 +692,10 @@
   $fileid = readlink "$file/id" || '';
   
   # determine own MIME entity header for download
+  my $mime = $file;
+  $mime =~ s:/.*:/\@MIME:;
   my $mt = $ENV{FEXHOME}.'/etc/mime.types';
-  if ($http_client !~ /MSIE/ and $type =~ /x-mime/i and open $mt,'<',$mt) {
+  if (($type =~ /x-mime/i or -e $mime) and open $mt,'<',$mt) {
     $type = 'application/octet-stream';
     MIMETYPES: while (<$mt>) {
       chomp;
diff -Nru fex-20140917/cgi-bin/fuc fex-20150120/cgi-bin/fuc
--- fex-20140917/cgi-bin/fuc	2014-06-03 10:51:57.000000000 +0200
+++ fex-20150120/cgi-bin/fuc	2015-01-09 21:55:57.000000000 +0100
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 
 # FEX CGI for user control 
-# (subuser, groups, address book, one time upload key, auth-ID)
+# (subuser, groups, address book, one time upload key, auth-ID, etc)
 #
 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
 #
@@ -11,6 +11,9 @@
 use Fcntl 	qw(:flock);
 use Digest::MD5	qw(md5_hex);
 
+$CGI::LIST_CONTEXT_WARN = 0;
+$CGI::LIST_CONTEXT_WARN = 0;
+
 # add fex lib
 ($FEXLIB) = $ENV{FEXLIB} =~ /(.+)/;
 die "$0: no $FEXLIB\n" unless -d $FEXLIB;
@@ -36,7 +39,7 @@
 
 my $user = my $id = my $nid = my $ssid = my $comment = '';
 my $notification = my $reminder = my $disclaimer = '';
-my $encryption = my $pubkey = '';
+my $encryption = my $pubkey = my $mime = '';
 
 $akey = ''; # delete akey cookie
 
@@ -64,6 +67,7 @@
     $v =~ /^encryption$/i	? $encryption	= checkchars('parameter',$vv):
     $v =~ /^pubkey$/i		? $pubkey	= $vv:
     $v =~ /^reminder$/i		? $reminder	= checkchars('parameter',$vv):
+    $v =~ /^mime$/i		? $mime		= checkchars('parameter',$vv):
     $v =~ /^comment$/i  	? $comment	= decode_utf8(normalize($vv)):
     $v =~ /^id$/i   		? $id		= checkchars('auth-ID',$vv):
     $v =~ /^nid$/i  		? $nid		= checkchars('auth-ID',$vv):
@@ -327,7 +331,7 @@
     my $rows = ($ab =~ tr/\n//) + 5;
     pq(qq(
       '<h2>Edit address book</h2>'
-      '<table border=1>'
+      '<table border=0>'
       '  <tr align="left"><th>Entry:<th>alias<th>e-mail address<th># optional comment</tr>'
       '  <tr align="left"><td>Example:<td><code>Framstag</code><td><code>framstag\@rus.uni-stuttgart.de</code><td><code># Ulli Horlacher</code></tr>'
       '</table>'
@@ -336,7 +340,7 @@
       '      accept-charset="UTF-8"'
       '      enctype="multipart/form-data">'
       '  <input type="hidden" name="akey" value="$akey">'
-      '  <textarea name="ab" cols="80" rows="$rows">$ab</textarea><br>'
+      '  <textarea name="ab" cols="160" rows="$rows">$ab</textarea><br>'
       '  <input type="submit" value="submit">'
       '</form>'
       '<p>'
@@ -414,6 +418,29 @@
     '<p>'
     '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
     '</body></html>'
+  ));
+  &reexec;
+}
+
+if ($user and $mime eq 'yes') {
+  open $mime,'>',"$user/\@MIME" or http_die("cannot write $user/\@MIME - $!\n");
+  close $mime;
+  pq(qq(
+    '<h3>Downloads will now be displayed (if possible).<h3>'
+    '<p>'
+    '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+    '</body></html>'
+  ));
+  &reexec;
+}
+
+if ($user and $mime eq 'no') {
+  unlink "$user/\@MIME";
+  pq(qq(
+    '<h3>Downloads will now be saved.<h3>'
+    '<p>'
+    '<a href="/foc?akey=$akey">back to F*EX operation control</a>'
+    '</body></html>'
   ));
   &reexec;
 }
diff -Nru fex-20140917/cgi-bin/fup fex-20150120/cgi-bin/fup
--- fex-20140917/cgi-bin/fup	2014-09-17 21:16:08.000000000 +0200
+++ fex-20150120/cgi-bin/fup	2015-01-18 01:22:56.000000000 +0100
@@ -16,6 +16,7 @@
 use Cwd			qw'abs_path';
 
 use constant DS => 60*60*24;
+use constant M  => 1024*1024;
 
 # add fex lib
 die "$0: no \$FEXLIB\n" unless $ENV{FEXLIB};
@@ -40,7 +41,7 @@
 our ($FEXHOME);
 our ($spooldir,$durl,$tmpdir,$logdir,$docdir,$hostname,$admin,$fra);
 our ($keep_default,$recipient_quota,$sender_quota);
-our ($sendmail,$mdomain,$fop_auth,$faillog);
+our ($sendmail,$mdomain,$fop_auth,$mail_auth,$faillog);
 our ($dkeydir,$ukeydir,$akeydir,$skeydir,$gkeydir,$xkeydir);
 our $akey = '';
 our $dkey = '';
@@ -118,8 +119,8 @@
 $uid = randstring(8) unless $uid; # upload ID
 
 # user requests for forgotten ID
-$id_forgotten = $id if $id =~ /^"?\?"?/;
-if ($from and $id_forgotten and not $fop_auth and not $nomail) {
+$id_forgotten = $id if $id =~ /^"?\?"?$/;
+if ($from and $id_forgotten and $mail_authid and not ($fop_auth or $nomail)) {
   &check_status($from);
   &id_forgotten;
   exit;
@@ -154,10 +155,6 @@
   $nomail = $anonymous;
 }
 
-if ($to and (readlink "$to/\@NOTIFICATION"||'') =~ /^no/i) {
-  $nomail = 'recipient';
-}
-
 $comment = 'NOMAIL' if $nomail and not $comment;
 
 # one time token
@@ -315,7 +312,6 @@
                                                                      
   if ($command eq 'CHECKQUOTA') {
     http_die("illegal command \"$command\"") if $public or $anonymous;
-    my ($quota,$du);
     nvt_print('HTTP/1.1 204 OK');
     # nvt_print("X-SID: $ENV{SID}") if $ENV{SID};
     ($quota,$du) = check_sender_quota($muser||$from);
@@ -326,36 +322,45 @@
     exit;
   }
 
+  if ($command eq 'LISTSETTINGS') {
+    http_die("illegal command \"$command\"") if $public or $anonymous;
+    nvt_print('HTTP/1.1 204 OK');
+    # nvt_print("X-SID: $ENV{SID}") if $ENV{SID};
+    ($quota,$du) = check_sender_quota($muser||$from);
+    nvt_print("X-Sender-Quota: $quota $du")    if $quota;
+    ($quota,$du) = check_recipient_quota($muser||$from);
+    nvt_print("X-Recipient-Quota: $quota $du") if $quota;
+    $autodelete = lc(readlink "$from/\@AUTODELETE" || $autodelete);
+    nvt_print("X-Autodelete: $autodelete");
+    $keep = readlink "$from/\@KEEP" || $keep;
+    nvt_print("X-Default-Keep: $keep");
+    $locale = readlink "$from/\@LOCALE" || $default_locale || 'english';
+    nvt_print("X-Default-Locale: $locale");
+    $mime = -e "$from/\@MIME" ? 'yes' : 'no';
+    nvt_print("X-MIME: $mime");
+    nvt_print('');
+    exit;
+  }
+
   if ($command eq 'RENOTIFY') {
     http_die("illegal command \"$command\"") if $public or $anonymous;
-    my ($file,$filename,$mtime,$comment,$keep);
     my $nfile = '';
     if ($dkey) {
       # resend notification e-mail
-      $file = readlink(".dkeys/$dkey")
+      $file = readlink("$dkeydir/$dkey")
         or html_error($error,"illegal DKEY $dkey");
       $file =~ s:^../::;
-      $nfile = $file;
-      $mtime = mtime("$file/data") 
-        or html_error($error,"illegal DKEY $dkey");
-      $comment = slurp("$file/comment")||'';
-      $keep = untaint(
-        readlink "$file/keep" || 
-        readlink "$file/../../\@KEEP" || 
-        $keep_default
-      );
-      $filename = filename($file);
-
-      notify(
-        status     => 'new',
-        dkey       => $dkey,
-        filename   => $filename,
-        keep       => $keep-int((time-$mtime)/DS),
-        comment    => $comment,
-        autodelete => readlink "$file/autodelete" || $autodelete,
+      $file = untaint($file);
+      unlink "$file/download"; # re-allow download from any ip address
+      notify_locale($dkey,'new');
+      http_header(
+        '200 OK',
+        "X-Notify: $file",
       );
-    } 
-    http_header('200 OK');
+      $nfile = $file;
+    } else {
+      http_header('200 OK');
+    }
     print html_header($head);
     # list sent files
     print "<h3>Files from $from, ",
@@ -366,7 +371,7 @@
       next if $file =~ m:(.+?)/: and -l $1;
       $size = -s "$file/data";
       next unless $size;
-      $size = int($size/1024/1024+0.5);
+      $size = int($size/M+0.5);
       $filename = $comment = '';
       my $rto = $file;
       $rto =~ s:/.*::;
@@ -390,7 +395,8 @@
         }
         my $rkeep = untaint(readlink "$file/keep"||$keep_default)
                     - int((time-mtime("$file/filename"))/DS);
-        if ($comment =~ /NOMAIL/) {
+        if ($comment =~ /NOMAIL/ or 
+           (readlink "$to/\@NOTIFICATION"||'') =~ /^no/i) {
           printf "%8s MB [%s d] %s/%s/%s\n",
                  $size,
                  $rkeep,
@@ -432,7 +438,7 @@
         next if $file =~ m:(.+?)/: and -l $1;
         $size = -s "$file/data";
         next unless $size;
-        $size = int($size/1024/1024+0.5);
+        $size = int($size/M+0.5);
         $filename = $comment = '';
         my $rto = $file;
         $rto =~ s:/.*::;
@@ -487,7 +493,7 @@
           $filename = $comment = '';
           $size = -s "$file/data";
           next unless $size;
-          $size = int($size/1024/1024+0.5);
+          $size = int($size/M+0.5);
           if ($dkey = readlink "$file/dkey") {
             print "\nfrom $from :\n" unless $url;
             $file =~ m:.*/(.+):;
@@ -611,14 +617,17 @@
 
   if (@to and $command eq 'CHECKRECIPIENT') {
     http_die("illegal command \"$command\"") if $public or $anonymous;
+    check_rr($from,@to);
     nvt_print('HTTP/1.1 204 OK');
     nvt_print("X-SID: $sid") if $sid;
     foreach my $to (@group?@group:@to) {
       # my $options = sprintf "(autodelete=%s,keep=%s,locale=%s)",
       # readlink "$to/\@LOCALE"||$locale||$locale{$to}||$default_locale;
-      my $options = sprintf "(autodelete=%s,keep=%s)",
-                    $autodelete{$to}||$autodelete,
-                    $keep{$to}||$keep_default;
+      my $options = sprintf "(autodelete=%s,keep=%s,locale=%s,notification=%s)",
+        $autodelete{$to}||$autodelete,
+        $keep{$to}||$keep_default,
+        readlink("$to/\@LOCALE")||$default_locale,
+        readlink("$to/\@NOTIFICATION")||'full';
       nvt_print("X-Recipient: $to $options");
     }
     nvt_print('');
@@ -680,19 +689,19 @@
 }
 
 # quotas 
-if ($from and $id and $rid eq $id and @to and not $flink) {
+if ($from and $id and $rid eq $id and @to and not $flink and not $seek) {
   my ($quota,$du);
   
   # check sender quota
   ($quota,$du) = check_sender_quota($muser||$from);
-  if ($quota and $du+$cl/1024/1024 > $quota) {
+  if ($quota and $du+$cl/M > $quota) {
     http_die("you are overquota");
   }
   
   # check recipient quota
   foreach my $to (@to) {
     ($quota,$du) = check_recipient_quota($to);
-    if ($quota and $du+$cl/1024/1024 > $quota) {
+    if ($quota and $du+$cl/M > $quota) {
       http_die("$to cannot receive files: is overquota");
     }
   }
@@ -822,6 +831,8 @@
   elsif ($from and $id and $id eq $rid and ($addto or not $submit or not @to)
          and not ($gkey or $skey or $okey or $public or $anonymous))
   {
+    present_locales('/fup');
+    
     @ab = ("<option></option>");
     
     # select menu from server address book
@@ -840,12 +851,14 @@
     unless (@to) {
       unless ($nomail) {
         foreach (glob "$from/\@GROUP/*") {
-          s:.*/::;
-          push @ab,"<option>\@$_</option>" unless /~$/;
+          if (-f and not -l) {
+            s:.*/::;
+            push @ab,"<option>\@$_</option>" unless /~$/;
+          }
         }
       }
     }
-    
+      
     my $ab64 = b64("from=$from&id=$id");
 #     '<form class="uploadform" name="upload"'
     pq(qq(
@@ -949,8 +962,12 @@
       '    }'
       '    return false;'
       '  }'
-      '</script>'
-      '<script type="text/javascript">'
+      ''
+      '  function checkupload() {'
+      '    var file  = document.forms["upload"].elements["file"].value;'
+      '    if (file == "") { alert("No file selected"); }'
+      '  }'
+      ''
       '  function reportsize() {'
       '    var form = document.forms["upload"];'
       '    var filesize = form.file.files[0].size;'
@@ -1005,13 +1022,15 @@
         ));
       }
       my $toh = "group $group:<ul>";
+      my $toc = join(',',@group);
       foreach my $gm (@group) { $toh .= "<li>$gm" }
       $toh .= "</ul>";
       pq(qq(
         '  <input type="hidden" name="id" value="$id">'
         '  <table border="1">'
         '    <tr><td>sender:<td>$from</tr>'
-        '    <tr><td>recipient(s):<td>$toh</tr>'
+        '    <tr><td>recipient(s):'
+        '        <td><input type="hidden" name="to" value="$toc">$toh</tr>'
       ));
     } else {
       my $toc = join(',',@to);
@@ -1106,11 +1125,12 @@
       '    </tr>'
       '    <tr title="If you want to send more than one file, then put them in a zip or tar archive">'
       '        <td>file:'
-      '        <td><input type="file" name="file"    size="80" value="$file" onchange="reportsize();">'
+      '        <td><input type="file" name="file" size="80" value="$file" onchange="reportsize();">'
       '    </tr>'
       '    <tr><td>file size:<td id="filesize"></td></tr>'
       '  </table>'
-      '  <p><input type="submit" value="upload">'
+      '  <p>
+      '  <input type="submit" value="upload" onclick="checkupload()">'
       '<p>'
       '</form>'
     ));
@@ -1139,7 +1159,7 @@
     exit;
   }
 
-  present_locales('fup');
+  present_locales('/fup');
 
   if ($ENV{REQUEST_METHOD} eq 'POST') {
     pq(qq(
@@ -1148,23 +1168,22 @@
       '</h3></font>'
     ));
   }
-  
+
   pq(qq(
-    '<form action="/fup" '
-    '      method="post" '
-    '      accept-charset="ISO-8859-1" '
+    '<form action="/fup"'
+    '      method="post"'
+    '      accept-charset="ISO-8859-1"'
     '      enctype="multipart/form-data">'
     '  <table>'
     '    <tr><td>sender:'
     '        <td><input type="text"     name="from" size="40" value="$from"></tr>'
-    '    <tr><td>auth-ID:       '
+    '    <tr><td>auth-ID:'
     '        <td><input type="password" name="id"   size="16" value="$id" autocomplete="off"></tr>'
-    '  </table> '
+    '  </table>'
   ));
-  unless ($fop_auth or $nomail) {
+  if ($mail_authid and not ($fop_auth or $nomail)) {
 #    pq(qq(
-#      'If you have lost your auth-ID use "?"'
-#      'and your auth-ID will be sent by e-mail to you.'
+#      'If you enter "?" as your auth-ID then it will be sent by e-mail to you.'
 #      '<p>'
 #    ));
     pq(qq(
@@ -1277,6 +1296,12 @@
   }
 }
 
+# additional last check
+foreach $to (@to) {
+  checkaddress($to) or 
+    http_die("<code>$to</code> is not a valid e-mail address");
+}
+
 $to = join(',',@to);
 
 # file overwriting for anonymous is only possible if his client has the 
@@ -1318,12 +1343,13 @@
   foreach (@group?@group:@to) {
     my $to = $_;
     $to =~ s/:\w+=.*//; # remove options from address
-    $filed  = "$to/$from/$fkey";
-    $save   = "$filed/data";
-    $upload = "$filed/upload";
+    $filed     = "$to/$from/$fkey";
+    $save      = "$filed/data";
+    $upload    = "$filed/upload";
+    $download  = "$filed/download";
     $dkey{$to} = readlink "$filed/dkey";
-    unlink $save unless -s $save;
-    unlink $save and $overwrite{$to}++;
+    $overwrite{$to}++ if -f $save and not -f $download;
+    unlink $save,$download;
     rename $upload,$save or http_die("cannot rename $upload to $save - $!\n");
     
     # log dkey
@@ -1337,27 +1363,9 @@
     }
     
     # send notification e-mails if necessary
-    if (not $nomail and ($comment or not $overwrite{$to})) {
-      my $locale = readlink "$to/\@LOCALE" || readlink "$filed/locale" 
-                   || $default_locale;
-      my $lf = untaint("$FEXHOME/locale/$locale/lib/lf.pl");
-      require $lf if -f $lf;
-      unless ($notify{$locale}) {
-        $locale = 'english';
-        $lf = "$FEXHOME/lib/lf.pl";
-        if (-f $lf) { require $lf }
-        else        { $notify{$locale} = \&notify }
-      }
-      &{$notify{$locale}}(
-# notify(
-        status	   => "new",
-        dkey	   => $dkey{$to},
-        filename   => $filename,
-        keep	   => $keep{$to}||$keep||$keep_default,
-        comment	   => $comment,
-        replyto	   => $replyto,
-        autodelete => $autodelete{$to}||$autodelete,
-      );
+    if (not $nomail and (readlink "$to/\@NOTIFICATION"||'') !~ /^no/i
+        and ($comment or not $overwrite{$to})) {
+      notify_locale($dkey{$to},'new');
       debuglog("notify $filed [$filename] '$comment'");
     }
   }
@@ -1384,14 +1392,17 @@
     $cookie = $1 if $ENV{HTTP_COOKIE} =~ /anonymous=([\w:]+)/;
     $cookie .= ':'.$dkey if $cookie !~ /$dkey/;
     nvt_print("Set-Cookie: anonymous=$cookie");
+    $keep{$to} = readlink("$to/\@KEEP")||$keep_default;
   }
   foreach (@group?@group:@to) {
     my $to = $_;
     $to =~ s/:\w+=.*//; # remove options from address
-    my $options = sprintf "(autodelete=%s,keep=%s,locale=%s)",
-      readlink "$to/$from/$fkey/autodelete"||$autodelete,
-      readlink "$to/$from/$fkey/keep"||$keep_default,
-      readlink "$to/$from/$fkey/locale"||$locale;
+    my $file = "$to/$from/$fkey";
+    my $options = sprintf "(autodelete=%s,keep=%s,locale=%s,notification=%s)",
+      readlink("$file/autodelete")||$autodelete,
+      readlink("$file/keep")||readlink("$to/\@KEEP")||$keep_default,
+      readlink("$to/\@LOCALE")||readlink("$file/locale")||$default_locale,
+      readlink("$to/\@NOTIFICATION")||'full';
     nvt_print("X-Recipient: $to $options");
     nvt_print("X-Location: $durl/$dkey{$to}/$fkey") unless $restricted;
   }
@@ -1407,50 +1418,66 @@
 print html_header($head);
 
 if ($nostore) {
-  printf "%s (%s MB) received\n",$file,$ndata/1024/1024;
-} elsif (not $restricted and ($anonymous or $nomail or $from eq $to)) {
-  my $size = $ndata<2*1024      ? sprintf "%s B",$ndata:
-             $ndata<2*1024*1024 ? sprintf "%s kB",int($ndata/1024):
-                                  sprintf "%s MB",int($ndata/1024/1024);
+  printf "%s (%s MB) received\n",$file,$ndata/M;
+} elsif (not $restricted and ($anonymous or $from eq $to)) {
+  my $size = $ndata<2*1024 ? sprintf "%s B",$ndata:
+             $ndata<2*M	   ? sprintf "%s kB",int($ndata/1024):
+                             sprintf "%s MB",int($ndata/M);
   pq(qq(
-    '$file ($size) received and saved<p>'
+    '<code>$file</code> ($size) received and saved<p>'
     'Download URL for copy & paste:'
+    '<h2>$durl/$dkey{$to}/$fkey</h2>'
+    'Link is valid for $keep{$to} days!<p>'
   ));
-  if ($xkey) {
-    my $x = "$durl//$xkey";
-    $x =~ s:/fop::;
-    print "<h2><code>$x</code></h2>\n";
-  } else {
-    foreach my $to (@to) {
-      print "<h2><code>$to</code>:<br></h2>\n" if $to !~ /^anonymous/;
-      print "<h2>$durl/$dkey{$to}/$fkey</h2>\n";
-    }
-  }
-  print "Link is valid for $keep days!<p>\n";
 } else {
   if ($ndata<2*1024) {
-    print "$file ($ndata B) received and saved<p>\n";
+    print "<code>$file</code> ($ndata B) received and saved<p>\n";
     if (not $boring and not $seek) {
       print "Ehh... $ndata <b>BYTES</b>?! You are kidding?<p>\n";
     }
-  } elsif ($ndata<2*1024*1024) {
+  } elsif ($ndata<2*M) {
     $ndata = int($ndata/1024);
-    print "$file ($ndata kB) received and saved<p>\n";
+    print "<code>$file</code> ($ndata kB) received and saved<p>\n";
     if ($ndata<1024 and not ($boring or $seek)) {
       print "Using F*EX for less than 1 MB: ",
-        "ever heard of MIME e-mail? :-)<p>\n";
+        "ever heard of MIME e-mail? &#9786;<p>\n";
     }
   } else {
-    $ndata = int($ndata/1024/1024);
-    print "$file ($ndata MB) received and saved<p>\n";
+    $ndata = int($ndata/M);
+    print "<code>$file</code> ($ndata MB) received and saved<p>\n";
   }
+  print "<ul>\n";
   foreach $to (@to) {
-    if ($overwrite{$to}) { 
-      print "(old $file for $to overwritten)<p>\n" 
+    print "<li>";
+    if ($nomail or $nomail{$to}) {
+      if ($restricted) {
+        rmrf("$to/$from/$fkey");
+        print "<code>$file</code> removed because you are a restricted user ".
+              "and recipient $to cannot receive e-mail<p>\n";
+      } else {
+        pq(qq(
+          '$to cannot receive e-mail &rarr;'
+          '<h3><font color="red">'
+          '  No notification e-mail has been sent to $to!'
+          '</font></h3>'
+          'Download URL for copy & paste:'
+        ));
+        if ($xkey) {
+          my $x = "$durl{$to}//$xkey";
+          $x =~ s:/fop::;
+          print "<h2><code>$x</code></h2>\n";
+        } else {
+          print "<h2>$durl/$dkey{$to}/$fkey</h2>\n";
+          print "Link is valid for $keep{$to} days!<p>\n";
+        }
+      }
+    } elsif ($overwrite{$to} and not $comment) { 
+      print "(old <code>$file</code> for $to overwritten)<p>\n" 
     } else { 
-      print "$to notified<p>\n" 
+      print "$to notified<p>\n"
     }
   }
+  print "</ul>\n";
 }
 
 if ($okey) {
@@ -1463,6 +1490,9 @@
   elsif ($akey)   { print "&akey=$akey&to=$to" }
   print "&bwlimit=$bwlimit&autodelete=$autodelete&keep=$keep\">";
   print "send another file</a>\n";
+  if ($http_client !~ /fexsend/ and $http_client =~ /Linux/i) {
+    print qq'<p>Hi Linux-user, try <a href="/FAQ/user.html#Why_should_I_use_a_special_F_EX_client">fexsend</a>! &#9786;<p>\n';
+  }
   print &logout;
 }
 
@@ -1959,10 +1989,9 @@
           symlink untaint($flink),$upload;
         } else {
           unlink $upload if -l $upload;
-          open $upload,'>>',$upload or http_die("cannot create $upload - $!");
-          unless (flock($upload,LOCK_EX|LOCK_NB)) {
+          open $upload,'>>',$upload or http_die("cannot write $upload - $!");
+          flock($upload,LOCK_EX|LOCK_NB) or
             http_die("<code>$file</code> locked: a transfer is already in progress");
-          }
           unless ($seek) {
             seek $upload,0,0;
             truncate $upload,0;
@@ -1976,7 +2005,6 @@
       
       unlink "$filed/autodelete",
              "$filed/error",
-             "$filed/download",
              "$filed/restrictions",
              "$filed/locale",
              "$filed/keep",
@@ -1984,6 +2012,8 @@
              "$filed/id",
              "$filed/ip",
              "$filed/speed",
+             "$filed/replyto",
+             "$filed/useragent",
              "$filed/comment",
              "$filed/notify";
       unlink "$filed/size" unless $seek;
@@ -2021,13 +2051,20 @@
       }
       mksymlink("$filed/id",$fileid) if $fileid;
       mksymlink("$filed/ip",$ra)     if $ra;
+      if ($http_client and open $http_client,'>',"$filed/useragent") {
+        print {$http_client} $http_client,"\n";
+        close $http_client;
+      }
       if ($_ = readlink "$to/\@LOCALE") {
-        mksymlink("$filed/locale",$_);
+        # mksymlink("$filed/locale",$_);
       } elsif ($locale{$to}) {
         mksymlink("$filed/locale",$locale{$to});
-      } elsif ($locale) {
+      } elsif ($locale and $locale ne $default_locale) {
         mksymlink("$filed/locale",$locale);
       }
+      if ($replyto and $replyto =~ /.@./) {
+        mksymlink("$filed/replyto",$replyto);
+      }
     
       my $arh = "$from/\@ALLOWED_RHOSTS";
       if (-s $arh) {
@@ -2039,6 +2076,10 @@
         close $fh;
       }
     
+      if ((readlink "$to/\@NOTIFICATION"||'') =~ /^no/i) {
+        $nomail{$to} = 'NOTIFICATION';
+      }
+
       if ($nomail) {
         open $fh,'>',"$filed/notify" and close $fh;
       } 
@@ -2053,7 +2094,8 @@
       unless ($dkey = readlink("$filed/dkey") and -l "$dkeydir/$dkey") {
         $dkey = randstring(8);
         unlink "$dkeydir/$dkey";
-        symlink "../$filed","$dkeydir/$dkey" or die "cannot symlink $dkeydir/$dkey";
+        symlink "../$filed","$dkeydir/$dkey" 
+          or http_die("cannot symlink $dkeydir/$dkey ($!)");
         unlink "$filed/dkey";
         symlink $dkey,"$filed/dkey";
       }
@@ -2085,7 +2127,7 @@
     # upload link has been already created, no data to read any more
     $to = join(',',@to);
     fuplog($to,$fkey,0);
-    debuglog("upload successfull, dkey=$dkey");
+    debuglog("upload link successfull, dkey=$dkey");
   }
 
   # regular file
@@ -2292,8 +2334,11 @@
       $u = "$u\@$hostname";
     }
     if ($u eq 'nettest') { 
-      $u = "$u\@$mdomain"  if -d "$u\@$mdomain";
-      $u = "$u\@$hostname" if -d "$u\@$hostname";
+      if ($mdomain and -d "$u\@$mdomain") {
+        $u .= "\@$mdomain"
+      } elsif (-d "$u\@$hostname") {
+        $u .= "\@$hostname"    
+      }
     }
     if    ($u =~ /@/)          { push @ua,$u } 
     elsif ($mdomain)           { push @ua,"$u\@$mdomain" } 
@@ -2363,15 +2408,16 @@
       http_die("cannot create directory $nfile") unless -d $nfile;
       unlink "$nfile/data",
              "$nfile/upload",
+             "$nfile/download",
              "$nfile/autodelete",
              "$nfile/error",
-             "$nfile/download",
              "$nfile/restrictions",
              "$nfile/keep",
              "$nfile/header",
              "$nfile/id",
              "$nfile/speed",
              "$nfile/comment",
+             "$nfile/replyto",
              "$nfile/notify";
       if ($comment) {
         open $comment,'>',"$nfile/comment";
@@ -2384,18 +2430,22 @@
       symlink($keep||$keep_default,         "$nfile/keep");
                     copy("$file/id",        "$nfile/id");
                     copy("$file/ip",        "$nfile/ip");
-      $filename   = copy("$file/filename",  "$nfile/filename",'NEW:');
+                    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);
         unlink "$dkeydir/$dkey";
-        symlink "../$nfile","$dkeydir/$dkey" or die "cannot symlink $dkeydir/$dkey";
+        symlink "../$nfile","$dkeydir/$dkey" 
+          or http_die("cannot symlink $dkeydir/$dkey");
         unlink "$nfile/dkey";
-        symlink $dkey,"$nfile/dkey";
+        symlink $dkey,"$nfile/dkey" 
+          or http_die("cannot create $nfile/dkey - $!");
       }
       
-      if ($nomail) {
+      if ($nomail or $nomail{$to}) {
         if ($filename) {
           my $url = "$durl/$dkey/".normalize_filename($filename);
           pq(qq(
@@ -2405,24 +2455,7 @@
           ));
         }
       } else {
-        my $locale = readlink "$to/\@LOCALE" || $::locale 
-          || readlink "$file/locale" || 'english';
-        my $lf = untaint("$FEXHOME/locale/$locale/lib/lf.pl");
-        require $lf if -f $lf;
-        if ($notify{$locale}) {
-          mksymlink("$nfile/locale",$locale);
-        } else {
-          $locale = 'english';
-          $notify{$locale} = \&notify unless $notify{$locale};
-        }
-        &{$notify{$locale}}(
-          status	=> "new",
-          dkey	   	=> $dkey,
-          filename	=> $filename,
-          keep		=> $keep||$keep_default,
-          comment    	=> $comment||'',
-          autodelete 	=> $autodelete,
-        );
+        notify_locale($dkey,'new');
         fuplog($to,urlencode($filename),"(forwarded)");
         if ($filename) {
           pq(qq(
@@ -2462,6 +2495,7 @@
 sub modify {
   my $file = shift;
   my $filename = filename($file);
+  my $dkey = readlink "$file/$dkey";
   my $to;
   my @parameter;
 
@@ -2483,14 +2517,7 @@
       print {$comment} $comment;
       close $comment;
     }
-    notify(
-      status	 => "new",
-      dkey	 => $dkey,
-      filename	 => $filename,
-      keep	 => $keep||$keep_default,
-      comment	 => $comment,
-      autodelete => $autodelete,
-    );
+    notify_locale($dkey,'new');
     push @parameter,'COMMENT';
   }
   http_header('200 OK');
@@ -2864,7 +2891,7 @@
     while (<$df>) {
       if (/^.+?\s+\d+\s+\d+\s+(\d+)/ and $req/1024 > $1) {
         $free = int($1/1024);
-        $uprq = int($req/1024/1024);
+        $uprq = int($req/M);
         if (not $nomail and open P,"|$sendmail -t") {
           pq(P,qq(
             'From: $admin'
@@ -2924,7 +2951,10 @@
   my ($sig) = @_;
   my $msg;
   my $to = join(',',@to);
-  
+
+  $SIG{__DIE__} = 'DEFAULT';
+  foreach (keys %SIG) { $SIG{$_} = 'DEFAULT' }
+
   $msg = @_ ? "@_" : '???';
   $msg =~ s/\n/ /g;
   $msg =~ s/\s+$//;
@@ -2941,7 +2971,6 @@
            $rb?"(after $rb bytes)":"";
     close $log;
   }
-  $SIG{__DIE__} = '';
   if ($sig eq 'DIE') {
     shift;
     die "$msg\n";
@@ -2997,7 +3026,7 @@
 
 sub check_camel {
   my ($logo,$camel);
-  local $\;
+  local $/;
   
   if (open $logo,"$docdir/logo.jpg") {
     $camel = md5_hex(<$logo>) eq 'ad8a95bba8dd1a61d70bd38611bc2059';
diff -Nru fex-20140917/cgi-bin/fur fex-20150120/cgi-bin/fur
--- fex-20140917/cgi-bin/fur	2014-09-15 16:00:08.000000000 +0200
+++ fex-20150120/cgi-bin/fur	2014-12-19 10:17:59.000000000 +0100
@@ -9,6 +9,9 @@
 use CGI::Carp	qw(fatalsToBrowser);
 use Fcntl 	qw(:flock :seek :mode);
 
+$CGI::LIST_CONTEXT_WARN = 0;
+$CGI::LIST_CONTEXT_WARN = 0;
+
 # import from fex.ph
 our (@local_hosts,@local_domains,@local_rhosts,@local_rdomains);
 our (@registration_hosts,@registration_domains);
@@ -39,6 +42,13 @@
 
 &check_maint;
 
+unless (@local_domains or @local_rdomains) {
+  html_error($error,
+    "No domains for registrations are defined.",
+    "Contact $ENV{SERVER_ADMIN} for details."
+  );
+}
+
 # look for CGI parameters
 foreach my $v (param) {
   my $vv = despace(param($v));
@@ -67,8 +77,8 @@
   unless ($user and $id) {
     http_die("no registration data for key $confirm");
   }
-  unless (-d $user) {
-    mkdir $user,0770 or http_die("mkdir $user - $!\n");
+  unless (-f "$user/.auto") {
+    http_die("registration expired");
   }
   # if (-f "$user/@") { http_die("$user is already activated") }
   open $user,'>',"$user/@" or http_die("open $user/@ - $!\n");
@@ -98,6 +108,7 @@
   exit;
 }
 
+
 unless ($user or $exuser or $demouser) {
   http_header("200 OK");
   print html_header($head);
@@ -211,13 +222,16 @@
   } else {
     html_error($error,"<code>$exuser</code> is not an email address");
   }
-  if (-f "$exuser/@") {
-    html_error($error,"<code>$exuser</code> does already exist");
-  }
   $user = $exuser;
 } elsif ($demouser) {
   $user = $demouser;
-} elsif (@local_domains) {
+} elsif ($user) {
+  unless (@local_domains) {
+    html_error($error,
+      "No local domains for registration are defined.",
+      "Contact $ENV{SERVER_ADMIN} for details."
+    );
+  }
   my $mydomains = join('|',@local_domains);
   $mydomains =~ s/\./\\./g;
   $mydomains =~ s/\*/.*/g;
@@ -237,6 +251,8 @@
       "Contact $ENV{SERVER_ADMIN} for details."
     );
   }
+} else {
+  html_error($error,"No user type found.");
 }
 
 unless (checkforbidden($user)) {
@@ -265,6 +281,7 @@
   $rf = "$exuser/\@ALLOWED_RECIPIENTS";
   open $rf,'>',$rf or http_die("cannot write $rf - $!\n");
   print {$rf} "\@LOCAL_RDOMAINS\n";
+  print {$rf} "# See also file \@ALLOWED_RHOSTS\n";
   close $rf;
   # recipients ip restrictions
   $rf = "$exuser/\@ALLOWED_RHOSTS";
diff -Nru fex-20140917/cgi-bin/rup fex-20150120/cgi-bin/rup
--- fex-20140917/cgi-bin/rup	2014-05-26 18:16:07.000000000 +0200
+++ fex-20150120/cgi-bin/rup	2015-01-15 17:15:54.000000000 +0100
@@ -128,12 +128,8 @@
       foreach my $file (glob "$oto/$from/*/data") {
         next if $file =~ m:/STDFEX/:;
         $file =~ s:/data$::;
-        if (open $file,'<',"$file/filename") {
-          my $filename = <$file> || '';
-          close $file;
-          if ($filename) {
-            print "\t<option>$filename</option>\n";
-          }
+        if ($filename = filename($file)) {
+          print "\t<option>$filename</option>\n";
         }
       }
     }
@@ -205,34 +201,14 @@
   unlink "$nto/$from/$fkey/notify";
   unlink "$nto/$from/$fkey/error";
   unlink "$nto/$from/$fkey/download";
+  if (slurp("$oto/$from/$fkey/$comment") =~ 'NOMAIL') {
+    unlink "$nto/$from/$fkey/comment";
+  }
   $dkey = randstring(8);
   symlink $dkey,"$nto/$from/$fkey/dkey";
   symlink "../$nto/$from/$fkey","$dkeydir/$dkey";
-  if (open $fkey,'<',"$nto/$from/$fkey/filename") {
-    chomp($filename = <$fkey>||'');
-    close $fkey;
-  }
-  $filename = $fkey unless $filename;
-  $keep = readlink "$nto/$from/$fkey/keep" || 0;
-  if (not $keep and open $fkey,'<',"$nto/$from/$fkey/keep") {
-    chomp($keep = <$fkey>||'');
-    close $fkey;
-  }
-  if (open $fkey,'<',"$nto/$from/$fkey/comment") {
-    chomp($comment = <$fkey>||'');
-    close $fkey;
-    if ($comment eq 'NOMAIL') {
-      $comment = '';
-      unlink "$nto/$from/$fkey/comment";
-    }
-  }
-  notify(
-    status	 => "new",
-    dkey	 => $dkey,
-    filename	 => $filename,
-    keep	 => $keep||$keep_default,
-    comment	 => $comment||'',
-  );
+  $filename = filename("$nto/$from/$fkey") || $fkey;
+  notify_locale($dkey,'new');
   ruplog("$oto/$from/$fkey ==> $nto");
   http_header("200 OK");
   print html_header('F*EX redirect');
diff -Nru fex-20140917/cgi-bin/sex fex-20150120/cgi-bin/sex
--- fex-20140917/cgi-bin/sex	2013-08-31 22:32:26.000000000 +0200
+++ fex-20150120/cgi-bin/sex	2014-09-19 11:09:12.000000000 +0200
@@ -66,7 +66,9 @@
   unless (-p $fifo) {
     mkfifo($fifo,0600) or error(503,"Cannot create $fifo : $!");
   }
-  
+
+  sexlog($mode);
+
   my $lock = "$stream/lock";
   open $lock,'>>',$lock or error(503,"Cannot open $lock : $!");
   flock $lock,LOCK_EX|LOCK_NB or error(409,"$stream already in use");
@@ -104,17 +106,17 @@
   }
   
   header('200 OK');
-  sexlog($mode);
 
-  $SIG{PIPE} = sub { sleep 1; rmrf($stream); exit; };
+  $B = 0;
+  $shutdown = sub { sexlog($B); rmrf($stream); exit; };
+  $SIG{PIPE} = sub { sleep 1; &$shutdown; };
   # syswrite $fifo,$data if $data;
-  while (sysread(STDIN,$_,$bs)) {
+  while ($b = sysread(STDIN,$_,$bs)) {
+    $B += $b;
     syswrite $fifo,$_ or die $!;
   }
-  
-  rmrf($stream);
-  exit;
 
+  &$shutdown;
 }
 elsif ($mode eq 'POP') {
   $stream =~ s:/STDSTR:/PUBLIC: if $id eq 'public';
diff -Nru fex-20140917/debian/changelog fex-20150120/debian/changelog
--- fex-20140917/debian/changelog	2014-12-15 10:17:32.000000000 +0100
+++ fex-20150120/debian/changelog	2015-01-21 10:20:40.000000000 +0100
@@ -1,3 +1,32 @@
+fex (20150120-2) unstable; urgency=medium
+
+  * Fix fexget perl warning with upstream fix
+
+ -- Kilian Krause <kilian@debian.org>  Wed, 21 Jan 2015 10:20:03 +0100
+
+fex (20150120-1) unstable; urgency=high
+
+  * New upstream release: 20150120 (Closes: #773751)
+   - SECURITY FIX: race condition between fur and fex_cleanup may create
+     internal instead of external user
+   - several small bugs are fixed
+   - fexwall also mails to sub and group users
+   - optional HTTP basic authentication for htdoc/ directory
+   - several SSL/TLS related fixes including default TLS for https connections
+   - locale selection in upload form, too
+   - better SSL configuration for fexsend,fexget,sexsend
+   - autoview option for fexget
+   - save-or-display (MIME) option for download
+   - new config variable $mail_authid to (dis)allow mailing of forgotten
+     auth-IDs
+  * Update lintian override to ignore :sexsend:sexget: symlink which is
+    interpreted by fexsrv directly
+  * Recommend ca-certificates to verify remote server in fex-utils
+  * Don't fail in postinst while looking up fex in trusted_users
+    (Closes: #774854)
+
+ -- Kilian Krause <kilian@debian.org>  Tue, 20 Jan 2015 15:56:05 +0100
+
 fex (20140917-2) unstable; urgency=high
 
   * Re-add symlinks in htdocs/download - avoid otherwise broken symlinks in
diff -Nru fex-20140917/debian/control fex-20150120/debian/control
--- fex-20140917/debian/control	2014-12-15 10:17:32.000000000 +0100
+++ fex-20150120/debian/control	2015-01-21 10:20:40.000000000 +0100
@@ -10,7 +10,8 @@
 
 Package: fex
 Architecture: all
-Depends: ${misc:Depends}, ${perl:Depends}, adduser, xinetd | inet-superserver, libdigest-md5-file-perl, ucf, libjs-jquery, exim4 | postfix | mail-transport-agent
+Depends: ${misc:Depends}, ${perl:Depends}, adduser, xinetd | inet-superserver,
+ libdigest-md5-file-perl, ucf, libjs-jquery, exim4 | postfix | mail-transport-agent
 Recommends: perl-modules, libnet-dns-perl, fex-utils, libsocket6-perl, wget
 Description: web service for transferring very large files
  F*EX (Frams's Fast File EXchange) is a service that can be used to allow
@@ -46,7 +47,8 @@
 Package: fex-utils
 Architecture: all
 Depends: ${misc:Depends}, ${perl:Depends}
-Recommends: libdigest-md5-file-perl, libnet-sslglue-perl, libnet-inet6glue-perl
+Recommends: libdigest-md5-file-perl, libnet-sslglue-perl,
+ libnet-inet6glue-perl, ca-certificates
 Suggests: wget, fex
 Description: web service for transferring very large files (utils)
  F*EX (Frams's Fast File EXchange) is a service that can be used to allow
diff -Nru fex-20140917/debian/fex.lintian-overrides fex-20150120/debian/fex.lintian-overrides
--- fex-20140917/debian/fex.lintian-overrides	2014-12-15 10:17:32.000000000 +0100
+++ fex-20150120/debian/fex.lintian-overrides	2015-01-21 10:20:40.000000000 +0100
@@ -2,3 +2,6 @@
 # the default configuration without any localisation
 # Thus the recursive symlink is required and cannot be avoided
 fex binary: symlink-is-self-recursive usr/share/fex/locale/english ..
+# This symlink is interpreted by the fexsrv daemon and therefore not usable as
+# file system path
+fex binary: file-in-unusual-dir :sexsend:sexget:
diff -Nru fex-20140917/debian/fex.postinst fex-20150120/debian/fex.postinst
--- fex-20140917/debian/fex.postinst	2014-12-15 10:17:32.000000000 +0100
+++ fex-20150120/debian/fex.postinst	2015-01-21 10:20:40.000000000 +0100
@@ -200,8 +200,8 @@
     fi
     if [ -f /etc/exim/exim.conf -o -f /var/lib/exim4/config.autogenerated ]; then
         # exim4 found...
-        [ ! -f /var/lib/exim4/config.autogenerated ]||ISTRUSTED=$(egrep '^\s*(MAIN_TRUSTED_USERS|trusted_users)\s*=.*fex' /var/lib/exim4/config.autogenerated)
-        [ ! -f /etc/exim/exim.conf ]||ISTRUSTED=$(egrep '^\s*(MAIN_TRUSTED_USERS|trusted_users)\s*=.*fex' /etc/exim/exim.conf)
+        [ ! -f /var/lib/exim4/config.autogenerated ]||ISTRUSTED=$(egrep '^\s*(MAIN_TRUSTED_USERS|trusted_users)\s*=.*fex' /var/lib/exim4/config.autogenerated||true)
+        [ ! -f /etc/exim/exim.conf ]||ISTRUSTED=$(egrep '^\s*(MAIN_TRUSTED_USERS|trusted_users)\s*=.*fex' /etc/exim/exim.conf||true)
         [ -z "$ISTRUSTED" ]||echo "You're running exim4 and fex isn't in the trusted_users list. Consider adding or email notifications may not work!"
     fi
     
diff -Nru fex-20140917/debian/htdocs.md5/20141219-1 fex-20150120/debian/htdocs.md5/20141219-1
--- fex-20140917/debian/htdocs.md5/20141219-1	1970-01-01 01:00:00.000000000 +0100
+++ fex-20150120/debian/htdocs.md5/20141219-1	2015-01-21 10:20:40.000000000 +0100
@@ -0,0 +1,24 @@
+89b404d0404eb3b0eba54a662e04c8ce  htdocs/FAQ/admin.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/admin.html
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/all.html
+91e99f12ccb9f7cc3c399d5c3b0de1aa  htdocs/FAQ/faq.pl
+18ea97a73b75ac6a09cd6176405575c9  htdocs/FAQ/local.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/local.html
+9957852ccdef1f1196b55abf18fe5f61  htdocs/FAQ/meta.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/meta.html
+8efac8c29f2493f7cf587397a3383573  htdocs/FAQ/misc.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/misc.html
+736fe1c5be22c469b1bb973230cdd29d  htdocs/FAQ/user.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/user.html
+8fef1010d65fcae7afcc991d0a889378  htdocs/SEX.html
+1f3d7acc70377496f95c5adddaf4ca7b  htdocs/action-fex-camel.gif
+9518c02523c765d590465393659bfa13  htdocs/dynamic.html
+d41d8cd98f00b204e9800998ecf8427e  htdocs/favicon.ico
+0341cffa891dc5bb3221fbf3edf95202  htdocs/features.html
+54c81afa02df999d3f096685caa5258a  htdocs/fup_template.html
+bdf9506feb34a5f22eb302e48ba9d9df  htdocs/index.html
+ad8a95bba8dd1a61d70bd38611bc2059  htdocs/logo.jpg
+f71d20196d4caf35b6a670db8c70b03d  htdocs/robots.txt
+968a8facfcdd185ad696b86b67ec63ff  htdocs/small_logo.jpg
+863744e12ec319a227c113b3c189c41f  htdocs/sup.html
+9c4136f2a7158d704e9999f052104377  htdocs/tools.html
diff -Nru fex-20140917/debian/htdocs.md5/20150120-1 fex-20150120/debian/htdocs.md5/20150120-1
--- fex-20140917/debian/htdocs.md5/20150120-1	1970-01-01 01:00:00.000000000 +0100
+++ fex-20150120/debian/htdocs.md5/20150120-1	2015-01-21 10:20:40.000000000 +0100
@@ -0,0 +1,26 @@
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/FAQ.html
+bcb051e62706c3e156077b9368de91cd  htdocs/FAQ/admin.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/admin.html
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/all.html
+91e99f12ccb9f7cc3c399d5c3b0de1aa  htdocs/FAQ/faq.pl
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/index.html
+6f7e1a56ba9a8e5a42def936729e2089  htdocs/FAQ/local.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/local.html
+9957852ccdef1f1196b55abf18fe5f61  htdocs/FAQ/meta.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/meta.html
+8efac8c29f2493f7cf587397a3383573  htdocs/FAQ/misc.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/misc.html
+736fe1c5be22c469b1bb973230cdd29d  htdocs/FAQ/user.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/user.html
+8fef1010d65fcae7afcc991d0a889378  htdocs/SEX.html
+1f3d7acc70377496f95c5adddaf4ca7b  htdocs/action-fex-camel.gif
+9518c02523c765d590465393659bfa13  htdocs/dynamic.html
+d41d8cd98f00b204e9800998ecf8427e  htdocs/favicon.ico
+0341cffa891dc5bb3221fbf3edf95202  htdocs/features.html
+54c81afa02df999d3f096685caa5258a  htdocs/fup_template.html
+bdf9506feb34a5f22eb302e48ba9d9df  htdocs/index.html
+ad8a95bba8dd1a61d70bd38611bc2059  htdocs/logo.jpg
+f71d20196d4caf35b6a670db8c70b03d  htdocs/robots.txt
+968a8facfcdd185ad696b86b67ec63ff  htdocs/small_logo.jpg
+863744e12ec319a227c113b3c189c41f  htdocs/sup.html
+9c4136f2a7158d704e9999f052104377  htdocs/tools.html
diff -Nru fex-20140917/debian/htdocs.md5/20150120-2 fex-20150120/debian/htdocs.md5/20150120-2
--- fex-20140917/debian/htdocs.md5/20150120-2	1970-01-01 01:00:00.000000000 +0100
+++ fex-20150120/debian/htdocs.md5/20150120-2	2015-01-21 10:20:40.000000000 +0100
@@ -0,0 +1,26 @@
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/FAQ.html
+bcb051e62706c3e156077b9368de91cd  htdocs/FAQ/admin.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/admin.html
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/all.html
+91e99f12ccb9f7cc3c399d5c3b0de1aa  htdocs/FAQ/faq.pl
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/index.html
+6f7e1a56ba9a8e5a42def936729e2089  htdocs/FAQ/local.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/local.html
+9957852ccdef1f1196b55abf18fe5f61  htdocs/FAQ/meta.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/meta.html
+8efac8c29f2493f7cf587397a3383573  htdocs/FAQ/misc.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/misc.html
+736fe1c5be22c469b1bb973230cdd29d  htdocs/FAQ/user.faq
+6d3b691d1e3b1786c0bf735604fb9817  htdocs/FAQ/user.html
+8fef1010d65fcae7afcc991d0a889378  htdocs/SEX.html
+1f3d7acc70377496f95c5adddaf4ca7b  htdocs/action-fex-camel.gif
+9518c02523c765d590465393659bfa13  htdocs/dynamic.html
+d41d8cd98f00b204e9800998ecf8427e  htdocs/favicon.ico
+0341cffa891dc5bb3221fbf3edf95202  htdocs/features.html
+54c81afa02df999d3f096685caa5258a  htdocs/fup_template.html
+bdf9506feb34a5f22eb302e48ba9d9df  htdocs/index.html
+ad8a95bba8dd1a61d70bd38611bc2059  htdocs/logo.jpg
+f71d20196d4caf35b6a670db8c70b03d  htdocs/robots.txt
+968a8facfcdd185ad696b86b67ec63ff  htdocs/small_logo.jpg
+863744e12ec319a227c113b3c189c41f  htdocs/sup.html
+9c4136f2a7158d704e9999f052104377  htdocs/tools.html
diff -Nru fex-20140917/debian/patches/03_fexget_search_ca.patch fex-20150120/debian/patches/03_fexget_search_ca.patch
--- fex-20140917/debian/patches/03_fexget_search_ca.patch	1970-01-01 01:00:00.000000000 +0100
+++ fex-20150120/debian/patches/03_fexget_search_ca.patch	2015-01-21 10:20:40.000000000 +0100
@@ -0,0 +1,123 @@
+Backported from http://fex.rus.uni-stuttgart.de:8080/download/fexget to avoid perl warning
+--- a/bin/fexget
++++ b/bin/fexget
+@@ -30,7 +30,7 @@ our $SH;
+ our ($fexhome,$idf,$tmpdir,$windoof,$useragent);
+ our ($xv,%autoview);
+ our $bs = 2**16; # blocksize for tcp-reading and writing file
+-our $version = 20150120;
++our $version = 20150121;
+ our $CTYPE = 'ISO-8859-1';
+ our $fexsend = $ENV{FEXSEND} || 'fexsend';
+ 
+@@ -157,48 +157,7 @@ if ($opt_H) {
+   exit;
+ }
+ 
+-# set SSL/TLS options
+-$SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+-foreach my $opt (qw(
+-  SSL_version
+-  SSL_cipher_list 
+-  SSL_verify_mode 
+-  SSL_ca_path 
+-  SSL_ca_file)
+-) {
+-  my $env = uc($opt);
+-  $env =~ s/_//g;
+-  $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+-}
+-
+-if ($SSL{SSL_verify_mode}) {
+-  &search_ca;
+-  unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+-    die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+-  }
+-} elsif (defined($SSL{SSL_verify_mode})) {
+-  # user has set SSLVERIFY=0 !
+-} else {
+-  &search_ca;
+-  $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+-}
+-
+-sub search_ca {
+-  local $_;
+-  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+-  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+-    if (-f) {
+-      $SSL{SSL_ca_file} = $_;
+-      return;
+-    }
+-  }
+-  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+-    if (-f) {
+-      $SSL{SSL_ca_path} = $_;
+-      return;
+-    }
+-  }
+-}
++&get_ssl_env;
+ 
+ my $ffl = "$tmpdir/fexget"; 		# F*EX files list (cache)
+ 
+--- a/htdocs/download/fexget
++++ b/htdocs/download/fexget
+@@ -30,7 +30,7 @@ our $SH;
+ our ($fexhome,$idf,$tmpdir,$windoof,$useragent);
+ our ($xv,%autoview);
+ our $bs = 2**16; # blocksize for tcp-reading and writing file
+-our $version = 20150120;
++our $version = 20150121;
+ our $CTYPE = 'ISO-8859-1';
+ our $fexsend = $ENV{FEXSEND} || 'fexsend';
+ 
+@@ -157,48 +157,7 @@ if ($opt_H) {
+   exit;
+ }
+ 
+-# set SSL/TLS options
+-$SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+-foreach my $opt (qw(
+-  SSL_version
+-  SSL_cipher_list 
+-  SSL_verify_mode 
+-  SSL_ca_path 
+-  SSL_ca_file)
+-) {
+-  my $env = uc($opt);
+-  $env =~ s/_//g;
+-  $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+-}
+-
+-if ($SSL{SSL_verify_mode}) {
+-  &search_ca;
+-  unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+-    die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+-  }
+-} elsif (defined($SSL{SSL_verify_mode})) {
+-  # user has set SSLVERIFY=0 !
+-} else {
+-  &search_ca;
+-  $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+-}
+-
+-sub search_ca {
+-  local $_;
+-  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+-  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+-    if (-f) {
+-      $SSL{SSL_ca_file} = $_;
+-      return;
+-    }
+-  }
+-  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+-    if (-f) {
+-      $SSL{SSL_ca_path} = $_;
+-      return;
+-    }
+-  }
+-}
++&get_ssl_env;
+ 
+ my $ffl = "$tmpdir/fexget"; 		# F*EX files list (cache)
+ 
diff -Nru fex-20140917/debian/patches/series fex-20150120/debian/patches/series
--- fex-20140917/debian/patches/series	2014-12-15 10:17:32.000000000 +0100
+++ fex-20150120/debian/patches/series	2015-01-21 10:20:40.000000000 +0100
@@ -1,2 +1,3 @@
 01_xinetd.patch
 02_fex.ph_no_newrelease.patch
+03_fexget_search_ca.patch
diff -Nru fex-20140917/debian/rules fex-20150120/debian/rules
--- fex-20140917/debian/rules	2014-12-15 10:17:32.000000000 +0100
+++ fex-20150120/debian/rules	2015-01-21 10:20:40.000000000 +0100
@@ -47,6 +47,8 @@
 	dh_installdocs
 	cp doc/new debian/fex/usr/share/doc/fex/NEWS
 	echo "fex_$(DEBVERSION)" >debian/fex/usr/share/fex/htdocs/version
+	# update file system paths in FAQ
+	sed -i -e 's,/home/fex/,/var/lib/fex/,g' debian/fex/usr/share/fex/htdocs/FAQ/*
 	@(cd debian/fex/usr/share/fex/;find htdocs/ -type f|grep -v -e version -e FAQ/jquery.js -e download/ |LC_ALL=C sort|xargs -n 1 md5sum >../../../../fex.md5.check)
 	if diff -q debian/fex.md5.check debian/htdocs.md5/$(DEB_NOEPOCH_VERSION) >/dev/null;then \
 		echo "Correct htdocs md5sum found."; \
diff -Nru fex-20140917/doc/Changes fex-20150120/doc/Changes
--- fex-20140917/doc/Changes	2014-09-17 21:34:21.000000000 +0200
+++ fex-20150120/doc/Changes	2015-01-17 11:44:54.000000000 +0100
@@ -1,5 +1,50 @@
+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-13 fexsend: added option -N resend notification email
+           resending notification email deletes download ip restriction
+           fup: fixed bug sending to groups broken
+2015-01-10 fexsend: added option -S show server/user settings
+           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
+2014-12-25 fexget,fexsend,sexsend: use default SSL_cipher_list
+                                   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-23 fexsend: $HOME/.fex/config with $opt_* and %alias variables
+           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-03 fup: remove file after upload if restricted user has set NOMAIL
+           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-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-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-10-23 fexsend: on multiple recipients check only the first for resume
+2014-10-14 fac: added option -L (list files detailed)
+2014-10-01 fex_cleanup: fixed bug wrong default spool for virtual hosts
+2014-09-19 sex: added transfered bytes to sex.log
 2014-09-17 fup: fixed bug no locales presentation
 2014-09-14 fex_cleanup: send new release notification to $admin
+2014-09-11 dop: exclude .* and *~ from stream files
 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)
@@ -32,7 +77,7 @@
 2014-05-25 fup: fixed bug insecure dependency when forwarding a file
                 to a user which has set a default keep value
 2014-05-23 fexget: fixed bug download fails on big file and slow disk
-2014-05-12 set Reply-To in notification e-mails for @remote_domains
+2014-05-12 set Reply-To in notification emails for @remote_domains
 2014-05-03 fup: fixed bug wrong (old) keep time on forword-copy (bounce)
 2014-04-10 fexsend: added "exclude from archive" option -#
 2014-03-28 fexsend: do not copy "NOMAIL" comment in forward
diff -Nru fex-20140917/doc/concept fex-20150120/doc/concept
--- fex-20140917/doc/concept	2014-09-11 11:05:43.000000000 +0200
+++ fex-20150120/doc/concept	2015-01-17 11:29:28.000000000 +0100
@@ -132,11 +132,13 @@
 
 option -D means "delay autodelete": do not delete the the file directly
 after download, but with the nightly fex_cleanup cronjob. More downloads
-are possible only from the same client (identified by cookie).
+are possible only from the same client (identified by cookie or ip
+address). 
 
 option -K means "keep file": do not delete the file after download, but
 only after expiration date (normally 5 days). More downloads are possible
-only from the same client (identified by cookie).
+only from the same client (identified by cookie or ip address).
+
 
 If you fex a file to yourself (sender = recipient), then the resulting
 download link is valid for any client and can be downloaded everywhere
@@ -155,8 +157,8 @@
 These options are also possible in the server address book (see CGI fuc).
 
 If you need more security, then set in fex.ph:
-$fop_auth = 1;
-$force_https = 1;
+$fop_auth = 'yes';
+$force_https = 'yes';
 
 With $fop_auth upload is restricted to registered users and download
 requires (HTTP) authorization. The credentials are the F*EX user email
@@ -225,22 +227,26 @@
 	fup.log				log of file uploads
 	fur.log				log of user self registrations
 	sex.log				log of stream exchanges
-	$from/@				regular user auth-ID
-	$from/@SUBUSER			subuser addresses and IDs
-	$from/@ALLOWED_RECIPIENTS	recipients restrictions for this user
-	$from/@ADDRESS_BOOK		users recipient address book
-	$from/@GROUP			directory of F*EX user groups
-	$from/@OKEY			directory with one time upload keys
-	$from/@QUOTA			sender and recipient quotas
-	$from/@AUTODELETE		autodelete default
-	$from/@KEEP			keep default
-	$from/@LOCALE			locale default
+	$user/@				regular user auth-ID
+	$user/@SUBUSER			subuser addresses and IDs
+	$user/@ALLOWED_RECIPIENTS	recipients restrictions for this user
+	$user/@ALLOWED_RHOSTS		recipient's hosts restrictions
+	$user/@UPLOAD_HOSTS		upload hosts restrictions
+	$user/@DOWNLOAD_HOSTS		download hosts restrictions
+	$user/@ADDRESS_BOOK		users recipient address book
+	$user/@GROUP			directory of F*EX user groups
+	$user/@OKEY			directory with one time upload keys
+	$user/@QUOTA			sender and recipient quotas
+	$user/@AUTODELETE		autodelete default
+	$user/@KEEP			keep default
+	$user/@LOCALE			locale default
 	$user/@CAPTIVE			user must not change his settings
 	$user/@FEXYOURSELF		user can only fex to himself via 
                                         web interface
 	$to/$from/$file/upload		file data in upload progress
 	$to/$from/$file/filename	original file name
 	$to/$from/$file/size		original file size
+	$to/$from/$file/useragent	HTTP header User-Agent
 	$to/$from/$file/data		file data after complete upload
 	$to/$from/$file/keep		keep time (autoexpire) in days
 	$to/$from/$file/autodelete	autodelete option: YES NO or DELAY
@@ -251,6 +257,7 @@
 	$to/$from/$file/error		error message if file has gone
 	$to/$from/$file/download	log of successful downloads
 	$to/$from/$file/restrictions	IP based download restrictions
+                                        (see $user/@ALLOWED_RHOSTS)
 	$to/$from/$file/dkey		download key
 	$to/$from/$file/locale		locale
 
@@ -287,7 +294,7 @@
 The download key (DKEY) is a unique identifier for - guess what -
 downloading. It also prevents an attacker to get the file, because only
 the recipient knows the DKEY as part of the download URL from the
-notification email.
+notification email. 
 
 XKEY is an optional extra download key to have a short download URL in
 shape http://YOURFEXSERVER//XKEY
@@ -366,6 +373,10 @@
 - the filename must not contain a "@"
 - the filename must not end with "~"
 
+To enable HTTP basic authentication, write your access token to a file
+named .htauth which will protect all files in this directory. An user will
+be prompted for this password by his web browser.
+
 To restrict the access to specific client IP addresses, put these IPs into
 a file named .htaccessfrom which will protect all files below this
 directory. You can name single IPs, also as IP ranges (example:
@@ -433,8 +444,12 @@
 
 It is also possible to create this stream file as a regular file. Then the
 content must be the file names you want in the streaming archive.
+
 Note: you may only use relative paths and without "../" elements.
 
+Note: Files beginning with a . or ending with ~ will not be included in
+the download stream.
+
 cronjob fex_cleanup is run once a day and deletes expired uploads, removes
 inactive accounts and does some other spool houskeeping. See: crontab -l
 
diff -Nru fex-20140917/doc/Contribs fex-20150120/doc/Contribs
--- fex-20140917/doc/Contribs	2013-07-09 17:59:00.000000000 +0200
+++ fex-20150120/doc/Contribs	2014-12-26 00:18:59.000000000 +0100
@@ -26,6 +26,11 @@
 Daniel Dieckmann <Daniel_Dieckmann@genua.de>
   - fexget proxy support
 
+Kilian Krause <kilian@debian.org>
+  - Debian package maintainer
+  - SSL security enhancements
+  - annoying, but useful requests ;-)
+
 Hanno Hirsch <superhanno@gmx.de>:
   - bug hunting
 
diff -Nru fex-20140917/doc/installation fex-20150120/doc/installation
--- fex-20140917/doc/installation	2014-08-28 23:21:51.000000000 +0200
+++ fex-20150120/doc/installation	2014-09-23 23:19:46.000000000 +0200
@@ -45,12 +45,13 @@
 
 echo "fex 80/tcp" >> /etc/services
 echo "fex stream tcp nowait fex /home/fex/bin/fexsrv fexsrv" >> /etc/inetd.conf
-# now restart inetd
+# restart inetd or reboot
 useradd -s /bin/bash -c "File EXchange" -m fex
-pwd # --> FEXSOURCEDIR
+cd FEXSOURCEDIR
+chown -R fex .
 su - fex
 cd FEXSOURCEDIR
-cp -av bin cgi-bin lib etc htdocs doc $HOME
+rsync -av bin cgi-bin lib etc htdocs doc $HOME
 cd $HOME
 mkdir spool
 chmod 700 spool
diff -Nru fex-20140917/doc/newfeatures fex-20150120/doc/newfeatures
--- fex-20140917/doc/newfeatures	2014-08-06 00:37:11.000000000 +0200
+++ fex-20150120/doc/newfeatures	2015-01-14 08:53:59.000000000 +0100
@@ -1,6 +1,27 @@
 New features for users
 ----------------------
 
+2015-01-12:
+
+- user configuration: save-or-display (MIME) for download
+
+- fexsend has new option -S show server/user settings
+
+- fexsend has new option -N resend notification email
+
+                                                  
+2014-12-24:
+
+- the CLI clients respect the environment variables SSLVERIFY SSLVERSION
+  SSLCAPATH SSLCAFILE SSLCIPHERLIST to enhance HTTPS security
+
+- new $HOME/.fex/config for the CLI clients, you can set there the variables
+  $opt_* %autoview %alias
+  see "fexsend -H" and "fexget -H" for details
+
+- fexget autoviews images after download
+
+
 2014-08-06:
 
 - you as the sender can download the file, too, without auto-deleting it
diff -Nru fex-20140917/doc/SSL fex-20150120/doc/SSL
--- fex-20140917/doc/SSL	2013-06-20 20:44:26.000000000 +0200
+++ fex-20150120/doc/SSL	2015-01-07 15:15:10.000000000 +0100
@@ -19,6 +19,10 @@
 execargs = perl -T /home/fex/bin/fexsrv stunnel
 EOD
 
+case $(lsb_release -a 2>/dev/null) in 
+  *CentOS*) echo 'fips = no' >>stunnel.conf;;
+esac
+
 chown -R fex .
 
 stunnel=$(which stunnel4)
diff -Nru fex-20140917/doc/version fex-20150120/doc/version
--- fex-20140917/doc/version	2014-09-17 22:07:21.000000000 +0200
+++ fex-20150120/doc/version	2015-01-20 10:59:25.000000000 +0100
@@ -1 +1 @@
-fex-20140917
+fex-20150120
diff -Nru fex-20140917/htdocs/download/fexget fex-20150120/htdocs/download/fexget
--- fex-20140917/htdocs/download/fexget	1970-01-01 01:00:00.000000000 +0100
+++ fex-20150120/htdocs/download/fexget	2015-01-19 13:59:57.000000000 +0100
@@ -0,0 +1,1074 @@
+#!/usr/bin/perl -w
+
+# CLI client for the FEX service for retrieving files
+#
+# see also: fexsend
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Perl Artistic Licence
+
+use 5.006;
+use strict qw'vars subs';
+use Config;
+use POSIX;
+use Encode;
+use Getopt::Std;
+use File::Basename;
+use Socket;
+use IO::Handle;
+use IO::Socket::INET;
+use Time::HiRes 'time';
+use constant k => 2**10;
+use constant M => 2**20;
+
+eval 'use Net::INET6Glue::INET_is_INET6';
+
+$| = 1;
+
+our $SH;
+our ($fexhome,$idf,$tmpdir,$windoof,$useragent);
+our ($xv,%autoview);
+our $bs = 2**16; # blocksize for tcp-reading and writing file
+our $version = 20150120;
+our $CTYPE = 'ISO-8859-1';
+our $fexsend = $ENV{FEXSEND} || 'fexsend';
+
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
+# inquire default character set
+# cannot use "use I18N::Langinfo" because of no windows support!
+eval {
+  local $^W = 0;
+  require I18N::Langinfo;
+  I18N::Langinfo->import(qw'langinfo CODESET');
+  $CTYPE = langinfo(CODESET());
+};
+
+if ($Config{osname} =~ /^mswin/i) {
+  $windoof = $Config{osname};
+  $ENV{HOME} = $ENV{USERPROFILE};
+  $fexhome = $ENV{FEXHOME} || $ENV{HOME}.'/fex';
+  $tmpdir = $ENV{FEXTMP} || $ENV{TMP} || "$fexhome/tmp";
+  $idf = "$fexhome/id";
+  $useragent = sprintf("fexget-$version (%s %s)",
+                       $Config{osname},$Config{archname});
+  $SSL{SSL_verify_mode} = 0;
+  chdir $ENV{USERPROFILE}.'\Desktop';
+  # open XX,'>XXXXXX';close XX;
+} else {
+  $0 =~ s:(.*)/:: and $ENV{PATH} .= ":$1";
+  $fexhome = $ENV{FEXHOME} || $ENV{HOME}.'/.fex';
+  $tmpdir = $ENV{FEXTMP} || "$fexhome/tmp";
+  $idf = "$fexhome/id";
+  $_ = `(lsb_release -d||uname -a)2>/dev/null`||'';
+  chomp;
+  s/^Description:\s+//;
+  $useragent = "fexget-$version ($_)";
+}
+
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
+
+my $usage = <<EOD;
+usage: $0 [-v] [-m limit] [-s filename] [-o] [-k] [-X] [-P proxy:port] F*EX-URL(s)
+   or: $0 [-v] -d F*EX-URL(s)
+   or: $0 [-v] -f F*EX-URL(s) e-mail-address
+   or: $0 [-v] -a
+   or: $0 -l [-i tag]
+   or: $0 -H
+options: -v verbose mode
+         -m limit kB/s
+         -s save to filename (-s- means: write to STDOUT/pipe)
+         -o overwrite existing file
+  	 -k keep on server after download
+  	 -X do not extract archive files or autoview file
+  	 -d delete without download
+  	 -f forward a file to another recipient
+         -a get all files (implies -X)
+  	 -l list files on server
+         -i tag alternate server/account, see: $fexsend -h
+         -P use Proxy for connection to the F*EX server
+         -H show hints and examples
+argument: F*EX-URL may be file number (see: $0 -l)
+EOD
+
+my $hints = <<'EOD';
+When you download a file with extension .jpg .gif .png or .tif an image viewer
+will be started. This can be xv or xdg-open.
+In $HOME/.fex/config.pl you can set your prefered autoview applications:
+
+%autoview = (
+  '\.(gif|jpg|png|tiff?)' => 'my_prefered_image_viewer',
+  '\.(avi|mp4|mov)'       => 'vlc -f',
+  '\.pdf'                 => 'evince',
+);
+
+For HTTPS you can set the environment variables:
+SSLVERIFY=1                 # activate server identity verification
+SSLVERSION=TLSv1            # this is the default
+SSLCAPATH=/etc/ssl/certs    # path to trusted (root) certificates
+SSLCAFILE=/etc/ssl/cert.pem # file with trusted (root) certificates
+SSLCIPHERLIST=HIGH:!3DES    # see http://www.openssl.org/docs/apps/ciphers.html
+
+You can set these environment variables also in $HOME/.fex/config.pl, as well as
+the $opt_* variables, e.g.:
+  
+$ENV{SSLVERSION} = 'TLSv1';
+${'opt_+'} = 1;
+$opt_m = 200;
+EOD
+
+if ($windoof and not @ARGV and not $ENV{PROMPT}) {
+  # restart with cmd.exe to have mouse cut+paste
+  my $cmd = "cmd /k \"$0\"";
+  # print "$cmd\n";
+  exec $cmd;
+  exit;
+}
+
+my $atype = '\.(tgz|tar|zip|7z)$';
+my $proxy = '';
+my $proxy_prefix = '';
+my $chunksize;
+
+our ($opt_h,$opt_v,$opt_l,$opt_d,$opt_m,$opt_z,$opt_K,$opt_o,$opt_a);
+our ($opt_s,$opt_k,$opt_i,$opt_V,$opt_X,$opt_f,$opt_P,$opt_L,$opt_H);
+$opt_m = $opt_h = $opt_v = $opt_l = $opt_d = $opt_K = $opt_o = $opt_a = 0;
+$opt_V = $opt_X = $opt_f = $opt_L = $opt_H = 0;
+${'opt_+'} = 0;
+$opt_s = $opt_k = $opt_i = $opt_P = '';
+$_ = "$fexhome/config.pl"; require if -f;
+getopts('hvVHlLdkzoaXf+m:s:i:K:P:') or die $usage;
+$opt_k = '?KEEP' if $opt_k;
+
+if ($opt_m =~ /(\d+)/) {
+  $opt_m = $1
+} else {
+  $opt_m = 0
+}
+
+print "Version: $version\n" if $opt_V;
+die $usage                  if $opt_h;
+if ($opt_H) {
+  print $hints;
+  exit;
+}
+
+# set SSL/TLS options
+$SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+foreach my $opt (qw(
+  SSL_version
+  SSL_cipher_list 
+  SSL_verify_mode 
+  SSL_ca_path 
+  SSL_ca_file)
+) {
+  my $env = uc($opt);
+  $env =~ s/_//g;
+  $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+}
+
+if ($SSL{SSL_verify_mode}) {
+  &search_ca;
+  unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+    die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+  }
+} elsif (defined($SSL{SSL_verify_mode})) {
+  # user has set SSLVERIFY=0 !
+} else {
+  &search_ca;
+  $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+my $ffl = "$tmpdir/fexget"; 		# F*EX files list (cache)
+
+my @rcamel = (
+'
+(_*)  _  _     
+   \\\\/ \\/ \\
+    \  __  )=*
+    //\\\\//\\\\   
+',
+'     \\\\/\\\\/ 
+',
+'    //\\\\//\\\\
+');
+
+# get fexlog
+if ($opt_z) {
+  my $cmd = "$fexsend -Z";
+  $cmd .= " -i $opt_i" if $opt_i;
+  warn "$cmd\n" if $opt_v;
+  exec $cmd;
+  die "$0: cannot run $cmd : $!\n";
+}
+
+if ($opt_l) {
+  &list;
+  exit;
+}
+
+if ($opt_L) {
+  my $cmd = "$fexsend -L";
+  $cmd .= " -i $opt_i" if $opt_i;
+  warn "$cmd\n" if $opt_v;
+  exec $cmd;
+  die "$0: cannot run $cmd : $!\n";
+}
+
+if ($opt_P) {
+  if ($opt_P =~ /^([\w.-]+:\d+)(:(\d+))?/) {
+    $proxy = $1;
+    $chunksize = $3 || 0;
+  } else {
+    die "$0: proxy must be: SERVER:PORT\n";
+  }
+}
+
+if ($opt_a) {
+  $opt_X = $opt_a;
+  die $usage if @ARGV;
+  &list;
+  print "\n";
+  if (open $ffl,$ffl) {
+    while (<$ffl>) {
+      push @ARGV,$1 if /^\s+(\d+)/;
+    }
+    close $ffl;
+  }
+} else {
+  unless (@ARGV) {
+    if ($windoof) {
+      my $url;
+      for (;;) {
+        print "download-URL: ";
+        chomp($url = <STDIN>);
+        if ($url =~ /^http/) {
+          @ARGV = ($url);
+          last;
+        }
+      }
+    } else {
+      die $usage;
+    }
+  }
+}
+
+my ($file,%files,$download,$server,$port,$fop);
+
+if ($opt_f) {
+  unless ($ENV{FEXID} or -f $ENV{HOME}.'/.fex/id') {
+    die "$0: no local FEXID\n";
+  }
+  $opt_f = pop(@ARGV);
+  if ($opt_f =~ /^\d+$|^https?:/) {
+    die "$0: $opt_f is not an e-mail address\n";
+  }
+}
+
+URL: foreach my $url (@ARGV) {
+
+  # do not overrun server
+  sleep 1 if $fop;
+
+  if ($url !~ /^http/) {
+    unless (%files) {
+      open $ffl,$ffl or die "$0: no $ffl, use first: $0 -l\n";
+      my $from = '';
+      while (<$ffl>) {
+        if (/^from (.+) :$/) {
+          $from = $1;
+        } elsif (/^\s*(\d+)\)\s+\d+ MB.* (http\S+)/) {
+          push @{$files{all}},$2;
+          push @{$files{$from}},$2;
+        }
+      }
+      close $ffl;
+    }
+
+    if ($url =~ /^(\d+)$/) {
+      $url = ${files{all}}[$1-1] or die "$0: unknown file number\n";
+    }
+  }
+
+  if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/.*fop/\S+)}) {
+    $server = $2;
+    $port   = $4 || ($1?443:80);
+    $fop    = $5;
+  } else {
+    die "$0: unknown F*EX URL $url\n";
+  }
+
+  if ($proxy) {
+    if    ($port == 80)   { $proxy_prefix = "http://$server"; }
+    elsif ($port == 443)  { $proxy_prefix = "" }
+    else                  { $proxy_prefix = "http://$server:$port"; }
+  }
+
+  serverconnect($server,$port);
+
+  if ($opt_f) {
+    forward($url);
+    next;
+  }
+
+  if ($opt_d) {
+    my @r = del($url);
+    $_ = shift @r;
+    if (/^HTTP.* 200/) {
+      ($file) = grep { $_ = $1 if /^X-File:\s+(.+)/ } @r;
+      $file = $url unless $file;
+      $file =~ s:.*/::;
+      printf "%s deleted\n",urldecode($file);
+    } else {
+      s:HTTP/[\d\. ]+::;
+      die "$0: server response: $_";
+    }
+    next;
+  }
+
+  if ($opt_K) {
+    my @r = keep($url);
+    $_ = shift @r;
+    if (/^HTTP.* 200/) {
+      $file = $url;
+      $file =~ s:.*/::;
+      print "$file kept\n";
+    } else {
+      s:HTTP/[\d\. ]+::;
+      die "$0: server response: $_";
+    }
+    next;
+  }
+
+  $download = download($server,$port,$fop);
+  exit if $opt_s eq '-';
+  unlink $download unless -s $download;
+  exit 2 unless -f $download;
+  
+  if ($windoof) {
+    print "READY\n";
+    exit;
+  }
+
+  if (not $opt_X and $download =~ /\.gpg$/) {
+    if (-t) {
+      print "decrypt \"$download\"? ";
+      $_ = <STDIN>||'y';
+      unless (/^[y\n]/i) {
+        print "keeping \"$download\"\n";
+        exit;
+      }
+    }
+    if (system('gpg',$download) == 0) {
+      unlink $download;
+      $download =~ s/\.gpg$//;
+    }
+  }
+
+  unless ($opt_X) {
+    
+    foreach my $a (keys %autoview) {
+      if ($download =~ /$a$/i and $autoview{$a}) {
+        printf "run \"%s %s\" [Yn] ? ",$autoview{$a},basename($download);
+        $_ = <STDIN>||'';
+        system sprintf("%s %s",$autoview{$a},quote($download)) if /^y|^$/i;
+        next URL;
+      }
+    }
+    
+    if ($ENV{DISPLAY} and $download =~ /\.(gif|jpg|png|tiff?)$/i) {
+      # see also mimeopen and xdg-mime
+      if (my $xv = $xv || pathsearch('xv') || pathsearch('xdg-open')) {
+        printf "run \"%s %s\" [Yn] ? ",basename($xv),basename($download);
+        $_ = <STDIN>||'';
+        system $xv,$download if /^y|^$/i;
+        next URL;
+      }
+    }
+  
+    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') }
+      else { die "$0: unknown archive \"$download\"\n" }
+      if ($? == 0) {
+        unlink $download;
+      } else {
+        die "$0: keeping \"$download\"\n";
+      }
+    }
+  }
+
+}
+
+exit;
+
+sub extract {
+  my $l = shift;
+  my $x = shift;
+  my $d = $download;
+  my $xd = '.';
+  local $_;
+  
+  if (-t and not $windoof) {
+    print "Files in archive:\n";
+    system(split(' ',$l),$download);
+    $d =~ s:.*/:./:;
+    $d =~ s/\.[^.]+$//;
+    for (;;) {
+      $xd = inquire("extract to directory (Ctrl-C to keep archive): ",$d);
+      last if $xd =~ s:^(\./*)*!?$:./:;
+      if ($xd eq '-') {
+        print "keeping $download\n";
+        exit;
+      }    
+      if ($xd !~ s/!$//) {
+        if (-d $xd) {
+          print "directory $xd does already exist, add \"!\" to overwrite\n";
+          redo;
+        }
+        unless (mkdir $xd) {
+          print "cannot mkdir $xd - $!\n";
+          redo;
+        }
+      }
+      unless (chdir $xd) {
+        print "cannot chdir $xd - $!\n";
+        redo;
+      }
+      last;
+    }
+  }
+  print "extracting to $xd :\n";
+  system(split(' ',$x),$download);
+}
+
+sub del {
+  my $url = shift;
+  my ($server,$port);
+  my $del;
+  my @r;
+
+  if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/fop/.+)}) {
+    $server = $2;
+    $port   = $4 || ($1?443:80);
+    $del    = $5.'?DELETE';
+  } else {
+    die "$0: unknown F*EX URL $url\n";
+  }
+
+  sendheader("$server:$port","GET $del HTTP/1.1","User-Agent: $useragent");
+  while (<$SH>) {
+    s/\r//;
+    last if /^\n/; # ignore HTML output
+    warn "<-- $_" if $opt_v;
+    push @r,$_;
+  }
+  die "$0: no response from fex server $server\n" unless @r;
+  return @r;
+}
+
+
+sub forward {
+  my $url = shift;
+  my ($server,$port);
+  my ($uri,$dkey,$list,$cmd,$n);
+  my @r;
+
+  if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/fop/.+)}) {
+    $server = $2;
+    $port   = $4 || ($1?443:80);
+    $uri    = $5;
+  } else {
+    die "$0: unknown F*EX URL $url\n";
+  }
+
+  sendheader(
+    "$server:$port",
+    "GET $uri?COPY HTTP/1.1",
+    "User-Agent: $useragent",
+  );
+  
+  $_ = <$SH>;
+  die "$0: no reply from fex server $server\n" unless $_;
+  warn "<-- $_" if $opt_v;
+  
+  unless (/^HTTP.*200/) {
+    s/^HTTP.... \d+ //;
+    die "$0: $_";
+  }
+  
+  while (<$SH>) {
+    s/\r//;
+    last if /^\n/; # ignore HTML output
+    $dkey = $1 if /^Location:.*\/(\w+)\/.+/;
+    warn "<-- $_" if $opt_v;
+  }
+
+  $cmd = 'fexsend -l >/dev/null 2>&1';
+  print "$cmd\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";
+  while (<$list>) {
+    if (/^\s+(\d+)\) (\w+)/ and $2 eq $dkey) {
+      $n = $1;
+      $cmd = "fexsend -b $n $opt_f";
+      print "$cmd\n" if $opt_v;
+      system $cmd;
+      last;
+    }
+  }
+  close $list;
+  
+  if ($n) {
+    $cmd = "fexsend -d $n >/dev/null 2>&1";
+    print "$cmd\n" if $opt_v;
+    system $cmd;
+  } else {
+    warn "$0: forwarding failed\n";
+  }
+}
+
+
+sub keep {
+  my $url = shift;
+  my ($server,$port);
+  my $keep;
+  my (@hh,@r);
+
+  if ($url =~ m{^http(s?)://([\w\.\-]+)(:(\d+))?(/fop/.+)}) {
+    $server = $2;
+    $port   = $4 || ($1?443:80);
+    $keep    = "$5?KEEP=$opt_K";
+  } else {
+    die "$0: unknown F*EX URL $url\n";
+  }
+
+  push @hh,"GET $keep HTTP/1.1",
+           "Host: $server:$port",
+           "User-Agent: $useragent",
+           "";
+
+  foreach (@hh) {
+    warn $_,"\n" if $opt_v;
+    print $SH $_,"\r\n";
+  }
+  while (<$SH>) {
+    s/\r//;
+    last if /^\n/;
+    push @r,$_;
+  }
+  die "$0: no response from fex server $server\n" unless @r;
+  grep { warn "\t$_" } @r if $opt_v;
+  return @r;
+}
+
+
+sub download {
+  my ($server,$port,$fop,$nocheck) = @_;
+  my ($file,$download,$ssl,$pipe,$filesize,$checkstorage);
+  my (@hh,@r);
+  my ($t0,$t1,$t2,$tt,$tm,$ts,$kBs,$b,$bt,$tb,$B,$buf);
+  my $length = 0;
+  my $seek = 0;
+  my $tc = 0;
+  local $_;
+  local *X;
+
+  if ($opt_s) {
+    $file = $opt_s;
+    if ($opt_s eq '-') {
+      $pipe = $download = $opt_s;
+    } elsif (-p $opt_s or -c $opt_s) {
+      $download = $opt_s;
+    } 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");
+    my $reply = $_ = <$SH>;
+    unless (defined $_ and /\w/) {
+      die "$0: no response from server\n";
+    }
+    warn "<-- $_" if $opt_v;
+    unless (/^HTTP\/[\d.]+ 200/) {
+      s:HTTP/[\d. ]+::;
+      die "$0: server response: $_";
+    }
+    while (<$SH>) {
+      s/\r//;
+      warn "<-- $_" if $opt_v;
+      last if /^\r?\n/;
+      if (/^Content-Disposition: attachment; filename="(.+)"/i) {
+        $file = locale(decode_utf8($1));
+          $file =~ s:.*/::;
+      }
+    }
+    unless ($file) {
+      $file = $fop;
+      $file =~ s:.*/::;
+    }
+    $download = $file.'.tmp';
+    $seek = -s $download || 0;
+  }
+
+  push @hh,"GET $proxy_prefix$fop$opt_k HTTP/1.1",
+           "User-Agent: $useragent",
+           "Connection: close";
+  push @hh,"Range: bytes=$seek-" if $seek;
+
+  # HTTPS needs a new connection for actually downloading the file
+  serverconnect($server,$port) if $opt_P and $port == 443;
+  sendheader("$server:$port",@hh);
+  $_ = <$SH>;
+  die "$0: no response from fex server $server\n" unless $_;
+  s/\r//;
+
+  if (/^HTTP\/[\d.]+ 2/) {
+    warn "<-- $_" if $opt_v;
+    while (<$SH>) {
+      s/\r//;
+      warn "<-- $_" if $opt_v;
+      last if /^\r?\n/;
+      if (/^Content-length:\s*(\d+)/i) {
+        $length = $1;
+      } elsif (/^X-Size: (\d+)/i) {
+        $filesize = $1;
+      }
+    }
+  } else {
+    s/HTTP\/[\d.]+ \d+ //;
+    die "$0: bad server reply: $_";
+  }
+
+  if ($pipe) {
+    *X = *STDOUT;
+  } else {
+    if ($opt_s and $opt_s eq $download) {
+      open X,'>',$download or die "$0: cannot write to \"$download\" - $!\n";
+      $checkstorage = $filesize unless $nocheck;
+    } else {
+      if (-e $file and not $opt_o) {
+        die "$0: destination file \"$file\" does already exist\n";
+      }
+      if ($seek) {
+        open X,'>>',$download or die "$0: cannot write to \"$download\" - $!\n";
+      } else {
+        open X,'>',$download or die "$0: cannot write to \"$download\" - $!\n";
+        $checkstorage = $filesize unless $nocheck;
+      }
+    }
+    if ($checkstorage and not $nocheck) {
+      $t0 = time;
+      my $n = 0;
+      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";
+        };
+        $n++;
+        print STDERR "checking storage... ".$n." MB\r";
+      }
+      close X or do {
+        unlink $download;
+        die "\n$0: cannot write $download - $!\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 {
+        # retry after timeout
+        return(download($server,$port,$fop,'nocheck'))
+      }
+    }
+  }
+
+  $t0 = $t1 = $t2 = int(time);
+  $tb = $B = 0;
+  printf STDERR "resuming at byte %s\n",$seek if $seek;
+  print $rcamel[0] if ${'opt_+'};
+  while ($B < $length and $b = read $SH,$buf,$bs) {
+    syswrite X,$buf;
+    $B += $b;
+    $tb += $b;
+    $bt += $b;
+    $t2 = time;
+    if (${'opt_+'} and int($t2*10)>$tc) {
+      print $rcamel[$tc%2+1];
+      $tc = int($t2*10);
+    }
+    if (int($t2) > $t1) {
+      $kBs = int($bt/k/($t2-$t1));
+      $kBs = int($tb/k/($t2-$t0)) if $kBs < 10;
+      $t1 = $t2;
+      $bt = 0;
+      # smaller block size is better on slow links
+      $bs = 4096 if $bs>4096 and $tb/($t2-$t0)<65536;
+      if ($tb<10*M) {
+        printf STDERR "%s: %d kB (%d%%) %d kB/s \r",
+                      $download,
+                      int(($tb+$seek)/k),
+                      int(($tb+$seek)/($length+$seek)*100),
+                      $kBs;
+      } else {
+        printf STDERR "%s: %d MB (%d%%) %d kB/s        \r",
+                      $download,
+                      int(($tb+$seek)/M),
+                      int(($tb+$seek)/($length+$seek)*100),
+                      $kBs;
+      }
+    }
+    if ($opt_m) {
+      if ($t2 == $t0 and $B > $opt_m*k) {
+        print "\nsleeping...\r" if $opt_v;
+        sleep 1;
+      } else {
+        while ($t2 > $t0 and $tb/k/($t2-$t0) > $opt_m) {
+          print "\nsleeping...\r" if $opt_v;
+          sleep 1;
+          $t2 = time;
+        }
+      }
+    }
+  }
+  close $SH;
+  close X;
+  
+  print $rcamel[2] if ${'opt_+'};
+
+  $tt = $t2-$t0;
+  $tm = int($tt/60);
+  $ts = $tt-$tm*60;
+  $kBs = int($tb/k/($tt||1));
+  if ($seek) {
+    printf STDERR "$file: %d MB, last %d MB in %d s (%d kB/s)      \n",
+                  int(($tb+$seek)/M),int($tb/M),$tt,$kBs;
+  } else {
+    printf STDERR "$file: %d MB in %d s (%d kB/s)      \n",
+                  int($tb/M),$tt,$kBs;
+  }
+
+  if ($tb != $length) {
+    if ($windoof) {
+      exec "\"$0\" @ARGV";
+      exit;
+    } else {
+      die "$0: $server annouced $length bytes, but only $tb bytes has been read\n";
+    }
+  }
+
+  unless ($pipe or -p $download or -c $download) {
+    my @s = stat $file if -e $file;
+    rename $download,$file
+      or die "$0: cannot rename \"$download\" to \"$file\" - $!\n";
+    chmod $s[2],$file if @s;
+  }
+
+  return sprintf("%s/%s",getcwd(),$file);
+}
+
+
+sub list {
+  my $cmd = "$fexsend -L";
+  $cmd .= " -i $opt_i" if $opt_i;
+  if ($opt_v) {
+    $cmd .= " -v";
+    warn "$cmd\n";
+  }
+  open $cmd,"$cmd|" or die "$0: cannot run $cmd : $!\n";
+  open $ffl,'>',$ffl or die "$0: cannot open $ffl : $!\n";
+  my $n;
+  while (<$cmd>) {
+    if (/\d MB .*http/) {
+      $n++;
+      printf {$ffl} "%4d) %s",$n,$_;
+      s:http[^\"]*/::;
+      printf        "%4d) %s",$n,$_;
+    } else {
+      print;
+      print {$ffl} $_;
+    }
+  }
+}
+
+
+sub locale {
+  my $string = shift;
+
+  if ($CTYPE) {
+    if ($CTYPE =~ /UTF-?8/i) {
+      return $string;
+    } elsif (grep { $CTYPE =~ /^$_$/i } Encode->encodings()) {
+      return encode($CTYPE,$string);
+    } else {
+      return encode('ISO-8859-1',$string);
+    }
+  }
+
+  return $string;
+}
+
+
+sub pathsearch {
+  my $prg = shift;
+  
+  foreach my $dir (split(':',$ENV{PATH})) {
+    return "$dir/$prg" if -x "$dir/$prg";
+  }
+}
+
+    
+sub quote {
+  local $_ = shift;
+  s/([^\w¡-ÿ_%\/=~:.,-])/\\$1/g;
+  return $_;
+}
+
+    
+{
+  my $tty;
+
+  sub inquire {
+    my $prompt = shift;
+    my $default = shift;
+    local $| = 1;
+    local $_;
+
+    if (defined $default) {
+      unless ($tty) {
+        chomp($tty = `tty 2>/dev/null`);
+        eval { local $^W; require "sys/ioctl.ph"; };
+      }
+
+      if (defined(&TIOCSTI) and $tty and open($tty,'>',$tty)) {
+        print $prompt;
+        foreach my $a (split("",$default)) { ioctl($tty,&TIOCSTI,$a) } 
+        chomp($_ = <STDIN>||'');
+      } else {
+        $prompt =~ s/([\?:=]\s*)/ [$default]$1/ or $prompt .= " [$default]";
+        print $prompt;
+        chomp($_ = <STDIN>||'');
+        $_ = $default unless length;
+      }
+    } else {
+      print $prompt;
+      chomp($_ = <STDIN>||'');
+    }
+
+    return $_;
+  }    
+}    
+
+
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
+sub urldecode {
+  local $_ = shift;
+  s/\%([a-f\d]{2})/chr(hex($1))/ige;
+  return $_;
+}
+
+
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  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) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
+sub encode_b64 {
+  my $res = "";
+  my $eol = "\n";
+  my $padding;
+  
+  pos($_[0]) = 0;
+  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+  $res =~ tr|` -_|AA-Za-z0-9+/|;
+  $padding = (3-length($_[0])%3)%3;
+  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+  return $res;
+}
diff -Nru fex-20140917/htdocs/download/fexsend fex-20150120/htdocs/download/fexsend
--- fex-20140917/htdocs/download/fexsend	1970-01-01 01:00:00.000000000 +0100
+++ fex-20150120/htdocs/download/fexsend	2015-01-16 15:52:53.000000000 +0100
@@ -0,0 +1,3055 @@
+#!/usr/bin/perl -w
+
+# CLI client for the F*EX service (send, list, delete)
+#
+# see also: fexget
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Perl Artistic Licence
+
+use 5.006;
+use strict qw'vars subs';
+use Encode;
+use Config;
+use Socket;
+use IO::Handle;
+use IO::Socket::INET;
+use Getopt::Std;
+use File::Basename;
+use Cwd qw'abs_path';
+use Fcntl qw':flock :mode';
+use Digest::MD5 qw'md5_hex';  # encrypted ID / SID
+use Time::HiRes qw'time';
+# use Smart::Comments;
+use constant k => 2**10;
+use constant M => 2**20;
+
+eval 'use Net::INET6Glue::INET_is_INET6';
+
+&update if "@ARGV" eq 'UPDATE';
+
+$| = 1;
+
+our ($SH,$fexhome,$idf,$tmpdir,$windoof,$useragent,$editor,$nomail);
+our ($anonymous,$public);
+our ($tpid,$frecipient);
+our ($FEXID,$FEXXX,$HOME);
+our (%alias);
+our $chunksize = 0;
+our $version = 20150120;
+our $_0 = $0;
+our $DEBUG;
+
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
+if ($Config{osname} =~ /^mswin/i) {
+  $windoof = $Config{osname};
+  $HOME = $ENV{USERPROFILE};
+  $fexhome = $ENV{FEXHOME} || $HOME.'\fex';
+  $tmpdir = $ENV{FEXTMP} || $ENV{TEMP} || "$fexhome\\tmp";
+  $idf = "$fexhome\\id";
+  $editor = $ENV{EDITOR} || 'notepad.exe';
+  $useragent = sprintf("fexsend-$version (%s %s)",
+                       $Config{osname},$Config{archname});
+  $SSL{SSL_verify_mode} = 0;
+} else {
+  $0 =~ s:.*/::;
+  $HOME = (getpwuid($<))[7]||$ENV{HOME};
+  $fexhome = $HOME.'/.fex';
+  $tmpdir = $ENV{FEXTMP} || "$fexhome/tmp";
+  $idf = "$fexhome/id";
+  $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')) {
+  eval { require } or warn $@;
+}
+
+my $from = '';
+my $to = '';
+my $id = '';
+my $skey = '';
+my $gkey = '';
+my $atype = '';		# archive type
+my $fexcgi;		# F*EX CGI URL
+my @files;		# files to send
+my %AB = ();		# server based address book
+my ($server,$port,$sid);
+my $proxy = '';
+my $proxy_prefix = '';
+my $features = ''; 
+my $timeout = 30; 	# server timeout
+my $fexlist = "$tmpdir/fexlist";
+my ($usage,$hints);
+my $xx = $0 =~ /^xx/;
+
+if ($xx) {
+  $usage = "usage: send file(s):               xx [:slot] file...\n".
+           "   or: send STDIN:                 xx [:slot] -\n".
+           "   or: send pipe:                  ... | xx [:slot] \n".
+           "   or: get file(s) or STDIN:       xx [:slot] \n".
+           "   or: get file(s) no-questions:   xx [:slot] --\n".
+           "examples: dmesg | xx\n".
+           "          xx project\n".
+           "          xx --\n".
+           "          xx :conf /etc /boot\n";
+} else {
+  $usage = <<EOD;
+usage: $0 [options] file(s) [@] recipient(s)
+   or: $0 [special options]
+   or: $0 -f \# recipient(s)
+   or: $0 -x \# [-C -k -D -K -S]
+options: -v           verbose mode
+         -d           delete file on fex server
+         -c           compress file
+         -g           encrypt file with gpg
+         -m limit     limit throughput (kB/s)
+         -i tag       use ID data [tag] 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
+         -K           no auto-delete after download
+         -M           MIME-file (to be displayed in recipient\'s webbrowser)
+         -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)
+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
+EOD
+#   or: $0 -R FEX-URL e-mail
+#         -R FEX mail  self-register your e-mail address at FEX server
+
+  $hints = <<EOD;
+$0 hints and more options:
+  
+usage: $0 [options] file recipient(s)
+
+Recipient can be a comma separated address list. Example:
+  $0 big.file framstag\@rus.uni-stuttgart.de,webmaster\@flupp.org
+
+Recipient can be an alias from your server address book 
+(use "$0 -A" to edit it). Example:
+  $0 big.file framstag
+
+Recipient can be a SKEY URL, which you have received from a regular F*EX user.
+When using this URL you are a subuser of this full user and the file will be 
+sent to him. Example:
+  $0 big.file http://fex.rus.uni-stuttgart.de/fup?skey=4285f8cdd881626524fba686d5f0a83a
+
+Recipient can be a GKEY URL, which you have received from a regular F*EX user.
+Using this URL you are a member of his group and the file will be sent to all
+members of this group. Example:
+  $0 big.file http://fex.rus.uni-stuttgart.de/fup?gkey=50d26547b1e8c1110beb8748fc1d9444
+
+When you use "FEX-URL/anonymous" as recipient and your F*EX administrator has 
+allowed anonymous upload for your IP address then no auth-ID is needed.
+    
+"." as recipient means fex to yourself and show immediately the download URL 
+(no notification e-mail will be sent). Example:
+  $0 software.tar .
+
+"//" as recipient means fex to yourself and create extra short download URL.
+Example:
+  $0 software.tar //
+
+If you want a Bcc of the notification e-mail then add '!bcc!' to the comment:
+fexsend -C '!bcc! for me and you' ...
+
+Additional special options:
+
+  -. sends a short instead of a detailed notification e-mail
+  -/ does not upload the file, but tells the server to link it
+  -= uses an alias name as file name
+  -# excludes files (# is list separator) from archive -a
+  -n sends no notification e-mail, but shows the download URL immediately
+  -q is quiet mode
+  -r ADDRESS sets e-mail Reply-To ADDRESS
+  -F activates female mode
+  -U show authorized URL
+  -+ is an undocumented feature - test it :-)
+    
+To manage your subuser and groups or forward or redirect files, use a 
+webbrowser with the URL from "$0 -U", e.g.:  firefox \$($0 -U)
+
+If you want to copy-forward an already uploaded file to another recipient,
+then you first have to query the file number with:
+  $0 -l
+and then copy-forward it with:
+  $0 -b # other\@address
+Where # is the file number.
+
+If you want to modify the keep time, comment or auto-delete behaviour of an
+already uploaded file then you first have to query the file number with:
+  $0 -l
+and then for example set the keep time to 30 days with:
+  $0 -x # -k 30
+Where # is the file number.
+
+With option -a you can send several files or whole directories within a single
+archive file. The archive types tar and tgz are build on-the-fly (streaming) 
+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
+
+For HTTPS you can set the environment variables:
+SSLVERIFY=1                 # activate server identity verification
+SSLVERSION=TLSv1            # this is the default
+SSLCAPATH=/etc/ssl/certs    # path to trusted (root) certificates
+SSLCAFILE=/etc/ssl/cert.pem # file with trusted (root) certificates
+SSLCIPHERLIST=HIGH:!3DES    # see http://www.openssl.org/docs/apps/ciphers.html
+  
+Partner program xx is an internet clipboard. See: xx -h
+  
+Partner program fexget is for downloading. See: fexget -h
+  
+For temporary usage of a HTTP proxy use: 
+  $0 -P your_proxy:port:chunksize_in_MB file recipient
+Example:
+  $0 -P wwwproxy.uni-stuttgart.de.de:8080:1024 4GB.tar .
+  
+For temporary usage of an alternative F*EX server or user use: 
+  FEXID="FEXSERVER USER AUTHID" $0 file recipient
+Example:
+  FEXID="fex.flupp.org gaga\@flupp.org blubb" $0 big.file framstag\@rus.uni-stuttgart.de
+
+You can define aliases (and optional fexsend options) in \$HOME/.fex/config.pl:
+  %alias = (
+    'alias1' => 'user1\@domain1.org',
+    'alias2' => 'user2\@domain2.org',
+    'both'   => 'user1\@domain1.org,user2\@domain2.org',
+    'extra'  => 'extra\@special.net:-i other -K -k 30',
+  );
+
+fexsend also respects aliases in $HOME/.mutt/aliases
+The alias priority is (descending):
+\$HOME/.fex/config.pl
+\$HOME/.mutt/aliases 
+fexserver address book  
+
+In \$HOME/.fex/config.pl you can also set the SSL* environment variables and the
+\$opt_* variables, e.g.:
+  
+\$ENV{SSLVERSION} = 'TLSv1';
+\${'opt_+'} = 1;
+\$opt_m = 200;
+EOD
+}
+
+my @rcamel = (
+'
+     _  _  c*_)
+    / \/ \//
+ *=(  __  /
+    \\\\/\\\\/
+',
+'    \\\\/\\\\/ 
+',
+'   //\\\\//\\\\
+');
+
+autoflush STDERR;
+
+if ($windoof and not @ARGV and not $ENV{PROMPT}) {
+  # restart with cmd.exe to have mouse cut+paste
+  exec qw'cmd /k',$0,'-W';
+  exit;
+}
+
+unless (-d $fexhome) {
+  mkdir $fexhome,0700 or die "$0: cannot create FEXHOME $fexhome - $!\n";
+}
+
+unless (-d $tmpdir) {
+  mkdir $tmpdir,0700 or die "$0: cannot create tmpdir $tmpdir - $!\n";
+}
+
+my @_ARGV = @ARGV; # save arguments
+
+our ($opt_q,$opt_h,$opt_H,$opt_v,$opt_m,$opt_c,$opt_k,$opt_d,$opt_l,$opt_I,
+     $opt_K,$opt_D,$opt_u,$opt_f,$opt_a,$opt_C,$opt_R,$opt_M,$opt_L,$opt_Q,
+     $opt_A,$opt_i,$opt_z,$opt_Z,$opt_b,$opt_P,$opt_x,$opt_X,$opt_V,$opt_U,
+     $opt_s,$opt_o,$opt_g,$opt_F,$opt_n,$opt_r,$opt_S,$opt_N);
+
+if ($xx) {
+  $opt_q = 1 if @ARGV and $ARGV[-1] eq '--' and pop @ARGV or not -t STDOUT;
+  $opt_h = $opt_v = $opt_m = $opt_I = 0;
+  $opt_X = '';
+  $_ = "$fexhome/config.pl"; require if -f;
+  getopts('hvIm:') or die $usage;
+} else {
+  $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;
+  $opt_S = $opt_N = 0;
+  ${'opt_@'} = ${'opt_!'} = ${'opt_+'} = ${'opt_.'} = ${'opt_/'} = 0;
+  ${'opt_='} = ${'opt_#'} = '';
+  $opt_u = $opt_f = $opt_a = $opt_C = $opt_i = $opt_b = $opt_P = $opt_X = '';
+  $opt_s = $opt_r = '';
+  $_ = "$fexhome/config.pl"; require if -f;
+  getopts('hHvcdognVDKlILUARWMFzZqQS@!+./r:m:k:u:f:a:s:C:i:b:P:x:X:N:=:#:') 
+    or die $usage;
+
+  if ($opt_H) {
+    print $hints;
+    exit;
+  }
+  
+  if ($opt_V) {
+    print "Version: $version\n";
+  }
+  
+  if ($opt_K and $opt_D) {
+    die "$0: you cannot use both options -D and -K\n";
+  }
+
+  if ($opt_a and $opt_c) {
+    die "$0: you cannot use both options -a and -c\n";
+  }
+
+  if ($opt_a and $opt_s) {
+    die "$0: you cannot use both options -a and -s\n";
+  }
+
+  if ($opt_g and $opt_c) {
+    $opt_c = 0;
+  }
+
+  $opt_f ||= $opt_b;
+  if ($opt_f and $opt_f !~ /^\d+$/) {
+    die "$0: option -f needs a number, see $0 -l\n";
+  }
+
+  if ($opt_I and $opt_R) {
+    die "$0: you cannot use both options -I and -R\n";
+  }
+
+  # $opt_C is COMMENT command in F*EX protocol
+  $opt_C =    
+    ($opt_d)		? 'DELETE':
+    ($opt_l or $opt_L)	? 'LIST':
+    ($opt_Q)		? 'CHECKQUOTA':
+    ($opt_S)		? 'LISTSETTINGS':
+    ($opt_Z)		? 'RECEIVEDLOG':
+    ($opt_z)		? 'SENDLOG':
+    (${'opt_!'})	? 'FOPLOG':
+  $opt_C;
+  
+  $opt_D =     
+    ($opt_D) ? 'DELAY':
+    ($opt_K) ? 'NO':
+  $opt_D;
+}
+
+&get_ssl_env;
+
+if ($opt_h) {
+  female_mode("show help?") if $opt_F;
+  print $usage;
+  exit;
+}
+
+
+if ($opt_R) {
+  &register;
+  exit;
+}
+
+
+die $usage if $opt_m and $opt_m !~ /^\d+/;
+
+if ($opt_P) { 
+  if ($opt_P =~ /^([\w.-]+:\d+)(:(\d+))?/) {
+    $proxy = $1;
+    $chunksize = $3 || 0;
+  } else {
+    die "$0: proxy must be: SERVER:PORT\n";
+  }
+}
+
+if ($FEXID = $ENV{FEXID}) {
+  $FEXID = decode_b64($FEXID) if $FEXID !~ /\s/;
+  ($fexcgi,$from,$id) = split(/\s+/,$FEXID);
+} else {
+  if ($windoof and not -f $idf) { &init_id }
+  if (open $idf,$idf) {
+    &get_id($idf);
+    close $idf;
+  }
+}
+
+if ($xx) {
+  # convert old idxx file
+  if ($idf and open $idf,$idf.'xx') {
+    &get_id($idf);
+    close $idf;
+    if (open $idf,'>>',$idf) {
+      print {$idf} "\n[xx]\n",
+                   "$fexcgi\n",
+                   "$from\n",
+                   "$id\n";
+      close $idf;
+      unlink $idf.'xx';
+    }
+  }
+  
+  # special xx ID?
+  if ($FEXXX = $ENV{FEXXX}) {
+    $FEXXX = decode_b64($FEXXX) if $FEXXX !~ /\s/;
+    ($fexcgi,$from,$id) = split(/\s+/,$FEXXX);
+  } elsif (open $idf,$idf) {
+    while (<$idf>) {
+      if (/^\[xx\]/) {
+        $proxy = $proxy_prefix = '';
+        &get_id($idf);
+        last;
+      }
+    }
+    close $idf;
+  }
+  
+} else {
+
+  # alternativ ID?
+  if ($opt_i) {
+    $proxy = $proxy_prefix = '';
+    open $idf,$idf or die "$0: cannot open $idf - $!\n";
+    while (<$idf>) {
+      if (/^\[$opt_i\]/) {
+        &get_id($idf);
+        last;
+      }
+    }
+    close $idf;
+    die "$0: no [$opt_i] in $idf\n" unless $_;
+  }
+}
+
+if ($opt_I) {
+  if ($xx) { &show_id } 
+  else     { &init_id }
+  exit;
+}
+
+if (@ARGV > 1 and $ARGV[-1] =~ /(^|\/)anonymous/) {
+  $fexcgi = $1 if $ARGV[-1] =~ s:(.+)/::;
+  die "usage: $0 [options] file FEXSERVER/anonymous\n" unless $fexcgi;
+  $anonymous = $from = 'anonymous';
+  $sid = $id = 'ANONYMOUS';
+} elsif (@ARGV > 1 and $id eq 'PUBLIC') {
+  $public = $sid = $id;
+} elsif (@ARGV > 1 and $ARGV[-1] =~ m{^(https?://[\w.-]+(:\d+)?/fup\?[sg]key=\w+)}) {
+  $fexcgi = $1;
+  $skey = $1 if $fexcgi =~ /skey=(\w+)/;
+  $gkey = $1 if $fexcgi =~ /gkey=(\w+)/;
+} else {
+
+  $fexcgi = $opt_u if $opt_u;
+  
+  if (not -e $idf and not ($fexcgi and $from and $id)) {
+    die "$0: no ID file $idf found, use \"fexsend -I\" to create it\n";
+  }
+  
+  unless ($fexcgi) {
+    die "$0: no FEX URL found, use \"$0 -u URL\" or \"$0 -I\"\n";
+  }
+  
+  unless ($from and $id) {
+    die "$0: no sender found, use \"$0 -f FROM:ID\" or \"$0 -I\"\n";
+  }
+
+  if ($fexcgi !~ /^http/) {
+    if ($fexcgi =~ /:443/) { $fexcgi = "https://$fexcgi"; }
+    else                   { $fexcgi = "http://$fexcgi"; }
+  }
+
+}
+
+$server = $fexcgi;
+
+$port = 80;
+$port = 443 if $server =~ s{https://}{};
+$port = $1  if $server =~ s/:(\d+)//;
+
+if (0 and $port == 443) {
+  $opt_s and die "$0: cannot use -s with https due to stunnel bug\n"; 
+  $opt_g and die "$0: cannot use -g with https due to stunnel bug\n"; 
+}
+
+$server =~ s{http://}{};
+$server =~ s{/.*}{};
+
+# $chunksize = 4*k unless $chunksize;
+$chunksize *= M;
+
+if ($proxy) {
+  if    ($port == 80)  { $proxy_prefix = "http://$server"; }
+  elsif ($port != 443) { $proxy_prefix = "http://$server:$port"; }
+}
+
+# xx: special file exchange between own accounts
+if ($xx) {
+  my $transferfile = "$tmpdir/STDFEX";
+  # slot?
+  if ($0 eq 'xxx') {
+    $transferfile = "$tmpdir/xx:xxx";
+  } elsif (@ARGV and $ARGV[0] =~ /^:([\w.=+-]+)$/) {
+    $transferfile = "$tmpdir/xx:$1";
+    shift @ARGV;
+  }
+  open my $lock,'>>',$transferfile 
+    or die "$0: cannot write $transferfile - $!\n";
+  flock($lock,LOCK_EX|LOCK_NB)
+    or die "$0: $transferfile is locked by another process\n";
+  truncate $transferfile,0;
+  if (not @ARGV and -t) {
+    &get_xx($transferfile);
+  } else {
+    &send_xx($transferfile);
+  }
+  exit;
+} 
+
+# regular fexsend
+
+&inquire if $windoof and not @ARGV and not
+            ($opt_l or $opt_L or $opt_Q or $opt_A or $opt_U or $opt_I or
+             $opt_f or $opt_x or $opt_N);
+
+if (${'opt_.'}) {
+  $opt_C = "!SHORTMAIL! $opt_C";
+}
+
+if ($opt_n or $opt_C =~ /NOMAIL|!#!/) {
+  $nomail = 'NOMAIL';
+}
+
+unless ($skey or $gkey or $anonymous) {
+  if (not $opt_q and (
+    $opt_f||$opt_x||$opt_Q||$opt_l||$opt_L||$opt_U||$opt_z||$opt_Z||$opt_A
+    ||$opt_d||${'opt_!'}||${'opt_@'})
+  ) { warn "Server/User: $fexcgi/$from\n" }
+}
+
+if    ($opt_V and not @ARGV)    	{ exit }
+if    ($opt_f) 				{ &forward } 
+elsif ($opt_x) 				{ &modify } 
+elsif ($opt_N) 				{ &renotify } 
+elsif ($opt_Q) 				{ &query_quotas } 
+elsif ($opt_S) 				{ &query_settings } 
+elsif ($opt_l or $opt_L)		{ &list } 
+elsif ($opt_U)				{ &show_URL } 
+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 }
+else 					{ &send_fex }
+
+exit;
+
+
+# initialize ID file or show ID
+sub init_id {
+  my $tag;
+  my $proxy = '';
+  
+  if ($opt_I) {
+    $tag = shift @ARGV;
+    die $usage if @ARGV;
+  }
+  
+  $fexcgi = $from = $id = '';
+  
+  unless (-d $fexhome) {
+    mkdir $fexhome,0700 or die "$0: cannot create FEXHOME $fexhome - $!\n";
+  }
+
+  # show ID
+  if (not $tag and open $idf,$idf) {
+    if ($opt_i) {
+      while (<$idf>) {
+        last if /^\[$opt_i\]/;
+      }
+    }
+    $fexcgi = <$idf>;
+    $from   = <$idf>;
+    $id     = <$idf>;
+    close $idf;
+    if ($id) {
+      chomp($fexcgi,$from,$id);
+      $FEXID = encode_b64("$fexcgi $from $id");
+      if (-t STDIN) {
+        print "# hint: to edit the ID file $idf use \"$0 -I .\" #\n";
+        print "export FEXID=$FEXID\n";
+        print "history -d \$((HISTCMD-1));history -d \$((HISTCMD-1))\n";
+      } else {
+        print "FEXID=$FEXID\n";
+      }
+      exit;
+    } else {
+      die "$0: no ID data found\n";
+    }
+  }
+
+  if ($tag and $tag eq '.') { exec $ENV{EDITOR}||'vi',$idf }
+  
+  if ($tag) { print "F*EX server URL for [$tag]: " }
+  else      { print "F*EX server URL: " }
+  $fexcgi = <STDIN>;
+  $fexcgi =~ s/[\s\n]//g;
+  die "you MUST provide a FEX-URL!\n" unless $fexcgi;
+  if ($fexcgi =~ /\?/) {
+    $from = $1 if $fexcgi =~ /\bfrom=(.+?)(&|$)/i;
+    $id   = $1 if $fexcgi =~ /\bid=(.+?)(&|$)/i;
+    $skey = $1 if $fexcgi =~ /\bskey=(.+?)(&|$)/i;
+    $gkey = $1 if $fexcgi =~ /\bgkey=(.+?)(&|$)/i;
+    $fexcgi =~ s/\?.*//;
+  }
+  unless ($fexcgi =~ /^[_:=\w\-\.\/\@\%]+$/) {
+    die "\"$fexcgi\" is not a legal FEX-URL!\n";
+  }
+  $fexcgi =~ s:/fup/*$::;
+  print "proxy address (hostname:port or empty if none): ";
+  $proxy = <STDIN>;
+  $proxy =~ s/[\s\n]//g;
+  if ($proxy =~ /^[\w.-]+:\d+$/) { 
+    $proxy = "!$proxy";
+  } elsif ($proxy =~ /\S/) { 
+    die "wrong proxy address format\n";
+  } else { 
+    $proxy = "";
+  }
+  if ($proxy) {
+    print "proxy POST limit in MB (use 2048 if unknown): ";
+    $_ = <STDIN>;
+    if (/(\d+)/) {
+      $proxy .= "[$1]";
+    }
+  }
+  if ($skey) {
+    $from = 'SUBUSER';
+    $id = $skey;
+  } elsif ($gkey) {
+    $from = 'GROUPMEMBER';
+    $id = $gkey;
+  } else {
+    unless ($from) {
+      print "Your e-mail address as registered at $fexcgi: ";
+      $from = <STDIN>;
+      $from =~ s/[\s\n]//g;
+      die "you MUST provide your e-mail address!\n" unless $from;
+    }
+    unless ($from =~ /^[_:=\w\-\.\/\@\%\+]+$/) {
+      die "\"$from\" is not a legal e-mail address!\n";
+    }
+    unless ($id) {
+      print "Your auth-ID for $from at $fexcgi: ";
+      $id = <STDIN>;
+      $id =~ s/[\s\n]//g;
+      die "you MUST provide your ID!\n" unless $id;
+    }
+  }
+  if (open $idf,'>>',$idf) {
+    print {$idf} "\n[$tag]\n" if $tag and -s $idf;
+    print {$idf} "$fexcgi$proxy\n",
+                 "$from\n",
+                 "$id\n";
+    close $idf;
+    print "data written to $idf\n";
+  } else {
+    die "$0: cannot write to $idf - $!\n";
+  }
+}
+
+
+sub show_id {
+  my ($fexcgi,$from,$id);
+  if (open $idf,$idf) {
+    $fexcgi = <$idf>;
+    $from   = <$idf>;
+    $id     = <$idf>;
+    while (<$idf>) {
+      if (/^\[xx\]/) {
+        $fexcgi = <$idf>;
+        $from   = <$idf>;
+        $id     = <$idf>;
+      }
+    }
+    close $idf;
+    die "$0: too few data in $idf" unless defined $id;
+    chomp($fexcgi);
+    chomp($from);
+    chomp($id);
+    $FEXXX = encode_b64("$fexcgi $from $id");
+    if (-t STDIN) {
+      print "export FEXXX=$FEXXX\n";
+      print "history -d \$((HISTCMD-1));history -d \$((HISTCMD-1))\n";
+    } else {
+      print "FEXXX=$FEXXX\n";
+    }
+  } else {
+    die "$0: cannot read $idf - $!\n";
+  }
+}
+
+
+sub register {
+  my $fs = shift @ARGV or die $usage;
+  my $mail = shift @ARGV or die $usage;
+  my $port;
+  my ($server,$user,$id);
+
+  die "$0: $idf does already exist\n" if -e $idf;
+
+  if ($fs =~ /^https/) {
+    die "$0: cannot handle https at this time\n";
+  }
+
+  $fs =~ s{^http://}{};
+  $fs =~ s{/.*}{};
+  if ($fs =~ s/:(\d+)//) { $port = $1 }
+  else                   { $port = 80 }
+
+  tcpconnect($fs,$port);
+  sendheader("$fs:$port","GET $proxy_prefix/fur?user=$mail&verify=no HTTP/1.1");
+  http_response();
+
+  while (<$SH>) {
+    s/\r//;
+    printf "<-- $_"if $opt_v;
+    last if /^\s*$/;
+  }
+
+  while (<$SH>) {
+    s/\r//;
+    printf "<-- $_"if $opt_v;
+    if (m{http://(.*)/fup\?from=(.+)&ID=(.+)}) {
+      $server = $1;
+      $user = $2;
+      $id = $3;
+
+      if (open F,">$idf") {
+        print F "$server\n",
+                "$user\n",
+                "$id\n";
+        close F;
+        chmod 0600,$idf;
+        print "user data written to $idf\n";
+        print "you can now fex!\n";
+        exit;
+      } else {
+        die "$0: cannot write to $idf - $!\n";
+      }
+    }
+  }
+
+  die "$0: no account data received from F*EX server\n";
+
+}
+
+
+sub send_xx {
+  my $transferfile = shift;
+  my $file = '';
+  my (@r,@tar);
+  
+  $SIG{PIPE} = $SIG{INT} = sub {
+    unlink $transferfile;
+    exit 3;
+  };
+  
+  if ($0 eq 'xxx') { @tar = qw'tar -cv' }
+  else             { @tar = qw'tar -cvz' }
+
+  if (-t) {
+    if ("@ARGV" eq '-') {
+      # store STDIN to transfer file
+      shelldo("cat >> $transferfile");
+    } elsif (@ARGV) {
+      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);
+          chdir $dir or die "$0: $dir - $!\n";
+        } else {
+          $file = $ARGV[0];
+        }
+        if (-l $file) {
+          shelldo(@tar,qw'--dereference -f',$transferfile,$file);
+        } else {
+          shelldo(@tar,'-f',$transferfile,$file);
+        }
+      } else {
+        shelldo(@tar,'-f',$transferfile,@ARGV);
+      }
+      if ($?) {
+        unlink $transferfile;
+        if ($? == 2) {
+          die "$0: interrupted making tar transfer file\n";
+        } else {
+          die "$0: error while making tar transfer file\n";
+        }
+      }
+    }
+  } else {
+    # write input from pipe to transfer file
+    shelldo("cat >> $transferfile");
+  }
+
+  die "$0: no transfer file\n" unless -s $transferfile;
+  
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  @r = formdatapost(
+    from	=> $from,
+    to		=> $from,
+    id		=> $sid,
+    file	=> $transferfile,
+    comment	=> 'NOMAIL',
+    autodelete	=> $transferfile =~ /STDFEX/ ? 'NO' : 'DELAY',
+  );
+  
+  # open P,'|w3m -T text/html -dump' or die "$0: w3m - $!\n";
+  # print P @r;
+  http_response(@r);
+  if ($transferfile =~ /:/ and $0 ne 'xxx') {
+    if ("@r" =~ /\s(X-)?Location: (http.*)\s/) {
+      print "wget -O- $2 | tar xvzf -\n";
+    }
+  }
+  
+  unlink $transferfile;
+}
+
+
+sub query_quotas {
+  my (@r,$r);
+  local $_;
+
+  female_mode("query quotas?") if $opt_F;
+
+  @r = formdatapost(
+    from	=> $from,
+    to		=> $from,
+    id		=> $sid,
+    command	=> $opt_C, 
+  );
+  die "$0: no response from fex server $server\n" unless @r;
+  $_ = shift @r;
+  unless (/^HTTP.* 2/) {
+    s:HTTP/[\d\. ]+::;
+    die "$0: server response: $_\n";
+  }
+  if (($_) = grep(/^X-Sender-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "sender quota (used): $1 ($2) MB\n";
+  } else {
+    print "sender quota: unlimited\n";
+  }
+  if (($_) = grep(/^X-Recipient-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "recipient quota (used): $1 ($2) MB\n";
+  } else {
+    print "recipient quota: unlimited\n";
+  }
+}
+
+
+sub query_settings {
+  my (@r,$r);
+  local $_;
+
+  female_mode("query settings?") if $opt_F;
+
+  if ($FEXID) {
+    print "ID data from \$FEXID\n";
+  } elsif (-f $idf) {
+    print "ID data from $idf\n";
+  } else {
+    die "$0: found no ID\n";
+  }
+  print "server: $fexcgi\n";
+  print "user: $from\n";
+  print "auth-ID: $id\n";
+  print "login URL: ";
+  &show_URL;
+  
+  @r = formdatapost(
+    from	=> $from,
+    to		=> $from,
+    id		=> $sid,
+    command	=> $opt_C, 
+  );
+  die "$0: no response from fex server $server\n" unless @r;
+  $_ = shift @r;
+  unless (/^HTTP.* 2/) {
+    s:HTTP/[\d\. ]+::;
+    die "$0: server response: $_\n";
+  }
+  if (($_) = grep(/^X-Autodelete/,@r) and /:\s+(\w+)/) {
+    print "autodelete: $1\n";
+  }
+  if (($_) = grep(/^X-Default-Keep/,@r) and /(\d+)/) {
+    print "default keep: $1 days\n";
+  }
+  if (($_) = grep(/^X-Default-Locale/,@r) and /:\s+(\w+)/) {
+    print "default locale: $1\n";
+  }
+  if (($_) = grep(/^X-MIME/,@r) and /:\s+(\w+)/) {
+    print "display file with browser: $1\n";
+  }
+  if (($_) = grep(/^X-Sender-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "sender quota (used): $1 ($2) MB\n";
+  } else {
+    print "sender quota: unlimited\n";
+  }
+  if (($_) = grep(/^X-Recipient-Quota/,@r) and /(\d+)\s+(\d+)/) {
+    print "recipient quota (used): $1 ($2) MB\n";
+  } else {
+    print "recipient quota: unlimited\n";
+  }
+}
+
+
+# list spool
+sub list {
+  my (@r,$r);
+  my ($data,$dkey,$n);
+  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/) {
+          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";
+        }
+        exit;
+      }
+    }
+    die "$0: file \#$n not found in fexlist\n";
+  } else {
+    @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/) {
+    s:HTTP/[\d\. ]+::;
+    die "$0: server response: $_\n";
+  }
+  
+  # list sent files
+  if ($opt_l) {
+    open $fexlist,">$fexlist" or die "$0: cannot write $fexlist - $!\n";
+    foreach (@r) {
+      next unless /<pre>/ or $data;
+      $data = 1;
+      last if m:</pre>:;
+      if (/<a href=".*dkey=(\w+).*?">/) { $dkey = $1 }
+      else                              { $dkey = '' }
+#      $_ = encode_utf8($_);
+      s/<.*?>//g;
+      if (/^(to .* :)/) {
+        print "\n$1\n";
+        print {$fexlist} "\n$1\n";
+      } elsif (m/(\d+) MB (.+)/) {
+        $n++;
+        printf "%4s) %8d MB %s\n","#$n",$1,$2;
+        printf {$fexlist} "%3d) %s %s\n",$n,$dkey,$2;
+      }
+    }
+    close $fexlist;
+  } 
+  
+  # list received files
+  if ($opt_L) {
+    foreach (@r) {
+      next unless /<pre>/ or $data;
+      $data = 1;
+      next if m:<pre>:;
+      last if m:</pre>:;
+      if (/(from .* :)/) {
+        print "\n$1\n";
+      }
+      if (m{(\d+) (MB.*)<a href="(https?://.*/fop/\w+/.+)">(.+)</a>( ".*")?}) {
+        printf "%8d %s%s%s\n",$1,$2,$3,($5||'');
+      }
+    }
+  }
+}
+
+
+sub show_URL {
+  printf "%s/fup/%s\n",$fexcgi,encode_b64("from=$from&id=$id");
+}
+
+
+sub get_log {
+  my (@r);
+  local $_;
+  
+  @r = formdatapost(
+    from	=> $from,
+    to		=> $from,
+    id		=> $sid,
+    command	=> $opt_C, 
+  );
+  die "$0: no response from fex server $server\n" unless @r;
+  $_ = shift @r;
+  unless (/^HTTP.* 200/) {
+    s:HTTP/[\d\. ]+::;
+    die "$0: server response: $_\n";
+  }
+  while (shift @r) {}
+  foreach (@r) { print "$_\n" }
+}
+
+
+sub show_address_book {
+  my (%AB,@r);
+  my $alias;
+  local $_;
+  
+  %AB = query_address_book($server,$port,$from);
+  foreach $alias (sort keys %AB) {
+    next if $alias eq 'ADDRESS_BOOK';
+    $_ = sprintf "%s = %s (%s) # %s\n",
+                 $alias,
+                 $AB{$alias},
+                 $AB{$alias}->{options},
+                 $AB{$alias}->{comment};
+    s/ \(\)//;
+    s/ \# $//;
+    print;
+  }
+}
+
+
+sub purge {
+  die "$0: not yet implemented\n";
+}
+
+
+sub delete {
+  my ($to,$file);
+
+  while (@ARGV) {
+    $opt_d = shift @ARGV;
+    die "$usage: $0 -d #\n" if $opt_d !~ /^\d+$/;
+  
+    open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+    while (<$fexlist>) {
+      if (/^to (.+\@.+) :/) {
+        $to = $1;
+      } elsif (/^\s*(\d+)\) (\w+) (.+)/ and $1 eq $opt_d) {
+        serverconnect($server,$port) unless $SH;
+        sendheader(
+          "$server:$port",
+          "GET $proxy_prefix/fop/$2/$2?DELETE HTTP/1.1",
+          "User-Agent: $useragent",
+        );
+        $_ = <$SH>||'';
+        s/\r//;
+        print "<-- $_" if $opt_v;
+        if (/^HTTP.* 200/) {
+          while (<$SH>) {
+            s/\r//;
+            last if /^\n/; # ignore HTML output
+            print "<-- $_" if $opt_v;
+            if (/^X-File:.*\/(.+)/) {
+              printf "%s deleted\n",decode_utf8(urldecode($1));
+            }
+          }
+          undef $SH;
+        } elsif (s:HTTP/[\d\. ]+::) {
+          die "$0: server response: $_";
+        } else {
+          die "$0: no response from fex server $server\n";
+        }
+        last;
+      }
+    }
+    close $fexlist;
+    sleep 1; # do not overrun server
+  }
+
+  exit;
+}
+
+
+sub send_fex {
+  my @to;
+  my $file = '';
+  my @files = ();
+  my ($data,$aname,$alias);
+  my (@r,$r);
+  my $ma = $HOME.'/.mutt/aliases';
+  my $t0 = time;
+  my $transferfile;
+  my @transferfiles;
+  local $_;
+  
+  if ($from =~ /^SUBUSER|GROUPMEMBER$/) {
+    $to = '_';
+  } else {
+    # look for single @ in arguments
+    for (my $i=1; $i<$#ARGV; $i++) {
+      if ($ARGV[$i] eq '@') {
+        $ARGV[$i] = join(',',@ARGV[$i+1 .. $#ARGV]);
+        $#ARGV = $i;
+        last;
+      }
+    }
+    $to = pop @ARGV or die $usage;
+    if ($to eq '.') {
+      $to = $from;
+      $nomail = $opt_C ||= 'NOMAIL';
+    }
+    if ($to eq ':') {
+      $to = $from;
+      $nomail = $opt_C ||= 'NOMAIL';
+    }
+    if ($opt_g and $to =~ /,/) {
+      die "$0: encryption is supported to only one recipient\n";
+    }
+    if ($to =~ m{^https?://.*/fup\?skey=(\w+)}) {
+      $from = 'SUBUSER';
+      $to = '_';
+      $id = $1;
+    }
+    if ($to =~ m{^https?://.*/fup\?gkey=(\w+)}) {
+      $from = 'GROUPMEMBER';
+      $to = '_';
+      $id = $1;
+    }
+  }
+  @to = split(',',lc($to));
+  
+  die $usage unless @ARGV or $opt_a or $opt_s;
+  die $usage if $opt_s and @ARGV;
+
+  # early serverconnect necessary for X-Features info
+  serverconnect($server,$port);
+
+  if ($anonymous) {
+    my $aok;
+    sendheader("$server:$port","OPTIONS FEX HTTP/1.1");
+    $_ = <$SH>||'';
+    s/\r//;
+    die "$0: no response from fex server $server\n" unless $_;
+    print "<-- $_" if $opt_v;
+    if (/^HTTP.* 201/) {
+      while (<$SH>) {
+        s/\r//;
+        print "<-- $_" if $opt_v;
+        last unless /\w/;
+        $aok = $_ if /X-Features:.*ANONYMOUS/;
+      }
+      die "$0: no anonymous support on server $server\n" unless $aok;
+    } else {
+      die "$0: bad response from server $server : $_\n";
+    }
+  } elsif ($public) {
+  } else {
+    
+    query_sid($server,$port);
+    
+    if ($from eq 'SUBUSER') {
+      $skey = $sid;
+      # die "skey=$skey\nid=$id\nsid=$sid\n";
+    }
+
+    if ($from eq 'GROUPMEMBER') {
+      $gkey = $sid;
+    }
+    
+    if ($to eq '.') {
+      @to = ($from);
+      $opt_C ||= 'NOMAIL';
+    } elsif ($to =~ m:^(//.*):) {
+      my $xkey = $1;
+      if ($features =~ /XKEY/) {
+        @to = ($from);
+        $opt_C = $xkey;
+      } else {
+        die "$0: server does not support XKEY\n";
+      }
+    } elsif (grep /^[^@]*$/,@to and not $skey and not $gkey) {
+      %AB = query_address_book($server,$port,$from);
+      if ($proxy) {
+        serverconnect($server,$port);
+        query_sid($server,$port);
+      }
+      foreach $to (@to) {
+        # alias in local config?
+        if ($alias{$to}) {
+          if ($alias{$to} =~ /(.+?):(.+)/) {
+            my $ato = $1;
+            my $opt = $2;
+            my @argv = @_ARGV;
+            pop @argv;
+            # special extra upload
+            system $0,split(/\s/,$opt),@argv,$ato;
+            $to = '';
+          } else {
+            $to = $alias{$to};
+          }
+        }
+        # alias in server address book?
+        elsif ($AB{$to}) {  
+          # do not substitute alias with expanded addresses because then 
+          # keep and autodelete options from address book will get lost
+          # $to = $AB{$to};
+        } 
+        # look for mutt aliases
+        elsif ($to !~ /@/ and $to ne $from and open $ma,$ma) {
+          $alias = $to;
+          while (<$ma>) {
+            if (/^alias \Q$to\E\s/i) {
+              chomp;
+              s/\s*#.*//;
+              s/\(.*?\)//;
+              s/\s+$//;
+              s/.*\s+//;
+              s/[<>]//g;
+              if (/,/) {
+                warn "$0: ignoring mutt multi-alias $to = $alias\n";
+                last;
+              }
+              if (/@/) {
+                $alias = $_;
+                warn "$0: found mutt alias $to = $alias\n";
+                last;
+              }
+            }
+          }
+          close $ma;
+          $to = $alias;
+        }
+      }
+    }
+  
+    $to = join(',',grep /./,@to) or exit;
+    warn "Server/User: $fexcgi/$from\n" unless $opt_q;
+  
+    if (
+      not $skey and not $gkey
+      and $features =~ /CHECKRECIPIENT/ 
+      and $opt_C !~ /^(DELETE|LIST|RECEIVEDLOG|SENDLOG|FOPLOG)$/
+    ) {
+      checkrecipient($from,$to);
+      if ($proxy) {
+        serverconnect($server,$port);
+        query_sid($server,$port);
+      }
+    }
+  }
+
+  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";
+    $opt_a = <STDIN>;
+    $opt_a =~ s/^\s+//;
+    $opt_a =~ s/\s+$//;
+  }
+
+  if ($opt_s) {
+    $opt_s =~ s/^=//;
+    $opt_s =~ s:.*/::;
+    $opt_s =~ s/[^\w_.+-]/_/g;
+    @files = ($opt_s);
+  } elsif ($opt_a) {
+    $opt_a =~ s/^=//;
+    $opt_a =~ s:.*/::;
+    $opt_a =~ s/[^\w_.+-]/_/g;
+    if ($opt_a =~ /(.+)\.(zip|tar|tgz|7z)$/) {
+      $aname = $1;
+      $atype = $2;
+    } else {
+      die "$0: archive name must be one of ".
+          "$opt_a.tar $opt_a.tgz $opt_a.zip\n";
+    }
+    # no file argument left?
+    unless (@ARGV) {
+      # use file name as archive name
+      push @ARGV,$aname;
+      $opt_a =~ s:/+$::g;
+      $opt_a =~ s:.*/::g;
+    }
+    foreach my $file (@ARGV) {
+      die "$0: cannot read $file\n" unless -l $file or -r $file;
+    }
+    $opt_a .= ".$atype" if $opt_a !~ /\.$atype$/;
+    $transferfile = "$tmpdir/$opt_a";
+    unlink $transferfile;
+    print "Making fex archive ($opt_a):\n";
+    if ($atype eq 'zip') {
+      if ($windoof) {
+        # if ($opt_c) { system(qw'7z a -tzip',$transferfile,@ARGV) }
+        # else        { system(qw'7z a -tzip -mm=copy',$transferfile,@ARGV) }
+        system(qw'7z a -tzip',$transferfile,@ARGV);
+        @files = ($transferfile);
+      } else {
+        # zip archives must be < 2 GB, so split as necessary
+        @files = zipsplit($transferfile,@ARGV);
+        if (scalar(@files) == 1) {
+          $transferfile = $files[0];
+          $transferfile =~ s/_1.zip$/.zip/;
+          rename $files[0],$transferfile;
+          @files = ($transferfile);
+        }
+      }
+      @transferfiles =  @files;
+    } elsif ($atype eq '7z') {
+      # http://www.7-zip.org/
+      my @X = (); # exclude list
+      if (${'opt_#'}) {
+        foreach my $x (split('#',${'opt_#'})) {
+          push @X,"-x!$x";
+        }
+      }
+      if ($opt_c) { system(qw'7z a',@X,$transferfile,@ARGV) }
+      else        { system(qw'7z a -t7z -mx0',@X,$transferfile,@ARGV) }
+      @transferfiles = @files = ($transferfile);
+    } elsif ($atype eq 'tar') {
+      if ($windoof) {
+        system(qw'7z a -ttar',$transferfile,@ARGV);
+        @transferfiles = @files = ($transferfile);
+      } else {
+        ## tar is now handled by formdatapost()
+        # system(qw'tar cvf',$transferfile,@ARGV);
+        @files = ($opt_a);
+      }
+    } elsif ($atype eq 'tgz') {
+      if ($windoof) {
+        die "$0: archive type tgz not available, use tar, zip or 7z\n";
+      } else {
+        ## tgz is now handled by formdatapost()
+        # system(qw'tar cvzf',$transferfile,@ARGV);
+        @files = ($opt_a);
+      }
+    } else {
+      die "$0: unknown archive format \"$atype\"\n";
+    }
+    
+    if (@transferfiles) {
+      
+      # error in making transfer archive?
+      if ($?) {
+        unlink @transferfiles;
+        die "$0: $! - aborting upload\n";
+      }
+      
+      # maybe timeout, so make new connect
+      if (time-$t0 >= $timeout) {
+        serverconnect($server,$port);
+        query_sid($server,$port) unless $anonymous;
+      }
+      
+    }
+    
+  } else {
+    
+    unless (@ARGV) {
+      if ($windoof) {
+        &inquire;
+      } else {
+        die $usage;
+      }
+    }
+    
+    foreach (@ARGV) {
+      my $file = $_;
+      unless ($opt_d) {
+        unless (-f $file) {
+          if (-e $file) {
+            die "$0: $file is not a regular file, try option -a\n"
+          } else {
+            die "$0: $file does not exist\n";
+          }
+        }
+        die "$0: cannot read $file\n" unless -r $file;
+      }
+      push @files,$file;
+    }
+  }
+
+  if (${'opt_/'}) {
+    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";
+      }
+    }
+  }
+  
+  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";
+    }
+    female_mode("send file $file?") if $opt_F;
+    @r = formdatapost(
+      from		=> $from,
+      to		=> $to,
+      replyto		=> $opt_r,
+      id		=> $sid,
+      file		=> $file,
+      keep		=> $opt_k,
+      comment		=> $opt_C,
+      autodelete	=> $opt_D, 
+    );
+
+    if (not @r or not grep /\w/,@r) {
+      die "$0: no response from server\n";
+    }
+    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";
+      } else {
+        $r =~ s/.*?:\s*//;
+        $r =~ s/<.+?>//g;
+        die "$0: server error: $r\n";
+      }
+    }
+    if (($r) = grep /<h3>\Q$file/,@r) {
+      $r =~ s/<.+?>//g;
+      print "$r\n";
+    }
+    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);
+      foreach (@r) {
+        if (/^(X-)?(Recipient.*)/i) {
+          $recipient = $2;
+          if (/notification=no/i) { $nonot = 1 }
+          else                    { $nonot = 0 }
+        }
+        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";
+          }
+        }
+      }
+    }
+  }
+  
+  # delete transfer tmp file
+  unlink $transferfile if $transferfile;
+}
+
+
+sub forward {
+  my (@r);
+  my ($to,$n,$dkey,$file,$req);
+  my $status = 1;
+  local $_;
+  
+  # look for single @ in arguments
+  for (my $i=1; $i<$#ARGV; $i++) {
+    if ($ARGV[$i] eq '@') {
+      $ARGV[$i] = join(',',@ARGV[$i+1 .. $#ARGV]);
+      $#ARGV = $i;
+      last;
+    }
+  }
+
+  # if ($windoof and not @ARGV) { &inquire }
+  $to = pop @ARGV or die $usage;
+  $to = $from if $to eq '.';
+
+  open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+  while (<$fexlist>) {
+    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_f) {
+      $n = $1;
+      $dkey = $2;
+      $file = $3;
+      if ($file =~ s/ "(.*)"$//) {
+        $opt_C ||= $1 if $1 ne 'NOMAIL';
+      }
+      last;
+    }
+  }
+  close $fexlist;
+  
+  unless ($n) {
+    die "$0: file #$opt_f not found in fexlist\n";
+  }
+
+  female_mode("forward file #$opt_f?") if $opt_F;
+
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  $req = "GET $proxy_prefix/fup?"
+        ."from=$from&ID=$sid&to=$to&dkey=$dkey&command=FORWARD";
+  $req .= "&comment=$opt_C"	if $opt_C;
+  $req .= "&keep=$opt_k"	if $opt_k;
+  $req .= "&autodelete=$opt_D"	if $opt_D;
+  $req .= "&$opt_X"		if $opt_X;
+  $req .= " HTTP/1.1";
+  sendheader("$server:$port",$req);
+  http_response();
+  while (<$SH>) { 
+    if ($opt_v) {
+      print;
+      $status = 0 if /\Q"$file"/;
+    } else {
+      if (/\Q"$file"/) {
+        print;
+        $status = 0;
+      }
+    }
+  }
+  
+  if ($status) {
+    die "$0: server failed, rerun command with option -v\n";
+  }
+  exit;
+}
+
+
+sub renotify {
+  my (@r);
+  my ($to,$n,$dkey,$file,$req,$recipient);
+  local $_;
+
+  die $usage if @ARGV;
+
+  open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+  while (<$fexlist>) {
+    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_N) {
+      $n = $1;
+      $dkey = $2;
+      last;
+    }
+  }
+  close $fexlist;
+  
+  unless ($n) {
+    die "$0: file #$opt_N not found in fexlist\n";
+  }
+
+  female_mode("resend notification for file #$opt_N?") if $opt_F;
+
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  $req = "GET $proxy_prefix/fup?"
+        ."from=$from&ID=$sid&dkey=$dkey&command=RENOTIFY"
+        ." HTTP/1.1";
+  sendheader("$server:$port",$req);
+  http_response();
+  while (<$SH>) {
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    last if /^\s*$/;
+    if (/^X-Notify: (.+)\/(.+)\/(.+)/) {
+      $recipient = $1;
+      $file = $3;
+    }
+  }
+  
+  if ($file) {
+    print "notification e-mail for $file has been resent to $recipient\n";
+  } else {
+    if ($opt_v) {
+      die "$0: server failed\n";
+    } else {
+      die "$0: server failed, rerun command with option -v\n";
+    }
+  }
+  
+  exit;
+}
+
+
+sub modify {
+  my (@r);
+  my ($n,$dkey,$file,$req);
+  local $_;
+  
+  die $usage if @ARGV;
+  die $usage unless $opt_C or $opt_k or $opt_D;
+  
+  open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
+  while (<$fexlist>) {
+    if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_x) {
+      $n = $1;
+      $dkey = $2;
+      $file = $3;
+      $file =~ s/ "(.*)"$//;
+      last;
+    }
+  }
+  close $fexlist;
+  
+  unless ($n) {
+    die "$0: file #$opt_x not found in fexlist\n";
+  }
+
+  female_mode("modify file #$opt_x?") if $opt_F;
+  
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  $req = "GET $proxy_prefix/fup?"
+        ."from=$from&ID=$sid&dkey=$dkey&command=MODIFY";
+  $req .= "&comment=$opt_C"	if $opt_C;
+  $req .= "&keep=$opt_k"	if $opt_k;
+  $req .= "&autodelete=$opt_D"	if $opt_D;
+  $req .= " HTTP/1.1";
+  sendheader("$server:$port",$req);
+  http_response();
+  while (<$SH>) { 
+    if ($opt_v) {
+      print "<-- $_";
+    } else {
+      print if /\Q$file/;
+    }
+  }
+  
+  exit;
+}
+
+
+sub get_xx {
+  my $transferfile = shift;
+  my $ft = '';
+  local $_;
+  
+  # get transfer file from FEX server
+  unless ($SH) {
+    serverconnect($server,$port);
+    query_sid($server,$port);
+  }
+  
+  xxget($from,$sid,$transferfile);
+  
+  # empty file?
+  unless (-s $transferfile) {
+    unlink $transferfile;
+    exit;
+  }
+  
+  # no further processing if delivering to pipe
+  exec 'cat',$transferfile unless -t STDOUT;
+  
+  if ($ft = `file $transferfile 2>/dev/null`) {
+    if ($ft =~ /compressed/) {
+      rename $transferfile,"$transferfile.gz";
+      shelldo(ws("gunzip $transferfile.gz"));
+    }
+    $ft = `file $transferfile`;
+  } 
+  # file command failed, so we look ourself into the file...
+  elsif (open $transferfile,$transferfile) {
+    read $transferfile,$_,4;
+    close $transferfile;
+    # gzip magic?
+    if (/\x1F\x8B\x08\x00/) {
+      rename $transferfile,"$transferfile.gz";
+      shelldo(ws("gunzip $transferfile.gz"));
+      # assuming tar
+      $ft = 'tar archive';
+    }
+  }
+  if ($ft =~ /tar archive/) {
+    rename $transferfile,"$transferfile.tar";
+    $transferfile .= '.tar';
+    if ($opt_q) {
+      $_ = 'y';
+    } else {
+      print "Files in transfer-container:\n\n";
+      shelldo(ws("tar tvf $transferfile"));
+      print "\nExtract these files? [Yn] ";
+      $_ = <STDIN>;
+    }
+    if (/^n/i) {
+      print "keeping $transferfile\n";
+    } else {
+      system("tar xvf $transferfile && rm $transferfile");
+      die "$0: error while untaring, see $transferfile\n" if -f $transferfile;
+    }
+  } else {
+    exec 'cat',$transferfile;
+  }
+  exit;
+}
+
+
+sub formdatapost {
+  my %P = @_; 
+  my ($boundary,$filename,$filesize,$length,$buf,$file,$fpsize,$resume,$seek);
+  my ($flink);
+  my (@hh,@hb,@r,@pv,$to);
+  my ($bytes,$t,$bt);
+  my ($t0,$t1,$t2,$tt,$tc);
+  my $bs = 2**16;        # blocksize for reading and sending file
+  my $fileid = int(time);
+  my $chunk = 0;
+  my $connection = '';
+  my $pct = '';
+  my ($tar,$aname,$atype,$tarlist,$tarerror,$location,$transferfile);
+  local $_;
+
+  if (defined($file = $P{file})) {
+    
+    $to = $AB{$P{to}} || $P{to}; # for gpg
+    
+    # special file: stream from STDIN
+    if ($opt_s) {
+      $filename = encode_utf8($file);
+      $filesize = -1;
+    }
+    
+    # compression?
+    if ($opt_c) {
+      my ($if,$of);
+      $if = $file;
+      $if =~ s/([^_\w\.\-])/\\$1/g;
+      $transferfile = $tmpdir . '/' . basename($file) . '.gz';
+      $of = $transferfile;
+      $of =~ s/([^_\w\.\-])/\\$1/g;
+      shelldo("gzip <$if>$of");
+      $filesize = -s $transferfile;
+      die "$0: cannot gzip $file\n" unless $filesize;
+      $file = $transferfile;
+    } 
+    
+    # special file: tar-on-the-fly
+    if (not $windoof and $opt_a and $file =~ /(.+)\.(tar|tgz)$/) {
+      $aname = $1;
+      $atype = $2;
+      $tarlist  = "$tmpdir/$aname.list";
+      $tarerror = "$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-";
+      } else {
+        $tar .= " -f-";
+      }
+      if (${'opt_#'}) {
+        foreach my $x (split('#',${'opt_#'})) {
+          $tar .= " --exclude=$x";
+        }
+      }
+      foreach (@ARGV) {
+        $file = $_;
+        $file =~ s/([^\w\-\@\#%,.=+~_:])/\\$1/g;
+        $tar .= ' '.$file;
+      }
+      # print "calculating archive size... ";
+      open $tar,"$tar 2>$tarerror|" or die "$0: cannot run tar - $!\n";
+      $t0 = int(time) if -t STDOUT;
+      while ($b = read $tar,$_,$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 $tar) {
+        $_ = '';
+        if (open $tarerror,$tarerror) {
+          local $/;
+          $_ = <$tarerror>;
+          close $tarerror;
+        }
+        unlink $tarlist,$tarerror;
+        die "$0: tar error:\n$_";
+      }
+      $file = "$aname.$atype";
+      $filename = encode_utf8($file);
+      undef $SH; # force reconnect (timeout!)
+    } 
+    
+    # single file
+    else {
+      $filename = encode_utf8(${'opt_='} || $file);
+    
+      if ($windoof) {
+        $filename =~ s/^[a-z]://;
+        $filename =~ s/.*\\//;
+      }
+      $filename =~ s:.*/::;
+      $filename =~ s:[\r\n]+: :g;
+      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";
+      }
+    }
+
+    $filename .= '.gpg' if $opt_g;
+
+    unless ($opt_d) {
+      if ($opt_g) {
+        $filesize = -1;
+        $fileid = int(time);
+      } else {
+        if ($opt_a) {
+          $fileid = md5_hex(fmd(@ARGV));
+        } else {
+          $fileid = fileid($file);
+        }
+      }
+    }
+  
+  } else {
+    $file = $filename = '';
+    $filesize = 0;
+  }
+
+  FORMDATAPOST:
+    
+  @hh = (); # HTTP header
+  @hb = (); # HTTP body
+  @r = ();
+  $seek = 0;
+  $resume = '';
+  $chunk++;
+
+  unless ($SH) {
+    serverconnect($server,$port);
+    query_sid($server,$port) unless $anonymous;
+  }
+  
+  $P{id} = $sid; # ugly hack!
+  
+  # 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 ($proxy) {
+      sleep 1;    # do not overrun proxy
+      serverconnect($server,$port);
+    }
+  }
+  
+  # file part size
+  if ($chunksize and $proxy and $port != 443 
+      and $filesize - $seek > $chunksize - $bs) {
+    if ($features !~ /MULTIPOST/) {
+      die sprintf("$0: server does not support chunked multi-POST needed for"
+                  ." files > %d MB via proxy\n",$chunksize/M);
+    }
+    $opt_o = 0; # no overwriting mode for next chunks
+    $fpsize = $chunksize - $bs;
+  } else {
+    $fpsize = $filesize - $seek;
+  }
+
+  $boundary = randstring(48);
+  
+  $P{seek} = $seek;
+  $P{filesize} = $filesize;
+
+  # send HTTP POST variables
+  if ($skey) {
+    $P{skey} = $skey;
+    @pv = qw'from to skey keep autodelete comment seek filesize';
+  } elsif ($gkey) {
+    $P{gkey} = $gkey;
+    @pv = qw'from to gkey keep autodelete comment seek filesize';
+  } else {
+    @pv = qw'from to id replyto keep autodelete comment command seek filesize';
+  }
+  foreach my $v (@pv) {
+    if ($P{$v}) {
+      my $name = uc($v);
+      push @hb,"--$boundary";
+      push @hb,"Content-Disposition: form-data; name=\"$name\"";
+      push @hb,"";
+      push @hb,encode_utf8($P{$v});
+    }
+  }
+  
+  # at last, POST the file
+  if ($file) {
+    push @hb,"--$boundary";
+    push @hb,"Content-Disposition: form-data; name=\"FILE\"; filename=\"$filename\"";
+    unless ($opt_d) {
+      if ($opt_M) { push @hb,"Content-Type: application/x-mime" }
+      else        { push @hb,"Content-Type: application/octet-stream" }
+      if (${'opt_/'}) {
+        $flink = abs_path($file);
+        push @hb,"Content-Location: $flink";
+      } else {
+        # push @hb,"Content-Length: " . ((-s $file||0) - $seek); # optional header!
+        push @hb,"Content-Length: $fpsize"; # optional header! NOT filesize!
+        push @hb,"X-File-ID: $fileid";
+      }
+      push @hb,"";
+    }
+    push @hb,"";
+    # prevent proxy chunked mode reply
+    $connection = "close";
+  }
+
+  push @hb,"--$boundary--";
+
+  if ($fpsize < 0) {
+    $length = $fpsize;
+  } else {
+    $length = length(join('',@hb)) + scalar(@hb)*2 + $fpsize;
+  }
+
+  if ($file and not $opt_d) {
+    if ($flink) { $hb[-2] = $flink }
+    else        { $hb[-2] = '(file content)' }
+  }
+  # any other extra URL arguments
+  my $opt_X = '';
+  $opt_X = "?$::opt_X" if $::opt_X and $file;
+
+  # HTTP header
+  push @hh,"POST $proxy_prefix/fup$opt_X HTTP/1.1";
+  push @hh,"Host: $server:$port";
+  push @hh,"User-Agent: $useragent";
+  push @hh,"Content-Length: $length";
+  push @hh,"Content-Type: multipart/form-data; boundary=$boundary";
+  push @hh,"Connection: $connection" if $connection;
+  push @hh,'';
+
+  if ($opt_v) {
+    print "--> $_\n" foreach (@hh,@hb);
+  }
+
+  $SIG{PIPE} = \&sigpipehandler;
+#    foreach $sig (keys %SIG) {
+#      eval '$SIG{$sig} = sub { print "\n!!! SIGNAL '.$sig.' !!!\n"; exit; }';
+#    }
+
+  if ($file) {
+    pop @hb;
+    pop @hb unless $flink;
+    nvtsend(@hh,@hb) or do {
+      warn "$0: server has closed the connection, reconnecting...\n";
+      sleep 3;
+      goto FORMDATAPOST; # necessary: new $sid ==> new @hh
+    };
+    
+    unless ($opt_d or $flink) {
+      
+      $t0 = $t2 = int(time);
+      $tt = $t0-1;
+      $t1 = 0;
+      $tc = 0;
+      
+      if ($opt_s) {
+        if ($opt_g) {
+          open $file,"gpg -e -r $to|" or die "$0: cannot run gpg - $!\n";
+        } else {
+          open $file,'>&=STDIN' or die "$0: cannot open STDIN - $!\n";
+        }
+      } elsif ($tar) {
+        if ($opt_g) {
+          open $file,"$tar|gpg -e -r $to|" or die "$0: cannot run tar&gpg - $!\n";
+        } else {
+          open $file,"$tar|" or die "$0: cannot run tar - $!\n";
+        }
+        if (-t STDOUT) {
+          $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>) {
+                  print ' 'x(length($file)+40),"\r",$_;
+                }
+                sleep 1;
+              }
+            }
+            exit;
+          }
+          $SIG{CHLD} = 'IGNORE';
+        }
+        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;
+          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";
+          seek $file,$seek,0;
+        }
+        binmode $file;
+      }
+      
+      $bytes = 0;
+      autoflush $SH 0;
+      
+      print $rcamel[0] if ${'opt_+'};
+
+      while (my $b = read $file,$buf,$bs) {
+        print {$SH} $buf or &sigpipehandler;
+        $bytes += $b;
+        if ($filesize > 0 and $bytes+$seek > $filesize) {
+          die "$0: $file filesize has grown while uploading\n";
+        }
+        $bt += $b;
+        $t2 = time;
+        if (${'opt_+'} and int($t2*10)>$tc) {
+          print $rcamel[$tc%2+1];
+          $tc = int($t2*10);
+        }
+        if (not $opt_q and -t STDOUT and int($t2)>$t1) {
+          &sigpipehandler unless $SH->connected;
+          # smaller block size is better on slow links
+          $bs = 4096 if $t1 and $bs>4096 and $bytes/($t2-$t0)<65536;
+          if ($filesize > 0) {
+            $pct = sprintf "(%d%%)",int(($bytes+$seek)/$filesize*100);
+          }
+          if ($bytes>2*M and $bs>4096) {
+            printf STDERR "%s: %d MB of %d MB %s %d kB/s        \r",
+                   $opt_s||$opt_a||$file,
+                   int(($bytes+$seek)/M),
+                   int($filesize/M),
+                   $pct,
+                   int($bt/k/($t2-$tt));
+          } else {
+            printf STDERR "%s: %d kB of %d MB %s %d kB/s        \r",
+                   $opt_s||$opt_a||$file,
+                   int(($bytes+$seek)/k),
+                   int($filesize/M),
+                   $pct,
+                   int($bt/k/($t2-$tt));
+          }
+          $t1 = $t2;
+          # time window for transfer rate calculation
+          if ($t2-$tt>10) {
+            $bt = 0;
+            $tt = $t2;
+          }
+        }
+        last if $filesize > 0 and $bytes >= $fpsize;
+        sleep 1 while ($opt_m and $bytes/k/(time-$t0||1) > $opt_m);
+      }
+      close $file; # or die "$0: error while reading $file - $!\n";
+      $tt = ($t2-$t0)||1;
+      
+      print $rcamel[2] if ${'opt_+'};
+      
+      # terminate tar verbose output job
+      if ($tpid) {
+        sleep 2;
+        kill 9,$tpid;
+        unlink $tarlist;
+      }
+    
+      unless ($opt_q) {
+        if (not $chunksize and $bytes+$seek < $filesize) {
+          die "$0: $file filesize has shrunk while uploading\n";
+        }
+        
+        if ($seek or $chunksize and $chunksize < $filesize) {
+          if ($fpsize>2*M) {
+            printf STDERR "%s: %d MB in %d s (%d kB/s)",
+                           $opt_s||$opt_a||$file,
+                           int($bytes/M),
+                           $tt,
+                           int($bytes/k/$tt);
+            if ($bytes+$seek == $filesize) {
+              printf STDERR ", total %d MB\n",int($filesize/M);
+            } else {
+              printf STDERR ", chunk #%d : %d MB\n",
+                            $chunk,int(($bytes+$seek)/M);
+            }
+          } else {
+            printf STDERR "%s: %d kB in %d s (%d kB/s)",
+                          $opt_s||$opt_a||$file,
+                          int($bytes/k),
+                          $tt,
+                          int($bytes/k/$tt);
+            if ($bytes+$seek == $filesize) {
+              printf STDERR ", total %d kB\n",int($filesize/k);
+            } else {
+              printf STDERR ", chunk #%d : %d kB\n",
+                            $chunk,int(($bytes+$seek)/k);
+            }
+          }
+        } else {
+          if ($bytes>2*M) {
+            printf STDERR "%s: %d MB in %d s (%d kB/s)        \n",
+                          $opt_s||$opt_a||$file,
+                          int($bytes/M),
+                          $tt,
+                          int($bytes/k/$tt);
+          } else {
+            printf STDERR "%s: %d kB in %d s (%d kB/s)        \n",
+                          $opt_s||$opt_a||$file,
+                          int($bytes/k),
+                          $tt,
+                          int($bytes/k/$tt);
+          }
+        }
+        
+        if (-t STDOUT and not ($opt_s or $opt_g)) {
+          print STDERR "waiting for server ok..."
+        }
+      }
+    }
+    
+    autoflush $SH 1;
+    print {$SH} "\r\n--$boundary--\r\n";
+
+    # special handling of streaming file because of stunnel tcp shutdown bug
+    if ($opt_s or $opt_g) {
+      close $SH;
+      sleep 1;
+      serverconnect($server,$port);
+      query_sid($server,$port) unless $anonymous;
+      ($seek,$location) = query_file($server,$port,$P{to},$P{from},$sid,
+                                     $filename,$fileid);
+      if ($seek != $bytes) {
+        die "$0: streamed $bytes bytes but server received $seek bytes\n";
+      }
+      return "X-Location: $location\n";
+    }
+    
+    if ($flink) {
+      $bytes = -s $flink;
+      if ($bytes>2*M) {
+        printf STDERR "%s: %d MB\n",$flink,int($bytes/M);
+      } else {
+        printf STDERR "%s: %d kB\n",$flink,int($bytes/k);
+      }
+    }
+  } else {
+    autoflush $SH 1;
+    nvtsend(@hh,@hb);
+  }
+
+  # SuSe: Can't locate object method "BINMODE" via package "IO::Socket::SSL::SSL_HANDLE"
+  # binmode $SH,':utf8'; 
+  
+  if (not $opt_q and $file and -t STDOUT) {
+    print STDERR "\r                         \r";
+  }
+  while (<$SH>) {
+    s/[\r\n]+//;
+    print "<-- $_\n" if $opt_v;
+    last if @r and $r[0] =~ / 204 / and /^$/ or /<\/html>/i;
+    push @r,decode_utf8($_);
+  }
+  
+  if ($file) {
+    close $SH;
+    undef $SH;
+    if ($proxy and $fpsize+$seek < $filesize) {
+      goto FORMDATAPOST;
+    }
+  }
+  
+  return @r;
+}
+
+
+sub randstring {
+    my $n = shift;
+    my @rc = ('A'..'Z','a'..'z',0..9 );
+    my $rn = @rc;
+    my $rs;
+
+    for (1..$n) { $rs .= $rc[int(rand($rn))] };
+    return $rs;
+}
+
+
+sub zipsplit {
+  my $zipbase = shift;
+  my @files = @_;
+  my @zipfiles = ();
+  my $file;
+  my ($zsize,$size,$n);
+
+  $zipbase =~ s/\.zip$//;
+  map { s/([^_\w\+\-\.])/\\$1/g } @files;
+
+  open my $ff,"find @files|" or die "$0: cannot search for @_ - $!\n";
+  @files = ();
+
+  zipfile: for (;;) {
+    $n++;
+    if ($n eq 10) {
+      unlink @zipfiles;
+      die "$0: too many zip-archives\n";
+    }
+    $zsize = 0;
+    while ($file = <$ff>) {
+      chomp $file;
+      # next if -l $file or not -f $file;
+      next unless -f $file;
+      $size = -s $file;
+      if ($size > 2147480000) {
+        unlink @zipfiles;
+        die "$0: $file too big for zip\n";
+      }
+      if ($zsize + $size > 2147000000) {
+        push @zipfiles,zip($zipbase.'_'.$n.'.zip',@files);
+        @files = ($file);
+        next zipfile;
+      } else {
+        push @files,$file;
+        $zsize += $size;
+      }
+    }
+    close $ff;
+    last;
+  }
+  push @zipfiles,zip($zipbase.'_'.$n.'.zip',@files);
+  return @zipfiles;
+}
+
+
+sub zip {
+  no strict 'refs';
+  my $zip = shift;
+  my $cmd;
+  local $_;
+
+  unlink $zip;
+  # if ($opt_c) { $cmd = "zip -@ $zip" }
+  # else        { $cmd = "zip -0 -@ $zip" }
+  $cmd = "zip -@ $zip";
+  if (${'opt_#'}) {
+    ${'opt_#'} =~ s/#/ /g;
+    $cmd .= " -x ".${'opt_#'};
+  }
+  print $cmd,"\n" if $opt_v;
+  open $cmd,"|$cmd" or die "$0: cannot create $zip - $!\n";
+  foreach (@_) { 
+    print {$cmd} $_."\n";
+    print "  $_\n" if $opt_v;
+  }
+  close $cmd or die "$0: zip failed - $!\n";
+
+  return $zip;
+}
+
+
+sub getline {
+  my $file = shift;
+  local $_;
+  
+  while (<$file>) {
+    chomp;
+    s/^#.*//;
+    s/\s+#.*//;
+    s/^\s+//;
+    s/\s+$//;
+    return $_ if length($_);
+  }
+  return '';
+}
+
+
+sub query_file {
+  my ($server,$port,$to,$from,$id,$filename,$fileid) = @_;
+  my $seek = 0;
+  my $qfileid = '';
+  my ($head,$location);
+  my ($response,$fexsrv);
+  local $_;
+  
+  $to =~ s/,.*//;
+  $to =~ s/:\w+=.*//;
+  $to = $AB{$to} if $AB{$to};
+  $filename =~ s/([^_=:,;<>()+.\w\-])/'%'.uc(unpack("H2",$1))/ge; # urlencode
+  if ($skey) {
+    $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??SKEY=$id HTTP/1.1";
+  } elsif ($gkey) {
+    $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??GKEY=$id HTTP/1.1";
+  } else {
+    $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??ID=$id HTTP/1.1";
+  }
+  sendheader("$server:$port",$head);
+  $_ = <$SH>;
+  unless (defined $_ and /\w/) {
+    die "$0: no response from server\n";
+  }
+  s/\r//;
+  print "<-- $_" if $opt_v;
+  unless (/^HTTP.* 200/) {
+    s:HTTP/[\d\. ]+::;
+    $response = $_;
+    while (<$SH>) {
+      s/\r//;
+      print "<-- $_" if $opt_v;
+      $fexsrv = $_ if /^(Server: fexsrv|X-Features:)/;
+      last if /^\s*$/;
+    }
+    die "$0: no fexserver at $server:$port\n" unless $fexsrv;
+    die "$0: server response: $response";
+  }
+  while (<$SH>) {
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    last if /^$/;
+    if (/^Content-Length:\s+(\d+)/)	{ $seek = $1 }
+    if (/^X-File-ID:\s+(.+)/)		{ $qfileid = $1 }
+    if (/^X-Features:\s+(.+)/)		{ $features = $1 }
+    if (/^X-Location:\s+(.+)/)		{ $location = $1 }
+  }
+
+  # return true seek only if file is identified
+  $seek = 0 if $qfileid and $qfileid ne $fileid;
+  
+  return ($seek,$location);
+}
+
+
+sub edit_address_book {
+  my ($user) = @_;
+  my $alias;
+  my $ab = "$fexhome/ADDRESS_BOOK";
+  my (%AB,@r);
+  local $_;
+  
+  die "$0: address book not available for subusers\n"      if $skey;
+  die "$0: address book not available for group members\n" if $gkey;
+
+  female_mode("edit your address book?") if $opt_F;
+
+  %AB = query_address_book($server,$port,$user);
+  if ($AB{ADDRESS_BOOK} !~ /\w/) {
+    $AB{ADDRESS_BOOK} = 
+      "# Format: alias e-mail-address # Comment\n".
+      "# Example:\n".
+      "framstag framstag\@rus.uni-stuttgart.de\n";
+  }
+  open $ab,">$ab" or die "$0: cannot write to $ab - $!\n";
+  print {$ab} $AB{ADDRESS_BOOK};
+  close $ab;
+  
+  system $editor,$ab;
+  exit unless -s $ab;
+
+  $opt_o = $opt_A;
+  
+  serverconnect($server,$port);
+  query_sid($server,$port);
+  
+  @r = formdatapost(
+ 	from		=> $user,
+        to		=> $user,
+        id		=> $sid,
+        file		=> $ab,
+  );
+  
+  unlink $ab,$ab.'~';
+}
+
+
+sub query_address_book {
+  my ($server,$port,$user) = @_;
+  my ($req,$alias,$address,$options,$comment,$cl,$ab,$b);
+  my %AB;
+  local $_;
+
+  unless ($SH) {
+    serverconnect($server,$port);
+    query_sid($server,$port);
+  }
+  
+  $req = "GET $proxy_prefix/fop/$user/$user/ADDRESS_BOOK?ID=$sid HTTP/1.1";
+  sendheader("$server:$port",$req);
+  $_ = <$SH>;
+  unless (defined $_ and /\w/) {
+    die "$0: no response from server\n";
+  }
+  s/\r//;
+  print "<-- $_" if $opt_v;
+  unless (/^HTTP.* 200/) {
+    if (/^HTTP.* 404/) {
+      while (<$SH>) { last if /^\r?\n/ }
+      return;
+    } else {
+      # s:HTTP/[\d\. ]+::;
+      # die "$0: server response: $_";
+      close $SH;
+      undef $SH;
+      return ();
+    }
+  }
+  while (<$SH>) {
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    last if /^$/;
+    $cl = $1 if /^Content-Length: (\d+)/;
+  }
+  
+  if ($cl) {
+    while (<$SH>) {
+      $b += length;
+      $ab .= $_;
+      s/[\r\n]//g;
+      s/^\s+//;
+      s/\s+$//;
+      print "<-- $_\n" if $opt_v;
+      s/\s*#\s*(.*)//;
+      if ($_) {
+        $comment = $1||'';
+        ($alias,$address,$options) = split;
+        if ($address) {
+          if ($options) { $options =~ s/[()]//g }
+          else          { $options = '' }
+          $AB{$alias} = $address;
+          $AB{$alias}->{options} = $options||'';
+          $AB{$alias}->{comment} = $comment||'';
+          if ($options and $options =~ /keep=(\d+)/i) {
+            $AB{$alias}->{keep} = $1;
+          }
+          if ($options and $options =~ /autodelete=(\w+)/i) {
+            $AB{$alias}->{autodelete} = $1;
+          }
+        }
+      }
+      last if $b >= $cl;
+    }
+  }
+  
+  $AB{ADDRESS_BOOK} = $ab;
+  
+  return %AB;
+}
+
+
+# sets global $sid $features $timeout # ugly hack! :-}
+sub query_sid {
+  my ($server,$port) = @_;
+  my ($req,$fexsrv);
+  local $_;
+
+  $sid = $id;
+
+  if ($port eq 443) {
+    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";
+  } else {
+    $req = "GET SID HTTP/1.1";
+  }
+
+  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;
+    
+  if (/^HTTP.* [25]0[01] /) {
+    if (not $proxy and $port ne 443 and /^HTTP.* 201 (.+)/) {
+      $sid = 'MD5H:'.md5_hex($id.$1);
+    }
+    while (<$SH>) {
+      s/\r//;
+      print "<-- $_" if $opt_v;
+      $features = $1 if /^X-Features: (.+)/;
+      $timeout = $1  if /^X-Timeout: (\d+)/;
+      last if /^\n/;
+    }
+  } elsif (/^HTTP.* 301 /) {
+    while (<$SH>) { last if /Location/ }
+    die "$0: cannot use $server:$port because server has a redirection to\n".$_;
+  } else {
+    # no SID support - perhaps transparent web proxy?
+    while (<$SH>) {
+      s/\r//;
+      print "<-- $_" if $opt_v;
+      $fexsrv = $_ if /^(Server: fexsrv|X-Features:)/;
+      last if /^\s*$/;
+    }
+    die "$0: no fexserver at $server:$port\n" unless $fexsrv;
+    serverconnect($server,$port);
+    $sid = $id;
+  }
+  
+  # warn "proxy: $proxy\n";
+  if ($proxy) {
+    serverconnect($server,$port);
+    $sid = $id;
+  }
+  
+}
+
+
+sub xxget {
+  my ($from,$id,$save) = @_;
+  my $bs = 4096;
+  my $xx = $save;
+  my ($url,$B,$b,$t0,$t1,$cl);
+  my ($ts,$tso);
+  local $_;
+
+  $xx =~ s:.*/::;
+  $url = "$proxy_prefix/fop/$from/$from/$xx?ID=$id";
+
+  sendheader("$server:$port","GET $url HTTP/1.0","User-Agent: $useragent");
+  http_response();
+  while (<$SH>) {
+    s/\r//;
+    print "<-- $_" if $opt_v;
+    $cl = $1 if /^Content-Length:\s(\d+)/;
+    # $ft = $1 if /^X-File-Type:\s(.+)/;
+    last if /^$/;
+  }
+
+  die "$0: no Content-Length in server-reply\n" unless $cl;
+  
+  open F,">$save" or die "$0: cannot write to $save - $!\n";
+  binmode F;
+  
+  $t0 = $t1 = int(time);
+  $tso = '';
+  
+  while ($b = read($SH,$_,$bs)) {
+    $B += $b;
+    print F;
+    if (int(time) > $t1) {
+      $t1 = int(time);
+      $ts = ts($B,$cl);
+      if ($ts ne $tso) {
+        print STDERR $ts,"\r";
+        $tso = $ts;
+      }
+    }
+    sleep 1 while ($opt_m and $B/k/(time-$t0||1) > $opt_m);
+  }
+  
+  print STDERR ts($B,$cl),"\n";
+  close F;
+}
+
+
+# transfer status
+sub ts {
+  my ($b,$tb) = @_;
+  return sprintf("transferred: %d MB (%d%%)",int($b/M),int($b/$tb*100));
+}
+  
+
+sub sigpipehandler {
+  $SIG{ALRM} = sub { };
+  if (fileno $SH) {
+    alarm(1);
+    @_ = <$SH>;
+    alarm(0);
+    kill 9,$tpid if $tpid;
+    if (@_ and $opt_v) {
+      die "\n$0: ($$) server error: @_\n";
+    }
+    if (@_ and $_[0] =~ /^HTTP.* \d+ (.*)/) {
+      die "\n$0: server error: $1\n";
+    }
+  }
+  $timeout *= 2;
+  warn "\n$0: connection to $server died\n";
+  warn "retrying after $timeout seconds...\n";
+  sleep $timeout;
+  if ($windoof) { exec $^X,$0,@_ARGV }
+  else          { exec $_0,@_ARGV }
+  die $!;
+}
+
+
+sub checkrecipient {
+  my ($from,$to) = @_;
+  my @r;
+  local $_;
+  
+  @r = formdatapost(
+	from	=> $from,
+        to	=> $to,
+        id	=> $sid,
+        command	=> 'CHECKRECIPIENT',
+  );
+
+  $_ = shift @r or die "$0: no reply from server\n";
+
+  if (/ 2\d\d /) {
+    foreach (@r) {
+      last if /^$/;
+      if (s/X-(Recipient: .+)/$1\n/) {
+        s/autodelete=\w+/autodelete=$opt_D/ if $opt_D;
+        s/keep=\d+/keep=$opt_k/             if $opt_k;
+        print;
+        $frecipient ||= (split)[1];
+      }
+    }
+  } else {
+    http_response($_,@r);
+  }
+}
+
+
+# get ID data from ID file
+sub get_id {
+  my $idf = shift;
+
+  $fexcgi = getline($idf) || die "$0: no FEX-URL in $idf\n";
+  $from   = getline($idf) || die "$0: no FROM in $idf\n";
+  $id     = getline($idf) || die "$0: no ID in $idf\n";
+  if ($fexcgi =~ s/!([\w.-]+:\d+)(:(\d+))?//) {
+    $proxy = $1;
+    $chunksize = $3 || 0;
+  }
+  unless ($fexcgi =~ /^[_:=\w\-\.\/\@\%]+$/) {
+    die "$0: illegal FEX-URL \"$fexcgi\" in $idf\n";
+  }
+  unless ($from =~ /^[_:=\w\-\.\/\@\%\+]+$/) {
+    die "$0: illegal FROM \"$from\" in $idf\n";
+  }
+  $fexcgi =~ s:/+$::;
+}
+
+
+# for windows
+sub inquire {
+  my ($file,$to);
+  for (;;) {
+    print "file to send: ";
+    chomp($file = <STDIN>);
+    $file =~ s/^\"//;
+    $file =~ s/\"$//;
+    last if -e $file;
+    warn "$file does not exist\n";
+  }
+  print "recipient (e-mail address): ";
+  chomp($to = <STDIN>);
+  die $usage unless $to;
+  unless ($opt_n) {
+    print "comment: ";
+    chomp($opt_C = <STDIN>);
+  }
+  @ARGV = ($file,$to);
+}
+
+
+sub shelldo {
+  if (system(@_) < 0) { die "failed: @_\n" }
+}
+
+
+# emulate seek on a pipe
+sub readahead {
+  my $fh = shift; # filehandle
+  my $ba = shift; # bytes ahead
+  my $bs = 2**16;
+  my $s = 0;
+  my $n;
+  local $_;
+  
+  while ($s < $ba) { 
+    $n = $ba-$s;
+    $n = $bs if $n > $bs; 
+    $s += read $fh,$_,$n; 
+  }
+}
+
+
+# fileid is inode and mtime
+sub fileid {
+  my @s = stat(shift);
+  return @s ? $s[1].$s[9] : int(time);
+}
+
+
+# collect file meta data (filename, inode, mtime)
+sub fmd {
+  my @files = @_;
+  my ($file,$dir);
+  my $fmd = '';
+  
+  foreach $file (@files) {
+    if (not -l $file and -d $file) {
+      $dir = $file;
+      if (opendir $dir,$dir) {
+        while (defined ($file = readdir($dir))) {
+          next if $file eq '..';
+          if ($file eq '.') {
+            $fmd .= $file.fileid($dir);
+          } else {
+            $fmd .= fmd("$dir/$file");
+          }
+        }
+        closedir $dir;
+      }
+    } else {
+      $fmd .= $file.fileid($file);
+    }
+  }
+  
+  return $fmd;
+}
+
+
+# from MIME::Base64::Perl
+sub decode_b64 {
+  local $_ = shift;
+  my $uu = '';
+  my ($i,$l);
+  
+  tr|A-Za-z0-9+=/||cd;
+  s/=+$//;
+  tr|A-Za-z0-9+/| -_|;
+  return "" unless length;
+
+  $l = (length)-60;
+  for ($i = 0; $i <= $l; $i += 60) {
+    $uu .= "M" . substr($_,$i,60);
+  }
+  $_ = substr($_,$i);
+  if (length) {
+    $uu .= chr(32+(length)*3/4) . $_;
+  }
+  return unpack("u",$uu);
+}
+
+
+sub female_mode {
+  local $_;
+  if (open my $tty,'/dev/tty') {
+    print "@_\n";
+    print "  [y] yes\n",
+          "  [n] no\n",
+          "  [p] perhaps - don't know\n",
+          "your choice: ";
+    $_ = <$tty> || '';
+    close $tty;
+    if (/^y/i) { return }
+    if (/^n/i) { exit }
+    if (/^p/i) { int(rand(2)) ? return : exit }
+    female_mode(@_);
+  }
+}
+
+
+sub http_response {
+  local $_ = shift || <$SH>;
+  my @r = @_;
+  my $error;
+
+  $_ = <$SH> unless $_;
+  unless (defined $_ and /\w/) {
+    die "$0: no response from server\n";
+  }
+  s/\r?\n//;
+  # CGI fatalsToBrowser
+  if (/^HTTP.* 500/) {
+    @r = <$SH> unless @r;
+    @r = ()    unless @r;
+    die "$0: server error: $_\n@r\n";
+  }
+  unless (/^HTTP.* 200/) {
+    $error = $_;
+    $error =~ s/HTTP.[\s\d.]+//;
+    if ($opt_v) {
+      print "<-- $_";
+      print "<-- $_" while <$SH>;
+    }
+    die "$0: server error: $error\n";
+  }
+
+  print "<-- $_\n" if $opt_v;
+  return $_;
+}
+
+
+sub ws {
+  local $_ = shift;
+  return split;
+}
+
+
+sub update {
+  my $cfb = '### common functions ###';
+  my $cfc;
+  
+  local $/;
+  
+  open $0,$0 or die "cannot read $0 - $!\n";
+  $_ = <$0>;
+  close $0;
+  s/.*\n$cfb\n//s;
+  $cfc = $_;
+  
+  foreach my $p (qw(fexget sexsend)) {
+    open $p,$p or die "cannot read $p - $!\n";
+    $_ = <$p>;
+    close $p;
+    s/\n$cfb.*/\n$cfb\n$cfc/s;
+    system "vv -s $p";
+    open $p,'>',$p or die "cannot write $p - $!\n";
+    print {$p} $_;
+    close $p;
+  }
+
+  exec "l $0 fexget sexsend";
+  exit;
+}
+
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
+sub urldecode {
+  local $_ = shift;
+  s/\%([a-f\d]{2})/chr(hex($1))/ige;
+  return $_;
+}
+
+
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  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) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
+sub encode_b64 {
+  my $res = "";
+  my $eol = "\n";
+  my $padding;
+  
+  pos($_[0]) = 0;
+  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+  $res =~ tr|` -_|AA-Za-z0-9+/|;
+  $padding = (3-length($_[0])%3)%3;
+  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+  return $res;
+}
diff -Nru fex-20140917/htdocs/download/sexget fex-20150120/htdocs/download/sexget
--- fex-20140917/htdocs/download/sexget	1970-01-01 01:00:00.000000000 +0100
+++ fex-20150120/htdocs/download/sexget	2015-01-19 13:59:57.000000000 +0100
@@ -0,0 +1,723 @@
+#!/usr/bin/perl -w
+
+# client for stream exchange of the FEX service
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Perl Artistic Licence
+
+# sexsend / sexget / sexxx
+
+use Getopt::Std;
+use Socket;
+use IO::Handle;
+use IO::Socket::INET;
+use Digest::MD5 qw(md5_hex);  # encypted ID / SID 
+
+use constant k => 2**10;
+use constant M => 2**20;
+
+eval 'use Net::INET6Glue::INET_is_INET6';
+
+our $version = 20150120;
+
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
+
+$0 =~ s:.*/::;
+$| = 1;
+
+# sexsend is default
+$usage = 
+  "usage: ... | $0 [options] [SEX-URL/]recipient [stream]\n".
+  "options: -v           verbose mode\n".
+  "         -g           show transfer rate\n".
+  "         -V           show version\n".
+  "         -t timeout   timeout in s (waiting for recipient)\n".
+  "special: recipient may be \"public\" or \"anonymous\" or \".\"\n".
+  "see also: sexget, sexxx\n".
+  "example: tail -f /var/log/syslog | $0 fex.flupp.org/admin log\n";
+
+if ($0 eq 'sexget' or $0 eq 'fuckme') {
+  $usage = 
+    "usage: $0 [options] [[SEX-URL/]user:ID] [stream]\n".
+    "options: -v           verbose mode\n".
+    "         -g           show transfer rate\n".
+    "         -V           show version\n".
+    "arguments: user:ID    use this user & ID\n".
+    "                      (ID may be \"public\" or user:ID may be \"anonymous\")\n".
+    "           stream     name of the stream\n".
+    "see also: sexsend, sexxx\n".
+    "example: $0 log | grep kernel\n";
+}
+
+if ($0 eq 'sexxx') {
+  $usage = 
+    "usage: $0 [-v] [-g] [-c] [-u [SEX-URL/]user] [-s stream] [files...]\n".
+    "usage: $0 [-v] [-g]      [-u [SEX-URL/]user] [-s stream] | ...\n".
+    "options: -v               verbose mode\n".
+    "         -g               show transfer rate\n".
+    "         -q               quiet mode\n".
+    "         -c               compress files\n".
+    "         -u SEX-URL/user  SEX-URL and user (default: use FEXID/FEXXX)\n".
+    "         -s stream        stream name (default: xx)\n".
+    "see also: sexsend, sexget\n".
+    "examples: $0 -s config /etc /usr/local/etc\n".
+    "          $0 > backup.tar\n";
+}
+
+$fexhome = $ENV{FEXHOME} || $ENV{HOME}.'/.fex';
+$user = $id = '';
+$type = $timeout = $stream = $mode = '';
+$idf = "$fexhome/id";
+$bs = $ENV{BS} || 2**16; # I/O blocksize
+
+# server URL, user and auth-ID
+if ($FEXID = $ENV{FEXID}) {
+  $FEXID = decode_b64($FEXID) if $FEXID !~ /\s/;
+  ($fexcgi,$user,$id) = split(/\s+/,$FEXID);
+} else {
+  if (open $idf,$idf) {
+    chomp($fexcgi = <$idf>) or die "$0: no FEX-URL in $idf\n";
+    chomp($user = <$idf>)   or die "$0: no FROM in $idf\n";
+    chomp($id = <$idf>)     or die "$0: no ID in $idf\n";
+    close $idf;
+    despace($fexcgi,$user,$id);
+    unless ($fexcgi =~ /^[_:=\w\-\.\/\@\%]+$/) {
+      die "$0: illegal FEX-URL \"$fexcgi\" in $idf\n";
+    }
+    unless ($user =~ /^[_:=\w\-\.\/\@\%\+]+$/) {
+      die "$0: illegal FROM \"$user\" in $idf\n";
+    }
+  }
+}
+
+$opt_h = $opt_v = $opt_V = $opt_q = 0;
+$opt_u = $opt_s = $opt_c = $opt_t = '';
+
+$_ = "$fexhome/config.pl"; require if -f;
+
+if ($0 eq 'sexxx') {
+  
+  # xx server URL, user and auth-ID
+  if ($FEXXX = $ENV{FEXXX}) {
+    $FEXXX = decode_b64($FEXXX) if $FEXXX !~ /\s/;
+    ($fexcgi,$user,$id) = split(/\s+/,$FEXXX);
+  } elsif (open $idf,$idf) {
+    while (<$idf>) {
+      if (/^\[xx\]/) {
+        chomp($fexcgi = <$idf>) or die "$0: no xx FEX-URL in $idf\n";
+        chomp($user = <$idf>)   or die "$0: no xx FROM in $idf\n";
+        chomp($id = <$idf>)     or die "$0: no xx ID in $idf\n";
+        last;
+      }
+    }
+    close $idf;
+  }
+  
+  getopts('hgvcu:s:') or die $usage;
+  die $usage if $opt_h;
+  die $usage unless -t;
+
+  if ($opt_c) {
+    $opt_c = 'z';
+    $type = '&type=GZIP';
+  }
+
+  if ($opt_u) {
+    $fexcgi = $1 if $opt_u =~ s:(.+)/::;
+    $user = $opt_u;
+  }
+
+  unless ($fexcgi) {
+    die "$0: no xx user found, use \"$0 -u SEX-URL/user\"\n";
+  }
+
+  unless ($user) {
+    die "$0: no xx user found, use \"$0 -u user\"\n";
+  }
+  
+} elsif ($0 eq 'sexget' or $0 eq 'fuckme') {
+  getopts('hgvVdu:') or die $usage;
+  die $usage if $opt_h;
+
+
+  if ($opt_V) {
+    print "Version: $version\n";
+    exit unless @ARGV;
+  }
+  
+  if (not $opt_u and @ARGV and $ARGV[0] =~ m{^anonymous|/|:}) {
+    $opt_u = shift @ARGV;
+  }
+  
+  if ($opt_u) {
+    $fexcgi = $1 if $opt_u =~ s:(.+)/::;
+    ($user,$id) = split(':',$opt_u);
+    if ($user =~ /^anonymous/) {
+      $anonymous = $user;
+    } elsif (not $id) {
+      die $usage;
+    }
+  }
+
+  unless ($fexcgi) {
+    die "$0: no SEX URL found, use \"$0 -u SEX-URL/recipient\" or \"fexsend -I\"\n";
+  }
+  
+  unless ($user) {
+    die "$0: no recipient found, use \"$0 -u SEX-URL/recipient\" or \"fexsend -I\"\n";
+  }
+  
+} else { # sexsend
+  
+  $opt_g = 1;
+  getopts('hguvqVTt:') or die $usage;
+  die $usage if $opt_h;
+
+  if ($opt_V) {
+    print "Version: $version\n";
+    exit unless @ARGV;
+  }
+  
+  if ($opt_t and $opt_t =~ /^\d+$/) {
+    $timeout = "&timeout=$opt_t";
+  }
+
+  my $save_user = $user;
+  $user = shift or die $usage;
+  $fexcgi = $1 if $user =~ s:(.+)/::;
+  
+  if ($user =~ /^anonymous/) {
+    die "$0: need SEX-URL with anonymous SEX\n" unless $fexcgi;
+    $mode = 'anonymous';
+  } elsif ($user eq 'public') {
+    unless ($id) {
+      die "$0: public SEX not possible without FEXID, set it with \"fexsend -I\"\n";
+    }
+    $mode = $user;
+    $user = $save_user;
+  } elsif ($user eq '.') {
+    open $idf,$idf or die "$0: no $idf\n";
+    $_ = <$idf>;
+    $user = <$idf>||'';
+    chomp $user;
+  } else {
+    unless ($fexcgi) {
+      die "$0: no SEX URL found, use \"$0 SEX-URL/recipient\" or \"fexsend -I\"\n";
+    }
+  }
+  
+}
+
+&get_ssl_env;
+
+$fexcgi =~ s(^http://)()i;
+$fexcgi =~ s(/fup.*)();
+$server = $fexcgi;
+
+if    ($server =~ s(^https://)()i) { $port = 443 } 
+elsif ($server =~ /:(\d+)/)        { $port = $1 } 
+else                               { $port = 80 }    
+
+$server =~ s([:/].*)();
+
+## set up tcp/ip connection
+# $iaddr = gethostbyname($server) 
+#          or die "$0: cannot find ip-address for $server $!\n";
+# socket(SH,PF_INET,SOCK_STREAM,getprotobyname('tcp')) or die "$0: socket $!\n";
+# connect(SH,sockaddr_in($port,$iaddr)) or die "$0: connect $!\n";
+# warn "connecting $server:$port user=$user\n";
+if ($port == 443) {
+  if ($opt_v and %SSL) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
+  eval "use IO::Socket::SSL";
+  die "$0: cannot load IO::Socket::SSL\n" if $@;
+  $SH = IO::Socket::SSL->new(                                                  
+    PeerAddr => $server,                                                       
+    PeerPort => $port,                                                         
+    Proto    => 'tcp',
+    %SSL
+  );                                                                           
+} else {                                                                       
+  $SH = IO::Socket::INET->new(
+    PeerAddr => $server,
+    PeerPort => $port,
+    Proto    => 'tcp',                                                         
+  );                                                                           
+}
+
+die "cannot connect $server:$port - $!\n" unless $SH;                          
+warn "TCPCONNECT to $server:$port\n" if $opt_v;
+
+# autoflush $SH 1;
+autoflush STDERR;
+
+$SIG{PIPE} = \&sigpipehandler;
+
+if ($0 eq 'sexget' or $0 eq 'fuckme') {
+  $stream = "&stream=" . shift if @ARGV;
+  if ($anonymous) {
+    $cid = 'anonymous';
+  } elsif ($id eq 'public') {
+    $cid = 'public';
+  } else {
+    $cid = query_sid($server,$port,$id);
+  }
+  request("GET /sex?BS=$bs&user=$user&ID=$cid$stream HTTP/1.0");
+  transfer($SH,STDOUT);
+  # print while sysread $SH,$_,$bs;
+  exit;
+}
+
+if ($0 eq 'sexxx') {
+  $stream = "&stream=" . ($opt_s || 'xx');
+  if (@ARGV) {
+    warn "streaming:\n";
+    open my $tar,'-|','tar',"cv${opt_c}f",'-',@ARGV or die "$0: cannot run tar - $!\n";
+    request("POST /sex?BS=$bs&user=$user$type$stream HTTP/1.0");
+    transfer($tar,$SH);
+    # while (read $tar,$_,$bs) { syswrite $SH,$_ }
+  } else {
+    $cid = query_sid($server,$port,$id);
+    request("GET /sex?BS=$bs&user=$user&ID=$cid$stream HTTP/1.0");
+    $opt_c = 'z' if $H{'CONTENT-TYPE'} =~ /gzip/i;
+    if (-t STDOUT) {
+      print "extracting from stream:\n";
+      open $out,"|tar xv${opt_c}f -" or die "$0: cannot run tar - $!\n";
+    } else {
+      if ($opt_c) {
+        open $out,"|gzip -d" or die "$0: cannot run gunzip - $!\n";
+      } else {
+        $out = *STDOUT;
+      }
+    }
+    print {$out} $_ while sysread $SH,$_,$bs;
+  }
+  exit;
+}
+
+# sexsend
+$stream = "&stream=" . shift if @ARGV;
+
+if ($mode eq 'anonymous') {
+  unless ($opt_q) {
+    print "http://$server:$port/sex?user=$user&ID=anonymous$stream\n";;
+    printf "http://$server:$port/sex?%s\n";,
+           encode_b64("user=$user&ID=anonymous$stream");
+  }
+  $mode = "&mode=anonymous";
+} elsif ($mode eq 'public') {
+  die "$0: need user/ID when sending to public, set it with fexsend -I\n" unless $user and $id;
+  unless ($opt_q) {
+    print "http://$server:$port/sex?user=$user&ID=public$stream\n";;
+    printf "http://$server:$port/sex?%s\n";,
+           encode_b64("user=$user&ID=public$stream");
+  }
+  $cid = query_sid($server,$port,$id);
+  $mode = "&ID=$cid&mode=public";
+} else {
+  # $user = checkalias($user) unless $opt_d;
+}
+
+request("POST /sex?BS=$bs&user=$user$mode$type$timeout$stream HTTP/1.0");
+print STDERR "==> (streaming ...)\n" if $opt_v;
+
+transfer(STDIN,$SH);
+  
+exit;
+
+
+sub transfer {
+  my $source = shift;
+  my $destination = shift;
+  my ($t0,$t1,$tt);
+  my ($B,$b,$bt);
+  
+  $t0 = $t2 = time;
+  $tt = $t0-1;
+  $t1 = 0;
+
+  while ($b = sysread $source,$_,$bs) {
+    print {$destination} $_ or die "$0: link failure - $!\n";
+    $B += $b;
+    $bt += $b;
+    $t2 = time;
+    if ($t2>$t1) {
+      if ($opt_g) {
+        if ($B>2*M) {
+          printf STDERR "%d MB %d kB/s        \r",
+            int($B/M),int($bt/k/($t2-$tt));
+        } else {
+          printf STDERR "%d kB %d kB/s        \r",
+            int($B/k),int($bt/k/($t2-$tt));
+        }
+      }
+      $t1 = $t2;
+      if ($t2-$tt>10) {
+        sleep 1; # be nice to bandwith
+        $bt = 0;
+        $tt = $t2;
+      }
+    }
+  }
+
+  die "$0: no stream data\n" unless $B;
+  
+  $tt = (time-$t0)||1;
+  
+  if ($opt_v or $opt_g) {
+    if ($B>2097152) {
+      printf STDERR "transfered: %d MB in %d s with %d kB/s\n",
+        int($B/1048576),$tt,int($B/1024/$tt);
+    } elsif($B>2048) {
+      printf STDERR "transfered: %d kB in %d s with %d kB/s\n",
+        int($B/1024),$tt,int($B/1024/$tt);
+    } else {
+      printf STDERR "transfered: %d B in %d s with %d kB/s\n",
+        $B,$tt,int($B/1024/$tt);
+    }
+  }
+  
+}
+
+
+sub request {
+  my $req = shift;
+  
+  print STDERR "==> $req\n" if $opt_v;
+  syswrite $SH,"$req\r\n\r\n";
+  for (;;) {
+    unless (defined($_ = &getline)) {
+      die "$0: server has closed the connection\n";
+    }
+    if (/^HTTP\/[\d\.]+ 200/) {
+      print STDERR "<== $_" if $opt_v;
+      last;
+    } elsif (/^HTTP\/[\d\.]+ 199/) {
+      print STDERR "<== $_" if $opt_v;
+    } else {
+      if ($opt_v) {
+        print STDERR "<== $_";
+        exit 3;
+      } else {
+        s:^HTTP/[ \d\.]+::;
+        s/\r//;
+        die "$0: server response: $_";
+      }
+    }
+  }
+  while (defined($_ = &getline)) {
+    last if /^\s*$/;
+    $H{uc($1)} = $2 if /(.+):\s*(.+)/;
+    print STDERR "<== $_" if $opt_v;
+  }
+}
+
+# check for (mutt) alias
+sub checkalias {
+  my $to = shift;
+  if ($to !~ /@/ and open F,$ENV{HOME}.'/.mutt/aliases') {
+    while (<F>) {
+      next if /,/;
+      if (/^alias $to\s/i) {
+        chomp;
+        s/\s*#.*//;
+        s/\s+$//;
+        s/.*\s+//;
+        s/<//;
+        s/>//;
+        $to = $_;
+        warn "$0: found alias, using address $to\n";
+        die unless $to;
+        last;
+      }
+    }
+    close F;
+  }
+  return $to;
+}
+
+sub despace {
+  foreach (@_) {
+    s/^\s+//;
+    s/\s+$//;
+  }
+}
+
+sub query_sid {
+  my ($server,$port,$id) = @_;
+  my $req;
+  local $_;
+  
+  $req = "GET SID HTTP/1.1";
+  print STDERR "==> $req\n" if $opt_v;
+  syswrite $SH,"$req\r\n\r\n";
+  $_ = &getline;
+  unless (defined $_ and /\w/) { 
+    print STDERR "\n" if $opt_v;
+    die "$0: no response from server\n";
+  }
+  s/\r//;
+  if (/^HTTP.* 201 (.+)/) {
+    print STDERR "<== $_" if $opt_v;
+    $id = 'MD5H:'.md5_hex($id.$1);
+    while (defined($_ = &getline)) { 
+      s/\r//;
+      last if /^\n/;
+      print STDERR "<== $_" if $opt_v;
+    }
+  } else {
+    die "$0: $server does not support session ID\n";
+  }
+  return $id;
+}
+
+sub sigpipehandler { 
+  local $_ = '';
+  $SIG{ALRM} = sub { };
+  alarm(1);
+  $_ = &getline||'';
+  if (/^HTTP.* \d+ (.*)/) {
+    if ($opt_v) {
+      die "\n$0: server error: @_\n";
+    } else {
+      die "\n$0: server error: $1\n";
+    }
+  } else {
+    die "\n$0: got SIGPIPE (server closed connection)\n";
+  }
+}
+
+# read one text line from $SH;
+sub getline {
+  my $line = '';
+  my $c;
+
+  local $SIG{ALRM} = sub { die "$0: timeout while waiting for server reply\n" };
+  alarm($opt_t||300);
+  
+  # must use sysread to avoid perl line buffering
+  while (sysread $SH,$c,1) {
+    $line .= $c;
+    last if $c eq "\n";
+  }
+  
+  alarm(0);
+  
+  return $line;
+}
+
+# from MIME::Base64::Perl
+sub decode_b64 {
+  local $_ = shift;
+  my $uu = '';
+  my ($i,$l);
+  
+  tr|A-Za-z0-9+=/||cd;
+  s/=+$//;
+  tr|A-Za-z0-9+/| -_|;
+  return "" unless length;
+
+  $l = (length) - 60;
+  for ($i = 0; $i <= $l; $i += 60) {
+    $uu .= "M" . substr($_,$i,60);
+  }
+  $_ = substr($_,$i);
+  if (length) {
+    $uu .= chr(32 + (length)*3/4) . $_;
+  }
+  return unpack ("u",$uu);
+}
+
+
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
+sub urldecode {
+  local $_ = shift;
+  s/\%([a-f\d]{2})/chr(hex($1))/ige;
+  return $_;
+}
+
+
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  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) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
+sub encode_b64 {
+  my $res = "";
+  my $eol = "\n";
+  my $padding;
+  
+  pos($_[0]) = 0;
+  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+  $res =~ tr|` -_|AA-Za-z0-9+/|;
+  $padding = (3-length($_[0])%3)%3;
+  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+  return $res;
+}
diff -Nru fex-20140917/htdocs/download/sexsend fex-20150120/htdocs/download/sexsend
--- fex-20140917/htdocs/download/sexsend	1970-01-01 01:00:00.000000000 +0100
+++ fex-20150120/htdocs/download/sexsend	2015-01-19 13:59:57.000000000 +0100
@@ -0,0 +1,723 @@
+#!/usr/bin/perl -w
+
+# client for stream exchange of the FEX service
+#
+# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
+#
+# Perl Artistic Licence
+
+# sexsend / sexget / sexxx
+
+use Getopt::Std;
+use Socket;
+use IO::Handle;
+use IO::Socket::INET;
+use Digest::MD5 qw(md5_hex);  # encypted ID / SID 
+
+use constant k => 2**10;
+use constant M => 2**20;
+
+eval 'use Net::INET6Glue::INET_is_INET6';
+
+our $version = 20150120;
+
+my %SSL = (SSL_version => 'TLSv1');
+my $sigpipe;
+
+if (-f ($_ = '/etc/fex/config.pl')) {
+  eval { require } or warn $@;
+}
+
+$0 =~ s:.*/::;
+$| = 1;
+
+# sexsend is default
+$usage = 
+  "usage: ... | $0 [options] [SEX-URL/]recipient [stream]\n".
+  "options: -v           verbose mode\n".
+  "         -g           show transfer rate\n".
+  "         -V           show version\n".
+  "         -t timeout   timeout in s (waiting for recipient)\n".
+  "special: recipient may be \"public\" or \"anonymous\" or \".\"\n".
+  "see also: sexget, sexxx\n".
+  "example: tail -f /var/log/syslog | $0 fex.flupp.org/admin log\n";
+
+if ($0 eq 'sexget' or $0 eq 'fuckme') {
+  $usage = 
+    "usage: $0 [options] [[SEX-URL/]user:ID] [stream]\n".
+    "options: -v           verbose mode\n".
+    "         -g           show transfer rate\n".
+    "         -V           show version\n".
+    "arguments: user:ID    use this user & ID\n".
+    "                      (ID may be \"public\" or user:ID may be \"anonymous\")\n".
+    "           stream     name of the stream\n".
+    "see also: sexsend, sexxx\n".
+    "example: $0 log | grep kernel\n";
+}
+
+if ($0 eq 'sexxx') {
+  $usage = 
+    "usage: $0 [-v] [-g] [-c] [-u [SEX-URL/]user] [-s stream] [files...]\n".
+    "usage: $0 [-v] [-g]      [-u [SEX-URL/]user] [-s stream] | ...\n".
+    "options: -v               verbose mode\n".
+    "         -g               show transfer rate\n".
+    "         -q               quiet mode\n".
+    "         -c               compress files\n".
+    "         -u SEX-URL/user  SEX-URL and user (default: use FEXID/FEXXX)\n".
+    "         -s stream        stream name (default: xx)\n".
+    "see also: sexsend, sexget\n".
+    "examples: $0 -s config /etc /usr/local/etc\n".
+    "          $0 > backup.tar\n";
+}
+
+$fexhome = $ENV{FEXHOME} || $ENV{HOME}.'/.fex';
+$user = $id = '';
+$type = $timeout = $stream = $mode = '';
+$idf = "$fexhome/id";
+$bs = $ENV{BS} || 2**16; # I/O blocksize
+
+# server URL, user and auth-ID
+if ($FEXID = $ENV{FEXID}) {
+  $FEXID = decode_b64($FEXID) if $FEXID !~ /\s/;
+  ($fexcgi,$user,$id) = split(/\s+/,$FEXID);
+} else {
+  if (open $idf,$idf) {
+    chomp($fexcgi = <$idf>) or die "$0: no FEX-URL in $idf\n";
+    chomp($user = <$idf>)   or die "$0: no FROM in $idf\n";
+    chomp($id = <$idf>)     or die "$0: no ID in $idf\n";
+    close $idf;
+    despace($fexcgi,$user,$id);
+    unless ($fexcgi =~ /^[_:=\w\-\.\/\@\%]+$/) {
+      die "$0: illegal FEX-URL \"$fexcgi\" in $idf\n";
+    }
+    unless ($user =~ /^[_:=\w\-\.\/\@\%\+]+$/) {
+      die "$0: illegal FROM \"$user\" in $idf\n";
+    }
+  }
+}
+
+$opt_h = $opt_v = $opt_V = $opt_q = 0;
+$opt_u = $opt_s = $opt_c = $opt_t = '';
+
+$_ = "$fexhome/config.pl"; require if -f;
+
+if ($0 eq 'sexxx') {
+  
+  # xx server URL, user and auth-ID
+  if ($FEXXX = $ENV{FEXXX}) {
+    $FEXXX = decode_b64($FEXXX) if $FEXXX !~ /\s/;
+    ($fexcgi,$user,$id) = split(/\s+/,$FEXXX);
+  } elsif (open $idf,$idf) {
+    while (<$idf>) {
+      if (/^\[xx\]/) {
+        chomp($fexcgi = <$idf>) or die "$0: no xx FEX-URL in $idf\n";
+        chomp($user = <$idf>)   or die "$0: no xx FROM in $idf\n";
+        chomp($id = <$idf>)     or die "$0: no xx ID in $idf\n";
+        last;
+      }
+    }
+    close $idf;
+  }
+  
+  getopts('hgvcu:s:') or die $usage;
+  die $usage if $opt_h;
+  die $usage unless -t;
+
+  if ($opt_c) {
+    $opt_c = 'z';
+    $type = '&type=GZIP';
+  }
+
+  if ($opt_u) {
+    $fexcgi = $1 if $opt_u =~ s:(.+)/::;
+    $user = $opt_u;
+  }
+
+  unless ($fexcgi) {
+    die "$0: no xx user found, use \"$0 -u SEX-URL/user\"\n";
+  }
+
+  unless ($user) {
+    die "$0: no xx user found, use \"$0 -u user\"\n";
+  }
+  
+} elsif ($0 eq 'sexget' or $0 eq 'fuckme') {
+  getopts('hgvVdu:') or die $usage;
+  die $usage if $opt_h;
+
+
+  if ($opt_V) {
+    print "Version: $version\n";
+    exit unless @ARGV;
+  }
+  
+  if (not $opt_u and @ARGV and $ARGV[0] =~ m{^anonymous|/|:}) {
+    $opt_u = shift @ARGV;
+  }
+  
+  if ($opt_u) {
+    $fexcgi = $1 if $opt_u =~ s:(.+)/::;
+    ($user,$id) = split(':',$opt_u);
+    if ($user =~ /^anonymous/) {
+      $anonymous = $user;
+    } elsif (not $id) {
+      die $usage;
+    }
+  }
+
+  unless ($fexcgi) {
+    die "$0: no SEX URL found, use \"$0 -u SEX-URL/recipient\" or \"fexsend -I\"\n";
+  }
+  
+  unless ($user) {
+    die "$0: no recipient found, use \"$0 -u SEX-URL/recipient\" or \"fexsend -I\"\n";
+  }
+  
+} else { # sexsend
+  
+  $opt_g = 1;
+  getopts('hguvqVTt:') or die $usage;
+  die $usage if $opt_h;
+
+  if ($opt_V) {
+    print "Version: $version\n";
+    exit unless @ARGV;
+  }
+  
+  if ($opt_t and $opt_t =~ /^\d+$/) {
+    $timeout = "&timeout=$opt_t";
+  }
+
+  my $save_user = $user;
+  $user = shift or die $usage;
+  $fexcgi = $1 if $user =~ s:(.+)/::;
+  
+  if ($user =~ /^anonymous/) {
+    die "$0: need SEX-URL with anonymous SEX\n" unless $fexcgi;
+    $mode = 'anonymous';
+  } elsif ($user eq 'public') {
+    unless ($id) {
+      die "$0: public SEX not possible without FEXID, set it with \"fexsend -I\"\n";
+    }
+    $mode = $user;
+    $user = $save_user;
+  } elsif ($user eq '.') {
+    open $idf,$idf or die "$0: no $idf\n";
+    $_ = <$idf>;
+    $user = <$idf>||'';
+    chomp $user;
+  } else {
+    unless ($fexcgi) {
+      die "$0: no SEX URL found, use \"$0 SEX-URL/recipient\" or \"fexsend -I\"\n";
+    }
+  }
+  
+}
+
+&get_ssl_env;
+
+$fexcgi =~ s(^http://)()i;
+$fexcgi =~ s(/fup.*)();
+$server = $fexcgi;
+
+if    ($server =~ s(^https://)()i) { $port = 443 } 
+elsif ($server =~ /:(\d+)/)        { $port = $1 } 
+else                               { $port = 80 }    
+
+$server =~ s([:/].*)();
+
+## set up tcp/ip connection
+# $iaddr = gethostbyname($server) 
+#          or die "$0: cannot find ip-address for $server $!\n";
+# socket(SH,PF_INET,SOCK_STREAM,getprotobyname('tcp')) or die "$0: socket $!\n";
+# connect(SH,sockaddr_in($port,$iaddr)) or die "$0: connect $!\n";
+# warn "connecting $server:$port user=$user\n";
+if ($port == 443) {
+  if ($opt_v and %SSL) {
+    foreach my $v (keys %SSL) {
+      printf "%s => %s\n",$v,$SSL{$v};
+    }
+  }
+  eval "use IO::Socket::SSL";
+  die "$0: cannot load IO::Socket::SSL\n" if $@;
+  $SH = IO::Socket::SSL->new(                                                  
+    PeerAddr => $server,                                                       
+    PeerPort => $port,                                                         
+    Proto    => 'tcp',
+    %SSL
+  );                                                                           
+} else {                                                                       
+  $SH = IO::Socket::INET->new(
+    PeerAddr => $server,
+    PeerPort => $port,
+    Proto    => 'tcp',                                                         
+  );                                                                           
+}
+
+die "cannot connect $server:$port - $!\n" unless $SH;                          
+warn "TCPCONNECT to $server:$port\n" if $opt_v;
+
+# autoflush $SH 1;
+autoflush STDERR;
+
+$SIG{PIPE} = \&sigpipehandler;
+
+if ($0 eq 'sexget' or $0 eq 'fuckme') {
+  $stream = "&stream=" . shift if @ARGV;
+  if ($anonymous) {
+    $cid = 'anonymous';
+  } elsif ($id eq 'public') {
+    $cid = 'public';
+  } else {
+    $cid = query_sid($server,$port,$id);
+  }
+  request("GET /sex?BS=$bs&user=$user&ID=$cid$stream HTTP/1.0");
+  transfer($SH,STDOUT);
+  # print while sysread $SH,$_,$bs;
+  exit;
+}
+
+if ($0 eq 'sexxx') {
+  $stream = "&stream=" . ($opt_s || 'xx');
+  if (@ARGV) {
+    warn "streaming:\n";
+    open my $tar,'-|','tar',"cv${opt_c}f",'-',@ARGV or die "$0: cannot run tar - $!\n";
+    request("POST /sex?BS=$bs&user=$user$type$stream HTTP/1.0");
+    transfer($tar,$SH);
+    # while (read $tar,$_,$bs) { syswrite $SH,$_ }
+  } else {
+    $cid = query_sid($server,$port,$id);
+    request("GET /sex?BS=$bs&user=$user&ID=$cid$stream HTTP/1.0");
+    $opt_c = 'z' if $H{'CONTENT-TYPE'} =~ /gzip/i;
+    if (-t STDOUT) {
+      print "extracting from stream:\n";
+      open $out,"|tar xv${opt_c}f -" or die "$0: cannot run tar - $!\n";
+    } else {
+      if ($opt_c) {
+        open $out,"|gzip -d" or die "$0: cannot run gunzip - $!\n";
+      } else {
+        $out = *STDOUT;
+      }
+    }
+    print {$out} $_ while sysread $SH,$_,$bs;
+  }
+  exit;
+}
+
+# sexsend
+$stream = "&stream=" . shift if @ARGV;
+
+if ($mode eq 'anonymous') {
+  unless ($opt_q) {
+    print "http://$server:$port/sex?user=$user&ID=anonymous$stream\n";;
+    printf "http://$server:$port/sex?%s\n";,
+           encode_b64("user=$user&ID=anonymous$stream");
+  }
+  $mode = "&mode=anonymous";
+} elsif ($mode eq 'public') {
+  die "$0: need user/ID when sending to public, set it with fexsend -I\n" unless $user and $id;
+  unless ($opt_q) {
+    print "http://$server:$port/sex?user=$user&ID=public$stream\n";;
+    printf "http://$server:$port/sex?%s\n";,
+           encode_b64("user=$user&ID=public$stream");
+  }
+  $cid = query_sid($server,$port,$id);
+  $mode = "&ID=$cid&mode=public";
+} else {
+  # $user = checkalias($user) unless $opt_d;
+}
+
+request("POST /sex?BS=$bs&user=$user$mode$type$timeout$stream HTTP/1.0");
+print STDERR "==> (streaming ...)\n" if $opt_v;
+
+transfer(STDIN,$SH);
+  
+exit;
+
+
+sub transfer {
+  my $source = shift;
+  my $destination = shift;
+  my ($t0,$t1,$tt);
+  my ($B,$b,$bt);
+  
+  $t0 = $t2 = time;
+  $tt = $t0-1;
+  $t1 = 0;
+
+  while ($b = sysread $source,$_,$bs) {
+    print {$destination} $_ or die "$0: link failure - $!\n";
+    $B += $b;
+    $bt += $b;
+    $t2 = time;
+    if ($t2>$t1) {
+      if ($opt_g) {
+        if ($B>2*M) {
+          printf STDERR "%d MB %d kB/s        \r",
+            int($B/M),int($bt/k/($t2-$tt));
+        } else {
+          printf STDERR "%d kB %d kB/s        \r",
+            int($B/k),int($bt/k/($t2-$tt));
+        }
+      }
+      $t1 = $t2;
+      if ($t2-$tt>10) {
+        sleep 1; # be nice to bandwith
+        $bt = 0;
+        $tt = $t2;
+      }
+    }
+  }
+
+  die "$0: no stream data\n" unless $B;
+  
+  $tt = (time-$t0)||1;
+  
+  if ($opt_v or $opt_g) {
+    if ($B>2097152) {
+      printf STDERR "transfered: %d MB in %d s with %d kB/s\n",
+        int($B/1048576),$tt,int($B/1024/$tt);
+    } elsif($B>2048) {
+      printf STDERR "transfered: %d kB in %d s with %d kB/s\n",
+        int($B/1024),$tt,int($B/1024/$tt);
+    } else {
+      printf STDERR "transfered: %d B in %d s with %d kB/s\n",
+        $B,$tt,int($B/1024/$tt);
+    }
+  }
+  
+}
+
+
+sub request {
+  my $req = shift;
+  
+  print STDERR "==> $req\n" if $opt_v;
+  syswrite $SH,"$req\r\n\r\n";
+  for (;;) {
+    unless (defined($_ = &getline)) {
+      die "$0: server has closed the connection\n";
+    }
+    if (/^HTTP\/[\d\.]+ 200/) {
+      print STDERR "<== $_" if $opt_v;
+      last;
+    } elsif (/^HTTP\/[\d\.]+ 199/) {
+      print STDERR "<== $_" if $opt_v;
+    } else {
+      if ($opt_v) {
+        print STDERR "<== $_";
+        exit 3;
+      } else {
+        s:^HTTP/[ \d\.]+::;
+        s/\r//;
+        die "$0: server response: $_";
+      }
+    }
+  }
+  while (defined($_ = &getline)) {
+    last if /^\s*$/;
+    $H{uc($1)} = $2 if /(.+):\s*(.+)/;
+    print STDERR "<== $_" if $opt_v;
+  }
+}
+
+# check for (mutt) alias
+sub checkalias {
+  my $to = shift;
+  if ($to !~ /@/ and open F,$ENV{HOME}.'/.mutt/aliases') {
+    while (<F>) {
+      next if /,/;
+      if (/^alias $to\s/i) {
+        chomp;
+        s/\s*#.*//;
+        s/\s+$//;
+        s/.*\s+//;
+        s/<//;
+        s/>//;
+        $to = $_;
+        warn "$0: found alias, using address $to\n";
+        die unless $to;
+        last;
+      }
+    }
+    close F;
+  }
+  return $to;
+}
+
+sub despace {
+  foreach (@_) {
+    s/^\s+//;
+    s/\s+$//;
+  }
+}
+
+sub query_sid {
+  my ($server,$port,$id) = @_;
+  my $req;
+  local $_;
+  
+  $req = "GET SID HTTP/1.1";
+  print STDERR "==> $req\n" if $opt_v;
+  syswrite $SH,"$req\r\n\r\n";
+  $_ = &getline;
+  unless (defined $_ and /\w/) { 
+    print STDERR "\n" if $opt_v;
+    die "$0: no response from server\n";
+  }
+  s/\r//;
+  if (/^HTTP.* 201 (.+)/) {
+    print STDERR "<== $_" if $opt_v;
+    $id = 'MD5H:'.md5_hex($id.$1);
+    while (defined($_ = &getline)) { 
+      s/\r//;
+      last if /^\n/;
+      print STDERR "<== $_" if $opt_v;
+    }
+  } else {
+    die "$0: $server does not support session ID\n";
+  }
+  return $id;
+}
+
+sub sigpipehandler { 
+  local $_ = '';
+  $SIG{ALRM} = sub { };
+  alarm(1);
+  $_ = &getline||'';
+  if (/^HTTP.* \d+ (.*)/) {
+    if ($opt_v) {
+      die "\n$0: server error: @_\n";
+    } else {
+      die "\n$0: server error: $1\n";
+    }
+  } else {
+    die "\n$0: got SIGPIPE (server closed connection)\n";
+  }
+}
+
+# read one text line from $SH;
+sub getline {
+  my $line = '';
+  my $c;
+
+  local $SIG{ALRM} = sub { die "$0: timeout while waiting for server reply\n" };
+  alarm($opt_t||300);
+  
+  # must use sysread to avoid perl line buffering
+  while (sysread $SH,$c,1) {
+    $line .= $c;
+    last if $c eq "\n";
+  }
+  
+  alarm(0);
+  
+  return $line;
+}
+
+# from MIME::Base64::Perl
+sub decode_b64 {
+  local $_ = shift;
+  my $uu = '';
+  my ($i,$l);
+  
+  tr|A-Za-z0-9+=/||cd;
+  s/=+$//;
+  tr|A-Za-z0-9+/| -_|;
+  return "" unless length;
+
+  $l = (length) - 60;
+  for ($i = 0; $i <= $l; $i += 60) {
+    $uu .= "M" . substr($_,$i,60);
+  }
+  $_ = substr($_,$i);
+  if (length) {
+    $uu .= chr(32 + (length)*3/4) . $_;
+  }
+  return unpack ("u",$uu);
+}
+
+
+### common functions ###
+
+
+sub mtime {
+  my @d = localtime((stat shift)[9]);
+  return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
+}
+
+
+sub urldecode {
+  local $_ = shift;
+  s/\%([a-f\d]{2})/chr(hex($1))/ige;
+  return $_;
+}
+
+
+sub get_ssl_env {
+  # set SSL/TLS options
+  $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
+  foreach my $opt (qw(
+    SSL_version
+    SSL_cipher_list 
+    SSL_verify_mode 
+    SSL_ca_path 
+    SSL_ca_file)
+  ) {
+    my $env = uc($opt);
+    $env =~ s/_//g;
+    $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
+  }
+
+  if ($SSL{SSL_verify_mode}) {
+    &search_ca;
+    unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
+      die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
+    }
+  } elsif (defined($SSL{SSL_verify_mode})) {
+    # user has set SSLVERIFY=0 !
+  } else {
+    &search_ca;
+    $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
+  }
+}
+
+sub search_ca {
+  local $_;
+  return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
+  foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
+    if (-f) {
+      $SSL{SSL_ca_file} = $_;
+      return;
+    }
+  }
+  foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
+    if (-f) {
+      $SSL{SSL_ca_path} = $_;
+      return;
+    }
+  }
+}
+
+
+sub serverconnect {
+  my ($server,$port) = @_;
+  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) {
+      printf "--> %s\n",$connect if $opt_v;
+      nvtsend($connect,"");
+      $_ = <$SH>;
+      s/\r//;
+      printf "<-- $_"if $opt_v;
+      unless (/^HTTP.1.. 200/) {
+        die "$0: proxy error : $_";
+      }
+      eval "use IO::Socket::SSL";
+      die "$0: cannot load IO::Socket::SSL\n" if $@;
+      $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
+    }
+  } else {
+    tcpconnect($server,$port);
+  }
+#  if ($port == 443 and $opt_v) {
+#    printf "%s\n",$SH->get_cipher();
+#  }
+}
+
+
+# set up tcp/ip connection
+sub tcpconnect {
+  my ($server,$port) = @_;
+  
+  if ($SH) {
+    close $SH;
+    undef $SH;
+  }
+  
+  if ($port == 443) {
+    # eval "use IO::Socket::SSL qw(debug3)";
+    eval "use IO::Socket::SSL";
+    die "$0: cannot load IO::Socket::SSL\n" if $@;
+    $SH = IO::Socket::SSL->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+      %SSL
+    );
+  } else {
+    $SH = IO::Socket::INET->new(
+      PeerAddr => $server,
+      PeerPort => $port,
+      Proto    => 'tcp',
+    );
+  }
+  
+  if ($SH) {
+    autoflush $SH 1;
+  } else {
+    die "$0: cannot connect $server:$port - $@\n";
+  }
+  
+  print "TCPCONNECT to $server:$port\n" if $opt_v;
+}
+
+
+sub sendheader {
+  my $sp = shift;
+  my @head = @_;
+  my $head;
+  
+  push @head,"Host: $sp";
+  
+  foreach $head (@head) {
+    print "--> $head\n" if $opt_v;
+    print {$SH} $head,"\r\n";
+  }
+  print "-->\n" if $opt_v;
+  print {$SH} "\r\n";
+}
+
+
+sub nvtsend {
+  local $SIG{PIPE} = sub { $sigpipe = "@_" };
+  
+  $sigpipe = '';
+  
+  die "$0: internal error: no active network handle\n" unless $SH;
+  die "$0: remote host has closed the link\n" unless $SH->connected;
+  
+  foreach my $line (@_) {
+    print {$SH} $line,"\r\n";
+    if ($sigpipe) {
+      undef $SH;
+      return 0;
+    }
+  }
+  
+  return 1;
+}
+
+
+# from MIME::Base64::Perl
+sub encode_b64 {
+  my $res = "";
+  my $eol = "\n";
+  my $padding;
+  
+  pos($_[0]) = 0;
+  $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+  $res =~ tr|` -_|AA-Za-z0-9+/|;
+  $padding = (3-length($_[0])%3)%3;
+  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+  return $res;
+}
diff -Nru fex-20140917/htdocs/FAQ/admin.faq fex-20150120/htdocs/FAQ/admin.faq
--- fex-20140917/htdocs/FAQ/admin.faq	2014-08-25 09:15:02.000000000 +0200
+++ fex-20150120/htdocs/FAQ/admin.faq	2014-12-19 16:31:01.000000000 +0100
@@ -19,6 +19,9 @@
      /home/fex/bin/fac -u memyselfandi@my.do.main secret-auth-id
    </pre>
    Then log in using the web interface: http://YOURFEXSERVER/
+   
+   ... and join the F*EX mailing list! &#9786;
+   https://listserv.uni-stuttgart.de/mailman/listinfo/fex
 
 Q: What is /home/fex/bin/fac and /home/fex/cgi-bin/fac ?
 A: fac stands for F*EX Admin Control
@@ -83,6 +86,12 @@
      /home/fex/bin/fac -R framstag@rus.uni-stuttgart.de
    </pre>
 
+Q: How can I change user settings like quota, restrictions or keep time?
+A: Use <code>/home/fex/bin/fac</code>
+
+Q: How can I delete or temporarly disable a user?
+A: Use <code>/home/fex/bin/fac</code>
+
 Q: I have BIG files already on the fexserver host. Can I upload just a
    link instead of the whole file?
 A: Set in fex.ph:
@@ -125,7 +134,7 @@
 
 Q: I need ACLs for group access, a file browser and integration in my
    native file system.
-A: This is beyond the scope of F*EX, which is designed for file transfer.
+A: This is beyond the scope of F*EX, which is designed for efficient file transfer only.
 
 Q: Feature/design XY is missing.
 A: Contact the author <framstag@rus.uni-stuttgart.de>
diff -Nru fex-20140917/htdocs/FAQ/faq.pl fex-20150120/htdocs/FAQ/faq.pl
--- fex-20140917/htdocs/FAQ/faq.pl	2014-04-03 15:32:58.000000000 +0200
+++ fex-20150120/htdocs/FAQ/faq.pl	2014-11-17 18:27:32.000000000 +0100
@@ -13,7 +13,7 @@
 printf "Frequently Asked Questions: %s</h1>\n",ucfirst($faq);
 
 if ($faq ne 'local') {
-  print "<h3>Section: \n";
+  print "<h3>Sections: ";
   foreach $s (@sections,'All') {
     if ($s =~ /$faq/i) {
       print "<b>$s</b>\n";
@@ -41,8 +41,10 @@
     };
     ($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
@@ -72,8 +74,13 @@
   $t = $s if $faq eq 'all';
 
   for ($n = 0; $n < scalar(@{$Q{$c}}); $n++) {
-    printf "<tr valign=top><th align=left>%s&nbsp;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;
   }
 }
 
@@ -88,10 +95,15 @@
   $t = $s if $faq eq 'all';
 
   for ($n = 0; $n < scalar(@{$Q{$c}}); $n++) {
+    $q = ${Q{$c}[$n]};
+    $qa = anchor($q);
     print "<p>\n";
     print "<table>\n";
-    printf "<tr valign=top><th><a name=\"%s%d\">%s&nbsp;Q%d:</a><td><b>%s</b></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 "</table>\n";
@@ -119,3 +131,12 @@
   s/\s+$//;
   return "<pre>$_</pre>\n";
 }
+
+sub anchor {
+  local $_ = shift;
+  s/<.+?>//g;
+  s/\(.+?\)//g;
+  s/\W/_/g;
+  s/_+$//;
+  return $_;
+}
diff -Nru fex-20140917/htdocs/FAQ/meta.faq fex-20150120/htdocs/FAQ/meta.faq
--- fex-20140917/htdocs/FAQ/meta.faq	2014-08-25 09:21:25.000000000 +0200
+++ fex-20150120/htdocs/FAQ/meta.faq	2014-12-19 16:31:42.000000000 +0100
@@ -3,7 +3,7 @@
    and use cases http://fex.rus.uni-stuttgart.de/usecases/
 
 Q: Why not use one of the commercial services like DropLoad, ALLPeers, YouSendIt, etc?
-A: * They have a limit of 2 GB or even less.
+A: * They have a file size limit of 2 GB or even less.
    * Their security and privacy status is unknown (ever heard of "Snowden & NSA"?).
    * They are not open source based.
    * There are no UNIX (CLI) clients for them.
@@ -64,9 +64,9 @@
    * European Commission Institute for Energy and Transport http://fex.jrc.nl
    * High Performance Computing Center Stuttgart http://fex.hlrs.de
    * 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
    * Palo Alto Research Center (Xerox PARC) http://parcftp.parc.com
-   * Open Computing Facility University of California at Berkeley http://fex.ocf.berkeley.edu
    * Baden-Württembergs extended LAN http://fex.belwue.de
    * Deutsche Kinemathek Museum f&uuml;r Film und Fernsehen http://upload.deutsche-kinemathek.de
 
diff -Nru fex-20140917/htdocs/FAQ/user.faq fex-20150120/htdocs/FAQ/user.faq
--- fex-20140917/htdocs/FAQ/user.faq	2014-09-03 16:38:17.000000000 +0200
+++ fex-20150120/htdocs/FAQ/user.faq	2014-12-16 12:21:30.000000000 +0100
@@ -18,6 +18,17 @@
 Q: My recipient has lost the notification email with the download-URL. What can I do?
 A: You can resend the notification email via "user config & operation control"
 
+Q: Why should I use a special F*EX client?
+A: When you are using F*EX with your webbrowser, you are limited to its restrictions.
+   With a special F*EX client http://$HTTP_HOST$/tools.html you can
+
+   * resume an aborted transfer
+   * send several files or even whole directory trees at once
+   * stream files
+   * transfer files via command line
+   * use an Internet clipboard http://fex.rus.uni-stuttgart.de/usecases/xx.html
+   * do much more :-)
+
 Q: How can I upload several files at once?
 A: Put your files in an archive file (ZIP). Your web browser cannot do that.
    Or you can use a F*EX client, see http://$HTTP_HOST$/tools.html
@@ -27,6 +38,9 @@
    Firefox and Google Chrome have no limitation.
    But remember: No web browser is able to resume an interrupted upload. You need a special F*EX client like fexsend or schwuppdiwupp for resuming, see http://$HTTP_HOST$/tools.html
 
+Q: I need to send a file bigger than my quota allows. What can I do?
+A: Simply ask $SERVER_ADMIN$ to raise your quota.
+
 Q: Why is the upload status window empty and I cannot see the progress bar?
 A: Most probably you are using a (enforced) web proxy, which cannot handle dynamic HTML pages.
    A workaround is using Google Chrome, which shows the upload status by itself.
@@ -46,7 +60,7 @@
 
 Q: Can I use a download manager/accelerator?
 A: Generally, no, because they suck: they are not RFC compliant and produce a LOT of unnecessary server load.
-   But there is one exception: axel [http://axel.alioth.debian.org/]
+   But there is one exception: axel http://axel.alioth.debian.org/
 
 Q: When I hit [ESC] in firefox the upload is canceled. Why?
 A: This is a built-in feature of firefox: ESC terminates the current operation.
@@ -67,8 +81,15 @@
 Q: I have uploaded a file to a list of recipients. Will the file be deleted after the first recipient has dowloaded it?
 A: No. Every recipient gets his own copy of the file which is independant from the others.
 
-Q: The default keep time is too short for me, I need more. How can I set it?
-A: Use fexsend, ask your fexmaster or read the source code :-)
+Q: The default keep time is too short for me (sender), I need more. How can I set it?
+A: Use fexsend, ask $SERVER_ADMIN$ or read the source code :-)
+
+Q: The default keep time is too short for me (recipient), I need more. How can I set it?
+A: Ask $SERVER_ADMIN$ to raise your default KEEP value.
+
+Q: I forgot to download a file. Now it is expired. How can I obtain it nevertheless?
+A: An expired file is definitively deleted. Even the admin cannot restore it.
+   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).
@@ -100,3 +121,16 @@
 Q: I cannot login with Internet Explorer, it tells me "This page can't be displayed". What shall I do?
 A: Use Firefox or any other Internet-compatible web browser, that Internet Explorer is not.
    This is one of the many bugs of Internet Explorer.
+
+Q: I have recived a "file.7z". How can I extract it on my Mac?
+A: For example with "Stuffit Expander":
+   https://itunes.apple.com/us/app/stuffit-expander/id405580712?mt=12
+   http://my.smithmicro.com/stuffit-expander-mac-download.html
+
+Q: How can I prevent the fexsend error <code>SSL3_GET_SERVER_CERTIFICATE:certificate verify failed</code>?
+A: Set the environment variable <code>SSLVERIFY=0</code>
+
+   Rationale: 
+   Your openssl library cannot resolve the SSL certification path. 
+   With <code>SSLVERIFY=0</code> you tell openssl to ignore certification verification.
+   Yes, this is a crude workaround :-}
diff -Nru fex-20140917/htdocs/features.html fex-20150120/htdocs/features.html
--- fex-20140917/htdocs/features.html	2014-08-25 09:36:16.000000000 +0200
+++ fex-20150120/htdocs/features.html	2014-12-17 16:02:39.000000000 +0100
@@ -51,6 +51,7 @@
   <li>optional authentification by LDAP, RADIUS, POP, IMAP, mailman
   <li>server available for UNIX and Windows hosts
   <li>about 10 times faster than apache
+  <li><b>very</b> low memory usage
   <li>(reverse) proxy support
   <li>F*EX is a HTTP web-service and needs no firewall-tunnels
   <li>works with NAT or DHCP clients, too
diff -Nru fex-20140917/htdocs/version fex-20150120/htdocs/version
--- fex-20140917/htdocs/version	2014-09-17 22:07:21.000000000 +0200
+++ fex-20150120/htdocs/version	2015-01-20 10:59:25.000000000 +0100
@@ -1 +1 @@
-fex-20140917
+fex-20150120
diff -Nru fex-20140917/install fex-20150120/install
--- fex-20140917/install	2014-08-27 19:16:27.000000000 +0200
+++ fex-20150120/install	2014-12-17 09:41:40.000000000 +0100
@@ -83,11 +83,14 @@
   $premiss++;
 }
 
-if ( -x '/usr/lib/sendmail') {
-  print "found /usr/lib/sendmail\n";
-} elsif ( -x '/usr/sbin/sendmail') {
-  print "found /usr/sbin/sendmail\n";
-} else {
+foreach (qw'/usr/lib/sendmail /usr/sbin/sendmail') {
+  if (-x) {
+    $sendmail = $_;
+    print "found $sendmail\n";
+    last;
+  }
+}
+unless ($sendmail) {
   print "sendmail NOT found\n";
   $premiss++;  
 }
@@ -171,7 +174,7 @@
 if  (-d "$FEXHOME/spool") {
   warn "checking spool ...\n";
   &convert_spool;
-  system "chown -R fex $spooldir";
+  system "chown -R fex $spooldir/";
 } else {
   $newinstall = $FEXHOME;
   chmod 0700,$FEXHOME;
@@ -365,23 +368,33 @@
   print "\n";
   print "F*EX update installed.\n";
   print "You can inform your users about the new features with:\n";
-  print "$FEXHOME/bin/fexwall 'new features on $hostname' ".
+  print "$FEXHOME/bin/fexwall 'new F*EX features on $hostname' ".
         "< $FEXHOME/doc/newfeatures\n";
 }
 
 if (@local_rdomains and not @local_rhosts) {
-  print "WARNING:\n";
+  print "\nWARNING:\n";
   print "In $fph you have @local_rdomains but not @local_rhosts!\n";
   print "Selfregistrating of external users will not work!\n";
   print "See ${fph}_new/\n";
 }
-  
+
+if (`$sendmail -h 2>&1` =~ /exim/ and 
+    `grep trusted_users /etc/exim4/exim4.conf 2>/dev/null` !~ /\bfex\b/) {
+  print "\nWARNING:\n";
+  print "$sendmail is exim\n";
+  print "You MUST set in your exim4.conf:\n";
+  print "trusted_users = mail : uucp : fex\n";
+}
 exit;
 
 
 sub convert_spool {
   my ($f,$d,$to,$from,$link);
   
+  local $) = $FEX[3];
+  local $> = $FEX[2];
+
   our ($spooldir,$skeydir,$gkeydir);
   $ENV{FEXLIB} = $FEXLIB = "$FEXHOME/lib";
   require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
diff -Nru fex-20140917/lib/dop fex-20150120/lib/dop
--- fex-20140917/lib/dop	2014-09-11 00:08:33.000000000 +0200
+++ fex-20150120/lib/dop	2014-12-20 11:41:20.000000000 +0100
@@ -54,8 +54,7 @@
       "Connection: close",
       ""
     );
-    exec($FEXHOME.'/bin/fexsrv') if $ENV{KEEP_ALIVE};
-    exit; 
+    &reexec;
   }
 
   # watchdog documents
@@ -74,6 +73,7 @@
   my ($var,$env,$con);
   my @files;
   my $htmldoc = '';
+  my $htauth;
   my @s;
   my $s = 0;
   my $b = 0;
@@ -86,6 +86,8 @@
   }
 
   security_check($file);
+  $htauth = dirname($file).'/.htauth';
+  require_auth($htauth,$file) if -f $htauth;
 
   if (-f $file) {
     # normal file
@@ -337,6 +339,7 @@
   my @files = ();
   my $uri = $ENV{REQUEST_URI};
   my $allowed;
+  my ($htindex,$htauth);
   local $_;
   
   $uri =~ s:/+$::;
@@ -344,7 +347,11 @@
 
   security_check($dir);
   
-  open my $htindex,"$dir/.htindex" or http_error(403);
+  $htindex = "$dir/.htindex";
+  $htauth  = "$dir/.htauth";
+  
+  open $htindex,$htindex or http_error(403);
+  require_auth($htauth,$dir) if -f $htauth;
   
   # .htindex may contain listing regexp
   chomp ($allowed = <$htindex>||'.');
@@ -536,6 +543,49 @@
     
 }
 
+# HTTP Basic authentication
+sub require_auth {
+  my $htauth = shift;
+  my $doc = shift;
+  my ($realm,$auth);
+  my @http_auth;
+  my $uri = $ENV{REQUEST_URI} || '/';
+  
+  $uri =~ s/\/index\.html$//;
+  $uri =~ s/\/$//;
+
+  if (-d $doc or $doc =~ /\/index\.html$/) {
+    $realm = $uri;
+  } else {
+    $realm = dirname($uri);
+  }
+  
+  $auth = slurp($htauth);
+  unless ($auth and $realm) {
+    http_header("200 OK");
+    print html_header("$ENV{SERVER_NAME} no authentication");
+    pq(qq(
+      '<h3><code>$htauth</code> missing</h3>'
+      '</body></html>'
+    ));
+    exit;
+  }
+  chomp $auth;
+  
+  if ($ENV{HTTP_AUTHORIZATION} and $ENV{HTTP_AUTHORIZATION} =~ /Basic\s+(.+)/) 
+  { @http_auth = split(':',decode_b64($1)) }
+  if (@http_auth != 2 or $http_auth[1] ne $auth) {
+    http_header(
+      '401 Authorization Required',
+      "WWW-Authenticate: Basic realm=\"$realm\"",
+      'Content-Length: 0',
+    );
+    # control back to fexsrv for further HTTP handling
+    &reexec;
+  }
+}
+
+
 # function for <<perl-code>> inside HTML documents
 sub out {
   $__ .= join('',@_);
diff -Nru fex-20140917/lib/fex.ph fex-20150120/lib/fex.ph
--- fex-20140917/lib/fex.ph	2014-08-16 12:13:02.000000000 +0200
+++ fex-20150120/lib/fex.ph	2015-01-17 11:43:35.000000000 +0100
@@ -87,7 +87,10 @@
 
 ## Allow or disallow overwriting of files
 $overwrite = 'YES';
-   
+
+## Allow user requests for forgotten auth-IDs (then send by email)
+$mail_authid = 'YES';
+                                                  
 ## optional: from which hosts and for which mail domains users may 
 ##           register themselves as full users (must set both!)
 # @local_hosts = qw(127.0.0.1 ::1 10.10.100.0-10.10.200.255 129.69.1.129);
diff -Nru fex-20140917/lib/fex.pp fex-20150120/lib/fex.pp
--- fex-20140917/lib/fex.pp	2014-09-14 01:16:50.000000000 +0200
+++ fex-20150120/lib/fex.pp	2015-01-17 11:41:00.000000000 +0100
@@ -54,6 +54,10 @@
 $mailmode = 'auto';
 $bcc = 'fex';
 $default_locale = '';
+$fop_auth = 0;
+$mail_authid = 'yes';
+$force_https = 0;
+$debug = 0;
 
 $FHS = -f '/etc/fex/fex.ph' and -d '/usr/share/fex/lib';
 # Debian FHS
@@ -74,6 +78,11 @@
 # local config
 require "$FEXLIB/fex.ph" or die "$0: cannot load $FEXLIB/fex.ph - $!";
 
+$fop_auth	= 0 if $fop_auth	=~ /no/i;
+$mail_authid	= 0 if $mail_authid	=~ /no/i;
+$force_https	= 0 if $force_https	=~ /no/i;
+$debug		= 0 if $debug		=~ /no/i;
+  
 # check for name based virtual host
 $vhost = vhost($ENV{'HTTP_HOST'});
 
@@ -132,6 +141,8 @@
   }
 }
 
+$default_locale ||= 'english';
+
 unless ($durl) {
   my $host = '';
   my $port = 0;
@@ -649,6 +660,7 @@
   local $_;
 
   $a .= '@'.$mdomain if $mdomain and $a !~ /@/;
+  return $a if -d "$spooldir/$a"; # ok, if user already exists
   if (@forbidden_recipients) {
     foreach (@forbidden_recipients) {
       $fr = quotemeta;
@@ -754,11 +766,16 @@
 
   if (open $file,'<',"$file/filename") {
     $filename = <$file>||'';
-    close $file;
     chomp $filename;
+    close $file;
   }
   
-  return $filename ? $filename : '???';
+  unless ($filename) {
+    $filename = $file;
+    $filename =~ s:.*/::;
+  }
+  
+  return $filename;
 }
 
 
@@ -843,15 +860,14 @@
 sub faillog {
   my $request = shift;
   my $n = 1;
-  my $ra = $ENV{REMOTE_ADDR};
 
-  if ($faillog and $max_fail_handler and open $ra,"+>>$faillog") {
-    flock($ra,LOCK_EX);
-    seek $ra,0,SEEK_SET;
-    $n++ while <$ra>;
-    printf {$ra} "%s %s\n",isodate(time),$request;
-    close $ra;
-    &$max_fail_handler($ra) if $n > $max_fail;
+  if ($faillog and $max_fail_handler and open $faillog,"+>>$faillog") {
+    flock($faillog,LOCK_EX);
+    seek $faillog,0,SEEK_SET;
+    $n++ while <$faillog>;
+    printf {$faillog} "%s %s\n",isodate(time),$request;
+    close $faillog;
+    &$max_fail_handler($ENV{REMOTE_ADDR}) if $n > $max_fail;
   }
 }
 
@@ -915,7 +931,7 @@
   my $sender = shift;
   my $squota = $sender_quota||0;
   my $du = 0;
-  my ($file,@file,$size,%size,$qf,$qs);
+  my ($file,$size,%file,$data);
   local $_;
   
   if (open $qf,'<',"$sender/\@QUOTA") {
@@ -925,15 +941,22 @@
     }
     close $qf;
   }
-  $qs = "*/$sender/*";
-  if (glob $qs and open $qs,untaint("du $qs 2>/dev/null|")) {
-    while (<$qs>) {
-      $du += $1 if /^(\d+)/;
+  
+  foreach $file (glob "*/$sender/*") {
+    $data = "$file/data";
+    if (not -l $data and $size = -s $data) {
+      # count hard links only once (= same inode)
+      my $i = (stat($data))[1]||0;
+      unless ($file{$i}) {
+        $du += $size;
+        $file{$i} = $i;
+      }
+    } elsif (-f "$file/upload" and $size = readlink "$file/size") {
+      $du += $size;
     }
-    close $qs;
   }
   
-  return($squota,int($du/1024));
+  return($squota,int($du/1024/1024));
 }
 
 
@@ -942,6 +965,7 @@
   my $recipient = shift;
   my $rquota = $recipient_quota||0;
   my $du = 0;
+  my ($file,$size);
   local $_;
   
   if (open my $qf,'<',"$recipient/\@QUOTA") {
@@ -951,9 +975,12 @@
     }
     close $qf;
   }
-  foreach my $data (glob("$recipient/*/*/data $recipient/*/*/upload")) {
-    unless (-l $data) {
-      $du += -s $data||0;
+  
+  foreach $file (glob "$recipient/*/*") {
+    if (-f "$file/upload" and $size = readlink "$file/size") {
+      $du += $size;
+    } elsif (not -l "$file/data" and $size = -s "$file/data") {
+      $du += $size;
     }
   }
   
@@ -1142,6 +1169,50 @@
   }
 }
 
+sub notify_locale {
+  my $dkey = shift;
+  my $status = shift || 'new';
+  my ($to,$keep,$locale,$file,$filename,$comment,$autodelete,$replyto,$mtime);
+  local $_;
+
+  if ($dkey =~ m:/.+/.+/:) {
+    $file = $dkey;
+    $dkey = readlink("$file/dkey");
+  } else {
+    $file = readlink("$dkeydir/$dkey") 
+      or http_die("internal error: no DKEY $DKEY");
+  }
+  $file =~ s:^../::;
+  $filename = filename($file);
+  $to = $file;
+  $to =~ s:/.*::;
+  $mtime = mtime("$file/data") or http_die("internal error: no $file/data");
+  $comment = slurp("$file/comment") || '';
+  $replyto = readlink "$file/replyto" || '';
+  $autodelete = readlink "$file/autodelete" 
+             || readlink "$to/\@AUTODELETE" 
+             || $::autodelete;
+  $keep = readlink "$file/keep" 
+       || readlink "$to/\@KEEP" 
+       || $keep_default;
+  
+  $locale = readlink "$to/\@LOCALE" || readlink "$file/locale" || 'english';
+  $_ = untaint("$FEXHOME/locale/$locale/lib/lf.pl");
+  require if -f;
+  unless ($notify{$locale}) {
+    $locale = 'english';
+    $notify{$locale} ||= \&notify;
+  }
+  return &{$notify{$locale}}(
+    status     => $status,
+    dkey       => $dkey,
+    filename   => $filename,
+    keep       => $keep-int((time-$mtime)/DS),
+    comment    => $comment,
+    autodelete => $autodelete,
+    replyto    => $replyto,
+  );
+}
 
 ### locale functions ###
 # will be extracted by install process and saved in $FEXHOME/lib/lf.pl
@@ -1348,6 +1419,7 @@
   print {$sendmail} $header,"\n",$body;
   close $sendmail
     or $! and http_die("cannot send notification e-mail (sendmail error $!)\n");
+  return $to;
 }
 
 
diff -Nru fex-20140917/lib/fup.pl fex-20150120/lib/fup.pl
--- fex-20140917/lib/fup.pl	2013-05-09 12:13:09.000000000 +0200
+++ fex-20150120/lib/fup.pl	2014-11-07 13:01:11.000000000 +0100
@@ -10,7 +10,7 @@
 After download or after $keep_default days the server deletes the file.
 F*EX is not an archive!
 <p>
-See also <a href="/FAQ/FAQ.html">questions & answers</a> and
+See also <a href="/FAQ/">questions & answers</a> and
 <a href="http://fex.rus.uni-stuttgart.de/usecases/";>use cases</a>.
 <p><hr><p>
 <address>
@@ -26,12 +26,12 @@
 <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>
 or Firefox or Google Chrome which have no size limit.<br>
-You also need a F*EX client for resuming interrupted uploads. Your web browser cannot do this.
+You also need a <a href="/fuc?show=tools">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>.
 <p>
-See also the <a href="/FAQ/FAQ.html">FAQ<a> and
+See also the <a href="/FAQ/user.html">FAQ<a> and
 <a href="http://fex.rus.uni-stuttgart.de/usecases/";>use cases</a>.
 <p><hr><p>
 <address>
diff -Nru fex-20140917/locale/czech/htdocs/FAQ.html fex-20150120/locale/czech/htdocs/FAQ.html
--- fex-20140917/locale/czech/htdocs/FAQ.html	2012-09-28 19:03:08.000000000 +0200
+++ fex-20150120/locale/czech/htdocs/FAQ.html	1970-01-01 01:00:00.000000000 +0100
@@ -1,271 +0,0 @@
-<HTML>
-This FAQ has 3 parts:</p>
-<ul>
-  <li><a href="#Meta">Meta</a>
-  <li><a href="#User">User</a>
-  <li><a href="#Admin">Admin</a>
-</ul>
-<p>
-<PRE>
-<a name="meta">
-<a name="Meta">
-Meta questions:
-===============
-
-<a name="1"><a href="#1">Q</a>: Why name "F*EX" and not shortly "FEX"?
-
-A: At publication time there was already an (older) program named "FEX" on
-   freshmeat.net. 
-
-
-<a name="2"><a href="#2">Q</a>: Why not use one of the commercial services like
-   DropLoad, ALLPeers, YouSendIt, etc?
-
-A: They have a limit of 2 GB or even less.
-   Their security and privacy status is unknown.
-   They are not open source based.
-   There are no UNIX (CLI) clients for them.
-   They need java, active-X, flash or other evil plugins.
-   It is unknown how long they will exist - DropLoad and ALLPeers already
-   have terminated their business.
- 
-
-<a name="3"><a href="#3">Q</a>: 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
-   <a href="http://fex.rus.uni-stuttgart.de/Vortrag/tosa.html";>http://fex.rus.uni-stuttgart.de/Vortrag/tosa.html</a>
-
-
-<a name="4"><a href="#4">Q</a>: Where can I get the F*EX sources?
-
-A: <a href="http://fex.rus.uni-stuttgart.de/fex.html";>http://fex.rus.uni-stuttgart.de/fex.html</a>
-
-
-<a name="5"><a href="#5">Q</a>: What do I need to install F*EX?
-
-A: A UNIX host with a DNS entry and smtp for outgoing e-mail.
-   And you must be root on this host.
-
-
-<a name="6"><a href="#6">Q</a>: What is DNS and smtp?
-
-A: Do not install F*EX. It is beyond your horizon.
-
-
-<a name="7"><a href="#7">Q</a>: Who is the author?
-
-A: Ulli Horlacher &lt;<a href="mailto:framstag@rus.uni-stuttgart.de";>framstag@rus.uni-stuttgart.de</a>>
-
-
-<a name="8"><a href="#8">Q</a>: Which licence does F*EX have?
-
-A: Perl Artistic free software, see <a href="http://fex.rus.uni-stuttgart.de/doc/Licence";>http://fex.rus.uni-stuttgart.de/doc/Licence</a>
-
-
-<a name="9"><a href="#9">Q</a>: Is there a F*EX mailing list?
-
-A: <a href="https://listserv.uni-stuttgart.de/mailman/listinfo/fex";>https://listserv.uni-stuttgart.de/mailman/listinfo/fex</a>
-
-
-<a name="10"><a href="#10">Q</a>: Where can I get commercial support for F*EX?   
-
-A: Contact <a href="mailto:fex@nepustil.net";>fex@nepustil.net</a> <a href="http://www.nepustil.net/";>http://www.nepustil.net/</a>
-
-
-<a name="11"><a href="#11">Q</a>: I have more/other questions than in this document!
-
-A: Ask the author &lt;<a href="mailto:framstag@rus.uni-stuttgart.de";>framstag@rus.uni-stuttgart.de</a>>
-
-
-<a name="User">
-User questions:
-===============
-
-<a name="12"><a href="#12">Q</a>: What is the "auth-ID"?
-
-A: The auth-ID is an internal identification which authentificates the user.
-   It will be first generated by the admin or the automatic registration
-   process and can later be modified by you, the user. Think of some kind
-   of a low security password.
-
-
-<a name="13"><a href="#13">Q</a>: Can I use a HTTP proxy?
-
-A: Yes.
-
-
-<a name="14"><a href="#14">Q</a>: I have uploaded a HUGE file but misspelled my recipient's address. Now I
-   have got an error bounce e-mail. Must I re-upload the HUGE file?
-
-A: No, it is not necessary. You can redirect the file with 
-   <a href="http://$HTTP_HOST$/rup";>http://$HTTP_HOST$/rup</a>
-
-
-<a name="15"><a href="#15">Q</a>: I have uploaded a HUGE file but forgot another recipient. 
-   Must I re-upload the HUGE file?
-
-A: No, it is not necessary. You can forward-copy the file with 
-   <a href="http://$HTTP_HOST$/foc";>http://$HTTP_HOST$/foc</a>
-
-
-<a name="16"><a href="#16">Q</a>: I cannot upload files > 2 GB with my web browser!?
-
-A: All web browsers I am aware of have bugs in their HTML-FORM
-   implementation. The limit mostly is 2 GB, sometimes 4 GB. 
-   
-   You have to use a special F*EX client to upload files > 2 GB, see
-   <a href="http://$HTTP_HOST$/tools.html";>http://$HTTP_HOST$/tools.html</a>
-
-
-<a name="17"><a href="#17">Q</a>: My download was aborted before it was finished. Can I resume the download?
-
-A: F*EX supports resuming at download, but your client also has to support
-   this feature. Firefox eg is missing this HTTP feature, you need an other
-   client like opera, wget or fexget.
-
-
-<a name="18"><a href="#18">Q</a>: My upload was aborted before it was finished. Can I resume the upload?
-
-A: F*EX supports resuming at upload, but your client also has to support it.
-   No web browser has this feature, you need a special F*EX client like
-   fexsend, schwuppdiwupp or F*IX. 
-   See <a href="http://$HTTP_HOST$/tools.html";>http://$HTTP_HOST$/tools.html</a>
-
-
-<a name="19"><a href="#19">Q</a>: My webbrowser cannot start the java client F*IX, it says:
-   "found no java runtime environment, cannot start F*IX upload applet"
-
-A: A java plugin for your webbrowser is missing. On Debian and Ubuntu you
-   can install it with: "sudo aptitude install sun-java6-plugin"
-
-
-<a name="20"><a href="#20">Q</a>: When I hit [ESC] in firefox the upload is canceled. Why?
-
-A: This is a built-in feature of firefox: ESC terminates the current operation.
-   Simple solution: do not hit ESC in Firefox. 
-   Complex solution: ask the Firefox developers to add keyboard configuration.
-
-
-<a name="21"><a href="#21">Q</a>: Sending as a F*EX user is easy, but how to receive files from others,
-   outside?
-
-A: Register them as your subusers or create a F*EX group 
-   with <a href="http://$HTTP_HOST$/fuc";>http://$HTTP_HOST$/fuc</a>
-   
-
-<a name="22"><a href="#22">Q</a>: 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 requests the file
-   several times at once. Otherwise they would report an error to the user.
-
-A: Your fexmaster has set AUTODELETE=DELAY as default, which means that
-   the autodelete cleanup process is called once a day.
-
-A: Power users (use the source, Luke!) can set a "do not delete after
-   download" flag.
-
-
-<a name="23"><a href="#23">Q</a>: The default keep time is too short for me, I need more. How can I set it?
-
-A: Use fexsend, ask your fexmaster or read the source code :-)
-
-
-<a name="24"><a href="#24">Q</a>: I cannot download files with Internet Explorer, it tells me "Cannot
-   open Internet site". What shall I do?
-   
-A: Use Firefox or any other Internet-compatible web browser, that Internet
-   Explorer is not. This is one of the many bugs of Internet Explorer.
-
-
-
-<a name="Admin">
-Admin questions:
-================
-
-<a name="25"><a href="#25">Q</a>: 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
-   <a href="http://gpl.univ-avignon.fr/filez/";>http://gpl.univ-avignon.fr/filez/</a>
-   <a href="http://freshmeat.net/projects/eventh/";>http://freshmeat.net/projects/eventh/</a>
-   <a href="http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html";>http://www.schaarwaechter.de/sp/projekte/dateiaustausch.html</a> (German only!)
-   which implement a file exchange as pure CGIs, but with a 2 GB file size limit.
-
-
-<a name="26"><a href="#26">Q</a>: F*EX is not working at all! I cannot connect to it with my web browser!
-
-A: Check your routing, ipfilters and firewall setup. 
-   Also check whether your xinetd is linked with tcp-wrapper and configure
-   it correctly (hosts.allow).
-   F*EX needs port 80/tcp (HTTP) and optional port 443/tcp (HTTPS).
-
-
-<a name="27"><a href="#27">Q</a>: F*EX is too complicated! I need something more simplified.
-
-A: Try <a href="http://www.home.unix-ag.org/simon/woof.html";>http://www.home.unix-ag.org/simon/woof.html</a>
-
-
-<a name="28"><a href="#28">Q</a>: How can I integrate F*EX in the existing user management at my site?
-
-A: F*EX has several authentification modules: 
-   local, RADIUS, LDAP, mailman and POP
-   For the last 4 please contact the author <a href="mailto:framstag@rus.uni-stuttgart.de";>framstag@rus.uni-stuttgart.de</a>
-   
-
-<a name="29"><a href="#29">Q</a>: I want that all of my local users can use F*EX automaticly. How?
-
-A: Let them register theirselves with http://yourfexserver/fur
-   You have to edit lib/fex.ph and set (example):
-   @local_hosts = qw(127.0.0.1 10.10.100.0-10.10.255.255);
-   @local_domains = qw(flupp.org ulm.sub.net);
-   (Of course you have to add your real local hosts/networks!)
-
-
-<a name="30"><a href="#30">Q</a>: I need more security! How can I enable HTTPS?
-
-A: Read doc/SSL and also look for "fop_auth" in doc/concept 
-
-
-<a name="31"><a href="#31">Q</a>: I need a corporate identity look. How can I configure F*EX in this way?
-
-A: See variable @H1_extra in fex.ph and you can add HTML code to 
-   htdocs/header.html
-
-A: See htdocs/fup_template.html, modify it to your needs and use it as your
-   start-page.
-
-
-<a name="32"><a href="#32">Q</a>: F*EX is too complicated for my tie users. I need a simplified upload form.
-
-A: See htdocs/fup_template.html
-
-
-<a name="33"><a href="#33">Q</a>: I want the Bcc mails to fex (admin user) to be sent to another address.
-
-A: Use procmail or write OTHERADDRESS to /home/fex/.forward
-
-
-<a name="34"><a href="#34">Q</a>: Can I get a localized version in my native languange?
-
-A: With your help, yes. Please contact the author <a href="mailto:framstag@rus.uni-stuttgart.de";>framstag@rus.uni-stuttgart.de</a>
-
-
-Misc questions:
-===============
-
-<a name="35"><a href="#35">Q</a>: F*EX is great! Can I join the developing team? What needs to be done?
-
-A: Contact the author <a href="mailto:framstag@rus.uni-stuttgart.de";>framstag@rus.uni-stuttgart.de</a>
-   Requested features are:
-   
-     - a F*EX plugin for thunderbird or outlook
-     - more (other) languange support
-</PRE></HTML>
diff -Nru fex-20140917/locale/german/htdocs/FAQ.html fex-20150120/locale/german/htdocs/FAQ.html
--- fex-20140917/locale/german/htdocs/FAQ.html	2012-09-28 19:00:04.000000000 +0200
+++ fex-20150120/locale/german/htdocs/FAQ.html	2014-11-26 17:06:53.000000000 +0100
@@ -1,4 +1,6 @@
 <HTML>
+<h3>Dieses Dokument ist veraltet. Bitte benutzen Sie die
+    <a href="/FAQ/meta.html?locale=english">englische FAQ</a>.</h3>
 <PRE>
 Allgemeine Fragen:
 ==================
diff -Nru fex-20140917/locale/italian/htdocs/FAQ.html fex-20150120/locale/italian/htdocs/FAQ.html
--- fex-20140917/locale/italian/htdocs/FAQ.html	2012-09-28 19:01:55.000000000 +0200
+++ fex-20150120/locale/italian/htdocs/FAQ.html	2014-12-02 12:47:54.000000000 +0100
@@ -1,4 +1,7 @@
 <HTML>
+<h3>Questo documento non &egrave; aggiornato. Si prega di utilizzare il
+    <a href="/FAQ/meta.html?locale=english">inglese FAQ</a>.</h3>
+<PRE>
 <PRE>
 Domande Generiche:
 ==================
diff -Nru fex-20140917/locale/spanish/htdocs/FAQ.html fex-20150120/locale/spanish/htdocs/FAQ.html
--- fex-20140917/locale/spanish/htdocs/FAQ.html	2012-09-28 19:00:54.000000000 +0200
+++ fex-20150120/locale/spanish/htdocs/FAQ.html	2014-11-26 16:52:42.000000000 +0100
@@ -1,4 +1,6 @@
 <HTML>
+<h3>Este documento est&aacute; obsoleto. Por favor, use
+   <a href="/FAQ/meta.html?locale=english">FAQ en ingl&eacute;s</a>.</h3>
 <PRE>
 Meta preguntas:
 ===============
@@ -12,11 +14,11 @@
 P: &iquest;Por qu&eacute; no emplear un servicio comercial
    DropLoad, ALLPeers, YouSendIt, etc?
    
-R: Tienen un l&iacute;mite de 2GB e incluso menos.
+R: Tienen un l&iacute;mite de 2GB incluso &oacute; menos.
    Su estado de privacidad y seguridad es desconocido.
    No est&aacute;n basados en software abierto.
    No existe ning&uacute;n cliente UNIX (CLI) para ellos.
-   Necesita java, active-X, flash u otros plugins endemeniados.
+   Necesitan java, active-X, flash u otros plugins endemeniados.
    Se desconoce cuanto durar&aacute;n - DropLoad y ALLPeers
    ha finalizado sus asuntos.
 
diff -Nru fex-20140917/locale/translations fex-20150120/locale/translations
--- fex-20140917/locale/translations	2014-09-15 16:02:51.000000000 +0200
+++ fex-20150120/locale/translations	2015-01-20 10:58:26.000000000 +0100
@@ -179,8 +179,8 @@
 si vous n'avez pas déjà un compte F*EX
 
 You may also use <a href="/fup?from=anonymous&to=$a">anonymous upload</a>
-Sie k&ouml;nnen auch <a href="/fup?from=anonymous&to=$a">anonymen Upload</a> verwenden.
-Du kosch au <a href="/fup?from=anonymous&to=$a">anonymes Nufflada</a> nemma.
+Sie k&ouml;nnen auch <a href="/fup?from=anonymous&to=$a">anonymen Upload</a> verwenden
+Du kosch au <a href="/fup?from=anonymous&to=$a">anonymes Nufflada</a> nemma
 Se tambien puede usar <a href="/fup?from=anonymous&to=$a">anonymous upload</a>
 Tamén pode usar <a href="/fup?from=anonymous&to=$a">o envío anónimo</a>
 Potresti anhe utilizzare <a href="/fup?from=anonymous&to=$a">anonymous upload</a>
@@ -188,13 +188,13 @@
 Vous pouvez aussi utiliser <a href="/fup?from=anonymous&to=$a">l'upload anonyme</a>
 
 You may also use <a href="/sup.html">simple upload</a>
-Sie k&ouml;nnen auch <a href="/sup.html">vereinfachten Upload</a> verwenden.
-Du kosch au <a href="/sup.html">oifachs Nufflada</a> nemma.
-You may also use <a href="/sup.html">simple upload</a>
-You may also use <a href="/sup.html">simple upload</a>
-You may also use <a href="/sup.html">simple upload</a>
-You may also use <a href="/sup.html">simple upload</a>
+Sie k&ouml;nnen auch <a href="/sup.html">vereinfachten Upload</a> verwenden
+Du kosch au <a href="/sup.html">oifachs Nufflada</a> nemma
+Puede tambien usar <a href="/sup.html">subir simplificado</a>
 You may also use <a href="/sup.html">simple upload</a>
+Potresti anche usare <a href="/sup.html">caricamento semplice</a>
+Můşete také pouşít <a href="/sup.html">jednoduché nahrávání</a>.
+Vous pouvez également utiliser <a href="/sup.html">l'upload simple</a>
 
 <code>$file</code> already exists for
 <code>$file</code> existiert bereits f&uuml;r
@@ -295,6 +295,24 @@
 Neodesílat <a href="/fuc?reminder=no&akey=$akey">şádná připomenutí</a> (současné nastavení: <em>odeslat upozornění</em>)
 Ne pas recevoir <a href="/fuc?reminder=no&akey=$akey">les rappels</a> par e-mail (configuration actuelle: <em>rappels envoyés</em>)
 
+Save</a> files after download (current setting: <em>display</em>
+Speichere</a> Dateien nach dem download (aktuelle Einstellung: <em>anzeigen</em>
+Schpeicher</a> die Dateia nochm Ronderlada (aktuelle Eischtellong: <em>ozeiga</em>
+Save</a> downloads (current setting: <em>display</em>
+Save</a> downloads (current setting: <em>display</em>
+Save</a> downloads (current setting: <em>display</em>
+Save</a> downloads (current setting: <em>display</em>
+Save</a> downloads (current setting: <em>display</em>
+
+Display</a> files when downloading with web browser (current setting: <em>save</em>
+Anzeige</a> von Dateien direkt beim download (aktuelle Einstellung: <em>abspeichern</em>
+Ozeiga</a> von Dateia beim Ronderlada (aktuelle Eischtellong: <em>abschpeichra</em>
+Display</a> downloads (current setting: <em>save</em>
+Display</a> downloads (current setting: <em>save</em>
+Display</a> downloads (current setting: <em>save</em>
+Display</a> downloads (current setting: <em>save</em>
+Display</a> downloads (current setting: <em>save</em>
+
 You will now get no reminder notification e-mails
 Sie werden nun keine Erinnerung E-Mails erhalten
 Du wirsch jetzt koine Drodenka E-Mails meh bekomma
@@ -331,6 +349,24 @@
 E-maily s upozorněním jsou nyní odesílány ve zkráceném formátu
 Les emails de notifications sont maintenant au format simple
 
+Downloads will now be saved
+Downloads werden nun gespeichert
+Downloads werdet ab jetzt gschpeichert
+Descargas están guardadas
+Downloads will now be saved
+I downloads saranno ora salvati
+Stahované soubory se nyní uloşí
+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)
+Descargas están indicadas ahora (si posible)
+Downloads will now be displayed (if possible)
+I downloads saranno mostrati (se possibile)
+Stahované soubory se nyní zobrazí (je-li to moşné)
+Les téléchargements cont maintenant être affichés (si possible)
+
 E-mail disclaimer reset to default
 Der E-Mail Disclaimer wurde auf Standard zur&uuml;ckgesetzt
 Dr E-Mail Oh&auml;ngsl isch wieder orginal
@@ -1008,7 +1044,7 @@
 
 delete file after download
 Datei nach dem Download l&ouml;schen
-Datie nochm Ronderlada l&ouml;scha
+Datei nochm Ronderlada l&ouml;scha
 borrar el fichero tras su descarga
 borrar o ficheiro trala s&uacute;a descarga
 cancella il file dopo il download
@@ -1036,11 +1072,11 @@
 delete file $autodelete days after download
 L&ouml;sche Datei $autodelete Tage nach dem Download
 L&ouml;sch Datei $autodelete Dag nochm ronderlada
+borrar archivo $autodelete dias despues del descargar
 delete file $autodelete days after download
-delete file $autodelete days after download
-delete file $autodelete days after download
-delete file $autodelete days after download
-delete file $autodelete days after download
+cancella file $autodelete giorni dopo il download
+smazat soubor po $autodelete dnech po staşení
+effacer $autodelete jours aprÚs le téléchargement
 
 F*EX service
 F*EX-Service
@@ -1411,6 +1447,15 @@
 a <
 et <
 
+You are a restricted user and may only fex to these recipients:
+Sie sind ein eingeschr&auml;nkter Benutzer und k&ouml;nnen nur an diese Empf&auml;nger fexen:
+Du bisch a eigeschr&auml;nktr Benutzr ond kosch bloss an die Empf&auml;ngr fexa:
+Usted es un usuario restringido y solo puede enviar a estos destinatarios:
+You are a restricted user and may only fex to these recipients:
+Sei un utente limitato e puoi inviare solo a questi destinatari:
+Jste uşivatel s omezením a můşete odesílat pouze těmto příjemcům:
+Vous êtes un utilisateur restreint et vous ne pouvez utiliser fex que pour ces destinataires:
+
 fex yourself
 eigene Adresse verwenden
 fex dir selbr
@@ -1780,6 +1825,15 @@
 velikost souboru
 taille du fichier
 
+No file selected
+Keine Datei ausgew&auml;hlt
+Koi Datei ausgw&auml;hlt
+Ningún archivo seleccionado
+No file selected
+Nessun file selezionato
+ŜádnÜ soubor nebyl vybrán
+Aucun fichier sélectionné
+
 no filename?!
 Kein Dateiname?!
 Koin Dateinome?!
@@ -2007,21 +2061,21 @@
 
 your e-mail address
 Ihre E-Mail Adresse
-dei E-Mail Adresss
+dei E-Mail Adress
 s&uacute; direcci&oacute;n de correo electr&oacute;nico
 o s&eacute;u enderezo de correo electr&oacute;nico
 il tuo indirizzo e-mail
 vaše e-mailová adresa
 votre adresse e-mail
 
-Subject: F*EX user registration
-Subject: F*EX-Benutzer Registrierung
-Subject: F*EX-Benutzrregischdrierong
-Sujeto: Registro del usuario de F*EX
-Suxeito: Rexistro do usuario de F*EX
-Oggetto: registrazione utente F*EX
-Předmět: Registrace F*EX uşivatele
-Sujet: Enregistrement F*EX
+F*EX user registration
+F*EX-Benutzer Registrierung
+F*EX-Benutzrregischdrierong
+Registro del usuario de F*EX
+Rexistro do usuario de F*EX
+Registrazione utente F*EX
+Registrace F*EX uÅŸivatele
+Enregistrement F*EX
 
 $user has been auto-registrated with
 $user wurde auto-registriert mit
@@ -2032,14 +2086,14 @@
 $user byl automaticky zaregistrován s
 l'utilisateur $user a été automatiquement enregistré avec
 
-Subject: F*EX user registration request
-Subject: F*EX-Benutzer Registrierungs-Anfrage
-Subject: F*EX-Benutzrregischdrierongsofrog
-Sujeto: Petici&oacute;n de registro de usuario F*EX
-Suxeito: Petici&oacute;n de rexistro de usuario F*EX
-Oggetto: richiesta registrazione utente F*EX
-Předmět: Poşadavek na zaregistrování F*EX uşivatele
-Sujet: Demande d'enregistrement d'utilisateur F*EX
+F*EX user registration request
+F*EX-Benutzer Registrierungs-Anfrage
+F*EX-Benutzrregischdrierongsofrog
+Petici&oacute;n de registro de usuario F*EX
+Petici&oacute;n de rexistro de usuario F*EX
+Richiesta registrazione utente F*EX
+Poşadavek na zaregistrování F*EX uşivatele
+Demande d'enregistrement d'utilisateur F*EX
 
 To activate your new F*EX account go to this URL:
 Um Ihren neuen F*EX-Account zu aktivieren oeffnen Sie diese URL:
@@ -2089,11 +2143,11 @@
 is not an email address
 ist keine E-Mail-Adresse
 isch koi E-Mail-Adress
+no es una dirección email
 is not an email address
-is not an email address
-is not an email address
-is not an email address
-is not an email address
+non Ú un indirizzo e-mail
+není e-mailová adresa
+n'est pas une adresse électronique
 
 F*EX redirect ERROR
 F*EX Umadressierungs-FEHLER
@@ -2365,6 +2419,15 @@
 Pro více informací se podívejte na $index
 Voir $index pour plus d'informations
 
+No notification e-mail has been sent to $to
+Es wurde keine Benachrichtigungs-E-Mail an $to verschickt
+$to isch ned benochritigt worda
+No se ha enviado ning&uacute;n correo electr&oacute;nico de notificaci&oacute;n a $to
+No notification e-mail has been sent to $to
+Nessuna e-mail di notifica Ú stata inviata a $to
+ŜádnÜ e-mail s oznámením nebyl $to odeslán
+Aucun email de notification n'a été envoyé à $to
+
 Ehh... $ndata <b>BYTES</b>?! You are kidding
 Moment... $ndata <b>BYTES</b>?! Das ist wohl ein Witz
 Moment amole $ndata <b>BYTES</b>?! Wilsch me verarsche
@@ -2410,23 +2473,23 @@
 URL pro staşení okopírovat a vloşit
 URL de téléchargement pour copier/coller
 
-Link is valid for $keep days
-Dieser Link ist f&uuml;r $keep Tage g&uuml;ltig
-Sell Link isch fir $keep Dag giltig
-El enlace es válido durante $keep días
-A ligazón é válida durante $keep días
-Il link Ú valido per $keep giorni
-Odkaz je platnÜ $keep dny(ů)
-Le lien restera valide $keep jours
-
-old $file for $to overwritten
-vorhandenes $file f&uuml;r $to &uuml;berschrieben
-alts $file fir $to iberschrieba
-El anterior $file para $to se ha sobreescrito
-Sobrescribiuse o anterior $file para $to
-vecchio file $file per $to sovrascritto
-původní soubor $file pro $to byl nahrazen
-$file pour $to reécrit.
+Link is valid for $keep{$to} days
+Dieser Link ist f&uuml;r $keep{$to} Tage g&uuml;ltig
+Sell Link isch fir $keep{$to} Dag giltig
+El enlace es válido durante $keep{$to} días
+A ligazón é válida durante $keep{$to} días
+Il link Ú valido per $keep{$to} giorni
+Odkaz je platnÜ $keep{$to} dny(ů)
+Le lien restera valide $keep{$to} jours
+
+old <code>$file</code> for $to overwritten
+vorhandenes <code>$file</code> f&uuml;r $to &uuml;berschrieben
+alts <code>$file</code> fir $to iberschrieba
+El anterior <code>$file</code> para $to se ha sobreescrito
+Sobrescribiuse o anterior <code>$file</code> para $to
+vecchio file <code>$file</code> per $to sovrascritto
+původní soubor <code>$file</code> pro $to byl nahrazen
+<code>$file</code> pour $to reécrit.
 
 $to notified
 $to benachrichtigt
@@ -2437,6 +2500,24 @@
 $to byl informován
 $to prévenu
 
+<code>$file</code> removed because you are a restricted user
+<code>$file</code> wurde gel&ouml;scht weil Sie ein eingeschr&auml;nkter Benutzer sind 
+<code>$file</code> isch wieder gl&ouml;scht worda weil du a bschr&auml;nkter Benutzer bisch
+<code>$file</code> se ha borrado porque usted es un usuario restringido
+<code>$file</code> removed because you are a restricted user
+<code>$file</code> rimosso perchÚ sei un utente limitato
+<code>$file</code> odstraněn, protoşe jste uşivatel s omezením
+<code>$file</code> supprimé car vous êtes un utilisateur restreint
+
+and recipient $to cannot receive e-mail
+und Empf&auml;nger $to keine E-Mail empfangen kann
+ond Empf&auml;ngr $to ko koi E-Mail empfanga
+y el destinatario $to no puede recibir correos electr&oacute;nicos
+and recipient $to cannot receive e-mail
+ed il destinatario $to non può ricevere e-mail
+a příjemce $to nemůşe přijímat poštu
+et le destinataire $to ne peut pas recevoir d'email
+
 send another file
 eine weitere Datei schicken
 a weitere Datei schicka
@@ -2692,80 +2773,80 @@
 ERROR: no upload received
 FEHLER: es wurde kein Upload empfangen
 FEHLER: do isch fei nix okomma
+ERROR: ningún subir recibido
 ERROR: no upload received
-ERROR: no upload received
-ERROR: no upload received
-ERROR: no upload received
-ERROR: no upload received
+ERRORE: nessun caricamento ricevuto
+CHYBA: şádnÜ soubor nebyl nahrán
+ERREUR: aucun upload reçu
 
 You cannot send to more than one group
 Sie k&ouml;nnen nicht an mehrere Gruppen senden
 Du kansch net an mehrere Gruppa senda
+No puede enviar a varios grupos
 You cannot send to more than one group
-You cannot send to more than one group
-You cannot send to more than one group
-You cannot send to more than one group
-You cannot send to more than one group
+Non puoi spedire a più di un gruppo
+Nemůşete odesílat více neş jedné skupině
+Vous ne pouvez pas envoyer à plus d'un groupe
 
 file transfer aborted
 Dateitransfer abgebrochen
 Dateitransfer abbrocha
+Trasmisión del archivo aborto
 file transfer aborted
-file transfer aborted
-file transfer aborted
-file transfer aborted
-file transfer aborted
+trasferimento file interrotto
+přenos souboru přerušen
+transfert de fichier abandonné
 
 <code>$from</code> is not allowed to upload from IP $ra
 <code>$from</code> darf nicht von IP-Adresse $ra hochladen
 <code>$from</code> darf ned von IP-Adress $ra hochlada
+<code>$from</code> no está permitido a subir de esta dirección IP $ra
 <code>$from</code> is not allowed to upload from IP $ra
-<code>$from</code> is not allowed to upload from IP $ra
-<code>$from</code> is not allowed to upload from IP $ra
-<code>$from</code> is not allowed to upload from IP $ra
-<code>$from</code> is not allowed to upload from IP $ra
+<code>$from</code> non Ú consentito caricarlo da IP $ra
+<code>$from</code> z IP adresy $ra není dovoleno nahrávat soubory
+<code>$from</code> n'est pas autorisé à uploader vers l'IP $ra
 
 Group <code>$to</code> does not exist
 Gruppe <code>$to</code> existiert nicht
 Grupp <code>$to</code> gibts net
+Grupo <code>$to</code> no existe
 Group <code>$to</code> does not exist
-Group <code>$to</code> does not exist
-Group <code>$to</code> does not exist
-Group <code>$to</code> does not exist
-Group <code>$to</code> does not exist
+Il gruppo <code>$to</code> non esiste
+Skupina <code>$to</code> neexistuje
+Le groupe <code>$to</code> n'existe pas
 
 server runs in NOMAIL mode - groups ($to) are not allowed
 Server l&auml;uft im NOMAIL Modus - Gruppen ($to) sind nicht erlaubt
 Server l&auml;uft em NOMAIL Modus - Gruppa ($to) senn net erlaubt
+Servidor está en modo NOMAIL - grupos ($to) no están permitidos
 server runs in NOMAIL mode - groups ($to) are not allowed
-server runs in NOMAIL mode - groups ($to) are not allowed
-server runs in NOMAIL mode - groups ($to) are not allowed
-server runs in NOMAIL mode - groups ($to) are not allowed
-server runs in NOMAIL mode - groups ($to) are not allowed
+server eseguto in modalità NOMAIL - i gruppi ($to) non sono permessi
+Server běşí v reşimu NOMAIL - skupiny ($to) nejsou povoleny
+Le serveur tourne en mode NOMAIL - les groupes ($to) ne sont pas autorisés
 
 File not found
 Datei nicht gefunden
 Datei net gfonda
+Archivo no encontrado
 File not found
-File not found
-File not found
-File not found
-File not found
+File non trovato
+Soubor nenalezen
+Fichier introuvable
 
 <code>$to</code> is not a valid recipient
 <code>$to</code> ist kein g&uuml;ltiger Empf&auml;nger
 <code>$to</code> isch koi g&uuml;ltigr Empf&auml;ngr
+<code>$to</code> no es un destinario válido
 <code>$to</code> is not a valid recipient
-<code>$to</code> is not a valid recipient
-<code>$to</code> is not a valid recipient
-<code>$to</code> is not a valid recipient
-<code>$to</code> is not a valid recipient
+<code>$to</code> non Ú un destinatario valido
+<code>$to</code> není platnÜ příjemce
+<code>$to</code> n'est pas un destinataire valide
 
 File $file already exists in your outgoing spool
 Datei $file existiert bereits im ausgehenden Spool
 Datei $file gibts fei scho em nausganganda Spool
+Archivo $file ya existe en sú spool saliente
 File $file already exists in your outgoing spool
-File $file already exists in your outgoing spool
-File $file already exists in your outgoing spool
-File $file already exists in your outgoing spool
-File $file already exists in your outgoing spool
+Il file $file esiste già nel tuo spool in uscita
+Soubor $file se jiş ve frontě k odeslání nachází
+Le fichier $file exite déjà dans votre spool sortant
diff -Nru fex-20140917/upgrade fex-20150120/upgrade
--- fex-20140917/upgrade	2014-08-27 19:16:27.000000000 +0200
+++ fex-20150120/upgrade	2014-12-17 09:41:40.000000000 +0100
@@ -83,11 +83,14 @@
   $premiss++;
 }
 
-if ( -x '/usr/lib/sendmail') {
-  print "found /usr/lib/sendmail\n";
-} elsif ( -x '/usr/sbin/sendmail') {
-  print "found /usr/sbin/sendmail\n";
-} else {
+foreach (qw'/usr/lib/sendmail /usr/sbin/sendmail') {
+  if (-x) {
+    $sendmail = $_;
+    print "found $sendmail\n";
+    last;
+  }
+}
+unless ($sendmail) {
   print "sendmail NOT found\n";
   $premiss++;  
 }
@@ -171,7 +174,7 @@
 if  (-d "$FEXHOME/spool") {
   warn "checking spool ...\n";
   &convert_spool;
-  system "chown -R fex $spooldir";
+  system "chown -R fex $spooldir/";
 } else {
   $newinstall = $FEXHOME;
   chmod 0700,$FEXHOME;
@@ -365,23 +368,33 @@
   print "\n";
   print "F*EX update installed.\n";
   print "You can inform your users about the new features with:\n";
-  print "$FEXHOME/bin/fexwall 'new features on $hostname' ".
+  print "$FEXHOME/bin/fexwall 'new F*EX features on $hostname' ".
         "< $FEXHOME/doc/newfeatures\n";
 }
 
 if (@local_rdomains and not @local_rhosts) {
-  print "WARNING:\n";
+  print "\nWARNING:\n";
   print "In $fph you have @local_rdomains but not @local_rhosts!\n";
   print "Selfregistrating of external users will not work!\n";
   print "See ${fph}_new/\n";
 }
-  
+
+if (`$sendmail -h 2>&1` =~ /exim/ and 
+    `grep trusted_users /etc/exim4/exim4.conf 2>/dev/null` !~ /\bfex\b/) {
+  print "\nWARNING:\n";
+  print "$sendmail is exim\n";
+  print "You MUST set in your exim4.conf:\n";
+  print "trusted_users = mail : uucp : fex\n";
+}
 exit;
 
 
 sub convert_spool {
   my ($f,$d,$to,$from,$link);
   
+  local $) = $FEX[3];
+  local $> = $FEX[2];
+
   our ($spooldir,$skeydir,$gkeydir);
   $ENV{FEXLIB} = $FEXLIB = "$FEXHOME/lib";
   require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";

Attachment: signature.asc
Description: Digital signature


Reply to: