You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by jm...@apache.org on 2008/03/15 19:37:24 UTC

svn commit: r637451 - /spamassassin/trunk/sa-update.raw

Author: jm
Date: Sat Mar 15 11:37:22 2008
New Revision: 637451

URL: http://svn.apache.org/viewvc?rev=637451&view=rev
Log:
bug 5752: add sa-update --install switch, to allow installation of already-downloaded rule update tarballs without performing a download

Modified:
    spamassassin/trunk/sa-update.raw

Modified: spamassassin/trunk/sa-update.raw
URL: http://svn.apache.org/viewvc/spamassassin/trunk/sa-update.raw?rev=637451&r1=637450&r2=637451&view=diff
==============================================================================
--- spamassassin/trunk/sa-update.raw (original)
+++ spamassassin/trunk/sa-update.raw Sat Mar 15 11:37:22 2008
@@ -124,6 +124,7 @@
 my %opt = ();
 @{$opt{'gpgkey'}} = ();
 @{$opt{'channel'}} = ();
+@{$opt{'install'}} = ();
 my $GPG_ENABLED = 1;
 
 $opt{'gpghomedir'} = File::Spec->catfile($LOCAL_RULES_DIR, 'sa-update-keys');
@@ -142,6 +143,7 @@
   'gpghomedir=s'			=> \$opt{'gpghomedir'},
   'channel=s'				=> $opt{'channel'},
 
+  'install=s'                           => $opt{'install'},
   'import=s'			        => \$opt{'import'},
   'gpgkeyfile=s'			=> \$opt{'gpgkeyfile'},
   'channelfile=s'			=> \$opt{'channelfile'},
@@ -352,12 +354,16 @@
   }
 }
 
-my $res = Net::DNS::Resolver->new();
+my ($res, $ua);
 
-my $ua = LWP::UserAgent->new();
-$ua->agent("sa-update/$VERSION/$SAVersion");
-$ua->timeout(60);      # a good long timeout; 10 is too short for Coral!
-$ua->env_proxy;
+if (!$opt{'install'}) {
+  $res = Net::DNS::Resolver->new();
+
+  $ua = LWP::UserAgent->new();
+  $ua->agent("sa-update/$VERSION/$SAVersion");
+  $ua->timeout(60);      # a good long timeout; 10 is too short for Coral!
+  $ua->env_proxy;
+}
 
 # Generate a temporary file to put channel content in for later use ...
 my ($content_file, $tfh) = Mail::SpamAssassin::Util::secure_tmpfile();
@@ -391,6 +397,12 @@
   dbg("channel: channel cf file $CFFile");
   dbg("channel: channel pre file $PREFile");
 
+  my $instfile;
+  if ($opt{'install'}) {
+    $instfile = shift @{$opt{'install'}};
+    dbg("channel: installing from file $instfile");
+  }
+
   my($mirby, $mirby_time);
   my $mirby_path = File::Spec->catfile($UPDDir, "MIRRORED.BY");
 
@@ -411,35 +423,50 @@
     close(CF);
   }
 
-  # Setup the channel version DNS query
-  my $DNSQ = "$RevSAVersion.$channel";
-
   my $newV;
