Alarm.pm 7.39 KB
Newer Older
1
2
3
4
5
6
7
8
9
# -*- indent-tabs-mode: nil; -*-
# vim:ft=perl:et:sw=4
# $Id$

# Sympa - SYsteme de Multi-Postage Automatique
#
# Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel
# Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
# 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites
10
# Copyright (c) 2011, 2012, 2013, 2014, 2015 GIP RENATER
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

package Sympa::Alarm;

use strict;
use warnings;

30
use Sympa::Bulk;
31
use Conf;
32
use Sympa::Log;
33
use Sympa::Mailer;
34
use Sympa::Message::Template;
35
36
use tools;

37
use base qw(Class::Singleton);
38

39
40
my $log = Sympa::Log->instance;

41
42
43
44
45
46
# Constructor for Class::Singleton.
sub _new_instance {
    my $class = shift;

    bless {
        use_bulk => undef,
47
48
        _stack   => {},
    } => $class;
49
50
}

51
sub store {
52
53
54
55
    my $self    = shift;
    my $message = shift;
    my $rcpt    = shift;
    my %options = @_;
56

57
58
    my $mailer =
        $self->{use_bulk} ? Sympa::Bulk->new : Sympa::Mailer->instance;
59
    my $operation = $options{operation};
60
61
62
63
64
65
66
67
68
69

    my $robot_id;
    if (ref $message->{context} eq 'Sympa::List') {
        $robot_id = $message->{context}->{'domain'};
    } elsif ($message->{context} and $message->{context} ne '*') {
        $robot_id = $message->{context};
    } else {
        $robot_id = '*';
    }

70
71
72
73
    $self->{_stack}->{$robot_id}{$operation}{'first'} = time
        unless $self->{_stack}->{$robot_id}{$operation}{'first'};
    $self->{_stack}->{$robot_id}{$operation}{'counter'}++;
    $self->{_stack}->{$robot_id}{$operation}{'last'} = time;
74

75
    if ($self->{_stack}->{$robot_id}{$operation}{'counter'} > 3) {
76
        my @rcpts = ref $rcpt ? @$rcpt : ($rcpt);
77
78

        # stack if too much messages w/ same code
79
        $log->syslog('info', 'Stacking message about "%s" for %s (%s)',
80
            $operation, join(', ', @rcpts), $robot_id)
sikeda's avatar
sikeda committed
81
            unless $operation eq 'logs_failed';
82
        foreach my $rcpt (@rcpts) {
83
84
            push @{$self->{_stack}
                    ->{$robot_id}{$operation}{'messages'}{$rcpt}},
85
86
                $message->as_string;
        }
sikeda's avatar
sikeda committed
87
        return 1;
88
    } else {
89
90
91
92
93
94
95
        # Overwrite envelope sender
        $message->{envelope_sender} =
            Conf::get_robot_conf($robot_id, 'request');
        #FIXME: Priority would better to be '0', isn't it?
        $message->{priority} =
            Conf::get_robot_conf($robot_id, 'sympa_priority');

96
        return $mailer->store($message, $rcpt);
97
98
99
100
    }
}

