From: Don Armstrong Date: Sat, 5 Aug 2017 19:27:14 +0000 (-0700) Subject: Merge remote-tracking branch 'origin/master' into database X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=0c2305d4c89d165140bc955a50f3248c9c9ba7ad;hp=-c;p=debbugs.git Merge remote-tracking branch 'origin/master' into database --- 0c2305d4c89d165140bc955a50f3248c9c9ba7ad diff --combined Debbugs/Bugs.pm index 5ae0afa,f8e049d..095ed76 --- a/Debbugs/Bugs.pm +++ b/Debbugs/Bugs.pm @@@ -55,10 -55,10 +55,10 @@@ use Params::Validate qw(validate_with : use IO::File; use Debbugs::Status qw(splitpackages get_bug_status); use Debbugs::Packages qw(getsrcpkgs getpkgsrc); -use Debbugs::Common qw(getparsedaddrs package_maintainer getmaintainers make_list); +use Debbugs::Common qw(getparsedaddrs package_maintainer getmaintainers make_list hash_slice); use Fcntl qw(O_RDONLY); use MLDBM qw(DB_File Storable); - use List::Util qw(first); + use List::AllUtils qw(first); use Carp; =head2 get_bugs @@@ -152,65 -152,55 +152,65 @@@ bug should not =cut +my $_non_search_key_regex = qr/^(bugs|archive|usertags|schema)$/; + +my %_get_bugs_common_options = + (package => {type => SCALAR|ARRAYREF, + optional => 1, + }, + src => {type => SCALAR|ARRAYREF, + optional => 1, + }, + maint => {type => SCALAR|ARRAYREF, + optional => 1, + }, + submitter => {type => SCALAR|ARRAYREF, + optional => 1, + }, + severity => {type => SCALAR|ARRAYREF, + optional => 1, + }, + status => {type => SCALAR|ARRAYREF, + optional => 1, + }, + tag => {type => SCALAR|ARRAYREF, + optional => 1, + }, + owner => {type => SCALAR|ARRAYREF, + optional => 1, + }, + dist => {type => SCALAR|ARRAYREF, + optional => 1, + }, + correspondent => {type => SCALAR|ARRAYREF, + optional => 1, + }, + affects => {type => SCALAR|ARRAYREF, + optional => 1, + }, + function => {type => CODEREF, + optional => 1, + }, + bugs => {type => SCALAR|ARRAYREF, + optional => 1, + }, + archive => {type => BOOLEAN|SCALAR, + default => 0, + }, + usertags => {type => HASHREF, + optional => 1, + }, + schema => {type => OBJECT, + optional => 1, + }, + ); + + +my $_get_bugs_options = {%_get_bugs_common_options}; sub get_bugs{ my %param = validate_with(params => \@_, - spec => {package => {type => SCALAR|ARRAYREF, - optional => 1, - }, - src => {type => SCALAR|ARRAYREF, - optional => 1, - }, - maint => {type => SCALAR|ARRAYREF, - optional => 1, - }, - submitter => {type => SCALAR|ARRAYREF, - optional => 1, - }, - severity => {type => SCALAR|ARRAYREF, - optional => 1, - }, - status => {type => SCALAR|ARRAYREF, - optional => 1, - }, - tag => {type => SCALAR|ARRAYREF, - optional => 1, - }, - owner => {type => SCALAR|ARRAYREF, - optional => 1, - }, - dist => {type => SCALAR|ARRAYREF, - optional => 1, - }, - correspondent => {type => SCALAR|ARRAYREF, - optional => 1, - }, - affects => {type => SCALAR|ARRAYREF, - optional => 1, - }, - function => {type => CODEREF, - optional => 1, - }, - bugs => {type => SCALAR|ARRAYREF, - optional => 1, - }, - archive => {type => BOOLEAN|SCALAR, - default => 0, - }, - usertags => {type => HASHREF, - optional => 1, - }, - }, - ); + spec => $_get_bugs_options, + ); # Normalize options my %options = %param; @@@ -223,7 -213,7 +223,7 @@@ return keys %bugs; } # A configuration option will set an array that we'll use here instead. - for my $routine (qw(Debbugs::Bugs::get_bugs_by_idx Debbugs::Bugs::get_bugs_flatfile)) { + for my $routine (qw(Debbugs::Bugs::get_bugs_by_db Debbugs::Bugs::get_bugs_by_idx Debbugs::Bugs::get_bugs_flatfile)) { my ($package) = $routine =~ m/^(.+)\:\:/; eval "use $package;"; if ($@) { @@@ -398,17 -388,45 +398,17 @@@ searches =cut + +my $_get_bugs_by_idx_options = + {hash_slice(%_get_bugs_common_options, + (qw(package submitter severity tag archive), + qw(owner src maint bugs correspondent), + qw(affects usertags)) + ) + }; sub get_bugs_by_idx{ my %param = validate_with(params => \@_, - spec => {package => {type => SCALAR|ARRAYREF, - optional => 1, - }, - submitter => {type => SCALAR|ARRAYREF, - optional => 1, - }, - severity => {type => SCALAR|ARRAYREF, - optional => 1, - }, - tag => {type => SCALAR|ARRAYREF, - optional => 1, - }, - archive => {type => BOOLEAN, - default => 0, - }, - owner => {type => SCALAR|ARRAYREF, - optional => 1, - }, - src => {type => SCALAR|ARRAYREF, - optional => 1, - }, - maint => {type => SCALAR|ARRAYREF, - optional => 1, - }, - bugs => {type => SCALAR|ARRAYREF, - optional => 1, - }, - correspondent => {type => SCALAR|ARRAYREF, - optional => 1, - }, - affects => {type => SCALAR|ARRAYREF, - optional => 1, - }, - usertags => {type => HASHREF, - optional => 1, - }, - }, + spec => $_get_bugs_by_idx_options ); my %bugs = (); @@@ -432,11 -450,11 +432,11 @@@ delete @param{qw(maint src)}; $param{package} = [@packages]; } - my $keys = grep {$_ !~ /^(archive|usertags|bugs)$/} keys(%param); + my $keys = grep {$_ !~ $_non_search_key_regex} keys(%param); die "Need at least 1 key to search by" unless $keys; my $arc = $param{archive} ? '-arc':''; my %idx; - for my $key (grep {$_ !~ /^(archive|usertags|bugs)$/} keys %param) { + for my $key (grep {$_ !~ $_non_search_key_regex} keys %param) { my $index = $key; $index = 'submitter-email' if $key eq 'submitter'; $index = "$config{spool_dir}/by-${index}${arc}.idx"; @@@ -479,138 -497,6 +479,138 @@@ } +=head2 get_bugs_by_db + +This routine uses the database to try to speed up +searches. + + +=cut + +my $_get_bugs_by_db_options = + {hash_slice(%_get_bugs_common_options, + (qw(package submitter severity tag archive), + qw(owner src maint bugs correspondent), + qw(affects usertags)) + ), + schema => {type => OBJECT, + }, + }; +sub get_bugs_by_db{ + my %param = validate_with(params => \@_, + spec => $_get_bugs_by_db_options, + ); + my %bugs = (); + + my $keys = grep {$_ !~ $_non_search_key_regex} keys(%param); + die "Need at least 1 key to search by" unless $keys; + my $rs = $param{schema}->resultset('Bug'); + if (exists $param{package}) { + $rs = $rs->search({-or => {map 'bin_package.'}}) + } + if (exists $param{severity}) { + $rs = $rs->search({-or => {map {('severity.severity' => $_)} + make_list($param{severity})}, + }, + {join => 'severity'}, + ); + } + for my $key (qw(owner submitter done)) { + if (exists $param{$key}) { + $rs = $rs->search({-or => {map {("${key}.addr" => $_)} + make_list($param{$key})}, + }, + {join => $key}, + ); + } + } + if (exists $param{correspondent}) { + $rs = $rs->search({-or => {map {('message_correspondents.addr' => $_)} + make_list($param{correspondent})}, + }, + {join => {correspondent => + {bug_messages => + {message => 'message_correspondents'}}}}, + ); + } + if (exists $param{affects}) { + $rs = $rs->search({-or => {map {('bin_pkg.pkg' => $_, + 'src_pkg.pkg' => $_, + )} + make_list($param{affects}), + }, + }, + {join => [{bug_affects_binpackages => 'bin_pkg'}, + {bug_affects_srcpackages => 'src_pkg'}, + ], + }, + ); + } + if (exists $param{package}) { + $rs = $rs->search({-or => {map {('bin_pkg.pkg' => $_)} + make_list($param{package})}, + }, + {join => {bug_binpackages => 'bin_pkg'}}); + } + if (exists $param{maintainer}) { + $rs = $rs->search({-or => {map {(correspondent => $_ eq '' ? undef : $_, + correspondent2 => $_ eq '' ? undef : $_, + )} + make_list($param{maintainer}) + } + }, + {join => {bug_affects_binpackage => + {bin_pkg => + {bin_ver => + {src_ver => + {maintainer => 'correspondent'} + }}}, + {bug_affects_srcpackage => + {src_pkg => + {src_ver => + {maintainer => 'correspondent'} + }}}} + } + ); + } + if (exists $param{src}) { + $rs = $rs->search({-or => {map {('src_pkg.pkg' => $_)} + make_list($param{src})}, + }, + {join => {bug_srcpackages => 'src_pkg'}}); + } + # tags are very odd, because we must handle usertags. + if (exists $param{tag}) { + # bugs from usertags which matter + my %bugs_matching_usertags; + for my $bug (make_list(grep {defined $_ } + @{$param{usertags}}{make_list($param{tag})})) { + $bugs_matching_usertags{$bug} = 1; + } + # we want all bugs which either match the tag name given in + # param, or have a usertag set which matches one of the tag + # names given in param. + $rs = $rs->search({-or => {map {('tag.tag' => $_)} + make_list($param{tag}), + map {('me.id' => $_)} + keys %bugs_matching_usertags + }, + }, + {join => {bug_tags => 'tag'}}); + } + if (exists $param{bugs}) { + $rs = $rs->search({-or => {map {('me.id' => $_)} + make_list($param{bugs})} + }); + } + # handle archive + if (defined $param{archive} and $param{archive} ne 'both') { + $rs = $rs->search({'me.archived' => $param{archive}}); + } + return $rs->get_column('id')->all(); +} + + =head2 get_bugs_flatfile This is the fallback search routine. It should be able to complete all @@@ -618,15 -504,55 +618,15 @@@ searches. [Or at least, that's the idea =cut +my $_get_bugs_flatfile_options = + {hash_slice(%_get_bugs_common_options, + map {$_ eq 'dist'?():($_)} keys %_get_bugs_common_options + ) + }; + sub get_bugs_flatfile{ my %param = validate_with(params => \@_, - spec => {package => {type => SCALAR|ARRAYREF, - optional => 1, - }, - src => {type => SCALAR|ARRAYREF, - optional => 1, - }, - maint => {type => SCALAR|ARRAYREF, - optional => 1, - }, - submitter => {type => SCALAR|ARRAYREF, - optional => 1, - }, - severity => {type => SCALAR|ARRAYREF, - optional => 1, - }, - status => {type => SCALAR|ARRAYREF, - optional => 1, - }, - tag => {type => SCALAR|ARRAYREF, - optional => 1, - }, - owner => {type => SCALAR|ARRAYREF, - optional => 1, - }, - correspondent => {type => SCALAR|ARRAYREF, - optional => 1, - }, - affects => {type => SCALAR|ARRAYREF, - optional => 1, - }, -# not yet supported -# dist => {type => SCALAR|ARRAYREF, -# optional => 1, -# }, - bugs => {type => SCALAR|ARRAYREF, - optional => 1, - }, - archive => {type => BOOLEAN, - default => 1, - }, - usertags => {type => HASHREF, - optional => 1, - }, - function => {type => CODEREF, - optional => 1, - }, - }, + spec => $_get_bugs_flatfile_options ); my $flatfile; if ($param{archive}) { diff --combined Debbugs/CGI/Bugreport.pm index fe037f0,7883cd5..dccae95 --- a/Debbugs/CGI/Bugreport.pm +++ b/Debbugs/CGI/Bugreport.pm @@@ -27,6 -27,7 +27,7 @@@ None known use warnings; use strict; + use utf8; use vars qw($VERSION $DEBUG %EXPORT_TAGS @EXPORT_OK @EXPORT); use Exporter qw(import); @@@ -34,7 -35,7 +35,7 @@@ use IO::Scalar use Params::Validate qw(validate_with :types); use Digest::MD5 qw(md5_hex); use Debbugs::Mail qw(get_addresses :reply); -use Debbugs::MIME qw(decode_rfc1522 create_mime_message); +use Debbugs::MIME qw(decode_rfc1522 create_mime_message parse_to_mime_entity); use Debbugs::CGI qw(:url :html :util); use Debbugs::Common qw(globify_scalar english_join); use Debbugs::UTF8; @@@ -44,6 -45,7 +45,7 @@@ use POSIX qw(strftime) use Encode qw(decode_utf8 encode_utf8); use URI::Escape qw(uri_escape_utf8); use Scalar::Util qw(blessed); + use List::AllUtils qw(sum); use File::Temp; BEGIN{ @@@ -126,7 -128,6 +128,6 @@@ sub display_entity my $output = globify_scalar($param{output}); my $entity = $param{entity}; my $ref = $param{bug_num}; - my $top = $param{outer}; my $xmessage = $param{msg_num}; my $attachments = $param{attachments}; @@@ -263,11 -264,24 +264,24 @@@ my $body = $entity->bodyhandle->as_string; $body = convert_to_utf8($body,$charset//'utf8'); $body = html_escape($body); + my $css_class = "message"; # Attempt to deal with format=flowed if ($content_type =~ m/format\s*=\s*\"?flowed\"?/i) { $body =~ s{^\ }{}mgo; # we ignore the other things that you can do with # flowed e-mails cause they don't really matter. + $css_class .= " flowed"; + } + + # if the message is composed entirely of lines which are separated by + # newlines, wrap it. [Allow the signature to have special formatting.] + if ($body =~ /^([^\n]+\n\n)*[^\n]*\n?(-- \n.+)*$/s or + # if the first 20 lines in the message which have any non-space + # characters are larger than 100 characters more often than they + # are not, then use CSS to try to impose sensible wrapping + sum(0,map {length ($_) > 100?1:-1} grep {/\S/} split /\n/,$body,20) > 0 + ) { + $css_class .= " wrapping"; } # Add links to URLs # We don't html escape here because we escape above; @@@ -290,10 -304,10 +304,10 @@@ ) { # Add links to CVE vulnerabilities (closes #568464) $body =~ s{(^|\s|[\(\[])(CVE-\d{4}-\d{4,})(\s|[,.-\[\]\)]|$)} - {$1$2$3}gxm; + {$1$2$3}gxm; } if (not exists $param{att}) { - print {$output} qq(
$body
\n); + print {$output} qq(
$body
\n); } } return 0; @@@ -321,7 -335,17 +335,7 @@@ sub handle_email_message my $entity; my $tempdir; if (not blessed $record) { - my $parser = MIME::Parser->new(); - # this will be cleaned up once it goes out of scope - $tempdir = File::Temp->newdir(); - $parser->output_under($tempdir->dirname()); - if ($record->{inner_file}) { - $entity = $parser->parse($record->{fh}) or - die "Unable to parse entity"; - } else { - $entity = $parser->parse_data($record->{text}) or - die "Unable to parse entity"; - } + $entity = parse_to_mime_entity($record); } else { $entity = $record; } @@@ -363,7 -387,7 +377,7 @@@ sub handle_record # $record->{text} is not in perl's internal encoding; convert it my $text = decode_rfc1522(decode_utf8(record_text($record))); my ($time) = $text =~ //; - my $class = $text =~ /^(?:Acknowledgement|Reply|Information|Report|Notification)/m ? 'infmessage':'msgreceived'; + my $class = $text =~ /^(?:Acknowledgement|Information|Report|Notification)/m ? 'infmessage':'msgreceived'; $output .= $text; # Link to forwarded http:// urls in the midst of the report # (even though these links already exist at the top) @@@ -372,7 -396,7 +386,7 @@@ # Add links to the cloned bugs $output =~ s{(Bug )(\d+)( cloned as bugs? )(\d+)(?:\-(\d+)|)}{$1.bug_links(bug=>$2).$3.bug_links(bug=>(defined $5)?[$4..$5]:$4)}eo; # Add links to merged bugs - $output =~ s{(?<=Merged )([\d\s]+)(?=\.)}{join(' ',map {bug_links(bug=>$_)} (split /\s+/, $1))}eo; + $output =~ s{(?<=Merged )([\d\s]+)(?=[\.<])}{join(' ',map {bug_links(bug=>$_)} (split /\s+/, $1))}eo; # Add links to blocked bugs $output =~ s{(?<=Blocking bugs)(?:( of )(\d+))?( (?:added|set to|removed):\s+)([\d\s\,]+)} {(defined $2?$1.bug_links(bug=>$2):'').$3. @@@ -381,7 -405,7 +395,7 @@@ (\d+(?:,\s+\d+)*(?:\,?\s+and\s+\d+)?)} {$1.(defined $3?$2.bug_links(bug=>$3):'').$4. english_join([map {bug_links(bug=>$_)} (split /\,?\s+(?:and\s+)?/, $5)])}xeo; - $output =~ s{([Aa]dded|[Rr]emoved)( indication that bug )(\d+)( blocks )([\d\s\,]+)} + $output =~ s{([Aa]dded|[Rr]emoved)( indication that bug )(\d+)( blocks ?)([\d\s\,]+)} {$1.$2.(bug_links(bug=>$3)).$4. english_join([map {bug_links(bug=>$_)} (split /\,?\s+(?:and\s+)?/, $5)])}eo; # Add links to reassigned packages @@@ -392,19 -416,20 +406,20 @@@ if (defined $time) { $output .= ' ('.strftime('%a, %d %b %Y %T GMT',gmtime($time)).') '; } - $output .= ' $bug_number, options => {msg => ($msg_number+1)}, links_only => 1, ) - ) . '">Full text and full text, rfc822 format available.'; + ) . '">mbox, '. + qq{link).

}; - $output = qq(

\n\n) . $output . "
\n"; + $output = qq(

