<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;"># $Id: TLDownload.pm 71152 2024-05-02 17:03:11Z karl $
# TeXLive::TLDownload.pm - module for abstracting the download modes
# Copyright 2009-2024 Norbert Preining
# This file is licensed under the GNU General Public License version 2
# or any later version.

use strict; use warnings;

package TeXLive::TLDownload;

use TeXLive::TLUtils;
use TeXLive::TLConfig;

my $svnrev = '$Revision: 71152 $';
my $_modulerevision;
if ($svnrev =~ m/: ([0-9]+) /) {
  $_modulerevision = $1;
} else {
  $_modulerevision = "unknown";
}
sub module_revision {
  return $_modulerevision;
}

# since Net::HTTP and Net::FTP are shipped by the same packages
# we only test for Net::HTTP, if that fails, let us know ;-)
our $net_lib_avail = 0;
eval { require LWP; };
if ($@) {
  debug("LWP is not available, falling back to wget.\n");
  $net_lib_avail = 0;
} else {
  require LWP::UserAgent;
  require HTTP::Status;
  $net_lib_avail = 1;
  ddebug("LWP available, doing persistent downloads.\n");
}


sub new
{
  my $class = shift;
  my %params = @_;
  my $self = {};
  $self-&gt;{'initcount'} = 0;
  bless $self, $class;
  $self-&gt;reinit(defined($params{'certificates'}) ? $params{'certificates'} : "");
  return $self;
}




sub reinit {
  my $self = shift;
  my $certs = shift;
  
  # Irritatingly, as of around version 6.52, when env_proxy is set, LWP
  # started unconditionally complaining if the environment contains
  # differing case-insensitive like foo=1 and FOO=2. Even on systems
  # that have case-sensitive environments, and even about variables that
  # have nothing whatsoever to do with LWP (like foo).
  # https://github.com/libwww-perl/libwww-perl/issues/372
  # 
  # So, only pass env_proxy=&gt;1 when creating the UserAgent if there are
  # in fact *_proxy variables (case-insensitive, just in case) set in
  # the environment.
  # 
  my @env_proxy = ();
  if (grep { /_proxy/i } keys %ENV ) {
    @env_proxy = ("env_proxy", 1);
  }
  #
  # Set HTTPS_CA_FILE to the TL provided certificate bundle
  # for systems that don't have a system-wide certificate bundle
  # in particular MacOS.
  if ((! exists $ENV{'HTTPS_CA_FILE'}) &amp;&amp; $certs) {
    debug("Setting env var HTTPS_CA_FILE to " . $certs ."\n");
    $ENV{'HTTPS_CA_FILE'} = $certs
  }
  #
  my $ua = LWP::UserAgent-&gt;new(
    agent =&gt; "texlive/lwp",
    # use LWP::ConnCache, and keep 1 connection open
    keep_alive =&gt; 1,
    timeout =&gt; $TeXLive::TLConfig::NetworkTimeout,
    @env_proxy,
  );
  $self-&gt;{'ua'} = $ua;
  $self-&gt;{'enabled'} = 1;
  $self-&gt;{'errorcount'} = 0;
  $self-&gt;{'initcount'} += 1;
}

sub enabled {
  my $self = shift;
  return $self-&gt;{'enabled'};
}
sub disabled
{
  my $self = shift;
  return (!$self-&gt;{'enabled'});
}
sub enable
{
  my $self = shift;
  $self-&gt;{'enabled'} = 1;
  # also reset the error conter
  $self-&gt;reset_errorcount;
}
sub disable
{
  my $self = shift;
  $self-&gt;{'enabled'} = 0;
}
sub initcount
{
  my $self = shift;
  return $self-&gt;{'initcount'};
}
sub errorcount
{
  my $self = shift;
  if (@_) { $self-&gt;{'errorcount'} = shift }
  return $self-&gt;{'errorcount'};
}
sub incr_errorcount
{
  my $self = shift;
  return(++$self-&gt;{'errorcount'});
}
sub decr_errorcount
{
  my $self = shift;
  if ($self-&gt;errorcount &gt; 0) {
    return(--$self-&gt;{'errorcount'});
  } else {
    return($self-&gt;errorcount(0));
  }
}

sub reset_errorcount {
  my $self = shift;
  $self-&gt;{'errorcount'} = 0;
}

sub get_file {
  my ($self,$url,$out,$size) = @_;
  #
  # automatically disable if error count is getting too big
  if ($self-&gt;errorcount &gt; $TeXLive::TLConfig::MaxLWPErrors) {
    $self-&gt;disable;
  }
  # return if disabled
  return if $self-&gt;disabled;
  #
  my $realout = $out;
  my ($outfh, $outfn);
  if ($out eq "|") {
    ($outfh, $outfn) = tl_tmpfile();
    $realout = $outfn;
  }
  my $response = $self-&gt;{'ua'}-&gt;get($url, ':content_file' =&gt; $realout);
  if ($response-&gt;is_success) {
    $self-&gt;decr_errorcount;
    if ($out ne "|") {
      return 1;
    } else {
      # seek to beginning of file
      seek $outfh, 0, 0;
      return $outfh;
    }
  } else {
    debug("TLDownload::get_file: response error: "
            . $response-&gt;status_line . " (for $url)\n");
    $self-&gt;incr_errorcount;
    return;
  }
}



1;
__END__


=head1 NAME

C&lt;TeXLive::TLDownload&gt; -- TeX Live persistent downloads via LWP

=head1 SYNOPSIS

  use TeXLive::TLDownload;

  $TeXLive::TLDownload::net_lib_avail
  my $dl = TeXLive::TLDownload-&gt;new();
  $dl-&gt;get_file($relpath, $output [, $expected_size ]);
  if ($dl-&gt;enabled) ...
  if ($dl-&gt;disabled) ...
  $dl-&gt;enable;
  $dl-&gt;disable;
  $dl-&gt;errorcount([n]);
  $dl-&gt;incr_errorcount;
  $dl-&gt;decr_errorcount;
  $dl-&gt;reset_errorcount;

=head1 DESCRIPTION

The C&lt;TeXLive::TLDownload&gt; is a wrapper around the LWP modules that
allows for persistent connections and different protocols.  At load
time it checks for the existence of the LWP module(s), and sets
C&lt;$TeXLive::TLDownload::net_lib_avail&gt; accordingly.

=head2 Using proxies

Please see C&lt;LWP::UserAgent&gt; for details, in a nut shell one can
specify proxies by setting C&lt;I&lt;protocol&gt;_proxy&gt; variables.

=head2 Automatic disabling

The TLDownload module implements some automatic disabling feature. 
Every time a download did not succeed an internal counter (errorcount)
is increased, everytime it did succeed it is decreased (to a minimum of 0).
If the number of error goes above the maximal error count, the download
object will be disabled and get_file always returns undef.

In this cases the download can be reset with the reset_errorcount and
enable function.

=head1 SEE ALSO

LWP

=head1 AUTHORS AND COPYRIGHT

This script and its documentation were written for the TeX Live
distribution (L&lt;https://tug.org/texlive&gt;) and both are licensed under the
GNU General Public License Version 2 or later.

=cut

### Local Variables:
### perl-indent-level: 2
### tab-width: 2
### indent-tabs-mode: nil
### End:
# vim:set tabstop=2 expandtab: #
</pre></body></html>