3 # CLI client for the F*EX service (send, list, delete)
 
   7 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
 
   9 # Perl Artistic Licence
 
  12 use strict qw'vars subs';
 
  21 use Fcntl qw':flock :mode';
 
  22 use Digest::MD5 qw'md5_hex';  # encrypted ID / SID
 
  23 use Time::HiRes qw'time';
 
  24 # use Smart::Comments;
 
  25 use constant k => 2**10;
 
  26 use constant M => 2**20;
 
  28 eval 'use Net::INET6Glue::INET_is_INET6';
 
  30 &update if "@ARGV" eq 'UPDATE';
 
  34 our ($SH,$fexhome,$idf,$tmpdir,$windoof,$useragent,$editor,$nomail);
 
  35 our ($anonymous,$public);
 
  36 our ($tpid,$frecipient);
 
  37 our ($FEXID,$FEXXX,$HOME);
 
  40 our $version = 20150729;
 
  44 my %SSL = (SSL_version => 'TLSv1');
 
  47 if ($Config{osname} =~ /^mswin/i) {
 
  48   $windoof = $Config{osname};
 
  49   $HOME = $ENV{USERPROFILE};
 
  50   $fexhome = $ENV{FEXHOME} || $HOME.'\fex';
 
  51   $tmpdir = $ENV{FEXTMP} || $ENV{TEMP} || "$fexhome\\tmp";
 
  52   $idf = "$fexhome\\id";
 
  53   $editor = $ENV{EDITOR} || 'notepad.exe';
 
  54   $useragent = sprintf("fexsend-$version (%s %s)",
 
  55                        $Config{osname},$Config{archname});
 
  56   $SSL{SSL_verify_mode} = 0;
 
  59   $HOME = (getpwuid($<))[7]||$ENV{HOME};
 
  60   $fexhome = $HOME.'/.fex';
 
  61   $tmpdir = $ENV{FEXTMP} || "$fexhome/tmp";
 
  63   $editor = $ENV{EDITOR} || 'vi';
 
  64   $_ = `(lsb_release -d||uname -a)2>/dev/null`||'';
 
  67   $useragent = "fexsend-$version ($_)";
 
  71 if (-f ($_ = '/etc/fex/config.pl')) {
 
  72   eval { require } or warn $@;
 
  80 my $atype = '';         # archive type
 
  81 my $fexcgi;             # F*EX CGI URL
 
  82 my @files;              # files to send
 
  83 my %AB = ();            # server based address book
 
  84 my ($server,$port,$sid,$https);
 
  86 my $proxy_prefix = '';
 
  88 my $timeout = 30;       # server timeout
 
  89 my $fexlist = "$tmpdir/fexlist";
 
  94   $usage = "usage: send file(s):               xx [:slot] file...\n".
 
  95            "   or: send STDIN:                 xx [:slot] -\n".
 
  96            "   or: send pipe:                  ... | xx [:slot] \n".
 
  97            "   or: get file(s) or STDIN:       xx [:slot] \n".
 
  98            "   or: get file(s) no-questions:   xx [:slot] --\n".
 
  99            "examples: dmesg | xx\n".
 
 102            "          xx :conf /etc /boot\n";
 
 105 usage: $0 [options] file(s) [@] recipient(s)
 
 106    or: $0 [special options]
 
 107    or: $0 -f \# recipient(s)
 
 108    or: $0 -x \# [-C -k -D -K -S]
 
 109 options: -v           verbose mode
 
 110          -d           delete file on fex server
 
 111          -c           compress file with gzip
 
 112          -g           encrypt file with gpg
 
 113          -m limit     limit throughput (kB/s)
 
 114          -i tag       use ID data [tag] from ID file
 
 115          -C comment   add comment to notification e-mail
 
 116          -k max       keep file max days on fex server
 
 117          -D           delay auto-delete after download
 
 118          -K           no auto-delete after download
 
 119          -M           MIME-file (to be displayed in recipient\'s webbrowser)
 
 120          -o           overwrite mode, do not resume
 
 121          -a archive   put files in archive (.zip .7z .tar .tgz)
 
 122          -s stream    read data from pipe and upload it with stream name
 
 123 special options: -I      initialize ID file or show ID
 
 124                  -I tag  add alternate ID data (secondary logins) to ID file
 
 125                  -l      list sent files numbered (# needed for -f -x -d -N)
 
 126                  -f \#    forward already uploaded file to another recipient
 
 127                  -x \#    modify options -C -k -D -K for already uploaded file
 
 128                  -d \#    delete file on fex server
 
 129                  -N \#    resend notification e-mail
 
 131                  -A      edit server address book (aliases)
 
 132                  -S      show server/user settings and auth-ID
 
 133                  -H      show hints, examples and more options
 
 135                  (\# is a file number, see output from $0 -l)
 
 136 examples: $0 visualization.mpg framstag\@rus.uni-stuttgart.de
 
 137           $0 -a images.zip *.jpg webmaster\@flupp.org,metoo
 
 138           lshw | $0 -s hardware.list admin\@flupp.org
 
 140 #   or: $0 -R FEX-URL e-mail
 
 141 #         -R FEX mail  self-register your e-mail address at FEX server
 
 144 $0 hints and more options:
 
 146 usage: $0 [options] file recipient(s)
 
 148 Recipient can be a comma separated address list. Example:
 
 149   $0 big.file framstag\@rus.uni-stuttgart.de,webmaster\@flupp.org
 
 151 Recipient can be an alias from your server address book 
 
 152 (use "$0 -A" to edit it). Example:
 
 155 Recipient can be a SKEY URL, which you have received from a regular F*EX user.
 
 156 When using this URL you are a subuser of this full user and the file will be 
 
 157 sent to him. Example:
 
 158   $0 big.file http://fex.rus.uni-stuttgart.de/fup?skey=4285f8cdd881626524fba686d5f0a83a
 
 160 Recipient can be a GKEY URL, which you have received from a regular F*EX user.
 
 161 Using this URL you are a member of his group and the file will be sent to all
 
 162 members of this group. Example:
 
 163   $0 big.file http://fex.rus.uni-stuttgart.de/fup?gkey=50d26547b1e8c1110beb8748fc1d9444
 
 165 When you use "FEX-URL/anonymous" as recipient and your F*EX administrator has 
 
 166 allowed anonymous upload for your IP address then no auth-ID is needed.
 
 168 "." as recipient means fex to yourself and show immediately the download URL 
 
 169 (no notification e-mail will be sent). Example:
 
 172 "//" as recipient means fex to yourself and create extra short download URL.
 
 176 If you want a Bcc of the notification e-mail then add '!bcc!' to the comment:
 
 177 fexsend -C '!bcc! for me and you' ...
 
 179 Additional special options:
 
 181   -. sends a short instead of a detailed notification e-mail
 
 182   -/ does not upload the file, but tells the server to link it
 
 183   -= uses an alias name as file name
 
 184   -# excludes files (# is list separator) from archive -a
 
 185   -n sends no notification e-mail, but shows the download URL immediately
 
 187   -r ADDRESS sets e-mail Reply-To ADDRESS
 
 188   -F activates female mode
 
 189   -U show authorized URL
 
 190   -+ is an undocumented feature - test it :-)
 
 192 To manage your subuser and groups or forward or redirect files, use a 
 
 193 webbrowser with the URL from "$0 -U", e.g.:  firefox \$($0 -U)
 
 195 If you want to copy-forward an already uploaded file to another recipient,
 
 196 then you first have to query the file number with:
 
 198 and then copy-forward it with:
 
 199   $0 -b # other\@address
 
 200 Where # is the file number.
 
 202 You can list an uploaded file in more detail with
 
 204 Where # is the file number.
 
 206 If you want to modify the keep time, comment or auto-delete behaviour of an
 
 207 already uploaded file then you first have to query the file number with:
 
 209 and then for example set the keep time to 30 days with:
 
 211 Where # is the file number.
 
 213 With option -a you can send several files or whole directories within a single
 
 214 archive file. The archive types tar and tgz are build on-the-fly (streaming) 
 
 215 whereas archive types zip and 7z need a temporary archive file on local disk.
 
 217 With option -s you can send any data coming from a pipe (STDIN) as a file
 
 218 without wasting local disc space.
 
 220 With option -X you can specify any parameter, e.g.: -X autodelete=yes
 
 222 For HTTPS you can set the environment variables:
 
 223 SSLVERIFY=1                 # activate server identity verification
 
 224 SSLVERSION=TLSv1            # this is the default
 
 225 SSLCAPATH=/etc/ssl/certs    # path to trusted (root) certificates
 
 226 SSLCAFILE=/etc/ssl/cert.pem # file with trusted (root) certificates
 
 227 SSLCIPHERLIST=HIGH:!3DES    # see http://www.openssl.org/docs/apps/ciphers.html
 
 229 Partner program xx is an internet clipboard. See: xx -h
 
 231 Partner program fexget is for downloading. See: fexget -h
 
 233 For temporary usage of a HTTP proxy use: 
 
 234   $0 -P your_proxy:port:chunksize_in_MB file recipient
 
 236   $0 -P wwwproxy.uni-stuttgart.de.de:8080:1024 4GB.tar .
 
 238 For temporary usage of an alternative F*EX server or user use: 
 
 239   FEXID="FEXSERVER USER AUTHID" $0 file recipient
 
 241   FEXID="fex.flupp.org gaga\@flupp.org blubb" $0 big.file framstag\@rus.uni-stuttgart.de
 
 243 You can define aliases (and optional fexsend options) in \$HOME/.fex/config.pl:
 
 245     'alias1' => 'user1\@domain1.org',
 
 246     'alias2' => 'user2\@domain2.org',
 
 247     'both'   => 'user1\@domain1.org,user2\@domain2.org',
 
 248     'extra'  => 'extra\@special.net:-i other -K -k 30',
 
 251 fexsend also respects aliases in $HOME/.mutt/aliases
 
 252 The alias priority is (descending):
 
 253 \$HOME/.fex/config.pl
 
 255 fexserver address book  
 
 257 In \$HOME/.fex/config.pl you can also set the SSL* environment variables and the
 
 258 \$opt_* variables, e.g.:
 
 260 \$ENV{SSLVERSION} = 'TLSv1';
 
 280 if ($windoof and not @ARGV and not $ENV{PROMPT}) {
 
 281   # restart with cmd.exe to have mouse cut+paste
 
 282   exec qw'cmd /k',$0,'-W';
 
 286 unless (-d $fexhome) {
 
 287   mkdir $fexhome,0700 or die "$0: cannot create FEXHOME $fexhome - $!\n";
 
 290 unless (-d $tmpdir) {
 
 291   mkdir $tmpdir,0700 or die "$0: cannot create tmpdir $tmpdir - $!\n";
 
 294 my @_ARGV = @ARGV; # save arguments
 
 296 our ($opt_q,$opt_h,$opt_H,$opt_v,$opt_m,$opt_c,$opt_k,$opt_d,$opt_l,$opt_I,
 
 297      $opt_K,$opt_D,$opt_u,$opt_f,$opt_a,$opt_C,$opt_R,$opt_M,$opt_L,$opt_Q,
 
 298      $opt_A,$opt_i,$opt_z,$opt_Z,$opt_b,$opt_P,$opt_x,$opt_X,$opt_V,$opt_U,
 
 299      $opt_s,$opt_o,$opt_g,$opt_F,$opt_n,$opt_r,$opt_S,$opt_N);
 
 302   $opt_q = 1 if @ARGV and $ARGV[-1] eq '--' and pop @ARGV or not -t STDOUT;
 
 303   $opt_h = $opt_v = $opt_m = $opt_I = 0;
 
 305   $_ = "$fexhome/config.pl"; require if -f;
 
 306   getopts('hvIm:') or die $usage;
 
 308   $opt_h = $opt_v = $opt_m = $opt_c = $opt_k = $opt_d = $opt_l = $opt_I = 0;
 
 309   $opt_H = $opt_K = $opt_D = $opt_R = $opt_M = $opt_L = $opt_Q = $opt_A = 0;
 
 310   $opt_x = $opt_o = $opt_g = $opt_V = $opt_U = $opt_F = $opt_n = $opt_q = 0;
 
 312   ${'opt_@'} = ${'opt_!'} = ${'opt_+'} = ${'opt_.'} = ${'opt_/'} = 0;
 
 313   ${'opt_='} = ${'opt_#'} = '';
 
 314   $opt_u = $opt_f = $opt_a = $opt_C = $opt_i = $opt_b = $opt_P = $opt_X = '';
 
 315   $opt_s = $opt_r = '';
 
 316   $_ = "$fexhome/config.pl"; require if -f;
 
 317   getopts('hHvcdognVDKlILUARWMFzZqQS@!+./r:m:k:u:f:a:s:C:i:b:P:x:X:N:=:#:') 
 
 326     print "Version: $version\n";
 
 329   if ($opt_K and $opt_D) {
 
 330     die "$0: you cannot use both options -D and -K\n";
 
 333   if ($opt_a and $opt_c) {
 
 334     die "$0: you cannot use both options -a and -c\n";
 
 337   if ($opt_a and $opt_s) {
 
 338     die "$0: you cannot use both options -a and -s\n";
 
 341   if ($opt_g and $opt_c) {
 
 346   if ($opt_f and $opt_f !~ /^\d+$/) {
 
 347     die "$0: option -f needs a number, see $0 -l\n";
 
 350   if ($opt_I and $opt_R) {
 
 351     die "$0: you cannot use both options -I and -R\n";
 
 354   # $opt_C is COMMENT command in F*EX protocol
 
 357     ($opt_l or $opt_L)  ? 'LIST':
 
 358     ($opt_Q)            ? 'CHECKQUOTA':
 
 359     ($opt_S)            ? 'LISTSETTINGS':
 
 360     ($opt_Z)            ? 'RECEIVEDLOG':
 
 361     ($opt_z)            ? 'SENDLOG':
 
 362     (${'opt_!'})        ? 'FOPLOG':
 
 374   female_mode("show help?") if $opt_F;
 
 386 die $usage if $opt_m and $opt_m !~ /^\d+/;
 
 389   if ($opt_P =~ /^([\w.-]+:\d+)(:(\d+))?/) {
 
 391     $chunksize = $3 || 0;
 
 393     die "$0: proxy must be: SERVER:PORT\n";
 
 397 if ($FEXID = $ENV{FEXID}) {
 
 398   $FEXID = decode_b64($FEXID) if $FEXID !~ /\s/;
 
 399   ($fexcgi,$from,$id) = split(/\s+/,$FEXID);
 
 401   if ($windoof and not -f $idf) { &init_id }
 
 402   if (open $idf,$idf) {
 
 409   # convert old idxx file
 
 410   if ($idf and open $idf,$idf.'xx') {
 
 413     if (open $idf,'>>',$idf) {
 
 414       print {$idf} "\n[xx]\n",
 
 424   if ($FEXXX = $ENV{FEXXX}) {
 
 425     $FEXXX = decode_b64($FEXXX) if $FEXXX !~ /\s/;
 
 426     ($fexcgi,$from,$id) = split(/\s+/,$FEXXX);
 
 427   } elsif (open $idf,$idf) {
 
 430         $proxy = $proxy_prefix = '';
 
 442     $proxy = $proxy_prefix = '';
 
 443     open $idf,$idf or die "$0: cannot open $idf - $!\n";
 
 451     die "$0: no [$opt_i] in $idf\n" unless $_;
 
 456   if ($xx) { &show_id } 
 
 461 if (@ARGV > 1 and $ARGV[-1] =~ /(^|\/)anonymous/) {
 
 462   $fexcgi = $1 if $ARGV[-1] =~ s:(.+)/::;
 
 463   die "usage: $0 [options] file FEXSERVER/anonymous\n" unless $fexcgi;
 
 464   $anonymous = $from = 'anonymous';
 
 465   $sid = $id = 'ANONYMOUS';
 
 466 } elsif (@ARGV > 1 and $id eq 'PUBLIC') {
 
 467   $public = $sid = $id;
 
 468 } elsif (@ARGV > 1 and $ARGV[-1] =~ m{^(https?://[\w.-]+(:\d+)?/fup\?[sg]key=\w+)}) {
 
 470   $skey = $1 if $fexcgi =~ /skey=(\w+)/;
 
 471   $gkey = $1 if $fexcgi =~ /gkey=(\w+)/;
 
 474   $fexcgi = $opt_u if $opt_u;
 
 476   if (not -e $idf and not ($fexcgi and $from and $id)) {
 
 477     die "$0: no ID file $idf found, use \"fexsend -I\" to create it\n";
 
 481     die "$0: no FEX URL found, use \"$0 -u URL\" or \"$0 -I\"\n";
 
 484   unless ($from and $id) {
 
 485     die "$0: no sender found, use \"$0 -f FROM:ID\" or \"$0 -I\"\n";
 
 488   if ($fexcgi !~ /^http/) {
 
 489     if ($fexcgi =~ /:443/) { $fexcgi = "https://$fexcgi" }
 
 490     else                   { $fexcgi = "http://$fexcgi" }
 
 498 $port = 443 if $server =~ s{https://}{};
 
 499 $port = $1  if $server =~ s/:(\d+)//;
 
 502   # $opt_s and die "$0: cannot use -s with https due to stunnel bug\n"; 
 
 503   # $opt_g and die "$0: cannot use -g with https due to stunnel bug\n"; 
 
 507 $server =~ s{http://}{};
 
 510 # $chunksize = 4*k unless $chunksize;
 
 514   if    ($port == 80)  { $proxy_prefix = "http://$server" }
 
 515   elsif ($port != 443) { $proxy_prefix = "http://$server:$port" }
 
 518 # xx: special file exchange between own accounts
 
 520   my $transferfile = "$tmpdir/STDFEX";
 
 523     $transferfile = "$tmpdir/xx:xxx";
 
 524   } elsif (@ARGV and $ARGV[0] =~ /^:([\w.=+-]+)$/) {
 
 525     $transferfile = "$tmpdir/xx:$1";
 
 528   open my $lock,'>>',$transferfile 
 
 529     or die "$0: cannot write $transferfile - $!\n";
 
 530   flock($lock,LOCK_EX|LOCK_NB)
 
 531     or die "$0: $transferfile is locked by another process\n";
 
 532   truncate $transferfile,0;
 
 533   if (not @ARGV and -t) {
 
 534     &get_xx($transferfile);
 
 536     &send_xx($transferfile);
 
 543 &inquire if $windoof and not @ARGV and not
 
 544             ($opt_l or $opt_L or $opt_Q or $opt_A or $opt_U or $opt_I or
 
 545              $opt_f or $opt_x or $opt_N);
 
 548   $opt_C = "!SHORTMAIL! $opt_C";
 
 551 if ($opt_n or $opt_C =~ /NOMAIL|!#!/) {
 
 555 unless ($skey or $gkey or $anonymous) {
 
 557     $opt_f||$opt_x||$opt_Q||$opt_l||$opt_L||$opt_U||$opt_z||$opt_Z||$opt_A
 
 558     ||$opt_d||${'opt_!'}||${'opt_@'})
 
 559   ) { warn "Server/User: $fexcgi/$from\n" }
 
 562 if    ($opt_V and not @ARGV)            { exit }
 
 563 if    ($opt_f)                          { &forward } 
 
 564 elsif ($opt_x)                          { &modify } 
 
 565 elsif ($opt_N)                          { &renotify } 
 
 566 elsif ($opt_Q)                          { &query_quotas } 
 
 567 elsif ($opt_S)                          { &query_settings } 
 
 568 elsif ($opt_l or $opt_L)                { &list } 
 
 569 elsif ($opt_U)                          { &show_URL } 
 
 570 elsif ($opt_z or $opt_Z or ${'opt_!'})  { &get_log } 
 
 571 elsif ($opt_A)                          { edit_address_book($from) }
 
 572 elsif (${'opt_@'})                      { &show_address_book } 
 
 573 elsif ($opt_d and $anonymous)           { &purge }
 
 574 elsif ($opt_d and $ARGV[-1] =~ /^\d+$/) { &delete }
 
 580 # initialize ID file or show ID
 
 590   $fexcgi = $from = $id = '';
 
 592   unless (-d $fexhome) {
 
 593     mkdir $fexhome,0700 or die "$0: cannot create FEXHOME $fexhome - $!\n";
 
 597   if (not $tag and open $idf,$idf) {
 
 600         last if /^\[$opt_i\]/;
 
 608       chomp($fexcgi,$from,$id);
 
 609       $FEXID = encode_b64("$fexcgi $from $id");
 
 611         print "# hint: to edit the ID file $idf use \"$0 -I .\" #\n";
 
 612         print "export FEXID=$FEXID\n";
 
 613         print "history -d \$((HISTCMD-1));history -d \$((HISTCMD-1))\n";
 
 615         print "FEXID=$FEXID\n";
 
 619       die "$0: no ID data found\n";
 
 623   if ($tag and $tag eq '.') { exec $ENV{EDITOR}||'vi',$idf }
 
 625   if ($tag) { print "F*EX server URL for [$tag]: " }
 
 626   else      { print "F*EX server URL: " }
 
 628   $fexcgi =~ s/[\s\n]//g;
 
 629   die "you MUST provide a FEX-URL!\n" unless $fexcgi;
 
 630   if ($fexcgi =~ /\?/) {
 
 631     $from = $1 if $fexcgi =~ /\bfrom=(.+?)(&|$)/i;
 
 632     $id   = $1 if $fexcgi =~ /\bid=(.+?)(&|$)/i;
 
 633     # $skey = $1 if $fexcgi =~ /\bskey=(.+?)(&|$)/i;
 
 634     # $gkey = $1 if $fexcgi =~ /\bgkey=(.+?)(&|$)/i;
 
 635     die "$0: cannot use GKEY URL in ID file\n" if $fexcgi =~ /gkey=/i;
 
 636     die "$0: cannot use SKEY URL in ID file\n" if $fexcgi =~ /skey=/i;
 
 639   unless ($fexcgi =~ /^[_:=\w\-\.\/\@\%]+$/) {
 
 640     die "\"$fexcgi\" is not a legal FEX-URL!\n";
 
 642   $fexcgi =~ s:/fup/*$::;
 
 643   print "proxy address (hostname:port or empty if none): ";
 
 645   $proxy =~ s/[\s\n]//g;
 
 646   if ($proxy =~ /^[\w.-]+:\d+$/) { 
 
 648   } elsif ($proxy =~ /\S/) { 
 
 649     die "wrong proxy address format\n";
 
 654     print "proxy POST limit in MB (use 2048 if unknown): ";
 
 664     $from = 'GROUPMEMBER';
 
 668       print "Your e-mail address as registered at $fexcgi: ";
 
 670       $from =~ s/[\s\n]//g;
 
 671       die "you MUST provide your e-mail address!\n" unless $from;
 
 673     unless ($from =~ /^[_:=\w\-\.\/\@\%\+]+$/) {
 
 674       die "\"$from\" is not a legal e-mail address!\n";
 
 677       print "Your auth-ID for $from at $fexcgi: ";
 
 680       die "you MUST provide your ID!\n" unless $id;
 
 683   if (open $idf,'>>',$idf) {
 
 684     print {$idf} "\n[$tag]\n" if $tag and -s $idf;
 
 685     print {$idf} "$fexcgi$proxy\n",
 
 689     print "data written to $idf\n";
 
 691     die "$0: cannot write to $idf - $!\n";
 
 697   my ($fexcgi,$from,$id);
 
 698   if (open $idf,$idf) {
 
 710     die "$0: too few data in $idf" unless defined $id;
 
 714     $FEXXX = encode_b64("$fexcgi $from $id");
 
 716       print "export FEXXX=$FEXXX\n";
 
 717       print "history -d \$((HISTCMD-1));history -d \$((HISTCMD-1))\n";
 
 719       print "FEXXX=$FEXXX\n";
 
 722     die "$0: cannot read $idf - $!\n";
 
 728   my $fs = shift @ARGV or die $usage;
 
 729   my $mail = shift @ARGV or die $usage;
 
 731   my ($server,$user,$id);
 
 733   die "$0: $idf does already exist\n" if -e $idf;
 
 735   if ($fs =~ /^https/) {
 
 736     die "$0: cannot handle https at this time\n";
 
 739   $fs =~ s{^http://}{};
 
 741   if ($fs =~ s/:(\d+)//) { $port = $1 }
 
 744   tcpconnect($fs,$port);
 
 745   sendheader("$fs:$port","GET $proxy_prefix/fur?user=$mail&verify=no HTTP/1.1");
 
 750     printf "<-- $_"if $opt_v;
 
 756     printf "<-- $_"if $opt_v;
 
 757     if (m{http://(.*)/fup\?from=(.+)&ID=(.+)}) {
 
 762       if (open F,">$idf") {
 
 768         print "user data written to $idf\n";
 
 769         print "you can now fex!\n";
 
 772         die "$0: cannot write to $idf - $!\n";
 
 777   die "$0: no account data received from F*EX server\n";
 
 783   my $transferfile = shift;
 
 787   $SIG{PIPE} = $SIG{INT} = sub {
 
 788     unlink $transferfile;
 
 792   if ($0 eq 'xxx') { @tar = qw'tar -cv' }
 
 793   else             { @tar = qw'tar -cvz' }
 
 796     if ("@ARGV" eq '-') {
 
 797       # store STDIN to transfer file
 
 798       shelldo("cat >> $transferfile");
 
 800       print "making tar transfer file $transferfile :\n";
 
 801       # single file? then add this directly 
 
 802       if (scalar @ARGV == 1) {
 
 804         # strip path if not ending with /
 
 805         if ($ARGV[0] =~ m:(.+)/(.+): and $2 !~ m:/$:) {
 
 806           ($dir,$file) = ($1,$2);
 
 807           chdir $dir or die "$0: $dir - $!\n";
 
 812           shelldo(@tar,qw'--dereference -f',$transferfile,$file);
 
 814           shelldo(@tar,'-f',$transferfile,$file);
 
 817         shelldo(@tar,'-f',$transferfile,@ARGV);
 
 820         unlink $transferfile;
 
 822           die "$0: interrupted making tar transfer file\n";
 
 824           die "$0: error while making tar transfer file\n";
 
 829     # write input from pipe to transfer file
 
 830     shelldo("cat >> $transferfile");
 
 833   die "$0: no transfer file\n" unless -s $transferfile;
 
 835   serverconnect($server,$port);
 
 836   query_sid($server,$port);
 
 842     file        => $transferfile,
 
 844     autodelete  => $transferfile =~ /STDFEX/ ? 'NO' : 'DELAY',
 
 847   # open P,'|w3m -T text/html -dump' or die "$0: w3m - $!\n";
 
 850   if ($transferfile =~ /:/ and $0 ne 'xxx') {
 
 851     if ("@r" =~ /\s(X-)?Location: (http.*)\s/) {
 
 852       print "wget -O- $2 | tar xvzf -\n";
 
 856   unlink $transferfile;
 
 864   female_mode("query quotas?") if $opt_F;
 
 872   die "$0: no response from fex server $server\n" unless @r;
 
 874   unless (/^HTTP.* 2/) {
 
 876     die "$0: server response: $_\n";
 
 878   if (($_) = grep(/^X-Sender-Quota/,@r) and /(\d+)\s+(\d+)/) {
 
 879     print "sender quota (used): $1 ($2) MB\n";
 
 881     print "sender quota: unlimited\n";
 
 883   if (($_) = grep(/^X-Recipient-Quota/,@r) and /(\d+)\s+(\d+)/) {
 
 884     print "recipient quota (used): $1 ($2) MB\n";
 
 886     print "recipient quota: unlimited\n";
 
 895   female_mode("query settings?") if $opt_F;
 
 898     print "ID data from \$FEXID\n";
 
 900     print "ID data from $idf\n";
 
 902     die "$0: found no ID\n";
 
 904   print "server: $fexcgi\n";
 
 905   print "user: $from\n";
 
 906   print "auth-ID: $id\n";
 
 916   die "$0: no response from fex server $server\n" unless @r;
 
 918   unless (/^HTTP.* 2/) {
 
 920     die "$0: server response: $_\n";
 
 922   if (($_) = grep(/^X-Autodelete/,@r) and /:\s+(\w+)/) {
 
 923     print "autodelete: $1\n";
 
 925   if (($_) = grep(/^X-Default-Keep/,@r) and /(\d+)/) {
 
 926     print "default keep: $1 days\n";
 
 928   if (($_) = grep(/^X-Default-Locale/,@r) and /:\s+(\w+)/) {
 
 929     print "default locale: $1\n";
 
 931   if (($_) = grep(/^X-MIME/,@r) and /:\s+(\w+)/) {
 
 932     print "display file with browser: $1\n";
 
 934   if (($_) = grep(/^X-Sender-Quota/,@r) and /(\d+)\s+(\d+)/) {
 
 935     print "sender quota (used): $1 ($2) MB\n";
 
 937     print "sender quota: unlimited\n";
 
 939   if (($_) = grep(/^X-Recipient-Quota/,@r) and /(\d+)\s+(\d+)/) {
 
 940     print "recipient quota (used): $1 ($2) MB\n";
 
 942     print "recipient quota: unlimited\n";
 
 953   female_mode("list spooled files?") if $opt_F;
 
 955   if ($opt_l and $n = shift @ARGV and $n =~ /^\d+$/) {
 
 956     open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
 
 958       if (/^\s*(\d+)\) (\w+) (.+)/ and $1 eq $n) {
 
 959         serverconnect($server,$port) unless $SH;
 
 962           "GET $proxy_prefix/fop/$2/$2?LIST HTTP/1.1",
 
 963           "User-Agent: $useragent",
 
 967         print "<-- $_" if $opt_v;
 
 969           print "<-- $_" if $opt_v;
 
 977         } elsif (s:HTTP/[\d\. ]+::) {
 
 978           die "$0: server response: $_";
 
 980           die "$0: no response from fex server $server\n";
 
 985     die "$0: file \#$n not found in fexlist\n";
 
 989       to        => $opt_l ? '*' : $from,
 
 993   die "$0: no response from fex server $server\n" unless @r;
 
 995   unless (/^HTTP.* 200/) {
 
 997     die "$0: server response: $_\n";
 
1002     open $fexlist,">$fexlist" or die "$0: cannot write $fexlist - $!\n";
 
1004       next unless /<pre>/ or $data;
 
1007       if (/<a href=".*dkey=(\w+).*?">/) { $dkey = $1 }
 
1009 #      $_ = encode_utf8($_);
 
1016         print {$fexlist} "\n$1\n";
 
1017       } elsif (m/(\d+) MB (.+)/) {
 
1019         printf "%4s) %8d MB %s\n","#$n",$1,$2;
 
1020         printf {$fexlist} "%3d) %s %s\n",$n,$dkey,$2;
 
1026   # list received files
 
1029       next unless /<pre>/ or $data;
 
1033       if (/(from .* :)/) {
 
1036       if (m{(\d+) (MB.*)<a href="(https?://.*/fop/\w+/.+)">(.+)</a>( ".*")?}) {
 
1037         printf "%8d %s%s%s\n",$1,$2,$3,($5||'');
 
1045   printf "%s/fup/%s\n",$fexcgi,encode_b64("from=$from&id=$id");
 
1059   die "$0: no response from fex server $server\n" unless @r;
 
1061   unless (/^HTTP.* 200/) {
 
1063     die "$0: server response: $_\n";
 
1066   foreach (@r) { print "$_\n" }
 
1070 sub show_address_book {
 
1075   %AB = query_address_book($server,$port,$from);
 
1076   foreach $alias (sort keys %AB) {
 
1077     next if $alias eq 'ADDRESS_BOOK';
 
1078     $_ = sprintf "%s = %s (%s) # %s\n",
 
1081                  $AB{$alias}->{options},
 
1082                  $AB{$alias}->{comment};
 
1091   die "$0: not yet implemented\n";
 
1099     $opt_d = shift @ARGV;
 
1100     die "$usage: $0 -d #\n" if $opt_d !~ /^\d+$/;
 
1102     open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
 
1103     while (<$fexlist>) {
 
1104       if (/^to (.+\@.+) :/) {
 
1106       } elsif (/^\s*(\d+)\) (\w+) (.+)/ and $1 eq $opt_d) {
 
1107         serverconnect($server,$port) unless $SH;
 
1110           "GET $proxy_prefix/fop/$2/$2?DELETE HTTP/1.1",
 
1111           "User-Agent: $useragent",
 
1115         print "<-- $_" if $opt_v;
 
1116         if (/^HTTP.* 200/) {
 
1119             last if /^\n/; # ignore HTML output
 
1120             print "<-- $_" if $opt_v;
 
1121             if (/^X-File:.*\/(.+)/) {
 
1122               printf "%s deleted\n",decode_utf8(urldecode($1));
 
1126         } elsif (s:HTTP/[\d\. ]+::) {
 
1127           die "$0: server response: $_";
 
1129           die "$0: no response from fex server $server\n";
 
1135     sleep 1; # do not overrun server
 
1146   my ($data,$aname,$alias);
 
1153   if ($from =~ /^SUBUSER|GROUPMEMBER$/) {
 
1156     # look for single @ in arguments
 
1157     for (my $i=1; $i<$#ARGV; $i++) {
 
1158       if ($ARGV[$i] eq '@') {
 
1159         $ARGV[$i] = join(',',@ARGV[$i+1 .. $#ARGV]);
 
1164     $to = pop @ARGV or die $usage;
 
1167       $nomail = $opt_C ||= 'NOMAIL';
 
1171       $nomail = $opt_C ||= 'NOMAIL';
 
1173     if ($opt_g and $to =~ /,/) {
 
1174       die "$0: encryption is supported to only one recipient\n";
 
1176     if ($to =~ m{^https?://.*/fup\?skey=(\w+)}) {
 
1181     if ($to =~ m{^https?://.*/fup\?gkey=(\w+)}) {
 
1182       $from = 'GROUPMEMBER';
 
1187   @to = split(',',lc($to));
 
1189   die $usage unless @ARGV or $opt_a or $opt_s;
 
1190   die $usage if $opt_s and @ARGV;
 
1192   # early serverconnect necessary for X-Features info
 
1193   serverconnect($server,$port);
 
1197     sendheader("$server:$port","OPTIONS FEX HTTP/1.1");
 
1200     die "$0: no response from fex server $server\n" unless $_;
 
1201     print "<-- $_" if $opt_v;
 
1202     if (/^HTTP.* 201/) {
 
1205         print "<-- $_" if $opt_v;
 
1207         $aok = $_ if /X-Features:.*ANONYMOUS/;
 
1209       die "$0: no anonymous support on server $server\n" unless $aok;
 
1211       die "$0: bad response from server $server : $_\n";
 
1216     query_sid($server,$port);
 
1218     if ($from eq 'SUBUSER') {
 
1220       # die "skey=$skey\nid=$id\nsid=$sid\n";
 
1223     if ($from eq 'GROUPMEMBER') {
 
1229       $opt_C ||= 'NOMAIL';
 
1230     } elsif ($to =~ m:^(//.*):) {
 
1232       if ($features =~ /XKEY/) {
 
1236         die "$0: server does not support XKEY\n";
 
1238     } elsif (grep /^[^@]*$/,@to and not $skey and not $gkey) {
 
1239       %AB = query_address_book($server,$port,$from);
 
1241         serverconnect($server,$port);
 
1242         query_sid($server,$port);
 
1245         # alias in local config?
 
1247           if ($alias{$to} =~ /(.+?):(.+)/) {
 
1252             # special extra upload
 
1253             system $0,split(/\s/,$opt),@argv,$ato;
 
1259         # alias in server address book?
 
1261           # do not substitute alias with expanded addresses because then 
 
1262           # keep and autodelete options from address book will get lost
 
1265         # look for mutt aliases
 
1266         elsif ($to !~ /@/ and $to ne $from) {
 
1267           $to = get_mutt_alias($to);
 
1272     $to = join(',',grep /./,@to) or exit;
 
1273     # warn "Server/User: $fexcgi/$from\n" unless $opt_q;
 
1276       not $skey and not $gkey
 
1278       and $features =~ /CHECKRECIPIENT/ 
 
1279       and $opt_C !~ /^(DELETE|LIST|RECEIVEDLOG|SENDLOG|FOPLOG)$/
 
1281       checkrecipient($from,$to);
 
1283         serverconnect($server,$port);
 
1284         query_sid($server,$port);
 
1289   if (@ARGV > 1 and not ($opt_a or $opt_s or $opt_d)) {
 
1290     print "Archive name (name.tar, name.tgz or name.zip) or [ENTER] to send file for file:\n";
 
1299     $opt_s =~ s/[^\w_.+-]/_/g;
 
1304     $opt_a =~ s/[^\w_.+-]/_/g;
 
1305     if ($opt_a =~ /(.+)\.(zip|tar|tgz|7z)$/) {
 
1309       die "$0: archive name must be one of ".
 
1310           "$opt_a.tar $opt_a.tgz $opt_a.zip\n";
 
1312     # no file argument left?
 
1314       # use file name as archive name
 
1319     foreach my $file (@ARGV) {
 
1320       die "$0: cannot read $file\n" unless -l $file or -r $file;
 
1322     $opt_a .= ".$atype" if $opt_a !~ /\.$atype$/;
 
1323     $transferfile = "$tmpdir/$opt_a";
 
1324     unlink $transferfile;
 
1325     print "Making fex archive ($opt_a):\n";
 
1326     if ($atype eq 'zip') {
 
1328         # if ($opt_c) { system(qw'7z a -tzip',$transferfile,@ARGV) }
 
1329         # else        { system(qw'7z a -tzip -mm=copy',$transferfile,@ARGV) }
 
1330         system(qw'7z a -tzip',$transferfile,@ARGV);
 
1331         @files = ($transferfile);
 
1333         # zip archives must be < 2 GB, so split as necessary
 
1334         @files = zipsplit($transferfile,@ARGV);
 
1335         if (scalar(@files) == 1) {
 
1336           $transferfile = $files[0];
 
1337           $transferfile =~ s/_1.zip$/.zip/;
 
1338           rename $files[0],$transferfile;
 
1339           @files = ($transferfile);
 
1342       @transferfiles =  @files;
 
1343     } elsif ($atype eq '7z') {
 
1344       # http://www.7-zip.org/
 
1345       my @X = (); # exclude list
 
1347         foreach my $x (split('#',${'opt_#'})) {
 
1351       if ($opt_c) { system(qw'7z a',@X,$transferfile,@ARGV) }
 
1352       else        { system(qw'7z a -t7z -mx0',@X,$transferfile,@ARGV) }
 
1353       @transferfiles = @files = ($transferfile);
 
1354     } elsif ($atype eq 'tar') {
 
1356         system(qw'7z a -ttar',$transferfile,@ARGV);
 
1357         @transferfiles = @files = ($transferfile);
 
1359         ## tar is now handled by formdatapost()
 
1360         # system(qw'tar cvf',$transferfile,@ARGV);
 
1363     } elsif ($atype eq 'tgz') {
 
1365         die "$0: archive type tgz not available, use tar, zip or 7z\n";
 
1367         ## tgz is now handled by formdatapost()
 
1368         # system(qw'tar cvzf',$transferfile,@ARGV);
 
1372       die "$0: unknown archive format \"$atype\"\n";
 
1375     if (@transferfiles) {
 
1377       # error in making transfer archive?
 
1379         unlink @transferfiles;
 
1380         die "$0: $! - aborting upload\n";
 
1383       # maybe timeout, so make new connect
 
1384       if (time-$t0 >= $timeout) {
 
1385         serverconnect($server,$port);
 
1386         query_sid($server,$port) unless $anonymous;
 
1406             die "$0: $file is not a regular file, try option -a\n"
 
1408             die "$0: $file does not exist\n";
 
1411         die "$0: cannot read $file\n" unless -r $file;
 
1418     foreach my $file (@files) {
 
1419       my @s = stat($file);
 
1420       unless (@s and ($s[2] & S_IROTH) and -r $file) {
 
1421         die "$0: $file is not world readable\n";
 
1426   foreach my $file (@files) {
 
1427     sleep 1;    # do not overrun server!
 
1428     unless (-s $file or $opt_d or $opt_a or $opt_s) {
 
1429       die "$0: cannot send empty file $file\n";
 
1431     female_mode("send file $file?") if $opt_F;
 
1440       autodelete        => $opt_D, 
 
1443     if (not @r or not grep /\w/,@r) {
 
1444       die "$0: no response from server\n";
 
1446     if (($r) = grep /^ERROR:/,@r) {
 
1447       if ($anonymous and $r =~ /purge it/) {
 
1448         die "$0: file is already on server for $to - use another anonymous recipent\n";
 
1452         die "$0: server error: $r\n";
 
1455     if (($r) = grep /<h3>\Q$file/,@r) {
 
1459     if ($opt_a !~ /^afex_\d+\.tar$/ and $file !~ /afex_\d+\.tar$/) {
 
1460       # print grep({s/^(X-Recipient:.*\((.+)\))/Parameters: $2\n/i} @r);
 
1462       my ($recipient,$location);
 
1464         if (/^(X-)?(Recipient.*)/i) {
 
1466           if (/notification=no/i) { $nonot = 1 }
 
1469         if (/^(X-)?(Location.*)/i) {
 
1471           if ($from eq $to or $from =~ /^\Q$to\E@/i 
 
1472               or $nomail or $anonymous or $nonot) {
 
1473             print "$recipient\n";
 
1474             print "$location\n";
 
1478       unless ($opt_d or $location) {
 
1479         if (scalar(@r) == 1) {
 
1480           die "$0: server error: @r\n";
 
1482           if ($r[0] !~ /HTTP.1.. 2/ and $r[0] =~ /HTTP.[\s\d.]+(.+)/) {
 
1483             die "$0: server error: $1\n";
 
1485             die "$0: server error:\n".join("\n",@r)."\n";
 
1492   # delete transfer tmp file
 
1493   unlink $transferfile if $transferfile;
 
1499   my ($to,$n,$dkey,$file,$req);
 
1503   # look for single @ in arguments
 
1504   for (my $i=1; $i<$#ARGV; $i++) {
 
1505     if ($ARGV[$i] eq '@') {
 
1506       $ARGV[$i] = join(',',@ARGV[$i+1 .. $#ARGV]);
 
1512   # if ($windoof and not @ARGV) { &inquire }
 
1513   $to = pop @ARGV or die $usage;
 
1514   $to = $from if $to eq '.';
 
1515   if ($to !~ /@/ and $to ne $from) {
 
1516     $to = get_mutt_alias($to);
 
1519   open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
 
1520   while (<$fexlist>) {
 
1521     if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_f) {
 
1525       if ($file =~ s/ "(.*)"$//) {
 
1526         $opt_C ||= $1 if $1 ne 'NOMAIL';
 
1534     die "$0: file #$opt_f not found in fexlist\n";
 
1537   female_mode("forward file #$opt_f?") if $opt_F;
 
1539   serverconnect($server,$port);
 
1540   query_sid($server,$port);
 
1542   $req = "GET $proxy_prefix/fup?"
 
1543         ."from=$from&ID=$sid&to=$to&dkey=$dkey&command=FORWARD";
 
1544   $req .= "&comment=$opt_C"     if $opt_C;
 
1545   $req .= "&keep=$opt_k"        if $opt_k;
 
1546   $req .= "&autodelete=$opt_D"  if $opt_D;
 
1547   $req .= "&$opt_X"             if $opt_X;
 
1548   $req .= " HTTP/1.1";
 
1549   sendheader("$server:$port",$req);
 
1552   $fp =~ s/[^\w_.-]/.+/g; # because of UTF8 filename
 
1555     $status = 0 if /"$fp"/;
 
1556     print if $opt_v or /"$fp"/;
 
1560     die "$0: server failed, rerun command with option -v\n";
 
1568   my ($to,$n,$dkey,$file,$req,$recipient);
 
1571   die $usage if @ARGV;
 
1573   open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
 
1574   while (<$fexlist>) {
 
1575     if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_N) {
 
1584     die "$0: file #$opt_N not found in fexlist\n";
 
1587   female_mode("resend notification for file #$opt_N?") if $opt_F;
 
1589   serverconnect($server,$port);
 
1590   query_sid($server,$port);
 
1592   $req = "GET $proxy_prefix/fup?"
 
1593         ."from=$from&ID=$sid&dkey=$dkey&command=RENOTIFY"
 
1595   sendheader("$server:$port",$req);
 
1599     print "<-- $_" if $opt_v;
 
1601     if (/^X-Notify: (.+)\/(.+)\/(.+)/) {
 
1608     print "notification e-mail for $file has been resent to $recipient\n";
 
1611       die "$0: server failed\n";
 
1613       die "$0: server failed, rerun command with option -v\n";
 
1623   my ($n,$dkey,$file,$req);
 
1626   die $usage if @ARGV;
 
1627   die $usage unless $opt_C or $opt_k or $opt_D;
 
1629   open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
 
1630   while (<$fexlist>) {
 
1631     if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_x) {
 
1635       $file =~ s/ "(.*)"$//;
 
1642     die "$0: file #$opt_x not found in fexlist\n";
 
1645   female_mode("modify file #$opt_x?") if $opt_F;
 
1647   serverconnect($server,$port);
 
1648   query_sid($server,$port);
 
1650   $req = "GET $proxy_prefix/fup?"
 
1651         ."from=$from&ID=$sid&dkey=$dkey&command=MODIFY";
 
1652   $req .= "&comment=$opt_C"     if $opt_C;
 
1653   $req .= "&keep=$opt_k"        if $opt_k;
 
1654   $req .= "&autodelete=$opt_D"  if $opt_D;
 
1655   $req .= " HTTP/1.1";
 
1656   sendheader("$server:$port",$req);
 
1671   my $transferfile = shift;
 
1675   # get transfer file from FEX server
 
1677     serverconnect($server,$port);
 
1678     query_sid($server,$port);
 
1681   xxget($from,$sid,$transferfile);
 
1684   unless (-s $transferfile) {
 
1685     unlink $transferfile;
 
1689   # no further processing if delivering to pipe
 
1690   exec 'cat',$transferfile unless -t STDOUT;
 
1692   if ($ft = `file $transferfile 2>/dev/null`) {
 
1693     if ($ft =~ /compressed/) {
 
1694       rename $transferfile,"$transferfile.gz";
 
1695       shelldo(ws("gunzip $transferfile.gz"));
 
1697     $ft = `file $transferfile`;
 
1699   # file command failed, so we look ourself into the file...
 
1700   elsif (open $transferfile,$transferfile) {
 
1701     read $transferfile,$_,4;
 
1702     close $transferfile;
 
1704     if (/\x1F\x8B\x08\x00/) {
 
1705       rename $transferfile,"$transferfile.gz";
 
1706       shelldo(ws("gunzip $transferfile.gz"));
 
1708       $ft = 'tar archive';
 
1711   if ($ft =~ /tar archive/) {
 
1712     rename $transferfile,"$transferfile.tar";
 
1713     $transferfile .= '.tar';
 
1717       print "Files in transfer-container:\n\n";
 
1718       shelldo(ws("tar tvf $transferfile"));
 
1719       print "\nExtract these files? [Yn] ";
 
1723       print "keeping $transferfile\n";
 
1725       my $untar = "tar xvf";
 
1726       # if ($> == 0 and `tar --help 2>&1` =~ /gnu/) {
 
1727       #  $untar = "tar --no-same-owner -xvf";
 
1729       system("$untar $transferfile && rm $transferfile");
 
1730       die "$0: error while untaring, see $transferfile\n" if -f $transferfile;
 
1733     exec 'cat',$transferfile;
 
1741   my ($boundary,$filename,$filesize,$length,$buf,$file,$fpsize,$resume,$seek);
 
1743   my (@hh,@hb,@r,@pv,$to);
 
1745   my ($t0,$t1,$t2,$tt,$tc);
 
1746   my $bs = 2**16;        # blocksize for reading and sending file
 
1747   my $fileid = int(time);
 
1749   my $connection = '';
 
1751   my ($tar,$aname,$atype,$tarlist,$tarerror,$location,$transferfile);
 
1754   if (defined($file = $P{file})) {
 
1756     $to = $AB{$P{to}} || $P{to}; # for gpg
 
1758     # special file: stream from STDIN
 
1760       $filename = encode_utf8($file);
 
1768       $if =~ s/([^_\w\.\-])/\\$1/g;
 
1769       $transferfile = $tmpdir . '/' . basename($file) . '.gz';
 
1770       $of = $transferfile;
 
1771       $of =~ s/([^_\w\.\-])/\\$1/g;
 
1772       shelldo("gzip <$if>$of");
 
1773       $filesize = -s $transferfile;
 
1774       die "$0: cannot gzip $file\n" unless $filesize;
 
1775       $file = $transferfile;
 
1778     # special file: tar-on-the-fly
 
1779     if (not $windoof and $opt_a and $file =~ /(.+)\.(tar|tgz)$/) {
 
1782       $tarlist  = "$tmpdir/$aname.list";
 
1783       $tarerror = "$tmpdir/$aname.error";
 
1785       $tar .= 'z' if $atype eq 'tgz';
 
1786       if (`tar --help 2>/dev/null` =~ /--index-file/) {
 
1787         $tar .= " --index-file=$tarlist -f-";
 
1792         foreach my $x (split('#',${'opt_#'})) {
 
1793           $tar .= " --exclude=$x";
 
1798         $file =~ s/([^\w\-\@\#%,.=+~_:])/\\$1/g;
 
1801       # print "calculating archive size... ";
 
1802       open $tar,"$tar 2>$tarerror|" or die "$0: cannot run tar - $!\n";
 
1803       $t0 = int(time) if -t STDOUT;
 
1804       while ($b = read $tar,$_,$bs) {
 
1809             printf "Archive size: %d MB\r",int($filesize/M);
 
1814       printf "Archive size: %d MB\n",int($filesize/M) if -t STDOUT;
 
1815       unless (close $tar) {
 
1817         if (open $tarerror,$tarerror) {
 
1822         unlink $tarlist,$tarerror;
 
1823         die "$0: tar error:\n$_";
 
1825       $file = "$aname.$atype";
 
1826       $filename = encode_utf8($file);
 
1827       undef $SH; # force reconnect (timeout!)
 
1832       $filename = encode_utf8(${'opt_='} || $file);
 
1835         $filename =~ s/^[a-z]://;
 
1836         $filename =~ s/.*\\//;
 
1838       $filename =~ s:.*/::;
 
1839       $filename =~ s:[\r\n]+: :g;
 
1842       } elsif (not $opt_g and not $opt_s) {
 
1843         $filesize = -s $file or die "$0: $file is empty or not readable\n";
 
1847     $filename .= '.gpg' if $opt_g;
 
1852         $fileid = int(time);
 
1855           $fileid = md5_hex(fmd(@ARGV));
 
1857           $fileid = fileid($file);
 
1863     $file = $filename = '';
 
1869   @hh = (); # HTTP header
 
1870   @hb = (); # HTTP body
 
1877     serverconnect($server,$port);
 
1878     query_sid($server,$port) unless $anonymous;
 
1881   $P{id} = $sid; # ugly hack!
 
1883   # ask server if this file has been already sent
 
1884   if ($file and not $xx and not 
 
1885       ($opt_s or $opt_g or $opt_o or $opt_d or $opt_l or $opt_L or ${'opt_/'}))
 
1887     ($seek,$location) = query_file($server,$port,$frecipient||$P{to},$P{from},
 
1888                                    $P{id},$filename,$fileid);
 
1889     if ($filesize == $seek) {
 
1890       print "Location: $location\n" if $location and $nomail;
 
1891       warn "$0: $file has been already transferred\n";
 
1893     } elsif ($seek and $seek < $filesize) {
 
1894       $resume = " (resuming at byte $seek)";
 
1895     } elsif ($filesize <= $seek) {
 
1899       sleep 1;    # do not overrun proxy
 
1900       serverconnect($server,$port);
 
1905   if ($chunksize and $proxy and $port != 443 
 
1906       and $filesize - $seek > $chunksize - $bs) {
 
1907     if ($features !~ /MULTIPOST/) {
 
1908       die sprintf("$0: server does not support chunked multi-POST needed for"
 
1909                   ." files > %d MB via proxy\n",$chunksize/M);
 
1911     $opt_o = 0; # no overwriting mode for next chunks
 
1912     $fpsize = $chunksize - $bs;
 
1914     $fpsize = $filesize - $seek;
 
1917   $boundary = randstring(48);
 
1920   $P{filesize} = $filesize;
 
1922   # send HTTP POST variables
 
1925     @pv = qw'from to skey keep autodelete comment seek filesize';
 
1928     @pv = qw'from to gkey keep autodelete comment seek filesize';
 
1930     @pv = qw'from to id replyto keep autodelete comment command seek filesize';
 
1932   foreach my $v (@pv) {
 
1935       push @hb,"--$boundary";
 
1936       push @hb,"Content-Disposition: form-data; name=\"$name\"";
 
1938       push @hb,encode_utf8($P{$v});
 
1942   # at last, POST the file
 
1944     push @hb,"--$boundary";
 
1945     push @hb,"Content-Disposition: form-data; name=\"FILE\"; filename=\"$filename\"";
 
1947       if ($opt_M) { push @hb,"Content-Type: application/x-mime" }
 
1948       else        { push @hb,"Content-Type: application/octet-stream" }
 
1950         $flink = abs_path($file);
 
1951         push @hb,"Content-Location: $flink";
 
1953         # push @hb,"Content-Length: " . ((-s $file||0) - $seek); # optional header!
 
1954         push @hb,"Content-Length: $fpsize"; # optional header! NOT filesize!
 
1955         push @hb,"X-File-ID: $fileid";
 
1960     # prevent proxy chunked mode reply
 
1961     $connection = "close";
 
1964   push @hb,"--$boundary--";
 
1969     $length = length(join('',@hb)) + scalar(@hb)*2 + $fpsize;
 
1972   if ($file and not $opt_d) {
 
1973     if ($flink) { $hb[-2] = $flink }
 
1974     else        { $hb[-2] = '(file content)' }
 
1976   # any other extra URL arguments
 
1978   $opt_X = "?$::opt_X" if $::opt_X and $file;
 
1981   push @hh,"POST $proxy_prefix/fup$opt_X HTTP/1.1";
 
1982   push @hh,"Host: $server:$port";
 
1983   push @hh,"User-Agent: $useragent";
 
1984   push @hh,"Content-Length: $length";
 
1985   push @hh,"Content-Type: multipart/form-data; boundary=$boundary";
 
1986   push @hh,"Connection: $connection" if $connection;
 
1990     print "--> $_\n" foreach (@hh,@hb);
 
1993   $SIG{PIPE} = \&sigpipehandler;
 
1994 #    foreach $sig (keys %SIG) {
 
1995 #      eval '$SIG{$sig} = sub { print "\n!!! SIGNAL '.$sig.' !!!\n"; exit; }';
 
2000     pop @hb unless $flink;
 
2001     nvtsend(@hh,@hb) or do {
 
2002       warn "$0: server has closed the connection, reconnecting...\n";
 
2004       goto FORMDATAPOST; # necessary: new $sid ==> new @hh
 
2007     unless ($opt_d or $flink) {
 
2009       $t0 = $t2 = int(time);
 
2016           open $file,"gpg -e -r $to|" or die "$0: cannot run gpg - $!\n";
 
2018           open $file,'>&=STDIN' or die "$0: cannot open STDIN - $!\n";
 
2022           open $file,"$tar|gpg -e -r $to|" or die "$0: cannot run tar&gpg - $!\n";
 
2024           open $file,"$tar|" or die "$0: cannot run tar - $!\n";
 
2028           if (defined $tpid and $tpid == 0) {
 
2030             if (open $tarlist,$tarlist) {
 
2031               # print "\n$tar|\n"; system "ls -l $tarlist";
 
2033                 while (<$tarlist>) {
 
2034                   print ' 'x(length($file)+40),"\r",$_;
 
2041           $SIG{CHLD} = 'IGNORE';
 
2044           print "Fast forward to byte $seek (resuming)\n";
 
2045           readahead($file,$seek);
 
2050           $fileq =~ s/([^\w\-\@\#%,.=+~_:])/\\$1/g;
 
2051           open $file,"gpg -e -r $to <$fileq|" or die "$0: cannot run gpg - $!\n";
 
2053           open $file,$file or die "$0: cannot read $file - $!\n";
 
2062       print $rcamel[0] if ${'opt_+'};
 
2064       $SIG{ALRM} = sub { retry("timed out") };
 
2065       while (my $b = read $file,$buf,$bs) {
 
2068           print {$SH} $buf or &sigpipehandler;
 
2070           syswrite $SH,$buf or &sigpipehandler;
 
2074         if ($filesize > 0 and $bytes+$seek > $filesize) {
 
2075           die "$0: $file filesize has grown while uploading\n";
 
2079         if (${'opt_+'} and int($t2*10)>$tc) {
 
2080           print $rcamel[$tc%2+1];
 
2083         if (not $opt_q and -t STDOUT and int($t2)>$t1) {
 
2084           &sigpipehandler unless $SH->connected;
 
2085           # smaller block size is better on slow links
 
2086           $bs = 4096 if $t1 and $bs>4096 and $bytes/($t2-$t0)<65536;
 
2087           if ($filesize > 0) {
 
2088             $pct = sprintf "(%d%%)",int(($bytes+$seek)/$filesize*100);
 
2090           if ($bytes>2*M and $bs>4096) {
 
2091             printf STDERR "%s: %d MB of %d MB %s %d kB/s        \r",
 
2092                    $opt_s||$opt_a||$file,
 
2093                    int(($bytes+$seek)/M),
 
2096                    int($bt/k/($t2-$tt));
 
2098             printf STDERR "%s: %d kB of %d MB %s %d kB/s        \r",
 
2099                    $opt_s||$opt_a||$file,
 
2100                    int(($bytes+$seek)/k),
 
2103                    int($bt/k/($t2-$tt));
 
2106           # time window for transfer rate calculation
 
2112         last if $filesize > 0 and $bytes >= $fpsize;
 
2113         sleep 1 while ($opt_m and $bytes/k/(time-$t0||1) > $opt_m);
 
2115       close $file; # or die "$0: error while reading $file - $!\n";
 
2118       print $rcamel[2] if ${'opt_+'};
 
2120       # terminate tar verbose output job
 
2128         if (not $chunksize and $bytes+$seek < $filesize) {
 
2129           die "$0: $file filesize has shrunk while uploading\n";
 
2132         if ($seek or $chunksize and $chunksize < $filesize) {
 
2134             printf STDERR "%s: %d MB in %d s (%d kB/s)",
 
2135                            $opt_s||$opt_a||$file,
 
2139             if ($bytes+$seek == $filesize) {
 
2140               printf STDERR ", total %d MB\n",int($filesize/M);
 
2142               printf STDERR ", chunk #%d : %d MB\n",
 
2143                             $chunk,int(($bytes+$seek)/M);
 
2146             printf STDERR "%s: %d kB in %d s (%d kB/s)",
 
2147                           $opt_s||$opt_a||$file,
 
2151             if ($bytes+$seek == $filesize) {
 
2152               printf STDERR ", total %d kB\n",int($filesize/k);
 
2154               printf STDERR ", chunk #%d : %d kB\n",
 
2155                             $chunk,int(($bytes+$seek)/k);
 
2160             printf STDERR "%s: %d MB in %d s (%d kB/s)        \n",
 
2161                           $opt_s||$opt_a||$file,
 
2166             printf STDERR "%s: %d kB in %d s (%d kB/s)        \n",
 
2167                           $opt_s||$opt_a||$file,
 
2174         if (-t STDOUT and not ($opt_s or $opt_g)) {
 
2175           print STDERR "waiting for server ok..."
 
2181     print {$SH} "\r\n--$boundary--\r\n";
 
2183     # special handling of streaming file because of stunnel tcp shutdown bug
 
2184     if ($opt_s or $opt_g) {
 
2187       serverconnect($server,$port);
 
2188       query_sid($server,$port) unless $anonymous;
 
2189       ($seek,$location) = query_file($server,$port,$P{to},$P{from},$sid,
 
2191       if ($seek != $bytes) {
 
2192         die "$0: streamed $bytes bytes but server received $seek bytes\n";
 
2194       return "X-Location: $location\n";
 
2200         printf STDERR "%s: %d MB\n",$flink,int($bytes/M);
 
2202         printf STDERR "%s: %d kB\n",$flink,int($bytes/k);
 
2210   # SuSe: Can't locate object method "BINMODE" via package "IO::Socket::SSL::SSL_HANDLE"
 
2211   # binmode $SH,':utf8'; 
 
2213   if (not $opt_q and $file and -t STDOUT) {
 
2214     print STDERR "\r                         \r";
 
2218     print "<-- $_\n" if $opt_v;
 
2219     last if @r and $r[0] =~ / 204 / and /^$/ or /<\/html>/i;
 
2220     push @r,decode_utf8($_);
 
2226     if ($proxy and $fpsize+$seek < $filesize) {
 
2237     my @rc = ('A'..'Z','a'..'z',0..9 );
 
2241     for (1..$n) { $rs .= $rc[int(rand($rn))] };
 
2247   my $zipbase = shift;
 
2251   my ($zsize,$size,$n);
 
2253   $zipbase =~ s/\.zip$//;
 
2254   map { s/([^_\w\+\-\.])/\\$1/g } @files;
 
2256   open my $ff,"find @files|" or die "$0: cannot search for @_ - $!\n";
 
2263       die "$0: too many zip-archives\n";
 
2266     while ($file = <$ff>) {
 
2268       # next if -l $file or not -f $file;
 
2269       next unless -f $file;
 
2271       if ($size > 2147480000) {
 
2273         die "$0: $file too big for zip\n";
 
2275       if ($zsize + $size > 2147000000) {
 
2276         push @zipfiles,zip($zipbase.'_'.$n.'.zip',@files);
 
2287   push @zipfiles,zip($zipbase.'_'.$n.'.zip',@files);
 
2299   # if ($opt_c) { $cmd = "zip -@ $zip" }
 
2300   # else        { $cmd = "zip -0 -@ $zip" }
 
2301   $cmd = "zip -@ $zip";
 
2303     ${'opt_#'} =~ s/#/ /g;
 
2304     $cmd .= " -x ".${'opt_#'};
 
2306   print $cmd,"\n" if $opt_v;
 
2307   open $cmd,"|$cmd" or die "$0: cannot create $zip - $!\n";
 
2309     print {$cmd} $_."\n";
 
2310     print "  $_\n" if $opt_v;
 
2312   close $cmd or die "$0: zip failed - $!\n";
 
2328     return $_ if length($_);
 
2335   my ($server,$port,$to,$from,$id,$filename,$fileid) = @_;
 
2338   my ($head,$location);
 
2339   my ($response,$fexsrv);
 
2344   $to = $AB{$to} if $AB{$to};
 
2345   $filename =~ s/([^_=:,;<>()+.\w\-])/'%'.uc(unpack("H2",$1))/ge; # urlencode
 
2347     $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??SKEY=$id HTTP/1.1";
 
2349     $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??GKEY=$id HTTP/1.1";
 
2351     $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??ID=$id HTTP/1.1";
 
2353   sendheader("$server:$port",$head);
 
2355   unless (defined $_ and /\w/) {
 
2356     die "$0: no response from server\n";
 
2359   print "<-- $_" if $opt_v;
 
2360   unless (/^HTTP.* 200/) {
 
2365       print "<-- $_" if $opt_v;
 
2366       $fexsrv = $_ if /^(Server: fexsrv|X-Features:)/;
 
2369     die "$0: no fexserver at $server:$port\n" unless $fexsrv;
 
2370     die "$0: server response: $response";
 
2374     print "<-- $_" if $opt_v;
 
2376     if (/^Content-Length:\s+(\d+)/)     { $seek = $1 }
 
2377     if (/^X-File-ID:\s+(.+)/)           { $qfileid = $1 }
 
2378     if (/^X-Features:\s+(.+)/)          { $features = $1 }
 
2379     if (/^X-Location:\s+(.+)/)          { $location = $1 }
 
2382   # return true seek only if file is identified
 
2383   $seek = 0 if $qfileid and $qfileid ne $fileid;
 
2385   return ($seek,$location);
 
2389 sub edit_address_book {
 
2392   my $ab = "$fexhome/ADDRESS_BOOK";
 
2396   die "$0: address book not available for subusers\n"      if $skey;
 
2397   die "$0: address book not available for group members\n" if $gkey;
 
2399   female_mode("edit your address book?") if $opt_F;
 
2401   %AB = query_address_book($server,$port,$user);
 
2402   if ($AB{ADDRESS_BOOK} !~ /\w/) {
 
2404       "# Format: alias e-mail-address # Comment\n".
 
2406       "framstag framstag\@rus.uni-stuttgart.de\n";
 
2408   open $ab,">$ab" or die "$0: cannot write to $ab - $!\n";
 
2409   print {$ab} $AB{ADDRESS_BOOK};
 
2417   serverconnect($server,$port);
 
2418   query_sid($server,$port);
 
2431 sub query_address_book {
 
2432   my ($server,$port,$user) = @_;
 
2433   my ($req,$alias,$address,$options,$comment,$cl,$ab,$b);
 
2438     serverconnect($server,$port);
 
2439     query_sid($server,$port);
 
2442   $req = "GET $proxy_prefix/fop/$user/$user/ADDRESS_BOOK?ID=$sid HTTP/1.1";
 
2443   sendheader("$server:$port",$req);
 
2445   unless (defined $_ and /\w/) {
 
2446     die "$0: no response from server\n";
 
2449   print "<-- $_" if $opt_v;
 
2450   unless (/^HTTP.* 200/) {
 
2451     if (/^HTTP.* 404/) {
 
2452       while (<$SH>) { last if /^\r?\n/ }
 
2455       # s:HTTP/[\d\. ]+::;
 
2456       # die "$0: server response: $_";
 
2464     print "<-- $_" if $opt_v;
 
2466     $cl = $1 if /^Content-Length: (\d+)/;
 
2476       print "<-- $_\n" if $opt_v;
 
2480         ($alias,$address,$options) = split;
 
2482           if ($options) { $options =~ s/[()]//g }
 
2483           else          { $options = '' }
 
2484           $AB{$alias} = $address;
 
2485           $AB{$alias}->{options} = $options||'';
 
2486           $AB{$alias}->{comment} = $comment||'';
 
2487           if ($options and $options =~ /keep=(\d+)/i) {
 
2488             $AB{$alias}->{keep} = $1;
 
2490           if ($options and $options =~ /autodelete=(\w+)/i) {
 
2491             $AB{$alias}->{autodelete} = $1;
 
2499   $AB{ADDRESS_BOOK} = $ab;
 
2505 # sets global $sid $features $timeout # ugly hack! :-}
 
2507   my ($server,$port) = @_;
 
2514     return if $features;    # early return if we know enough
 
2515     $req = "OPTIONS FEX HTTP/1.1";
 
2517     return if $features;    # early return if we know enough
 
2518     $req = "GET $proxy_prefix/SID HTTP/1.1";
 
2520     $req = "GET SID HTTP/1.1";
 
2523   sendheader("$server:$port",$req,"User-Agent: $useragent");
 
2525   unless (defined $_ and /\w/) {
 
2526     print "\n" if $opt_v;
 
2527     die "$0: no response from server\n";
 
2530   print "<-- $_" if $opt_v;
 
2532   if (/^HTTP.* [25]0[01] /) {
 
2533     if (not $proxy and $port ne 443 and /^HTTP.* 201 (.+)/) {
 
2534       $sid = 'MD5H:'.md5_hex($id.$1);
 
2538       print "<-- $_" if $opt_v;
 
2539       $features = $1 if /^X-Features: (.+)/;
 
2540       $timeout = $1  if /^X-Timeout: (\d+)/;
 
2543   } elsif (/^HTTP.* 301 /) {
 
2544     while (<$SH>) { last if /Location/ }
 
2545     die "$0: cannot use $server:$port because server has a redirection to\n".$_;
 
2547     # no SID support - perhaps transparent web proxy?
 
2550       print "<-- $_" if $opt_v;
 
2551       $fexsrv = $_ if /^(Server: fexsrv|X-Features:)/;
 
2554     die "$0: no fexserver at $server:$port\n" unless $fexsrv;
 
2555     serverconnect($server,$port);
 
2559   # warn "proxy: $proxy\n";
 
2561     serverconnect($server,$port);
 
2569   my ($from,$id,$save) = @_;
 
2572   my ($url,$B,$b,$t0,$t1,$cl);
 
2577   $url = "$proxy_prefix/fop/$from/$from/$xx?ID=$id";
 
2579   sendheader("$server:$port","GET $url HTTP/1.0","User-Agent: $useragent");
 
2583     print "<-- $_" if $opt_v;
 
2584     $cl = $1 if /^Content-Length:\s(\d+)/;
 
2585     # $ft = $1 if /^X-File-Type:\s(.+)/;
 
2589   die "$0: no Content-Length in server-reply\n" unless $cl;
 
2591   open F,">$save" or die "$0: cannot write to $save - $!\n";
 
2594   $t0 = $t1 = int(time);
 
2597   while ($b = read($SH,$_,$bs)) {
 
2600     if (int(time) > $t1) {
 
2604         print STDERR $ts,"\r";
 
2608     sleep 1 while ($opt_m and $B/k/(time-$t0||1) > $opt_m);
 
2611   print STDERR ts($B,$cl),"\n";
 
2619   return sprintf("transferred: %d MB (%d%%)",int($b/M),int($b/$tb*100));
 
2623 sub sigpipehandler {
 
2629   local $SIG{ALRM} = sub { };
 
2635     kill 9,$tpid if $tpid;
 
2636     if (@r and $opt_v) {
 
2637       die "\n$0: ($$) server error: @r\n";
 
2639     if (@r and $r[0] =~ /^HTTP.* \d+ (.*)/) {
 
2640       die "\n$0: server error: $1\n";
 
2644   warn "\n$0: connection to $server $reason\n";
 
2645   warn "retrying after $timeout seconds...\n";
 
2647   if ($windoof) { exec $^X,$0,@_ARGV }
 
2648   else          { exec $_0,@_ARGV }
 
2653 sub checkrecipient {
 
2654   my ($from,$to) = @_;
 
2662         command => 'CHECKRECIPIENT',
 
2665   $_ = shift @r or die "$0: no reply from server\n";
 
2670       if (s/X-(Recipient: .+)/$1\n/) {
 
2671         s/autodelete=\w+/autodelete=$opt_D/ if $opt_D;
 
2672         s/keep=\d+/keep=$opt_k/             if $opt_k;
 
2674         $frecipient ||= (split)[1];
 
2678     http_response($_,@r);
 
2683 # get ID data from ID file
 
2687   $fexcgi = getline($idf) || die "$0: no FEX-URL in $idf\n";
 
2688   $from   = getline($idf) || die "$0: no FROM in $idf\n";
 
2689   $id     = getline($idf) || die "$0: no ID in $idf\n";
 
2690   if ($fexcgi =~ s/!([\w.-]+:\d+)(:(\d+))?//) {
 
2692     $chunksize = $3 || 0;
 
2694   unless ($fexcgi =~ /^[_:=\w\-\.\/\@\%]+$/) {
 
2695     die "$0: illegal FEX-URL \"$fexcgi\" in $idf\n";
 
2697   unless ($from =~ /^[_:=\w\-\.\/\@\%\+]+$/) {
 
2698     die "$0: illegal FROM \"$from\" in $idf\n";
 
2708     print "file to send: ";
 
2709     chomp($file = <STDIN>);
 
2713     warn "$file does not exist\n";
 
2715   print "recipient (e-mail address): ";
 
2716   chomp($to = <STDIN>);
 
2717   die $usage unless $to;
 
2720     chomp($opt_C = <STDIN>);
 
2722   @ARGV = ($file,$to);
 
2727   if (system(@_) < 0) { die "failed: @_\n" }
 
2731 # emulate seek on a pipe
 
2733   my $fh = shift; # filehandle
 
2734   my $ba = shift; # bytes ahead
 
2742     $n = $bs if $n > $bs; 
 
2743     $s += read $fh,$_,$n; 
 
2748 # fileid is inode and mtime
 
2750   my @s = stat(shift);
 
2751   return @s ? $s[1].$s[9] : int(time);
 
2755 sub get_mutt_alias {
 
2757   my $ma = $HOME.'/.mutt/aliases';
 
2761   open $ma,$ma or return $to;
 
2763     if (/^alias \Q$to\E\s/i) {
 
2771         warn "$0: ignoring mutt multi-alias $to = $alias\n";
 
2776         warn "$0: found mutt alias $to = $alias\n";
 
2782   return ($alias||$to);
 
2786 # collect file meta data (filename, inode, mtime)
 
2792   foreach $file (@files) {
 
2793     if (not -l $file and -d $file) {
 
2795       if (opendir $dir,$dir) {
 
2796         while (defined ($file = readdir($dir))) {
 
2797           next if $file eq '..';
 
2799             $fmd .= $file.fileid($dir);
 
2801             $fmd .= fmd("$dir/$file");
 
2807       $fmd .= $file.fileid($file);
 
2815 # from MIME::Base64::Perl
 
2821   tr|A-Za-z0-9+=/||cd;
 
2823   tr|A-Za-z0-9+/| -_|;
 
2824   return "" unless length;
 
2827   for ($i = 0; $i <= $l; $i += 60) {
 
2828     $uu .= "M" . substr($_,$i,60);
 
2832     $uu .= chr(32+(length)*3/4) . $_;
 
2834   return unpack("u",$uu);
 
2840   if (open my $tty,'/dev/tty') {
 
2844           "  [p] perhaps - don't know\n",
 
2848     if (/^y/i) { return }
 
2850     if (/^p/i) { int(rand(2)) ? return : exit }
 
2857   local $_ = shift || <$SH>;
 
2861   $_ = <$SH> unless $_;
 
2862   unless (defined $_ and /\w/) {
 
2863     die "$0: no response from server\n";
 
2865   print "<-- $_\n" if $opt_v;
 
2867   # CGI fatalsToBrowser
 
2868   if (/^HTTP.* 500/) {
 
2869     @r = <$SH> unless @r;
 
2871     die "$0: server error: $_\n@r\n";
 
2873   unless (/^HTTP.* 200/) {
 
2875     $error =~ s/HTTP.[\s\d.]+//;
 
2876     @r = <$SH> unless @r;
 
2880       $error .= "\n".$_ if /^Location/;
 
2881       print "<-- $_\n" if $opt_v;
 
2883     die "$0: server error: $error\n";
 
2886   print "<-- $_\n" if $opt_v;
 
2898   my $cfb = '### common functions ###';
 
2903   open $0,$0 or die "cannot read $0 - $!\n";
 
2909   foreach my $p (qw(fexget sexsend)) {
 
2910     open $p,$p or die "cannot read $p - $!\n";
 
2913     s/\n$cfb.*/\n$cfb\n$cfc/s;
 
2915     open $p,'>',$p or die "cannot write $p - $!\n";
 
2920   exec "l $0 fexget sexsend";
 
2924 ### common functions ###
 
2928   my @d = localtime((stat shift)[9]);
 
2929   return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
 
2935   s/\%([a-f\d]{2})/chr(hex($1))/ige;
 
2941   # set SSL/TLS options
 
2942   $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
 
2943   foreach my $opt (qw(
 
2952     $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
 
2955   if ($SSL{SSL_verify_mode}) {
 
2957     unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
 
2958       die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
 
2960   } elsif (defined($SSL{SSL_verify_mode})) {
 
2961     # user has set SSLVERIFY=0 !
 
2964     $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
 
2970   return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
 
2971   foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
 
2973       $SSL{SSL_ca_file} = $_;
 
2977   foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
 
2979       $SSL{SSL_ca_path} = $_;
 
2987   my ($server,$port) = @_;
 
2988   my $connect = "CONNECT $server:$port HTTP/1.1";
 
2992     tcpconnect(split(':',$proxy));
 
2994       printf "--> %s\n",$connect if $opt_v;
 
2995       nvtsend($connect,"");
 
2998       printf "<-- $_"if $opt_v;
 
2999       unless (/^HTTP.1.. 200/) {
 
3000         die "$0: proxy error : $_";
 
3003       $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
 
3006     tcpconnect($server,$port);
 
3008 #  if ($https and $opt_v) {
 
3009 #    printf "%s\n",$SH->get_cipher();
 
3014 # set up tcp/ip connection
 
3016   my ($server,$port) = @_;
 
3024     # eval "use IO::Socket::SSL qw(debug3)";
 
3026     $SH = IO::Socket::SSL->new(
 
3027       PeerAddr => $server,
 
3033     $SH = IO::Socket::INET->new(
 
3034       PeerAddr => $server,
 
3043     die "$0: cannot connect $server:$port - $@\n";
 
3046   print "TCPCONNECT to $server:$port\n" if $opt_v;
 
3051   eval "use IO::Socket::SSL";
 
3052   die "$0: cannot load IO::Socket::SSL\n" if $@;
 
3053   eval '$SSL{SSL_verify_mode} = 0 if Net::SSLeay::SSLeay() <= 9470143';
 
3055     foreach my $v (keys %SSL) {
 
3056       printf "%s => %s\n",$v,$SSL{$v};
 
3067   push @head,"Host: $sp";
 
3069   foreach $head (@head) {
 
3070     print "--> $head\n" if $opt_v;
 
3071     print {$SH} $head,"\r\n";
 
3073   print "-->\n" if $opt_v;
 
3079   local $SIG{PIPE} = sub { $sigpipe = "@_" };
 
3083   die "$0: internal error: no active network handle\n" unless $SH;
 
3084   die "$0: remote host has closed the link\n" unless $SH->connected;
 
3086   foreach my $line (@_) {
 
3087     print {$SH} $line,"\r\n";
 
3098 # from MIME::Base64::Perl
 
3105   $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
 
3106   $res =~ tr|` -_|AA-Za-z0-9+/|;
 
3107   $padding = (3-length($_[0])%3)%3;
 
3108   $res =~ s/.{$padding}$/'=' x $padding/e if $padding;