]> git.donarmstrong.com Git - debbugs.git/blob - Debbugs/Recipients.pm
a06e69249f4f41877ea92613b4aabf40cf6cb192
[debbugs.git] / Debbugs / Recipients.pm
1 # This module is part of debbugs, and is released
2 # under the terms of the GPL version 2, or any later version. See the
3 # file README and COPYING for more information.
4 # Copyright 2008 by Don Armstrong <don@donarmstrong.com>.
5 # $Id: perl_module_header.pm 1221 2008-05-19 15:00:40Z don $
6
7 package Debbugs::Recipients;
8
9 =head1 NAME
10
11 Debbugs::Recipients -- Determine recipients of messages from the bts
12
13 =head1 SYNOPSIS
14
15
16 =head1 DESCRIPTION
17
18
19 =head1 BUGS
20
21 None known.
22
23 =cut
24
25 use warnings;
26 use strict;
27 use vars qw($VERSION $DEBUG %EXPORT_TAGS @EXPORT_OK @EXPORT);
28 use base qw(Exporter);
29
30 BEGIN{
31      ($VERSION) = q$Revision: 1221 $ =~ /^Revision:\s+([^\s+])/;
32      $DEBUG = 0 unless defined $DEBUG;
33
34      @EXPORT = ();
35      %EXPORT_TAGS = (add    => [qw(add_recipients)],
36                      det    => [qw(determine_recipients)],
37                     );
38      @EXPORT_OK = ();
39      Exporter::export_ok_tags(keys %EXPORT_TAGS);
40      $EXPORT_TAGS{all} = [@EXPORT_OK];
41
42 }
43
44 use Debbugs::Config qw(:config);
45 use Params::Validate qw(:types validate_with);
46 use Debbugs::Common qw(:misc :util);
47 use Debbugs::Status qw(splitpackages isstrongseverity);
48
49 use Debbugs::Packages qw(binary_to_source);
50
51 use Debbugs::Mail qw(get_addresses);
52
53 use Carp;
54
55 =head2 add_recipients
56
57      add_recipients(data => $data,
58                     recipients => \%recipients;
59                    );
60
61 Given data (from read_bug or similar) (or an arrayref of data),
62 calculates the addresses which need to receive mail involving this
63 bug.
64
65 =over
66
67 =item data -- Data from read_bug or similar; can be an arrayref of data
68
69 =item recipients -- hashref of recipient data structure; pass to
70 subsequent calls of add_recipients or
71
72 =item debug -- optional 
73
74
75 =back
76
77 =cut
78
79
80 sub add_recipients {
81      # Data structure is:
82      #   maintainer email address &c -> assoc of packages -> assoc of bug#'s
83      my %param = validate_with(params => \@_,
84                                spec   => {data => {type => HASHREF|ARRAYREF,
85                                                   },
86                                           recipients => {type => HASHREF,
87                                                         },
88                                           debug => {type => HANDLE|SCALARREF,
89                                                     optional => 1,
90                                                    },
91                                           transcript => {type => HANDLE|SCALARREF,
92                                                          optional => 1,
93                                                         },
94                                           actions_taken => {type => HASHREF,
95                                                             default => {},
96                                                            },
97                                          },
98                               );
99
100      $param{transcript} = globify_scalar($param{transcript});
101      $param{debug} = globify_scalar($param{debug});
102      if (ref ($param{data}) eq 'ARRAY') {
103           for my $data (@{$param{data}}) {
104                add_recipients(data => $data,
105                               map {exists $param{$_}?($_,$param{$_}):()}
106                               qw(recipients debug transcript actions_taken)
107                              );
108           }
109           return;
110      }
111      my ($p, $addmaint);
112      my $anymaintfound=0; my $anymaintnotfound=0;
113      my $ref = $param{data}{bug_num};
114      for my $p (splitpackages($param{data}{package})) {
115           $p = lc($p);
116           if (defined $config{subscription_domain}) {
117                my @source_packages = binary_to_source(binary => $p,
118                                                       source_only => 1,
119                                                      );
120                if (@source_packages) {
121                     for my $source (@source_packages) {
122                          _add_address(recipients => $param{recipients},
123                                       address => "$source\@".$config{subscription_domain},
124                                       reason => $source,
125                                       type  => 'bcc',
126                                      );
127                     }
128                }
129                else {
130                     _add_address(recipients => $param{recipients},
131                                  address => "$p\@".$config{subscription_domain},
132                                  reason => $p,
133                                  type  => 'bcc',
134                                 );
135                }
136           }
137           if (defined $param{data}{severity} and defined $config{strong_list} and
138               isstrongseverity($param{data}{severity})) {
139                _add_address(recipients => $param{recipients},
140                             address => "$config{strong_list}\@".$config{list_domain},
141                             reason => $param{data}{severity},
142                             type  => 'bcc',
143                            );
144           }
145           my @maints = package_maintainer(binary => $p);
146           if (@maints) {
147               print {$param{debug}} "MR|".join(',',@maints)."|$p|$ref|\n";
148               _add_address(recipients => $param{recipients},
149                            address => \@maints,
150                            reason => $p,
151                            bug_num => $param{data}{bug_num},
152                            type  => 'cc',
153                           );
154               print {$param{debug}} "maintainer add >$p|".join(',',@maints)."<\n";
155           }
156           else {
157                print {$param{debug}} "maintainer none >$p<\n";
158                print {$param{transcript}} "Warning: Unknown package '$p'\n";
159                print {$param{debug}} "MR|unknown-package|$p|$ref|\n";
160                _add_address(recipients => $param{recipients},
161                             address => $config{unknown_maintainer_email},
162                             reason => $p,
163                             bug_num => $param{data}{bug_num},
164                             type  => 'cc',
165                            )
166                     if defined $config{unknown_maintainer_email} and
167                          length $config{unknown_maintainer_email};
168           }
169       }
170      if (defined $config{bug_subscription_domain} and
171          length $config{bug_subscription_domain}) {
172           _add_address(recipients => $param{recipients},
173                        address    => 'bug='.$param{data}{bug_num}.'@'.
174                                      $config{bug_subscription_domain},
175                        reason     => "bug $param{data}{bug_num}",
176                        bug_num    => $param{data}{bug_num},
177                        type       => 'bcc',
178                       );
179      }
180
181      if (length $param{data}{owner}) {
182           $addmaint = $param{data}{owner};
183           print {$param{debug}} "MO|$addmaint|$param{data}{package}|$ref|\n";
184           _add_address(recipients => $param{recipients},
185                        address => $addmaint,
186                        reason => "owner of $param{data}{bug_num}",
187                        bug_num => $param{data}{bug_num},
188                        type  => 'cc',
189                       );
190         print {$param{debug}} "owner add >$param{data}{package}|$addmaint<\n";
191      }
192      if (exists $param{actions_taken}) {
193           if (exists $param{actions_taken}{done} and
194               $param{actions_taken}{done} and
195               length($config{done_list}) and
196               length($config{list_domain})
197              ) {
198                _add_address(recipients => $param{recipients},
199                             type       => 'cc',
200                             address    => $config{done_list}.'@'.$config{list_domain},
201                             bug_num    => $param{data}{bug_num},
202                             reason     => "bug $param{data}{bug_num} done",
203                            );
204           }
205           if (exists $param{actions_taken}{forwarded} and
206               $param{actions_taken}{forwarded} and
207               length($config{forward_list}) and
208               length($config{list_domain})
209              ) {
210                _add_address(recipients => $param{recipients},
211                             type       => 'cc',
212                             address    => $config{forward_list}.'@'.$config{list_domain},
213                             bug_num    => $param{data}{bug_num},
214                             reason     => "bug $param{data}{bug_num} forwarded",
215                            );
216           }
217      }
218 }
219
220 =head2 determine_recipients
221
222      my @recipients = determine_recipients(recipients => \%recipients,
223                                            bcc => 1,
224                                           );
225      my %recipients => determine_recipients(recipients => \%recipients,);
226
227      # or a crazy example:
228      send_mail_message(message => $message,
229                        recipients =>
230                         [make_list(
231                           values %{{determine_recipients(
232                                 recipients => \%recipients)
233                                   }})
234                         ],
235                       );
236
237 Using the recipient hashref, determines the set of recipients.
238
239 If you specify one of C<bcc>, C<cc>, or C<to>, you will receive only a
240 LIST of recipients which the main should be Bcc'ed, Cc'ed, or To'ed
241 respectively. By default, a LIST with keys bcc, cc, and to is returned
242 with ARRAYREF values correponding to the users to whom a message
243 should be sent.
244
245 =over
246
247 =item address_only -- whether to only return mail addresses without reasons or realnamesq
248
249 =back
250
251 Passing more than one of bcc, cc or to is a fatal error.
252
253 =cut
254
255 sub determine_recipients {
256      my %param = validate_with(params => \@_,
257                                spec   => {recipients => {type => HASHREF,
258                                                         },
259                                           bcc        => {type => BOOLEAN,
260                                                          default => 0,
261                                                         },
262                                           cc         => {type => BOOLEAN,
263                                                          default => 0,
264                                                         },
265                                           to         => {type => BOOLEAN,
266                                                          default => 0,
267                                                         },
268                                           address_only => {type => BOOLEAN,
269                                                            default => 0,
270                                                           }
271                                          },
272                               );
273
274      if (1 < scalar grep {$param{$_}} qw(to cc bcc)) {
275           croak "Passing more than one of to, cc, or bcc is non-sensical";
276      }
277
278      my %final_recipients;
279      # start with the to recipients
280      for my $addr (keys %{$param{recipients}}) {
281           my $level = 'bcc';
282           my @reasons;
283           for my $reason (keys %{$param{recipients}{$addr}}) {
284                my @bugs;
285                for my $bug (keys %{$param{recipients}{$addr}{$reason}}) {
286                     push @bugs, $bug;
287                     my $t_level = $param{recipients}{$addr}{$reason}{$bug};
288                     if ($level eq 'to' or
289                         $t_level eq 'to') {
290                          $level = 'to';
291                     }
292                     elsif ($t_level eq 'cc') {
293                          $level = 'cc';
294                     }
295                }
296                # RFC 2822 comments cannot contain specials and
297                # unquoted () or \; there's no reason for us to allow
298                # insane things here, though, so we restrict this even
299                # more to 20-7E ( -~)
300                $reason =~ s/\\/\\\\/g;
301                $reason =~ s/([\)\(])/\\$1/g;
302                $reason =~ s/[^\x20-\x7E]//g;
303                push @reasons, $reason . ' for {'.join(',',@bugs).'}';
304           }
305           if ($param{address_only}) {
306                push @{$final_recipients{$level}}, get_addresses($addr);
307           }
308           else {
309                push @{$final_recipients{$level}}, $addr . ' ('.join(', ',@reasons).')';
310           }
311      }
312      for (qw(to cc bcc)) {
313           if ($param{$_}) {
314                if (exists $final_recipients{$_}) {
315                     return @{$final_recipients{$_}||[]};
316                }
317                return ();
318           }
319      }
320      return %final_recipients;
321 }
322
323
324 =head1 PRIVATE FUNCTIONS
325
326 =head2 _add_address
327
328           _add_address(recipients => $param{recipients},
329                        address => $addmaint,
330                        reason => $param{data}{package},
331                        bug_num => $param{data}{bug_num},
332                        type  => 'cc',
333                       );
334
335
336 =cut
337
338
339 sub _add_address {
340      my %param = validate_with(params => \@_,
341                                spec => {recipients => {type => HASHREF,
342                                                       },
343                                         bug_num    => {type => SCALAR,
344                                                        regex => qr/^\d*$/,
345                                                        default => '',
346                                                       },
347                                         reason     => {type => SCALAR,
348                                                        default => '',
349                                                       },
350                                         address    => {type => SCALAR|ARRAYREF,
351                                                       },
352                                         type       => {type => SCALAR,
353                                                        default => 'cc',
354                                                        regex   => qr/^(?:b?cc|to)$/i,
355                                                       },
356                                        },
357                               );
358      for my $addr (make_list($param{address})) {
359           if (lc($param{type}) eq 'bcc' and
360               exists $param{recipients}{$addr}{$param{reason}}{$param{bug_num}}
361              ) {
362                next;
363           }
364           elsif (lc($param{type}) eq 'cc' and
365                  exists $param{recipients}{$addr}{$param{reason}}{$param{bug_num}}
366                  and $param{recipients}{$addr}{$param{reason}}{$param{bug_num}} eq 'to'
367                 ) {
368                next;
369           }
370           $param{recipients}{$addr}{$param{reason}}{$param{bug_num}} = lc($param{type});
371      }
372 }
373
374 1;
375
376
377 __END__
378
379
380
381
382
383