\n\n) . $output . "

\n"; } elsif (/recips/) { my ($msg_id) = record_regex($record,qr/^Message-Id:\s+<(.+)>/i); @@@ -414,7 -439,8 +429,8 @@@ elsif (defined $msg_id) { $$seen_msg_ids{$msg_id} = 1; } - $output .= qq(

\n); + return () if defined $param{spam} and $param{spam}->is_spam($msg_id); + $output .= qq(


🔗\n); $output .= 'View this message in rfc822 format

'; $output .= handle_email_message($record, ref => $bug_number, @@@ -433,6 -459,7 +449,7 @@@ elsif (defined $msg_id) { $$seen_msg_ids{$msg_id} = 1; } + return () if defined $param{spam} and $param{spam}->is_spam($msg_id); # Incomming Mail Message my ($received,$hostname) = record_regex($record,qr/Received: \(at (\S+)\) by (\S+)\;/o); $output .= qq|

Message #$msg_number received at |. diff --combined Debbugs/Common.pm index aad3fe6,e892d70..ae7d8b4 --- a/Debbugs/Common.pm +++ b/Debbugs/Common.pm @@@ -40,12 -40,12 +40,13 @@@ BEGIN @EXPORT = (); %EXPORT_TAGS = (util => [qw(getbugcomponent getbuglocation getlocationpath get_hashname), qw(appendfile overwritefile buglog getparsedaddrs getmaintainers), + qw(getsourcemaintainers getsourcemaintainers_reverse), qw(bug_status), qw(getmaintainers_reverse), qw(getpseudodesc), qw(package_maintainer), qw(sort_versions), + qw(open_compressed_file), ], misc => [qw(make_list globify_scalar english_join checkpid), qw(cleanup_eval_fail), @@@ -240,7 -240,40 +241,40 @@@ sub overwritefile die "Unable to rename ${file}.new to $file: $!"; } + =head2 open_compressed_file + my $fh = open_compressed_file('foo.gz') or + die "Unable to open compressed file: $!"; + + + Opens a file; if the file ends in .gz, .xz, or .bz2, the appropriate + decompression program is forked and output from it is read. + + This routine by default opens the file with UTF-8 encoding; if you want some + other encoding, specify it with the second option. + + =cut + sub open_compressed_file { + my ($file,$encoding) = @_; + $encoding //= ':encoding(UTF-8)'; + my $fh; + my $mode = "<$encoding"; + my @opts; + if ($file =~ /\.gz$/) { + $mode = "-|$encoding"; + push @opts,'gzip','-dc'; + } + if ($file =~ /\.xz$/) { + $mode = "-|$encoding"; + push @opts,'xz','-dc'; + } + if ($file =~ /\.bz2$/) { + $mode = "-|$encoding"; + push @opts,'bzip2','-dc'; + } + open($fh,$mode,@opts,$file); + return $fh; + } @@@ -300,36 -333,6 +334,36 @@@ sub getmaintainers_reverse return $_maintainer_rev; } +=head2 getsourcemaintainers + + my $maintainer = getsourcemaintainers()->{debbugs} + +Returns a hashref of src_package => maintainer pairs. + +=cut + +our $_source_maintainer = undef; +our $_source_maintainer_rev = undef; +sub getsourcemaintainers { + return $_source_maintainer if defined $_source_maintainer; + package_maintainer(rehash => 1); + return $_source_maintainer; +} + +=head2 getsourcemaintainers_reverse + + my @src_packages = @{getsourcemaintainers_reverse->{'don@debian.org'}||[]}; + +Returns a hashref of maintainer => [qw(list of source packages)] pairs. + +=cut + +sub getsourcemaintainers_reverse{ + return $_source_maintainer_rev if defined $_source_maintainer_rev; + package_maintainer(rehash => 1); + return $_source_maintainer_rev; +} + =head2 package_maintainer my @s = package_maintainer(source => [qw(foo bar baz)], @@@ -355,6 -358,8 +389,6 @@@ files; defaults to =cut -our $_source_maintainer = undef; -our $_source_maintainer_rev = undef; sub package_maintainer { my %param = validate_with(params => \@_, spec => {source => {type => SCALAR|ARRAYREF, @@@ -393,7 -398,7 +427,7 @@@ for my $fn (@config{('source_maintainer_file', 'source_maintainer_file_override', 'pseudo_maint_file')}) { - next unless defined $fn; + next unless defined $fn and length $fn; if (not -e $fn) { warn "Missing source maintainer file '$fn'"; next; @@@ -409,7 -414,7 +443,7 @@@ for my $fn (@config{('maintainer_file', 'maintainer_file_override', 'pseudo_maint_file')}) { - next unless defined $fn; + next unless defined $fn and length $fn; if (not -e $fn) { warn "Missing maintainer file '$fn'"; next; @@@ -460,7 -465,7 +494,7 @@@ sub __add_to_hash } $type //= 'address'; my $fh = IO::File->new($fn,'r') or - die "Unable to open $fn for reading: $!"; + croak "Unable to open $fn for reading: $!"; binmode($fh,':encoding(UTF-8)'); while (<$fh>) { chomp; @@@ -500,7 -505,8 +534,8 @@@ sub getpseudodesc return $_pseudodesc if defined $_pseudodesc; $_pseudodesc = {}; __add_to_hash($config{pseudo_desc_file},$_pseudodesc) if - defined $config{pseudo_desc_file}; + defined $config{pseudo_desc_file} and + length $config{pseudo_desc_file}; return $_pseudodesc; } diff --combined Debbugs/Config.pm index 1811631,a02072c..596d053 --- a/Debbugs/Config.pm +++ b/Debbugs/Config.pm @@@ -47,6 -47,7 +47,7 @@@ BEGIN qw($gWebDomain $gHTMLSuffix $gCGIDomain $gMirrors), qw($gPackagePages $gSubscriptionDomain $gProject $gProjectTitle), qw($gMaintainer $gMaintainerWebpage $gMaintainerEmail $gUnknownMaintainerEmail), + qw($gPackageTrackingDomain $gUsertagPackageDomain), qw($gSubmitList $gMaintList $gQuietList $gForwardList), qw($gDoneList $gRequestList $gSubmitterList $gControlList), qw($gStrongList), @@@ -75,7 -76,6 +76,7 @@@ qw($gTemplateDir), qw($gDefaultPackage), qw($gSpamMaxThreads $gSpamSpamsPerThread $gSpamKeepRunning $gSpamScan $gSpamCrossassassinDb), + qw($gDebbugsDb), ], text => [qw($gBadEmailPrefix $gHTMLTail $gHTMLExpireNote), ], @@@ -152,12 -152,13 +153,13 @@@ set_default(\%config,'web_host_bug_dir' =item web_domain $gWebDomain - Full path of the web domain where bugs are kept, defaults to the - concatenation of L and L + Full path of the web domain where bugs are kept including the protocol (http:// + or https://). Defaults to the concatenation of 'http://', L and + L =cut - set_default(\%config,'web_domain',$config{web_host}.($config{web_host}=~m{/$}?'':'/').$config{web_host_bug_dir}); + set_default(\%config,'web_domain','http://'.$config{web_host}.($config{web_host}=~m{/$}?'':'/').$config{web_host_bug_dir}); =item html_suffix $gHTMLSuffix @@@ -170,7 -171,7 +172,7 @@@ set_default(\%config,'html_suffix','.ht =item cgi_domain $gCGIDomain Full path of the web domain where cgi scripts are kept. Defaults to - the concatentation of L and cgi. + the concatentation of L and cgi. =cut @@@ -188,21 -189,33 +190,33 @@@ set_default(\%config,'mirrors',[]) =item package_pages $gPackagePages Domain where the package pages are kept; links should work in a - package_pages/foopackage manner. Defaults to undef, which means that - package links will not be made. + package_pages/foopackage manner. Defaults to undef, which means that package + links will not be made. Should be prefixed with the appropriate protocol + (http/https). =cut set_default(\%config,'package_pages',undef); + =item package_tracking_domain $gPackageTrackingDomain + + Domain where the package pages are kept; links should work in a + package_tracking_domain/foopackage manner. Defaults to undef, which means that + package links will not be made. Should be prefixed with the appropriate protocol + (http or https). + + =cut + + set_default(\%config,'package_tracking_domain',undef); + =item package_pages $gUsertagPackageDomain Domain where where usertags of packages belong; defaults to $gPackagePages =cut - set_default(\%config,'usertag_package_domain',$config{package_pages}); + set_default(\%config,'usertag_package_domain',map {my $a = $_; defined $a?$a =~ s{https?://}{}:(); $a} $config{package_pages}); =item subscription_domain $gSubscriptionDomain @@@ -214,16 -227,25 +228,25 @@@ Domain where subscriptions to package l set_default(\%config,'subscription_domain',undef); + =item cc_all_mails_to_addr $gCcAllMailsToAddr + + Address to Cc (well, Bcc) all e-mails to + + =cut + + set_default(\%config,'cc_all_mails_to_addr',undef); + + =item cve_tracker $gCVETracker URI to CVE security tracker; in bugreport.cgi, CVE-2001-0002 becomes - linked to http://$config{cve_tracker}CVE-2001-002 + linked to $config{cve_tracker}CVE-2001-002 - Default: security-tracker.debian.org/tracker/ + Default: https://security-tracker.debian.org/tracker/ =cut - set_default(\%config,'cve_tracker','security-tracker.debian.org/tracker/'); + set_default(\%config,'cve_tracker','https://security-tracker.debian.org/tracker/'); =back @@@ -527,12 -549,12 +550,12 @@@ set_default(\%config,'removal_distribut For removal/archival purposes, all bugs are assumed to have these tags set. - Default: qw(unstable testing); + Default: qw(experimental unstable testing); =cut set_default(\%config,'removal_default_distribution_tags', - [qw(unstable testing)] + [qw(experimental unstable testing)] ); =item removal_strong_severity_default_distribution_tags @@@ -540,12 -562,12 +563,12 @@@ For removal/archival purposes, all bugs with strong severity are assumed to have these tags set. - Default: qw(unstable testing stable); + Default: qw(experimental unstable testing stable); =cut set_default(\%config,'removal_strong_severity_default_distribution_tags', - [qw(unstable testing stable)] + [qw(experimental unstable testing stable)] ); @@@ -868,6 -890,15 +891,15 @@@ Default arguments to pass to sendmail. set_default(\%config,'sendmail_arguments',[qw(-oem -oi)]); + =item envelope_from + + Envelope from to use for sent messages. If not set, whatever sendmail picks is + used. + + =cut + + set_default(\%config,'envelope_from',undef); + =item spam_scan Whether or not spamscan is being used; defaults to 0 (not being used @@@ -976,7 -1007,7 +1008,7 @@@ libravatar.cgi, our internal federated =cut - set_default(\%config,'libravatar_uri','http://'.$config{cgi_domain}.'/libravatar.cgi?email='); + set_default(\%config,'libravatar_uri',$config{cgi_domain}.'/libravatar.cgi?email='); =item libravatar_uri_options $gLibravatarUriOptions @@@ -1027,20 -1058,6 +1059,20 @@@ set_default(\%config,'libravatar_blackl =back +=head2 Database + +=over + +=item debbugs_db + +Name of debbugs PostgreSQL database service. If you wish to not use a service +file, provide a full DBD::Pg compliant data-source, for example: +C<"dbi:Pg:dbname=dbname"> + +=back + +set_default(\%config,'debbugs_db',undef); + =head2 Text Fields The following are the only text fields in general use in the scripts; @@@ -1087,10 -1104,11 +1119,11 @@@ set_default(\%config,'html_tail',<

