]> git.decadent.org.uk Git - dak.git/blobdiff - tools/debianqueued-0.9/debianqueued
debianqueued: remove notifications for incomplete uploads
[dak.git] / tools / debianqueued-0.9 / debianqueued
index f9974366950bf075937946fed73117c0c15ad55b..4fe899a03432b578e081802460a96c732e2b26d1 100755 (executable)
@@ -22,6 +22,10 @@ use Net::FTP;
 use Socket qw( PF_INET AF_INET SOCK_STREAM );
 use Config;
 use Sys::Hostname;
+use File::Copy;
+use Digest::MD5;
+
+setlocale(&POSIX::LC_ALL, "C");
 
 # ---------------------------------------------------------------------------
 #                                                              configuration
@@ -56,6 +60,7 @@ $junk         = @conf::test_binaries;
 $junk         = @conf::maintainer_mail;
 $junk         = @conf::targetdir_delayed;
 $junk         = $conf::mail ||= '/usr/sbin/sendmail';
+$junk         = $conf::overridemail;
 $conf::target = "localhost" if $conf::upload_method eq "copy";
 
 package main;
@@ -65,6 +70,8 @@ package main;
 ($main::hostname, undef, undef, undef, undef) = gethostbyname(hostname());
 
 my %packages = ();
+my $re_file_safe_prefix = qr/\A([a-zA-Z0-9][a-zA-Z0-9_.:~+-]*)/s;
+my $re_file_safe = qr/$re_file_safe_prefix\z/s;
 
 # extract -r and -k args
 $main::arg = "";
@@ -233,7 +240,6 @@ sub check_incoming_writable();
 sub rm(@);
 sub md5sum($);
 sub is_debian_file($);
-sub get_maintainer($);
 sub debian_file_stem($);
 sub msg($@);
 sub debug(@);
