File Coverage

blib/lib/Mail/MtPolicyd/Plugin/Quota.pm
Criterion Covered Total %
statement 41 49 83.6
branch 6 12 50.0
condition 2 6 33.3
subroutine 8 8 100.0
pod 1 4 25.0
total 58 79 73.4


line stmt bran cond sub pod time code
1             package Mail::MtPolicyd::Plugin::Quota;
2              
3 2     2   1577 use Moose;
  2         3  
  2         13  
4 2     2   8575 use namespace::autoclean;
  2         4  
  2         17  
5              
6             our $VERSION = '2.01'; # VERSION
7             # ABSTRACT: mtpolicyd plugin for accounting in sql tables
8              
9             extends 'Mail::MtPolicyd::Plugin';
10             with 'Mail::MtPolicyd::Plugin::Role::UserConfig' => {
11             'uc_attributes' => [
12             'enabled', 'field', 'threshold', 'action', 'metric'
13             ],
14             };
15             with 'Mail::MtPolicyd::Plugin::Role::PluginChain';
16              
17 2     2   208 use Mail::MtPolicyd::Plugin::Result;
  2         3  
  2         35  
18 2     2   522 use Time::Piece;
  2         6965  
  2         12  
19              
20              
21             has 'enabled' => ( is => 'rw', isa => 'Str', default => 'on' );
22              
23             has 'field' => ( is => 'rw', isa => 'Str', required => 1);
24             has 'metric' => ( is => 'rw', isa => 'Str', required => 1);
25             has 'time_pattern' => ( is => 'rw', isa => 'Str', default => '%Y-%m');
26             has 'threshold' => ( is => 'rw', isa => 'Int', required => 1);
27             has 'action' => ( is => 'rw', isa => 'Str', default => 'defer smtp traffic quota has been exceeded');
28              
29             with 'Mail::MtPolicyd::Role::Connection' => {
30             name => 'db',
31             type => 'Sql',
32             };
33             with 'Mail::MtPolicyd::Plugin::Role::SqlUtils';
34              
35             sub get_timekey {
36 2     2 0 2 my $self = shift;
37 2         6 return Time::Piece->new->strftime( $self->time_pattern );
38             }
39              
40             has 'table_prefix' => ( is => 'rw', isa => 'Str', default => 'acct_');
41              
42             sub run {
43 2     2 1 49 my ( $self, $r ) = @_;
44 2         50 my $session = $r->session;
45              
46 2 50       7 if( $self->get_uc( $session, 'enabled') eq 'off' ) {
47 0         0 return;
48             }
49 2         6 my $field = $self->get_uc( $session, 'field');
50 2         3 my $metric = $self->get_uc( $session, 'metric');
51 2         5 my $action = $self->get_uc( $session, 'action');
52 2         4 my $threshold = $self->get_uc( $session, 'threshold');
53              
54 2         63 my $key = $r->attr( $field );
55 2 50 33     18 if( ! defined $key || $key =~ /^\s*$/ ) {
56 0         0 $self->log( $r, 'field '.$field.' is empty in request. skipping quota check.');
57 0         0 return;
58             }
59              
60 2         10 my $count = $self->get_accounting_count( $r,
61             $field, $metric, $key );
62              
63 2 100       5 if( $count >= $threshold ) {
64 1 50       4 if( defined $action ) {
65 1         39 return Mail::MtPolicyd::Plugin::Result->new(
66             action => $action,
67             abort => 1,
68             );
69             }
70 0 0       0 if( defined $self->chain ) {
71 0         0 my $chain_result = $self->chain->run( $r );
72 0         0 return( @{$chain_result->plugin_results} );
  0         0  
73             }
74             }
75              
76 1         6 return;
77             }
78              
79              
80             sub get_table_name {
81 2     2 0 3 my ( $self, $field ) = @_;
82 2         56 return( $self->table_prefix . $field );
83             }
84              
85             sub get_accounting_count {
86 2     2 0 4 my ( $self, $r, $field, $metric, $key ) = @_;
87 2         64 my $dbh = $self->_db_handle;
88 2         6 my $where = {
89             'key' => $key,
90             'time' => $self->get_timekey,
91             };
92 2         45 my $table_name = $dbh->quote_identifier( $self->get_table_name($field) );
93             my $where_str = join(' AND ', map {
94 2         34 $dbh->quote_identifier($_).'='.$dbh->quote($where->{$_})
  4         41  
95             } keys %$where );
96 2         36 my $column_name = $dbh->quote_identifier( $metric );
97 2         25 my $sql = "SELECT $column_name FROM $table_name WHERE $where_str";
98              
99 2         11 my $count = $dbh->selectrow_array($sql);
100              
101 2 50 33     190 if( defined $count && $count =~ /^\d+$/ ) {
102 2         7 return $count;
103             }
104 0           return;
105             }
106              
107             __PACKAGE__->meta->make_immutable;
108              
109             1;
110              
111             __END__
112              
113             =pod
114              
115             =encoding UTF-8
116              
117             =head1 NAME
118              
119             Mail::MtPolicyd::Plugin::Quota - mtpolicyd plugin for accounting in sql tables
120              
121             =head1 VERSION
122              
123             version 2.01
124              
125             =head1 DESCRIPTION
126              
127             This plugin can be used to do accounting based on request fields.
128              
129             =head1 Example
130              
131             <Plugin quota-clients>
132             module = "Quota"
133             table_prefix = "acct_"
134              
135             # per month
136             time_pattern = "%Y-%m"
137             # per ip
138             field = "client_address"
139             # allow 1000 mails
140             metric = "count"
141             threshold = 1000
142             action = "defer you exceeded your monthly limit, please insert coin"
143             </Plugin>
144              
145             =head1 Configuration
146              
147             =head2 Parameters
148              
149             The module takes the following parameters:
150              
151             =over
152              
153             =item (uc_)enabled (default: on)
154              
155             Enable/disable this check.
156              
157             =item (uc_)field (required)
158              
159             The field used for accounting/quota.
160              
161             =item (uc_)metric (required)
162              
163             The metric on which the quota should be based.
164              
165             The Accounting module stores the following metrics:
166              
167             =over
168              
169             =item count
170              
171             Number of mails recivied.
172              
173             =item count_rcpt
174              
175             Number of mails recivied multiplied with number of recipients.
176              
177             =item size
178              
179             Size of mails recivied.
180              
181             =item size_rcpt
182              
183             Size of mails recivied multiplied with number of recipients.
184              
185             =back
186              
187             =item time_pattern (default: "%Y-%m")
188              
189             A format string for building the time key used to store counters.
190              
191             Default is to build counters on a monthly base.
192              
193             For example use:
194              
195             * "%Y-%W" for weekly
196             * "%Y-%m-%d" for daily
197              
198             See "man date" for format string sequences.
199              
200             You must use the same time_pattern as used in for the Accounting module.
201              
202             =item threshold (required)
203              
204             The quota limit.
205              
206             =item action (default: defer smtp traffic quota has been exceeded)
207              
208             The action to return when the quota limit has been reached.
209              
210             =item table_prefix (default: "acct_")
211              
212             A prefix to add to every table.
213              
214             The table name will be the prefix + field_name.
215              
216             =back
217              
218             =head1 AUTHOR
219              
220             Markus Benning <ich@markusbenning.de>
221              
222             =head1 COPYRIGHT AND LICENSE
223              
224             This software is Copyright (c) 2014 by Markus Benning <ich@markusbenning.de>.
225              
226             This is free software, licensed under:
227              
228             The GNU General Public License, Version 2, June 1991
229              
230             =cut