]> git.donarmstrong.com Git - debbugs.git/blobdiff - Debbugs/Status.pm
switch pod from =item to =head2
[debbugs.git] / Debbugs / Status.pm
index 55b29d5f3acdf986353a318ad0875b5c2f798544..be296fd428ba86b5317e0e16dcc93b4f6c9ac621 100644 (file)
@@ -32,6 +32,7 @@ status of a particular bug
 
 use warnings;
 use strict;
+
 use vars qw($VERSION $DEBUG %EXPORT_TAGS @EXPORT_OK @EXPORT);
 use base qw(Exporter);
 
@@ -55,15 +56,18 @@ BEGIN{
      %EXPORT_TAGS = (status => [qw(splitpackages get_bug_status buggy bug_archiveable),
                                qw(isstrongseverity bug_presence),
                               ],
-                    read   => [qw(readbug read_bug lockreadbug lockreadbugmerge)],
+                    read   => [qw(readbug read_bug lockreadbug lockreadbugmerge),
+                               qw(lock_read_all_merged_bugs),
+                              ],
                     write  => [qw(writebug makestatus unlockwritebug)],
                     versions => [qw(addfoundversions addfixedversions),
                                  qw(removefoundversions removefixedversions)
                                 ],
                     hook     => [qw(bughook bughook_archive)],
+                    fields   => [qw(%fields)],
                    );
      @EXPORT_OK = ();
-     Exporter::export_ok_tags(qw(status read write versions hook));
+     Exporter::export_ok_tags(qw(status read write versions hook fields));
      $EXPORT_TAGS{all} = [@EXPORT_OK];
 }
 
@@ -78,8 +82,9 @@ location. Valid locations are those understood by L</getbugcomponent>
 
 =cut
 
