]> git.donarmstrong.com Git - debbugs.git/blobdiff - cgi/bugreport.cgi
[project @ 2003-08-30 00:15:15 by cjwatson]
[debbugs.git] / cgi / bugreport.cgi
index 4e909fe394e6087304de5b93ce3ead663bf05f4d..c5339f19b39218d1bd68a7c21fd18218aad10be6 100755 (executable)
@@ -7,6 +7,7 @@ use POSIX qw(strftime tzset);
 use MIME::Parser;
 use MIME::Decoder;
 use IO::Scalar;
+use IO::Lines;
 
 #require '/usr/lib/debbugs/errorlib';
 require './common.pl';
@@ -14,23 +15,117 @@ require './common.pl';
 require '/etc/debbugs/config';
 require '/etc/debbugs/text';
 
-use vars(qw($gHTMLTail $gSpoolDir $gWebDomain));
+use vars(qw($gEmailDomain $gHTMLTail $gSpoolDir $gWebDomain));
 
 my %param = readparse();
 
 my $tail_html;
 
-my %maintainer = %{getmaintainers()};
-my %pkgsrc = %{getpkgsrc()};
-
-my $ref = $param{'bug'} || quit("No bug number");
+my $ref = $param{'bug'} || quitcgi("No bug number");
+$ref =~ /(\d+)/ or quitcgi("Invalid bug number");
+$ref = $1;
+my $short = "#$ref";
 my $msg = $param{'msg'} || "";
 my $att = $param{'att'};
 my $boring = ($param{'boring'} || 'no') eq 'yes'; 
+my $terse = ($param{'terse'} || 'no') eq 'yes';
 my $reverse = ($param{'reverse'} || 'no') eq 'yes';
 my $mbox = ($param{'mbox'} || 'no') eq 'yes'; 
+my $mime = ($param{'mime'} || 'yes') eq 'yes';
 
