/[qa]/trunk/pts/bin/control.pl
ViewVC logotype

Contents of /trunk/pts/bin/control.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 295 - (show annotations) (download)
Tue Feb 12 12:09:58 2002 UTC (11 years, 4 months ago) by hertzog
File MIME type: text/plain
File size: 10657 byte(s)
* Added keyword support to the PTS.
1 #!/usr/bin/perl -w
2
3 # Copyright 2002 Raphaƫl Hertzog <hertzog@debian.org>
4 # Available under the terms of the General Public License version 2
5 # or (at your option) any later version
6
7 use lib '/org/packages.qa.debian.org/perl';
8 use lib '/home/rhertzog/cvs/pts/perl';
9
10 use ConfirmationSpool;
11 use MIME::Parser;
12 use MIME::Entity;
13 use Mail::Address;
14 use DB_File;
15
16 use strict;
17 use vars qw($spool_dir $conf_template $sendmail);
18
19 =head1 Mailbot for pts@qa.debian.org
20
21 It accepts the following commands :
22 subscribe <package> [<email>]
23 unsubscribe <package> [<email>]
24 confirm <md5key>
25 list <package>
26 which [<email>]
27 help
28 quit
29 thanks
30 --
31
32 =head1 INSTALLATION INSTRUCTIONS
33
34 This script needs libmime-perl and libmailtools-perl.
35
36 =cut
37
38 require "common.pl";
39
40 # Create a ConfirmationSpool object to handle the email authentication
41 my $cs = ConfirmationSpool->new($spool_dir);
42 $cs->set_sendmail($sendmail);
43 $cs->set_confirmation_template($conf_template);
44 $cs->clean();
45
46 # Parse the mail
47 my $parser = MIME::Parser->new();
48 $parser->output_to_core(1);
49 my $mail = $parser->parse(\*STDIN) or die "Parse failed !\n";
50
51 # Stop if bad X-Loop
52 my $xloop = $mail->head()->get('X-Loop');
53 if (defined($xloop) && ($xloop =~ /pts\@qa.debian.org/)) {
54 exit 0;
55 }
56
57 # Extract the subject and the sender email
58 my $subject = $mail->head()->get("Subject") || "Your mail";
59 my $mid = $mail->head()->get("Message-ID");
60 my $ref = $mail->head()->get("References") || "";
61 my ($email_obj) = Mail::Address->parse($mail->head()->get("From"));
62 my $email = lc($email_obj->address());
63 my @cc;
64
65 # Find the text/plain part containing commands...
66 if ($mail->is_multipart()) {
67 $mail = $mail->parts(0); # Assume the first part is the interesting one
68 }
69
70 # Lines of the mail we got
71 my @lines = ("Subject: " . $subject, @{$mail->body()});
72
73 # Lines of the answer that we'll send
74 my @ans = ("Processing commands for pts\@qa.debian.org:\n", "\n");
75 my $nb_err = 0;
76 my %done;
77
78 foreach my $line (@lines) {
79 push @ans, "> $line";
80
81 # Hack for subject ...
82 if ($line =~ /^Subject: (?:Re\s*:\s*)?(.*)$/i) {
83 push @ans, "\n";
84 $line = $1;
85 }
86
87 # Try to detect commands
88 if ($line =~ /^\s*#/) {
89 next;
90
91 } elsif ($line =~ /^\s*subscribe\s+(\S+)(?:\s+(\S+))?/i) {
92 my ($package, $address) = (lc($1), lc($2));
93 $address = $email if (! (defined($address) && $address));
94 next if (defined($done{"SUBSCRIBE $package $address"})); # Not twice..
95 my @explanation;
96 ($package, @explanation) = map_package($package);
97 push @ans, @explanation;
98 $cs->ask_confirmation($address, "SUBSCRIBE $package $address",
99 { "PACKAGE" => $package });
100 $done{"SUBSCRIBE $package $address"} = 1;
101 push @ans, "A confirmation mail has been sent to $address.\n";
102 push @ans, "\n";
103 push @cc, $address if ($address ne $email);
104
105 } elsif ($line =~ /^\s*unsubscribe\s+(\S+)(?:\s+(\S+))?/i) {
106 my ($package, $address) = (lc($1), lc($2));
107 $address = $email if (! (defined($address) && $address));
108 my @explanation;
109 ($package, @explanation) = map_package($package);
110 push @ans, @explanation;
111 if (unsubscribe($address, $package)) {
112 push @ans, "$address has been unsubscribed from " .
113 "$package\@packages.qa.debian.org.\n";
114 } else {
115 push @ans, "$address is not subscribed, you can't unsubscribe.\n";
116 }
117 $done{"UNSUBSCRIBE $package $address"} = 1;
118 push @ans, "\n";
119 push @cc, $address if ($address ne $email);
120
121 } elsif ($line =~ /^\s*confirm\s+(\S+)/i) {
122 my $key = $1;
123 next if (defined($done{"CONFIRM $key"})); # Not twice..
124 my $cmd = $cs->confirm($key);
125 if (defined($cmd) && ($cmd =~ /^SUBSCRIBE (\S+) (\S+)/)) {
126 my ($package, $address) = (lc($1), lc($2));
127 if (subscribe($address, $package)) {
128 push @ans, "$address has been subscribed to " .
129 "$package\@packages.qa.debian.org.\n";
130 } else {
131 push @ans, "$address is already subscribed ...\n";
132 }
133 $done{"CONFIRM $key"} = 1;
134 push @cc, $address if ($address ne $email);
135 } else {
136 push @ans, "Confirmation failed. Retry with a new " .
137 "subscribe command.\n";
138 }
139 push @ans, "\n";
140
141 } elsif ($line =~ /^\s*which(?:\s+(\S+))?/i) {
142
143 my $address = lc($1);
144 $address = $email if (! (defined($address) && $address));
145 $done{"WHICH $address"} = 1;
146 push @ans, "Here's the list of subscriptions for $address :\n";
147 push @ans, map { $_ . "\n" } (which($address));
148 push @ans, "\n";
149
150 } elsif ($line =~ /^\s*list\s+(\S+)/i) {
151
152 my $package = lc($1);
153 $done{"LIST $package"} = 1;
154 push @ans, "Here's the list of subscribers to $package :\n";
155 push @ans, map { $_ . "\n" } (list($package));
156 push @ans, "\n";
157
158 } elsif ($line =~ /^\s*(?:keyword|tag)s?(?:\s+(\S+@\S+))?\s*$/i) {
159
160 my $address = lc($1);
161 $address = $email if (! (defined($address) && $address));
162 $done{"KEYWORD $address"} = 1;
163 push @ans, "Here's the default list of accepted keywords " .
164 "for $address :\n";
165 push @ans, map { "* " . $_ . "\n" } (get_default_tags($address));
166 push @ans, "\n";
167
168 } elsif ($line =~ /^\s*(?:keyword|tag)s?\s+(\S+)(?:\s+(\S+@\S+)?)\s*$/i) {
169
170 my $package = lc($1);
171 my $address = lc($2);
172 $address = $email if (! (defined($address) && $address));
173 $done{"KEYWORD $package $address"} = 1;
174 push @ans, "Here's the list of accepted keywords associated to " .
175 "package\n";
176 push @ans, "$package for $address :\n";
177 push @ans, map { "* " . $_ . "\n" } (get_tags($address, $package));
178 push @ans, "\n";
179
180 } elsif ($line =~ /^\s*(?:keyword|tag)s?(?:\s+(\S+@\S+))?\s+([-+=])\s+(\S+(?:\s+\S+)*)\s*$/i) {
181
182 my $address = lc($1);
183 $address = $email if (! (defined($address) && $address));
184 my $cmd = $2;
185 my @t = split(/[,\s]+/, lc($3));
186 $done{"KEYWORD $address $cmd @t"} = 1;
187 foreach (@t) {
188 push @ans, "WARNING: $_ is not a valid keyword.\n"
189 if (! is_valid_tag($_));
190 }
191 open_db_write();
192 if ($cmd eq "=") {
193 set_default_tags($address, @t);
194 } elsif ($cmd eq "+") {
195 my @tags = get_default_tags($address);
196 push @tags, @t;
197 set_default_tags($address, @tags);
198 } elsif ($cmd eq "-") {
199 my $check = sub {
200 foreach my $t (@t) {
201 return 0 if ($_[0] eq $t);
202 }
203 return 1;
204 };
205 my @tags = grep { &$check($_) } (get_default_tags($address));
206 set_default_tags($address, @tags);
207 }
208 push @ans, "Here's the new default list of accepted keywords " .
209 "for $address :\n";
210 push @ans, map { "* " . $_ . "\n" } (get_default_tags($address));
211 push @ans, "\n";
212 close_db();
213 push @cc, $address if ($address ne $email);
214
215
216 } elsif ($line =~ /^\s*(?:keyword|tag)s?\s+(\S+)(?:\s+(\S+@\S+))?\s+([-+=])\s+(\S+(?:\s+\S+)*)\s*$/i) {
217
218 my $package = lc($1);
219 my $address = lc($2);
220 $address = $email if (! (defined($address) && $address));
221 my $cmd = $3;
222 my @t = split(/[,\s]+/, lc($4));
223 $done{"KEYWORD $package $address $cmd @t"} = 1;
224 foreach (@t) {
225 push @ans, "$_ is not a valid keyword.\n" if (! is_valid_tag($_));
226 }
227 open_db_write();
228 if ($cmd eq "=") {
229 set_tags($address, $package, @t);
230 } elsif ($cmd eq "+") {
231 my @tags = get_tags($address, $package);
232 push @tags, @t;
233 set_tags($address, $package, @tags);
234 } elsif ($cmd eq "-") {
235 my $check = sub {
236 foreach my $t (@t) {
237 return 0 if ($_[0] eq $t);
238 }
239 return 1;
240 };
241 my @tags = grep { &$check($_) } (get_tags($address, $package));
242 set_tags($address, $package, @tags);
243 }
244 push @ans, "Here's the new list of accepted keywords associated to " .
245 "package\n";
246 push @ans, "$package for $address :\n";
247 push @ans, map { "* " . $_ . "\n" } (get_tags($address, $package));
248 push @ans, "\n";
249 close_db();
250 push @cc, $address if ($address ne $email);
251
252 } elsif ($line =~ /^\s*help/i) {
253 push @ans, <DATA>;
254 $done{"HELP"} = 1;
255
256 } elsif ($line =~ /^(--|\s*quit|\s*thanks?|\s*txs)/i) {
257 push @ans, "Stopping processing here.\n";
258 last;
259
260 } else {
261 # accept a few lines of garbage and then stop
262 if (++$nb_err > 5) {
263 if (scalar(keys %done)) {
264 push @ans, "Five lines without commands. Stopping. Bye.\n";
265 } else {
266 push @ans, "Too much garbage. Stopping.\n";
267 }
268 last;
269 }
270 }
271 }
272
273 # Prepare the subject for the answer
274 if ($subject !~ /^\s*Re: /) {
275 $subject = "Re: $subject";
276 }
277
278 my $answer = MIME::Entity->build(From => 'owner@packages.qa.debian.org',
279 To => $email,
280 Subject => $subject,
281 Encoding => '8bit',
282 'X-Loop' => 'pts@qa.debian.org',
283 'References' => "$ref $mid",
284 'In-Reply-To:' => $mid,
285 Data => \@ans);
286 my %uniq;
287 foreach (@cc) { $uniq{$_} = 1; }
288 @cc = (keys %uniq);
289 $answer->head()->add('Cc', join(", ", @cc)) if (scalar @cc);
290
291 open(MAIL, "| $sendmail -oi -t") || die "Can't fork sendmail: $!\n";
292 $answer->print(\*MAIL);
293 close MAIL or die "Problem happened with sendmail: $!\n";
294
295
296 __DATA__
297
298 Debian Package Tracking System
299 ------------------------------
300
301 The Package Tracking System (PTS) has the following commands:
302
303 subscribe <srcpackage> [<email>]
304 Subscribes <email> to all messages regarding <srcpackage>. If
305 <email> is not given, it subscribes the From address. If the
306 <srcpackage> is not a valid source package, you'll get a warning.
307 If it's a valid binary package, the mapping will automatically be
308 done for you.
309
310 unsubscribe <srcpackage> [<email>]
311 Unsubscribes <email> from <srcpackage>. Like the subscribe command,
312 it will use the From address if <email> is not given.
313
314 which [<email>]
315 Tells you which packages <email> is subscribed to.
316
317 keyword [<email>]
318 Tells you the keywords that you are accepting. Each mail sent through
319 the Package Tracking System is associated to a keyword and you receive
320 only the mails associated to keywords that you are accepting. Here is
321 the list of available keywords :
322 * bts : mails coming from the Debian Bug Tracking System
323 * bts-control : mails sent to control@bugs.debian.org
324 * buildd : failed build logs generated by build daemons
325 * summary : automatic summary mails about the state of a package
326 * upload-source : announce of a new source upload that has been installed
327 * upload-binary : announce of a new binary-only upload (porting)
328 * katie-other : other mails from ftpmasters (override disparity, etc.)
329
330 keyword <srcpackage> [<email>]
331 Same as previous item but for the given source package since
332 you may select a different set of keywords for each source package.
333
334 keyword [<email>] {+|-|=} <list of keywords>
335 Accept (+) or refuse (-) mails associated to the given keyword(s).
336 Define the list (=) of accepted keywords.
337
338 keyword <srcpackage> [<email>] {+|-|=} <list of keywords>
339 Same as previous item but overrides the keywords list for the indicated
340 source package.
341
342 quit
343 thanks
344 Stops processing commands.
345

Properties

Name Value
svn:eol-style native
svn:executable *
svn:keywords Author Date Id Revision

  ViewVC Help
Powered by ViewVC 1.1.5