-
-my %fields = (originator     => 'submitter',
+# these probably shouldn't be imported by most people, but
+# Debbugs::Control needs them, so they're now exportable
+our %fields = (originator     => 'submitter',
               date           => 'date',
               subject        => 'subject',
               msgid          => 'message-id',
@@ -97,6 +102,8 @@ my %fields = (originator     => 'submitter',
               blocks         => 'blocks',
               blockedby      => 'blocked-by',
              unarchived     => 'unarchived',
+             summary        => 'summary',
+             affects        => 'affects',
              );
 
 # Fields which need to be RFC1522-decoded in format versions earlier than 3.
@@ -129,6 +136,10 @@ path to the summary file instead of the bug number and/or location.
 
 =item summary -- complete path to the .summary file which will be read
 
+=item lock -- whether to obtain a lock for the bug to prevent
+something modifying it while the bug has been read. You B<must> call
+C<unfilelock();> if something not undef is returned from read_bug.
+
 =back
 
 One of C<bug> or C<summary> must be passed. This function will return
@@ -154,6 +165,9 @@ sub read_bug{
                                         summary  => {type => SCALAR,
                                                      optional => 1,
                                                     },
+                                        lock     => {type => BOOLEAN,
+                                                     optional => 1,
+                                                    },
                                        },
                             );
     die "One of bug or summary must be passed to read_bug"
@@ -171,6 +185,7 @@ sub read_bug{
         $status = getbugcomponent($lref, 'summary', $location);
         $log    = getbugcomponent($lref, 'log'    , $location);
         return undef unless defined $status;
+        return undef if not -e $status;
     }
     else {
         $status = $param{summary};
@@ -178,8 +193,17 @@ sub read_bug{
         $log =~ s/\.summary$/.log/;
         ($location) = $status =~ m/(db-h|db|archive)/;
     }
-    my $status_fh = IO::File->new($status, 'r') or
-        warn "Unable to open $status for reading: $!" and return undef;
+    if ($param{lock}) {
+       filelock("$config{spool_dir}/lock/$param{bug}");
+    }
+    my $status_fh = IO::File->new($status, 'r');
+    if (not defined $status_fh) {
+       warn "Unable to open $status for reading: $!";
+       if ($param{lock}) {
+           unfilelock();
+       }
+       return undef;
+    }
 
     my %data;
     my @lines;
@@ -195,6 +219,9 @@ sub read_bug{
     # Version 3 is the latest format version currently supported.
     if ($version > 3) {
         warn "Unsupported status version '$version'";
+        if ($param{lock}) {
+            unfilelock();
+        }
         return undef;
     }
 
@@ -229,6 +256,7 @@ sub read_bug{
     # Add log last modified time
     $data{log_modified} = (stat($log))[9];
     $data{location} = $location;
+    $data{archived} = (defined($location) and ($location eq 'archive'))?1:0;
     $data{bug_num} = $param{bug};
 
     return \%data;
@@ -248,10 +276,7 @@ See readbug above for information on what this returns
 
 sub lockreadbug {
     my ($lref, $location) = @_;
-    &filelock("$config{spool_dir}/lock/$lref");
-    my $data = read_bug(bug => $lref, location => $location);
-    &unfilelock unless defined $data;
-    return $data;
+    return read_bug(bug => $lref, location => $location, lock => 1);
 }
 
 =head2 lockreadbugmerge
@@ -283,6 +308,71 @@ sub lockreadbugmerge {
      return (2,$data);
 }
 
+=head2 lock_read_all_merged_bugs
+
+     my ($locks,@bug_data) = lock_read_all_merged_bugs($bug_num,$location);
+
+Performs a filelock, then reads the bug passed. If the bug is merged,
+locks the merge lock, then reads and locks all of the other merged
+bugs. Returns a list of the number of locks and the bug data for all
+of the merged bugs.
+
+Will also return undef if any of the merged bugs failed to be read,
+even if all of the others were read properly.
+
+=cut
+
+sub lock_read_all_merged_bugs {
+    my ($bug_num,$location) = @_;
+    my $locks = 0;
+    my @data = (lockreadbug(@_));
+    if (not @data or not defined $data[0]) {
+       return ($locks,undef);
+    }
+    $locks++;
+    if (not length $data[0]->{mergedwith}) {
+       return ($locks,@data);
+    }
+    unfilelock();
+    $locks--;
+    filelock("$config{spool_dir}/lock/merge");
+    $locks++;
+    @data = (lockreadbug(@_));
+    if (not @data or not defined $data[0]) {
+       unfilelock(); #for merge lock above
+       $locks--;
+       return ($locks,undef);
+    }
+    $locks++;
+    my @bugs = split / /, $data[0]->{mergedwith};
+    for my $bug (@bugs) {
+       my $newdata = undef;
+       if ($bug ne $bug_num) {
+           $newdata = lockreadbug($bug,$location);
+           if (not defined $newdata) {
+               for (1..$locks) {
+                   unfilelock();
+               }
+               $locks = 0;
+               warn "Unable to read bug: $bug while handling merged bug: $bug_num";
+               return ($locks,undef);
+           }
+           $locks++;
+           push @data,$newdata;
+       }
+       # perform a sanity check to make sure that the merged bugs are
+       # all merged with eachother
+       my $expectmerge= join(' ',grep {$_ != $bug } sort { $a <=> $b } (@bugs,$bug_num));
+       if ($newdata->{mergedwith} ne $expectmerge) {
+           for (1..$locks) {
+               unfilelock();
+           }
+           die "Bug $bug_num differs from bug $bug: ($newdata->{bug_num}: $newdata->{mergedwith}) vs. ($expectmerge) (".join(' ',@bugs).")";
+       }
+    }
+    return ($locks,@data);
+}
+
 
 my @v1fieldorder = qw(originator date subject msgid package
                       keywords done forwarded mergedwith severity);
@@ -791,9 +881,8 @@ dist, arch, and version. [The entries in this array must be in the
 "source/version" format.] Eventually this can be used to for caching.
 
 =item indicatesource -- if true, indicate which source packages this
-bug could belong to. Defaults to false. [Note that eventually we will
-properly allow bugs that only affect a source package, and this will
-become always on.]
+bug could belong to (or does belong to in the case of bugs assigned to
+a source package). Defaults to true.
 
 =back
 
@@ -832,7 +921,7 @@ sub get_bug_status {
                                                             optional => 1,
                                                            },
                                          indicatesource => {type => BOOLEAN,
-                                                            default => 0,
+                                                            default => 1,
                                                            },
                                         },
                              );
@@ -863,15 +952,30 @@ sub get_bug_status {
      $status{tags} = $status{keywords};
      my %tags = map { $_ => 1 } split ' ', $status{tags};
 
+     $status{package} = '' if not defined $status{package};
      $status{"package"} =~ s/\s*$//;
-     if ($param{indicatesource} and $status{package} ne '') {
-         $status{source} = join(', ',binarytosource($status{package}));
-     }
-     else {
-         $status{source} = 'unknown';
+     # if we aren't supposed to indicate the source, we'll return
+     # unknown here.
+     $status{source} = 'unknown';
+     if ($param{indicatesource}) {
+        my @packages = split /\s*,\s*/, $status{package};
+        my @source;
+        for my $package (@packages) {
+            next if $package eq '';
+            if ($package =~ /^src\:$/) {
+                push @source,$1;
+            }
+            else {
+                push @source, binarytosource($package);
+            }
+        }
+        if (@source) {
+            $status{source} = join(', ',@source);
+        }
      }
+
      $status{"package"} = 'unknown' if ($status{"package"} eq '');
-     $status{"severity"} = 'normal' if ($status{"severity"} eq '');
+     $status{"severity"} = 'normal' if (not defined $status{severity} or $status{"severity"} eq '');
 
      $status{"pending"} = 'pending';
      $status{"pending"} = 'forwarded'      if (length($status{"forwarded"}));
@@ -879,7 +983,8 @@ sub get_bug_status {
      $status{"pending"} = 'fixed'          if ($tags{fixed});
 
 
-     my $presence = bug_presence(map{(exists $param{$_})?($_,$param{$_}):()}
+     my $presence = bug_presence(status => \%status,
+                                map{(exists $param{$_})?($_,$param{$_}):()}
                                 qw(bug sourceversions arch dist version found fixed package)
                                );
      if (defined $presence) {
@@ -981,12 +1086,38 @@ sub bug_presence {
                    }
               }
          } elsif (defined $param{dist}) {
+              my %affects_distribution_tags;
+              @affects_distribution_tags{@{$config{affects_distribution_tags}}} =
+                   (1) x @{$config{affects_distribution_tags}};
+              my $some_distributions_disallowed = 0;
+              my %allowed_distributions;
+              for my $tag (split ' ', ($status{tags}||'')) {
+                  if (exists $config{distribution_aliases}{$tag} and
+                       exists $affects_distribution_tags{$config{distribution_aliases}{$tag}}) {
+                      $some_distributions_disallowed = 1;
+                      $allowed_distributions{$config{distribution_aliases}{$tag}} = 1;
+                  }
+                  elsif (exists $affects_distribution_tags{$tag}) {
+                      $some_distributions_disallowed = 1;
+                      $allowed_distributions{$tag} = 1;
+                  }
+              }
               foreach my $arch (make_list($param{arch})) {
-                   my @versions;
                    for my $package (split /\s*,\s*/, $status{package}) {
+                        my @versions;
                         foreach my $dist (make_list($param{dist})) {
+                             # if some distributions are disallowed,
+                             # and this isn't an allowed
+                             # distribution, then we ignore this
+                             # distribution for the purposees of
+                             # finding versions
+                             if ($some_distributions_disallowed and
+                                 not exists $allowed_distributions{$dist}) {
+                                  next;
+                             }
                              push @versions, getversions($package, $dist, $arch);
                         }
+                        next unless @versions;
                         my @temp = makesourceversions($package,
                                                       $arch,
                                                       @versions
@@ -1007,12 +1138,12 @@ sub bug_presence {
      my $maxbuggy = 'undef';
      if (@sourceversions) {
          $maxbuggy = max_buggy(bug => $param{bug},
-                                  sourceversions => \@sourceversions,
-                                  found => $status{found_versions},
-                                  fixed => $status{fixed_versions},
-                                  package => $status{package},
-                                  version_cache => $version_cache,
-                                 );
+                               sourceversions => \@sourceversions,
+                               found => $status{found_versions},
+                               fixed => $status{fixed_versions},
+                               package => $status{package},
+                               version_cache => $version_cache,
+                              );
      }
      elsif (defined $param{dist} and
            not exists $pseudo_desc->{$status{package}}) {