use Debbugs::Mail qw(send_mail_message);
use Debbugs::User;
use HTML::Entities qw(encode_entities);
+use Debbugs::Versions::Dpkg;
-use Debbugs::Config qw(:globals);
+use Debbugs::Config qw(:globals :config);
use Debbugs::CGI qw(html_escape);
+use Debbugs::Control qw(:archive :log);
+use Debbugs::Log qw(:misc);
+use Debbugs::Text qw(:templates);
+
+use Mail::RFC822::Address;
+
$lib_path = $gLibPath;
require "$lib_path/errorlib";
$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
$user =~ s/[(].*[)]//;
$user =~ s/^\s*(\S+)\s+.*$/$1/;
$user = "" unless (Debbugs::User::is_valid_user($user));
+my $indicated_user = 0;
my $quickabort = 0;
-my $fuckheads = "(" . join("|", @gFuckheads) . ")";
-if (@gFuckheads and $replyto =~ m/$fuckheads/) {
- &transcript("This service is unavailable.\n\n");
+my $fuckheads = "(" . join("|", @gExcludeFromControl) . ")";
+if (@gExcludeFromControl and $replyto =~ m/$fuckheads/) {
+ &transcript(fill_template('excluded_from_control'));
$quickabort = 1;
}
my $olduser = ($user ne "" ? " (was $user)" : "");
&transcript("Setting user to $newuser$olduser.\n");
$user = $newuser;
+ $indicated_user = 1;
} else {
&transcript("Selected user id ($newuser) invalid, sorry\n");
$errors++;
$user = "";
+ $indicated_user = 1;
}
} elsif (m/^usercategory\s+(\S+)(\s+\[hidden\])?\s*$/i) {
$ok++;
my @cats;
my $bad = 0;
my $catsec = 0;
- while (++$procline <= $#bodylines) {
+ if ($user eq "") {
+ &transcript("No valid user selected\n");
+ $errors++;
+ next;
+ }
+ if (not $indicated_user and defined $user) {
+ &transcript("User is $user\n");
+ $indicated_user = 1;
+ }
+ while (++$procline <= $#bodylines) {
unless ($bodylines[$procline] =~ m/^\s*([*+])\s*(\S.*)$/) {
$procline--;
last;
if (@cats) {
&transcript("Added usercategory $catname.\n\n");
$u->{"categories"}->{$catname} = [ @cats ];
+ if (not $hidden) {
+ push @{$u->{visible_cats}},$catname;
+ }
} else {
&transcript("Removed usercategory $catname.\n\n");
delete $u->{"categories"}->{$catname};
+ @{$u->{visible_cats}} = grep {$_ ne $catname} @{$u->{visible_cats}};
}
$u->write();
} elsif (m/^usertags?\s+\#?(-?\d+)\s+(([=+-])\s*)?(\S.*)?$/i) {
if ($user eq "") {
&transcript("No valid user selected\n");
$errors++;
+ $indicated_user = 1;
} elsif (&setbug) {
+ if (not $indicated_user and defined $user) {
+ &transcript("User is $user\n");
+ $indicated_user = 1;
+ }
&nochangebug;
my %ut;
Debbugs::User::read_usertags(\%ut, $user);
} while (&getnextbug);
}
}
- } elsif (m/^found\s+\#?(-?\d+)(?:\s+(\d.*))?$/i) {
+ } elsif (m{^found\s+\#?(-?\d+)
+ (?:\s+((?:$config{package_name_re}\/)?
+ $config{package_version_re}))?$}ix) {
$ok++;
$ref= $1;
$version= $2;
# tracking, because a bug may be closed by multiple
# people in different branches. Until we have something
# more flexible, we set it every time a bug is fixed,
- # and clear it precisely when a found command is
- # received for the rightmost fixed-in version, which
- # equates to the most recent fixing of the bug, or when
- # a versionless found command is received.
- if (defined $version) {
- my $lastfixed = $data->{fixed_versions}[-1];
- # TODO: what if $data->{package} is a source package?
+ # and clear it when a bug is found in a version greater
+ # than any version in which the bug is fixed or when
+ # a bug is found and there is no fixed version
+ if (defined $version) {
+ my ($version_only) = $version =~ m{([^/]+)$};
addfoundversions($data, $data->{package}, $version, 'binary');
- if (defined $lastfixed and not grep { $_ eq $lastfixed } @{$data->{fixed_versions}}) {
- $data->{done} = '';
- }
+ my @fixed_order = sort {Debbugs::Versions::Dpkg::vercmp($a,$b);}
+ map {s{.+/}{}; $_;} @{$data->{fixed_versions}};
+ if (not @fixed_order or (Debbugs::Versions::Dpkg::vercmp($version_only,$fixed_order[-1]) >= 0)) {
+ $action = "$gBug marked as found in version $version and reopened."
+ if length $data->{done};
+ $data->{done} = '';
+ }
} else {
# Versionless found; assume old-style "not fixed at
# all".
} while (&getnextbug);
}
}
- } elsif (m/^notfound\s+\#?(-?\d+)\s+(\d.*)$/i) {
+ } elsif (m[^notfound\s+\#?(-?\d+)
+ (?:\s+(?:$config{package_name_re}\/)?
+ ($config{package_version_re}))$]ix) {
$ok++;
$ref= $1;
$version= $2;
if (&setbug) {
- $action= "$gBug marked as not found in version $version.";
+ $action= "$gBug no longer marked as found in version $version.";
if (length($data->{done})) {
$extramessage= "(By the way, this $gBug is currently marked as done.)\n";
}
&addmaintainers($data);
removefoundversions($data, $data->{package}, $version, 'binary');
} while (&getnextbug);
- }
- } elsif (m/^submitter\s+\#?(-?\d+)\s+\!$/i ? ($newsubmitter=$replyto, 1) :
+ }
+ }
+ elsif (m[^fixed\s+\#?(-?\d+)\s+
+ ((?:$config{package_name_re}\/)?
+ $config{package_version_re})\s*$]ix) {
+ $ok++;
+ $ref= $1;
+ $version= $2;
+ if (&setbug) {
+ $action=
+ defined($version) ?
+ "$gBug marked as fixed in version $version." :
+ "$gBug reopened.";
+ do {
+ &addmaintainers($data);
+ addfixedversions($data, $data->{package}, $version, 'binary');
+ } while (&getnextbug);
+ }
+ }
+ elsif (m[^notfixed\s+\#?(-?\d+)\s+
+ ((?:$config{package_name_re}\/)?
+ $config{package_version_re})\s*$]ix) {
+ $ok++;
+ $ref= $1;
+ $version= $2;
+ if (&setbug) {
+ $action=
+ defined($version) ?
+ "$gBug no longer marked as fixed in version $version." :
+ "$gBug reopened.";
+ do {
+ &addmaintainers($data);
+ removefixedversions($data, $data->{package}, $version, 'binary');
+ } while (&getnextbug);
+ }
+ }
+ elsif (m/^submitter\s+\#?(-?\d+)\s+\!$/i ? ($newsubmitter=$replyto, 1) :
m/^submitter\s+\#?(-?\d+)\s+(\S.*\S)$/i ? ($newsubmitter=$2, 1) : 0) {
$ok++;
$ref= $1;
if ($ref =~ m/^-\d+$/ && defined $clonebugs{$ref}) {
$ref = $clonebugs{$ref};
}
- if (&getbug) {
+ if (not Mail::RFC822::Address::valid($newsubmitter)) {
+ transcript("$newsubmitter is not a valid e-mail address; not changing submitter\n");
+ $errors++;
+ }
+ elsif (&getbug) {
if (&checkpkglimit) {
&foundbug;
&addmaintainers($data);
&addmaintainers($data);
my $oldtitle = $data->{subject};
$data->{subject}= $newtitle;
- $action= "Changed $gBug title to $newtitle from $oldtitle.";
+ $action= "Changed $gBug title to `$newtitle' from `$oldtitle'.";
&savebug;
&transcript("$action\n");
if (length($data->{done})) {
}
}
}
- } elsif (m/^package\s+(\S.*\S)?\s*$/i) {
+ } elsif (m/^package\:?\s+(\S.*\S)?\s*$/i) {
$ok++;
my @pkgs = split /\s+/, $1;
if (scalar(@pkgs) > 0) {
&nochangebug;
}
}
+ } elsif (m/^unarchive\s+#?(\d+)$/i) {
+ $ok++;
+ $ref = $1;
+ $bug_affected{$ref} = 1;
+ my $transcript;
+ eval {
+ bug_unarchive(bug => $ref,
+ transcript => \$transcript,
+ affected_bugs => \%bug_affected,
+ requester => $header{from},
+ request_addr => $controlrequestaddr,
+ message => \@log,
+ );
+ };
+ if ($@) {
+ $errors++;
+ }
+ transcript($transcript."\n");
+ } elsif (m/^archive\s+#?(\d+)$/i) {
+ $ok++;
+ $ref = $1;
+ $bug_affected{$ref} = 1;
+ if (&setbug) {
+ if (exists $data->{unarchived}) {
+ my $transcript;
+ nochangebug();
+ eval {
+ bug_archive(bug => $ref,
+ transcript => \$transcript,
+ ignore_time => 1,
+ affected_bugs => \%bug_affected,
+ requester => $header{from},
+ request_addr => $controlrequestaddr,
+ message => \@log,
+ );
+ };
+ if ($@) {
+ $errors++;
+ }
+ transcript($transcript."\n");
+ }
+ else {
+ transcript("$gBug $ref has not been archived previously\n\n");
+ nochangebug();
+ $errors++;
+ }
+ }
} else {
&transcript("Unknown command or malformed arguments to command.\n\n");
$errors++;
$maintccs .= "Cc: " . join(",\n ",@maintccs) . "\n";
}
+my %packagepr;
+for my $maint (keys %maintccreasons) {
+ for my $package (keys %{$maintccreasons{$maint}}) {
+ next unless length $package;
+ $packagepr{$package} = 1;
+ }
+}
+my $packagepr = '';
+$packagepr = "X-${gProject}-PR-Package: " . join(keys %packagepr) . "\n" if keys %packagepr;
+
# Add Bcc's to subscribed bugs
push @bcc, map {"bugs=$_\@$gListDomain"} keys %bug_affected;
References: $header{'message-id'}
Message-ID: <handler.s.$nn.transcript\@$gEmailDomain>
Precedence: bulk
-X-$gProject-PR-Message: transcript
+${packagepr}X-$gProject-PR-Message: transcript
${transcript}Please contact me if you need assistance.
END
$repliedshow= join(', ',$replyto,@maintccaddrs);
+# -1 is the service.in log
&filelock("lock/-1");
open(AP,">>db-h/-1.log") || &quit("open db-h/-1.log: $!");
print(AP
html_escape($header{'from'})."</code>\n".
"to <code>".html_escape($controlrequestaddr)."</code>\n".
"\3\n".
- "\7\n",@{escapelog(@log)},"\n\3\n") || &quit("writing db-h/-1.log: $!");
+ "\7\n",escape_log(@log),"\n\3\n") || &quit("writing db-h/-1.log: $!");
close(AP) || &quit("open db-h/-1.log: $!");
&unfilelock;
utime(time,time,"db-h");
$midix++;
}
+sub fill_template{
+ my ($template,$extra_var) = @_;
+ $extra_var ||={};
+ my $variables = {config => \%config,
+ defined($ref)?(ref => $ref):(),
+ defined($data)?(data => $data):(),
+ %{$extra_var},
+ };
+ my $hole_var = {'&bugurl' =>
+ sub{"$_[0]: ".
+ 'http://'.$config{cgi_domain}.'/'.
+ Debbugs::CGI::bug_url($_[0]);
+ }
+ };
+ return fill_in_template(template => $template,
+ variables => $variables,
+ hole_var => $hole_var,
+ );
+}
+
+=head2 message_body_template
+
+ message_body_template('mail/ack',{ref=>'foo'});
+
+Creates a message body using a template
+
+=cut
+
+sub message_body_template{
+ my ($template,$extra_var) = @_;
+ $extra_var ||={};
+ my $body = fill_template($template,$extra_var);
+ return fill_template('mail/message_body',
+ {%{$extra_var},
+ body => $body,
+ },
+ );
+}
+
sub sendhelp {
&sendtxthelpraw("bug-log-mailserver.txt","instructions for request\@$gEmailDomain");
&sendtxthelpraw("bug-maint-mailcontrol.txt","instructions for control\@$gEmailDomain")
sub getbug {
&dlen("getbug $ref");
$lowstate eq 'idle' || die "$state ?";
- if (($data = &lockreadbug($ref))) {
+ # Only use unmerged bugs here
+ if (($data = &lockreadbug($ref,'db-h'))) {
$sref= $ref;
$lowstate= "open";
&dlex("getbug => 1");
$lowstate eq 'open' || die "$lowstate ?";
length($action) || die;
$ref == $sref || die "read $sref but saving $ref ?";
- my $hash = get_hashname($ref);
- open(L,">>db-h/$hash/$ref.log") || &quit("opening db-h/$hash/$ref.log: $!");
- print(L
- "\6\n".
- "<!-- time:".time." -->\n".
- "<strong>".html_escape($action)."</strong>\n".
- "Request was from <code>".html_escape($header{'from'})."</code>\n".
- "to <code>".html_escape($controlrequestaddr)."</code>. \n".
- "\3\n".
- "\7\n",@{escapelog(@log)},"\n\3\n") || &quit("writing db-h/$hash/$ref.log: $!");
- close(L) || &quit("closing db-h/$hash/$ref.log: $!");
+ append_action_to_log(bug => $ref,
+ action => $action,
+ requester => $header{from},
+ request_addr => $controlrequestaddr,
+ message => \@log,
+ get_lock => 0,
+ );
unlockwritebug($ref, $data);
$lowstate= "idle";
&dlex("savebug");