File Coverage

blib/lib/Mail/Milter/Authentication/Config.pm
Criterion Covered Total %
statement 71 103 68.9
branch 8 22 36.3
condition 5 10 50.0
subroutine 13 14 92.8
pod 5 5 100.0
total 102 154 66.2


line stmt bran cond sub pod time code
1             package Mail::Milter::Authentication::Config;
2             # ABSTRACT: Load config files for Authentication Milter
3 99     99   597 use strict;
  99         243  
  99         2501  
4 99     99   582 use warnings;
  99         204  
  99         4577  
5             our $VERSION = '20191206'; # VERSION
6              
7 99     99   584 use Mail::Milter::Authentication;
  99         199  
  99         2960  
8 99     99   3612 use Module::Load;
  99         6528  
  99         725  
9 99     99   43848 use Module::Loaded;
  99         56586  
  99         4979  
10              
11 99     99   596 use Exporter qw{ import };
  99         155  
  99         3454  
12             our @EXPORT_OK = qw{
13             get_config
14             set_config
15             default_config
16             };
17              
18 99     99   446 use JSON;
  99         296  
  99         450  
19 99     99   43283 use TOML;
  99         760152  
  99         76364  
20              
21              
22             our $PREFIX = '/etc';
23             our $IDENT = 'authentication_milter';
24             my $CONFIG;
25              
26              
27             sub default_config {
28 0     0 1 0 my $config = {
29             'debug' => 0,
30             'dryrun' => 0,
31             'logtoerr' => 0,
32             'error_log' => '/var/log/authentication_milter.err',
33             'connection' => 'inet:12345@localhost',
34             'umask' => '0000',
35             'runas' => 'nobody',
36             'rungroup' => 'nogroup',
37             'listen_backlog' => 20,
38             'min_children' => 20,
39             'max_children' => 200,
40             'min_spare_children' => 10,
41             'max_spare_children' => 20,
42             'max_requests_per_child' => 200,
43             'protocol' => 'milter',
44             'connect_timeout' => 30,
45             'command_timeout' => 30,
46             'content_timeout' => 300,
47             'addheader_timeout' => 30,
48             'dns_timeout' => 10,
49             'dns_retry' => 2,
50             'tempfail_on_error' => '1',
51             'tempfail_on_error_authenticated' => '0',
52             'tempfail_on_error_local' => '0',
53             'tempfail_on_error_trusted' => '0',
54             'milter_quarantine' => '0',
55             'ip_map' => {},
56             'handlers' => {}
57             };
58              
59 0         0 my $installed_handlers = Mail::Milter::Authentication::get_installed_handlers();
60 0         0 foreach my $handler ( @$installed_handlers ) {
61 0         0 my $handler_module = 'Mail::Milter::Authentication::Handler::' . $handler;
62 0         0 load $handler_module;
63 0 0       0 if ( $handler_module->can( 'default_config' ) ) {
64 0         0 $config->{'handlers'}->{ $handler } = $handler_module->default_config();
65             }
66             else {
67 0         0 $config->{'handlers'}->{ $handler } = {};
68             }
69             }
70              
71 0         0 return $config;
72              
73             }
74              
75              
76             sub set_config {
77 9     9 1 28 my ( $config ) = @_;
78              
79 9         22 my @load_handlers = keys %{ $config->{'handlers'} };
  9         45  
80 9         27 @load_handlers = grep { ! /^\!/ } @load_handlers;
  16         90  
81 9         31 $config->{'load_handlers'} = \@load_handlers;
82              
83 9   50     37 my $protocol = $config->{'protocol'} || 'milter';
84 9         22 $config->{'protocol'} = $protocol;
85 9         19 $CONFIG = $config;
86              
87 9         27 return;
88             }
89              
90              
91             sub load_file {
92 56     56 1 759 my ( $file ) = @_;
93              
94 56 50       1889 if ( !-e $file ) {
95 0         0 die "Could not find configuration file $file";
96             }
97              
98 56         530 my $text;
99             {
100 56   50     225 open my $cf, '<',
  56         6666  
101             $file || die "Could not open configuration file $file";
102 56         6021 my @t = <$cf>;
103 56         1010 close $cf;
104 56         1568 $text = join( q{}, @t );
105             }
106              
107 56         558 my $data;
108              
109 56 50       1560 if ( $file =~ /\.toml$/ ) {
110 0   0     0 $data = TOML::from_toml($text)
111             || die "Error parsing config file $file";
112             }
113             else {
114 56         2937 my $json = JSON->new();
115 56         5471 $json->relaxed(1);
116 56   50     16166 $data = $json->decode($text)
117             || die "Error parsing config file $file";
118             }
119              
120 56         475 return $data;
121             }
122              
123              
124             sub process_config {
125              
126 2567 50   2567 1 8362 if ( exists( $CONFIG->{ '_external_callback_processor' } ) ) {
127 0 0       0 if ( $CONFIG->{ '_external_callback_processor' }->can( 'process_config' ) ) {
128 0         0 $CONFIG->{ '_external_callback_processor' }->process_config( $CONFIG );
129             }
130             }
131              
132 2567         10508 return $CONFIG;
133             }
134              
135              
136             sub get_config {
137              
138 2567 100   2567 1 57650 if ( $CONFIG ) {
139 2511         6034 return process_config();
140             }
141              
142 56         962 my $file = $PREFIX . '/authentication_milter';
143 56         631 my $config;
144 56 50       3451 if ( -e $file . '.toml' ) {
145 0         0 $config = load_file( $file . '.toml' );
146             }
147             else {
148 56         1542 $config = load_file( $file . '.json' );
149             }
150              
151 56         343 my $folder = $PREFIX . '/authentication_milter.d';
152 56 50       1415 if ( -d $folder ) {
153 0         0 my $dh;
154 0         0 opendir $dh, $folder;
155             my @config_files =
156             sort
157 0         0 grep { $_ =~ /\.(json|toml)$/ }
158 0         0 grep { not $_ =~ /^\./ }
  0         0  
159             readdir($dh);
160 0         0 closedir $dh;
161 0         0 foreach my $file ( @config_files ) {
162 0         0 $file =~ /(^.*)\.(json|toml)$/;
163 0         0 my $handler = $1;
164             ## ToDo Consider what to do if config already exists in .json config
165 0         0 $config->{'handlers'}->{$handler} = load_file( join( '/', $folder, $file ) );
166             }
167             }
168              
169 56         744 my @load_handlers = keys %{ $config->{'handlers'} };
  56         937  
170 56         617 @load_handlers = grep { ! /^\!/ } @load_handlers;
  672         2856  
171 56         860 $config->{'load_handlers'} = \@load_handlers;
172              
173 56   100     1800 my $protocol = $config->{'protocol'} || 'milter';
174 56         834 $config->{'protocol'} = $protocol;
175              
176             # Have we specified an external callback processor?
177 56 50       902 if ( exists( $config->{ 'external_callback_processor' } ) ) {
178             # Try and load the handler
179 0         0 my $handler = $config->{ 'external_callback_processor' };
180 0 0       0 if ( ! is_loaded ( $handler ) ) {
181 0         0 eval {
182 99     99   799 no strict 'refs'; ## no critic;
  99         156  
  99         13522  
183 0         0 load $handler;
184 0         0 $config->{ '_external_callback_processor' } = $handler->new();
185             };
186 0 0       0 if ( my $error = $@ ) {
187 0         0 delete $config->{ 'external_callback_processor' };
188 0         0 warn "Error loading external callback processor module: $error";
189             }
190             }
191             }
192              
193 56         431 $CONFIG = $config;
194              
195 56         598 return process_config();
196              
197             }
198              
199             1;
200              
201             __END__
202              
203             =pod
204              
205             =encoding UTF-8
206              
207             =head1 NAME
208              
209             Mail::Milter::Authentication::Config - Load config files for Authentication Milter
210              
211             =head1 VERSION
212              
213             version 20191206
214              
215             =head1 SYNOPSIS
216              
217             Load in the configuration data, does some processing on handlers loaded before returning
218             config to the caller.
219              
220             If the $Mail::Milter::Authentication::Config::PREFIX variable is set then the config file
221             will be read from the supplied directory rather than /etc/
222              
223             =head1 DESCRIPTION
224              
225             Load in configuration data.
226              
227             =head1 FUNCTIONS
228              
229             =head2 I<default_config()>
230              
231             Return a default configuration including defaults from handler modules.
232              
233             This is not the default config used by the system if no config is present, rather it is the config
234             which is presented to the user as an example default config when using the help feature.
235              
236             =head2 I<set_config( $config )>
237              
238             Set the config hashref, primarily used for testing.
239              
240             =head2 I<load_file( $file )>
241              
242             Internal function used to load the config from /etc/authentication_milter.json
243              
244             =head2 I<process_config()>
245              
246             Process the loaded config with the callback if required.
247              
248             This is the name of a Module to load, the process_config method of the instantiated object
249             will be called with $config as the argument.g
250              
251             package ConfigProcessor;
252              
253             sub new {
254             ...
255             }
256              
257             sub process_config {
258             my ( $self, $config ) = @_;
259             return;
260             }
261              
262             1;
263              
264             =head2 I<get_config()>
265              
266             Return the config hashref, load from file(s) if required.
267              
268             =head1 AUTHOR
269              
270             Marc Bradshaw <marc@marcbradshaw.net>
271              
272             =head1 COPYRIGHT AND LICENSE
273              
274             This software is copyright (c) 2018 by Marc Bradshaw.
275              
276             This is free software; you can redistribute it and/or modify it under
277             the same terms as the Perl 5 programming language system itself.
278              
279             =cut