-my %status = %{getbugstatus($ref)} or &quit("Couldn't get bug status: $!");
+# Not used by this script directly, but fetch these so that pkgurl() and
+# friends can propagate them correctly.
+my $archive = ($param{'archive'} || 'no') eq 'yes';
+my $repeatmerged = ($param{'repeatmerged'} || 'yes') eq 'yes';
+set_option('archive', $archive);
+set_option('repeatmerged', $repeatmerged);
+
+my $buglog = buglog($ref);
+
+if ($ENV{REQUEST_METHOD} eq 'HEAD' and not defined($att) and not $mbox) {
+    print "Content-Type: text/html\n";
+    my @stat = stat $buglog;
+    if (@stat) {
+       my $mtime = strftime '%a, %d %b %Y %T GMT', gmtime($stat[9]);
+       print "Last-Modified: $mtime\n";
+    }
+    print "\n";
+    exit 0;
+}
+
+sub display_entity ($$$$\$\@);
+sub display_entity ($$$$\$\@) {
+    my $entity = shift;
+    my $ref = shift;
+    my $top = shift;
+    my $xmessage = shift;
+    my $this = shift;
+    my $attachments = shift;
+
+    my $head = $entity->head;
+    my $disposition = $head->mime_attr('content-disposition');
+    $disposition = 'inline' if not defined $disposition or $disposition eq '';
+    my $type = $entity->effective_type;
+    my $filename = $entity->head->recommended_filename;
+    $filename = '' unless defined $filename;
+
+    if ($top) {
+       $$this .= htmlsanit($entity->stringify_header) unless ($terse);
+       $$this .= "\n";
+    }
+
+    unless (($top and $type =~ m[^text(?:/plain)?(?:;|$)]) or
+           ($type =~ m[^multipart/])) {
+       push @$attachments, $entity;
+       my @dlargs = ($ref, "msg=$xmessage", "att=$#$attachments");
+       push @dlargs, "filename=$filename" if $filename ne '';
+       my $printname = $filename;
+       $printname = 'Message part ' . ($#$attachments + 1) if $filename eq '';
+       $$this .= '[<a href="' . dlurl(@dlargs) . qq{">$printname</a> } .
+                 "($type, $disposition)]\n\n";
+
+       if ($msg and defined($att) and $att eq $#$attachments) {
+           my $head = $entity->head;
+           chomp(my $type = $entity->effective_type);
+           my $body = $entity->stringify_body;
+           print "Content-Type: $type";
+           print "; name=$filename" if $filename ne '';
+           print "\n\n";
+           my $decoder = new MIME::Decoder($head->mime_encoding);
+           $decoder->decode(new IO::Scalar(\$body), \*STDOUT);
+           exit(0);
+       }
+    }
+
+    return if not $top and $disposition eq 'attachment' and not defined($att);
+    return unless ($type =~ m[^text/?] and $type !~ m[^text/html(?:;|$)]) or
+                 $type =~ m[^application/pgp(?:;|$)] or
+                 $entity->parts;
+
+    if ($entity->is_multipart) {
+       my @parts = $entity->parts;
+       foreach my $part (@parts) {
+           display_entity($part, $ref, 0, $xmessage,
+                          $$this, @$attachments);
+           $$this .= "\n";
+       }
+    } elsif ($entity->parts) {
+       # We must be dealing with a nested message.
+       $$this .= "<blockquote>\n";
+       my @parts = $entity->parts;
+       foreach my $part (@parts) {
+           display_entity($part, $ref, 1, $xmessage,
+                          $$this, @$attachments);
+           $$this .= "\n";
+       }
+       $$this .= "</blockquote>\n";
+    } else {
+       $$this .= htmlsanit($entity->bodyhandle->as_string) unless ($terse);
+    }
+}
+
+my %maintainer = %{getmaintainers()};
+my %pkgsrc = %{getpkgsrc()};
 
 my $indexentry;
 my $descriptivehead;
@@ -46,26 +141,45 @@ my $dtime = strftime "%a, %e %b %Y %T UTC", localtime;
 $tail_html = $debbugs::gHTMLTail;
 $tail_html =~ s/SUBSTITUTE_DTIME/$dtime/;
 
+my %status = %{getbugstatus($ref)};
+unless (%status) {
+    print <<EOF;
+Content-Type: text/html
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head><title>$debbugs::gProject $debbugs::gBug report logs - $short</title></head>
+<body>
+<h1>$debbugs::gProject $debbugs::gBug report logs - $short</h1>
+<p>There is no record of $debbugs::gBug $short.
+Try the <a href="http://$gWebDomain/">search page</a> instead.</p>
+$tail_html</body></html>
+EOF
+    exit 0;
+}
+
 $|=1;
 
 $tpack = lc $status{'package'};
-$tpack =~ s/[^-+._a-z0-9()].*$//;
+my @tpacks = splitpackages($tpack);
 
 if  ($status{severity} eq 'normal') {
        $showseverity = '';
-#} elsif (grep($status{severity} eq $_, @strongseverities)) {
+#} elsif (isstrongseverity($status{severity})) {
 #      $showseverity = "<strong>Severity: $status{severity}</strong>;\n";
 } else {
        $showseverity = "Severity: <em>$status{severity}</em>;\n";
 }
 
 $indexentry .= "<p>$showseverity";
-$indexentry .= "Package: <a href=\"" . pkgurl($status{package}) . "\">"
-           .htmlsanit($status{package})."</a>;\n";
+$indexentry .= htmlpackagelinks($status{package}, 0);
 
 $indexentry .= "Reported by: <a href=\"" . submitterurl($status{originator})
               . "\">" . htmlsanit($status{originator}) . "</a>;\n";
 
+$indexentry .= "Owned by: " . htmlsanit($status{owner}) . ";\n"
+              if length $status{owner};
+
 my $dummy = strftime "%a, %e %b %Y %T UTC", localtime($status{date});
 $indexentry .= "Date: ".$dummy.";\n<br>";
 
@@ -87,27 +201,45 @@ if (@merged) {
        push @descstates, $descmerged;
 }
 
-if (length($status{done})) {
+if (@{$status{found_versions}}) {
+    my $foundtext = 'Found in ';
+    $foundtext .= (@{$status{found_versions}} == 1) ? 'version ' : 'versions ';
+    $foundtext .= join ', ', map htmlsanit($_), @{$status{found_versions}};
+    push @descstates, $foundtext;
+}
+
+if (@{$status{fixed_versions}}) {
+    my $fixedtext = '<strong>Fixed</strong> in ';
+    $fixedtext .= (@{$status{fixed_versions}} == 1) ? 'version ' : 'versions ';
+    $fixedtext .= join ', ', map htmlsanit($_), @{$status{fixed_versions}};
+    if (length($status{done})) {
+       $fixedtext .= ' by ' . htmlsanit($status{done});
+    }
+    push @descstates, $fixedtext;
+} elsif (length($status{done})) {
        push @descstates, "<strong>Done:</strong> ".htmlsanit($status{done});
 } elsif (length($status{forwarded})) {
-       push @descstates, "<strong>Forwarded</strong> to ".htmlsanit($status{forwarded});
+       push @descstates, "<strong>Forwarded</strong> to ".maybelink($status{forwarded});
 }
 
 $indexentry .= join(";\n", @descstates) . ";\n<br>" if @descstates;
 
-my ($short, $tmaint, $tsrc);
-$short = $ref; $short =~ s/^\d+/#$&/;
-$tmaint = defined($maintainer{$tpack}) ? $maintainer{$tpack} : '(unknown)';
-$tsrc = defined($pkgsrc{$tpack}) ? $pkgsrc{$tpack} : '(unknown)';
-$descriptivehead= $indexentry."Maintainer for $status{package} is\n".
-            '<a href="http://'.$debbugs::gWebDomain.'/db/ma/l'.&maintencoded($tmaint).'.html">'.htmlsanit($tmaint).'</a>';
-$descriptivehead.= ";\nSource for $status{package} is\n".
-           '<a href="'.srcurl($tsrc)."\">$tsrc</a>";
-$descriptivehead.= ".</p>";
+$descriptivehead = $indexentry;
+foreach my $pkg (@tpacks) {
+    my $tmaint = defined($maintainer{$pkg}) ? $maintainer{$pkg} : '(unknown)';
+    my $tsrc = defined($pkgsrc{$pkg}) ? $pkgsrc{$pkg} : '(unknown)';
+
+    $descriptivehead .=
+            htmlmaintlinks(sub { $_[0] == 1 ? "Maintainer for $pkg is\n"
+                                            : "Maintainers for $pkg are\n" },
+                           $tmaint);
+    $descriptivehead .= ";\nSource for $pkg is\n".
+            '<a href="'.srcurl($tsrc)."\">$tsrc</a>" if ($tsrc ne "(unknown)");
+    $descriptivehead .= ".\n<br>";
+}
 
-my $buglog = buglog($ref);
-open L, "<$buglog" or &quit("open log for $ref: $!");
-if ($buglog !~ m#^\Q$gSpoolDir/db-h/#) {
+open L, "<$buglog" or &quitcgi("open log for $ref: $!");
+if ($buglog !~ m#^\Q$gSpoolDir/db#) {
     $descriptivehead .= "\n<p>Bug is <strong>archived</strong>. No further changes may be made.</p>";
 }
 
@@ -115,6 +247,8 @@ my $log='';
 
 my $xmessage = 1;
 my $suppressnext = 0;
+my $found_msgid = 0;
+my %seen_msgid = ();
 
 my $thisheader = '';
 my $this = '';
@@ -123,7 +257,7 @@ my $cmsg = 1;
 
 my $normstate= 'kill-init';
 my $linenum = 0;
-my $mail = '';
+my @mail = ();
 my @mails = ();
 while(my $line = <L>) {
        $linenum++;
@@ -145,13 +279,14 @@ while(my $line = <L>) {
                    || m/^(kill-init|kill-end) (incoming-recv|autocheck|recips|html)$/
                    || m/^kill-body go$/)
                {
-                       &quit("$ref: Transition from $normstate to $newstate at $linenum disallowed");
+                       &quitcgi("$ref: Transition from $normstate to $newstate at $linenum disallowed");
                }
 
-               if ($newstate eq 'go') {
-                       $this .= "<pre>\n";
-               }
+#$this .= "\n<br>states: $normstate $newstate<br>\n";
 
+#              if ($newstate eq 'go') {
+#                      $this .= "<pre>\n";
+#              }
                if ($newstate eq 'html') {
                        $this = '';
                }
@@ -164,84 +299,47 @@ while(my $line = <L>) {
 
                        $show = ($xmessage == $msg) if ($msg);
 
-                       push @mails, $mail if ( $mbox && $mail );
+                       push @mails, join( '', @mail ) if ( $mbox && @mail );
                        if ($show) {
-                               my $downloadHtml = '';
-                               if ($mail) {
+                               if (not $mime and @mail) {
+                                       $this .= htmlsanit(join '', @mail);
+                               } elsif (@mail) {
                                        my $parser = new MIME::Parser;
                                        $parser->tmp_to_core(1);
                                        $parser->output_to_core(1);
 #                                      $parser->output_under("/tmp");
-                                       my $entity = $parser->parse_data($mail);
-                                       # TODO: make local subdir, clean it outselves
+                                       my $entity = $parser->parse( new IO::Lines \@mail );
+                                       # TODO: make local subdir, clean it ourselves
                                        # the following does NOT delete the msg dirs in /tmp
-                                       END { $entity->purge; $parser->filer->purge; }
+                                       END { if ( $entity ) { $entity->purge; } if ( $parser ) { $parser->filer->purge; } }
                                        my @attachments = ();
-                                       if ( $entity->is_multipart ) {
-                                               my @parts = $entity->parts_DFS;
-#                                              $this .= htmlsanit($entity->head->stringify);
-                                               my @keep = ();
-                                               foreach ( @parts ) {
-                                                       my $head = $_->head;
-#                                                      $head->mime_attr("content-transfer-encoding" => "8bit")
-#                                                              if !$head->mime_attr("content-transfer-encoding");
-                                                       my ($disposition,$type) = (
-                                                               $head->mime_attr("content-disposition"),
-                                                               lc $head->mime_attr("content-type")
-                                                               );
-                                                       
-#print STDERR "'$type' '$disposition'\n";
-                                                       if ($disposition && ( $disposition eq "attachment" || $disposition eq "inline" ) && $_->head->recommended_filename ) {
-                                                               push @attachments, $_;
-                                                               my $file = $_->head->recommended_filename;
-                                                               $downloadHtml .= "View Attachment: <a href=\"".dlurl($ref,"msg=$xmessage","att=$#attachments","filename=$file")."\">$file</a>\n";
-                                                               if ($msg && defined($att) && $att eq $#attachments) {
-                                                                       my $head = $_->head;
-                                                                       my $type;
-                                                                       chomp($type = $head->mime_attr("content-type"));
-                                                                       my $body = $_->stringify_body;
-                                                                       print "Content-Type: $type; name=$file\n\n";
-                                                                       my $decoder = new MIME::Decoder($head->mime_encoding);
-                                                                       $decoder->decode(new IO::Scalar(\$body), \*STDOUT);
-                                                                       exit(0);
-                                                               }
-                                                               if ($type eq 'text/plain') {
-#                                                                      push @keep, $_;
-                                                               }
-#                                                              $this .= htmlsanit($_->head->stringify);
-                                                       } else {
-#                                                              $this .= htmlsanit($_->head->stringify);
-#                                                              push @keep, $_;
-                                                       }
-#                                                      $this .= "\n" . htmlsanit($_->stringify_body);
-                                               }
-#                                              $entity->parts(\@keep) if (!$msg);
-                                       }
-                                       $this .= htmlsanit($entity->stringify);
+                                       display_entity($entity, $ref, 1, $xmessage, $this, @attachments);
+                               }
+#                              if ($normstate eq 'go' || $normstate eq 'go-nox') {
+                               if ($normstate ne 'html') {
+                                       $this = "<pre>\n$this</pre>\n";
                                }
-                               $this = "$downloadHtml\n$this$downloadHtml" if $downloadHtml;
-                               $downloadHtml = '';
-                               $this = "<pre>\n$this</pre>\n"
-                                       if $normstate eq 'go' || $normstate eq 'go-nox';
-                               $this = "$thisheader$this" if $thisheader && !( $normstate eq 'html' );;
-                               $thisheader = '';
                                if ($normstate eq 'html') {
-                                       $this .= "  <em><A href=\"" . bugurl($ref, "msg=$xmessage") . "\">Full text</A> available.</em>";
+                                       $this .= "  <em><a href=\"" . bugurl($ref, "msg=$xmessage") . "\">Full text</a> available.</em>";
                                }
+                               $this = "$thisheader$this" if $thisheader && !( $normstate eq 'html' );;
+                               $thisheader = '';
+                               my $delim = $terse ? "<p>" : "<hr>";
                                if ($reverse) {
-                                       $log = "$this\n<hr>$log";
+                                       $log = "$this\n$delim$log";
                                } else {
-                                       $log .= "$this\n<hr>\n";
+                                       $log .= "$this\n$delim\n";
                                }
                        }
 
                        $xmessage++ if ($normstate ne 'html');
 
                        $suppressnext = $normstate eq 'html';
+                       $found_msgid = 0;
                }
                
                $normstate = $newstate;
-               $mail = '';
+               @mail = ();
                next;
        }
 
@@ -250,49 +348,71 @@ while(my $line = <L>) {
                my $pl= $_;
                $pl =~ s/\n+$//;
                m/^Received: \(at (\S+)\) by (\S+)\;/
-                       || &quit("bad line \`$pl' in state incoming-recv");
+                       || &quitcgi("bad line \`$pl' in state incoming-recv");
                $thisheader = "<h2>Message received at ".htmlsanit("$1\@$2")
                        . ":</h2>\n";
                $this = '';
                $normstate= 'go';
-               $mail .= $_;
+               push @mail, $_;
        } elsif ($normstate eq 'html') {
                $this .= $_;
        } elsif ($normstate eq 'go') {
-               if ($mail) {
-                       $mail .= $_;
+               s/^\030//;
+               if (!$suppressnext && !$found_msgid &&
+                   /^Message-ID: <(.*)>/i) {
+                       my $msgid = $1;
+                       $found_msgid = 1;
+                       if ($seen_msgid{$msgid}) {
+                               $suppressnext = 1;
+                       } else {
+                               $seen_msgid{$msgid} = 1;
+                       }
+               }
+               if (@mail) {
+                       push @mail, $_;
                } else {
                        $this .= htmlsanit($_);
                }
        } elsif ($normstate eq 'go-nox') {
                next if !s/^X//;
-               if ($mail) {
-                       $mail .= $_;
+               if (!$suppressnext && !$found_msgid &&
+                   /^Message-ID: <(.*)>/i) {
+                       my $msgid = $1;
+                       $found_msgid = 1;
+                       if ($seen_msgid{$msgid}) {
+                               $suppressnext = 1;
+                       } else {
+                               $seen_msgid{$msgid} = 1;
+                       }
+               }
+               if (@mail) {
+                       push @mail, $_;
                } else {
                        $this .= htmlsanit($_);
                }
         } elsif ($normstate eq 'recips') {
                if (m/^-t$/) {
-                       $this = "<h2>Message sent:</h2>\n";
+                       $thisheader = "<h2>Message sent:</h2>\n";
                } else {
                        s/\04/, /g; s/\n$//;
-                       $this = "<h2>Message sent to ".htmlsanit($_).":</h2>\n";
+                       $thisheader = "<h2>Message sent to ".htmlsanit($_).":</h2>\n";
                }
+               $this = "";
                $normstate= 'kill-body';
        } elsif ($normstate eq 'autocheck') {
                next if !m/^X-Debian-Bugs(-\w+)?: This is an autoforward from (\S+)/;
                $normstate= 'autowait';
                $thisheader = "<h2>Message received at $2:</h2>\n";
                $this = '';
-               $mail .= $_;
+               push @mail, $_;
        } elsif ($normstate eq 'autowait') {
                next if !m/^$/;
                $normstate= 'go-nox';
        } else {
-               &quit("$ref state $normstate line \`$_'");
+               &quitcgi("$ref state $normstate line \`$_'");
        }
 }
-&quit("$ref state $normstate at end") unless $normstate eq 'kill-end';
+&quitcgi("$ref state $normstate at end") unless $normstate eq 'kill-end';
 close(L);
 
 if ( $mbox ) {
@@ -303,31 +423,32 @@ if ( $mbox ) {
                        my $tmp = $lines[ 0 ];
                        $lines[ 0 ] = $lines[ 1 ];
                        $lines[ 1 ] = $tmp;
-                       $_ = join( "\n", @lines ) . "\n";
                }
                if ( !( $lines[ 0 ] =~ m/^From / ) ) {
-                       $ENV{ PATH } = "/bin:/usr/bin:/usr/local/bin";
-                       my $date = `date "+%a %b %d %T %Y"`;
-                       chomp $date;
-                       $_ = "From unknown $date\n" . $_;
+                       my $date = strftime "%a %b %d %T %Y", localtime;
+                       unshift @lines, "From unknown $date";
                }
+               map { s/^(>*From )/>$1/ } @lines[ 1 .. $#lines ];
+               $_ = join( "\n", @lines ) . "\n";
        }
        print join("", @mails );
        exit 0;
 }
 print "Content-Type: text/html\n\n";
 
+my $title = htmlsanit($status{subject});
+
 print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
 print "<HTML><HEAD>\n" . 
-    "<TITLE>$debbugs::gProject $debbugs::gBug report logs - $short</TITLE>\n" .
+    "<TITLE>$debbugs::gProject $debbugs::gBug report logs - $short - $title</TITLE>\n" .
     "</HEAD>\n" .
     '<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#0000FF" VLINK="#800080">' .
     "\n";
-print "<H1>" .  "$debbugs::gProject $debbugs::gBug report logs - <A HREF=\"mailto:$ref\@bugs.debian.org\">$short</A>" .
-      "<BR>" . htmlsanit($status{subject}) . "</H1>\n";
+print "<H1>" . "$debbugs::gProject $debbugs::gBug report logs - <A HREF=\"mailto:$ref\@$gEmailDomain\">$short</A>" .
+      "<BR>" . $title . "</H1>\n";
 
 print "$descriptivehead\n";
-printf "<p>View this report as an <a href=\"%s\">mbox folder</a>.</p>", mboxurl($ref);
+printf "<p>View this report as an <a href=\"%s\">mbox folder</a>.</p>\n", mboxurl($ref);
 print "<HR>";
 print "$log";
 print $tail_html;