diff --git a/en/cgi/query-pr-lib.pl b/en/cgi/query-pr-lib.pl
new file mode 100644
index 0000000000..a0b19e7d41
--- /dev/null
+++ b/en/cgi/query-pr-lib.pl
@@ -0,0 +1,162 @@
+#!/usr/bin/perl -Tw
+
+sub get_categories {
+ @categories = ();
+
+ open(Q, 'query-pr.web --list-categories 2>/dev/null |') ||
+ die "Cannot get categories\n";
+
+ while( To query the GNATS Database for specific PR number, please fill in
+this form: Alternatively, it is possible to select items you wish to search for.
+Multiple items are AND'ed together.) {
+ chop;
+ local ($cat, $desc, $responsible, $notify) = split(/:/);
+ push(@categories, $cat);
+ $catdesc{$cat} = $desc;
+ }
+}
+
+sub get_states {
+ @states = ();
+
+ open(Q, 'query-pr.web --list-states 2>/dev/null |') ||
+ die "Cannot get states\n";
+
+ while(
) {
+ chop;
+ local ($state, $type, $desc) = split(/:/);
+ push(@states, $state);
+ $statedesc{$state} = $desc;
+ }
+}
+
+sub get_classes {
+ @classes = ();
+
+ open(Q, 'query-pr.web --list-classes 2>/dev/null |') ||
+ die "Cannot get classes\n";
+
+ while(
) {
+ chop;
+ local ($class, $type, $desc) = split(/:/);
+ push(@classes, $class);
+ $classdesc{$class} = $desc;
+ }
+}
+
+sub displayform {
+print qq`
+
+To generate current list of all open PRs in GNATS database, just press
+the "Query PRs" button.
+'; $pr_e = '
';
$h1 = ''; $h1_e = '
';
$h3 = ''; $h3_e = '
';
$hr = '
';
$table = "";
$table_e = '
';
# Customizations for the look and feel of the summary tables.
$t_style = "";
} else {
$pr = ''; $pr_e = '';
$h1 = ''; $h1_e = '';
$h3 = ''; $h3_e = '';
$hr = "\n----------------------------------------" .
"---------------------------------------\n";
$table = '';
$table_e = '';
}
sub cgiparam {
local ($result) = @_;
$result =~ s/[^A-Za-z0-9+.@-]/"%".sprintf("%02X", unpack("C", $&))/ge;
$result;
}
sub header_info {
if ($html_mode) {
print &html_header("Current $project problem reports");
}
else {
print "Current $project problem reports\n";
}
if (!$input{'quiet'}) {
print "The following is a listing of current problems submitted by $project users. " .
'These represent problem reports covering all versions including ' .
'experimental development code and obsolete releases. ';
if ($html_mode) {
print <
) { - chop; - local ($cat, $desc, $responsible, $notify) = split(/:/); - push(@categories, $cat); - $catdesc{$cat} = $desc; - } -} - -sub get_states { - @states = (); - - open(Q, 'query-pr.web --list-states 2>/dev/null |') || - die "Cannot get states\n"; - - while() { - chop; - local ($state, $type, $desc) = split(/:/); - push(@states, $state); - $statedesc{$state} = $desc; - } -} - -sub get_classes { - @classes = (); - - open(Q, 'query-pr.web --list-classes 2>/dev/null |') || - die "Cannot get classes\n"; - - while() { - chop; - local ($class, $type, $desc) = split(/:/); - push(@classes, $class); - $classdesc{$class} = $desc; - } -} - sub read_gnats { local($report) = @_[0]; open(Q, "query-pr.web $report 2>/dev/null |") || die "Cannot query the PR's\n"; while() { chop; if(/^>Number:/) { $number = &getline($_); } elsif (/Arrival-Date:/) { $date = &getline($_); # strip timezone if any (between HH:MM:SS and YYYY at end of line): $date =~ s/(\d\d:\d\d:\d\d)\D+(\d{4})$/\1 \2/; ($dow,$mon,$day,$time,$year,$xtra) = split(/[ \t]+/, $date); $day = "0$day" if $day =~ /^[0-9]$/; $date = "$year/$mons{$mon}/$day"; } elsif (/>Last-Modified:/) { $lastmod = &getline($_); if ($lastmod =~ /^[ ]*$/) { $lastmod = $date; } else { # strip timezone if any (between HH:MM:SS and YYYY at end of line): $lastmod =~ s/(\d\d:\d\d:\d\d)\D+(\d{4})$/\1 \2/; ($dow,$mon,$day,$time,$year,$xtra) = split(/[ \t]+/, $lastmod); $day = "0$day" if $day =~ /^[0-9]$/; $lastmod = "$year/$mons{$mon}/$day"; } } elsif (/>Category:/) { $cat = &getline($_); } elsif (/>Severity:/) { $sev = &getline($_); } elsif (/>Responsible:/) { $resp = &getline($_); $resp =~ s/@.*//; $resp =~ tr/A-Z/a-z/; $resp = "" if (($resp =~ /$mail_unass/o) or ($resp =~ /$ports_unass/o)); $resp =~ s/^$mail_prefix//; } elsif (/>State:/) { $status = &getline($_); $status =~ s/(.).*/\1/; } elsif (/>Synopsis:/) { $syn = &getline($_); $syn =~ s/[\t]+/ /g; } elsif (/^$/) { $_ = sprintf("%s/%s", $cat, $number); $status{$_} = $status; $date{$_} = $date; $resp{$_} = $resp; $syn{$_} = $syn; $sev{$_} = $sev; $lastmod{$_} = $lastmod; push(@prs,$_); } } close(Q); } sub gnats_summary { local($report) = @_[0]; local($htmlmode) = @_[1]; local($counter) = 0; foreach (@prs) { $state = $status{$_}; $date = $date{$_}; $resp = $resp{$_}; $syn = $syn{$_}; $severity = $sev{$_}; ($cat, $number) = m|^([^/]+)/(\d+)$|; next if (($report ne '') && (eval($report) == 0)); if ($htmlmode) { $title = "$_"; $syn = &html_fixline($syn); gnats_summary_line_html($counter, $state, $date, $title, $resp, $syn); } else { $title = $_; gnats_summary_line_text($counter, $state, $date, $title, $resp, $syn); } $counter++; } if ($htmlmode) { print "${table_e}\n" if $counter; } else { print "${pr_e}\n" if $counter; } $counter; } sub gnats_summary_line_html { local($counter) = shift; local($state) = shift; local($date) = shift; local($title) = shift; local($resp) = shift; local($syn) = shift; if ($counter == 0) { print "$table\n" } print " S Submitted Tracker Resp. Description \n"; } sub gnats_summary_line_text { local($counter) = shift; local($state) = shift; local($date) = shift; local($title) = shift; local($resp) = shift; local($syn) = shift; # Print the banner line if this is the first iteration. print "${pr}\nS Submitted Tracker Resp. Description${hr}" if ($counter == 0); print "$state $date $title" . (' ' x (17 - length($_))) . $resp . (' ' x (10 - length($resp))) . substr($syn,0,39) . "\n"; } - -sub displayform { -print qq` - $state $date $title $resp $syn -Please select the items you wish to search for. Multiple items are AND'ed -together.
- -`; -} diff --git a/en/cgi/query-pr.cgi b/en/cgi/query-pr.cgi index edb51df2ed..10f4ac291c 100755 --- a/en/cgi/query-pr.cgi +++ b/en/cgi/query-pr.cgi @@ -1,1148 +1,1138 @@ #!/usr/bin/perl -Tw # # A "More Useful" GNATS query-pr Interface # # Copyright (C) 2006, Shaun Amott
-To generate current list of all open PRs in GNATS database, just press -the "Query PRs" button. -# All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # -# $FreeBSD: www/en/cgi/query-pr.cgi,v 1.59 2006/11/27 17:12:50 shaun Exp $ +# $FreeBSD: www/en/cgi/query-pr.cgi,v 1.60 2006/12/09 15:46:06 shaun Exp $ # use strict; -#use warnings; use MIME::Base64; # ports/converters/p5-MIME-Base64 use MIME::QuotedPrint; # use Convert::UU qw(uudecode uuencode); # ports/converters/p5-Convert-UU require './cgi-style.pl'; +require './query-pr-lib.pl'; use constant HTTP_HEADER => "Content-type: text/html; charset=UTF-8\r\n\r\n"; use constant HTTP_HEADER_PATCH => "Content-type: text/plain; charset=UTF-8\r\n\r\n"; use constant SECT_HEADER => 1; use constant SECT_SFIELDS => 2; use constant SECT_MFIELDS => 3; use constant ENCODING_BASE64 => 1; use constant ENCODING_QP => 2; use constant PATCH_ANY => 0x0001; use constant PATCH_DIFF => 0x0002; use constant PATCH_UUENC => 0x0004; use constant PATCH_UUENC_BIN => 0x0008; use constant PATCH_SHAR => 0x0010; use constant PATCH_BASE64 => 0x0020; my @fields_single = ( "Number", "Category", "Synopsis", "Confidential", "Severity", "Priority", "Responsible", "State", "Quarter", "Keywords", "Date-Required", "Class", "Submitter-Id", "Arrival-Date", "Closed-Date", "Last-Modified", "Originator", "Release", ); my @fields_multiple = ( "Organization", "Environment", "Description", "How-To-Repeat", "Fix", "Release-Note", "Audit-Trail", "Unformatted", ); my $fields_skip = "Confidential|Quarter|Keywords|Date-Required|Submitter-Id"; my $valid_category = '[a-z0-9][A-Za-z0-9-_]{1,25}'; my $valid_pr = '\d{1,8}'; my $binary_filetypes = '(?:\.gz|\.bz2|\.zip|\.tar)$'; my %fmt; my $f = ""; my $PR = -1; my $getpatch = -1; my $inpatch = 0; my $patchendhint = 0; my $category; my @query; my (%header, %sfields, %mfields); my $iscgi = defined $ENV{'SCRIPT_NAME'}; $ENV{'PATH'} = "/bin:/usr/bin:/usr/sbin:/sbin:/usr/local/bin"; $ENV{'QUERY_STRING'} ||= ""; $ENV{'SCRIPT_NAME'} ||= $0; # Junk from cgi-style.pl $main::hsty_base ||= ""; $main::t_style ||= ""; my $scriptname = htmlclean($ENV{'SCRIPT_NAME'}); my $querystring = htmlclean($ENV{'QUERY_STRING'}); # Do not change $self_url_base, unless you understand what it is for! # In particular: it is used as a delimiter between comments in the # Audit-Trail. my $self_url_base = "http://www.FreeBSD.org/cgi/query-pr.cgi?pr="; my $cvsweb_url = "http://www.FreeBSD.org/cgi/cvsweb.cgi/"; my $stylesheet = "$main::hsty_base/layout/css/query-pr.css"; #----------------------------------------------------------------------- # Format strings #----------------------------------------------------------------------- $fmt{'header_thead'} = < EOF $fmt{'header_tfoot'} = <
EOF $fmt{'header_trow'} = <%%(1): %%(2) EOF $fmt{'sfields_thead'} = <EOF $fmt{'sfields_trow'} = < %%(1): %%(2) EOF $fmt{'sfields_tfoot'} = <
EOF $fmt{'mfields_header'} = <%%(1): EOF $fmt{'mfields_header'} =~ s/\n+$//; $fmt{'mfields_footer'} = <EOF $fmt{'patchblock_thead'} = < Download %%(2) EOF $fmt{'patchblock_thead'} =~ s/\n+$//; $fmt{'patchblock_tfoot'} = <
EOF $fmt{'patchblock_tfoot'} =~ s/\n+$//; $fmt{'patchblock_tfoot'} =~ s/^\n+//; $fmt{'auditblock_thead'} = <EOF $fmt{'auditblock_tfoot'} = < %%(1) Changed
EOF $fmt{'auditblock_trow'} = <%%(1): %%(2) EOF $fmt{'responseblock_thead'} = <EOF $fmt{'responseblock_tfoot'} = < Reply via E-mail
EOF $fmt{'responseblock_textfoot'} = <EOF $fmt{'responseblock_texthead'} = < EOF $fmt{'responseblock_trow'} = < %%(1): %%(2) EOF $fmt{'unexpectedtext_thead'} = <EOF $fmt{'html_footerlinks'} = < Submit Followup | Raw PR | Find another PR EOF -$fmt{'query_form'} = < - -
- -EOF - $fmt{'trylatermsg'} = <- PR number: - Category: (optional) - Please try again later. EOF $fmt{'mime_boundary'} = < EOF $fmt{'quote_level_0'} = '> '; $fmt{'quote_level_1'} = '> '; $fmt{'quote_end'} = ''; $fmt{'empty'} = ' '; $fmt{'break'} = "
\n"; # From cgi-style.pl $main::t_style = ""; #----------------------------------------------------------------------- # Begin Code #----------------------------------------------------------------------- if ($ENV{'QUERY_STRING'}) { foreach (split(/&/, $ENV{'QUERY_STRING'})) { my ($key, $val) = map { s/%([0-9a-f]{2})/chr hex $1/egi; $_ } split /=/; $f = lc $val if ($key eq "f"); $PR = lc $val if ($key eq "pr" or $key eq "q"); $PR = lc $key if ($key =~ /^(?:$valid_category\/)?$valid_pr$/i); $category = lc $val if ($key eq "cat"); $getpatch = lc $val if ($key eq "getpatch"); } } unless (!$iscgi) { print HTTP_HEADER_PATCH if ($getpatch > 0 or $f eq "raw"); } ($category, $PR) = ($1, $2) if ($PR =~ /^($valid_category)\/($valid_pr)$/); $category = undef if ($category && $category !~ /^$valid_category$/); if ($PR !~ /^$valid_pr$/ || $PR < 0) { print html_header("Query PR Database"); - sprint('query_form'); + displayform(); print html_footer(); exit; } # Just in case $PR = int $PR; $PR = quotemeta $PR; if ($category) { $category = quotemeta $category; @query = split /\n/, qx(query-pr.web --full --category=${category} ${PR} 2>&1); } else { @query = split /\n/, qx(query-pr.web --full ${PR} 2>&1); } if (!@query or ($query[0] and $query[0] =~ /^query-pr(:?\.(:?real|web))?: /)) { print html_header("No PRs Matched Query"); - sprint('query_form', $PR || "", $category || ""); + displayform(); print html_footer(); exit; } elsif ($query[0] =~ /^lockf: /) { print html_header("PR Database Busy"); sprint('trylatermsg'); print html_footer(); exit; } if ($f eq "raw") { print "$_\n" foreach (@query); exit; } #----------------------------------------------------------------------- # Process Results from query-pr #----------------------------------------------------------------------- my $section = SECT_HEADER; my $mfield = $fields_multiple[0]; foreach my $line (@query) { my ($k, $v); if ($section == SECT_HEADER) { $section++ if ($line =~ /^\s*$/); next if ($line !~ /^([A-Z][A-Za-z0-9-_.]+): (.*)$/); ($k, $v) = ($1, $2); $k = lc $k; $header{$k} = $v; next; } if ($section == SECT_SFIELDS) { my $i = -1; my $f = 0; next if ($line !~ /^>([A-Z][A-Za-z-]+):\s*(.*)$/); ($k, $v) = ($1, $2); foreach (@fields_single) { if ($k eq $_) { $f = 1; last; } $i++; } if (!$f or $i == $#fields_single) { $section++; next; } $sfields{$k} = $v; next; } if ($section == SECT_MFIELDS) { my $f = 0; if ($line =~ /^>([A-Z][A-Za-z-]+):\s*(.*)$/) { foreach (@fields_multiple) { $f = 1 if $1 eq $_; next; } if ($f) { $mfield = $1; } else { push @{$mfields{$mfield}}, $2; } next; } push @{$mfields{$mfield}}, $line; next; } } $getpatch = 0 if ($getpatch < 0); if ($getpatch > 0) { extractpatch(); exit; } # Construct footer now we have enough information buildfooter(); print html_header(htmlclean("$sfields{'Category'}/$sfields{'Number'}: " . $sfields{'Synopsis'})); sprint('header_thead'); sprint('header_trow', 'From', htmlclean($header{'from'})); sprint('header_trow', 'Date', htmlclean($header{'date'})); sprint('header_trow', 'Subject', htmlclean($header{'subject'})); sprint('header_trow', 'Send-pr version', htmlclean($header{'x-send-pr-version'})); sprint('header_tfoot'); # Single-Line fields sprint('sfields_thead'); foreach (@fields_single) { my ($k, $v); $k = htmlclean($_); $v = htmlclean($sfields{$_}) || ""; $v =~ s/^(\S*).*$/$1\@FreeBSD.org<\/a>/ if ($_ eq "Responsible"); $v = "never" if ($_ eq "Last-Modified" and $v =~ /^\s*$/); next if ($_ =~ /$fields_skip/i); sprint('sfields_trow', $k, $v); } sprint('sfields_tfoot'); # Multiple-Line fields foreach my $field (@fields_multiple) { my $cfound = 0; sprint('mfields_header', $field); if ($field eq "Audit-Trail") { my %block; my $cliphack; my $blockwhy; my ($inblock, $inresponse, $mbreak) = (0, 0, 0); my $url = "${self_url_base}${PR}"; my $outp = ""; my %mime_headers; my $mime_boundary; my $mime_endheader; my $encoding = 0; # Hack for older PRs with no usable delimiter push @{$mfields{'Audit-Trail'}}, $url; $url = quotemeta $url; foreach (@{$mfields{$field}}) { # If we're sure we have a genuine Reply via E-mail block, # allow for a border case, where there is a space rather # than an empty line between the header and body. $_ = "" if ($cliphack && /^ {1,2}$/); $cliphack = 0; if ($inblock == 1 && (/^${url}\s*$/i || /^([A-Za-z_]+-Changed-From-To: .*)$/ || /^(From: )/)) { my $onnextline = ($1 ? 1 : 0); if ($blockwhy) { $blockwhy =~ s/
$//; $blockwhy = htmlparse($blockwhy); } sprint('auditblock_trow', "Why", $blockwhy || ""); undef %block; undef $blockwhy; $inblock = 0; $mbreak = 0; if ($inresponse) { if ($inpatch) { $inpatch = 0; sprint('patchblock_tfoot'); sprint('break'); } sprint('responseblock_textfoot') if ($inresponse > 1); sprint('responseblock_tfoot'); $inresponse = 0; } sprint('auditblock_tfoot'); next unless ($onnextline); } if (/^([A-Za-z_]+)-Changed-([A-Za-z_-]+?): (.*)$/) { my $w = $1; my $k = $2; if ($inresponse) { if ($inpatch) { $inpatch = 0; sprint('patchblock_tfoot'); sprint('break'); } sprint('responseblock_textfoot') if ($inresponse > 1); sprint('responseblock_tfoot'); $inresponse = 0; } if ($inblock == 0) { $block{'changed'} = $w; sprint('auditblock_thead', htmlclean($w)); $inblock = 1; } $block{$k} = $3; if ($k ne "Why") { sprint('auditblock_trow', htmlclean($k), htmlclean($block{$k})); next; } next; } elsif (/^(From|To|Cc|Subject|Date): (.*)$/) { my ($k, $v); $k = htmlclean($1); $v = htmlclean($2); if ($inresponse > 1) { if ($inpatch) { $inpatch = 0; sprint('patchblock_tfoot'); sprint('break'); } $mime_boundary = undef; $mime_endheader = 0; $encoding = 0; sprint('responseblock_textfoot'); sprint('responseblock_tfoot'); } if (!$inresponse || $inresponse > 1) { sprint('responseblock_thead'); } if ($k eq "From" or $k eq "Date") { sprint('responseblock_trow', $k, $v); } $inresponse = 1; $cliphack = 1; next; } elsif (/^\s/ and $inresponse == 1 and !$mbreak) { $cliphack = 1; next; } elsif (/^ (.*)$/) { next if ($inresponse and !$mbreak); if ($inresponse == 1) { sprint('responseblock_texthead'); $inresponse++; } # XXX - use trailing cfound if ($inresponse) { my $txt = $1; if ($txt !~ /^-+$/ && $txt !~ /(?:cut|snip)/i && $txt =~ /^--(\S+)$/) { $mime_boundary = $1 if (!defined $mime_boundary && !$inpatch); if ($1 =~ /^${mime_boundary}(--)?$/) { $mime_boundary = undef if (defined $1); if ($encoding == ENCODING_BASE64 and $outp ne "") { my $patchname; my $dp = $mime_headers{'disposition'}; if ($dp and $dp =~ /.*\bfilename=["']?([A-Za-z0-9\-\.:_]{6,36})["']?.*/) { $patchname = $1; } else { $patchname = "attachment.dat"; } if ($patchname =~ /$binary_filetypes/) { $outp = "(Binary attachment not viewable.)\n"; } else { $outp = decode_base64($outp); } $outp = "--- $patchname begins here ---\n" . $outp . "\n--- $patchname ends here ---\n"; parsepatches($_) foreach (split /\n/, $outp); $outp = ""; } sprint('mime_boundary'); $mime_endheader = 0; $encoding = 0; next; } } if (defined $mime_boundary && !$mime_endheader && !$inpatch) { if ($txt =~ /^Content-([A-Za-z-]{2,}):\s*(.*)\s*$/i) { $mime_headers{lc $1} = $2; next; } elsif ($txt =~ /^\s*(?:file)?name=["']?.*?["']?\s*$/i) { $mime_headers{'disposition'} ||= ""; if ($mime_headers{'disposition'} !~ /(?:file)?name=/) { $mime_headers{'disposition'} .= "; $txt"; } next; } else { $mime_endheader = 1; if ($mime_headers{'transfer-encoding'}) { my $enc = $mime_headers{'transfer-encoding'}; if ($enc =~ /^\s*["']?base64["']?\s*$/i) { $encoding = ENCODING_BASE64; } elsif ($enc =~ /^\s*["']?quoted-printable["']?\s*$/i) { $encoding = ENCODING_QP; } else { $encoding = 0; } } else { $encoding = 0; } } } if ($encoding == ENCODING_BASE64) { $outp .= $txt; next; } elsif ($encoding == ENCODING_QP) { # XXX: lines ending in = should be joined $txt =~ s/=$//; $txt = decode_qp($txt); } if ($txt =~ /^\s*((?:>\s*)+)/) { my $level = $1; $txt =~ s/^((?:>\s*)*={47})(=+\s*)$/$1 $2/; if ($level =~ s/.*?>.*?/./g) { my $i = 0; my @levels = split(/\s*>\s*/, $txt, length $level); my $last = pop @levels; foreach (@levels) { sprint('quote_level_'.(++$i % 2)); $_ = htmlclean($_); $_ = htmlparse($_); print; } print htmlclean($last); sprint('quote_end') while ($i--); sprint('break'); } } else { $patchendhint = 1 if ($txt eq '-- '); if ($inpatch or $txt) { parsepatches($txt) || ($inpatch || sprint('break')); } else { sprint('break'); } } } } elsif (/^$/ and $inresponse and !$mbreak) { # XXX: >line 1 ignored (but not needed) $mbreak = 1; next; } elsif (/^$/) { $mbreak = 0; next; } elsif (!$inblock and $_ !~ /^${url}\s*$/i) { if ($inresponse > 1) { if ($inpatch) { $inpatch = 0; sprint('patchblock_tfoot'); sprint('break'); } sprint('responseblock_textfoot'); sprint('responseblock_tfoot'); } sprint('unexpectedtext_thead'); print htmlclean($_); sprint('unexpectedtext_tfoot'); $inresponse = 0; next; } $cfound = ($_ ? 1 : 0) if (!$cfound); next if (!$cfound); if (!$_) { $cfound++; next; } else { print "\n" while (--$cfound); $cfound = 1; } $_ =~ s/^((?:>\s*)*={47})(=+\s*)$/$1 $2/; $_ = htmlclean($_); $blockwhy .= "$_
\n" if defined($block{'Why'}); } if ($inresponse) { if ($inpatch) { $inpatch = 0; sprint('patchblock_tfoot'); sprint('break'); } sprint('responseblock_textfoot') if ($inresponse > 1); sprint('responseblock_tfoot'); $inresponse = 0; } } elsif ($field eq "Fix") { foreach (@{$mfields{$field}}) { s/\s+$//; $cfound = ($_ ? 1 : 0) if (!$cfound); next if (!$cfound); if (!$_) { $cfound++; next; } else { sprint('break') while (--$cfound > 1); $cfound = 1; } parsepatches($_) || ($inpatch || sprint('break')); } if ($inpatch) { $inpatch = 0; sprint('patchblock_tfoot'); sprint('break'); } } else { foreach (@{$mfields{$field}}) { s/\s+$//; $cfound = ($_ ? 1 : 0) if (!$cfound); next if (!$cfound); if (!$_) { $cfound++; next; } else { sprint('break') while (--$cfound); $cfound = 1; } $_ = htmlclean($_); $_ = htmlparse($_); print; sprint('break'); } sprint('empty') if ($cfound <= 1); } sprint('mfields_footer'); } sprint('html_footerlinks'); print html_footer(); # DoS protection -- apparently. select undef, undef, undef, 0.35 unless (!$iscgi); exit; #----------------------------------------------------------------------- # Func: extractpatch() # Desc: Isolate the requested patch, and print unformatted to STDOUT. #----------------------------------------------------------------------- sub extractpatch { foreach (@{$mfields{'Fix'}}) { return if (parsepatches($_) == -1); } foreach (@{$mfields{'Audit-Trail'}}) { if (s/^ //) { return if (parsepatches($_) == -1); } else { $inpatch = 0; } } } #----------------------------------------------------------------------- # Func: sprint() # Desc: Merge provided list of strings into the desired message and # print the result to STDOUT. #----------------------------------------------------------------------- sub sprint { my $k = shift; my $msg = $fmt{$k}; if (!$msg) { warn "Message format \"$k\" not found"; return; } my $i = 1; foreach (@_) { $msg =~ s/%%()\(${i}\)/$_/g; $i++; } $msg =~ s/%%\([0-9]+\)//g; print $msg; } #----------------------------------------------------------------------- # Func: htmlclean() # Desc: Remove HTML entities from message and return the result. #----------------------------------------------------------------------- sub htmlclean { my $v = shift; return "" if (!$v); $v =~ s/&/&/g; $v =~ s/</g; $v =~ s/>/>/g; return $v; } #----------------------------------------------------------------------- # Func: htmlparse() # Desc: Perform any fancy formatting on the message (e.g. HTML-ify # URLs) and return the result. #----------------------------------------------------------------------- sub htmlparse { my $v = shift; return "" if (!$v); my $iv = 'A-Za-z0-9\-_\/#@\$\\\\'; $v =~ s/(?$1\/$2<\/a>/g; $v =~ s/((?:https?|ftps?):\/\/[^\s\/]+\/[][\w=.,\'\(\)\~\?\!\&\/\%\$\{\}:;@#+-]*)/$1<\/a>/g; $v =~ s/^RCS file: (\/home\/[A-Za-z0-9]+\/(.*?)),v$/RCS file: $1<\/a>,v/; return $v; } #----------------------------------------------------------------------- # Func: buildfooter() # Desc: Build the page footer links section. #----------------------------------------------------------------------- sub buildfooter { my ($newstr, $synopsis, $mail, $replyto, $pr, $cat); $pr = htmlclean($sfields{'Number'}); $cat = htmlclean($sfields{'Category'}); $synopsis = htmlclean($sfields{'Synopsis'}); $mail = $header{'from'}; if ($mail) { $mail =~ s/^\s*(.*)\s*$/$1/; $mail =~ s/.*<(.*)>.*/$1/; $mail =~ s/\s*\(.*\)\s*//; } $replyto = $header{'reply-to'}; if ($replyto) { $replyto =~ s/^\s*(.*)\s*$/$1/; $replyto =~ s/.*<(.*)>.*/$1/; $replyto =~ s/\s*\(.*\)\s*//; } $mail = $replyto if ($replyto); $mail .= '@FreeBSD.org' unless ($mail =~ /@/); $synopsis =~ s/[^a-zA-Z+.@-]/"%" . sprintf("%02X", unpack("C", $&))/eg; $mail =~ s/[^a-zA-Z+.@-]/"%" . sprintf("%02X", unpack("C", $&))/eg; $newstr = "mailto:bug-followup\@FreeBSD.org,${mail}?subject=Re:%20${cat}/${pr}:%20${synopsis}"; $fmt{'html_footerlinks'} =~ s/%%\(maillink\)/${newstr}/g; # Do some other replacements while here $fmt{$_} =~ s/%%\(pr\)/${pr}/g foreach (keys %fmt); } #----------------------------------------------------------------------- # Func: parsepatches() # Desc: Parse lines which might contain patches, adding HTML formatting # if requested. #----------------------------------------------------------------------- { # Local static variables my ($outp, $patchnum, $cfound, $lastcol, $lastrev, $context, $mime_boundary); sub parsepatches { $_ = shift; $outp ||= ""; $patchnum ||= 0; $cfound ||= 0; $context ||= 0; my $plus_s = ''; my $minus_s = ''; my $context_s = ''; my $revinfo_s = ''; my $at_s = ''; my $all_e = ''; my $maxcontext = 3; # XXX: This ought to be dynamic if (!$getpatch) { $cfound = ($_ ? 1 : 0) if (!$cfound); return 0 if (!$cfound); if (!$_) { $cfound++; return 0; } else { sprint('break') while (--$cfound > 1); $cfound = 1; } } if (/^--(\S+)$/ && $getpatch && !$inpatch) { if ($getpatch == $patchnum+1) { $mime_boundary = $1; return 0; } } if (/^Content-([A-Za-z-]{2,}):\s*(.*)\s*$/i && $getpatch) { if (!$inpatch) { my $k = lc $1; my $v = lc $2; if ($getpatch == $patchnum+1 and defined $mime_boundary) { if ($k eq "transfer-encoding" && $v =~ /\bbase64\b/) { $patchnum++; $inpatch |= PATCH_BASE64; } return 0; } } return 0; } if (defined $mime_boundary && /^--${mime_boundary}(?:--)?$/ && $getpatch && ($inpatch & PATCH_BASE64)) { $inpatch = 0; $mime_boundary = undef; if ($outp ne "") { print decode_base64($outp); $outp = ""; } return -1; } if (($inpatch & PATCH_BASE64) && $getpatch) { $outp .= $_; return 1; } if (/^---{1,8}\s?([A-Za-z0-9-_.,:%]+) (begins|starts) here/i && !$inpatch) { $patchnum++; $inpatch |= PATCH_ANY; return 1 if ($getpatch and $patchnum != $getpatch); $lastcol = undef; $lastrev = undef; sprint('patchblock_thead', $patchnum, htmlclean($1)) unless ($getpatch); return 1; } if (/^((?:(?:---|\*\*\*) (?:\S+)\s*(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) .*)|(diff -.*? .*? .*)|(Index: \S+)|(\*{3} \d+,\d+ \*{4}))$/ && !$inpatch) { $patchnum++; $inpatch |= PATCH_DIFF; return 1 if ($getpatch and $patchnum != $getpatch); $lastcol = undef; $lastrev = undef; sprint('patchblock_thead', $patchnum, "patch-$patchnum.diff") unless ($getpatch); } if (/^# This is a shell archive\. Save it in a file, remove anything before/ && !$inpatch) { $patchnum++; $inpatch |= PATCH_SHAR; return 1 if ($getpatch and $patchnum != $getpatch); $lastcol = undef; $lastrev = undef; sprint('patchblock_thead', $patchnum, "patch-$patchnum.shar") unless ($getpatch); } if (/^---{1,8}\s?[A-Za-z0-9-_.,:%]+ ends here/i && ($inpatch & PATCH_ANY)) { #$inpatch ^= PATCH_ANY; $inpatch = 0; $context = 0; sprint('patchblock_tfoot') unless ($getpatch); return (($patchnum == $getpatch) ? -1 : $inpatch) if ($getpatch); return $inpatch; } if (/^exit$/ && ($inpatch & PATCH_SHAR)) { $inpatch ^= PATCH_SHAR; print; sprint('patchblock_tfoot') unless ($getpatch); return 1; } if (/^begin \d\d\d (.*)/ && !($inpatch & PATCH_UUENC)) { if (!$inpatch) { $patchnum++; return 1 if ($getpatch and $patchnum != $getpatch); } sprint('patchblock_thead', $patchnum, "patch-$patchnum.uu") unless ($getpatch or $inpatch); $inpatch |= PATCH_UUENC; $inpatch |= PATCH_UUENC_BIN if ($1 =~ /$binary_filetypes/); } if ($inpatch) { if ($inpatch & PATCH_UUENC) { if (!$getpatch or $patchnum == $getpatch) { $outp .= "$_\n"; if (/^end$/) { $outp = uudecode($outp) unless (!$getpatch and $inpatch & PATCH_UUENC_BIN); $outp = htmlclean($outp) unless ($getpatch); print $outp; $inpatch ^= PATCH_UUENC; $outp = ""; $inpatch ^= PATCH_UUENC_BIN; # No outer container? sprint('patchblock_tfoot') if (!$inpatch and !$getpatch); return -1; } } } else { if (!$getpatch) { if (!($inpatch & PATCH_ANY)) { if (/^ / or $_ eq "") { $context++; } else { if ($context == $maxcontext and $patchendhint) { $context++; } else { $context = 0; } } if ($context > $maxcontext and $patchendhint) { $context = 0; # Disabled for now, since it doesn't # work quite right. # $inpatch = 0; # sprint('patchblock_tfoot'); # print; # return 0; } } $_=~ s/ $//; $_ = htmlclean($_); $_ = htmlparse($_); while (s/\t/" " x (8 - ((length($`)-1) % 8))/e) {}; # Obfustication coutesy of cdiff s/^(\+.*)$/${plus_s}$1${all_e}/o; s/^(-.*)$/${minus_s}$1${all_e}/o if !s/^(--- \d+,\d+ ----.*)$/${revinfo_s}$1${all_e}/o; s/^(\*\*\* \d+,\d+ *\*\*\*.*)$/${revinfo_s}$1${all_e}/o; s/^(\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*)$/${revinfo_s}$1${all_e}/o; s/^(!.*)$/${context_s}$1${all_e}/o; s/^(@@.*$)/${at_s}$1${all_e}/o; # if (/^1.(\d+)(\s+\(\w+\s+\d{2}-\w{3}-\d{2}\):\s)(.*)/) { # $lastcol = $lastcol || 0; # $lastcol++ if defined($lastrev) && $lastrev != $1; # $lastrev = $1; # $lastcol %= 6; # $_ = "\033[3" . ($lastcol + 1) . "m1.$1$2\033[0m$3\n"; # } } if (!$getpatch or $patchnum == $getpatch) { print; print "\n"; } } } else { if (!$getpatch) { $_ = htmlclean($_); $_ = htmlparse($_); print; } } return $inpatch; } } # ex: ts=4 sw=4