- Debian $config{bug} tracking system
+ Debian $config{bug} tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson. +

END diff --combined Debbugs/Log.pm index e97bfac,2531a6d..d824d9a --- a/Debbugs/Log.pm +++ b/Debbugs/Log.pm @@@ -41,6 -41,7 +41,7 @@@ use Debbugs::Common qw(getbuglocation g use Params::Validate qw(:types validate_with); use Encode qw(encode encode_utf8 is_utf8); use IO::InnerFile; + use feature 'state'; =head1 NAME @@@ -173,24 -174,28 +174,28 @@@ sub ne $param{inner_file} = 0; } else { - %param = validate_with(params => \@_, - spec => {bug_num => {type => SCALAR, - optional => 1, - }, - logfh => {type => HANDLE, - optional => 1, - }, - log_name => {type => SCALAR, - optional => 1, - }, - inner_file => {type => BOOLEAN, - default => 0, - }, - } - ); + state $spec = + {bug_num => {type => SCALAR, + optional => 1, + }, + logfh => {type => HANDLE, + optional => 1, + }, + log_name => {type => SCALAR, + optional => 1, + }, + inner_file => {type => BOOLEAN, + default => 0, + }, + }; + %param = validate_with(params => \@_, + spec => $spec, + ); } - if (grep({exists $param{$_} and defined $param{$_}} qw(bug_num logfh log_name)) ne 1) { - croak "Exactly one of bug_num, logfh, or log_name must be passed and must be defined"; + if (grep({exists $param{$_} and defined $param{$_}} + qw(bug_num logfh log_name)) ne 1) { + croak "Exactly one of bug_num, logfh, or log_name ". + "must be passed and must be defined"; } my $class = ref($this) || $this; @@@ -198,17 -203,25 +203,25 @@@ bless $self, $class; if (exists $param{logfh}) { - $self->{logfh} = $param{logfh} - } - elsif (exists $param{log_name}) { - $self->{logfh} = IO::File->new($param{log_name},'r') or - die "Unable to open bug log $param{log_name} for reading: $!"; - } - elsif (exists $param{bug_num}) { - my $location = getbuglocation($param{bug_num},'log'); - my $bug_log = getbugcomponent($param{bug_num},'log',$location); - $self->{logfh} = IO::File->new($bug_log, 'r') or - die "Unable to open bug log $bug_log for reading: $!"; + $self->{logfh} = $param{logfh} + } else { + my $bug_log; + if (exists $param{bug_num}) { + my $location = getbuglocation($param{bug_num},'log'); + $bug_log = getbugcomponent($param{bug_num},'log',$location); + } else { + $bug_log = $param{log_name}; + } + if ($bug_log =~ m/\.gz$/) { + my $oldpath = $ENV{'PATH'}; + $ENV{'PATH'} = '/bin:/usr/bin'; + open($self->{logfh},'-|','gzip','-dc',$bug_log) or + die "Unable to open $bug_log for reading: $!"; + $ENV{'PATH'} = $oldpath; + } else { + open($self->{logfh},'<',$bug_log) or + die "Unable to open $bug_log for reading: $!"; + } } $self->{state} = 'kill-init'; @@@ -236,7 -249,6 +249,7 @@@ sub read_recor my $record = {}; while (defined (my $line = <$logfh>)) { + $record->{start} = $logfh->tell() if not defined $record->{start}; chomp $line; ++$this->{linenum}; if (length($line) == 1 and exists $states{ord($line)}) { @@@ -500,7 -512,6 +513,6 @@@ sub record_regex } else { my @result = $record->{text} =~ m/$regex/; return @result; - return $record->{text}; } } @@@ -513,3 -524,8 +525,8 @@@ simply a means for extracting and rewri =cut 1; + + # Local Variables: + # indent-tabs-mode: nil + # cperl-indent-level: 4 + # End: diff --combined Debbugs/MIME.pm index adc4566,1d8fcb5..8644c35 --- a/Debbugs/MIME.pm +++ b/Debbugs/MIME.pm @@@ -41,9 -41,7 +41,9 @@@ BEGIN @EXPORT = (); - %EXPORT_TAGS = (mime => [qw(parse create_mime_message getmailbody)], + %EXPORT_TAGS = (mime => [qw(parse create_mime_message getmailbody), + qw(parse_to_mime_entity), + ], rfc1522 => [qw(decode_rfc1522 encode_rfc1522)], ); @EXPORT_OK=(); @@@ -56,7 -54,7 +56,7 @@@ use File::Temp qw(tempdir) use MIME::Parser; use POSIX qw(strftime); - use List::MoreUtils qw(apply); + use List::AllUtils qw(apply); # for convert_to_utf8 use Debbugs::UTF8 qw(convert_to_utf8); @@@ -90,43 -88,6 +90,43 @@@ sub getmailbod return undef; } +=item parse_to_mime_entity + + $entity = parse_to_mime_entity($record); + +Returns a MIME::Entity from a record (from Debbugs::Log), a filehandle, or a +scalar mail message. Will die upon failure. + +Intermediate parsing results will be output under a temporary directory which +should be cleaned up upon process exit. + +=cut + +sub parse_to_mime_entity { + my ($record) = @_; + my $parser = MIME::Parser->new(); + my $entity; + # this will be cleaned up once we exit + my $tempdir = File::Temp->newdir(); + $parser->output_dir($tempdir->dirname()); + if (ref($record) eq 'HASH') { + if ($record->{inner_file}) { + $entity = $parser->parse($record->{fh}) or + die "Unable to parse entity"; + } else { + $entity = $parser->parse_data($record->{text}) or + die "Unable to parse entity"; + } + } elsif (ref($record)) { + $entity = $parser->parse($record) or + die "Unable to parse entity"; + } else { + $entity = $parser->parse_data($record) or + die "Unable to parse entity"; + } + return $entity; +} + sub parse { # header and decoded body respectively diff --combined Debbugs/Packages.pm index d35f269,6980315..877466f --- a/Debbugs/Packages.pm +++ b/Debbugs/Packages.pm @@@ -39,7 -39,7 +39,7 @@@ use Storable qw(dclone) use Params::Validate qw(validate_with :types); use Debbugs::Common qw(make_list globify_scalar sort_versions); - use List::Util qw(min max); + use List::AllUtils qw(min max); use IO::File; @@@ -72,13 -72,14 +72,14 @@@ our $_pkgcomponent our $_srcpkg; sub getpkgsrc { return $_pkgsrc if $_pkgsrc; - return {} unless defined $Debbugs::Packages::gPackageSource; + return {} unless defined $config{package_source} and + length $config{package_source}; my %pkgsrc; my %pkgcomponent; my %srcpkg; my $fh = IO::File->new($config{package_source},'r') - or die("Unable to open $config{package_source} for reading: $!"); + or croak("Unable to open $config{package_source} for reading: $!"); while(<$fh>) { next unless m/^(\S+)\s+(\S+)\s+(\S.*\S)\s*$/; my ($bin,$cmp,$src)=($1,$2,$3); @@@ -186,15 -187,12 +187,15 @@@ sub binary_to_source cache => {type => HASHREF, default => {}, }, + schema => {type => OBJECT, + optional => 1, + }, }, ); # TODO: This gets hit a lot, especially from buggyversion() - probably # need an extra cache for speed here. - return () unless defined $gBinarySourceMap; + return () unless defined $gBinarySourceMap or defined $param{schema}; if ($param{scalar_only} or not wantarray) { $param{source_only} = 1; @@@ -206,59 -204,6 +207,59 @@@ my @versions = grep {defined $_} make_list(exists $param{version}?$param{version}:[]); my @archs = grep {defined $_} make_list(exists $param{arch}?$param{arch}:[]); return () unless @binaries; + + # any src:foo is source package foo with unspecified version + @source = map {/^src:(.+)$/? + [$1,'']:()} @binaries; + @binaries = grep {$_ !~ /^src:/} @binaries; + if ($param{schema}) { + if ($param{source_only}) { + @source = map {$_->[0]} @source; + my $src_rs = $param{schema}->resultset('SrcPkg')-> + search_rs({'binpkg.pkg' => [@binaries], + @versions?('bin_vers.ver' => [@versions]):(), + @archs?('arch.arch' => [@archs]):(), + }, + {join => {'src_vers'=> + {'bin_vers'=> ['arch','bin_pkg']} + }, + distinct => 1, + }, + ); + push @source, + map {$_->pkg} $src_rs->all; + if ($param{scalar_only}) { + return join(',',@source); + } + return @source; + + } + my $src_rs = $param{schema}->resultset('SrcVer')-> + search_rs({'bin_pkg.pkg' => [@binaries], + @versions?('bin_vers.ver' => [@versions]):(), + @archs?('arch.arch' => [@archs]):(), + }, + {join => ['src_pkg', + {'bin_vers' => ['arch','binpkg']}, + ], + distinct => 1, + }, + ); + push @source, + map {[$_->get_column('src_pkg.pkg'), + $_->get_column('src_ver.ver'), + ]} $src_rs->all; + if (not @source and not @versions and not @archs) { + $src_rs = $param{schema}->resultset('SrcPkg')-> + search_rs({pkg => [@binaries]}, + {distinct => 1}, + ); + push @source, + map {[$_->pkg, + ]} $src_rs->all; + } + return @source; + } my $cache_key = join("\1", join("\0",@binaries), join("\0",@versions), @@@ -269,6 -214,10 +270,6 @@@ @{$param{cache}{$cache_key}}; } for my $binary (@binaries) { - if ($binary =~ m/^src:(.+)$/) { - push @source,[$1,'']; - next; - } if (not tied %_binarytosource) { tie %_binarytosource, MLDBM => $config{binary_source_map}, O_RDONLY or die "Unable to open $config{binary_source_map} for reading"; @@@ -286,7 -235,6 +287,6 @@@ } } else { - my $found_one_version = 0; for my $version (@versions) { next unless exists $bin->{$version}; if (exists $bin->{$version}{all}) { @@@ -582,7 -530,6 +582,6 @@@ sub makesourceversions arch => 'source', versions => '0.1.1', guess_source => 1, - debug => \$debug, warnings => \$warnings, ); @@@ -625,7 -572,6 +624,6 @@@ sub make_source_versions }, ); my ($warnings) = globify_scalar(exists $param{warnings}?$param{warnings}:undef); - my ($debug) = globify_scalar(exists $param{debug} ?$param{debug} :undef); my @packages = grep {defined $_ and length $_ } make_list($param{package}); my @archs = grep {defined $_ } make_list ($param{arch}); diff --combined Debbugs/Status.pm index 42dc850,4b8d82e..62cba34 --- a/Debbugs/Status.pm +++ b/Debbugs/Status.pm @@@ -49,7 -49,7 +49,7 @@@ use File::Copy qw(copy) use Encode qw(decode encode is_utf8); use Storable qw(dclone); - use List::Util qw(min max); + use List::AllUtils qw(min max); use Carp qw(croak); @@@ -191,7 -191,6 +191,7 @@@ sub read_bug my $status; my $log; my $location; + my $report; if (not defined $param{summary}) { my $lref; ($lref,$location) = @param{qw(bug location)}; @@@ -201,16 -200,13 +201,16 @@@ } $status = getbugcomponent($lref, 'summary', $location); $log = getbugcomponent($lref, 'log' , $location); + $report = getbugcomponent($lref, 'report' , $location); return undef unless defined $status; return undef if not -e $status; } else { $status = $param{summary}; $log = $status; + $report = $status; $log =~ s/\.summary$/.log/; + $report =~ s/\.summary$/.report/; ($location) = $status =~ m/(db-h|db|archive)/; ($param{bug}) = $status =~ m/(\d+)\.summary$/; } @@@ -281,16 -277,22 +281,27 @@@ my $status_modified = (stat($status))[9]; # Add log last modified time $data{log_modified} = (stat($log))[9] // (stat("${log}.gz"))[9]; + my $report_modified = (stat($report))[9] // $data{log_modified}; $data{last_modified} = max($status_modified,$data{log_modified}); + # if the date isn't set (ancient bug), use the smallest of any of the modified + if (not defined $data{date} or not length($data{date})) { + $data{date} = min($report_modified,$status_modified,$data{log_modified}); + } $data{location} = $location; $data{archived} = (defined($location) and ($location eq 'archive'))?1:0; $data{bug_num} = $param{bug}; + # mergedwith occasionally is sorted badly. Fix it to always be sorted by <=> + # and not include this bug + if (defined $data{mergedwith} and + $data{mergedwith}) { + $data{mergedwith} = + join(' ', + grep { $_ != $data{bug_num}} + sort { $a <=> $b } + split / /, $data{mergedwith} + ); + } return \%data; } @@@ -312,39 -314,23 +323,42 @@@ our $ditch_empty = sub return grep {length $_} map {split $splitter} @t; }; -my $ditch_empty_space = sub {return &{$ditch_empty}(' ',@_)}; +our $sort_and_unique = sub { + my @v; + my %u; + my $all_numeric = 1; + for my $v (@_) { + if ($all_numeric and $v =~ /\D/) { + $all_numeric = 0; + } + next if exists $u{$v}; + $u{$v} = 1; + push @v, $v; + } + if ($all_numeric) { + return sort {$a <=> $b} @v; + } else { + return sort @v; + } +}; + +my $ditch_space_unique_and_sort = sub {return &{$sort_and_unique}(&{$ditch_empty}(' ',@_))}; my %split_fields = (package => \&splitpackages, affects => \&splitpackages, + # Ideally we won't have to split source, but because some consumers of + # get_bug_status cannot handle arrayref, we will split it here. + source => \&splitpackages, - blocks => $ditch_empty_space, - blockedby => $ditch_empty_space, + blocks => $ditch_space_unique_and_sort, + blockedby => $ditch_space_unique_and_sort, # this isn't strictly correct, but we'll split both of them for # the time being until we ditch all use of keywords everywhere # from the code - keywords => $ditch_empty_space, - tags => $ditch_empty_space, - found_versions => $ditch_empty_space, - fixed_versions => $ditch_empty_space, - mergedwith => $ditch_empty_space, + keywords => $ditch_space_unique_and_sort, + tags => $ditch_space_unique_and_sort, + found_versions => $ditch_space_unique_and_sort, + fixed_versions => $ditch_space_unique_and_sort, + mergedwith => $ditch_space_unique_and_sort, ); sub split_status_fields { @@@ -444,7 -430,6 +458,6 @@@ data =cut sub lockreadbugmerge { - my ($bug_num,$location) = @_; my $data = lockreadbug(@_); if (not defined $data) { return (0,undef); @@@ -542,7 -527,10 +555,10 @@@ sub lock_read_all_merged_bugs # are all merged with eachother # We do a cmp sort instead of an <=> sort here, because that's # what merge does - my $expectmerge= join(' ',grep {$_ != $bug } sort @bugs); + my $expectmerge= + join(' ',grep {$_ != $bug } + sort { $a <=> $b } + @bugs); if ($newdata->{mergedwith} ne $expectmerge) { for (1..$locks) { unfilelock(exists $param{locks}?$param{locks}:()); diff --combined debian/control index 19d9da1,7bd5999..6fadd40 --- a/debian/control +++ b/debian/control @@@ -6,16 -6,16 +6,18 @@@ Uploaders: Colin Watson = 5) + Build-Depends: debhelper (>= 9) Build-Depends-Indep: libparams-validate-perl, libmailtools-perl, libmime-tools-perl, libio-stringy-perl, libmldbm-perl, liburi-perl, libsoap-lite-perl, libcgi-simple-perl, libhttp-server-simple-perl, libtest-www-mechanize-perl, libmail-rfc822-address-perl, libsafe-hole-perl, libuser-perl, - libconfig-simple-perl, libtest-pod-perl, liblist-moreutils-perl, - libtext-template-perl, graphviz, + libconfig-simple-perl, libtest-pod-perl, liblist-allutils-perl, + # used by Debbugs::Libravatar and libravatar.cgi + libfile-libmagic-perl, libgravatar-url-perl, libwww-perl, imagemagick, + libdbix-class-perl, libdatetime-format-pg-perl, - libdatetime-format-mail-perl ++ libdatetime-format-mail-perl, + libtext-template-perl, graphviz, libtext-iconv-perl Homepage: http://wiki.debian.org/Teams/Debbugs Package: debbugs @@@ -24,8 -24,8 +26,8 @@@ Depends ${perl:Depends}, ${misc:Depends}, exim4 | mail-transport-agent, - libdebbugs-perl, - Recommends: debbugs-web + libdebbugs-perl + Recommends: debbugs-web (>= 2.6~) Suggests: spamassassin (>= 3.0), libcgi-alert-perl Description: bug tracking system based on the active Debian BTS Debian has a bug tracking system which files details of bugs reported by @@@ -41,16 -41,14 +43,16 @@@ Package: libdebbugs-perl Architecture: all Depends: - ${misc:Depends}, - ${perl:Depends}, libmailtools-perl, ed, libmime-tools-perl, + ${misc:Depends}, ${perl:Depends}, libmailtools-perl, ed, libmime-tools-perl, libio-stringy-perl, libmldbm-perl, liburi-perl, libsoap-lite-perl, libcgi-simple-perl, libparams-validate-perl, libtext-template-perl, libsafe-hole-perl, libmail-rfc822-address-perl, liblist-moreutils-perl, libtext-template-perl, -# used by Debbugs::Libravatar and libravatar.cgi - libfile-libmagic-perl, libgravatar-url-perl, libwww-perl, imagemagick + # used by Debbugs::Libravatar and libravatar.cgi + libfile-libmagic-perl, + libgravatar-url-perl, libwww-perl, imagemagick, + # used by the database + libdatetime-format-mail-perl, libdbix-class-perl, libdatetime-format-pg-perl Section: perl Description: modules used by the active Debian BTS Debian has a bug tracking system which files details of bugs reported by @@@ -65,9 -63,12 +67,12 @@@ Package: debbugs-web Architecture: all Depends: + ${perl:Depends}, ${misc:Depends}, libdebbugs-perl, apache2 | httpd Suggests: libcgi-alert-perl, libapache2-mod-perl2 + Replaces: debbugs (<< 2.4.2) + Breaks: debbugs (<< 2.4.2) Description: web scripts for the active Debian BTS Debian has a bug tracking system which files details of bugs reported by users and developers. Each bug is given a number, and is kept on file until @@@ -80,6 -81,7 +85,7 @@@ Package: debbugs-local Architecture: all Depends: + ${perl:Depends}, ${misc:Depends}, libdebbugs-perl, debbugs-web, libconfig-simple-perl, libuser-perl, rsync, libhttp-server-simple-perl, libnet-server-perl