-  my $dnsV = join(' ', do_txt_query($DNSQ));
-  if (defined $dnsV && $dnsV =~ /^(\d+)/) {
-    $newV = $1 if (!defined $newV || $1 > $newV);
-    dbg("dns: $DNSQ => $dnsV, parsed as $1");
-  }
+  if (!$instfile) {
+    # Setup the channel version DNS query
+    my $DNSQ = "$RevSAVersion.$channel";
+
+    my $dnsV = join(' ', do_txt_query($DNSQ));
+    if (defined $dnsV && $dnsV =~ /^(\d+)/) {
+      $newV = $1 if (!defined $newV || $1 > $newV);
+      dbg("dns: $DNSQ => $dnsV, parsed as $1");
+    }
 
-  # Not getting a response isn't a failure, there may just not be any updates
-  # for this SA version yet.
-  unless (defined $newV) {
-    dbg("channel: no updates available, skipping channel");
-    next;
-  }
+    # Not getting a response isn't a failure, there may just not be any updates
+    # for this SA version yet.
+    unless (defined $newV) {
+      dbg("channel: no updates available, skipping channel");
+      next;
+    }
 
-  # If this channel hasn't been installed before, or it's out of date,
-  # keep going.  Otherwise, skip it.
-  if ($currentV >= $newV) {
-    dbg("channel: current version is $currentV, new version is $newV, skipping channel");
-    next;
-  }
+    # If this channel hasn't been installed before, or it's out of date,
+    # keep going.  Otherwise, skip it.
+    if ($currentV >= $newV) {
+      dbg("channel: current version is $currentV, new version is $newV, skipping channel");
+      next;
+    }
 
-  # If we are only checking for update availability, exit now
-  if ( defined $opt{'checkonly'} ) {            
-    dbg("channel: $channel: update available, not downloading in checkonly mode");
-    $exit = 0;
-    next;
+    # If we are only checking for update availability, exit now
+    if ( defined $opt{'checkonly'} ) {            
+      dbg("channel: $channel: update available, not downloading in checkonly mode");
+      $exit = 0;
+      next;
+    }
+
+  } else {  # $instfile
+    if ($instfile !~ /(\d{6,})/) {
+      # this is a requirement
+      die "channel: $channel: --install file $instfile does not contain a 6-digit version number!\n";
+    }
+    $newV = $1;
+
+    if ( defined $opt{'checkonly'} ) {            
+      dbg("channel: $channel: --install and --checkonly, claiming update available");
+      $exit = 0;
+      next;
+    }
   }
 
   # we need a directory we control that we can use to aviod loading any rules
@@ -466,119 +493,127 @@
     $site_pre_linted = 1;
   }
 