@@ -383,7 +389,7 @@ while (1) {
 
   # ping target only if there is the possibility that we'll contact it (but
   # also don't wait too long).
-  my @have_changes = <*.changes *.commands>;
+  my @have_changes = <*.changes *.commands *.dak-commands>;
   for ( my $delayed_dirs = 0 ;
         $delayed_dirs <= $conf::max_delayed ;
         $delayed_dirs++ )
@@ -483,7 +489,7 @@ sub check_dir() {
            return
          );
 
-    # look for *.commands files but not in delayed queues
+    # look for *.commands and *.dak-commands files but not in delayed queues
     if ( $adelay == -1 ) {
       foreach $file (<*.commands>) {
         init_mail($file);
@@ -494,6 +500,15 @@ sub check_dir() {
         write_status_file() if $conf::statusdelay;
         finish_mail();
       } ## end foreach $file (<*.commands>)
+         foreach $file (<*.dak-commands>) {
+               init_mail($file);
+               block_signals();
+               process_dak_commands($file);
+               unblock_signals();
+               $main::dstat = "c";
+               write_status_file() if $conf::statusdelay;
+               finish_mail();
+         }
     } ## end if ( $adelay == -1 )
     opendir( INC, "." )
       or (
@@ -554,86 +569,6 @@ sub check_dir() {
         msg( "log",
              "Deleted stray file ${main::current_incoming_short}/$file\n" )
           if rm($file);
-      } elsif (
-        $age > $conf::no_changes_timeout
-        && is_debian_file($file)
-        &&
-
-        # not already reported
-          !( $stats[ST_MODE] & S_ISGID )
-        && ( $pattern   = debian_file_stem($file) )
-        && ( @job_files = glob($pattern) )
-        &&
-
-        # If a .changes is in the list, it has the same stem as the
-        # found file (probably a .orig.tar.gz). Don't report in this
-        # case.
-        !( grep( /\.changes$/, @job_files ) )
-              )
-      {
-        $maint = get_maintainer($file);
-
-        # Don't send a mail if this looks like the recompilation of a
-        # package for a non-i386 arch. For those, the maintainer field is
-        # useless :-(
-        if ( !grep( /(\.dsc|_(i386|all)\.deb)$/, @job_files ) ) {
-          msg( "log", "Found an upload without .changes and with no ",
-               ".dsc file\n" );
-          msg( "log",
-               "Not sending a report, because probably ",
-               "recompilation job\n" );
-        } elsif ($maint) {
-          init_mail();
-          $main::mail_addr = $maint;
-          $main::mail_addr = $1 if $main::mail_addr =~ /<([^>]*)>/;
-          $main::mail_subject =
-            "Incomplete upload found in " . "Debian upload queue";
-          msg(
-               "mail",
-               "Probably you are the uploader of the following "
-                 . "file(s) in\n"
-             );
-          msg( "mail", "the Debian upload queue directory:\n  " );
-          msg( "mail", join( "\n  ", @job_files ), "\n" );
-          msg(
-               "mail",
-               "This looks like an upload, but a .changes file "
-                 . "is missing, so the job\n"
-             );
-          msg( "mail", "cannot be processed.\n\n" );
-          msg(
-               "mail",
-               "If no .changes file arrives within ",
-               print_time( $conf::stray_remove_timeout - $age ),
-               ", the files will be deleted.\n\n"
-             );
-          msg(
-               "mail",
-               "If you didn't upload those files, please just "
-                 . "ignore this message.\n"
-             );
-          finish_mail();
-          msg(
-               "log",
-               "Sending problem report for an upload without a "
-                 . ".changes\n"
-             );
-          msg( "log", "Maintainer: $maint\n" );
-        } else {
-          msg(
-               "log",
-               "Found an upload without .changes, but can't "
-                 . "find a maintainer address\n"
-             );
-        } ## end else [ if ( !grep( /(\.dsc|_(i386|all)\.deb)$/...
-        msg( "log", "Files: @job_files\n" );
-
-        # remember we already have sent a mail regarding this file
-        foreach (@job_files) {
-          my @st = stat($_);
-          next if !@st;    # file may have disappeared in the meantime
-          chmod +( $st[ST_MODE] |= S_ISGID ), $_;
-        }
       } else {
         debug(
 "found stray file ${main::current_incoming_short}/$file, deleting in ",
@@ -667,7 +602,7 @@ outer_loop: while (<CHANGES>) {
 
         # forbid shell meta chars in the name, we pass it to a
         # subshell several times...
-        $field[5] =~ /^([a-zA-Z0-9.+_:@=%-][~a-zA-Z0-9.+_:@=%-]*)/;
+        $field[5] =~ /$re_file_safe/;
         if ( $1 ne $field[5] ) {
           msg( "log", "found suspicious filename $field[5]\n" );
           next;
@@ -690,7 +625,7 @@ sub process_changes($\@) {
        $pgplines,     @files,     @filenames,  @changes_stats,
        $failure_file, $retries,   $last_retry, $upload_time,
        $file,         $do_report, $ls_l,       $problems_reported,
-       $errs,         $pkgname,   $signator
+       $errs,         $pkgname,   $signator,   $extralines
      );
   local (*CHANGES);
   local (*FAILS);
@@ -707,12 +642,19 @@ sub process_changes($\@) {
   open( CHANGES, "<$changes" )
     or die "Cannot open ${main::current_incoming_short}/$changes: $!\n";
   $pgplines        = 0;
+  $extralines      = 0;
   $main::mail_addr = "";
   @files           = ();
 outer_loop: while (<CHANGES>) {
     if (/^---+(BEGIN|END) PGP .*---+$/) {
       ++$pgplines;
-    } elsif (/^Maintainer:\s*/i) {
+      next;
+    }
+    if ( $pgplines < 1 or $pgplines >= 3 ) {
+      $extralines++ if length $_ > 1;
+      next;
+    }
+    if (/^Maintainer:\s*/i) {
       chomp( $main::mail_addr = $' );
       $main::mail_addr = $1 if $main::mail_addr =~ /<([^>]*)>/;
     } elsif (/^Source:\s*/i) {
@@ -727,7 +669,7 @@ outer_loop: while (<CHANGES>) {
 
         # forbid shell meta chars in the name, we pass it to a
         # subshell several times...
-        $field[5] =~ /^([a-zA-Z0-9.+_:@=%-][~a-zA-Z0-9.+_:@=%-]*)/;
+        $field[5] =~ /$re_file_safe/;
         if ( $1 ne $field[5] ) {
           msg( "log", "found suspicious filename $field[5]\n" );
           msg(
@@ -758,6 +700,12 @@ outer_loop: while (<CHANGES>) {
   @$keep_list = @filenames;
 
   # some consistency checks
+  if ( $extralines ) {
+    msg( "log,mail",
+"$main::current_incoming_short/$changes contained lines outside the pgp signed "
+."part, cannot process\n" );
+    goto remove_only_changes;
+  } ## end if ( $extralines )
   if ( !$main::mail_addr ) {
     msg( "log,mail",
 "$main::current_incoming_short/$changes doesn't contain a Maintainer: field; "
@@ -1100,6 +1048,55 @@ outer_loop: while (<CHANGES>) {
   #}
 } ## end sub process_changes($\@)
 
+#
+# process one .dak-commands file
+#
+sub process_dak_commands {
+  my $commands = shift;
+
+  msg("log", "processing ${main::current_incoming_short}/$commands\n");
+
+  # TODO: get mail address from signed contents
+  # and NOT implement a third parser for armored PGP...
+  $main::mail_addr = undef;
+
+  # check signature
+  my $signator = pgp_check($commands);
+  if (!$signator) {
+       msg("log,mail",
+           "$main::current_incoming_short/$commands has bad PGP/GnuPG signature!\n");
+       msg("log,mail",
+               "Removing $main::current_incoming_short/$commands\n");
+       rm($commands);
+       return;
+  }
+  elsif ($signator eq 'LOCAL ERROR') {
+       debug("Can't check signature for $main::current_incoming_short/$commands -- don't process it for now");
+       return;
+  }
+  msg("log,mail", "(PGP/GnuPG signature by $signator)\n");
+
+  # check target
+  my @filenames = ($commands);
+  if (my $ls_l = is_on_target($commands, @filenames)) {
+       msg("log,mail", "$main::current_incoming_short/$commands is already present on target host:\n");
+       msg("log,mail", "$ls_l\n");
+       msg("log,mail", "Job $commands removed.\n");
+       rm($commands);
+       return;
+  }
+
+  if (!copy_to_target($commands)) {
+       msg("log,mail", "$commands couldn't be uploaded to target.\n");
+       msg("log,mail", "Giving up and removing it.\n");
+       rm($commands);
+       return;
+  }
+
+  rm($commands);
+  msg("mail", "$commands uploaded successfully to $conf::target\n");
+}
+
 #
 # process one .commands file
 #
@@ -1209,7 +1206,9 @@ outer_loop: while (<COMMANDS>) {
           $selecteddelayed = $1;
           s,^DELAYED/[0-9]+-day/,,;
         }
-        if ( $origword eq "--searchdirs" ) {
+        if (m,(^|/)\*,) {
+          msg("mail,log", "$_: filename component cannot start with a wildcard\n");
+        } elsif ( $origword eq "--searchdirs" ) {
           $selecteddelayed = -2;
         } elsif (m,/,) {
           msg(
@@ -1316,8 +1315,8 @@ outer_loop: while (<COMMANDS>) {
             if ( $afile =~ m/\.changes$/ ) {
               utime undef, undef, ("$dir/$afile");
             }
-            if ( !rename "$dir/$afile", "$target_dir/$afile" ) {
-              msg( "mail,log", "rename: $!\n" );
+            if ( !move("$dir/$afile", "$target_dir/$afile") ) {
+              msg( "mail,log", "move: $!\n" );
             } else {
               msg( "mail,log", "$afile moved to $target_delay-day\n" );
             }
@@ -1330,7 +1329,7 @@ outer_loop: while (<COMMANDS>) {
       } elsif ( $conf::upload_method ne "copy" ) {
         msg( "mail,log", "cancel not available\n" );
       } elsif (
-          $word[1] !~ m,^[a-zA-Z0-9.+_:@=%-][~a-zA-Z0-9.+_:@=%-]*\.changes$, )
+          $word[1] !~ m,$re_file_safe_prefix.changes\z, )
       {
         msg( "mail,log",
           "argument to cancel must be one .changes filename without path\n" );
@@ -1387,8 +1386,8 @@ sub age_delayed_queues() {
         my @thesefiles = ( $achanges =~ m,.*/([^/]*), );
         push( @thesefiles, get_filelist_from_known_good_changes($achanges) );
         for my $afile (@thesefiles) {
-          if ( !rename "$dir/$afile", "$target_dir/$afile" ) {
-            msg( "log", "rename: $!\n" );
+          if ( !move("$dir/$afile", "$target_dir/$afile") ) {
+            msg( "log", "move: $!\n" );
           } else {
             msg( "log", "$afile moved to $target_dir\n" );
           }
@@ -1631,6 +1630,13 @@ sub pgp_check($) {
   my $stat;
   local (*PIPE);
 
+  if ($file =~ /$re_file_safe/) {
+    $file = $1;
+  } else {
+    msg( "log", "Tainted filename, skipping: $file\n" );
+    return "LOCAL ERROR";
+  }
+
   $stat = 1;
   if ( -x $conf::gpg ) {
     debug(   "executing $conf::gpg --no-options --batch "
@@ -2126,14 +2132,12 @@ sub rm(@) {
 #
 sub md5sum($) {
   my $file = shift;
-  my $line;
-
-  chomp( $line = `$conf::md5sum $file` );
-  debug( "md5sum($file): ",
-           $? ? "exit status $?"
-         : $line =~ /^(\S+)/ ? $1
-         :                     "match failed" );
-  return $? ? "" : $line =~ /^(\S+)/ ? $1 : "";
+
+  open my $fh, "<", $file or return "";
+  my $md5 = $md5->addfile($fh);
+  close $fh;
+
+  return $md5->hexdigest;
 } ## end sub md5sum($)
 
 #
@@ -2145,67 +2149,6 @@ sub is_debian_file($) {
     && $file !~ /\.orig\.tar\.gz/;
 }
 
-#
-# try to extract maintainer email address from some a non-.changes file
-# return "" if not possible
-#
-sub get_maintainer($) {
-  my $file       = shift;
-  my $maintainer = "";
-  local (*F);
-
-  if ( $file =~ /\.diff\.gz$/ ) {
-
-    # parse a diff
-    open( F, "$conf::gzip -dc '$file' 2>/dev/null |" ) or return "";
-    while (<F>) {
-
-      # look for header line of a file */debian/control
-      last if m,^\+\+\+\s+[^/]+/debian/control(\s+|$),;
-    }
-    while (<F>) {
-      last if /^---/;   # end of control file patch, no Maintainer: found
-                        # inside control file patch look for Maintainer: field
-      $maintainer = $1, last if /^\+Maintainer:\s*(.*)$/i;
-    }
-    while (<F>) { }     # read to end of file to avoid broken pipe
-    close(F) or return "";
-  } elsif ( $file =~ /\.(deb|dsc|tar\.gz)$/ ) {
-    if ( $file =~ /\.deb$/ && $conf::ar ) {
-
-      # extract control.tar.gz from .deb with ar, then let tar extract
-      # the control file itself
-      open( F,
-                "($conf::ar p '$file' control.tar.gz | "
-              . "$conf::tar -xOf - "
-              . "--use-compress-program $conf::gzip "
-              . "control) 2>/dev/null |"
-          ) or return "";
-    } elsif ( $file =~ /\.dsc$/ ) {
-
-      # just do a plain grep
-      debug("get_maint: .dsc, no cmd");
-      open( F, "<$file" ) or return "";
-    } elsif ( $file =~ /\.tar\.gz$/ ) {
-
-      # let tar extract a file */debian/control
-      open( F,
-                "$conf::tar -xOf '$file' "
-              . "--use-compress-program $conf::gzip "
-              . "\\*/debian/control 2>&1 |"
-          ) or return "";
-    } else {
-      return "";
-    }
-    while (<F>) {
-      $maintainer = $1, last if /^Maintainer:\s*(.*)$/i;
-    }
-    close(F) or return "";
-  } ## end elsif ( $file =~ /\.(deb|dsc|tar\.gz)$/)
-
-  return $maintainer;
-} ## end sub get_maintainer($)
-
 #
 # return a pattern that matches all files that probably belong to one job
 #
@@ -2325,14 +2268,19 @@ sub send_mail($$$) {
     $Email::Send::Sendmail::SENDMAIL = $conf::mail;
   }
 
+  if ($conf::overridemail) {
+       $addr = $conf::overridemail;
+  }
+
   my $date = sprintf "%s",
     strftime( "%a, %d %b %Y %T %z", ( localtime(time) ) );
   my $message = <<__MESSAGE__;
 To: $addr
-From: Archive Administrator <dak\@ftp-master.debian.org>
+From: Debian FTP Masters <ftpmaster\@ftp-master.debian.org>
 Subject: $subject
 Date: $date
 X-Debian: DAK
+X-DAK: DAK
 __MESSAGE__
 
   if ( length $package ) {