sub flush {
101
102
103
    my $self    = shift;
    my %options = @_;

104
105
106
    my $mailer =
        $self->{use_bulk} ? Sympa::Bulk->new : Sympa::Mailer->instance;
    my $purge = $options{purge};
107

108
109
    foreach my $robot_id (keys %{$self->{_stack}}) {
        foreach my $operation (keys %{$self->{_stack}->{$robot_id}}) {
110
            my $first_age =
111
                time - $self->{_stack}->{$robot_id}{$operation}{'first'};
112
            my $last_age =
113
                time - $self->{_stack}->{$robot_id}{$operation}{'last'};
sikeda's avatar
sikeda committed
114
115
116
117
118
119
            # not old enough to send and first not too old
            next
                unless $purge
                    or $last_age > 30
                    or $first_age > 60;
            next
120
                unless $self->{_stack}->{$robot_id}{$operation}{'messages'};
sikeda's avatar
sikeda committed
121
122

            my %messages =
123
                %{$self->{_stack}->{$robot_id}{$operation}{'messages'}};
124
            $log->syslog(
sikeda's avatar
sikeda committed
125
126
127
128
129
                'info', 'Got messages about "%s" (%s)',
                $operation, join(', ', keys %messages)
            );

            ##### bulk send
130
            foreach my $rcpt (keys %messages) {
sikeda's avatar
sikeda committed
131
                my $param = {
132
                    to                    => $rcpt,
sikeda's avatar
sikeda committed
133
134
                    auto_submitted        => 'auto-generated',
                    operation             => $operation,
135
                    notification_messages => $messages{$rcpt},
sikeda's avatar
sikeda committed
136
137
138
139
                    boundary              => '----------=_'
                        . tools::get_message_id($robot_id)
                };

140
                $log->syslog('info', 'Send messages to %s', $rcpt);
sikeda's avatar
sikeda committed
141
142

                # Skip DB access because DB is not accessible
143
                $rcpt = [$rcpt]
144
145
                    if $operation eq 'missing_dbd'
                        or $operation eq 'no_db'
146
                        or $operation eq 'db_restored';
sikeda's avatar
sikeda committed
147

148
149
150
151
152
153
                my $message = Sympa::Message::Template->new(
                    context  => $robot_id,
                    template => 'listmaster_groupednotifications',
                    rcpt     => $rcpt,
                    data     => $param
                );
154
                unless ($message) {
155
                    $log->syslog(
156
157
158
                        'notice',
                        'Unable to send template "listmaster_groupnotification" to %s listmaster %s',
                        $robot_id,
159
                        $rcpt
160
161
162
                    ) unless $operation eq 'logs_failed';
                    return undef;
                }
163
                unless (defined $mailer->store($message, $rcpt)) {
164
                    $log->syslog(
sikeda's avatar
sikeda committed
165
166
167
                        'notice',
                        'Unable to send template "listmaster_groupnotification" to %s listmaster %s',
                        $robot_id,
168
                        $rcpt
sikeda's avatar
sikeda committed
169
170
                    ) unless $operation eq 'logs_failed';
                    return undef;
171
172
                }
            }
sikeda's avatar
sikeda committed
173

174
            $log->syslog('info', 'Cleaning stacked notifications');
175
            delete $self->{_stack}->{$robot_id}{$operation};
176
        }
sikeda's avatar
sikeda committed
177
178
    }
    return 1;
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
}

1;

__END__

=encoding utf-8

=head1 NAME

Sympa::Alarm - Spool on memory for listmaster notification

=head1 SYNOPSIS

    use Sympa::Alarm;
194
195
196
197
198
199
    my $alarm = Sympa::Alarm->instance;

    $alarm->store($message, $rcpt, $operation);

    $alarm->flush();
    $alarm->flush(purge => 1);
200
201
202

=head1 DESCRIPTION

203
204
L<Sympa::Alarm> implements on-memory spool for listmaster notification.

205
=head2 Methods
206
207
208

=over

209
210
211
212
=item instance ( )

I<Constructor>.
Creates a singleton instance of L<Sympa::Alarm> object.
213

214
215
216
217
218
219
220
Returns:

A new L<Sympa::Alarm> instance, or undef for failure.

=item store ( $message, $rcpt, operation => $operation )

I<Instance method>.
221
222
223
224
225
226
227
228
229
230
Stores a message of a operation to spool.

Parameters:

=over

=item $message

L<Sympa::Message> object to be stored.

231
232
233
234
235
=item $rcpt

Arrayref or scalar.  Recipient of notification.

=item operation => $operation
236
237
238
239
240
241
242
243
244

A string specifys tag of the message.

=back

Returns:

True value if succeed, otherwise C<undef>.

245
=item flush ( [ purge => $purge ] )
246

247
I<Instance method>.
248
249
250
251
252
253
Sends compiled messages in spool.

If true value is given as optional argument, all messages in spool will be
sent.

=back
254

255
=head2 Attribute
256

257
The instance of L<Sympa::Alarm> has following attribute.
258
259
260
261
262
263
264
265
266
267
268
269

=over

=item {use_bulk}

If set to be true, messages to be sent will be stored into spool
instead of being stored to sendmail.

Default is false.

=back

270
271
272
273
274
=head1 HISTORY

L<Sympa::Alarm> appeared on Sympa 6.2.

=cut