-  # Read in the MIRRORED.BY file if it exists
-  if (open(MIRBY, $mirby_path)) {
-    local $/ = undef;
-    $mirby = <MIRBY>;
-    close(MIRBY);
+  my $content;
+  my $SHA1;
+  my $GPG;
+  if (!$instfile) {
+    # Read in the MIRRORED.BY file if it exists
+    if (open(MIRBY, $mirby_path)) {
+      local $/ = undef;
+      $mirby = <MIRBY>;
+      close(MIRBY);
 
-    $mirby_time = (stat $mirby_path)[9];
-  }
-  else {
-    # We don't currently have the list of mirrors, so go grab it.
-    dbg("channel: no MIRRORED.BY file available");
-    my @mirrors = do_txt_query("mirrors.$channel");
-    unless (@mirrors) {
-      warn "error: no mirror data available for channel $channel\n";
-      channel_failed("channel: MIRRORED.BY file location was not in DNS");
-      next;
+      $mirby_time = (stat $mirby_path)[9];
     }
-    foreach my $mirror (@mirrors) {
-      $mirby = http_get($mirror);
+    else {
+      # We don't currently have the list of mirrors, so go grab it.
+      dbg("channel: no MIRRORED.BY file available");
+      my @mirrors = do_txt_query("mirrors.$channel");
+      unless (@mirrors) {
+        warn "error: no mirror data available for channel $channel\n";
+        channel_failed("channel: MIRRORED.BY file location was not in DNS");
+        next;
+      }
+      foreach my $mirror (@mirrors) {
+        $mirby = http_get($mirror);
+        unless ($mirby) {
+          dbg("channel: no mirror data available for channel $channel from $mirror");
+          next;
+        }
+        last;
+      }
       unless ($mirby) {
-        dbg("channel: no mirror data available for channel $channel from $mirror");
+        warn "error: no mirror data available for channel $channel\n";
+        channel_failed("channel: MIRRORED.BY contents were missing");
         next;
       }
-      last;
-    }
-    unless ($mirby) {
-      warn "error: no mirror data available for channel $channel\n";
-      channel_failed("channel: MIRRORED.BY contents were missing");
-      next;
+      $mirby_time = MIRBY_DOWNLOADED;
+
+      dbg("channel: MIRRORED.BY file retrieved");
     }
-    $mirby_time = MIRBY_DOWNLOADED;
 
-    dbg("channel: MIRRORED.BY file retrieved");
-  }
+    # Read in the list of mirrors
+    dbg("channel: reading MIRRORED.BY file");
+    my %mirrors = ();
+    my @mirrors = split(/^/, $mirby);
+    while(my $mirror = shift @mirrors) {
+      chomp $mirror;
+
+      $mirror =~ s/#.*$//;   # remove comments
+      $mirror =~ s/^\s+//;   # remove leading whitespace
+      $mirror =~ s/\s+$//;   # remove tailing whitespace
+      next unless ($mirror); # skip empty lines
+
+      # We only support HTTP right now
+      if ($mirror !~ m@^http://@i) {
+        dbg("channel: skipping non-HTTP mirror: $mirror");
+        next;
+      }
 
-  # Read in the list of mirrors
-  dbg("channel: reading MIRRORED.BY file");
-  my %mirrors = ();
-  my @mirrors = split(/^/, $mirby);
-  while(my $mirror = shift @mirrors) {
-    chomp $mirror;
-
-    $mirror =~ s/#.*$//;   # remove comments
-    $mirror =~ s/^\s+//;   # remove leading whitespace
-    $mirror =~ s/\s+$//;   # remove tailing whitespace
-    next unless ($mirror); # skip empty lines
-
-    # We only support HTTP right now
-    if ($mirror !~ m@^http://@i) {
-      dbg("channel: skipping non-HTTP mirror: $mirror");
-      next;
-    }
+      my @data;
 
-    my @data;
+      dbg("channel: found mirror $mirror");
 
-    dbg("channel: found mirror $mirror");
+      ($mirror,@data) = split(/\s+/, $mirror);
+      $mirror =~ s@/+$@@; # http://example.com/updates/ -> .../updates
+      $mirrors{$mirror}->{weight} = 1;
+      foreach (@data) {
+        my($k,$v) = split(/=/, $_, 2);
+        $mirrors{$mirror}->{$k} = $v;
+      }
+    }
 
-    ($mirror,@data) = split(/\s+/, $mirror);
-    $mirror =~ s@/+$@@; # http://example.com/updates/ -> .../updates
-    $mirrors{$mirror}->{weight} = 1;
-    foreach (@data) {
-      my($k,$v) = split(/=/, $_, 2);
-      $mirrors{$mirror}->{$k} = $v;
+    unless (keys %mirrors) {
+      warn "error: no mirrors available for channel $channel\n";
+      channel_failed("channel: no mirrors available");
+      next;
     }
-  }
 
-  unless (keys %mirrors) {
-    warn "error: no mirrors available for channel $channel\n";
-    channel_failed("channel: no mirrors available");
-    next;
-  }
+    # Now that we've laid the foundation, go grab the appropriate files
+    #
 
-  # Now that we've laid the foundation, go grab the appropriate files
-  #
-  my $content;
-  my $SHA1;
-  my $GPG;
+    # Loop through all available mirrors, choose from them randomly
+    # if the archive get fails, choose another mirror,
+    # if the get for the sha1 or gpg signature files, the channel fails
+    while (my $mirror = choose_mirror(\%mirrors)) {
+      # Grab the data hash for this mirror, then remove it from the list
+      my $mirror_info = $mirrors{$mirror};
+      delete $mirrors{$mirror};
+
+      dbg("channel: selected mirror $mirror");
+
+      # Actual archive file
+      $content = http_get("$mirror/$newV.tar.gz");
+      next unless $content;
+
+      # SHA1 of the archive file
+      $SHA1 = http_get("$mirror/$newV.tar.gz.sha1");
+      last unless $SHA1;
+
+      # if GPG is enabled, the GPG detached signature of the archive file
+      if ($GPG_ENABLED) {
+        $GPG = http_get("$mirror/$newV.tar.gz.asc");
+        last unless $GPG;
+      }
 
-  # Loop through all available mirrors, choose from them randomly
-  # if the archive get fails, choose another mirror,
-  # if the get for the sha1 or gpg signature files, the channel fails
-  while (my $mirror = choose_mirror(\%mirrors)) {
-    # Grab the data hash for this mirror, then remove it from the list
-    my $mirror_info = $mirrors{$mirror};
-    delete $mirrors{$mirror};
-
-    dbg("channel: selected mirror $mirror");
-
-    # Actual archive file
-    $content = http_get("$mirror/$newV.tar.gz");
-    next unless $content;
-
-    # SHA1 of the archive file
-    $SHA1 = http_get("$mirror/$newV.tar.gz.sha1");
-    last unless $SHA1;
-
-    # if GPG is enabled, the GPG detached signature of the archive file
-    if ($GPG_ENABLED) {
-      $GPG = http_get("$mirror/$newV.tar.gz.asc");
-      last unless $GPG;
-    }
-
-    # try to update our list of mirrors.
-    # a failure here doesn't cause channel failure.
-    if ($mirby_time != MIRBY_DOWNLOADED) {
-      my $mirby_tmp = http_get("$mirror/MIRRORED.BY", $mirby_time);
-      if ($mirby_tmp) {
-        $mirby = $mirby_tmp;
-        $mirby_time = MIRBY_DOWNLOADED;
+      # try to update our list of mirrors.
+      # a failure here doesn't cause channel failure.
+      if ($mirby_time != MIRBY_DOWNLOADED) {
+        my $mirby_tmp = http_get("$mirror/MIRRORED.BY", $mirby_time);
+        if ($mirby_tmp) {
+          $mirby = $mirby_tmp;
+          $mirby_time = MIRBY_DOWNLOADED;
+        }
       }
+
+      last;
     }
 
-    last;
+  } else {      # $instfile
+    dbg("channel: using --install files $instfile\{,.sha1,.asc\}");
+    $content = read_install_file($instfile);
+    $SHA1 = read_install_file($instfile.".sha1");
+    $GPG = read_install_file($instfile.".asc");
   }
 
   unless ($content && $SHA1 && (!$GPG_ENABLED || $GPG)) {
@@ -801,6 +836,11 @@
 
     {
       'try' => sub {
+        if ($instfile) {
+          dbg("channel: not creating MIRRORED.BY file due to --install");
+          return 1;
+        }
+
         # Write out the mirby file, not fatal if it doesn't work
         dbg("channel: creating MIRRORED.BY file");
         if (open(MBY, ">$mirby_path")) {
@@ -929,6 +969,16 @@
 
 ##############################################################################
 
+sub read_install_file {
+  my ($file) = @_;
+  open (IN, "<$file") or die "cannot open $file\n";
+  my $all = join('', <IN>);
+  close IN;
+  return $all;
+}
+
+##############################################################################
+
 sub write_channel_file {
   my ($filename, $contents) = @_;
 
@@ -1315,6 +1365,8 @@
                           Use multiple times for multiple channels
   --channelfile file      Retrieve updates from the channels in the file
   --checkonly             Check for update availability, do not install
+  --install filename      Install updates directly from this file. Signature
+                          verification will use "file.asc" and "file.sha1"
   --allowplugins          Allow updates to load plugin code
   --gpgkey key            Trust the key id to sign releases
                           Use multiple times for multiple keys
@@ -1373,6 +1425,16 @@
 
 Only check if an update is available, don't actually download and install it.
 The exit code will be C<0> or C<1> as described below.
+
+=item B<--install>
+
+Install updates from the named tar.gz file, instead of performing DNS lookups
+and HTTP invocations.  Files named B<file>.sha1 and B<file>.asc will be used
+for the SHA-1 and GPG signature, respectively.  The filename provided must
+contain a version number of at least 6 digits.
+
+Multiple B<--install> switches can be provided, with each matching one
+B<--channel>.
 
 =item B<--allowplugins>