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 = 20150615;
 
  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);
 
  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
 
 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+)//;
 
 501 if (0 and $port == 443) {
 
 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"; 
 
 506 $server =~ s{http://}{};
 
 509 # $chunksize = 4*k unless $chunksize;
 
 513   if    ($port == 80)  { $proxy_prefix = "http://$server" }
 
 514   elsif ($port != 443) { $proxy_prefix = "http://$server:$port" }
 
 517 # xx: special file exchange between own accounts
 
 519   my $transferfile = "$tmpdir/STDFEX";
 
 522     $transferfile = "$tmpdir/xx:xxx";
 
 523   } elsif (@ARGV and $ARGV[0] =~ /^:([\w.=+-]+)$/) {
 
 524     $transferfile = "$tmpdir/xx:$1";
 
 527   open my $lock,'>>',$transferfile 
 
 528     or die "$0: cannot write $transferfile - $!\n";
 
 529   flock($lock,LOCK_EX|LOCK_NB)
 
 530     or die "$0: $transferfile is locked by another process\n";
 
 531   truncate $transferfile,0;
 
 532   if (not @ARGV and -t) {
 
 533     &get_xx($transferfile);
 
 535     &send_xx($transferfile);
 
 542 &inquire if $windoof and not @ARGV and not
 
 543             ($opt_l or $opt_L or $opt_Q or $opt_A or $opt_U or $opt_I or
 
 544              $opt_f or $opt_x or $opt_N);
 
 547   $opt_C = "!SHORTMAIL! $opt_C";
 
 550 if ($opt_n or $opt_C =~ /NOMAIL|!#!/) {
 
 554 unless ($skey or $gkey or $anonymous) {
 
 556     $opt_f||$opt_x||$opt_Q||$opt_l||$opt_L||$opt_U||$opt_z||$opt_Z||$opt_A
 
 557     ||$opt_d||${'opt_!'}||${'opt_@'})
 
 558   ) { warn "Server/User: $fexcgi/$from\n" }
 
 561 if    ($opt_V and not @ARGV)            { exit }
 
 562 if    ($opt_f)                          { &forward } 
 
 563 elsif ($opt_x)                          { &modify } 
 
 564 elsif ($opt_N)                          { &renotify } 
 
 565 elsif ($opt_Q)                          { &query_quotas } 
 
 566 elsif ($opt_S)                          { &query_settings } 
 
 567 elsif ($opt_l or $opt_L)                { &list } 
 
 568 elsif ($opt_U)                          { &show_URL } 
 
 569 elsif ($opt_z or $opt_Z or ${'opt_!'})  { &get_log } 
 
 570 elsif ($opt_A)                          { edit_address_book($from) }
 
 571 elsif (${'opt_@'})                      { &show_address_book } 
 
 572 elsif ($opt_d and $anonymous)           { &purge }
 
 573 elsif ($opt_d and $ARGV[-1] =~ /^\d+$/) { &delete }
 
 579 # initialize ID file or show ID
 
 589   $fexcgi = $from = $id = '';
 
 591   unless (-d $fexhome) {
 
 592     mkdir $fexhome,0700 or die "$0: cannot create FEXHOME $fexhome - $!\n";
 
 596   if (not $tag and open $idf,$idf) {
 
 599         last if /^\[$opt_i\]/;
 
 607       chomp($fexcgi,$from,$id);
 
 608       $FEXID = encode_b64("$fexcgi $from $id");
 
 610         print "# hint: to edit the ID file $idf use \"$0 -I .\" #\n";
 
 611         print "export FEXID=$FEXID\n";
 
 612         print "history -d \$((HISTCMD-1));history -d \$((HISTCMD-1))\n";
 
 614         print "FEXID=$FEXID\n";
 
 618       die "$0: no ID data found\n";
 
 622   if ($tag and $tag eq '.') { exec $ENV{EDITOR}||'vi',$idf }
 
 624   if ($tag) { print "F*EX server URL for [$tag]: " }
 
 625   else      { print "F*EX server URL: " }
 
 627   $fexcgi =~ s/[\s\n]//g;
 
 628   die "you MUST provide a FEX-URL!\n" unless $fexcgi;
 
 629   if ($fexcgi =~ /\?/) {
 
 630     $from = $1 if $fexcgi =~ /\bfrom=(.+?)(&|$)/i;
 
 631     $id   = $1 if $fexcgi =~ /\bid=(.+?)(&|$)/i;
 
 632     # $skey = $1 if $fexcgi =~ /\bskey=(.+?)(&|$)/i;
 
 633     # $gkey = $1 if $fexcgi =~ /\bgkey=(.+?)(&|$)/i;
 
 634     die "$0: cannot use GKEY URL in ID file\n" if $fexcgi =~ /gkey=/i;
 
 635     die "$0: cannot use SKEY URL in ID file\n" if $fexcgi =~ /skey=/i;
 
 638   unless ($fexcgi =~ /^[_:=\w\-\.\/\@\%]+$/) {
 
 639     die "\"$fexcgi\" is not a legal FEX-URL!\n";
 
 641   $fexcgi =~ s:/fup/*$::;
 
 642   print "proxy address (hostname:port or empty if none): ";
 
 644   $proxy =~ s/[\s\n]//g;
 
 645   if ($proxy =~ /^[\w.-]+:\d+$/) { 
 
 647   } elsif ($proxy =~ /\S/) { 
 
 648     die "wrong proxy address format\n";
 
 653     print "proxy POST limit in MB (use 2048 if unknown): ";
 
 663     $from = 'GROUPMEMBER';
 
 667       print "Your e-mail address as registered at $fexcgi: ";
 
 669       $from =~ s/[\s\n]//g;
 
 670       die "you MUST provide your e-mail address!\n" unless $from;
 
 672     unless ($from =~ /^[_:=\w\-\.\/\@\%\+]+$/) {
 
 673       die "\"$from\" is not a legal e-mail address!\n";
 
 676       print "Your auth-ID for $from at $fexcgi: ";
 
 679       die "you MUST provide your ID!\n" unless $id;
 
 682   if (open $idf,'>>',$idf) {
 
 683     print {$idf} "\n[$tag]\n" if $tag and -s $idf;
 
 684     print {$idf} "$fexcgi$proxy\n",
 
 688     print "data written to $idf\n";
 
 690     die "$0: cannot write to $idf - $!\n";
 
 696   my ($fexcgi,$from,$id);
 
 697   if (open $idf,$idf) {
 
 709     die "$0: too few data in $idf" unless defined $id;
 
 713     $FEXXX = encode_b64("$fexcgi $from $id");
 
 715       print "export FEXXX=$FEXXX\n";
 
 716       print "history -d \$((HISTCMD-1));history -d \$((HISTCMD-1))\n";
 
 718       print "FEXXX=$FEXXX\n";
 
 721     die "$0: cannot read $idf - $!\n";
 
 727   my $fs = shift @ARGV or die $usage;
 
 728   my $mail = shift @ARGV or die $usage;
 
 730   my ($server,$user,$id);
 
 732   die "$0: $idf does already exist\n" if -e $idf;
 
 734   if ($fs =~ /^https/) {
 
 735     die "$0: cannot handle https at this time\n";
 
 738   $fs =~ s{^http://}{};
 
 740   if ($fs =~ s/:(\d+)//) { $port = $1 }
 
 743   tcpconnect($fs,$port);
 
 744   sendheader("$fs:$port","GET $proxy_prefix/fur?user=$mail&verify=no HTTP/1.1");
 
 749     printf "<-- $_"if $opt_v;
 
 755     printf "<-- $_"if $opt_v;
 
 756     if (m{http://(.*)/fup\?from=(.+)&ID=(.+)}) {
 
 761       if (open F,">$idf") {
 
 767         print "user data written to $idf\n";
 
 768         print "you can now fex!\n";
 
 771         die "$0: cannot write to $idf - $!\n";
 
 776   die "$0: no account data received from F*EX server\n";
 
 782   my $transferfile = shift;
 
 786   $SIG{PIPE} = $SIG{INT} = sub {
 
 787     unlink $transferfile;
 
 791   if ($0 eq 'xxx') { @tar = qw'tar -cv' }
 
 792   else             { @tar = qw'tar -cvz' }
 
 795     if ("@ARGV" eq '-') {
 
 796       # store STDIN to transfer file
 
 797       shelldo("cat >> $transferfile");
 
 799       print "making tar transfer file $transferfile :\n";
 
 800       # single file? then add this directly 
 
 801       if (scalar @ARGV == 1) {
 
 803         # strip path if not ending with /
 
 804         if ($ARGV[0] =~ m:(.+)/(.+): and $2 !~ m:/$:) {
 
 805           ($dir,$file) = ($1,$2);
 
 806           chdir $dir or die "$0: $dir - $!\n";
 
 811           shelldo(@tar,qw'--dereference -f',$transferfile,$file);
 
 813           shelldo(@tar,'-f',$transferfile,$file);
 
 816         shelldo(@tar,'-f',$transferfile,@ARGV);
 
 819         unlink $transferfile;
 
 821           die "$0: interrupted making tar transfer file\n";
 
 823           die "$0: error while making tar transfer file\n";
 
 828     # write input from pipe to transfer file
 
 829     shelldo("cat >> $transferfile");
 
 832   die "$0: no transfer file\n" unless -s $transferfile;
 
 834   serverconnect($server,$port);
 
 835   query_sid($server,$port);
 
 841     file        => $transferfile,
 
 843     autodelete  => $transferfile =~ /STDFEX/ ? 'NO' : 'DELAY',
 
 846   # open P,'|w3m -T text/html -dump' or die "$0: w3m - $!\n";
 
 849   if ($transferfile =~ /:/ and $0 ne 'xxx') {
 
 850     if ("@r" =~ /\s(X-)?Location: (http.*)\s/) {
 
 851       print "wget -O- $2 | tar xvzf -\n";
 
 855   unlink $transferfile;
 
 863   female_mode("query quotas?") if $opt_F;
 
 871   die "$0: no response from fex server $server\n" unless @r;
 
 873   unless (/^HTTP.* 2/) {
 
 875     die "$0: server response: $_\n";
 
 877   if (($_) = grep(/^X-Sender-Quota/,@r) and /(\d+)\s+(\d+)/) {
 
 878     print "sender quota (used): $1 ($2) MB\n";
 
 880     print "sender quota: unlimited\n";
 
 882   if (($_) = grep(/^X-Recipient-Quota/,@r) and /(\d+)\s+(\d+)/) {
 
 883     print "recipient quota (used): $1 ($2) MB\n";
 
 885     print "recipient quota: unlimited\n";
 
 894   female_mode("query settings?") if $opt_F;
 
 897     print "ID data from \$FEXID\n";
 
 899     print "ID data from $idf\n";
 
 901     die "$0: found no ID\n";
 
 903   print "server: $fexcgi\n";
 
 904   print "user: $from\n";
 
 905   print "auth-ID: $id\n";
 
 915   die "$0: no response from fex server $server\n" unless @r;
 
 917   unless (/^HTTP.* 2/) {
 
 919     die "$0: server response: $_\n";
 
 921   if (($_) = grep(/^X-Autodelete/,@r) and /:\s+(\w+)/) {
 
 922     print "autodelete: $1\n";
 
 924   if (($_) = grep(/^X-Default-Keep/,@r) and /(\d+)/) {
 
 925     print "default keep: $1 days\n";
 
 927   if (($_) = grep(/^X-Default-Locale/,@r) and /:\s+(\w+)/) {
 
 928     print "default locale: $1\n";
 
 930   if (($_) = grep(/^X-MIME/,@r) and /:\s+(\w+)/) {
 
 931     print "display file with browser: $1\n";
 
 933   if (($_) = grep(/^X-Sender-Quota/,@r) and /(\d+)\s+(\d+)/) {
 
 934     print "sender quota (used): $1 ($2) MB\n";
 
 936     print "sender quota: unlimited\n";
 
 938   if (($_) = grep(/^X-Recipient-Quota/,@r) and /(\d+)\s+(\d+)/) {
 
 939     print "recipient quota (used): $1 ($2) MB\n";
 
 941     print "recipient quota: unlimited\n";
 
 952   female_mode("list spooled files?") if $opt_F;
 
 954   if ($opt_l and $n = shift @ARGV and $n =~ /^\d+$/) {
 
 955     open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
 
 957       if (/^\s*(\d+)\) (\w+) (.+)/ and $1 eq $n) {
 
 958         serverconnect($server,$port) unless $SH;
 
 961           "GET $proxy_prefix/fop/$2/$2?LIST HTTP/1.1",
 
 962           "User-Agent: $useragent",
 
 966         print "<-- $_" if $opt_v;
 
 968           print "<-- $_" if $opt_v;
 
 976         } elsif (s:HTTP/[\d\. ]+::) {
 
 977           die "$0: server response: $_";
 
 979           die "$0: no response from fex server $server\n";
 
 984     die "$0: file \#$n not found in fexlist\n";
 
 988       to        => $opt_l ? '*' : $from,
 
 992   die "$0: no response from fex server $server\n" unless @r;
 
 994   unless (/^HTTP.* 200/) {
 
 996     die "$0: server response: $_\n";
 
1001     open $fexlist,">$fexlist" or die "$0: cannot write $fexlist - $!\n";
 
1003       next unless /<pre>/ or $data;
 
1006       if (/<a href=".*dkey=(\w+).*?">/) { $dkey = $1 }
 
1008 #      $_ = encode_utf8($_);
 
1015         print {$fexlist} "\n$1\n";
 
1016       } elsif (m/(\d+) MB (.+)/) {
 
1018         printf "%4s) %8d MB %s\n","#$n",$1,$2;
 
1019         printf {$fexlist} "%3d) %s %s\n",$n,$dkey,$2;
 
1025   # list received files
 
1028       next unless /<pre>/ or $data;
 
1032       if (/(from .* :)/) {
 
1035       if (m{(\d+) (MB.*)<a href="(https?://.*/fop/\w+/.+)">(.+)</a>( ".*")?}) {
 
1036         printf "%8d %s%s%s\n",$1,$2,$3,($5||'');
 
1044   printf "%s/fup/%s\n",$fexcgi,encode_b64("from=$from&id=$id");
 
1058   die "$0: no response from fex server $server\n" unless @r;
 
1060   unless (/^HTTP.* 200/) {
 
1062     die "$0: server response: $_\n";
 
1065   foreach (@r) { print "$_\n" }
 
1069 sub show_address_book {
 
1074   %AB = query_address_book($server,$port,$from);
 
1075   foreach $alias (sort keys %AB) {
 
1076     next if $alias eq 'ADDRESS_BOOK';
 
1077     $_ = sprintf "%s = %s (%s) # %s\n",
 
1080                  $AB{$alias}->{options},
 
1081                  $AB{$alias}->{comment};
 
1090   die "$0: not yet implemented\n";
 
1098     $opt_d = shift @ARGV;
 
1099     die "$usage: $0 -d #\n" if $opt_d !~ /^\d+$/;
 
1101     open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
 
1102     while (<$fexlist>) {
 
1103       if (/^to (.+\@.+) :/) {
 
1105       } elsif (/^\s*(\d+)\) (\w+) (.+)/ and $1 eq $opt_d) {
 
1106         serverconnect($server,$port) unless $SH;
 
1109           "GET $proxy_prefix/fop/$2/$2?DELETE HTTP/1.1",
 
1110           "User-Agent: $useragent",
 
1114         print "<-- $_" if $opt_v;
 
1115         if (/^HTTP.* 200/) {
 
1118             last if /^\n/; # ignore HTML output
 
1119             print "<-- $_" if $opt_v;
 
1120             if (/^X-File:.*\/(.+)/) {
 
1121               printf "%s deleted\n",decode_utf8(urldecode($1));
 
1125         } elsif (s:HTTP/[\d\. ]+::) {
 
1126           die "$0: server response: $_";
 
1128           die "$0: no response from fex server $server\n";
 
1134     sleep 1; # do not overrun server
 
1145   my ($data,$aname,$alias);
 
1152   if ($from =~ /^SUBUSER|GROUPMEMBER$/) {
 
1155     # look for single @ in arguments
 
1156     for (my $i=1; $i<$#ARGV; $i++) {
 
1157       if ($ARGV[$i] eq '@') {
 
1158         $ARGV[$i] = join(',',@ARGV[$i+1 .. $#ARGV]);
 
1163     $to = pop @ARGV or die $usage;
 
1166       $nomail = $opt_C ||= 'NOMAIL';
 
1170       $nomail = $opt_C ||= 'NOMAIL';
 
1172     if ($opt_g and $to =~ /,/) {
 
1173       die "$0: encryption is supported to only one recipient\n";
 
1175     if ($to =~ m{^https?://.*/fup\?skey=(\w+)}) {
 
1180     if ($to =~ m{^https?://.*/fup\?gkey=(\w+)}) {
 
1181       $from = 'GROUPMEMBER';
 
1186   @to = split(',',lc($to));
 
1188   die $usage unless @ARGV or $opt_a or $opt_s;
 
1189   die $usage if $opt_s and @ARGV;
 
1191   # early serverconnect necessary for X-Features info
 
1192   serverconnect($server,$port);
 
1196     sendheader("$server:$port","OPTIONS FEX HTTP/1.1");
 
1199     die "$0: no response from fex server $server\n" unless $_;
 
1200     print "<-- $_" if $opt_v;
 
1201     if (/^HTTP.* 201/) {
 
1204         print "<-- $_" if $opt_v;
 
1206         $aok = $_ if /X-Features:.*ANONYMOUS/;
 
1208       die "$0: no anonymous support on server $server\n" unless $aok;
 
1210       die "$0: bad response from server $server : $_\n";
 
1215     query_sid($server,$port);
 
1217     if ($from eq 'SUBUSER') {
 
1219       # die "skey=$skey\nid=$id\nsid=$sid\n";
 
1222     if ($from eq 'GROUPMEMBER') {
 
1228       $opt_C ||= 'NOMAIL';
 
1229     } elsif ($to =~ m:^(//.*):) {
 
1231       if ($features =~ /XKEY/) {
 
1235         die "$0: server does not support XKEY\n";
 
1237     } elsif (grep /^[^@]*$/,@to and not $skey and not $gkey) {
 
1238       %AB = query_address_book($server,$port,$from);
 
1240         serverconnect($server,$port);
 
1241         query_sid($server,$port);
 
1244         # alias in local config?
 
1246           if ($alias{$to} =~ /(.+?):(.+)/) {
 
1251             # special extra upload
 
1252             system $0,split(/\s/,$opt),@argv,$ato;
 
1258         # alias in server address book?
 
1260           # do not substitute alias with expanded addresses because then 
 
1261           # keep and autodelete options from address book will get lost
 
1264         # look for mutt aliases
 
1265         elsif ($to !~ /@/ and $to ne $from) {
 
1266           $to = get_mutt_alias($to);
 
1271     $to = join(',',grep /./,@to) or exit;
 
1272     # warn "Server/User: $fexcgi/$from\n" unless $opt_q;
 
1275       not $skey and not $gkey
 
1277       and $features =~ /CHECKRECIPIENT/ 
 
1278       and $opt_C !~ /^(DELETE|LIST|RECEIVEDLOG|SENDLOG|FOPLOG)$/
 
1280       checkrecipient($from,$to);
 
1282         serverconnect($server,$port);
 
1283         query_sid($server,$port);
 
1288   if (@ARGV > 1 and not ($opt_a or $opt_s or $opt_d)) {
 
1289     print "Archive name (name.tar, name.tgz or name.zip) or [ENTER] to send file for file:\n";
 
1298     $opt_s =~ s/[^\w_.+-]/_/g;
 
1303     $opt_a =~ s/[^\w_.+-]/_/g;
 
1304     if ($opt_a =~ /(.+)\.(zip|tar|tgz|7z)$/) {
 
1308       die "$0: archive name must be one of ".
 
1309           "$opt_a.tar $opt_a.tgz $opt_a.zip\n";
 
1311     # no file argument left?
 
1313       # use file name as archive name
 
1318     foreach my $file (@ARGV) {
 
1319       die "$0: cannot read $file\n" unless -l $file or -r $file;
 
1321     $opt_a .= ".$atype" if $opt_a !~ /\.$atype$/;
 
1322     $transferfile = "$tmpdir/$opt_a";
 
1323     unlink $transferfile;
 
1324     print "Making fex archive ($opt_a):\n";
 
1325     if ($atype eq 'zip') {
 
1327         # if ($opt_c) { system(qw'7z a -tzip',$transferfile,@ARGV) }
 
1328         # else        { system(qw'7z a -tzip -mm=copy',$transferfile,@ARGV) }
 
1329         system(qw'7z a -tzip',$transferfile,@ARGV);
 
1330         @files = ($transferfile);
 
1332         # zip archives must be < 2 GB, so split as necessary
 
1333         @files = zipsplit($transferfile,@ARGV);
 
1334         if (scalar(@files) == 1) {
 
1335           $transferfile = $files[0];
 
1336           $transferfile =~ s/_1.zip$/.zip/;
 
1337           rename $files[0],$transferfile;
 
1338           @files = ($transferfile);
 
1341       @transferfiles =  @files;
 
1342     } elsif ($atype eq '7z') {
 
1343       # http://www.7-zip.org/
 
1344       my @X = (); # exclude list
 
1346         foreach my $x (split('#',${'opt_#'})) {
 
1350       if ($opt_c) { system(qw'7z a',@X,$transferfile,@ARGV) }
 
1351       else        { system(qw'7z a -t7z -mx0',@X,$transferfile,@ARGV) }
 
1352       @transferfiles = @files = ($transferfile);
 
1353     } elsif ($atype eq 'tar') {
 
1355         system(qw'7z a -ttar',$transferfile,@ARGV);
 
1356         @transferfiles = @files = ($transferfile);
 
1358         ## tar is now handled by formdatapost()
 
1359         # system(qw'tar cvf',$transferfile,@ARGV);
 
1362     } elsif ($atype eq 'tgz') {
 
1364         die "$0: archive type tgz not available, use tar, zip or 7z\n";
 
1366         ## tgz is now handled by formdatapost()
 
1367         # system(qw'tar cvzf',$transferfile,@ARGV);
 
1371       die "$0: unknown archive format \"$atype\"\n";
 
1374     if (@transferfiles) {
 
1376       # error in making transfer archive?
 
1378         unlink @transferfiles;
 
1379         die "$0: $! - aborting upload\n";
 
1382       # maybe timeout, so make new connect
 
1383       if (time-$t0 >= $timeout) {
 
1384         serverconnect($server,$port);
 
1385         query_sid($server,$port) unless $anonymous;
 
1405             die "$0: $file is not a regular file, try option -a\n"
 
1407             die "$0: $file does not exist\n";
 
1410         die "$0: cannot read $file\n" unless -r $file;
 
1417     foreach my $file (@files) {
 
1418       my @s = stat($file);
 
1419       unless (@s and ($s[2] & S_IROTH) and -r $file) {
 
1420         die "$0: $file is not world readable\n";
 
1425   foreach my $file (@files) {
 
1426     sleep 1;    # do not overrun server!
 
1427     unless (-s $file or $opt_d or $opt_a or $opt_s) {
 
1428       die "$0: cannot send empty file $file\n";
 
1430     female_mode("send file $file?") if $opt_F;
 
1439       autodelete        => $opt_D, 
 
1442     if (not @r or not grep /\w/,@r) {
 
1443       die "$0: no response from server\n";
 
1445     if (($r) = grep /^ERROR:/,@r) {
 
1446       if ($anonymous and $r =~ /purge it/) {
 
1447         die "$0: file is already on server for $to - use another anonymous recipent\n";
 
1451         die "$0: server error: $r\n";
 
1454     if (($r) = grep /<h3>\Q$file/,@r) {
 
1458     if ($opt_a !~ /^afex_\d+\.tar$/ and $file !~ /afex_\d+\.tar$/) {
 
1459       # print grep({s/^(X-Recipient:.*\((.+)\))/Parameters: $2\n/i} @r);
 
1461       my ($recipient,$location);
 
1463         if (/^(X-)?(Recipient.*)/i) {
 
1465           if (/notification=no/i) { $nonot = 1 }
 
1468         if (/^(X-)?(Location.*)/i) {
 
1470           if ($from eq $to or $from =~ /^\Q$to\E@/i 
 
1471               or $nomail or $anonymous or $nonot) {
 
1472             print "$recipient\n";
 
1473             print "$location\n";
 
1480   # delete transfer tmp file
 
1481   unlink $transferfile if $transferfile;
 
1487   my ($to,$n,$dkey,$file,$req);
 
1491   # look for single @ in arguments
 
1492   for (my $i=1; $i<$#ARGV; $i++) {
 
1493     if ($ARGV[$i] eq '@') {
 
1494       $ARGV[$i] = join(',',@ARGV[$i+1 .. $#ARGV]);
 
1500   # if ($windoof and not @ARGV) { &inquire }
 
1501   $to = pop @ARGV or die $usage;
 
1502   $to = $from if $to eq '.';
 
1503   if ($to !~ /@/ and $to ne $from) {
 
1504     $to = get_mutt_alias($to);
 
1507   open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
 
1508   while (<$fexlist>) {
 
1509     if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_f) {
 
1513       if ($file =~ s/ "(.*)"$//) {
 
1514         $opt_C ||= $1 if $1 ne 'NOMAIL';
 
1522     die "$0: file #$opt_f not found in fexlist\n";
 
1525   female_mode("forward file #$opt_f?") if $opt_F;
 
1527   serverconnect($server,$port);
 
1528   query_sid($server,$port);
 
1530   $req = "GET $proxy_prefix/fup?"
 
1531         ."from=$from&ID=$sid&to=$to&dkey=$dkey&command=FORWARD";
 
1532   $req .= "&comment=$opt_C"     if $opt_C;
 
1533   $req .= "&keep=$opt_k"        if $opt_k;
 
1534   $req .= "&autodelete=$opt_D"  if $opt_D;
 
1535   $req .= "&$opt_X"             if $opt_X;
 
1536   $req .= " HTTP/1.1";
 
1537   sendheader("$server:$port",$req);
 
1540   $fp =~ s/[^\w_.-]/.+/g; # because of UTF8 filename
 
1543     $status = 0 if /"$fp"/;
 
1544     print if $opt_v or /"$fp"/;
 
1548     die "$0: server failed, rerun command with option -v\n";
 
1556   my ($to,$n,$dkey,$file,$req,$recipient);
 
1559   die $usage if @ARGV;
 
1561   open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
 
1562   while (<$fexlist>) {
 
1563     if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_N) {
 
1572     die "$0: file #$opt_N not found in fexlist\n";
 
1575   female_mode("resend notification for file #$opt_N?") if $opt_F;
 
1577   serverconnect($server,$port);
 
1578   query_sid($server,$port);
 
1580   $req = "GET $proxy_prefix/fup?"
 
1581         ."from=$from&ID=$sid&dkey=$dkey&command=RENOTIFY"
 
1583   sendheader("$server:$port",$req);
 
1587     print "<-- $_" if $opt_v;
 
1589     if (/^X-Notify: (.+)\/(.+)\/(.+)/) {
 
1596     print "notification e-mail for $file has been resent to $recipient\n";
 
1599       die "$0: server failed\n";
 
1601       die "$0: server failed, rerun command with option -v\n";
 
1611   my ($n,$dkey,$file,$req);
 
1614   die $usage if @ARGV;
 
1615   die $usage unless $opt_C or $opt_k or $opt_D;
 
1617   open $fexlist,$fexlist or die "$0: $fexlist - $!\n";
 
1618   while (<$fexlist>) {
 
1619     if (/^\s*(\d+)\) (\w+) \[\d+ d\] (.+)/ and $1 eq $opt_x) {
 
1623       $file =~ s/ "(.*)"$//;
 
1630     die "$0: file #$opt_x not found in fexlist\n";
 
1633   female_mode("modify file #$opt_x?") if $opt_F;
 
1635   serverconnect($server,$port);
 
1636   query_sid($server,$port);
 
1638   $req = "GET $proxy_prefix/fup?"
 
1639         ."from=$from&ID=$sid&dkey=$dkey&command=MODIFY";
 
1640   $req .= "&comment=$opt_C"     if $opt_C;
 
1641   $req .= "&keep=$opt_k"        if $opt_k;
 
1642   $req .= "&autodelete=$opt_D"  if $opt_D;
 
1643   $req .= " HTTP/1.1";
 
1644   sendheader("$server:$port",$req);
 
1659   my $transferfile = shift;
 
1663   # get transfer file from FEX server
 
1665     serverconnect($server,$port);
 
1666     query_sid($server,$port);
 
1669   xxget($from,$sid,$transferfile);
 
1672   unless (-s $transferfile) {
 
1673     unlink $transferfile;
 
1677   # no further processing if delivering to pipe
 
1678   exec 'cat',$transferfile unless -t STDOUT;
 
1680   if ($ft = `file $transferfile 2>/dev/null`) {
 
1681     if ($ft =~ /compressed/) {
 
1682       rename $transferfile,"$transferfile.gz";
 
1683       shelldo(ws("gunzip $transferfile.gz"));
 
1685     $ft = `file $transferfile`;
 
1687   # file command failed, so we look ourself into the file...
 
1688   elsif (open $transferfile,$transferfile) {
 
1689     read $transferfile,$_,4;
 
1690     close $transferfile;
 
1692     if (/\x1F\x8B\x08\x00/) {
 
1693       rename $transferfile,"$transferfile.gz";
 
1694       shelldo(ws("gunzip $transferfile.gz"));
 
1696       $ft = 'tar archive';
 
1699   if ($ft =~ /tar archive/) {
 
1700     rename $transferfile,"$transferfile.tar";
 
1701     $transferfile .= '.tar';
 
1705       print "Files in transfer-container:\n\n";
 
1706       shelldo(ws("tar tvf $transferfile"));
 
1707       print "\nExtract these files? [Yn] ";
 
1711       print "keeping $transferfile\n";
 
1713       my $untar = "tar xvf";
 
1714       # if ($> == 0 and `tar --help 2>&1` =~ /gnu/) {
 
1715       #  $untar = "tar --no-same-owner -xvf";
 
1717       system("$untar $transferfile && rm $transferfile");
 
1718       die "$0: error while untaring, see $transferfile\n" if -f $transferfile;
 
1721     exec 'cat',$transferfile;
 
1729   my ($boundary,$filename,$filesize,$length,$buf,$file,$fpsize,$resume,$seek);
 
1731   my (@hh,@hb,@r,@pv,$to);
 
1733   my ($t0,$t1,$t2,$tt,$tc);
 
1734   my $bs = 2**16;        # blocksize for reading and sending file
 
1735   my $fileid = int(time);
 
1737   my $connection = '';
 
1739   my ($tar,$aname,$atype,$tarlist,$tarerror,$location,$transferfile);
 
1742   if (defined($file = $P{file})) {
 
1744     $to = $AB{$P{to}} || $P{to}; # for gpg
 
1746     # special file: stream from STDIN
 
1748       $filename = encode_utf8($file);
 
1756       $if =~ s/([^_\w\.\-])/\\$1/g;
 
1757       $transferfile = $tmpdir . '/' . basename($file) . '.gz';
 
1758       $of = $transferfile;
 
1759       $of =~ s/([^_\w\.\-])/\\$1/g;
 
1760       shelldo("gzip <$if>$of");
 
1761       $filesize = -s $transferfile;
 
1762       die "$0: cannot gzip $file\n" unless $filesize;
 
1763       $file = $transferfile;
 
1766     # special file: tar-on-the-fly
 
1767     if (not $windoof and $opt_a and $file =~ /(.+)\.(tar|tgz)$/) {
 
1770       $tarlist  = "$tmpdir/$aname.list";
 
1771       $tarerror = "$tmpdir/$aname.error";
 
1773       $tar .= 'z' if $atype eq 'tgz';
 
1774       if (`tar --help 2>/dev/null` =~ /--index-file/) {
 
1775         $tar .= " --index-file=$tarlist -f-";
 
1780         foreach my $x (split('#',${'opt_#'})) {
 
1781           $tar .= " --exclude=$x";
 
1786         $file =~ s/([^\w\-\@\#%,.=+~_:])/\\$1/g;
 
1789       # print "calculating archive size... ";
 
1790       open $tar,"$tar 2>$tarerror|" or die "$0: cannot run tar - $!\n";
 
1791       $t0 = int(time) if -t STDOUT;
 
1792       while ($b = read $tar,$_,$bs) {
 
1797             printf "Archive size: %d MB\r",int($filesize/M);
 
1802       printf "Archive size: %d MB\n",int($filesize/M) if -t STDOUT;
 
1803       unless (close $tar) {
 
1805         if (open $tarerror,$tarerror) {
 
1810         unlink $tarlist,$tarerror;
 
1811         die "$0: tar error:\n$_";
 
1813       $file = "$aname.$atype";
 
1814       $filename = encode_utf8($file);
 
1815       undef $SH; # force reconnect (timeout!)
 
1820       $filename = encode_utf8(${'opt_='} || $file);
 
1823         $filename =~ s/^[a-z]://;
 
1824         $filename =~ s/.*\\//;
 
1826       $filename =~ s:.*/::;
 
1827       $filename =~ s:[\r\n]+: :g;
 
1830       } elsif (not $opt_g and not $opt_s) {
 
1831         $filesize = -s $file or die "$0: $file is empty or not readable\n";
 
1835     $filename .= '.gpg' if $opt_g;
 
1840         $fileid = int(time);
 
1843           $fileid = md5_hex(fmd(@ARGV));
 
1845           $fileid = fileid($file);
 
1851     $file = $filename = '';
 
1857   @hh = (); # HTTP header
 
1858   @hb = (); # HTTP body
 
1865     serverconnect($server,$port);
 
1866     query_sid($server,$port) unless $anonymous;
 
1869   $P{id} = $sid; # ugly hack!
 
1871   # ask server if this file has been already sent
 
1872   if ($file and not $xx and not 
 
1873       ($opt_s or $opt_g or $opt_o or $opt_d or $opt_l or $opt_L or ${'opt_/'}))
 
1875     ($seek,$location) = query_file($server,$port,$frecipient||$P{to},$P{from},
 
1876                                    $P{id},$filename,$fileid);
 
1877     if ($filesize == $seek) {
 
1878       print "Location: $location\n" if $location and $nomail;
 
1879       warn "$0: $file has been already transferred\n";
 
1881     } elsif ($seek and $seek < $filesize) {
 
1882       $resume = " (resuming at byte $seek)";
 
1883     } elsif ($filesize <= $seek) {
 
1887       sleep 1;    # do not overrun proxy
 
1888       serverconnect($server,$port);
 
1893   if ($chunksize and $proxy and $port != 443 
 
1894       and $filesize - $seek > $chunksize - $bs) {
 
1895     if ($features !~ /MULTIPOST/) {
 
1896       die sprintf("$0: server does not support chunked multi-POST needed for"
 
1897                   ." files > %d MB via proxy\n",$chunksize/M);
 
1899     $opt_o = 0; # no overwriting mode for next chunks
 
1900     $fpsize = $chunksize - $bs;
 
1902     $fpsize = $filesize - $seek;
 
1905   $boundary = randstring(48);
 
1908   $P{filesize} = $filesize;
 
1910   # send HTTP POST variables
 
1913     @pv = qw'from to skey keep autodelete comment seek filesize';
 
1916     @pv = qw'from to gkey keep autodelete comment seek filesize';
 
1918     @pv = qw'from to id replyto keep autodelete comment command seek filesize';
 
1920   foreach my $v (@pv) {
 
1923       push @hb,"--$boundary";
 
1924       push @hb,"Content-Disposition: form-data; name=\"$name\"";
 
1926       push @hb,encode_utf8($P{$v});
 
1930   # at last, POST the file
 
1932     push @hb,"--$boundary";
 
1933     push @hb,"Content-Disposition: form-data; name=\"FILE\"; filename=\"$filename\"";
 
1935       if ($opt_M) { push @hb,"Content-Type: application/x-mime" }
 
1936       else        { push @hb,"Content-Type: application/octet-stream" }
 
1938         $flink = abs_path($file);
 
1939         push @hb,"Content-Location: $flink";
 
1941         # push @hb,"Content-Length: " . ((-s $file||0) - $seek); # optional header!
 
1942         push @hb,"Content-Length: $fpsize"; # optional header! NOT filesize!
 
1943         push @hb,"X-File-ID: $fileid";
 
1948     # prevent proxy chunked mode reply
 
1949     $connection = "close";
 
1952   push @hb,"--$boundary--";
 
1957     $length = length(join('',@hb)) + scalar(@hb)*2 + $fpsize;
 
1960   if ($file and not $opt_d) {
 
1961     if ($flink) { $hb[-2] = $flink }
 
1962     else        { $hb[-2] = '(file content)' }
 
1964   # any other extra URL arguments
 
1966   $opt_X = "?$::opt_X" if $::opt_X and $file;
 
1969   push @hh,"POST $proxy_prefix/fup$opt_X HTTP/1.1";
 
1970   push @hh,"Host: $server:$port";
 
1971   push @hh,"User-Agent: $useragent";
 
1972   push @hh,"Content-Length: $length";
 
1973   push @hh,"Content-Type: multipart/form-data; boundary=$boundary";
 
1974   push @hh,"Connection: $connection" if $connection;
 
1978     print "--> $_\n" foreach (@hh,@hb);
 
1981   $SIG{PIPE} = \&sigpipehandler;
 
1982 #    foreach $sig (keys %SIG) {
 
1983 #      eval '$SIG{$sig} = sub { print "\n!!! SIGNAL '.$sig.' !!!\n"; exit; }';
 
1988     pop @hb unless $flink;
 
1989     nvtsend(@hh,@hb) or do {
 
1990       warn "$0: server has closed the connection, reconnecting...\n";
 
1992       goto FORMDATAPOST; # necessary: new $sid ==> new @hh
 
1995     unless ($opt_d or $flink) {
 
1997       $t0 = $t2 = int(time);
 
2004           open $file,"gpg -e -r $to|" or die "$0: cannot run gpg - $!\n";
 
2006           open $file,'>&=STDIN' or die "$0: cannot open STDIN - $!\n";
 
2010           open $file,"$tar|gpg -e -r $to|" or die "$0: cannot run tar&gpg - $!\n";
 
2012           open $file,"$tar|" or die "$0: cannot run tar - $!\n";
 
2016           if (defined $tpid and $tpid == 0) {
 
2018             if (open $tarlist,$tarlist) {
 
2019               # print "\n$tar|\n"; system "ls -l $tarlist";
 
2021                 while (<$tarlist>) {
 
2022                   print ' 'x(length($file)+40),"\r",$_;
 
2029           $SIG{CHLD} = 'IGNORE';
 
2032           print "Fast forward to byte $seek (resuming)\n";
 
2033           readahead($file,$seek);
 
2038           $fileq =~ s/([^\w\-\@\#%,.=+~_:])/\\$1/g;
 
2039           open $file,"gpg -e -r $to <$fileq|" or die "$0: cannot run gpg - $!\n";
 
2041           open $file,$file or die "$0: cannot read $file - $!\n";
 
2050       print $rcamel[0] if ${'opt_+'};
 
2052       $SIG{ALRM} = sub { retry("timed out") };
 
2053       while (my $b = read $file,$buf,$bs) {
 
2055         syswrite $SH,$buf or &sigpipehandler;
 
2058         if ($filesize > 0 and $bytes+$seek > $filesize) {
 
2059           die "$0: $file filesize has grown while uploading\n";
 
2063         if (${'opt_+'} and int($t2*10)>$tc) {
 
2064           print $rcamel[$tc%2+1];
 
2067         if (not $opt_q and -t STDOUT and int($t2)>$t1) {
 
2068           &sigpipehandler unless $SH->connected;
 
2069           # smaller block size is better on slow links
 
2070           $bs = 4096 if $t1 and $bs>4096 and $bytes/($t2-$t0)<65536;
 
2071           if ($filesize > 0) {
 
2072             $pct = sprintf "(%d%%)",int(($bytes+$seek)/$filesize*100);
 
2074           if ($bytes>2*M and $bs>4096) {
 
2075             printf STDERR "%s: %d MB of %d MB %s %d kB/s        \r",
 
2076                    $opt_s||$opt_a||$file,
 
2077                    int(($bytes+$seek)/M),
 
2080                    int($bt/k/($t2-$tt));
 
2082             printf STDERR "%s: %d kB of %d MB %s %d kB/s        \r",
 
2083                    $opt_s||$opt_a||$file,
 
2084                    int(($bytes+$seek)/k),
 
2087                    int($bt/k/($t2-$tt));
 
2090           # time window for transfer rate calculation
 
2096         last if $filesize > 0 and $bytes >= $fpsize;
 
2097         sleep 1 while ($opt_m and $bytes/k/(time-$t0||1) > $opt_m);
 
2099       close $file; # or die "$0: error while reading $file - $!\n";
 
2102       print $rcamel[2] if ${'opt_+'};
 
2104       # terminate tar verbose output job
 
2112         if (not $chunksize and $bytes+$seek < $filesize) {
 
2113           die "$0: $file filesize has shrunk while uploading\n";
 
2116         if ($seek or $chunksize and $chunksize < $filesize) {
 
2118             printf STDERR "%s: %d MB in %d s (%d kB/s)",
 
2119                            $opt_s||$opt_a||$file,
 
2123             if ($bytes+$seek == $filesize) {
 
2124               printf STDERR ", total %d MB\n",int($filesize/M);
 
2126               printf STDERR ", chunk #%d : %d MB\n",
 
2127                             $chunk,int(($bytes+$seek)/M);
 
2130             printf STDERR "%s: %d kB in %d s (%d kB/s)",
 
2131                           $opt_s||$opt_a||$file,
 
2135             if ($bytes+$seek == $filesize) {
 
2136               printf STDERR ", total %d kB\n",int($filesize/k);
 
2138               printf STDERR ", chunk #%d : %d kB\n",
 
2139                             $chunk,int(($bytes+$seek)/k);
 
2144             printf STDERR "%s: %d MB in %d s (%d kB/s)        \n",
 
2145                           $opt_s||$opt_a||$file,
 
2150             printf STDERR "%s: %d kB in %d s (%d kB/s)        \n",
 
2151                           $opt_s||$opt_a||$file,
 
2158         if (-t STDOUT and not ($opt_s or $opt_g)) {
 
2159           print STDERR "waiting for server ok..."
 
2165     print {$SH} "\r\n--$boundary--\r\n";
 
2167     # special handling of streaming file because of stunnel tcp shutdown bug
 
2168     if ($opt_s or $opt_g) {
 
2171       serverconnect($server,$port);
 
2172       query_sid($server,$port) unless $anonymous;
 
2173       ($seek,$location) = query_file($server,$port,$P{to},$P{from},$sid,
 
2175       if ($seek != $bytes) {
 
2176         die "$0: streamed $bytes bytes but server received $seek bytes\n";
 
2178       return "X-Location: $location\n";
 
2184         printf STDERR "%s: %d MB\n",$flink,int($bytes/M);
 
2186         printf STDERR "%s: %d kB\n",$flink,int($bytes/k);
 
2194   # SuSe: Can't locate object method "BINMODE" via package "IO::Socket::SSL::SSL_HANDLE"
 
2195   # binmode $SH,':utf8'; 
 
2197   if (not $opt_q and $file and -t STDOUT) {
 
2198     print STDERR "\r                         \r";
 
2202     print "<-- $_\n" if $opt_v;
 
2203     last if @r and $r[0] =~ / 204 / and /^$/ or /<\/html>/i;
 
2204     push @r,decode_utf8($_);
 
2210     if ($proxy and $fpsize+$seek < $filesize) {
 
2221     my @rc = ('A'..'Z','a'..'z',0..9 );
 
2225     for (1..$n) { $rs .= $rc[int(rand($rn))] };
 
2231   my $zipbase = shift;
 
2235   my ($zsize,$size,$n);
 
2237   $zipbase =~ s/\.zip$//;
 
2238   map { s/([^_\w\+\-\.])/\\$1/g } @files;
 
2240   open my $ff,"find @files|" or die "$0: cannot search for @_ - $!\n";
 
2247       die "$0: too many zip-archives\n";
 
2250     while ($file = <$ff>) {
 
2252       # next if -l $file or not -f $file;
 
2253       next unless -f $file;
 
2255       if ($size > 2147480000) {
 
2257         die "$0: $file too big for zip\n";
 
2259       if ($zsize + $size > 2147000000) {
 
2260         push @zipfiles,zip($zipbase.'_'.$n.'.zip',@files);
 
2271   push @zipfiles,zip($zipbase.'_'.$n.'.zip',@files);
 
2283   # if ($opt_c) { $cmd = "zip -@ $zip" }
 
2284   # else        { $cmd = "zip -0 -@ $zip" }
 
2285   $cmd = "zip -@ $zip";
 
2287     ${'opt_#'} =~ s/#/ /g;
 
2288     $cmd .= " -x ".${'opt_#'};
 
2290   print $cmd,"\n" if $opt_v;
 
2291   open $cmd,"|$cmd" or die "$0: cannot create $zip - $!\n";
 
2293     print {$cmd} $_."\n";
 
2294     print "  $_\n" if $opt_v;
 
2296   close $cmd or die "$0: zip failed - $!\n";
 
2312     return $_ if length($_);
 
2319   my ($server,$port,$to,$from,$id,$filename,$fileid) = @_;
 
2322   my ($head,$location);
 
2323   my ($response,$fexsrv);
 
2328   $to = $AB{$to} if $AB{$to};
 
2329   $filename =~ s/([^_=:,;<>()+.\w\-])/'%'.uc(unpack("H2",$1))/ge; # urlencode
 
2331     $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??SKEY=$id HTTP/1.1";
 
2333     $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??GKEY=$id HTTP/1.1";
 
2335     $head = "HEAD $proxy_prefix/fop/$to/$from/$filename??ID=$id HTTP/1.1";
 
2337   sendheader("$server:$port",$head);
 
2339   unless (defined $_ and /\w/) {
 
2340     die "$0: no response from server\n";
 
2343   print "<-- $_" if $opt_v;
 
2344   unless (/^HTTP.* 200/) {
 
2349       print "<-- $_" if $opt_v;
 
2350       $fexsrv = $_ if /^(Server: fexsrv|X-Features:)/;
 
2353     die "$0: no fexserver at $server:$port\n" unless $fexsrv;
 
2354     die "$0: server response: $response";
 
2358     print "<-- $_" if $opt_v;
 
2360     if (/^Content-Length:\s+(\d+)/)     { $seek = $1 }
 
2361     if (/^X-File-ID:\s+(.+)/)           { $qfileid = $1 }
 
2362     if (/^X-Features:\s+(.+)/)          { $features = $1 }
 
2363     if (/^X-Location:\s+(.+)/)          { $location = $1 }
 
2366   # return true seek only if file is identified
 
2367   $seek = 0 if $qfileid and $qfileid ne $fileid;
 
2369   return ($seek,$location);
 
2373 sub edit_address_book {
 
2376   my $ab = "$fexhome/ADDRESS_BOOK";
 
2380   die "$0: address book not available for subusers\n"      if $skey;
 
2381   die "$0: address book not available for group members\n" if $gkey;
 
2383   female_mode("edit your address book?") if $opt_F;
 
2385   %AB = query_address_book($server,$port,$user);
 
2386   if ($AB{ADDRESS_BOOK} !~ /\w/) {
 
2388       "# Format: alias e-mail-address # Comment\n".
 
2390       "framstag framstag\@rus.uni-stuttgart.de\n";
 
2392   open $ab,">$ab" or die "$0: cannot write to $ab - $!\n";
 
2393   print {$ab} $AB{ADDRESS_BOOK};
 
2401   serverconnect($server,$port);
 
2402   query_sid($server,$port);
 
2415 sub query_address_book {
 
2416   my ($server,$port,$user) = @_;
 
2417   my ($req,$alias,$address,$options,$comment,$cl,$ab,$b);
 
2422     serverconnect($server,$port);
 
2423     query_sid($server,$port);
 
2426   $req = "GET $proxy_prefix/fop/$user/$user/ADDRESS_BOOK?ID=$sid HTTP/1.1";
 
2427   sendheader("$server:$port",$req);
 
2429   unless (defined $_ and /\w/) {
 
2430     die "$0: no response from server\n";
 
2433   print "<-- $_" if $opt_v;
 
2434   unless (/^HTTP.* 200/) {
 
2435     if (/^HTTP.* 404/) {
 
2436       while (<$SH>) { last if /^\r?\n/ }
 
2439       # s:HTTP/[\d\. ]+::;
 
2440       # die "$0: server response: $_";
 
2448     print "<-- $_" if $opt_v;
 
2450     $cl = $1 if /^Content-Length: (\d+)/;
 
2460       print "<-- $_\n" if $opt_v;
 
2464         ($alias,$address,$options) = split;
 
2466           if ($options) { $options =~ s/[()]//g }
 
2467           else          { $options = '' }
 
2468           $AB{$alias} = $address;
 
2469           $AB{$alias}->{options} = $options||'';
 
2470           $AB{$alias}->{comment} = $comment||'';
 
2471           if ($options and $options =~ /keep=(\d+)/i) {
 
2472             $AB{$alias}->{keep} = $1;
 
2474           if ($options and $options =~ /autodelete=(\w+)/i) {
 
2475             $AB{$alias}->{autodelete} = $1;
 
2483   $AB{ADDRESS_BOOK} = $ab;
 
2489 # sets global $sid $features $timeout # ugly hack! :-}
 
2491   my ($server,$port) = @_;
 
2498     return if $features;    # early return if we know enough
 
2499     $req = "OPTIONS FEX HTTP/1.1";
 
2501     return if $features;    # early return if we know enough
 
2502     $req = "GET $proxy_prefix/SID HTTP/1.1";
 
2504     $req = "GET SID HTTP/1.1";
 
2507   sendheader("$server:$port",$req,"User-Agent: $useragent");
 
2509   unless (defined $_ and /\w/) {
 
2510     print "\n" if $opt_v;
 
2511     die "$0: no response from server\n";
 
2514   print "<-- $_" if $opt_v;
 
2516   if (/^HTTP.* [25]0[01] /) {
 
2517     if (not $proxy and $port ne 443 and /^HTTP.* 201 (.+)/) {
 
2518       $sid = 'MD5H:'.md5_hex($id.$1);
 
2522       print "<-- $_" if $opt_v;
 
2523       $features = $1 if /^X-Features: (.+)/;
 
2524       $timeout = $1  if /^X-Timeout: (\d+)/;
 
2527   } elsif (/^HTTP.* 301 /) {
 
2528     while (<$SH>) { last if /Location/ }
 
2529     die "$0: cannot use $server:$port because server has a redirection to\n".$_;
 
2531     # no SID support - perhaps transparent web proxy?
 
2534       print "<-- $_" if $opt_v;
 
2535       $fexsrv = $_ if /^(Server: fexsrv|X-Features:)/;
 
2538     die "$0: no fexserver at $server:$port\n" unless $fexsrv;
 
2539     serverconnect($server,$port);
 
2543   # warn "proxy: $proxy\n";
 
2545     serverconnect($server,$port);
 
2553   my ($from,$id,$save) = @_;
 
2556   my ($url,$B,$b,$t0,$t1,$cl);
 
2561   $url = "$proxy_prefix/fop/$from/$from/$xx?ID=$id";
 
2563   sendheader("$server:$port","GET $url HTTP/1.0","User-Agent: $useragent");
 
2567     print "<-- $_" if $opt_v;
 
2568     $cl = $1 if /^Content-Length:\s(\d+)/;
 
2569     # $ft = $1 if /^X-File-Type:\s(.+)/;
 
2573   die "$0: no Content-Length in server-reply\n" unless $cl;
 
2575   open F,">$save" or die "$0: cannot write to $save - $!\n";
 
2578   $t0 = $t1 = int(time);
 
2581   while ($b = read($SH,$_,$bs)) {
 
2584     if (int(time) > $t1) {
 
2588         print STDERR $ts,"\r";
 
2592     sleep 1 while ($opt_m and $B/k/(time-$t0||1) > $opt_m);
 
2595   print STDERR ts($B,$cl),"\n";
 
2603   return sprintf("transferred: %d MB (%d%%)",int($b/M),int($b/$tb*100));
 
2607 sub sigpipehandler {
 
2613   local $SIG{ALRM} = sub { };
 
2619     kill 9,$tpid if $tpid;
 
2620     if (@r and $opt_v) {
 
2621       die "\n$0: ($$) server error: @r\n";
 
2623     if (@r and $r[0] =~ /^HTTP.* \d+ (.*)/) {
 
2624       die "\n$0: server error: $1\n";
 
2628   warn "\n$0: connection to $server $reason\n";
 
2629   warn "retrying after $timeout seconds...\n";
 
2631   if ($windoof) { exec $^X,$0,@_ARGV }
 
2632   else          { exec $_0,@_ARGV }
 
2637 sub checkrecipient {
 
2638   my ($from,$to) = @_;
 
2646         command => 'CHECKRECIPIENT',
 
2649   $_ = shift @r or die "$0: no reply from server\n";
 
2654       if (s/X-(Recipient: .+)/$1\n/) {
 
2655         s/autodelete=\w+/autodelete=$opt_D/ if $opt_D;
 
2656         s/keep=\d+/keep=$opt_k/             if $opt_k;
 
2658         $frecipient ||= (split)[1];
 
2662     http_response($_,@r);
 
2667 # get ID data from ID file
 
2671   $fexcgi = getline($idf) || die "$0: no FEX-URL in $idf\n";
 
2672   $from   = getline($idf) || die "$0: no FROM in $idf\n";
 
2673   $id     = getline($idf) || die "$0: no ID in $idf\n";
 
2674   if ($fexcgi =~ s/!([\w.-]+:\d+)(:(\d+))?//) {
 
2676     $chunksize = $3 || 0;
 
2678   unless ($fexcgi =~ /^[_:=\w\-\.\/\@\%]+$/) {
 
2679     die "$0: illegal FEX-URL \"$fexcgi\" in $idf\n";
 
2681   unless ($from =~ /^[_:=\w\-\.\/\@\%\+]+$/) {
 
2682     die "$0: illegal FROM \"$from\" in $idf\n";
 
2692     print "file to send: ";
 
2693     chomp($file = <STDIN>);
 
2697     warn "$file does not exist\n";
 
2699   print "recipient (e-mail address): ";
 
2700   chomp($to = <STDIN>);
 
2701   die $usage unless $to;
 
2704     chomp($opt_C = <STDIN>);
 
2706   @ARGV = ($file,$to);
 
2711   if (system(@_) < 0) { die "failed: @_\n" }
 
2715 # emulate seek on a pipe
 
2717   my $fh = shift; # filehandle
 
2718   my $ba = shift; # bytes ahead
 
2726     $n = $bs if $n > $bs; 
 
2727     $s += read $fh,$_,$n; 
 
2732 # fileid is inode and mtime
 
2734   my @s = stat(shift);
 
2735   return @s ? $s[1].$s[9] : int(time);
 
2739 sub get_mutt_alias {
 
2741   my $ma = $HOME.'/.mutt/aliases';
 
2745   open $ma,$ma or return $to;
 
2747     if (/^alias \Q$to\E\s/i) {
 
2755         warn "$0: ignoring mutt multi-alias $to = $alias\n";
 
2760         warn "$0: found mutt alias $to = $alias\n";
 
2766   return ($alias||$to);
 
2770 # collect file meta data (filename, inode, mtime)
 
2776   foreach $file (@files) {
 
2777     if (not -l $file and -d $file) {
 
2779       if (opendir $dir,$dir) {
 
2780         while (defined ($file = readdir($dir))) {
 
2781           next if $file eq '..';
 
2783             $fmd .= $file.fileid($dir);
 
2785             $fmd .= fmd("$dir/$file");
 
2791       $fmd .= $file.fileid($file);
 
2799 # from MIME::Base64::Perl
 
2805   tr|A-Za-z0-9+=/||cd;
 
2807   tr|A-Za-z0-9+/| -_|;
 
2808   return "" unless length;
 
2811   for ($i = 0; $i <= $l; $i += 60) {
 
2812     $uu .= "M" . substr($_,$i,60);
 
2816     $uu .= chr(32+(length)*3/4) . $_;
 
2818   return unpack("u",$uu);
 
2824   if (open my $tty,'/dev/tty') {
 
2828           "  [p] perhaps - don't know\n",
 
2832     if (/^y/i) { return }
 
2834     if (/^p/i) { int(rand(2)) ? return : exit }
 
2841   local $_ = shift || <$SH>;
 
2845   $_ = <$SH> unless $_;
 
2846   unless (defined $_ and /\w/) {
 
2847     die "$0: no response from server\n";
 
2849   print "<-- $_\n" if $opt_v;
 
2851   # CGI fatalsToBrowser
 
2852   if (/^HTTP.* 500/) {
 
2853     @r = <$SH> unless @r;
 
2855     die "$0: server error: $_\n@r\n";
 
2857   unless (/^HTTP.* 200/) {
 
2859     $error =~ s/HTTP.[\s\d.]+//;
 
2860     @r = <$SH> unless @r;
 
2864       $error .= "\n".$_ if /^Location/;
 
2865       print "<-- $_\n" if $opt_v;
 
2867     die "$0: server error: $error\n";
 
2870   print "<-- $_\n" if $opt_v;
 
2882   my $cfb = '### common functions ###';
 
2887   open $0,$0 or die "cannot read $0 - $!\n";
 
2893   foreach my $p (qw(fexget sexsend)) {
 
2894     open $p,$p or die "cannot read $p - $!\n";
 
2897     s/\n$cfb.*/\n$cfb\n$cfc/s;
 
2899     open $p,'>',$p or die "cannot write $p - $!\n";
 
2904   exec "l $0 fexget sexsend";
 
2908 ### common functions ###
 
2912   my @d = localtime((stat shift)[9]);
 
2913   return sprintf('%d%02d%02d',$d[5]+1900,$d[4]+1,$d[3]);
 
2919   s/\%([a-f\d]{2})/chr(hex($1))/ige;
 
2925   # set SSL/TLS options
 
2926   $SSL{SSL_verify_mode} = $ENV{SSLVERIFY} if defined($ENV{SSLVERIFY});
 
2927   foreach my $opt (qw(
 
2936     $SSL{$opt} = $ENV{$env} if defined($ENV{$env});
 
2939   if ($SSL{SSL_verify_mode}) {
 
2941     unless ($SSL{SSL_ca_path} or $SSL{SSL_ca_file}) {
 
2942       die "$0: \$SSLVERIFYMODE, but not valid \$SSLCAPATH or \$SSLCAFILE\n";
 
2944   } elsif (defined($SSL{SSL_verify_mode})) {
 
2945     # user has set SSLVERIFY=0 !
 
2948     $SSL{SSL_verify_mode} = 1 if $SSL{SSL_ca_path} or $SSL{SSL_ca_file};
 
2954   return if $SSL{SSL_ca_file} or $SSL{SSL_ca_path};
 
2955   foreach (qw(/etc/ssl/certs/ca-certificates.crt)) {
 
2957       $SSL{SSL_ca_file} = $_;
 
2961   foreach (qw(/etc/ssl/certs /etc/pki/tls/certs)) {
 
2963       $SSL{SSL_ca_path} = $_;
 
2971   my ($server,$port) = @_;
 
2972   my $connect = "CONNECT $server:$port HTTP/1.1";
 
2976     tcpconnect(split(':',$proxy));
 
2978       printf "--> %s\n",$connect if $opt_v;
 
2979       nvtsend($connect,"");
 
2982       printf "<-- $_"if $opt_v;
 
2983       unless (/^HTTP.1.. 200/) {
 
2984         die "$0: proxy error : $_";
 
2987       $SH = IO::Socket::SSL->start_SSL($SH,%SSL);
 
2990     tcpconnect($server,$port);
 
2992 #  if ($port == 443 and $opt_v) {
 
2993 #    printf "%s\n",$SH->get_cipher();
 
2998 # set up tcp/ip connection
 
3000   my ($server,$port) = @_;
 
3008     # eval "use IO::Socket::SSL qw(debug3)";
 
3010     $SH = IO::Socket::SSL->new(
 
3011       PeerAddr => $server,
 
3017     $SH = IO::Socket::INET->new(
 
3018       PeerAddr => $server,
 
3027     die "$0: cannot connect $server:$port - $@\n";
 
3030   print "TCPCONNECT to $server:$port\n" if $opt_v;
 
3035   eval "use IO::Socket::SSL";
 
3036   die "$0: cannot load IO::Socket::SSL\n" if $@;
 
3037   eval '$SSL{SSL_verify_mode} = 0 if Net::SSLeay::SSLeay() <= 9470143';
 
3039     foreach my $v (keys %SSL) {
 
3040       printf "%s => %s\n",$v,$SSL{$v};
 
3051   push @head,"Host: $sp";
 
3053   foreach $head (@head) {
 
3054     print "--> $head\n" if $opt_v;
 
3055     print {$SH} $head,"\r\n";
 
3057   print "-->\n" if $opt_v;
 
3063   local $SIG{PIPE} = sub { $sigpipe = "@_" };
 
3067   die "$0: internal error: no active network handle\n" unless $SH;
 
3068   die "$0: remote host has closed the link\n" unless $SH->connected;
 
3070   foreach my $line (@_) {
 
3071     print {$SH} $line,"\r\n";
 
3082 # from MIME::Base64::Perl
 
3089   $res = join '',map(pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
 
3090   $res =~ tr|` -_|AA-Za-z0-9+/|;
 
3091   $padding = (3-length($_[0])%3)%3;
 
3092   $res =~ s/.{$padding}$/'=' x $padding/e if $padding;